Mercurial > sdl-ios-xcode
view Xcode-iPhoneOS/Demos/src/fireworks.c @ 4447:947201caa46e
Added automated test to Xcode project plus needed changes to SDL_RWFromFile to be OS X bundle aware.
The Mac OS X project has a new target called testsdl which builds the automated test. I looked at using Xcode's native unit test support, but the impedance mismatch between the existing automated test structure and Apple's was more than I could handle.
As such, the testsdl application is a full blown proper OS X application, which means it is a self-contained .app bundle. This immediately revealed some problems from the automated test. The largest problem was the assumption about the current working directory and where to find resources. (I suspect Windows may have a similar problem depending on circumstance.) To open resources, the test was looking in directories relative to the SDL source directory, but this will not work well with self-contained .app bundles and Xcode which can place its built applications almost anywhere. And for real-world situations, this is pretty useless anyway.
So I modified SDL_RWFromFile to do special things on OS X. First, it will look for a file in the .app bundle. If not found, it will fallback and just call fopen as it used to do.
I also had to modify the automated test itself because it had a contrieved test which called fopen directly to do read from an existing FILE pointer. In addition, there was a write test. Since a .app bundle is likely going to be read-only, I added a special case for OS X to write to NSTemporaryDirectory.
I expect these changes should work for both Mac and iPhone OS (which includes iPad).
I will update the iPhone Xcode project next.
Finally, FYI, the X11 automated test seems to be failing. Below is my output.
Pending breakpoint 4 - "-[NSException raise]" resolved
Platform : All tests successful (2)
SDL_RWops : All tests successful (5)
Rect : All tests successful (1)
SDL_Surface : All tests successful (6)
Rendering with cocoa driver : All tests successful (3)
Assert Failed!
Blit output not the same.
Test Case 'Renderer x11'
Test Suite 'Rendering with x11 driver'
Last SDL error ''
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetSurfaceBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorFailure: Set a breakpoint @ CGErrorBreakpoint() to catch errors as they are logged.
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetWindowBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetSurfaceBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetWindowBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetSurfaceBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetWindowBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetSurfaceBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetWindowBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetSurfaceBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetWindowBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetSurfaceBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetWindowBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSGetSurfaceBounds
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Sat May 8 00:30:34 iMacAL.local testsdl[71586] <Error>: kCGErrorIllegalArgument: CGSBindSurface: Invalid window 0xa150
Rendering with x11 driver : Failed 1 out of 4 testcases!
Rendering with dummy driver : All tests successful (3)
SDL_Audio : All tests successful (1)
Tests run with SDL 1.3.0 revision 1095906
System is running Mac OS X and is little endian
author | Eric Wing <ewing . public |-at-| gmail . com> |
---|---|
date | Sat, 08 May 2010 00:54:22 -0700 |
parents | 64ce267332c6 |
children | 06c7423f8c60 |
line wrap: on
line source
/* * fireworks.c * written by Holmes Futrell * use however you want */ #include "SDL.h" #include "SDL_opengles.h" #include "common.h" #include <math.h> #include <time.h> #define MILLESECONDS_PER_FRAME 16 /* about 60 frames per second */ #define ACCEL 0.0001f /* acceleration due to gravity, units in pixels per millesecond squared */ #define WIND_RESISTANCE 0.00005f /* acceleration per unit velocity due to wind resistance */ #define MAX_PARTICLES 2000 /* maximum number of particles displayed at once */ static GLuint particleTextureID; /* OpenGL particle texture id */ static SDL_bool pointSizeExtensionSupported; /* is GL_OES_point_size_array supported ? */ /* used to describe what type of particle a given struct particle is. emitter - this particle flies up, shooting off trail particles, then finally explodes into dust particles. trail - shoots off, following emitter particle dust - radiates outwards from emitter explosion */ enum particleType { emitter = 0, trail, dust }; /* struct particle is used to describe each particle displayed on screen */ struct particle { GLfloat x; /* x position of particle */ GLfloat y; /* y position of particle */ GLubyte color[4]; /* rgba color of particle */ GLfloat size; /* size of particle in pixels */ GLfloat xvel; /* x velocity of particle in pixels per milesecond */ GLfloat yvel; /* y velocity of particle in pixels per millescond */ int isActive; /* if not active, then particle is overwritten */ enum particleType type; /* see enum particleType */ } particles[MAX_PARTICLES]; /* this array holds all our particles */ static int num_active_particles; /* how many members of the particle array are actually being drawn / animated? */ /* function declarations */ void spawnTrailFromEmitter(struct particle *emitter); void spawnEmitterParticle(GLfloat x, GLfloat y); void explodeEmitter(struct particle *emitter); void initializeParticles(void); void initializeTexture(); int nextPowerOfTwo(int x); void drawParticles(); void stepParticles(void); /* helper function (used in texture loading) returns next power of two greater than or equal to x */ int nextPowerOfTwo(int x) { int val = 1; while (val < x) { val *= 2; } return val; } /* steps each active particle by timestep MILLESECONDS_PER_FRAME */ void stepParticles(void) { int i; struct particle *slot = particles; struct particle *curr = particles; for (i = 0; i < num_active_particles; i++) { /* is the particle actually active, or is it marked for deletion? */ if (curr->isActive) { /* is the particle off the screen? */ if (curr->y > SCREEN_HEIGHT) curr->isActive = 0; else if (curr->y < 0) curr->isActive = 0; if (curr->x > SCREEN_WIDTH) curr->isActive = 0; else if (curr->x < 0) curr->isActive = 0; /* step velocity, then step position */ curr->yvel += ACCEL * MILLESECONDS_PER_FRAME; curr->xvel += 0.0f; curr->y += curr->yvel * MILLESECONDS_PER_FRAME; curr->x += curr->xvel * MILLESECONDS_PER_FRAME; /* particle behavior */ if (curr->type == emitter) { /* if we're an emitter, spawn a trail */ spawnTrailFromEmitter(curr); /* if we've reached our peak, explode */ if (curr->yvel > 0.0) { explodeEmitter(curr); } } else { float speed = sqrt(curr->xvel * curr->xvel + curr->yvel * curr->yvel); /* if wind resistance is not powerful enough to stop us completely, then apply winde resistance, otherwise just stop us completely */ if (WIND_RESISTANCE * MILLESECONDS_PER_FRAME < speed) { float normx = curr->xvel / speed; float normy = curr->yvel / speed; curr->xvel -= normx * WIND_RESISTANCE * MILLESECONDS_PER_FRAME; curr->yvel -= normy * WIND_RESISTANCE * MILLESECONDS_PER_FRAME; } else { curr->xvel = curr->yvel = 0; /* stop particle */ } if (curr->color[3] <= MILLESECONDS_PER_FRAME * 0.1275f) { /* if this next step will cause us to fade out completely then just mark for deletion */ curr->isActive = 0; } else { /* otherwise, let's fade a bit more */ curr->color[3] -= MILLESECONDS_PER_FRAME * 0.1275f; } /* if we're a dust particle, shrink our size */ if (curr->type == dust) curr->size -= MILLESECONDS_PER_FRAME * 0.010f; } /* if we're still active, pack ourselves in the array next to the last active guy (pack the array tightly) */ if (curr->isActive) *(slot++) = *curr; } /* endif (curr->isActive) */ curr++; } /* the number of active particles is computed as the difference between old number of active particles, where slot points, and the new size of the array, where particles points */ num_active_particles = slot - particles; } /* This draws all the particles shown on screen */ void drawParticles() { /* draw the background */ glClear(GL_COLOR_BUFFER_BIT); /* set up the position and color pointers */ glVertexPointer(2, GL_FLOAT, sizeof(struct particle), particles); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(struct particle), particles[0].color); if (pointSizeExtensionSupported) { /* pass in our array of point sizes */ glPointSizePointerOES(GL_FLOAT, sizeof(struct particle), &(particles[0].size)); } /* draw our particles! */ glDrawArrays(GL_POINTS, 0, num_active_particles); /* update screen */ SDL_RenderPresent(); } /* This causes an emitter to explode in a circular bloom of dust particles */ void explodeEmitter(struct particle *emitter) { /* first off, we're done with this particle, so turn active off */ emitter->isActive = 0; int i; for (i = 0; i < 200; i++) { if (num_active_particles >= MAX_PARTICLES) return; /* come up with a random angle and speed for new particle */ float theta = randomFloat(0, 2.0f * 3.141592); float exponent = 3.0f; float speed = randomFloat(0.00, powf(0.17, exponent)); speed = powf(speed, 1.0f / exponent); /*select the particle at the end of our array */ struct particle *p = &particles[num_active_particles]; /* set the particles properties */ p->xvel = speed * cos(theta); p->yvel = speed * sin(theta); p->x = emitter->x + emitter->xvel; p->y = emitter->y + emitter->yvel; p->isActive = 1; p->type = dust; p->size = 15; /* inherit emitter's color */ p->color[0] = emitter->color[0]; p->color[1] = emitter->color[1]; p->color[2] = emitter->color[2]; p->color[3] = 255; /* our array has expanded at the end */ num_active_particles++; } } /* This spawns a trail particle from an emitter */ void spawnTrailFromEmitter(struct particle *emitter) { if (num_active_particles >= MAX_PARTICLES) return; /* select the particle at the slot at the end of our array */ struct particle *p = &particles[num_active_particles]; /* set position and velocity to roughly that of the emitter */ p->x = emitter->x + randomFloat(-3.0, 3.0); p->y = emitter->y + emitter->size / 2.0f; p->xvel = emitter->xvel + randomFloat(-0.005, 0.005); p->yvel = emitter->yvel + 0.1; /* set the color to a random-ish orangy type color */ p->color[0] = (0.8f + randomFloat(-0.1, 0.0)) * 255; p->color[1] = (0.4f + randomFloat(-0.1, 0.1)) * 255; p->color[2] = (0.0f + randomFloat(0.0, 0.2)) * 255; p->color[3] = (0.7f) * 255; /* set other attributes */ p->size = 10; p->type = trail; p->isActive = 1; /* our array has expanded at the end */ num_active_particles++; } /* spawns a new emitter particle at the bottom of the screen destined for the point (x,y). */ void spawnEmitterParticle(GLfloat x, GLfloat y) { if (num_active_particles >= MAX_PARTICLES) return; /* find particle at endpoint of array */ struct particle *p = &particles[num_active_particles]; /* set the color randomly */ switch (rand() % 4) { case 0: p->color[0] = 255; p->color[1] = 100; p->color[2] = 100; break; case 1: p->color[0] = 100; p->color[1] = 255; p->color[2] = 100; break; case 2: p->color[0] = 100; p->color[1] = 100; p->color[2] = 255; break; case 3: p->color[0] = 255; p->color[1] = 150; p->color[2] = 50; break; } p->color[3] = 255; /* set position to (x, SCREEN_HEIGHT) */ p->x = x; p->y = SCREEN_HEIGHT; /* set velocity so that terminal point is (x,y) */ p->xvel = 0; p->yvel = -sqrt(2 * ACCEL * (SCREEN_HEIGHT - y)); /* set other attributes */ p->size = 10; p->type = emitter; p->isActive = 1; /* our array has expanded at the end */ num_active_particles++; } /* just sets the endpoint of the particle array to element zero */ void initializeParticles(void) { num_active_particles = 0; } /* loads the particle texture */ void initializeTexture() { int bpp; /* texture bits per pixel */ Uint32 Rmask, Gmask, Bmask, Amask; /* masks for pixel format passed into OpenGL */ SDL_Surface *bmp_surface; /* the bmp is loaded here */ SDL_Surface *bmp_surface_rgba8888; /* this serves as a destination to convert the BMP to format passed into OpenGL */ bmp_surface = SDL_LoadBMP("stroke.bmp"); if (bmp_surface == NULL) { fatalError("could not load stroke.bmp"); } /* Grab info about format that will be passed into OpenGL */ SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ABGR8888, &bpp, &Rmask, &Gmask, &Bmask, &Amask); /* Create surface that will hold pixels passed into OpenGL */ bmp_surface_rgba8888 = SDL_CreateRGBSurface(0, bmp_surface->w, bmp_surface->h, bpp, Rmask, Gmask, Bmask, Amask); /* Blit to this surface, effectively converting the format */ SDL_BlitSurface(bmp_surface, NULL, bmp_surface_rgba8888, NULL); glGenTextures(1, &particleTextureID); glBindTexture(GL_TEXTURE_2D, particleTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nextPowerOfTwo(bmp_surface->w), nextPowerOfTwo(bmp_surface->h), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* this is where we actually pass in the pixel data */ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bmp_surface->w, bmp_surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, bmp_surface_rgba8888->pixels); /* free bmp surface and converted bmp surface */ SDL_FreeSurface(bmp_surface); SDL_FreeSurface(bmp_surface_rgba8888); } int main(int argc, char *argv[]) { SDL_Window *window; /* main window */ Uint32 startFrame; /* time frame began to process */ Uint32 endFrame; /* time frame ended processing */ Uint32 delay; /* time to pause waiting to draw next frame */ int done; /* should we clean up and exit? */ /* initialize SDL */ if (SDL_Init(SDL_INIT_VIDEO) < 0) { fatalError("Could not initialize SDL"); } /* seed the random number generator */ srand(time(NULL)); /* request some OpenGL parameters that may speed drawing */ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0); SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); /* create main window and renderer */ window = SDL_CreateWindow(NULL, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS); SDL_CreateRenderer(window, 0, 0); /* load the particle texture */ initializeTexture(); /* check if GL_POINT_SIZE_ARRAY_OES is supported this is used to give each particle its own size */ pointSizeExtensionSupported = SDL_GL_ExtensionSupported("GL_OES_point_size_array"); /* set up some OpenGL state */ glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnable(GL_POINT_SPRITE_OES); glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, 1); if (pointSizeExtensionSupported) { /* we use this to set the sizes of all the particles */ glEnableClientState(GL_POINT_SIZE_ARRAY_OES); } else { /* if extension not available then all particles have size 10 */ glPointSize(10); } done = 0; /* enter main loop */ while (!done) { startFrame = SDL_GetTicks(); SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { done = 1; } if (event.type == SDL_MOUSEBUTTONDOWN) { int which = event.button.which; int x, y; SDL_SelectMouse(which); SDL_GetMouseState(&x, &y); spawnEmitterParticle(x, y); } } stepParticles(); drawParticles(); endFrame = SDL_GetTicks(); /* figure out how much time we have left, and then sleep */ delay = MILLESECONDS_PER_FRAME - (endFrame - startFrame); if (delay > MILLESECONDS_PER_FRAME) { delay = MILLESECONDS_PER_FRAME; } if (delay > 0) { SDL_Delay(delay); } } /* delete textures */ glDeleteTextures(1, &particleTextureID); /* shutdown SDL */ SDL_Quit(); return 0; }