OpenGL ES - updating the vertex array, adding/removing vertices - ios

I want to implement functionality so that i can add/remove vertices to/from a vertex array during runtime.
Is there a common way of doing this?
The recommended format for vertex data seems to be C arrays of structs,
so i've tried the following. Keep a pointer to an array of Vertex structs as property:
#property Vertex *vertices;
and then make a new array and copy the data over
- (void) addVertex:(Vertex)newVertex
{
int numberOfVertices = sizeof(vertices) / sizeof(Vertex);
Vertex newArray[numberOfVertices + 1];
for (int i = 0; i < numberOfVertices; i++)
newArray[i] = vertices[i];
newArray[numberOfVertices] = newVertex;
self.vertices = newArray;
}
but no luck. I'm not exactly confident in C so probably this is really trivial..

This is how I just did it:
//verts is an NSMutableArray and I want to have an CGPoint c array to use with
// glVertexPointer(2, GL_FLOAT, 0, vertices);... so:
CGPoint vertices[[verts count]];
for(int i=0; i<[verts count]; i++)
{
vertices[i] = [[verts objectAtIndex:i] CGPointValue];
}

here's how i do it now:
// re-allocate the array dynamically.
// realloc() will act like malloc() if vertices == NULL
Vertex newVertex = {{x,y},{r,g,b,a}};
numberOfVertices++;
vertices = (Vertex *) realloc(vertices, sizeof(Vertex) * numberOfVertices);
if(vertices == NULL) NSLog(#"FAIL allocating memory for vertex array");
else vertices[numberOfVertices - 1] = newVertex;
// clean up memory once i don't need the array anymore
if(vertices != NULL) free(vertices);
i suppose icnivad's method above is more flexible since you can do more stuff with a NSMutableArray, but using plain C arrays with malloc/realloc should be (much?) faster .

Related

Bitmap causing memory leak on device

i am using simple bitmap technique to convert text into image , after that i divide this image into raster and later on i am calculating the percentage of black pixels in each raster rectangle. Every thing works fine on simulator but get crashed on Device.here is some related code
-(int)blackValue:(UIImage *)image rect:(CGRect)rect {
int pixelInRect = (int)rect.size.width * rect.size.height;
__block int blackCount = 0;
ImageBitmap * imageBitmap = [[ImageBitmap alloc] initWithImage:image bitmapInfo:(CGBitmapInfo)kCGImageAlphaNoneSkipLast];
for (int x = 0; x <(int) rect.size.width; x++) {
for (int y = 0; y < (int) rect.size.height; y++) {
Byte * pixel = [imageBitmap pixelAtX:(int)rect.origin.x Y:(int)rect.origin.y];
Byte red = pixel[0];
if (red < 0.1)
{
blackCount++;
}
}
}
return blackCount/pixelInRect;
}
- (NSDictionary *)rasterizeBitmap:(UIImage *)image size:(CGFloat)size {
CGFloat width =(int) (image.size.width/size);
CGFloat height =(int) (image.size.height/size);
NSMutableArray *fields = [NSMutableArray array];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
CGRect rect = CGRectMake(x * size, y * size, size, size);
CGPoint center = CGPointMake(x * size + size/2.0, image.size.height - (y * size + size/2.0));
double black = [self blackValue:image rect:rect];
Field * field = [[Field alloc] init];
field.center = center;
field.black = black;
[fields addObject:field];
}
}
return #{#"width":#(width) , #"fields":fields};
}
when i have try to run it in Profile i got the below result
Can some one suggestion me how can i over come the memory issue?
The problem is that you're manually allocating memory in your ImageBitmap object, but you are never releasing it.
The two suspects are the bitmap context (context), and the bitmap data (contextData). Both of these are not managed by ARC, so you'll want to be freeing both of these yourself once you are done with them.
In ARC, you can simply implement the dealloc method in your ImageBitmap class and put your cleanup code there.
For example:
-(void) dealloc {
CGContextRelease(context); // releases the bitmap context, if it was created (CGContextRelease checks for NULL)
free(contextData); // releases the bitmap data (it was explicitly created, so no need to check)
}
It's also worth noting you should make init unavailable, and mark your designated initialiser.
This is because you cannot use your imageFromContext and pixelAtX:Y: instance methods without having created your instance through your custom initWithSize:bitmapInfo: initialiser, as it creates the bitmap context and allocates the memory for the bitmap data.
Therefore if you were to create your instance by calling init, and call one of your instance methods, you would most likely get a crash.
To remedy this, you can mark the init method as unavailable in your ImageBitmap.h file, and also mark your initWithSize:bitmapInfo: method as the designated initialiser.
-(instancetype) init NS_UNAVAILABLE;
-(id)initWithSize:(CGSize)size bitmapInfo:(CGBitmapInfo)bmInfo NS_DESIGNATED_INITIALIZER;
All the NS_UNAVAILABLE does is prevent you from creating your instance by just calling init, forcing you to use your custom initialisers.
If you try to do [[ImageBitmap alloc] init], the compiler will show you the following error:
All the NS_DESIGNATED_INITIALIZER does is make sure that any extra initialisers in ImageBitmap must create new instances through your initialiser, and will show you the following warning if they don't:
See here for more info on NS_DESIGNATED_INITIALIZER.
Now, in practise these are really just formalities as you're the only one who's going to be using this, and you know you have to use the custom initialisers. However, it's good to get these formalities right if you ever want to share your code with other people.

How to compare various NSArrays in Objective-c?

My code contains four NSArrays, each of which contains two objects which represent XY-Coordinate at any point on the screen. Two or more arrays may contain same set of coordinates. I need to find the coordinate which has highest number of repetition among these four arrays. Can the isEqualTo: method help in this case?
One approach would be to maintain an NSDictionary, use the coordinates in your arrays as keys and then maintain a counter for each coordinate (i.e. key) that you increment whenever you see the same coordinate.
This could look somewhat like this:
NSMutableDictionary *coordinateCount = [[NSMutableDictionary alloc] init];
for (int i = 0; i < coordinates.length; i++) { // do this loop for each of your 4 arrays
Coordinate *c = coordinates[i];
if ([coordinateCount containsKey:c]) {
NSInteger count = [coordinateCount[c] integerValue];
count++;
coordinateCount[c] = #(count);
}
else {
coordinateCount[c] = #(1);
}
}
// now you can retrieve the max count value from all collected values
Note that this code is not tested and has to be adjusted depending on your types and variable names.

Randomizing object values without duplication

I'm creating a game with SpriteKit. I have 8 different colored balls positioned at 8 different designated CGPoints on the screen. Once the user gets to a certain score, I would like to randomize the colors of the balls to all be different colors, but I would like to get this result without any of the colors and types duplicating.
I added the balls as objects to a global NSMutableArray and set up a method to enumerate the array. I then wrote an arc4random method to pick a random color type from the array and then apply it to the old ball type. Unfortunately I am getting some duplicates. Does anybody have any suggestions to help me randomize my ball types without duplication?
FYI, I have taken ample time out to read other randomization methods and none of them seem to necessarily answer my question. I am on a deadline. Can somebody please help me?
-(void)ballRotation{
NSLog(#"initial ball list: %#",_ballList);
[_ballList enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
int selectedIndex = arc4random() % _ballList.count;
NSLog(#"Selected Index: %i", selectedIndex);
//get a remain list of temp
Ball *newBall = _ballList[idx];
//get the ball at the current _ballList index
Ball *oldBall = _ballList[selectedIndex];
//change the ball in the old position to have the type & texture of the randomly selected ball
oldBall.Type = newBall.Type;
oldBall.texture = newBall.texture;
NSLog(#"new ball list: %lu", newBall.Type);
NSLog(#"new ball list: %#", newBall.texture);
[_ballList removeObjectAtIndex:selectedIndex];
}];
}
Create the array with all the colors. Shuffle it and assign to each ball in order. See Fisher-Yates Shuffle. Here's a category to do it:
#import <Foundation/Foundation.h>
#interface NSMutableArray (KD)
-(void) kd_shuffleArray;
#end
#implementation NSMutableArray (KD)
-(void) kd_shuffleArray {
NSUInteger count = self.count;
for (int i=count-1; i>0; i--) {
int random = arc4random_uniform(i+1);
[self exchangeObjectAtIndex:i withObjectAtIndex:random];
}
}
#end
Oh, and you never want to add/remove to an array while enumerating. It's one of the deadly sins.
Create a mutable array that stores the colors that get selected. Each time you randomize and get a color, compare that color to all the colors stored in the "alreadyChosenColor" array. If the colors are equal, randomize again until it finally does not match a color already existing in the array.
Code:
//Create an array named allColorArray with all colors in it that you will use
bool unique = NO;
while(unique==NO)
{
unique = YES;
//randomIndex is a random int in the range of the allColorArray
randomColor = [allColorArray objectAtIndex:randomIndex]
for(int i=0;i<alreadyChosenColor.count;i++)
{
if([alreadyChosenColor objectAtIndex:i]== randomColor)
unique=false;
}
}
//Set SKSpritenode to use randomColor.
//add randomColor to alreadyChosenColor array.

Crash running OpenGL on iOS after memory warning

I am having trouble with an app with an OpenGL component crashing on iPad. The app throws a memory warning and crashes, but it doesn't appear to be using that much memory. Am I missing something?
The app is based on the Vuforia augmented reality system (borrows heavily from the ImageTargets sample). I have about 40 different models I need to include in my app, so in the interests of memory conservation I am loading the objects (and rendering textures etc) dynamically in the app as I need them. I tried to copy the UIScrollView lazy loading idea. The three 4mb allocations are the textures I have loaded into memory ready for when the user selects a different model to display.
Anything odd in here?
I don't know much at all about OpenGL (part of the reason why I chose the Vurforia engine). Anything in this screen shot below that should concern me? Note that Vurforia's ImageTagets sample app also has Uninitialized Texture Data (about one per frame), so I don't think this is the problem.
Any help would be appreciated!!
Here is the code that generates the 3D objects (in EAGLView):
// Load the textures for use by OpenGL
-(void)loadATexture:(int)texNumber {
if (texNumber >= 0 && texNumber < [tempTextureList count]) {
currentlyChangingTextures = YES;
[textureList removeAllObjects];
[textureList addObject:[tempTextureList objectAtIndex:texNumber]];
Texture *tex = [[Texture alloc] init];
NSString *file = [textureList objectAtIndex:0];
[tex loadImage:file];
[textures replaceObjectAtIndex:texNumber withObject:tex];
[tex release];
// Remove all old textures outside of the one we're interested in and the two on either side of the picker.
for (int i = 0; i < [textures count]; ++i) {
if (i < targetIndex - 1 || i > targetIndex + 1) {
[textures replaceObjectAtIndex:i withObject:#""];
}
}
// Render - Generate the OpenGL texture objects
GLuint nID;
Texture *texture = [textures objectAtIndex:texNumber];
glGenTextures(1, &nID);
[texture setTextureID: nID];
glBindTexture(GL_TEXTURE_2D, nID);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, [texture width], [texture height], 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)[texture pngData]);
// Set up objects using the above textures.
Object3D *obj3D = [[Object3D alloc] init];
obj3D.numVertices = rugNumVerts;
obj3D.vertices = rugVerts;
obj3D.normals = rugNormals;
obj3D.texCoords = rugTexCoords;
obj3D.texture = [textures objectAtIndex:texNumber];
[objects3D replaceObjectAtIndex:texNumber withObject:obj3D];
[obj3D release];
// Remove all objects except the one currently visible and the ones on either side of the picker.
for (int i = 0; i < [tempTextureList count]; ++i) {
if (i < targetIndex - 1 || i > targetIndex + 1) {
Object3D *obj3D = [[Object3D alloc] init];
[objects3D replaceObjectAtIndex:i withObject:obj3D];
[obj3D release];
}
}
if (QCAR::GL_20 & qUtils.QCARFlags) {
[self initShaders];
}
currentlyChangingTextures = NO;
}
}
Here is the code in the textures object.
- (id)init
{
self = [super init];
pngData = NULL;
return self;
}
- (BOOL)loadImage:(NSString*)filename
{
BOOL ret = NO;
// Build the full path of the image file
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
NSString* fullPath = [resourcePath stringByAppendingPathComponent:filename];
// Create a UIImage with the contents of the file
UIImage* uiImage = [UIImage imageWithContentsOfFile:fullPath];
if (uiImage) {
// Get the inner CGImage from the UIImage wrapper
CGImageRef cgImage = uiImage.CGImage;
// Get the image size
width = CGImageGetWidth(cgImage);
height = CGImageGetHeight(cgImage);
// Record the number of channels
channels = CGImageGetBitsPerPixel(cgImage)/CGImageGetBitsPerComponent(cgImage);
// Generate a CFData object from the CGImage object (a CFData object represents an area of memory)
CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
// Copy the image data for use by Open GL
ret = [self copyImageDataForOpenGL: imageData];
CFRelease(imageData);
}
return ret;
}
- (void)dealloc
{
if (pngData) {
delete[] pngData;
}
[super dealloc];
}
#end
#implementation Texture (TexturePrivateMethods)
- (BOOL)copyImageDataForOpenGL:(CFDataRef)imageData
{
if (pngData) {
delete[] pngData;
}
pngData = new unsigned char[width * height * channels];
const int rowSize = width * channels;
const unsigned char* pixels = (unsigned char*)CFDataGetBytePtr(imageData);
// Copy the row data from bottom to top
for (int i = 0; i < height; ++i) {
memcpy(pngData + rowSize * i, pixels + rowSize * (height - 1 - i), width * channels);
}
return YES;
}
Odds are, you're not seeing the true memory usage of your application. As I explain in this answer, the Allocations instrument hides memory usage from OpenGL ES, so you can't use it to measure the size of your application. Instead, use the Memory Monitor instrument, which I'm betting will show that your application is using far more RAM than you think. This is a common problem people run into when trying to optimize OpenGL ES on iOS using Instruments.
If you're concerned about which objects or resources could be accumulating in memory, you can use the heap shots functionality of the Allocations instrument to identify specific resources that are allocated but never removed when performing repeated tasks within your application. That's how I've tracked down textures and other items that were not being properly deleted.
Seeing some code would help, but I can make some gusses:
I have about 40 different models I need to include in my app, so in the interests of memory conservation I am loading the objects (and rendering textures etc) dynamically in the app as I need them. I tried to copy the UIScrollView lazy loading idea. The three 4mb allocations are the textures I have loaded into memory ready for when the user selects a different model to display.
(...)
This kind of approach is not ideal; and it's most likely the reason for your problems, if the memory is not properly deallocated. Eventually you'll run out of memory and then your process dies if you don't take proper precautions. It's very likely that the engine used has some memory leak, exposed by your access scheme.
Today operating systems don't differentiate between RAM and storage. To them it's all just memory and all address space is backed by the block storage system anyway (if there's actually some storage device attached doesn't matter).
So here's what you should do: Instead of read-ing your models into memory, you should memory map them (mmap). This tells the OS "this part of storage should be visible in address space" and the OS kernel will do all the necessary transfers when they're due.
Note that Vurforia's ImageTagets sample app also has Uninitialized Texture Data (about one per frame), so I don't think this is the problem.
This is a strong indicator, that OpenGL texture objects don't get properly deleted.
Any help would be appreciated!!
My advice: Stop programming like it was the 1970ies. Today's computers and operating systems work differently. See also http://www.varnish-cache.org/trac/wiki/ArchitectNotes

How to set instance variables from an NSMutableArray?

So I'm at the point where I'm starting to reduce my spaghetti code down.
So right now, I have 11 different dinosaur images that I've put into an NSMutableArray using a "for" loop. I've also declared CCSprite instance variables in which I was hoping I can set each dinosaur image to so that I can check bounding boxes, set positions, etc. So I pointed each object from the array to an instance variable So far, I have this code:
.h file:
CCSprite *dinosaur1_c;
CCSprite *dinosaur2_c;
CCSprite *dinosaur3_c;
CCSprite *dinosaur4_c;
CCSprite *dinosaur5_c;
CCSprite *dinosaur6_c;
CCSprite *dinosaur7_c;
CCSprite *dinosaur8_c;
CCSprite *dinosaur9_c;
CCSprite *dinosaur10_c;
CCSprite *dinosaur11_c;
.m file
NSMutableArray *dinoSprites = [[NSMutableArray alloc] init];
for( int i = 1, j = 0; i <= 11 && j <= 10; i++, j++ )
{
id dino = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:#"dinosaur%d-c.png", i]];
[dinoSprites addObject:dino];
[sceneSpriteBatchNode addChild:dino];
}
dinosaur1_c = (CCSprite *)[dinoSprites objectAtIndex:0];
dinosaur2_c = (CCSprite *)[dinoSprites objectAtIndex:1];
dinosaur3_c = (CCSprite *)[dinoSprites objectAtIndex:2];
dinosaur4_c = (CCSprite *)[dinoSprites objectAtIndex:3];
dinosaur5_c = (CCSprite *)[dinoSprites objectAtIndex:4];
dinosaur6_c = (CCSprite *)[dinoSprites objectAtIndex:5];
dinosaur7_c = (CCSprite *)[dinoSprites objectAtIndex:6];
dinosaur8_c = (CCSprite *)[dinoSprites objectAtIndex:7];
dinosaur9_c = (CCSprite *)[dinoSprites objectAtIndex:8];
dinosaur10_c = (CCSprite *)[dinoSprites objectAtIndex:9];
dinosaur11_c = (CCSprite *)[dinoSprites objectAtIndex:10];
This bit of code does work, but I'm sure it can be reduced. How would I be able to set each of these instance variables using the "for" loop?
I'm using these instance variables in other methods to set positions, check collisions/intersects, fade ins, etc.
I put an equivalent-code to better explain what I'm trying to do:
NSMutableArray *dinoSprites = [[NSMutableArray alloc] init];
for( int i = 1, j = 0; i <= 11 && j <= 10; i++, j++ )
{
id dino = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:#"dinosaur%d-c.png", i]];
[dinoSprites addObject:dino];
[sceneSpriteBatchNode addChild:dino];
// Set instance variables
dinosaur%i_c = (CCSprite *)[dinoSprites objectAtIndex:j];
}
Is there a way to achieve what I am asking? After 2.5 hours of searching, I still have come up with nothing. Just finding solutions for animation frames.
Am I missing something small or should I have a different way to point to each image in the array to set their positions, fade ins, check bounding boxes, etc?
Any ideas / inputs are greatly appreciated!! Thanks for taking the time to read this! :D
Instead of having eleven CCSprite instance variables, you should just have one NSArray.
So in your .h:
#property (strong) NSArray *dinoSprites;
In your .m, do a
#synthesize dinoSprites;
and then replace the code from your question with:
NSMutableArray *newDinoSprites = [[NSMutableArray alloc] init];
for( int i = 1, j = 0; i <= 11 && j <= 10; i++, j++ )
{
id dino = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:#"dinosaur%d-c.png", i]];
[newDinoSprites addObject:dino];
[sceneSpriteBatchNode addChild:dino];
}
self.dinoSprites = [newDinoSprites copy];
And then, whenever you need to refer to what you used to call, say, dinosaur8_c you would instead just use (CCSprite *)[dinoSprites objectAtIndex:7].
Is there a way to achieve what I am asking? After 2.5 hours of
searching, I still have come up with nothing. Just finding solutions
for animation frames.
You seem to want a variable whose value is another variable, so that you can iterate over a set of ivars. I can think of two ways to do something like what you're asking.
The first way is to use the address of each variable, i.e. a pointer to each variable. Since pointers themselves are scalar values, not objects, you'd need to use a C-style array:
CCSprite *ivars[] = {&dinosaur1_c, &dinosaur2_c, &dinosaur3_c, &dinosaur4_c};
for (int i = 0; i < 4; i++) {
*(ivars[i]) = [dinoSprites objectAtIndex:i];
}
The second way is to use key value coding. Construct the name of each ivar as a string, and then use that as the key in a call to -setValue:forKey: like this:
NSString *name;
for (int i = 0; i < [dinoSprites count]; i++) {
name = [NSString stringWithFormat:#"dinosaur%d_c", i];
[self setValue:[dinoSprites objectAtIndex:i] forKey:name];
}
All that said, I'd strongly encourage you to not take either of the preceding two approaches to your problem. It's very likely that there are much better solutions to your problem that don't involve having a separate ivar for each dinosaur. You've already got the dinosaurs in an array, so there's no need to create a separate ivar for each. As a general rule, if you ever find yourself creating numbered variables like this, you should probably step back and rethink what you're doing -- numbering your variables is a strong signal that you should be using an array instead.
So, in your case, I don't see any reason that you couldn't use the array you already have everywhere in your code. Instead of dinosaur6_c, you can obviously use [dinoSprites objectAtIndex:6]. But you'll really clean up your code if you think of ways to treat all the dinosaurs with the same code, so that you're never hard-coding specific indicies into your array. For example, if you need to set each dinosaur to a different position, there are at least two good ways to go about it. One is to compute the position, if possible, based on the index. You can do this if you're laying out the dinosaurs in a regular way, like on a 3 x n grid:
for (int i = 0; i < [dinoSprites count]; i++) {
int row = i / DINOS_PER_ROW;
int column = i % DINOS_PER_ROW;
CGPoint position = CGPointMake(row * rowHeight, column * columnWidth);
[[dinoSprites objectAtIndex:i] setPosition:position];
}
If the position of each dinosaur isn't computable from its index in the array, then you're probably currently hard-coding the positions in your code. You can clean that up by at least moving the positions to an array, and perhaps by reading that array in from a data file. That will make your code much shorter, easier to understand, and easier to maintain:
NSArray *positions = [NSArray arrayWithContentsOfURL:someURL]; // read positions from a property list
NSAssert([positions count] >= [dinoSprites count], #"Not enough positions!");
for (int i = 0; i < [dinoSprites count]; i++) {
CGPoint position = [[positions objectAtIndex:i] pointValue];
[[dinoSprites objectAtIndex:i] setPosition: position;
}
Of course, I'm just using the position of the sprite here as an example. You can do exactly the same thing with any attribute that you'd want to apply to a dinosaur, and you can even do them all at once. Instead of a positions array that you load from a property list, you could load an array of dictionaries, where each dictionary contains a number of attributes (color, favorite food, speed, hunger level, etc.).

Resources