From: dsc Date: Thu, 5 May 2011 02:58:51 +0000 (-0700) Subject: Fixes the mess with sparrow. X-Git-Tag: box2d-testbed~13 X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=54df41362c0a83ac44051dc64a9ed5981c9b8e5b;p=tanks-ios.git Fixes the mess with sparrow. --- diff --git a/.gitignore b/.gitignore index edc082c..9fac711 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Xcode -build/* +*/build/* *.pbxuser !default.pbxuser *.mode1v3 diff --git a/libs/sparrow/BUILDING b/libs/sparrow/BUILDING new file mode 100644 index 0000000..2f3e629 --- /dev/null +++ b/libs/sparrow/BUILDING @@ -0,0 +1,46 @@ +---------------------------------------------------------------------------------------------------- +Sparrow: Tips for building an app +---------------------------------------------------------------------------------------------------- + +--- A First look at Sparrow ------------------------------------------------------------------------ + +In the folder 'samples/demo', you will find an Xcode project that shows the most basic Sparrow +features and how to use them. Just open the project in Xcode, compile and run - everything should +work out of the box. + +--- Creating a new Xcode project that uses Sparrow ------------------------------------------------- + +In the folder 'samples/scaffold', you will find an Xcode project that contains a bare-bone Sparrow +application. We recommend that you use this project as a starting point for your game. Please follow +this steps to use this Scaffold: + +Preconditions: + +Sparrow is linked to your application via Xcode project references. This has the advantage that it's +easy to update Sparrow (just download a new release and overwrite the old one in the same directory) +and that you can easily step into Sparrow source code, I case you want to do so. + +This has to be done only once: + +1. Set up a shared build output directory that will be shared by all Xcode projects. + * In the Xcode preferences, tab: "Building", set "Place Build Products in" to + "Customized location" and specify a common build directory (anywhere you want). + * Set "Place Intermediate Build Files in" to "With build products." +2. Add a "Source Tree" variable that Xcode can use to dynamically find Sparrow. + * In the Xcode preferences, tab: "Source Trees", create a new Source Tree variable. + * For Sparrow, create SPARROW_SRC and let it point to /some_valid_path/sparrow/src/ + +Creating your new project: + +1. Copy the "scaffold"-folder to the place where you want to have your game project. +2. Open "AppScaffold.xcodeproj" +3. Build and run - just to see if everything works fine. If it does not work, check if you have + created the SPARROW_SRC variable in Xcode, and if it points to the right place. +4. In Xcodes menu, click on "Project" -> "Rename ..." +5. Enter the name of your game. +6. That's it! Now you can start to develop your game with Sparrow. + +Selecting target hardware: iPhone / iPod Touch / iPad + +1. Enter the project Settings, tab: "Build" +2. Select the target of your choice for the setting "Targeted Device Family" \ No newline at end of file diff --git a/libs/sparrow/CHANGELOG b/libs/sparrow/CHANGELOG new file mode 100644 index 0000000..8aeb392 --- /dev/null +++ b/libs/sparrow/CHANGELOG @@ -0,0 +1,110 @@ +---------------------------------------------------------------------------------------------------- +Sparrow: Changelog +---------------------------------------------------------------------------------------------------- + +version 1.1 - 2011-01-12 + +- added SPRenderTexture +- added SPUtils class for easy random number generation (more to come) +- added support for looping and reversing tweens (thanks, Shilo!) +- added new transition method: 'randomize' +- added support for uncompressed PVR texture formats (565, 5551, 4444) +- added simple way to use HD textures on the iPad +- added support for creating dynamic texture atlases (add/remove regions on the fly) +- added methods to access the glyphs of SPBitmapFont directly +- added new init method to SPImage: 'initWithContentsOfImage:(UIImage *)image' +- added more factory methods to different classes +- added support for changing the fps of SPMovieClip at runtime (thanks Shilo!) +- added AppleDoc inline API documentation +- added bash script that generates API documentation +- updated SPView class to be more robust +- updated general rendering code (moved more OpenGL calls to SPRenderTexture) +- fixed bug with canceled touch events +- fixed bug that could cause a breakdown of the touch handling (thanks Kodi!) +- fixed 'isEqual' method of SPMatrix (thanks Matt!) +- fixed bug that could cut the outermost pixels of HD textures +- Special thanks to numerous forum members for bug reports, suggestions and feedback! + +version 1.0 - 2010-10-24 + +- added support for PVRTC-textures +- added new init method to SPTexture to allow simple use of Core Graphics drawings +- added new init method to SPMovieClip that uses an NSArray containing the frames +- added method 'childByName:' to SPDisplayObjectContainer +- added method 'texturesStartingWith:' to SPTextureAtlas +- added method 'scaleBy:' to SPPoint +- added method 'interpolateFromPoint:toPoint:ratio:' to SPPoint +- added property 'textBounds' to SPTextField +- added property 'name' to SPDisplayObject +- added workaround for unit test problem in Xcode 3.2.4 +- updated SPCompiledSprite-class to be public +- updated texture utilities for sharper output +- updated scaffold so that it is easier to create an iPad application +- fixed bug that classes inheriting directly from SPQuad were not rendered +- fixed that last-moment changes were not displayed when pausing stage +- fixed bug that could lead to a white flash when textures were released +- Special thanks to numerous forum members for bug reports, suggestions and feedback! + +version 0.9 - 2010-07-30 + +- !!! interface change !!! + The IDs of vertices in SPQuad and SPImage have changed. ("colorOfVertex:", "texCoordsOfVertex:") + Before: 0 = top left, 1 = top right, 2 = bottom right, 3 = bottom left + AFTER: 0 = top left, 1 = top right, 2 = bottom left, 3 = bottom right +- greatly improved rendering speed of SPTextField when used with bitmap fonts +- greatly improved performance of touch event analysis +- added Sparrow atlas generator (sparrow/util/atlas_generator) +- added Sparrow texture resizer (sparrow/util/texture_scaler) +- added support for high resolution screens (aka iPhone4's retina display) +- added support for high resolution textures ("texture@2x.png") +- added support for high resolution texture atlases ("atlas@2x.xml") +- added support for high resolution bitmap fonts ("font@2x.xml") +- added support for loading textures that are not inside the application bundle +- added support for loading sounds that are not inside the application bundle +- changed base SDK to iOS 4.0 +- added new event: SP_EVENT_TYPE_MOVIE_COMPLETED in SPMovieClip class +- added experimental feature: SPCompiledSprite +- added some missing "@private" declarations +- fixed memory access violation when object was destroyed within an enter frame event listener +- fixed bug that alpha values were only used when a texture was active +- fixed bug that SPDelayedInvocation (aka SPJuggler::delayInvocationAtTarget) did not retain + its arguments +- fixed bug that cancelled touch events would inhibit further user input +- code cleanup (especially concerning designated initializers) +- Special thanks to: Mike, Baike, Paolo, Jule and Alex_H for bug reports, suggestions and feedback! + +version 0.8 - 2010-06-05 + +- added audio classes +- added SPMovieClip class +- added new transition functions: + - easeInBack / easeOutBack / easeInOutBack / easeOutInBack + - easeInElastic / easeOutElastic / easeInOutElastic / easeOutInElastic + - easeInBounce / easeOutBounce / easeInOutBounce / easeOutInBounce +- added 'removeTweensWithTarget:'-method to juggler +- added 'removeAllChildren'-method to SPDisplayObjectContainer +- added new text-only constructor to SPTextField +- added NSXMLParserDelegate protocol statement for iPhone SDK 4+ +- added packer2sparrow utility +- added hiero2sparrow utility +- changed transition function signature (removed delta) +- changed rotation handling: angles now clamped from -180 to +180 degrees. + this should make most rotation tweens more intuitive. +- changed license text to allow easy AppStore distribution +- changed scaffold project to support audio +- changed demo project + - new design + - new scene: sound + - new scene: movie +- fixed touch issues when view size != stage size +- fixed exception that occurred when the same object was added to a container twice +- fixed flickering at application start +- fixed method signatures in SPTexture.m +- 'removeChild'-method in SPDisplayObjectContainer no longer throws an exception when the + object is not a child, but now silently ignores the failure. +- the stage property is now accessible in the REMOVED_FROM_STAGE event +- disabled unit test execution in iPhone SDK < 3 (unit tests are only supported by iPhone SDK 3+) + +version 0.7 - 2010-01-14 + +- first public version diff --git a/libs/sparrow/LICENSE b/libs/sparrow/LICENSE new file mode 100644 index 0000000..7164bd5 --- /dev/null +++ b/libs/sparrow/LICENSE @@ -0,0 +1,33 @@ +---------------------------------------------------------------------------------------------- +Sparrow: Simplified BSD License +---------------------------------------------------------------------------------------------- + +Copyright 2010 incognitek. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + 3. Redistributions via the Apple App Store constitute an exception to section 2. It is + sufficient to add a copy of this license to the application's internet page (if available). + The content of the following disclaimer is still in effect. + +THIS SOFTWARE IS PROVIDED BY INCOGNITEK ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INCOGNITEK OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of incognitek. \ No newline at end of file diff --git a/libs/sparrow/README b/libs/sparrow/README new file mode 100644 index 0000000..9d9715c --- /dev/null +++ b/libs/sparrow/README @@ -0,0 +1,26 @@ +---------------------------------------------------------------------------------------------------- +Sparrow: an Open Source Framework for iPhone game development +---------------------------------------------------------------------------------------------------- + +--- What is Sparrow? ------------------------------------------------------------------------------- + +Sparrow is a pure Objective C library targeted on making game development as easy and hassle-free +as possible. Sparrow makes it possible to write fast OpenGL applications without having to touch +OpenGL or pure C (but easily allowing to do so, for those who wish). It uses a tried and tested +API that is easy to use and hard to misuse. + +--- Who is Sparrow for? ---------------------------------------------------------------------------- + +Obviously Sparrow is for iPhone developers, especially those involved in game development. You +will need to have a basic understanding of Objective C – but there’s no way around that on the +iPhone anyway. + +If you have already worked with Adobe™ Flash/Flex technology, you will immediately befriend with +Sparrow since it uses lots of similar concepts and naming schemes. That said, everything is +designed to be as intuitive as possible, so any Java™ or .Net™ developer will get the hang of it +quickly as well. + +--- How to start? ---------------------------------------------------------------------------------- + +* Read through the file 'BUILDING' for a quick start with Sparrow. +* Visit --> http://www.sparrow-framework.org <-- \ No newline at end of file diff --git a/libs/sparrow/doc/generate.sh b/libs/sparrow/doc/generate.sh new file mode 100755 index 0000000..c6bb5e6 --- /dev/null +++ b/libs/sparrow/doc/generate.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# This script creates a nice API reference documentation for the Sparrow source +# and installs it in Xcode. +# +# To execute it, you need the "AppleDoc"-tool. Download it here: +# http://www.gentlebytes.com/home/appledocapp/ + +echo "Please enter the version number (like '1.0'), followed by [ENTER]:" +read version + +appledoc \ + --project-name "Sparrow Framework" \ + --project-company "Incognitek" \ + --company-id com.incognitek \ + --project-version "$version" \ + --ignore ".m" \ + --ignore "_Internal.h" \ + --keep-undocumented-objects \ + --keep-undocumented-members \ + --keep-intermediate-files \ + --docset-bundle-id "org.sparrow-framework.docset" \ + --docset-bundle-name "Sparrow Framework API Documentation" \ + --docset-atom-filename "docset.atom" \ + --docset-feed-url "http://doc.sparrow-framework.org/core/feed/%DOCSETATOMFILENAME" \ + --docset-package-url "http://doc.sparrow-framework.org/core/feed/%DOCSETPACKAGEFILENAME" \ + --install-docset \ + --publish-docset \ + --output . \ + ../src/Classes + +echo +echo "Finished." diff --git a/libs/sparrow/src/Classes/SPALSound.h b/libs/sparrow/src/Classes/SPALSound.h new file mode 100644 index 0000000..20e250c --- /dev/null +++ b/libs/sparrow/src/Classes/SPALSound.h @@ -0,0 +1,45 @@ +// +// SPALSound.h +// Sparrow +// +// Created by Daniel Sperl on 28.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPSound.h" + +/** ------------------------------------------------------------------------------------------------ + + The SPALSound class is a concrete implementation of SPSound that uses OpenAL internally. + + Don't create instances of this class manually. Use [SPSound initWithContentsOfFile:] instead. + +------------------------------------------------------------------------------------------------- */ + +@interface SPALSound : SPSound +{ + @private + uint mBufferID; + double mDuration; +} + +/// -------------------- +/// @name Initialization +/// -------------------- + +/// Initializes a sound with its known properties. +- (id)initWithData:(const void *)data size:(int)size channels:(int)channels frequency:(int)frequency + duration:(double)duration; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The OpenAL buffer ID of the sound. +@property (nonatomic, readonly) uint bufferID; + +@end diff --git a/libs/sparrow/src/Classes/SPALSound.m b/libs/sparrow/src/Classes/SPALSound.m new file mode 100644 index 0000000..801960a --- /dev/null +++ b/libs/sparrow/src/Classes/SPALSound.m @@ -0,0 +1,94 @@ +// +// SPALSound.m +// Sparrow +// +// Created by Daniel Sperl on 28.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPALSound.h" +#import "SPALSoundChannel.h" +#import "SPAudioEngine.h" + +#import +#import + +@implementation SPALSound + +@synthesize duration = mDuration; +@synthesize bufferID = mBufferID; + +- (id)init +{ + [self release]; + return nil; +} + +- (id)initWithData:(void *)data size:(int)size channels:(int)channels frequency:(int)frequency + duration:(double)duration +{ + if (self = [super init]) + { + BOOL success = NO; + mDuration = duration; + + do + { + [SPAudioEngine start]; + + ALCcontext *const currentContext = alcGetCurrentContext(); + if (!currentContext) + { + NSLog(@"Could not get current OpenAL context"); + break; + } + + ALenum errorCode; + + alGenBuffers(1, &mBufferID); + errorCode = alGetError(); + if (errorCode != AL_NO_ERROR) + { + NSLog(@"Could not allocate OpenAL buffer (%x)", errorCode); + break; + } + + int format = (channels > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; + + alBufferData(mBufferID, format, data, size, frequency); + errorCode = alGetError(); + if (errorCode != AL_NO_ERROR) + { + NSLog(@"Could not fill OpenAL buffer (%x)", errorCode); + break; + } + + success = YES; + } + while (NO); + + if (!success) + { + [self release]; + return nil; + } + } + return self; +} + +- (SPSoundChannel *)createChannel +{ + return [[[SPALSoundChannel alloc] initWithSound:self] autorelease]; +} + +- (void) dealloc +{ + alDeleteBuffers(1, &mBufferID); + mBufferID = 0; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPALSoundChannel.h b/libs/sparrow/src/Classes/SPALSoundChannel.h new file mode 100644 index 0000000..957277c --- /dev/null +++ b/libs/sparrow/src/Classes/SPALSoundChannel.h @@ -0,0 +1,46 @@ +// +// SPALSoundChannel.h +// Sparrow +// +// Created by Daniel Sperl on 28.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPSoundChannel.h" + +@class SPALSound; + +/** ------------------------------------------------------------------------------------------------ + + The SPALSoundChannel class is a concrete implementation of SPSoundChannel that uses + OpenAL internally. + + Don't create instances of this class manually. Use [SPSound createChannel] instead. + +------------------------------------------------------------------------------------------------- */ + +@interface SPALSoundChannel : SPSoundChannel +{ + @private + SPALSound *mSound; + uint mSourceID; + float mVolume; + BOOL mLoop; + + double mStartMoment; + double mPauseMoment; + BOOL mInterrupted; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a sound channel from an SPALSound object. +- (id)initWithSound:(SPALSound *)sound; + +@end diff --git a/libs/sparrow/src/Classes/SPALSoundChannel.m b/libs/sparrow/src/Classes/SPALSoundChannel.m new file mode 100644 index 0000000..2ce4410 --- /dev/null +++ b/libs/sparrow/src/Classes/SPALSoundChannel.m @@ -0,0 +1,217 @@ +// +// SPALSoundChannel.m +// Sparrow +// +// Created by Daniel Sperl on 28.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPALSoundChannel.h" +#import "SPALSound.h" +#import "SPAudioEngine.h" +#import "SPMacros.h" + +#import // for CACurrentMediaTime +#import +#import + +// --- private interface --------------------------------------------------------------------------- + +@interface SPALSoundChannel () + +- (void)scheduleSoundCompletedEvent; +- (void)revokeSoundCompletedEvent; + +@end + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPALSoundChannel + +@synthesize volume = mVolume; +@synthesize loop = mLoop; + +- (id)init +{ + [self release]; + return nil; +} + +- (id)initWithSound:(SPALSound *)sound +{ + if (self = [super init]) + { + mSound = [sound retain]; + mVolume = 1.0f; + mLoop = NO; + mInterrupted = NO; + mStartMoment = 0.0; + mPauseMoment = 0.0; + + alGenSources(1, &mSourceID); + alSourcei(mSourceID, AL_BUFFER, sound.bufferID); + ALenum errorCode = alGetError(); + if (errorCode != AL_NO_ERROR) + { + NSLog(@"Could not create OpenAL source (%x)", errorCode); + [self release]; + return nil; + } + + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(onInterruptionBegan:) + name:SP_NOTIFICATION_AUDIO_INTERRUPTION_BEGAN object:nil]; + [nc addObserver:self selector:@selector(onInterruptionEnded:) + name:SP_NOTIFICATION_AUDIO_INTERRUPTION_ENDED object:nil]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + alSourceStop(mSourceID); + alSourcei(mSourceID, AL_BUFFER, 0); + alDeleteSources(1, &mSourceID); + mSourceID = 0; + [mSound release]; + [super dealloc]; +} + +- (void)play +{ + if (!self.isPlaying) + { + double now = CACurrentMediaTime(); + + if (mPauseMoment != 0.0) // paused + { + mStartMoment += now - mPauseMoment; + mPauseMoment = 0.0; + } + else // stopped + { + mStartMoment = now; + } + + [self scheduleSoundCompletedEvent]; + alSourcePlay(mSourceID); + } +} + +- (void)pause +{ + if (self.isPlaying) + { + [self revokeSoundCompletedEvent]; + mPauseMoment = CACurrentMediaTime(); + alSourcePause(mSourceID); + } +} + +- (void)stop +{ + [self revokeSoundCompletedEvent]; + mStartMoment = mPauseMoment = 0.0; + alSourceStop(mSourceID); +} + +- (BOOL)isPlaying +{ + ALint state; + alGetSourcei(mSourceID, AL_SOURCE_STATE, &state); + return state == AL_PLAYING; +} + +- (BOOL)isPaused +{ + ALint state; + alGetSourcei(mSourceID, AL_SOURCE_STATE, &state); + return state == AL_PAUSED; +} + +- (BOOL)isStopped +{ + ALint state; + alGetSourcei(mSourceID, AL_SOURCE_STATE, &state); + return state == AL_STOPPED; +} + +- (void)setLoop:(BOOL)value +{ + if (value != mLoop) + { + mLoop = value; + alSourcei(mSourceID, AL_LOOPING, mLoop); + } +} + +- (void)setVolume:(float)value +{ + if (value != mVolume) + { + mVolume = value; + alSourcef(mSourceID, AL_GAIN, mVolume); + } +} + +- (double)duration +{ + return [mSound duration]; +} + +- (void)scheduleSoundCompletedEvent +{ + if (mStartMoment != 0.0) + { + double remainingTime = mSound.duration - (CACurrentMediaTime() - mStartMoment); + [self revokeSoundCompletedEvent]; + if (remainingTime >= 0.0) + { + [self performSelector:@selector(dispatchSoundCompletedEvent) withObject:nil + afterDelay:remainingTime]; + } + } +} + +- (void)revokeSoundCompletedEvent +{ + [NSObject cancelPreviousPerformRequestsWithTarget:self + selector:@selector(dispatchSoundCompletedEvent) object:nil]; +} + +- (void)dispatchSoundCompletedEvent +{ + if (!mLoop) + { + SPEvent *event = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_SOUND_COMPLETED]; + [self dispatchEvent:event]; + [event release]; + } +} + +- (void)onInterruptionBegan:(NSNotification *)notification +{ + if (self.isPlaying) + { + [self revokeSoundCompletedEvent]; + mInterrupted = YES; + mPauseMoment = CACurrentMediaTime(); + } +} + +- (void)onInterruptionEnded:(NSNotification *)notification +{ + if (mInterrupted) + { + mStartMoment += CACurrentMediaTime() - mPauseMoment; + mPauseMoment = 0.0; + mInterrupted = NO; + [self scheduleSoundCompletedEvent]; + } +} + +@end diff --git a/libs/sparrow/src/Classes/SPAVSound.h b/libs/sparrow/src/Classes/SPAVSound.h new file mode 100644 index 0000000..ce66c0b --- /dev/null +++ b/libs/sparrow/src/Classes/SPAVSound.h @@ -0,0 +1,46 @@ +// +// SPAVSound.h +// Sparrow +// +// Created by Daniel Sperl on 29.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import + +#import "SPSound.h" + +/** ------------------------------------------------------------------------------------------------ + + The SPAVSound class is a concrete implementation of SPSound that uses AVAudioPlayer internally. + + Don't create instances of this class manually. Use [SPSound initWithContentsOfFile:] instead. + + */ + +@interface SPAVSound : SPSound +{ + @private + NSData *mSoundData; + double mDuration; +} + +/// -------------------- +/// @name Initialization +/// -------------------- + +/// Initializes a sound with the contents of a file and the known duration. +- (id)initWithContentsOfFile:(NSString *)path duration:(double)duration; + +/// ------------- +/// @name methods +/// ------------- + +/// Creates an AVAudioPlayer object from the sound. +- (AVAudioPlayer *)createPlayer; + +@end diff --git a/libs/sparrow/src/Classes/SPAVSound.m b/libs/sparrow/src/Classes/SPAVSound.m new file mode 100644 index 0000000..5b917e7 --- /dev/null +++ b/libs/sparrow/src/Classes/SPAVSound.m @@ -0,0 +1,56 @@ +// +// SPAVSound.m +// Sparrow +// +// Created by Daniel Sperl on 29.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPAVSound.h" +#import "SPAVSoundChannel.h" + +@implementation SPAVSound + +@synthesize duration = mDuration; + +- (id)init +{ + [self release]; + return nil; +} + +- (id)initWithContentsOfFile:(NSString *)path duration:(double)duration +{ + if (self = [super init]) + { + NSString *fullPath = [path isAbsolutePath] ? + path : [[NSBundle mainBundle] pathForResource:path ofType:nil]; + mSoundData = [[NSData alloc] initWithContentsOfMappedFile:fullPath]; + mDuration = duration; + } + return self; +} + +- (void)dealloc +{ + [mSoundData release]; + [super dealloc]; +} + +- (SPSoundChannel *)createChannel +{ + return [[[SPAVSoundChannel alloc] initWithSound:self] autorelease]; +} + +- (AVAudioPlayer *)createPlayer +{ + NSError *error = nil; + AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithData:mSoundData error:&error]; + if (error) NSLog(@"Could not create AVAudioPlayer: %@", [error description]); + return [player autorelease]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPAVSoundChannel.h b/libs/sparrow/src/Classes/SPAVSoundChannel.h new file mode 100644 index 0000000..8d722ae --- /dev/null +++ b/libs/sparrow/src/Classes/SPAVSoundChannel.h @@ -0,0 +1,43 @@ +// +// SPAVSoundChannel.h +// Sparrow +// +// Created by Daniel Sperl on 29.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import + +#import "SPSoundChannel.h" +#import "SPAVSound.h" + +/** ------------------------------------------------------------------------------------------------ + + The SPAVSoundChannel class is a concrete implementation of SPSoundChannel that uses AVAudioPlayer + internally. + + Don't create instances of this class manually. Use [SPSound createChannel] instead. + +------------------------------------------------------------------------------------------------- */ + +@interface SPAVSoundChannel : SPSoundChannel +{ + @private + SPAVSound *mSound; + AVAudioPlayer *mPlayer; + BOOL mPaused; + float mVolume; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a sound channel from an SPAVSound object. +- (id)initWithSound:(SPAVSound *)sound; + +@end diff --git a/libs/sparrow/src/Classes/SPAVSoundChannel.m b/libs/sparrow/src/Classes/SPAVSoundChannel.m new file mode 100644 index 0000000..81c411c --- /dev/null +++ b/libs/sparrow/src/Classes/SPAVSoundChannel.m @@ -0,0 +1,136 @@ +// +// SPAVSoundChannel.m +// Sparrow +// +// Created by Daniel Sperl on 29.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPAVSoundChannel.h" +#import "SPAudioEngine.h" +#import "SPMacros.h" + +@implementation SPAVSoundChannel + +- (id)init +{ + [self release]; + return nil; +} + +- (id)initWithSound:(SPAVSound *)sound +{ + if (self = [super init]) + { + mVolume = 1.0f; + mSound = [sound retain]; + mPlayer = [[sound createPlayer] retain]; + mPlayer.delegate = self; + [mPlayer prepareToPlay]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(onMasterVolumeChanged:) + name:SP_NOTIFICATION_MASTER_VOLUME_CHANGED object:nil]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [mPlayer release]; + [mSound release]; + [super dealloc]; +} + +- (void)play +{ + mPaused = NO; + [mPlayer play]; +} + +- (void)pause +{ + mPaused = YES; + [mPlayer pause]; +} + +- (void)stop +{ + mPaused = NO; + [mPlayer stop]; + mPlayer.currentTime = 0; +} + +- (BOOL)isPlaying +{ + return mPlayer.playing; +} + +- (BOOL)isPaused +{ + return mPaused && !mPlayer.playing; +} + +- (BOOL)isStopped +{ + return !mPaused && !mPlayer.playing; +} + +- (BOOL)loop +{ + return mPlayer.numberOfLoops < 0; +} + +- (void)setLoop:(BOOL)value +{ + mPlayer.numberOfLoops = value ? -1 : 0; +} + +- (float)volume +{ + return mVolume; +} + +- (void)setVolume:(float)value +{ + mVolume = value; + mPlayer.volume = value * [SPAudioEngine masterVolume]; +} + +- (double)duration +{ + return mPlayer.duration; +} + +- (void)onMasterVolumeChanged:(NSNotification *)notification +{ + self.volume = mVolume; +} + +#pragma mark AVAudioPlayerDelegate + +- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag +{ + [self dispatchEvent:[SPEvent eventWithType:SP_EVENT_TYPE_SOUND_COMPLETED]]; +} + +- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error +{ + NSLog(@"Error during sound decoding: %@", [error description]); +} + +- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player +{ + [player pause]; +} + +- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player +{ + [player play]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPAnimatable.h b/libs/sparrow/src/Classes/SPAnimatable.h new file mode 100644 index 0000000..4668d71 --- /dev/null +++ b/libs/sparrow/src/Classes/SPAnimatable.h @@ -0,0 +1,29 @@ +// +// SPAnimatable.h +// Sparrow +// +// Created by Daniel Sperl on 09.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +/** ------------------------------------------------------------------------------------------------ + + The SPAnimatable protocol describes objects that are animated depending on the passed time. + Any object that implements this protocol can be added to the SPJuggler. + +------------------------------------------------------------------------------------------------- */ + +@protocol SPAnimatable + +/// Advance the animation by a number of seconds. +- (void)advanceTime:(double)seconds; + +/// Indicates if the animation is finished. (The juggler will purge the object.) +@property (nonatomic, readonly) BOOL isComplete; + +@end diff --git a/libs/sparrow/src/Classes/SPAudioEngine.h b/libs/sparrow/src/Classes/SPAudioEngine.h new file mode 100644 index 0000000..d59fe5d --- /dev/null +++ b/libs/sparrow/src/Classes/SPAudioEngine.h @@ -0,0 +1,64 @@ +// +// SPAudioEngine.h +// Sparrow +// +// Created by Daniel Sperl on 14.11.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +#define SP_NOTIFICATION_MASTER_VOLUME_CHANGED @"masterVolumeChanged" +#define SP_NOTIFICATION_AUDIO_INTERRUPTION_BEGAN @"audioInterruptionBegan" +#define SP_NOTIFICATION_AUDIO_INTERRUPTION_ENDED @"audioInterruptionEnded" + +typedef enum { + SPAudioSessionCategory_AmbientSound = 'ambi', + SPAudioSessionCategory_SoloAmbientSound = 'solo', + SPAudioSessionCategory_MediaPlayback = 'medi', + SPAudioSessionCategory_RecordAudio = 'reca', + SPAudioSessionCategory_PlayAndRecord = 'plar', + SPAudioSessionCategory_AudioProcessing = 'proc' +} SPAudioSessionCategory; + +/** ------------------------------------------------------------------------------------------------ + + The SPAudioEngine prepares the system for audio playback and controls global volume. + + Before you play sounds, you should start an audio session. The type of the audio session + defines how iOS will handle audio processing and how iPod music will mix with your audio. + + * `SPAudioSessionCategory_AmbientSound:` iPod music mixes with your audio, audio silences on mute + * `SPAudioSessionCategory_SoloAmbientSound:` iPod music is silenced, audio silences on mute + * `SPAudioSessionCategory_MediaPlayback:` iPod music is silenced, audio continues on mute + * `SPAudioSessionCategory_RecordAudio:` iPod music is silenced, used for audio recording + * `SPAudioSessionCategory_PlayAndRecord:` iPod music is silenced, for simultaneous in- and output + * `SPAudioSessionCategory_AudioProcessing:` For using an audio hardware codec or signal processor + + */ + +@interface SPAudioEngine : NSObject + +/// ------------- +/// @name Methods +/// ------------- + +/// Starts an audio session with a specified category. Call this at the start of your application. ++ (void)start:(SPAudioSessionCategory)category; + +/// Starts an audio session with with the category 'SoloAmbientSound'. ++ (void)start; + +/// Stops the audio session. Call this before the application shuts down. ++ (void)stop; + +/// The master volume for all audio. Default: 1.0 ++ (float)masterVolume; + +/// Set the master volume for all audio. Range: [0.0 - 1.0] ++ (void)setMasterVolume:(float)volume; + +@end diff --git a/libs/sparrow/src/Classes/SPAudioEngine.m b/libs/sparrow/src/Classes/SPAudioEngine.m new file mode 100644 index 0000000..3f8bd06 --- /dev/null +++ b/libs/sparrow/src/Classes/SPAudioEngine.m @@ -0,0 +1,174 @@ +// +// SPAudioEngine.m +// Sparrow +// +// Created by Daniel Sperl on 14.11.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPAudioEngine.h" + +#import +#import +#import + +@interface SPAudioEngine () + ++ (BOOL)initAudioSession:(SPAudioSessionCategory)category; ++ (BOOL)initOpenAL; + ++ (void)beginInterruption; ++ (void)endInterruption; + ++ (void)postNotification:(NSString *)name object:(id)object; + +@end + +@implementation SPAudioEngine + +// --- C functions --- + +void interruptionCallback (void *inUserData, UInt32 interruptionState) +{ + if (interruptionState == kAudioSessionBeginInterruption) + [SPAudioEngine beginInterruption]; + else if (interruptionState == kAudioSessionEndInterruption) + [SPAudioEngine endInterruption]; +} + +// --- static members --- + +static ALCdevice *device = NULL; +static ALCcontext *context = NULL; +static float masterVolume = 1.0f; + +// --- + +- (id)init +{ + [self release]; + [NSException raise:NSGenericException format:@"Static class - do not initialize!"]; + return nil; +} + ++ (void)start:(SPAudioSessionCategory)category +{ + if (!device) + { + if ([SPAudioEngine initAudioSession:category]) + [SPAudioEngine initOpenAL]; + } +} + ++ (void)start +{ + [SPAudioEngine start:SPAudioSessionCategory_SoloAmbientSound]; +} + ++ (void)stop +{ + alcMakeContextCurrent(NULL); + alcDestroyContext(context); + alcCloseDevice(device); + + device = NULL; + context = NULL; + + AudioSessionSetActive(NO); +} + ++ (BOOL)initAudioSession:(SPAudioSessionCategory)category +{ + static BOOL sessionInitialized = NO; + OSStatus result; + + if (!sessionInitialized) + { + result = AudioSessionInitialize(NULL, NULL, interruptionCallback, NULL); + if (result != kAudioSessionNoError) + { + NSLog(@"Could not initialize audio session: %x", result); + return NO; + } + sessionInitialized = YES; + } + + UInt32 sessionCategory = category; + AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, + sizeof(sessionCategory), &sessionCategory); + + result = AudioSessionSetActive(YES); + if (result != kAudioSessionNoError) + { + NSLog(@"Could not activate audio session: %x", result); + return NO; + } + + return YES; +} + ++ (BOOL)initOpenAL +{ + alGetError(); // reset any errors + + device = alcOpenDevice(NULL); + if (!device) + { + NSLog(@"Could not open default OpenAL device"); + return NO; + } + + context = alcCreateContext(device, 0); + if (!context) + { + NSLog(@"Could not create OpenAL context for default device"); + return NO; + } + + BOOL success = alcMakeContextCurrent(context); + if (!success) + { + NSLog(@"Could not set current OpenAL context"); + return NO; + } + + return YES; +} + ++ (void)beginInterruption +{ + [SPAudioEngine postNotification:SP_NOTIFICATION_AUDIO_INTERRUPTION_BEGAN object:nil]; + alcMakeContextCurrent(NULL); + AudioSessionSetActive(NO); +} + ++ (void)endInterruption +{ + AudioSessionSetActive(YES); + alcMakeContextCurrent(context); + alcProcessContext(context); + [SPAudioEngine postNotification:SP_NOTIFICATION_AUDIO_INTERRUPTION_ENDED object:nil]; +} + ++ (float)masterVolume +{ + return masterVolume; +} + ++ (void)setMasterVolume:(float)volume +{ + masterVolume = volume; + alListenerf(AL_GAIN, volume); + [SPAudioEngine postNotification:SP_NOTIFICATION_MASTER_VOLUME_CHANGED object:nil]; +} + ++ (void)postNotification:(NSString *)name object:(id)object +{ + [[NSNotificationCenter defaultCenter] postNotification: + [NSNotification notificationWithName:name object:object]]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPBitmapChar.h b/libs/sparrow/src/Classes/SPBitmapChar.h new file mode 100644 index 0000000..07804fa --- /dev/null +++ b/libs/sparrow/src/Classes/SPBitmapChar.h @@ -0,0 +1,57 @@ +// +// SPBitmapChar.h +// Sparrow +// +// Created by Daniel Sperl on 12.10.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPImage.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPBitmapChar is an image that contains one char of a bitmap font. Its properties contain all + the information that is needed to arrange the char in a text. + + _You don't have to use this class directly in most cases._ + +------------------------------------------------------------------------------------------------- */ + +@interface SPBitmapChar : SPImage +{ + @private + int mCharID; + float mXOffset; + float mYOffset; + float mXAdvance; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a char with a texture and his properties. +- (id)initWithID:(int)charID texture:(SPTexture *)texture + xOffset:(float)xOffset yOffset:(float)yOffset xAdvance:(float)xAdvance; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The unicode ID of the char. +@property (nonatomic, readonly) int charID; + +/// The number of pixels to move the char in x direction on character arrangement. +@property (nonatomic, readonly) float xOffset; + +/// The number of pixels to move the char in y direction on character arrangement. +@property (nonatomic, readonly) float yOffset; + +/// The number of pixels the cursor has to be moved to the right for the next char. +@property (nonatomic, readonly) float xAdvance; + +@end diff --git a/libs/sparrow/src/Classes/SPBitmapChar.m b/libs/sparrow/src/Classes/SPBitmapChar.m new file mode 100644 index 0000000..e3124c2 --- /dev/null +++ b/libs/sparrow/src/Classes/SPBitmapChar.m @@ -0,0 +1,55 @@ +// +// SPBitmapChar.m +// Sparrow +// +// Created by Daniel Sperl on 12.10.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPBitmapChar.h" +#import "SPTexture.h" + +@implementation SPBitmapChar + +@synthesize charID = mCharID; +@synthesize xOffset = mXOffset; +@synthesize yOffset = mYOffset; +@synthesize xAdvance = mXAdvance; + +- (id)initWithID:(int)charID texture:(SPTexture *)texture + xOffset:(float)xOffset yOffset:(float)yOffset xAdvance:(float)xAdvance; +{ + if (self = [super initWithTexture:texture]) + { + mCharID = charID; + mXOffset = xOffset; + mYOffset = yOffset; + mXAdvance = xAdvance; + } + return self; +} + +- (id)initWithTexture:(SPTexture *)texture +{ + return [self initWithID:0 texture:texture xOffset:0 yOffset:0 xAdvance:texture.width]; +} + +- (id)init +{ + [self release]; + return nil; +} + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone*)zone; +{ + return [[[self class] allocWithZone:zone] initWithID:mCharID texture:self.texture + xOffset:mXOffset yOffset:mYOffset + xAdvance:mXAdvance]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPBitmapFont.h b/libs/sparrow/src/Classes/SPBitmapFont.h new file mode 100644 index 0000000..402ef57 --- /dev/null +++ b/libs/sparrow/src/Classes/SPBitmapFont.h @@ -0,0 +1,94 @@ +// +// SPBitmapFont.h +// Sparrow +// +// Created by Daniel Sperl on 12.10.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPBitmapChar.h" +#import "SPTextField.h" +#import "SPMacros.h" + +@class SPTexture; +@class SPDisplayObject; + +/** ------------------------------------------------------------------------------------------------ + + The SPBitmapFont class parses bitmap font files and arranges the glyphs in the form of a text. + + The class parses the XML format as it is used in the AngelCode Bitmap Font Generator. This is what + the file format looks like: + + + + + + + + + + + + + + _You don't have to use this class directly in most cases. SPTextField contains methods that + handle bitmap fonts for you._ + +------------------------------------------------------------------------------------------------- */ + +#ifdef __IPHONE_4_0 +@interface SPBitmapFont : NSObject +#else +@interface SPBitmapFont : NSObject +#endif +{ + @private + SPTexture *mFontTexture; + NSMutableDictionary *mChars; + NSString *mName; + float mSize; + float mLineHeight; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a bitmap font by parsing an XML file and uses the specified texture. +- (id)initWithContentsOfFile:(NSString *)path texture:(SPTexture *)texture; + +/// Initializes a bitmap font by parsing an XML file and loads the texture that is specified there. +- (id)initWithContentsOfFile:(NSString *)path; + +/// ------------- +/// @name Methods +/// ------------- + +/// Creates a single bitmap char with a certain character ID. +- (SPBitmapChar *)charByID:(int)charID; + +/// Creates a display object that contains the given text by arranging individual chars. +- (SPDisplayObject *)createDisplayObjectWithWidth:(float)width height:(float)height + text:(NSString *)text fontSize:(float)size color:(uint)color + hAlign:(SPHAlign)hAlign vAlign:(SPVAlign)vAlign + border:(BOOL)border; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The name of the font as it was parsed from the font file. +@property (nonatomic, readonly) NSString *name; + +/// The native size of the font. +@property (nonatomic, readonly) float size; + +/// The height of one line in pixels. +@property (nonatomic, assign) float lineHeight; + +@end diff --git a/libs/sparrow/src/Classes/SPBitmapFont.m b/libs/sparrow/src/Classes/SPBitmapFont.m new file mode 100644 index 0000000..ea81f05 --- /dev/null +++ b/libs/sparrow/src/Classes/SPBitmapFont.m @@ -0,0 +1,289 @@ +// +// SPBitmapFont.m +// Sparrow +// +// Created by Daniel Sperl on 12.10.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPBitmapFont.h" +#import "SPBitmapChar.h" +#import "SPTexture.h" +#import "SPRectangle.h" +#import "SPSubTexture.h" +#import "SPDisplayObject.h" +#import "SPSprite.h" +#import "SPImage.h" +#import "SPTextField.h" +#import "SPStage.h" +#import "SPNSExtensions.h" +#import "SPCompiledSprite.h" + +#define CHAR_SPACE 32 +#define CHAR_TAB 9 +#define CHAR_NEWLINE 10 + +// --- private interface --------------------------------------------------------------------------- + +@interface SPBitmapFont () + +- (void)parseFontXml:(NSString*)path; + +@end + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPBitmapFont + +@synthesize name = mName; +@synthesize lineHeight = mLineHeight; +@synthesize size = mSize; + +- (id)initWithContentsOfFile:(NSString *)path texture:(SPTexture *)texture +{ + if (self = [super init]) + { + mName = [[NSString alloc] initWithString:@"unknown"]; + mLineHeight = mSize = SP_DEFAULT_FONT_SIZE; + mFontTexture = [texture retain]; + + [self parseFontXml:path]; + } + return self; +} + +- (id)initWithContentsOfFile:(NSString *)path +{ + return [self initWithContentsOfFile:path texture:nil]; +} + +- (id)init +{ + [self release]; + return nil; +} + +- (void)parseFontXml:(NSString*)path +{ + SP_CREATE_POOL(pool); + + [mChars release]; + mChars = [[NSMutableDictionary alloc] init]; + + if (!path) return; + + float scale = [SPStage contentScaleFactor]; + NSString *fullPath = [[NSBundle mainBundle] pathForResource:path withScaleFactor:scale]; + NSURL *xmlUrl = [NSURL fileURLWithPath:fullPath]; + NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlUrl]; + xmlParser.delegate = self; + BOOL success = [xmlParser parse]; + + SP_RELEASE_POOL(pool); + + if (!success) + [NSException raise:SP_EXC_FILE_INVALID + format:@"could not parse bitmap font xml %@. Error code: %d, domain: %@", + path, xmlParser.parserError.code, xmlParser.parserError.domain]; + + [xmlParser release]; +} + +- (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName + namespaceURI:(NSString*)namespaceURI + qualifiedName:(NSString*)qName + attributes:(NSDictionary*)attributeDict +{ + if ([elementName isEqualToString:@"char"]) + { + int charID = [[attributeDict valueForKey:@"id"] intValue]; + float scale = mFontTexture.scale; + + SPRectangle *region = [[SPRectangle alloc] init]; + region.x = [[attributeDict valueForKey:@"x"] floatValue] / scale; + region.y = [[attributeDict valueForKey:@"y"] floatValue] / scale; + region.width = [[attributeDict valueForKey:@"width"] floatValue] / scale; + region.height = [[attributeDict valueForKey:@"height"] floatValue] / scale; + SPSubTexture *texture = [[SPSubTexture alloc] initWithRegion:region ofTexture:mFontTexture]; + [region release]; + + float xOffset = [[attributeDict valueForKey:@"xoffset"] floatValue] / scale; + float yOffset = [[attributeDict valueForKey:@"yoffset"] floatValue] / scale; + float xAdvance = [[attributeDict valueForKey:@"xadvance"] floatValue] / scale; + + SPBitmapChar *bitmapChar = [[SPBitmapChar alloc] initWithID:charID texture:texture + xOffset:xOffset yOffset:yOffset + xAdvance:xAdvance]; + [texture release]; + + [mChars setObject:bitmapChar forKey:[NSNumber numberWithInt:charID]]; + [bitmapChar release]; + } + else if ([elementName isEqualToString:@"info"]) + { + [mName release]; + mName = [[attributeDict valueForKey:@"face"] copy]; + mSize = [[attributeDict valueForKey:@"size"] floatValue]; + } + else if ([elementName isEqualToString:@"common"]) + { + mLineHeight = [[attributeDict valueForKey:@"lineHeight"] floatValue]; + } + else if ([elementName isEqualToString:@"page"]) + { + int id = [[attributeDict valueForKey:@"id"] intValue]; + if (id != 0) [NSException raise:SP_EXC_FILE_INVALID + format:@"Bitmap fonts with multiple pages are not supported"]; + if (!mFontTexture) + { + NSString *filename = [attributeDict valueForKey:@"file"]; + mFontTexture = [[SPTexture alloc] initWithContentsOfFile:filename]; + + // update sizes, now that we know the scale setting + mSize /= mFontTexture.scale; + mLineHeight /= mFontTexture.scale; + } + } +} + +- (SPBitmapChar *)charByID:(int)charID +{ + SPBitmapChar *bitmapChar = (SPBitmapChar *)[mChars objectForKey:[NSNumber numberWithInt:charID]]; + return [[bitmapChar copy] autorelease]; +} + +- (SPDisplayObject *)createDisplayObjectWithWidth:(float)width height:(float)height + text:(NSString *)text fontSize:(float)size color:(uint)color + hAlign:(SPHAlign)hAlign vAlign:(SPVAlign)vAlign + border:(BOOL)border +{ + SPSprite *lineContainer = [SPSprite sprite]; + + if (size == SP_NATIVE_FONT_SIZE) size = mSize; + float scale = size / mSize; + lineContainer.scaleX = lineContainer.scaleY = scale; + float containerWidth = width / scale; + float containerHeight = height / scale; + + int lastWhiteSpace = -1; + float currentX = 0; + SPSprite *currentLine = [SPSprite sprite]; + + for (int i=0; i containerWidth) + { + // remove characters and add them again to next line + int numCharsToRemove = lastWhiteSpace == -1 ? 1 : i - lastWhiteSpace; + int removeIndex = currentLine.numChildren - numCharsToRemove; + + for (int i=0; i +#import "SPDisplayObjectContainer.h" + +@class SPTexture; +@class SPImage; +@class SPTextField; +@class SPSprite; + +#define SP_EVENT_TYPE_TRIGGERED @"triggered" + +/** ------------------------------------------------------------------------------------------------ + + An SPButton is a simple button composed of an image and, optionally, text. + + You can pass a texture for up- and downstate of the button. If you do not provide a down stage, + the button is simply scaled a little when it is touched. + + In addition, you can overlay a text on the button. To customize the text, almost the same options + as those of SPTextField are provided. In addition, you can move the text to a certain position + with the help of the `textBounds` property. + + To react on touches on a button, there is special event type: `SP_EVENT_TYPE_TRIGGERED`. Use + this event instead of normal touch events - that way, the button will behave just like standard + iOS interface buttons. + +------------------------------------------------------------------------------------------------- */ + +@interface SPButton : SPDisplayObjectContainer +{ + @private + SPTexture *mUpState; + SPTexture *mDownState; + + SPSprite *mContents; + SPImage *mBackground; + SPTextField *mTextField; + SPRectangle *mTextBounds; + + float mScaleWhenDown; + float mAlphaWhenDisabled; + BOOL mEnabled; + BOOL mIsDown; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a button with textures for up- and down-state. _Designated Initializer_. +- (id)initWithUpState:(SPTexture*)upState downState:(SPTexture*)downState; + +/// Initializes a button with an up state texture and text. +- (id)initWithUpState:(SPTexture*)upState text:(NSString*)text; + +/// Initializes a button only with an up state. +- (id)initWithUpState:(SPTexture*)upState; + +/// Factory method. ++ (SPButton*)buttonWithUpState:(SPTexture*)upState downState:(SPTexture*)downState; + +/// Factory method. ++ (SPButton*)buttonWithUpState:(SPTexture*)upState text:(NSString*)text; + +/// Factory method. ++ (SPButton*)buttonWithUpState:(SPTexture*)upState; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The scale factor of the button on touch. Per default, a button with a down state texture won't scale. +@property (nonatomic, assign) float scaleWhenDown; + +/// The alpha value of the button when it is disabled. +@property (nonatomic, assign) float alphaWhenDisabled; + +/// Indicates if the button can be triggered. +@property (nonatomic, assign) BOOL enabled; + +/// The text that is displayed on the button. +@property (nonatomic, copy) NSString *text; + +/// The name of the font displayed on the button. May be a system font or a registered bitmap font. +@property (nonatomic, copy) NSString *fontName; + +/// The size of the font. +@property (nonatomic, assign) float fontSize; + +/// The color of the font. +@property (nonatomic, assign) uint fontColor; + +/// The texture that is displayed when the button is not being touched. +@property (nonatomic, retain) SPTexture *upState; + +/// The texture that is displayed while the button is touched. +@property (nonatomic, retain) SPTexture *downState; + +/// The bounds of the textfield on the button. Allows moving the text to a custom position. +@property (nonatomic, copy) SPRectangle *textBounds; + +@end diff --git a/libs/sparrow/src/Classes/SPButton.m b/libs/sparrow/src/Classes/SPButton.m new file mode 100644 index 0000000..c2cb93a --- /dev/null +++ b/libs/sparrow/src/Classes/SPButton.m @@ -0,0 +1,261 @@ +// +// SPButton.m +// Sparrow +// +// Created by Daniel Sperl on 13.07.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPButton.h" +#import "SPTouchEvent.h" +#import "SPTexture.h" +#import "SPGLTexture.h" +#import "SPImage.h" +#import "SPStage.h" +#import "SPSprite.h" +#import "SPTextField.h" + +// --- private interface --------------------------------------------------------------------------- + +@interface SPButton() + +- (void)resetContents; +- (void)createTextField; + +@end + + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPButton + +@synthesize scaleWhenDown = mScaleWhenDown; +@synthesize alphaWhenDisabled = mAlphaWhenDisabled; +@synthesize enabled = mEnabled; +@synthesize upState = mUpState; +@synthesize downState = mDownState; +@synthesize textBounds = mTextBounds; + +#define MAX_DRAG_DIST 40 + +- (id)initWithUpState:(SPTexture*)upState downState:(SPTexture*)downState; +{ + if (self = [super init]) + { + mUpState = [upState retain]; + mDownState = [downState retain]; + mContents = [[SPSprite alloc] init]; + mBackground = [[SPImage alloc] initWithTexture:upState]; + mTextField = nil; + mScaleWhenDown = 1.0f; + mAlphaWhenDisabled = 0.5f; + mEnabled = YES; + mIsDown = NO; + mTextBounds = [[SPRectangle alloc] initWithX:0 y:0 width:mUpState.width height:mUpState.height]; + + [mContents addChild:mBackground]; + [self addChild:mContents]; + [self addEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH]; + } + return self; +} + +- (id)initWithUpState:(SPTexture*)upState text:(NSString*)text +{ + self = [self initWithUpState:upState]; + self.text = text; + return self; +} + +- (id)initWithUpState:(SPTexture*)upState; +{ + self = [self initWithUpState:upState downState:upState]; + mScaleWhenDown = 0.9f; + return self; +} + +- (id)init +{ + SPTexture *texture = [[[SPGLTexture alloc] init] autorelease]; + return [self initWithUpState:texture]; +} + +- (void)onTouch:(SPTouchEvent*)touchEvent +{ + if (!mEnabled) return; + SPTouch *touch = [[touchEvent touchesWithTarget:self] anyObject]; + + if (touch.phase == SPTouchPhaseBegan) + { + mBackground.texture = mDownState; + mContents.scaleX = mContents.scaleY = mScaleWhenDown; + mContents.x = (1.0f - mScaleWhenDown) / 2.0f * mDownState.width; + mContents.y = (1.0f - mScaleWhenDown) / 2.0f * mDownState.height; + mIsDown = YES; + } + else if (touch.phase == SPTouchPhaseMoved && mIsDown) + { + // reset button when user dragged to far away after pushing + SPRectangle *buttonRect = [self boundsInSpace:self.stage]; + if (touch.globalX < buttonRect.x - MAX_DRAG_DIST || + touch.globalY < buttonRect.y - MAX_DRAG_DIST || + touch.globalX > buttonRect.x + buttonRect.width + MAX_DRAG_DIST || + touch.globalY > buttonRect.y + buttonRect.height + MAX_DRAG_DIST) + { + [self resetContents]; + } + } + else if (touch.phase == SPTouchPhaseEnded && mIsDown) + { + [self resetContents]; + [self dispatchEvent:[SPEvent eventWithType:SP_EVENT_TYPE_TRIGGERED]]; + } + else if (touch.phase == SPTouchPhaseCancelled && mIsDown) + { + [self resetContents]; + } +} + +- (void)resetContents +{ + mIsDown = NO; + mBackground.texture = mUpState; + mContents.x = mContents.y = 0; + mContents.scaleX = mContents.scaleY = 1.0f; +} + +- (void)setEnabled:(BOOL)value +{ + mEnabled = value; + if (mEnabled) + { + mContents.alpha = 1.0f; + } + else + { + mContents.alpha = mAlphaWhenDisabled; + [self resetContents]; + } +} + +- (void)setUpState:(SPTexture*)upState +{ + if (upState != mUpState) + { + [mUpState release]; + mUpState = [upState retain]; + if (!mIsDown) mBackground.texture = upState; + } +} + +- (void)setDownState:(SPTexture*)downState +{ + if (downState != mDownState) + { + [mDownState release]; + mDownState = [downState retain]; + if (mIsDown) mBackground.texture = downState; + } +} + +- (void)createTextField +{ + if (!mTextField) + { + mTextField = [[SPTextField alloc] initWithWidth:100 height:100 text:@""]; + mTextField.vAlign = SPVAlignCenter; + mTextField.hAlign = SPHAlignCenter; + [mContents addChild:mTextField]; + } + + mTextField.width = mTextBounds.width; + mTextField.height = mTextBounds.height; + mTextField.x = mTextBounds.x; + mTextField.y = mTextBounds.y; +} + +- (NSString*)text +{ + if (mTextField) return mTextField.text; + else return @""; +} + +- (void)setText:(NSString*)value +{ + [self createTextField]; + mTextField.text = value; +} + +- (void)setTextBounds:(SPRectangle *)value +{ + mTextBounds = [value copy]; + [self createTextField]; +} + +- (NSString*)fontName +{ + if (mTextField) return mTextField.fontName; + else return SP_DEFAULT_FONT_NAME; +} + +- (void)setFontName:(NSString*)value +{ + [self createTextField]; + mTextField.fontName = value; +} + +- (float)fontSize +{ + if (mTextField) return mTextField.fontSize; + else return SP_DEFAULT_FONT_SIZE; +} + +- (void)setFontSize:(float)value +{ + [self createTextField]; + mTextField.fontSize = value; +} + +- (uint)fontColor +{ + if (mTextField) return mTextField.color; + else return SP_DEFAULT_FONT_COLOR; +} + +- (void)setFontColor:(uint)value +{ + [self createTextField]; + mTextField.color = value; +} + ++ (SPButton*)buttonWithUpState:(SPTexture*)upState downState:(SPTexture*)downState +{ + return [[[SPButton alloc] initWithUpState:upState downState:downState] autorelease]; +} + ++ (SPButton*)buttonWithUpState:(SPTexture*)upState text:(NSString*)text +{ + return [[[SPButton alloc] initWithUpState:upState text:text] autorelease]; +} + ++ (SPButton*)buttonWithUpState:(SPTexture*)upState +{ + return [[[SPButton alloc] initWithUpState:upState] autorelease]; +} + +- (void)dealloc +{ + [self removeEventListenersAtObject:self forType:SP_EVENT_TYPE_TOUCH]; + [mTextBounds release]; + [mUpState release]; + [mDownState release]; + [mBackground release]; + [mTextField release]; + [mContents release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPCompiledSprite.h b/libs/sparrow/src/Classes/SPCompiledSprite.h new file mode 100644 index 0000000..81bcce0 --- /dev/null +++ b/libs/sparrow/src/Classes/SPCompiledSprite.h @@ -0,0 +1,61 @@ +// +// SPCompiledContainer.h +// Sparrow +// +// Created by Daniel Sperl on 15.07.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPSprite.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPCompiledSprite allows you to optimize the rendering of static parts of your display list. + + It analyzes the tree of children attached to it and optimizes the OpenGL rendering calls in a + way that makes rendering them extremely fast. The downside is that you will no longe see any + changes in the properties of the childs (position, rotation, alpha, etc.). To update the object + after changes have happened, simply call `compile` again. + + With the exception of this peculiarity, a compiled sprite can be use just like any other sprite. + + SPCompiledSprite *sprite = [SPCompiledSprite sprite]; + [sprite addChild:object1]; + [sprite addChild:object2]; + + [sprite compile]; // this call is optional, it will be done on rendering automatically. + +------------------------------------------------------------------------------------------------- */ + +@interface SPCompiledSprite : SPSprite +{ + @private + NSArray *mTextureSwitches; + NSMutableData *mColorData; + uint *mCurrentColors; + BOOL mAlphaChanged; + + uint mIndexBuffer; + uint mVertexBuffer; + uint mColorBuffer; + uint mTexCoordBuffer; +} + +/// ------------- +/// @name Methods +/// ------------- + +/// Compiles the children of the sprite to optimize rendering. After compilation, no changes in +/// the children will show up. Call the method again to make changes visible. +/// +/// @return Returns `YES` if compilation was successful. On error, it will `NSLog` the problem. +- (BOOL)compile; + +/// Factory method. ++ (SPCompiledSprite *)sprite; + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPCompiledSprite.m b/libs/sparrow/src/Classes/SPCompiledSprite.m new file mode 100644 index 0000000..d400f74 --- /dev/null +++ b/libs/sparrow/src/Classes/SPCompiledSprite.m @@ -0,0 +1,436 @@ +// +// SPCompiledContainer.m +// Sparrow +// +// Created by Daniel Sperl on 15.07.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPCompiledSprite.h" +#import "SPDisplayObjectContainer.h" +#import "SPPoint.h" +#import "SPMatrix.h" +#import "SPImage.h" +#import "SPQuad.h" +#import "SPTexture.h" +#import "SPRenderSupport.h" +#import "SPMacros.h" + +#import +#import +#import + +// --- SPQuad extension ---------------------------------------------------------------------------- + +@interface SPQuad (SPCompiledSprite_Extension) + +- (void)copyVertexCoords:(float *)vertexCoords colors:(uint *)colors textureCoords:(float *)texCoords; + +@end + +@implementation SPQuad (SPCompiledSprite_Extension) + +- (void)copyVertexCoords:(float *)vertexCoords colors:(uint *)colors textureCoords:(float *)texCoords +{ + if (vertexCoords) memcpy(vertexCoords, mVertexCoords, 8 * sizeof(float)); + if (colors) memcpy(colors, mVertexColors, 4 * sizeof(uint)); +} + +@end + +// --- SPImage extension --------------------------------------------------------------------------- + +@implementation SPImage (SPCompiledSprite_Extension) + +- (void)copyVertexCoords:(float *)vertexCoords colors:(uint *)colors textureCoords:(float *)texCoords +{ + [super copyVertexCoords:vertexCoords colors:colors textureCoords:texCoords]; + if (texCoords) memcpy(texCoords, mTexCoords, 8 * sizeof(float)); +} + +@end + +// --- private interface --------------------------------------------------------------------------- + +@interface SPCompiledSprite () + +- (BOOL)processVerticesOfObject:(SPDisplayObject *)object withMatrices:(NSMutableArray *)matrices + vertexData:(NSMutableData *)vertexData buffer:(void *)buffer; +- (BOOL)processColorsOfObject:(SPDisplayObject *)object withAlpha:(float)alpha + colorData:(NSMutableData *)colorData buffer:(void *)buffer; +- (BOOL)processTexturesOfObject:(SPDisplayObject *)object withTextures:(NSMutableArray *)textures + texCoordData:(NSMutableData *)texCoordData buffer:(void *)buffer; +- (void)updateColorData; +- (void)deleteBuffers; + +@end + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPCompiledSprite + +- (id)init +{ + return [super init]; +} + ++ (SPCompiledSprite *)sprite +{ + return [[[SPCompiledSprite alloc] init] autorelease]; +} + +- (void)dealloc +{ + free(mCurrentColors); + [mTextureSwitches release]; + [mColorData release]; + [self deleteBuffers]; + [super dealloc]; +} + +- (BOOL)compile +{ + SP_CREATE_POOL(pool); + + [self deleteBuffers]; + [mTextureSwitches release]; + [mColorData release]; + + free(mCurrentColors); + mCurrentColors = nil; + + void *scratchBuffer = malloc(MAX(8 * sizeof(float), 4 * sizeof(uint))); + + NSMutableData *vertexData = [[NSMutableData alloc] init]; + NSMutableData *colorData = [[NSMutableData alloc] init]; + NSMutableData *texCoordData = [[NSMutableData alloc] init]; + + NSMutableArray *matrices = [[NSMutableArray alloc] initWithObjects: + [SPMatrix matrixWithIdentity], nil]; + NSMutableArray *textures = [[NSMutableArray alloc] initWithObjects: + [NSNull null], [NSNumber numberWithInt:0], nil]; + + BOOL success; + + do + { + // compilation is done with an alpha of 1.0f, to get unscaled color data + float originalAlpha = self.alpha; + self.alpha = 1.0f; + + success = [self processVerticesOfObject:self withMatrices:matrices + vertexData:vertexData buffer:scratchBuffer]; + if (!success) break; + + success = [self processColorsOfObject:self withAlpha:self.alpha + colorData:colorData buffer:scratchBuffer]; + if (!success) break; + + success = [self processTexturesOfObject:self withTextures:textures + texCoordData:texCoordData buffer:scratchBuffer]; + + self.alpha = originalAlpha; + + } while (NO); + + if (success) + { + glGenBuffers(4, &mIndexBuffer); + + int numVertices = [vertexData length] / sizeof(float) / 2; + int numQuads = numVertices / 4; + int indexBufferSize = numQuads * 6; // 4 + 2 for degenerate triangles + GLushort *indices = malloc(indexBufferSize * sizeof(GLushort)); + + int pos = 0; + for (int i=0; i> 24) / 255.0f * alpha; + *newColors = [SPRenderSupport convertColor:origColor alpha:vertexAlpha premultiplyAlpha:pma]; + ++origColors; + ++newColors; + } + } + + // update buffer + glBindBuffer(GL_ARRAY_BUFFER, mColorBuffer); + glBufferSubData(GL_ARRAY_BUFFER, 0, mColorData.length, mCurrentColors); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + mAlphaChanged = NO; +} + +- (void)deleteBuffers +{ + glDeleteBuffers(4, &mIndexBuffer); + mIndexBuffer = mVertexBuffer = mColorBuffer = mTexCoordBuffer = 0; +} + +@end diff --git a/libs/sparrow/src/Classes/SPDelayedInvocation.h b/libs/sparrow/src/Classes/SPDelayedInvocation.h new file mode 100644 index 0000000..9dbea9f --- /dev/null +++ b/libs/sparrow/src/Classes/SPDelayedInvocation.h @@ -0,0 +1,58 @@ +// +// SPDelayedInvocation.h +// Sparrow +// +// Created by Daniel Sperl on 11.07.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPAnimatable.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPDelayedInvocation is a proxy object that will forward any methods that are called on it + to a certain target - but only after a certain time has passed. + + The easiest way to delay an invocation is by calling [SPJuggler delayInvocationAtTarget:byTime:]. + This method will create a delayed invocation for you, adding it to the juggler right away. + +------------------------------------------------------------------------------------------------- */ + + +@interface SPDelayedInvocation : NSObject +{ + @private + id mTarget; + NSMutableSet *mInvocations; + double mTotalTime; + double mCurrentTime; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a delayed invocation. +- (id)initWithTarget:(id)target delay:(double)time; + +/// Factory method. ++ (SPDelayedInvocation*)invocationWithTarget:(id)target delay:(double)time; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The target object to which messages will be forwarded. +@property (nonatomic, readonly) id target; + +/// The time messages will be delayed (in seconds). +@property (nonatomic, readonly) double totalTime; + +/// The time that has already passed (in seconds). +@property (nonatomic, assign) double currentTime; + +@end diff --git a/libs/sparrow/src/Classes/SPDelayedInvocation.m b/libs/sparrow/src/Classes/SPDelayedInvocation.m new file mode 100644 index 0000000..ba8e762 --- /dev/null +++ b/libs/sparrow/src/Classes/SPDelayedInvocation.m @@ -0,0 +1,93 @@ +// +// SPDelayedInvocation.m +// Sparrow +// +// Created by Daniel Sperl on 11.07.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPDelayedInvocation.h" + + +@implementation SPDelayedInvocation + +@synthesize totalTime = mTotalTime; +@synthesize currentTime = mCurrentTime; +@synthesize target = mTarget; + +- (id)initWithTarget:(id)target delay:(double)time +{ + if (!target) + { + [self release]; + return nil; + } + + if (self = [super init]) + { + mTotalTime = MAX(0.0001, time); // zero is not allowed + mCurrentTime = 0; + mTarget = [target retain]; + mInvocations = [[NSMutableSet alloc] init]; + } + return self; +} + +- (id)init +{ + [self release]; + return nil; +} + +- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector +{ + NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:aSelector]; + if (!sig) sig = [mTarget methodSignatureForSelector:aSelector]; + return sig; +} + +- (void)forwardInvocation:(NSInvocation*)anInvocation +{ + if ([mTarget respondsToSelector:[anInvocation selector]]) + { + anInvocation.target = mTarget; + [anInvocation retainArguments]; + [mInvocations addObject:anInvocation]; + } +} + +- (void)advanceTime:(double)seconds +{ + self.currentTime = mCurrentTime + seconds; +} + +- (void)setCurrentTime:(double)currentTime +{ + double previousTime = mCurrentTime; + mCurrentTime = MIN(mTotalTime, currentTime); + + if (previousTime < mTotalTime && mCurrentTime >= mTotalTime) + [mInvocations makeObjectsPerformSelector:@selector(invoke)]; +} + +- (BOOL)isComplete +{ + return mCurrentTime >= mTotalTime; +} + ++ (SPDelayedInvocation*)invocationWithTarget:(id)target delay:(double)time +{ + return [[[SPDelayedInvocation alloc] initWithTarget:target delay:time] autorelease]; +} + +- (void)dealloc +{ + [mTarget release]; + [mInvocations release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPDisplayObject.h b/libs/sparrow/src/Classes/SPDisplayObject.h new file mode 100644 index 0000000..2e85055 --- /dev/null +++ b/libs/sparrow/src/Classes/SPDisplayObject.h @@ -0,0 +1,172 @@ +// +// SPDisplayObject.h +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPEventDispatcher.h" +#import "SPRectangle.h" +#import "SPMatrix.h" + +@class SPDisplayObjectContainer; +@class SPStage; +@class SPRenderSupport; + +/** ------------------------------------------------------------------------------------------------ + + The SPDisplayObject class is the base class for all objects that are rendered on the screen. + + In Sparrow, all displayable objects are organized in a display tree. Only objects that are part of + the display tree will be displayed (rendered). + + The display tree consists of leaf nodes (SPImage, SPQuad) that will be rendered directly to + the screen, and of container nodes (subclasses of SPDisplayObjectContainer, like SPSprite). + A container is simply a display object that has child nodes - which can, again, be either leaf + nodes or other containers. + + At the root of the display tree, there is the SPStage, which is a container, too. To create a + Sparrow application, you let your main class inherit from SPStage, and build up your display + tree from there. + + A display object has properties that define its position in relation to its parent + (`x`, `y`), as well as its rotation and scaling factors (`scaleX`, `scaleY`). Use the `alpha` and + `visible` properties to make an object translucent or invisible. + + Every display object may be the target of touch events. If you don't want an object to be + touchable, you can disable the `touchable` property. When it's disabled, neither the object + nor its childs will receive any more touch events. + + *Points vs. Pixels* + + All sizes and distances are measured in points. What this means in pixels depends on the + contentScaleFactor of the stage. On a low resolution device (up to iPhone 3GS), one point is one + pixel. On devices with a retina display, one point may be 2 pixels. + + *Transforming coordinates* + + Within the display tree, each object has its own local coordinate system. If you rotate a container, + you rotate that coordinate system - and thus all the children of the container. + + Sometimes you need to know where a certain point lies relative to another coordinate system. + That's the purpose of the method `transformationMatrixToSpace:`. It will create a matrix that + represents the transformation of a point in one coordinate system to another. + + *Subclassing SPDisplayObject* + + As SPDisplayObject is an abstract class, you can't instantiate it directly, but have to use one of + its subclasses instead. There are already a lot of them available, and most of the time they will + suffice. + + However, you can create custom subclasses as well. That's especially useful when you want to + create an object with a custom render function. + + You will need to implement the following methods when you subclass SPDisplayObject: + + - (void)render:(SPRenderSupport*)support; + - (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetCoordinateSpace; + + Have a look at SPQuad for a sample implementation of those methods. + +------------------------------------------------------------------------------------------------- */ + +@interface SPDisplayObject : SPEventDispatcher +{ + @private + float mX; + float mY; + float mScaleX; + float mScaleY; + float mRotationZ; + float mAlpha; + BOOL mVisible; + BOOL mTouchable; + + SPDisplayObjectContainer *mParent; + double mLastTouchTimestamp; + NSString *mName; +} + +/// ------------- +/// @name Methods +/// ------------- + +/// Renders the display object with the help of a support object. +- (void)render:(SPRenderSupport*)support; + +/// Removes the object from its parent, if it has one. +- (void)removeFromParent; + +/// Creates a matrix that represents the transformation from the local coordinate system to another. +- (SPMatrix*)transformationMatrixToSpace:(SPDisplayObject*)targetCoordinateSpace; + +/// Returns a rectangle that completely encloses the object as it appears in another coordinate system. +- (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetCoordinateSpace; + +/// Transforms a point from the local coordinate system to global (stage) coordinates. +- (SPPoint*)localToGlobal:(SPPoint*)localPoint; + +/// Transforms a point from global (stage) coordinates to the local coordinate system. +- (SPPoint*)globalToLocal:(SPPoint*)globalPoint; + +/// Returns the object that is found topmost on a point in local coordinates, or nil if the test fails. +- (SPDisplayObject*)hitTestPoint:(SPPoint*)localPoint forTouch:(BOOL)isTouch; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The x coordinates of the object relative to the local coordinates of the parent. +@property (nonatomic, assign) float x; + +/// The y coordinates of the object relative to the local coordinates of the parent. +@property (nonatomic, assign) float y; + +/// The horizontal scale factor. "1" means no scale, negative values flip the object. +@property (nonatomic, assign) float scaleX; + +/// The vertical scale factor. "1" means no scale, negative values flip the object. +@property (nonatomic, assign) float scaleY; + +/// The width of the object in points. +@property (nonatomic, assign) float width; + +/// The height of the object in points. +@property (nonatomic, assign) float height; + +/// The rotation of the object in radians. (In Sparrow, all angles are measured in radians.) +@property (nonatomic, assign) float rotation; + +/// The opacity of the object. 0 = transparent, 1 = opaque. +@property (nonatomic, assign) float alpha; + +/// The visibility of the object. An invisible object will be untouchable. +@property (nonatomic, assign) BOOL visible; + +/// Indicates if this object (and its children) will receive touch events. +@property (nonatomic, assign) BOOL touchable; + +/// The bounds of the object relative to the local coordinates of the parent. +@property (nonatomic, readonly) SPRectangle *bounds; + +/// The display object container that contains this display object. +@property (nonatomic, readonly) SPDisplayObjectContainer *parent; + +/// The topmost object in the display tree the object is part of. +@property (nonatomic, readonly) SPDisplayObject *root; + +/// The stage the display object is connected to, or nil if it is not connected to a stage. +@property (nonatomic, readonly) SPStage *stage; + +/// The transformation matrix of the object relative to its parent. +@property (nonatomic, readonly) SPMatrix *transformationMatrix; + +/// The name of the display object (default: nil). Used by `childByName:` of display object containers. +@property (nonatomic, copy) NSString *name; + +@end diff --git a/libs/sparrow/src/Classes/SPDisplayObject.m b/libs/sparrow/src/Classes/SPDisplayObject.m new file mode 100644 index 0000000..6eab8f6 --- /dev/null +++ b/libs/sparrow/src/Classes/SPDisplayObject.m @@ -0,0 +1,317 @@ +// +// SPDisplayObject.m +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPDisplayObject.h" +#import "SPDisplayObject_Internal.h" +#import "SPDisplayObjectContainer.h" +#import "SPStage.h" +#import "SPMacros.h" +#import "SPTouchEvent.h" + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPDisplayObject + +@synthesize x = mX; +@synthesize y = mY; +@synthesize scaleX = mScaleX; +@synthesize scaleY = mScaleY; +@synthesize rotation = mRotationZ; +@synthesize parent = mParent; +@synthesize alpha = mAlpha; +@synthesize visible = mVisible; +@synthesize touchable = mTouchable; +@synthesize name = mName; + +- (id)init +{ + #ifdef DEBUG + if ([self isMemberOfClass:[SPDisplayObject class]]) + { + [self release]; + [NSException raise:SP_EXC_ABSTRACT_CLASS + format:@"Attempting to initialize abstract class SPDisplayObject."]; + return nil; + } + #endif + + if (self = [super init]) + { + mAlpha = 1.0f; + mScaleX = 1.0f; + mScaleY = 1.0f; + mVisible = YES; + mTouchable = YES; + } + return self; +} + +- (void)dealloc +{ + [mName release]; + [super dealloc]; +} + +- (void)render:(SPRenderSupport*)support +{ + // override in subclass +} + +- (void)removeFromParent +{ + [mParent removeChild:self]; +} + +- (SPMatrix*)transformationMatrixToSpace:(SPDisplayObject*)targetCoordinateSpace +{ + if (targetCoordinateSpace == self) + { + return [SPMatrix matrixWithIdentity]; + } + else if (!targetCoordinateSpace) + { + // targetCoordinateSpace 'nil' represents the target coordinate of the root object. + // -> move up from self to root + SPMatrix *selfMatrix = [[SPMatrix alloc] init]; + SPDisplayObject *currentObject = self; + while (currentObject) + { + [selfMatrix concatMatrix:currentObject.transformationMatrix]; + currentObject = currentObject->mParent; + } + return [selfMatrix autorelease]; + } + else if (targetCoordinateSpace->mParent == self) // optimization + { + SPMatrix *targetMatrix = targetCoordinateSpace.transformationMatrix; + [targetMatrix invert]; + return targetMatrix; + } + else if (mParent == targetCoordinateSpace) // optimization + { + return self.transformationMatrix; + } + + // 1.: Find a common parent of self and the target coordinate space. + // + // This method is used very often during touch testing, so we optimized the code. + // Instead of using an NSSet or NSArray (which would make the code much cleaner), we + // use a C array here to save the ancestors. + + static SPDisplayObject *ancestors[SP_MAX_DISPLAY_TREE_DEPTH]; + + int count = 0; + SPDisplayObject *commonParent = nil; + SPDisplayObject *currentObject = self; + while (currentObject && count < SP_MAX_DISPLAY_TREE_DEPTH) + { + ancestors[count++] = currentObject; + currentObject = currentObject->mParent; + } + + currentObject = targetCoordinateSpace; + while (currentObject && !commonParent) + { + for (int i=0; imParent; + } + + if (!commonParent) + [NSException raise:SP_EXC_NOT_RELATED format:@"Object not connected to target"]; + + // 2.: Move up from self to common parent + SPMatrix *selfMatrix = [[SPMatrix alloc] init]; + currentObject = self; + while (currentObject != commonParent) + { + [selfMatrix concatMatrix:currentObject.transformationMatrix]; + currentObject = currentObject->mParent; + } + + // 3.: Now move up from target until we reach the common parent + SPMatrix *targetMatrix = [[SPMatrix alloc] init]; + currentObject = targetCoordinateSpace; + while (currentObject && currentObject != commonParent) + { + [targetMatrix concatMatrix:currentObject.transformationMatrix]; + currentObject = currentObject->mParent; + } + + // 4.: Combine the two matrices + [targetMatrix invert]; + [selfMatrix concatMatrix:targetMatrix]; + [targetMatrix release]; + + return [selfMatrix autorelease]; +} + +- (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetCoordinateSpace +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD + format:@"Method needs to be implemented in subclass"]; + return nil; +} + +- (SPRectangle*)bounds +{ + return [self boundsInSpace:mParent]; +} + +- (SPDisplayObject*)hitTestPoint:(SPPoint*)localPoint forTouch:(BOOL)isTouch; +{ + // on a touch test, invisible or untouchable objects cause the test to fail + if (isTouch && (!mVisible || !mTouchable)) return nil; + + // otherwise, check bounding box + if ([[self boundsInSpace:self] containsPoint:localPoint]) return self; + else return nil; +} + +- (SPPoint*)localToGlobal:(SPPoint*)localPoint +{ + // move up until parent is nil + SPMatrix *transformationMatrix = [[SPMatrix alloc] init]; + SPDisplayObject *currentObject = self; + while (currentObject) + { + [transformationMatrix concatMatrix:currentObject.transformationMatrix]; + currentObject = [currentObject parent]; + } + + SPPoint *globalPoint = [transformationMatrix transformPoint:localPoint]; + [transformationMatrix release]; + return globalPoint; +} + +- (SPPoint*)globalToLocal:(SPPoint*)globalPoint +{ + // move up until parent is nil, then invert matrix + SPMatrix *transformationMatrix = [[SPMatrix alloc] init]; + SPDisplayObject *currentObject = self; + while (currentObject) + { + [transformationMatrix concatMatrix:currentObject.transformationMatrix]; + currentObject = [currentObject parent]; + } + + [transformationMatrix invert]; + SPPoint *localPoint = [transformationMatrix transformPoint:globalPoint]; + [transformationMatrix release]; + return localPoint; +} + +- (void)dispatchEvent:(SPEvent*)event +{ + // on one given moment, there is only one set of touches -- thus, + // we process only one touch event with a certain timestamp + if ([event isKindOfClass:[SPTouchEvent class]]) + { + SPTouchEvent *touchEvent = (SPTouchEvent*)event; + if (touchEvent.timestamp == mLastTouchTimestamp) return; + else mLastTouchTimestamp = touchEvent.timestamp; + } + + [super dispatchEvent:event]; +} + +- (float)width +{ + return [self boundsInSpace:mParent].width; +} + +- (void)setWidth:(float)value +{ + // this method calls 'self.scaleX' instead of changing mScaleX directly. + // that way, subclasses reacting on size changes need to override only the scaleX method. + + mScaleX = 1.0f; + float actualWidth = self.width; + if (actualWidth != 0.0f) self.scaleX = value / actualWidth; + else self.scaleX = 1.0f; +} + +- (float)height +{ + return [self boundsInSpace:mParent].height; +} + +- (void)setHeight:(float)value +{ + mScaleY = 1.0f; + float actualHeight = self.height; + if (actualHeight != 0.0f) self.scaleY = value / actualHeight; + else self.scaleY = 1.0f; +} + +- (void)setRotation:(float)value +{ + // clamp between [-180 deg, +180 deg] + while (value < -PI) value += TWO_PI; + while (value > PI) value -= TWO_PI; + mRotationZ = value; +} + +- (void)setAlpha:(float)value +{ + mAlpha = MAX(0.0f, MIN(1.0f, value)); +} + +- (SPDisplayObject*)root +{ + SPDisplayObject *currentObject = self; + while (currentObject->mParent) + currentObject = currentObject->mParent; + return currentObject; +} + +- (SPStage*)stage +{ + SPDisplayObject *root = self.root; + if ([root isKindOfClass:[SPStage class]]) return (SPStage*) root; + else return nil; +} + +- (SPMatrix*)transformationMatrix +{ + SPMatrix *matrix = [[SPMatrix alloc] init]; + + if (mScaleX != 1.0f || mScaleY != 1.0f) [matrix scaleXBy:mScaleX yBy:mScaleY]; + if (mRotationZ != 0.0f) [matrix rotateBy:mRotationZ]; + if (mX != 0.0f || mY != 0.0f) [matrix translateXBy:mX yBy:mY]; + + return [matrix autorelease]; +} + +@end + +// ------------------------------------------------------------------------------------------------- + +@implementation SPDisplayObject (Internal) + +- (void)setParent:(SPDisplayObjectContainer*)parent +{ + // only assigned, not retained -- otherwise, we would create a circular reference. + mParent = parent; +} + +- (void)dispatchEventOnChildren:(SPEvent *)event +{ + [self dispatchEvent:event]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPDisplayObjectContainer.h b/libs/sparrow/src/Classes/SPDisplayObjectContainer.h new file mode 100644 index 0000000..4fc405f --- /dev/null +++ b/libs/sparrow/src/Classes/SPDisplayObjectContainer.h @@ -0,0 +1,100 @@ +// +// SPDisplayObjectContainer.h +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPDisplayObject.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPDisplayObjectContainer represents a collection of display objects. + + It is the base class of all display objects that act as a container for other objects. By + maintaining an ordered list of children, it defines the back-to-front positioning of the children + within the display tree. + + A container does not have size in itself. The width and height properties represent the extents + of its children. Changing those properties will scale all children accordingly. + + As this is an abstract class, you can't instantiate it directly, but have to + use a subclass instead. The most lightweight container class is SPSprite. + + *Adding and removing children* + + The class defines methods that allow you to add or remove children. When you add a child, it will + be added at the foremost position, possibly occluding a child that was added before. You can access + the children via an index. The first child will have index 0, the second child index 1, etc. + + Adding and removing objects from a container triggers non-bubbling events. + + * `SP_EVENT_TYPE_ADDED`: the object was added to a parent. + * `SP_EVENT_TYPE_ADDED_TO_STAGE`: the object was added to a parent that is connected to the stage, + thus becoming visible now. + * `SP_EVENT_TYPE_REMOVED`: the object was removed from a parent. + * `SP_EVENT_TYPE_REMOVED_FROM_STAGE`: the object was removed from a parent that is connected to + the stage, thus becoming invisible now. + + Especially the `ADDED_TO_STAGE` event is very helpful, as it allows you to automatically execute + some logic (e.g. start an animation) when an object is rendered the first time. + +------------------------------------------------------------------------------------------------- */ + +@interface SPDisplayObjectContainer : SPDisplayObject +{ + @private + NSMutableArray *mChildren; +} + +/// ------------- +/// @name Methods +/// ------------- + +/// Adds a child to the container. It will be at the topmost position. +- (void)addChild:(SPDisplayObject *)child; + +/// Adds a child to the container at a certain index. +- (void)addChild:(SPDisplayObject *)child atIndex:(int)index; + +/// Determines if a certain object is a child of the container (recursively). +- (BOOL)containsChild:(SPDisplayObject *)child; + +/// Returns a child object at a certain index. +- (SPDisplayObject *)childAtIndex:(int)index; + +/// Returns a child object with a certain name (non-recursively). +- (SPDisplayObject *)childByName:(NSString *)name; + +/// Returns the index of a child within the container. +- (int)childIndex:(SPDisplayObject *)child; + +/// Removes a child from the container. If the object is not a child, nothing happens. +- (void)removeChild:(SPDisplayObject *)child; + +/// Removes a child at a certain index. Children above the child will move down. +- (void)removeChildAtIndex:(int)index; + +/// Removes all children from the container. +- (void)removeAllChildren; + +/// Swaps the indexes of two children. +- (void)swapChild:(SPDisplayObject*)child1 withChild:(SPDisplayObject*)child2; + +/// Swaps the indexes of two children. +- (void)swapChildAtIndex:(int)index1 withChildAtIndex:(int)index2; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The number of children of this container. +@property (nonatomic, readonly) int numChildren; + + +@end diff --git a/libs/sparrow/src/Classes/SPDisplayObjectContainer.m b/libs/sparrow/src/Classes/SPDisplayObjectContainer.m new file mode 100644 index 0000000..f9420c1 --- /dev/null +++ b/libs/sparrow/src/Classes/SPDisplayObjectContainer.m @@ -0,0 +1,250 @@ +// +// SPDisplayObjectContainer.m +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPDisplayObjectContainer.h" +#import "SPEnterFrameEvent.h" +#import "SPDisplayObject_Internal.h" +#import "SPMacros.h" + +// --- C functions --------------------------------------------------------------------------------- + +static void getChildEventListeners(SPDisplayObject *object, NSString *eventType, + NSMutableArray *listeners) +{ + // some events (ENTER_FRAME, ADDED_TO_STAGE, etc.) are dispatched very often and traverse + // the entire display tree -- thus, it pays off handling them in their own c function. + + if ([object hasEventListenerForType:eventType]) + [listeners addObject:object]; + + if ([object isKindOfClass:[SPDisplayObjectContainer class]]) + for (SPDisplayObject *child in (SPDisplayObjectContainer *)object) + getChildEventListeners(child, eventType, listeners); +} + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPDisplayObjectContainer + +- (id)init +{ + #if DEBUG + if ([[self class] isEqual:[SPDisplayObjectContainer class]]) + { + [NSException raise:SP_EXC_ABSTRACT_CLASS + format:@"Attempting to instantiate SPDisplayObjectContainer directly."]; + [self release]; + return nil; + } + #endif + + if (self = [super init]) + { + mChildren = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)addChild:(SPDisplayObject *)child +{ + [self addChild:child atIndex:[mChildren count]]; +} + +- (void)addChild:(SPDisplayObject *)child atIndex:(int)index +{ + if (index >= 0 && index <= [mChildren count]) + { + [child retain]; + [child removeFromParent]; + [mChildren insertObject:child atIndex:MIN(mChildren.count, index)]; + child.parent = self; + + SPEvent *addedEvent = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_ADDED]; + [child dispatchEvent:addedEvent]; + [addedEvent release]; + + if (self.stage) + { + SPEvent *addedToStageEvent = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_ADDED_TO_STAGE]; + [child dispatchEventOnChildren:addedToStageEvent]; + [addedToStageEvent release]; + } + + [child release]; + } + else [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"Invalid child index"]; +} + +- (BOOL)containsChild:(SPDisplayObject *)child +{ + if ([self isEqual:child]) return YES; + + for (SPDisplayObject *currentChild in mChildren) + { + if ([currentChild isKindOfClass:[SPDisplayObjectContainer class]]) + { + if ([(SPDisplayObjectContainer *)currentChild containsChild:child]) return YES; + } + else + { + if (currentChild == child) return YES; + } + } + + return NO; +} + +- (SPDisplayObject *)childAtIndex:(int)index +{ + return [mChildren objectAtIndex:index]; +} + +- (SPDisplayObject *)childByName:(NSString *)name +{ + for (SPDisplayObject *currentChild in mChildren) + if ([currentChild.name isEqualToString:name]) return currentChild; + + return nil; +} + +- (int)childIndex:(SPDisplayObject *)child +{ + int index = [mChildren indexOfObject:child]; + if (index == NSNotFound) return SP_NOT_FOUND; + else return index; +} + +- (void)removeChild:(SPDisplayObject *)child +{ + int childIndex = [self childIndex:child]; + if (childIndex != SP_NOT_FOUND) + [self removeChildAtIndex:childIndex]; +} + +- (void)removeChildAtIndex:(int)index +{ + if (index >= 0 && index < [mChildren count]) + { + SPDisplayObject *child = [[mChildren objectAtIndex:index] retain]; + + SPEvent *remEvent = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_REMOVED]; + [child dispatchEvent:remEvent]; + [remEvent release]; + + if (self.stage) + { + SPEvent *remFromStageEvent = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_REMOVED_FROM_STAGE]; + [child dispatchEventOnChildren:remFromStageEvent]; + [remFromStageEvent release]; + } + + [mChildren removeObjectAtIndex:index]; + child.parent = nil; + + [child release]; + } + else [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"Invalid child index"]; +} + +- (void)swapChild:(SPDisplayObject*)child1 withChild:(SPDisplayObject*)child2 +{ + int index1 = [self childIndex:child1]; + int index2 = [self childIndex:child2]; + [self swapChildAtIndex:index1 withChildAtIndex:index2]; +} + +- (void)swapChildAtIndex:(int)index1 withChildAtIndex:(int)index2 +{ + int numChildren = [mChildren count]; + if (index1 < 0 || index1 >= numChildren || index2 < 0 || index2 >= numChildren) + [NSException raise:SP_EXC_INVALID_OPERATION format:@"invalid child indices"]; + [mChildren exchangeObjectAtIndex:index1 withObjectAtIndex:index2]; +} + +- (void)removeAllChildren +{ + for (int i=mChildren.count-1; i>=0; --i) + [self removeChildAtIndex:i]; +} + +- (int)numChildren +{ + return [mChildren count]; +} + +- (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetCoordinateSpace +{ + int numChildren = [mChildren count]; + + if (numChildren == 0) + return [SPRectangle rectangleWithX:0 y:0 width:0 height:0]; + else if (numChildren == 1) + return [[mChildren objectAtIndex:0] boundsInSpace:targetCoordinateSpace]; + else + { + float minX = FLT_MAX, maxX = -FLT_MAX, minY = FLT_MAX, maxY = -FLT_MAX; + for (SPDisplayObject *child in mChildren) + { + SPRectangle *childBounds = [child boundsInSpace:targetCoordinateSpace]; + minX = MIN(minX, childBounds.x); + maxX = MAX(maxX, childBounds.x + childBounds.width); + minY = MIN(minY, childBounds.y); + maxY = MAX(maxY, childBounds.y + childBounds.height); + } + return [SPRectangle rectangleWithX:minX y:minY width:maxX-minX height:maxY-minY]; + } +} + +- (SPDisplayObject*)hitTestPoint:(SPPoint*)localPoint forTouch:(BOOL)isTouch; +{ + if (isTouch && (!self.visible || !self.touchable)) + return nil; + + for (int i=[mChildren count]-1; i>=0; --i) // front to back! + { + SPDisplayObject *child = [mChildren objectAtIndex:i]; + SPMatrix *transformationMatrix = [self transformationMatrixToSpace:child]; + SPPoint *transformedPoint = [transformationMatrix transformPoint:localPoint]; + SPDisplayObject *target = [child hitTestPoint:transformedPoint forTouch:isTouch]; + if (target) return target; + } + + return nil; +} + +- (void)dispatchEventOnChildren:(SPEvent *)event +{ + // the event listeners might modify the display tree, which could make the loop crash. + // thus, we collect them in a list and iterate over that list instead. + + NSMutableArray *listeners = [[NSMutableArray alloc] init]; + getChildEventListeners(self, event.type, listeners); + [listeners makeObjectsPerformSelector:@selector(dispatchEvent:) withObject:event]; + [listeners release]; +} + +- (void)dealloc +{ + // 'self' is becoming invalid; thus, we have to remove any references to it. + [mChildren makeObjectsPerformSelector:@selector(setParent:) withObject:nil]; + [mChildren release]; + [super dealloc]; +} + +#pragma mark NSFastEnumeration + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf + count:(NSUInteger)len +{ + return [mChildren countByEnumeratingWithState:state objects:stackbuf count:len]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPDisplayObject_Internal.h b/libs/sparrow/src/Classes/SPDisplayObject_Internal.h new file mode 100644 index 0000000..6ee335b --- /dev/null +++ b/libs/sparrow/src/Classes/SPDisplayObject_Internal.h @@ -0,0 +1,20 @@ +// +// SPDisplayObject_Internal.h +// Sparrow +// +// Created by Daniel Sperl on 03.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPDisplayObject.h" + +@interface SPDisplayObject (Internal) + +- (void)setParent:(SPDisplayObjectContainer*)parent; +- (void)dispatchEventOnChildren:(SPEvent *)event; + +@end diff --git a/libs/sparrow/src/Classes/SPEnterFrameEvent.h b/libs/sparrow/src/Classes/SPEnterFrameEvent.h new file mode 100644 index 0000000..535bc86 --- /dev/null +++ b/libs/sparrow/src/Classes/SPEnterFrameEvent.h @@ -0,0 +1,54 @@ +// +// SPEnterFrameEvent.h +// Sparrow +// +// Created by Daniel Sperl on 30.04.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPEvent.h" + +#define SP_EVENT_TYPE_ENTER_FRAME @"enterFrame" + +/** ------------------------------------------------------------------------------------------------ + + An SPEnterFrameEvent is triggered once per frame and is dispatched to all objects in the + display tree. + + It contains information about the time that has passed since the last frame. That way, you + can easily make animations that are independet of the frame rate, but take the passed time + into account. + +------------------------------------------------------------------------------------------------- */ + +@interface SPEnterFrameEvent : SPEvent +{ + @private + double mPassedTime; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes an enter frame event with the passed time. _Designated Initializer_. +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles passedTime:(double)seconds; + +/// Initializes an enter frame event that does not bubble (recommended). +- (id)initWithType:(NSString*)type passedTime:(double)seconds; + +/// Factory method. ++ (SPEnterFrameEvent*)eventWithType:(NSString*)type passedTime:(double)seconds; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The time that has passed since the last frame (in seconds). +@property (nonatomic, readonly) double passedTime; + +@end diff --git a/libs/sparrow/src/Classes/SPEnterFrameEvent.m b/libs/sparrow/src/Classes/SPEnterFrameEvent.m new file mode 100644 index 0000000..50aa213 --- /dev/null +++ b/libs/sparrow/src/Classes/SPEnterFrameEvent.m @@ -0,0 +1,48 @@ +// +// SPEnterFrameEvent.m +// Sparrow +// +// Created by Daniel Sperl on 30.04.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPEnterFrameEvent.h" + + +@implementation SPEnterFrameEvent + +@synthesize passedTime = mPassedTime; + +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles passedTime:(double)seconds +{ + if (self = [super initWithType:type bubbles:bubbles]) + { + mPassedTime = seconds; + } + return self; +} + +- (id)initWithType:(NSString*)type passedTime:(double)seconds; +{ + return [self initWithType:type bubbles:NO passedTime:seconds]; +} + +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles +{ + return [self initWithType:type bubbles:bubbles passedTime:0.0f]; +} + ++ (SPEnterFrameEvent*)eventWithType:(NSString*)type passedTime:(double)seconds +{ + return [[[SPEnterFrameEvent alloc] initWithType:type passedTime:seconds] autorelease]; +} + +- (void)dealloc +{ + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPEvent.h b/libs/sparrow/src/Classes/SPEvent.h new file mode 100644 index 0000000..a4c2a90 --- /dev/null +++ b/libs/sparrow/src/Classes/SPEvent.h @@ -0,0 +1,93 @@ +// +// SPEvent.h +// Sparrow +// +// Created by Daniel Sperl on 27.04.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +#define SP_EVENT_TYPE_ADDED @"added" +#define SP_EVENT_TYPE_ADDED_TO_STAGE @"addedToStage" +#define SP_EVENT_TYPE_REMOVED @"removed" +#define SP_EVENT_TYPE_REMOVED_FROM_STAGE @"removedFromStage" + +@class SPEventDispatcher; + +/** ------------------------------------------------------------------------------------------------ + + The SPEvent class contains data that describes an event. + + `SPEventDispatcher`s create instances of this class and send them to registered listeners. An event + contains information that characterizes an event, most importantly the event type and if the event + bubbles. The target of an event is the object that dispatched it. + + For some event types, this information is sufficient; other events may need additional information + to be carried to the listener. + In that case, you can subclass SPEvent and add properties with all the information you require. + The SPEnterFrameEvent is an example for this practice; it adds a property about the time that + has passed since the last frame. + + Furthermore, the event class contains methods that can stop the event from being processed by + other listeners - either completely or at the next bubble stage. + +------------------------------------------------------------------------------------------------- */ + +@interface SPEvent : NSObject +{ + @private + SPEventDispatcher *mTarget; + SPEventDispatcher *mCurrentTarget; + NSString *mType; + BOOL mStopsImmediatePropagation; + BOOL mStopsPropagation; + BOOL mBubbles; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes an event object that can be passed to listeners. _Designated Initializer_. +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles; + +/// Initializes a non-bubbling event. +- (id)initWithType:(NSString*)type; + +/// Factory method. ++ (SPEvent*)eventWithType:(NSString*)type bubbles:(BOOL)bubbles; + +/// Factory method. ++ (SPEvent*)eventWithType:(NSString*)type; + +/// ------------- +/// @name Methods +/// ------------- + +/// Prevents any other listeners from receiving the event. +- (void)stopImmediatePropagation; + +/// Prevents listeners at the next bubble stage from receiving the event. +- (void)stopPropagation; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// A string that identifies the event. +@property (nonatomic, readonly) NSString *type; + +/// Indicates if event will bubble. +@property (nonatomic, readonly) BOOL bubbles; + +/// The object that dispatched the event. +@property (nonatomic, readonly) SPEventDispatcher *target; + +/// The object the event is currently bubbling at. +@property (nonatomic, readonly) SPEventDispatcher *currentTarget; + +@end diff --git a/libs/sparrow/src/Classes/SPEvent.m b/libs/sparrow/src/Classes/SPEvent.m new file mode 100644 index 0000000..66b0b81 --- /dev/null +++ b/libs/sparrow/src/Classes/SPEvent.m @@ -0,0 +1,104 @@ +// +// SPEvent.m +// Sparrow +// +// Created by Daniel Sperl on 27.04.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPEvent.h" +#import "SPEvent_Internal.h" + +@implementation SPEvent + +@synthesize target = mTarget; +@synthesize currentTarget = mCurrentTarget; +@synthesize type = mType; +@synthesize bubbles = mBubbles; + +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles +{ + if (self = [super init]) + { + mType = [[NSString alloc] initWithString:type]; + mBubbles = bubbles; + } + return self; +} + +- (id)initWithType:(NSString*)type +{ + return [self initWithType:type bubbles:NO]; +} + +- (id)init +{ + return [self initWithType:@"undefined"]; +} + +- (void)stopImmediatePropagation +{ + mStopsImmediatePropagation = YES; +} + +- (void)stopPropagation +{ + mStopsPropagation = YES; +} + ++ (SPEvent*)eventWithType:(NSString*)type bubbles:(BOOL)bubbles +{ + return [[[SPEvent alloc] initWithType:type bubbles:bubbles] autorelease]; +} + ++ (SPEvent*)eventWithType:(NSString*)type +{ + return [[[SPEvent alloc] initWithType:type] autorelease]; +} + +- (void)dealloc +{ + [mType release]; + [mTarget release]; + [mCurrentTarget release]; + [super dealloc]; +} + +@end + +// ------------------------------------------------------------------------------------------------- + +@implementation SPEvent (Internal) + +- (BOOL)stopsImmediatePropagation +{ + return mStopsImmediatePropagation; +} + +- (BOOL)stopsPropagation +{ + return mStopsPropagation; +} + +- (void)setTarget:(SPEventDispatcher*)target +{ + if (target != mTarget) + { + [mTarget release]; + mTarget = [target retain]; + } +} + +- (void)setCurrentTarget:(SPEventDispatcher*)currentTarget +{ + if (currentTarget != mCurrentTarget) + { + [mCurrentTarget release]; + mCurrentTarget = [currentTarget retain]; + } +} + +@end diff --git a/libs/sparrow/src/Classes/SPEventDispatcher.h b/libs/sparrow/src/Classes/SPEventDispatcher.h new file mode 100644 index 0000000..264e9da --- /dev/null +++ b/libs/sparrow/src/Classes/SPEventDispatcher.h @@ -0,0 +1,82 @@ +// +// SPEventDispatcher.h +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPEvent.h" + +/** ------------------------------------------------------------------------------------------------ + + The SPEventDispatcher class is the base for all classes that dispatch events. + + The event mechanism is a key feature of Sparrow's architecture. Objects can communicate with + each other over events. + + An event dispatcher can dispatch events (objects of type SPEvent or one of its subclasses) + to objects that have registered themselves as listeners. A string (the event type) is used to + identify different events. + + Here is a sample: + + // dispatching an event + [self dispatchEvent:[SPEvent eventWithType:@"eventType"]]; + + // listening to an event from 'object' + [object addEventListener:@selector(onEvent:) atObject:self forType:@"eventType"]; + + // the corresponding event listener + - (void)onEvent:(SPEvent *)event + { + // an event was triggered + } + + As SPDisplayObject, the base object of all rendered objects, inherits from SPEventDispatcher, + the event mechanism is tightly bound to the display list. Events that have their `bubbles`-property + enabled will rise up the display list until they reach its root (normally the stage). That means + that a listener can register for the event type not only on the object that will dispatch it, but + on any object that is a direct or indirect parent of the dispatcher. + + Different to _Adobe Flash_, events in Sparrow do not have a capture-phase. + + @see [SPEvent] + @see [SPDisplayObject] + +------------------------------------------------------------------------------------------------- */ + +@interface SPEventDispatcher : NSObject +{ + @private + NSMutableDictionary *mEventListeners; +} + +/// ------------- +/// @name Methods +/// ------------- + +/// Registers an event listener at a certain object. +- (void)addEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType + retainObject:(BOOL)retain; + +/// Registers an event listener at a certain object without retaining it (recommended). +- (void)addEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType; + +/// Removes an event listener at an object. +- (void)removeEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType; + +/// Removes all event listeners at an objct that have a certain type. +- (void)removeEventListenersAtObject:(id)object forType:(NSString*)eventType; + +/// Dispatches an event to all objects that have registered for events of the same type. +- (void)dispatchEvent:(SPEvent*)event; + +/// Returns if there are listeners registered for a certain event type. +- (BOOL)hasEventListenerForType:(NSString*)eventType; + +@end diff --git a/libs/sparrow/src/Classes/SPEventDispatcher.m b/libs/sparrow/src/Classes/SPEventDispatcher.m new file mode 100644 index 0000000..9721b9d --- /dev/null +++ b/libs/sparrow/src/Classes/SPEventDispatcher.m @@ -0,0 +1,140 @@ +// +// SPEventDispatcher.m +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPEventDispatcher.h" +#import "SPDisplayObject.h" +#import "SPEvent_Internal.h" +#import "SPMacros.h" +#import "SPNSExtensions.h" + +@implementation SPEventDispatcher + +- (void)addEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType + retainObject:(BOOL)doRetain +{ + if (!mEventListeners) + mEventListeners = [[NSMutableDictionary alloc] init]; + + NSInvocation *invocation = [NSInvocation invocationWithTarget:object selector:listener]; + if (doRetain) [invocation retainArguments]; + + // When an event listener is added or removed, a new NSArray object is created, instead of + // changing the array. The reason for this is that we can avoid creating a copy of the NSArray + // in the "dispatchEvent"-method, which is called far more often than + // "add"- and "removeEventListener". + + NSArray *listeners = [mEventListeners objectForKey:eventType]; + if (!listeners) + { + listeners = [[NSArray alloc] initWithObjects:invocation, nil]; + [mEventListeners setObject:listeners forKey:eventType]; + [listeners release]; + } + else + { + listeners = [listeners arrayByAddingObject:invocation]; + [mEventListeners setObject:listeners forKey:eventType]; + } +} + +- (void)addEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType +{ + [self addEventListener:listener atObject:object forType:eventType retainObject:NO]; +} + +- (void)removeEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType +{ + NSArray *listeners = [mEventListeners objectForKey:eventType]; + if (listeners) + { + NSMutableArray *remainingListeners = [[NSMutableArray alloc] init]; + for (NSInvocation *inv in listeners) + { + if (inv.target != object || (listener != nil && inv.selector != listener)) + [remainingListeners addObject:inv]; + } + + if (remainingListeners.count == 0) [mEventListeners removeObjectForKey:eventType]; + else [mEventListeners setObject:remainingListeners forKey:eventType]; + + [remainingListeners release]; + } +} + +- (void)removeEventListenersAtObject:(id)object forType:(NSString*)eventType +{ + [self removeEventListener:nil atObject:object forType:eventType]; +} + +- (BOOL)hasEventListenerForType:(NSString*)eventType +{ + return [mEventListeners objectForKey:eventType] != nil; +} + +- (void)dispatchEvent:(SPEvent*)event +{ + NSMutableArray *listeners = [mEventListeners objectForKey:event.type]; + if (!event.bubbles && !listeners) return; // no need to do anything. + + // if the event already has a current target, it was re-dispatched by user -> we change the + // target to 'self' for now, but undo that later on (instead of creating a copy, which could + // lead to the creation of a huge amount of objects). + SPEventDispatcher *previousTarget = event.target; + if (!event.target || event.currentTarget) event.target = self; + event.currentTarget = self; + + [self retain]; // the event listener could release 'self', so we have to make sure that it + // stays valid while we're here. + + BOOL stopImmediatPropagation = NO; + if (listeners.count != 0) + { + // we can enumerate directly over the array, since "add"- and "removeEventListener" won't + // change it, but instead always create a new array. + [listeners retain]; + for (NSInvocation *inv in listeners) + { + [inv setArgument:&event atIndex:2]; + [inv invoke]; + if (event.stopsImmediatePropagation) + { + stopImmediatPropagation = YES; + break; + } + } + [listeners release]; + } + + if (!stopImmediatPropagation) + { + event.currentTarget = nil; // this is how we can find out later if the event was redispatched + if (event.bubbles && !event.stopsPropagation && [self isKindOfClass:[SPDisplayObject class]]) + { + SPDisplayObject *target = (SPDisplayObject*)self; + [target.parent dispatchEvent:event]; + } + } + + if (previousTarget) event.target = previousTarget; + + // we use autorelease instead of release to avoid having to make additional "retain"-calls + // in calling methods (like "dispatchEventsOnChildren"). Those methods might be called very + // often, so we save some time by avoiding that. + [self autorelease]; +} + +- (void)dealloc +{ + [mEventListeners release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPEvent_Internal.h b/libs/sparrow/src/Classes/SPEvent_Internal.h new file mode 100644 index 0000000..acace14 --- /dev/null +++ b/libs/sparrow/src/Classes/SPEvent_Internal.h @@ -0,0 +1,23 @@ +// +// SPEvent_Internal.h +// Sparrow +// +// Created by Daniel Sperl on 03.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPEvent.h" + +@interface SPEvent (Internal) + +- (BOOL)stopsImmediatePropagation; +- (BOOL)stopsPropagation; +- (void)setTarget:(SPEventDispatcher*)target; +- (void)setCurrentTarget:(SPEventDispatcher*)currentTarget; + +@end + diff --git a/libs/sparrow/src/Classes/SPGLTexture.h b/libs/sparrow/src/Classes/SPGLTexture.h new file mode 100644 index 0000000..71b2e44 --- /dev/null +++ b/libs/sparrow/src/Classes/SPGLTexture.h @@ -0,0 +1,82 @@ +// +// SPGLTexture.h +// Sparrow +// +// Created by Daniel Sperl on 27.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +#import "SPTexture.h" +#import "SPMacros.h" + +@class SPRectangle; + +typedef enum +{ + SPTextureFormatRGBA, + SPTextureFormatAlpha, + SPTextureFormatPvrtcRGB2, + SPTextureFormatPvrtcRGBA2, + SPTextureFormatPvrtcRGB4, + SPTextureFormatPvrtcRGBA4, + SPTextureFormat565, + SPTextureFormat5551, + SPTextureFormat4444 +} SPTextureFormat; + +typedef struct +{ + SPTextureFormat format; + int width; + int height; + int numMipmaps; + BOOL generateMipmaps; + BOOL premultipliedAlpha; +} SPTextureProperties; + +/** ------------------------------------------------------------------------------------------------ + + The SPGLTexture class is a concrete implementation of the abstract class SPTexture, + containing a standard 2D OpenGL texture. + + Don't use this class directly, but load textures with the init-methods of SPTexture instead. + +------------------------------------------------------------------------------------------------- */ + +@interface SPGLTexture : SPTexture +{ + @private + uint mTextureID; + float mWidth; + float mHeight; + float mScale; + BOOL mRepeat; + BOOL mPremultipliedAlpha; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a texture with raw pixel data and a set of properties. +- (id)initWithData:(const void *)imgData properties:(SPTextureProperties)properties; + +/// Factory method. ++ (SPGLTexture*)textureWithData:(const void *)imgData properties:(SPTextureProperties)properties; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// Indicates if the texture should repeat like a wallpaper or stretch the outermost pixels. +@property (nonatomic, assign) BOOL repeat; + +/// The scale factor, which influences `width` and `height` properties. +@property (nonatomic, assign) float scale; + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPGLTexture.m b/libs/sparrow/src/Classes/SPGLTexture.m new file mode 100644 index 0000000..5ff43a4 --- /dev/null +++ b/libs/sparrow/src/Classes/SPGLTexture.m @@ -0,0 +1,183 @@ +// +// SPGLTexture.m +// Sparrow +// +// Created by Daniel Sperl on 27.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPGLTexture.h" +#import "SPMacros.h" +#import "SPRectangle.h" + +#import +#import + +@implementation SPGLTexture + +@synthesize textureID = mTextureID; +@synthesize repeat = mRepeat; +@synthesize hasPremultipliedAlpha = mPremultipliedAlpha; +@synthesize scale = mScale; + +- (id)initWithData:(const void*)imgData properties:(SPTextureProperties)properties +{ + if (self = [super init]) + { + mWidth = properties.width; + mHeight = properties.height; + mRepeat = NO; + mPremultipliedAlpha = properties.premultipliedAlpha; + mScale = 1.0f; + + GLenum glTexType = GL_UNSIGNED_BYTE; + GLenum glTexFormat; + int bitsPerPixel; + BOOL compressed = NO; + + switch (properties.format) + { + default: + case SPTextureFormatRGBA: + bitsPerPixel = 8; + glTexFormat = GL_RGBA; + break; + case SPTextureFormatAlpha: + bitsPerPixel = 8; + glTexFormat = GL_ALPHA; + break; + case SPTextureFormatPvrtcRGBA2: + compressed = YES; + bitsPerPixel = 2; + glTexFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + break; + case SPTextureFormatPvrtcRGB2: + compressed = YES; + bitsPerPixel = 2; + glTexFormat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + break; + case SPTextureFormatPvrtcRGBA4: + compressed = YES; + bitsPerPixel = 4; + glTexFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + break; + case SPTextureFormatPvrtcRGB4: + compressed = YES; + bitsPerPixel = 4; + glTexFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + break; + case SPTextureFormat565: + bitsPerPixel = 16; + glTexFormat = GL_RGB; + glTexType = GL_UNSIGNED_SHORT_5_6_5; + break; + case SPTextureFormat5551: + bitsPerPixel = 16; + glTexFormat = GL_RGBA; + glTexType = GL_UNSIGNED_SHORT_5_5_5_1; + break; + case SPTextureFormat4444: + bitsPerPixel = 16; + glTexFormat = GL_RGBA; + glTexType = GL_UNSIGNED_SHORT_4_4_4_4; + break; + } + + glGenTextures(1, &mTextureID); + glBindTexture(GL_TEXTURE_2D, mTextureID); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + + if (!compressed) + { + if (properties.numMipmaps > 0 || properties.generateMipmaps) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + else + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + if (properties.numMipmaps == 0 && properties.generateMipmaps) + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + + int levelWidth = mWidth; + int levelHeight = mHeight; + unsigned char *levelData = (unsigned char *)imgData; + + for (int level=0; level<=properties.numMipmaps; ++level) + { + int size = levelWidth * levelHeight * bitsPerPixel / 8; + glTexImage2D(GL_TEXTURE_2D, level, glTexFormat, levelWidth, levelHeight, + 0, glTexFormat, glTexType, levelData); + levelData += size; + levelWidth /= 2; + levelHeight /= 2; + } + } + else + { + // 'generateMipmaps' not supported for compressed textures + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, properties.numMipmaps == 0 ? + GL_LINEAR : GL_LINEAR_MIPMAP_NEAREST); + + int levelWidth = mWidth; + int levelHeight = mHeight; + unsigned char *levelData = (unsigned char *)imgData; + + for (int level=0; level<=properties.numMipmaps; ++level) + { + int size = MAX(32, levelWidth * levelHeight * bitsPerPixel / 8); + glCompressedTexImage2D(GL_TEXTURE_2D, level, glTexFormat, + levelWidth, levelHeight, 0, size, levelData); + levelData += size; + levelWidth /= 2; + levelHeight /= 2; + } + } + + glBindTexture(GL_TEXTURE_2D, 0); + + } + return self; +} + +- (id)init +{ + return [self initWithData:NULL properties:(SPTextureProperties){ .width = 32, .height = 32 }]; +} + +- (float)width +{ + return mWidth / mScale; +} + +- (float)height +{ + return mHeight / mScale; +} + +- (void)setRepeat:(BOOL)value +{ + mRepeat = value; + glBindTexture(GL_TEXTURE_2D, mTextureID); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); +} + ++ (SPGLTexture*)textureWithData:(const void *)imgData properties:(SPTextureProperties)properties +{ + return [[[SPGLTexture alloc] initWithData:imgData properties:properties] autorelease]; +} + +- (void)dealloc +{ + glDeleteTextures(1, &mTextureID); + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPImage.h b/libs/sparrow/src/Classes/SPImage.h new file mode 100644 index 0000000..06e4d3b --- /dev/null +++ b/libs/sparrow/src/Classes/SPImage.h @@ -0,0 +1,72 @@ +// +// SPImage.h +// Sparrow +// +// Created by Daniel Sperl on 19.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPQuad.h" + +@class SPTexture; +@class SPPoint; + +/** ------------------------------------------------------------------------------------------------ + + An SPImage displays a quad with a texture mapped onto it. + + Sparrow uses the SPTexture class to represent textures. To display a texture, you have to map + it on a quad - and that's what SPImage is for. + + As SPImage inherits from SPQuad, you can also give it a color. The resulting color is then the + result of the multiplication between the color of the texture and the color of the quad. That way, + you can easily tint textures with a certain color. Furthermore, SPImage allows the manipulation of + texture coordinates. + +------------------------------------------------------------------------------------------------- */ + +@interface SPImage : SPQuad +{ + @protected + SPTexture *mTexture; + float mTexCoords[8]; +} + +/// -------------------- +/// @name Initialization +/// -------------------- + +/// Initialize a quad with a texture mapped onto it. _Designated Initializer_. +- (id)initWithTexture:(SPTexture*)texture; + +/// Initialize a quad with a texture loaded from a file. +- (id)initWithContentsOfFile:(NSString*)path; + +/// Factory method. ++ (SPImage*)imageWithTexture:(SPTexture*)texture; + +/// Factory method. ++ (SPImage*)imageWithContentsOfFile:(NSString*)path; + +/// ------------- +/// @name Methods +/// ------------- + +/// Sets the texture coordinates of a vertex. Coordinates are in the range [0, 1]. +- (void)setTexCoords:(SPPoint*)coords ofVertex:(int)vertexID; + +/// Gets the texture coordinates of a vertex. +- (SPPoint*)texCoordsOfVertex:(int)vertexID; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The texture that is displayed on the quad. +@property (nonatomic, retain) SPTexture *texture; + +@end diff --git a/libs/sparrow/src/Classes/SPImage.m b/libs/sparrow/src/Classes/SPImage.m new file mode 100644 index 0000000..732a432 --- /dev/null +++ b/libs/sparrow/src/Classes/SPImage.m @@ -0,0 +1,80 @@ +// +// SPImage.m +// Sparrow +// +// Created by Daniel Sperl on 19.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPImage.h" +#import "SPPoint.h" +#import "SPTexture.h" +#import "SPGLTexture.h" + +@implementation SPImage + +@synthesize texture = mTexture; + +- (id)initWithTexture:(SPTexture*)texture; +{ + if (!texture) [NSException raise:SP_EXC_INVALID_OPERATION format:@"texture cannot be nil!"]; + + if (self = [super initWithWidth:texture.width height:texture.height]) + { + self.texture = texture; + mTexCoords[0] = 0.0f; mTexCoords[1] = 0.0f; + mTexCoords[2] = 1.0f; mTexCoords[3] = 0.0f; + mTexCoords[4] = 0.0f; mTexCoords[5] = 1.0f; + mTexCoords[6] = 1.0f; mTexCoords[7] = 1.0f; + } + return self; +} + +- (id)initWithContentsOfFile:(NSString*)path +{ + return [self initWithTexture:[SPTexture textureWithContentsOfFile:path]]; +} + +- (id)initWithWidth:(float)width height:(float)height +{ + SPTextureProperties properties = { .width = width, .height = height }; + return [self initWithTexture:[SPGLTexture textureWithData:NULL properties:properties]]; +} + +- (void)setTexCoords:(SPPoint*)coords ofVertex:(int)vertexID +{ + if (vertexID < 0 || vertexID > 3) + [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"invalid vertex id"]; + + mTexCoords[2*vertexID ] = coords.x; + mTexCoords[2*vertexID+1] = coords.y; +} + +- (SPPoint*)texCoordsOfVertex:(int)vertexID +{ + if (vertexID < 0 || vertexID > 3) + [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"invalid vertex id"]; + + return [SPPoint pointWithX:mTexCoords[vertexID*2] y:mTexCoords[vertexID*2+1]]; +} + ++ (SPImage*)imageWithTexture:(SPTexture*)texture +{ + return [[[SPImage alloc] initWithTexture:texture] autorelease]; +} + ++ (SPImage*)imageWithContentsOfFile:(NSString*)path +{ + return [[[SPImage alloc] initWithContentsOfFile:path] autorelease]; +} + +- (void)dealloc +{ + [mTexture release]; + [super dealloc]; +} + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPJuggler.h b/libs/sparrow/src/Classes/SPJuggler.h new file mode 100644 index 0000000..c7b48da --- /dev/null +++ b/libs/sparrow/src/Classes/SPJuggler.h @@ -0,0 +1,88 @@ +// +// SPJuggler.h +// Sparrow +// +// Created by Daniel Sperl on 09.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPAnimatable.h" + +/** ------------------------------------------------------------------------------------------------ + + The SPJuggler takes objects that implement SPAnimatable (e.g. `SPTween`s) and executes them. + + A juggler is a simple object. It does no more than saving a list of objects implementing + `SPAnimatable` and advancing their time if he is told to do so (by calling its own `advanceTime:` + method). When an animation is completed, it throws it away. + + There is a default juggler in every stage. You can access it by calling + + SPJuggler *juggler = self.stage.juggler; + + in any object that has access to the stage. You can, however, create juggler objects yourself, too. + That way, you can group your game into logical components that handle their animations independently. + + A cool feature of the juggler is to delay method calls. Say you want to remove an object from its + parent 2 seconds from now. Call: + + [[juggler delayInvocationAtTarget:object byTime:2.0] removeFromParent]; + + This line of code will execute the following method 2 seconds in the future: + + [object removeFromParent]; + + The Sparrow blog contains three extensive articles about the juggler: + + * http://www.sparrow-framework.org/2010/08/tweens-jugglers-animating-your-stage/ + * http://www.sparrow-framework.org/2010/09/tweens-jugglers-an-in-depth-look-at-the-juggler/ + * http://www.sparrow-framework.org/2010/10/tweens-jugglers-unleashed/ + +------------------------------------------------------------------------------------------------- */ + +@interface SPJuggler : NSObject +{ + @private + NSMutableSet *mObjects; + double mElapsedTime; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Factory method. ++ (SPJuggler *)juggler; + +/// ------------- +/// @name Methods +/// ------------- + +/// Adds an object to the juggler. +- (void)addObject:(id)object; + +/// Removes an object from the juggler. +- (void)removeObject:(id)object; + +/// Removes all objects at once. +- (void)removeAllObjects; + +/// Removes all objects of type `SPTween` that have a certain target. +- (void)removeTweensWithTarget:(id)object; + +/// Delays the execution of a certain method. Returns a proxy object on which to call the method +/// instead. Execution will be delayed until `time` has passed. +- (id)delayInvocationAtTarget:(id)target byTime:(double)time; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The total life time of the juggler. +@property (nonatomic, readonly) double elapsedTime; + +@end diff --git a/libs/sparrow/src/Classes/SPJuggler.m b/libs/sparrow/src/Classes/SPJuggler.m new file mode 100644 index 0000000..831919a --- /dev/null +++ b/libs/sparrow/src/Classes/SPJuggler.m @@ -0,0 +1,96 @@ +// +// SPJuggler.m +// Sparrow +// +// Created by Daniel Sperl on 09.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPJuggler.h" +#import "SPAnimatable.h" +#import "SPDelayedInvocation.h" + +@implementation SPJuggler + +@synthesize elapsedTime = mElapsedTime; + +- (id)init +{ + if (self = [super init]) + { + mObjects = [[NSMutableSet alloc] init]; + mElapsedTime = 0.0; + } + return self; +} + +- (BOOL)isComplete +{ + return NO; +} + +- (void)advanceTime:(double)seconds +{ + mElapsedTime += seconds; + + // we need work with a copy, since user-code could modify the collection during the enumeration + for (id object in [mObjects allObjects]) + { + [object advanceTime:seconds]; + if (object.isComplete) [self removeObject:object]; + } +} + +- (void)addObject:(id)object +{ + if (object) + [mObjects addObject:object]; +} + +- (void)removeObject:(id)object +{ + [mObjects removeObject:object]; +} + +- (void)removeAllObjects; +{ + [mObjects removeAllObjects]; +} + +- (void)removeTweensWithTarget:(id)object +{ + SEL targetSel = @selector(target); + NSMutableSet *remainingObjects = [[NSMutableSet alloc] init]; + + for (id currentObject in mObjects) + { + if (![currentObject respondsToSelector:targetSel] || ![[currentObject target] isEqual:object]) + [remainingObjects addObject:currentObject]; + } + + [mObjects release]; + mObjects = remainingObjects; +} + +- (id)delayInvocationAtTarget:(id)target byTime:(double)time +{ + SPDelayedInvocation *delayedInvoc = [SPDelayedInvocation invocationWithTarget:target delay:time]; + [self addObject:delayedInvoc]; + return delayedInvoc; +} + ++ (SPJuggler *)juggler +{ + return [[[SPJuggler alloc] init] autorelease]; +} + +- (void)dealloc +{ + [mObjects release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPMacros.h b/libs/sparrow/src/Classes/SPMacros.h new file mode 100644 index 0000000..aea1b3e --- /dev/null +++ b/libs/sparrow/src/Classes/SPMacros.h @@ -0,0 +1,56 @@ +// +// SPMacros.h +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import + +// constants + +#define PI 3.14159265359f +#define PI_HALF 1.57079632679f +#define TWO_PI 6.28318530718f + +#define SP_FLOAT_EPSILON 0.0001f + +#define SP_WHITE 0xFFFFFF +#define SP_BLACK 0x000000 + +#define SP_NOT_FOUND -1 +#define SP_MAX_DISPLAY_TREE_DEPTH 16 + +// exceptions + +#define SP_EXC_ABSTRACT_CLASS @"AbstractClass" +#define SP_EXC_ABSTRACT_METHOD @"AbstractMethod" +#define SP_EXC_NOT_RELATED @"NotRelated" +#define SP_EXC_INDEX_OUT_OF_BOUNDS @"IndexOutOfBounds" +#define SP_EXC_INVALID_OPERATION @"InvalidOperation" +#define SP_EXC_FILE_NOT_FOUND @"FileNotFound" +#define SP_EXC_FILE_INVALID @"FileInvalid" + +// macros + +#define SP_CREATE_POOL(pool) NSAutoreleasePool *(pool) = [[NSAutoreleasePool alloc] init]; +#define SP_RELEASE_POOL(pool) [(pool) release]; + +#define SP_R2D(rad) ((rad) / PI * 180.0f) +#define SP_D2R(deg) ((deg) / 180.0f * PI) + +#define SP_COLOR_PART_ALPHA(color) (((color) >> 24) & 0xff) +#define SP_COLOR_PART_RED(color) (((color) >> 16) & 0xff) +#define SP_COLOR_PART_GREEN(color) (((color) >> 8) & 0xff) +#define SP_COLOR_PART_BLUE(color) ( (color) & 0xff) + +#define SP_COLOR(r, g, b) (((r) << 16) | ((g) << 8) | (b)) + +#define SP_IS_FLOAT_EQUAL(f1, f2) (fabsf((f1)-(f2)) < SP_FLOAT_EPSILON) + + diff --git a/libs/sparrow/src/Classes/SPMatrix.h b/libs/sparrow/src/Classes/SPMatrix.h new file mode 100644 index 0000000..7ca72df --- /dev/null +++ b/libs/sparrow/src/Classes/SPMatrix.h @@ -0,0 +1,99 @@ +// +// SPMatrix.h +// Sparrow +// +// Created by Daniel Sperl on 26.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPPoolObject.h" + +@class SPPoint; + +/** ------------------------------------------------------------------------------------------------ + + The SPMatrix class describes an affine, 2D transformation Matrix. It provides methods to + manipulate the matrix in convenient ways, and can be used to transform points. + + The matrix has the following form: + + |a c tx| + |b d ty| + |0 0 1| + +------------------------------------------------------------------------------------------------- */ + +@interface SPMatrix : SPPoolObject +{ + @private + float mA, mB, mC, mD; + float mTx, mTy; +} + +/// ----------------- +/// @name Intializers +/// ----------------- + +/// Initializes a matrix with the specified components. _Designated Initializer_. +- (id)initWithA:(float)a b:(float)b c:(float)c d:(float)d tx:(float)tx ty:(float)ty; + +/// Initializes an identity matrix. +- (id)init; + +/// Factory method. ++ (SPMatrix*)matrixWithA:(float)a b:(float)b c:(float)c d:(float)d tx:(float)tx ty:(float)ty; + +/// Factory method. ++ (SPMatrix*)matrixWithIdentity; + +/// ------------- +/// @name Methods +/// ------------- + +/// Compares to matrices. +- (BOOL)isEqual:(id)other; + +/// Concatenates a matrix with the current matrix, combining the geometric effects of the two. +- (void)concatMatrix:(SPMatrix*)matrix; + +/// Translates the matrix along the x and y axes. +- (void)translateXBy:(float)dx yBy:(float)dy; + +/// Applies a scaling transformation to the matrix. +- (void)scaleXBy:(float)sx yBy:(float)sy; + +/// Applies a uniform scaling transformation to the matrix. +- (void)scaleBy:(float)scale; + +/// Applies a rotation on the matrix (angle in RAD). +- (void)rotateBy:(float)angle; + +/// Sets each matrix property to a value that causes a null transformation. +- (void)identity; + +/// Performs the opposite transformation of the matrix. +- (void)invert; + +/// Applies the geometric transformation represented by the matrix to the specified point. +- (SPPoint*)transformPoint:(SPPoint*)point; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// Matrix component. +@property (nonatomic, assign) float a; +@property (nonatomic, assign) float b; +@property (nonatomic, assign) float c; +@property (nonatomic, assign) float d; +@property (nonatomic, assign) float tx; +@property (nonatomic, assign) float ty; + +/// The determinant of the matrix. +@property (nonatomic, readonly) float determinant; + +@end diff --git a/libs/sparrow/src/Classes/SPMatrix.m b/libs/sparrow/src/Classes/SPMatrix.m new file mode 100644 index 0000000..2e83df0 --- /dev/null +++ b/libs/sparrow/src/Classes/SPMatrix.m @@ -0,0 +1,165 @@ +// +// SPMatrix.m +// Sparrow +// +// Created by Daniel Sperl on 26.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPMatrix.h" +#import "SPPoint.h" +#import "SPMacros.h" + +#define U 0 +#define V 0 +#define W 1 + +@implementation SPMatrix + +@synthesize a=mA, b=mB, c=mC, d=mD, tx=mTx, ty=mTy; + +// --- c functions --- + +static void setValues(SPMatrix *matrix, float a, float b, float c, float d, float tx, float ty) +{ + matrix->mA = a; + matrix->mB = b; + matrix->mC = c; + matrix->mD = d; + matrix->mTx = tx; + matrix->mTy = ty; +} + +// --- + +- (id)initWithA:(float)a b:(float)b c:(float)c d:(float)d tx:(float)tx ty:(float)ty +{ + if (self = [super init]) + { + mA = a; mB = b; mC = c; mD = d; + mTx = tx; mTy = ty; + } + return self; +} + +- (id)init +{ + return [self initWithA:1 b:0 c:0 d:1 tx:0 ty:0]; +} + +- (void)setValuesA:(float)a b:(float)b c:(float)c d:(float)d tx:(float)tx ty:(float)ty +{ + mA = a; mB = b; mC = c; mD = d; + mTx = tx; mTy = ty; +} + +- (float)determinant +{ + return mA * mD - mC * mB; +} + +- (void)concatMatrix:(SPMatrix*)matrix +{ + setValues(self, matrix->mA * mA + matrix->mC * mB, + matrix->mB * mA + matrix->mD * mB, + matrix->mA * mC + matrix->mC * mD, + matrix->mB * mC + matrix->mD * mD, + matrix->mA * mTx + matrix->mC * mTy + matrix->mTx * W, + matrix->mB * mTx + matrix->mD * mTy + matrix->mTy * W); +} + +- (void)translateXBy:(float)dx yBy:(float)dy +{ + mTx += dx; + mTy += dy; +} + +- (void)scaleXBy:(float)sx yBy:(float)sy +{ + mA *= sx; + mB *= sy; + mC *= sx; + mD *= sy; + mTx *= sx; + mTy *= sy; +} + +- (void)scaleBy:(float)scale +{ + [self scaleXBy:scale yBy:scale]; +} + +- (void)rotateBy:(float)angle +{ + SPMatrix *rotMatrix = [[SPMatrix alloc] initWithA:cosf(angle) b:sinf(angle) + c:-sinf(angle) d:cosf(angle) tx:0 ty:0]; + [self concatMatrix:rotMatrix]; + [rotMatrix release]; +} + +- (void)identity +{ + setValues(self, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); +} + +- (SPPoint*)transformPoint:(SPPoint*)point +{ + return [SPPoint pointWithX:mA*point.x + mC*point.y + mTx + y:mB*point.x + mD*point.y + mTy]; +} + +- (void)invert +{ + float det = self.determinant; + setValues(self, mD/det, -mB/det, -mC/det, mA/det, (mC*mTy-mD*mTx)/det, (mB*mTx-mA*mTy)/det); +} + +- (BOOL)isEqual:(id)other +{ + if (other == self) return YES; + else if (!other || ![other isKindOfClass:[self class]]) return NO; + else + { + SPMatrix *matrix = (SPMatrix*)other; + return SP_IS_FLOAT_EQUAL(mA, matrix->mA) && SP_IS_FLOAT_EQUAL(mB, matrix->mB) && + SP_IS_FLOAT_EQUAL(mC, matrix->mC) && SP_IS_FLOAT_EQUAL(mD, matrix->mD) && + SP_IS_FLOAT_EQUAL(mTx, matrix->mTx) && SP_IS_FLOAT_EQUAL(mTy, matrix->mTy); + } +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"(a=%f, b=%f, c=%f, d=%f, tx=%f, ty=%f)", + mA, mB, mC, mD, mTx, mTy]; +} + ++ (SPMatrix*)matrixWithA:(float)a b:(float)b c:(float)c d:(float)d tx:(float)tx ty:(float)ty +{ + return [[[SPMatrix alloc] initWithA:a b:b c:c d:d tx:tx ty:ty] autorelease]; +} + ++ (SPMatrix*)matrixWithIdentity +{ + return [[[SPMatrix alloc] init] autorelease]; +} + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone*)zone; +{ + return [[[self class] allocWithZone:zone] initWithA:mA b:mB c:mC d:mD + tx:mTx ty:mTy]; +} + +#pragma mark SPPoolObject + ++ (SPPoolInfo *)poolInfo +{ + static SPPoolInfo poolInfo; + return &poolInfo; +} + +@end diff --git a/libs/sparrow/src/Classes/SPMovieClip.h b/libs/sparrow/src/Classes/SPMovieClip.h new file mode 100644 index 0000000..c3e85c8 --- /dev/null +++ b/libs/sparrow/src/Classes/SPMovieClip.h @@ -0,0 +1,136 @@ +// +// SPMovieClip.h +// Sparrow +// +// Created by Daniel Sperl on 01.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPSprite.h" +#import "SPTexture.h" +#import "SPAnimatable.h" +#import "SPImage.h" +#import "SPSoundChannel.h" + +#define SP_EVENT_TYPE_MOVIE_COMPLETED @"movieCompleted" + +/** ------------------------------------------------------------------------------------------------ + + An SPMovieClip is a simple way to display an animation depicted by a number of textures. + + You can add the frames one by one or pass them all at once (in an array) at initialization time. + The movie clip will have the width and height of the first frame. + + At initialization, you can specify the desired framerate. You can, however, manually give each + frame a custom duration. You can also play a sound whenever a certain frame appears. + + The methods `play` and `pause` control playback of the movie. You will receive an event of type + `SP_EVENT_TYPE_MOVIE_COMPLETED` when the movie finished playback (except when you enabled looping). + + As any animated object, a movie clip has to be added to a juggler (or have its `advanceTime:` + method called regularly) to run. + +------------------------------------------------------------------------------------------------- */ + +@interface SPMovieClip : SPImage +{ + @private + NSMutableArray *mFrames; + NSMutableArray *mSounds; + NSMutableArray *mFrameDurations; + + double mDefaultFrameDuration; + double mTotalDuration; + double mElapsedTime; + BOOL mLoop; + BOOL mPlaying; + int mCurrentFrame; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a movie with the first frame and the default number of frames per second. _Designated initializer_. +- (id)initWithFrame:(SPTexture *)texture fps:(float)fps; // designated initializer + +/// Initializes a movie with an array of textures and the default number of frames per second. +- (id)initWithFrames:(NSArray *)textures fps:(float)fps; + +/// Factory method. ++ (SPMovieClip *)movieWithFrame:(SPTexture *)texture fps:(float)fps; + +/// Factory method. ++ (SPMovieClip *)movieWithFrames:(NSArray *)textures fps:(float)fps; + +/// -------------------------------- +/// @name Frame Manipulation Methods +/// -------------------------------- + +/// Adds a frame with the default duration. +- (int)addFrame:(SPTexture *)texture; + +/// Adds a frame with a specified duration. +- (int)addFrame:(SPTexture *)texture withDuration:(double)duration; + +/// Inserts a frame at the index specified. +- (void)insertFrame:(SPTexture *)texture atIndex:(int)frameID; + +/// Removes the frame at the index specified. +- (void)removeFrameAtIndex:(int)frameID; + +/// Sets the texture of a certain frame. +- (void)setFrame:(SPTexture *)texture atIndex:(int)frameID; + +/// Sets the sound that will be played back when a certain frame is active. +- (void)setSound:(SPSoundChannel *)sound atIndex:(int)frameID; + +/// Sets the duration of a certain frame in seconds. +- (void)setDuration:(double)duration atIndex:(int)frameID; + +/// Returns the texture of a frame at a certain index. +- (SPTexture *)frameAtIndex:(int)frameID; + +/// Returns the sound of a frame at a certain index. +- (SPSoundChannel *)soundAtIndex:(int)frameID; + +/// Returns the duration (in seconds) of a frame at a certain index. +- (double)durationAtIndex:(int)frameID; + +/// ---------------------- +/// @name Playback Methods +/// ---------------------- + +/// Start playback. Beware that the clip has to be added to a juggler, too! +- (void)play; + +/// Pause playback. +- (void)pause; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The number of frames of the clip. +@property (nonatomic, readonly) int numFrames; + +/// The accumulated duration of all frames. +@property (nonatomic, readonly) double duration; + +/// Indicates if the movie is currently playing. +@property (nonatomic, readonly) BOOL isPlaying; + +/// Indicates if the movie is looping. +@property (nonatomic, assign) BOOL loop; + +/// The ID of the frame that is currently displayed. +@property (nonatomic, assign) int currentFrame; + +/// The default frames per second. Used when you add a frame without specifying a duration. +@property (nonatomic, assign) float fps; + +@end diff --git a/libs/sparrow/src/Classes/SPMovieClip.m b/libs/sparrow/src/Classes/SPMovieClip.m new file mode 100644 index 0000000..bbabe34 --- /dev/null +++ b/libs/sparrow/src/Classes/SPMovieClip.m @@ -0,0 +1,274 @@ +// +// SPMovieClip.m +// Sparrow +// +// Created by Daniel Sperl on 01.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPMovieClip.h" +#import "SPMacros.h" + +// --- private interface --------------------------------------------------------------------------- + +@interface SPMovieClip () + +- (void)updateCurrentFrame; +- (void)playCurrentSound; +- (void)checkIndex:(int)frameID; + +@end + + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPMovieClip + +@synthesize loop = mLoop; +@synthesize isPlaying = mPlaying; +@synthesize currentFrame = mCurrentFrame; +@synthesize duration = mTotalDuration; + +- (id)initWithFrame:(SPTexture *)texture fps:(float)fps +{ + if (self = [super initWithTexture:texture]) + { + self.fps = fps; + mLoop = YES; + mPlaying = YES; + mTotalDuration = 0.0; + mElapsedTime = 0.0; + mCurrentFrame = 0; + mFrames = [[NSMutableArray alloc] init]; + mSounds = [[NSMutableArray alloc] init]; + mFrameDurations = [[NSMutableArray alloc] init]; + [self addFrame:texture]; + } + return self; +} + +- (id)initWithFrames:(NSArray *)textures fps:(float)fps +{ + if (textures.count == 0) + [NSException raise:SP_EXC_INVALID_OPERATION format:@"empty texture array"]; + + [self initWithFrame:[textures objectAtIndex:0] fps:fps]; + + if (textures.count > 1) + for (int i=1; i mFrames.count) + [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"invalid frame index"]; +} + ++ (SPMovieClip *)movieWithFrame:(SPTexture *)texture fps:(float)fps +{ + return [[[SPMovieClip alloc] initWithFrame:texture fps:fps] autorelease]; +} + ++ (SPMovieClip *)movieWithFrames:(NSArray *)textures fps:(float)fps +{ + return [[[SPMovieClip alloc] initWithFrames:textures fps:fps] autorelease]; +} + +#pragma mark SPAnimatable + +- (void)advanceTime:(double)seconds +{ + if (!mPlaying || (!mLoop && mElapsedTime == mTotalDuration)) return; + + double previousElapsedTime = mElapsedTime; + mElapsedTime += seconds; + + if (mLoop) + { + while (mElapsedTime > mTotalDuration) + mElapsedTime -= mTotalDuration; + } + else + { + mElapsedTime = MIN(mTotalDuration, mElapsedTime); + } + + double durationSum = 0.0; + int i = 0; + + for (NSNumber *frameDuration in mFrameDurations) + { + double fd = [frameDuration doubleValue]; + if (durationSum + fd >= mElapsedTime) + { + if (mCurrentFrame != i) + { + mCurrentFrame = i; + [self updateCurrentFrame]; + [self playCurrentSound]; + } + break; + } + + ++i; + durationSum += fd; + } + + if (!mLoop && previousElapsedTime < mTotalDuration && mElapsedTime >= mTotalDuration) + [self dispatchEvent:[SPEvent eventWithType:SP_EVENT_TYPE_MOVIE_COMPLETED]]; +} + +- (BOOL)isComplete +{ + return NO; +} + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPNSExtensions.h b/libs/sparrow/src/Classes/SPNSExtensions.h new file mode 100644 index 0000000..8b981d0 --- /dev/null +++ b/libs/sparrow/src/Classes/SPNSExtensions.h @@ -0,0 +1,39 @@ +// +// SPNSAdditions.h +// Sparrow +// +// Created by Daniel Sperl on 13.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +/** Sparrow extensions for the NSInvocation class. */ +@interface NSInvocation (SPNSExtensions) + +/// Creates an invocation with a specified target and selector. ++ (NSInvocation *)invocationWithTarget:(id)target selector:(SEL)selector; + +@end + +/** Sparrow extensions for the NSString class. */ +@interface NSString (SPNSExtensions) + +/// Creates a string by appending a suffix to a filename in front of its extension. +- (NSString *)stringByAppendingSuffixToFilename:(NSString *)suffix; + +@end + +/** Sparrow extensions for the NSBundle class. */ +@interface NSBundle (SPNSExtensions) + +/// Determines if a resource with a certain scale factor suffix (@2x) exists. +/// +/// @return Returns the path to the scaled resource if it exists; otherwise, the path to the +// unscaled resource - or nil if that does not exist, either. +- (NSString *)pathForResource:(NSString *)name withScaleFactor:(float)factor; + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPNSExtensions.m b/libs/sparrow/src/Classes/SPNSExtensions.m new file mode 100644 index 0000000..9453d16 --- /dev/null +++ b/libs/sparrow/src/Classes/SPNSExtensions.m @@ -0,0 +1,50 @@ +// +// SPNSAdditions.m +// Sparrow +// +// Created by Daniel Sperl on 13.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPNSExtensions.h" + +@implementation NSInvocation (SPNSExtensions) + ++ (NSInvocation*)invocationWithTarget:(id)target selector:(SEL)selector +{ + NSMethodSignature *signature = [target methodSignatureForSelector:selector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + invocation.selector = selector; + invocation.target = target; + return invocation; +} + +@end + +@implementation NSString (SPNSExtensions) + +- (NSString *)stringByAppendingSuffixToFilename:(NSString *)suffix +{ + return [[self stringByDeletingPathExtension] stringByAppendingFormat:@"%@.%@", + suffix, [self pathExtension]]; +} + +@end + +@implementation NSBundle (SPNSExtensions) + +- (NSString *)pathForResource:(NSString *)name withScaleFactor:(float)factor +{ + if (factor != 1.0f) + { + NSString *suffix = [NSString stringWithFormat:@"@%@x", [NSNumber numberWithFloat:factor]]; + NSString *path = [self pathForResource:[name stringByAppendingSuffixToFilename:suffix] ofType:nil]; + if (path) return path; + } + return [self pathForResource:name ofType:nil]; +} + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPPoint.h b/libs/sparrow/src/Classes/SPPoint.h new file mode 100644 index 0000000..2e635d7 --- /dev/null +++ b/libs/sparrow/src/Classes/SPPoint.h @@ -0,0 +1,82 @@ +// +// SPPoint.h +// Sparrow +// +// Created by Daniel Sperl on 23.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPPoolObject.h" + +/** The SPPoint class describes a two dimensional point or vector. */ + +@interface SPPoint : SPPoolObject +{ + @private + float mX; + float mY; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a point with its x and y components. _Designated Initializer_. +- (id)initWithX:(float)x y:(float)y; + +/// Initializes a point with the distance and angle in respect to the origin. +- (id)initWithPolarLength:(float)length angle:(float)angle; + +/// Factory method. ++ (SPPoint *)pointWithPolarLength:(float)length angle:(float)angle; + +/// Factory method. ++ (SPPoint *)pointWithX:(float)x y:(float)y; + +/// Factory method. ++ (SPPoint *)point; + +/// ------------- +/// @name Methods +// -------------- + +/// Adds a point to the current point and returns the resulting point. +- (SPPoint *)addPoint:(SPPoint *)point; + +/// Substracts a point from the current point and returns the resulting point. +- (SPPoint *)subtractPoint:(SPPoint *)point; + +/// Scales the point by a certain factor and returns the resulting point. +- (SPPoint *)scaleBy:(float)scalar; + +/// Scales the line segment between the origin and the current point to one. +- (SPPoint *)normalize; + +/// Compares two points. +- (BOOL)isEqual:(id)other; + +/// Calculates the distance between two points. ++ (float)distanceFromPoint:(SPPoint *)p1 toPoint:(SPPoint *)p2; + +/// Determines a point between two specified points. `ratio = 0 -> p1, ratio = 1 -> p2` ++ (SPPoint *)interpolateFromPoint:(SPPoint *)p1 toPoint:(SPPoint *)p2 ratio:(float)ratio; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// Point component. +@property (nonatomic, assign) float x; +@property (nonatomic, assign) float y; + +/// The distance to the origin (or the length of the vector). +@property (readonly) float length; + +/// The angle between the origin and the point (in RAD). +@property (readonly) float angle; + +@end diff --git a/libs/sparrow/src/Classes/SPPoint.m b/libs/sparrow/src/Classes/SPPoint.m new file mode 100644 index 0000000..a6ffe87 --- /dev/null +++ b/libs/sparrow/src/Classes/SPPoint.m @@ -0,0 +1,142 @@ +// +// SPPoint.m +// Sparrow +// +// Created by Daniel Sperl on 23.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPPoint.h" +#import "SPMacros.h" +#import + +// --- class implementation ------------------------------------------------------------------------ + +#define SQ(x) ((x)*(x)) + +@implementation SPPoint + +@synthesize x = mX; +@synthesize y = mY; + +// designated initializer +- (id)initWithX:(float)x y:(float)y +{ + if (self = [super init]) + { + mX = x; + mY = y; + } + return self; +} + +- (id)initWithPolarLength:(float)length angle:(float)angle +{ + return [self initWithX:cosf(angle)*length y:sinf(angle)*length]; +} + +- (id)init +{ + return [self initWithX:0.0f y:0.0f]; +} + +- (float)length +{ + return sqrtf(SQ(mX) + SQ(mY)); +} + +- (float)angle +{ + return atan2f(mY, mX); +} + +- (SPPoint*)addPoint:(SPPoint*)point +{ + SPPoint *result = [[SPPoint alloc] initWithX:mX+point->mX y:mY+point->mY]; + return [result autorelease]; +} + +- (SPPoint*)subtractPoint:(SPPoint*)point +{ + SPPoint *result = [[SPPoint alloc] initWithX:mX-point->mX y:mY-point->mY]; + return [result autorelease]; +} + +- (SPPoint *)scaleBy:(float)scalar +{ + SPPoint *result = [[SPPoint alloc] initWithX:mX * scalar y:mY * scalar]; + return [result autorelease]; +} + +- (SPPoint*)normalize +{ + if (mX == 0 && mY == 0) + [NSException raise:SP_EXC_INVALID_OPERATION format:@"Cannot normalize point in the origin"]; + + float inverseLength = 1.0f / self.length; + SPPoint *result = [[SPPoint alloc] initWithX:mX * inverseLength y:mY * inverseLength]; + return [result autorelease]; +} + +- (BOOL)isEqual:(id)other +{ + if (other == self) return YES; + else if (!other || ![other isKindOfClass:[self class]]) return NO; + else + { + SPPoint *point = (SPPoint*)other; + return SP_IS_FLOAT_EQUAL(mX, point->mX) && SP_IS_FLOAT_EQUAL(mY, point->mY); + } +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"(x=%f, y=%f)", mX, mY]; +} + ++ (float)distanceFromPoint:(SPPoint*)p1 toPoint:(SPPoint*)p2 +{ + return sqrtf(SQ(p2->mX - p1->mX) + SQ(p2->mY - p1->mY)); +} + ++ (SPPoint *)interpolateFromPoint:(SPPoint *)p1 toPoint:(SPPoint *)p2 ratio:(float)ratio +{ + float invRatio = 1.0f - ratio; + return [SPPoint pointWithX:invRatio * p1->mX + ratio * p2->mX + y:invRatio * p1->mY + ratio * p2->mY]; +} + ++ (SPPoint *)pointWithPolarLength:(float)length angle:(float)angle +{ + return [[[SPPoint alloc] initWithPolarLength:length angle:angle] autorelease]; +} + ++ (SPPoint *)pointWithX:(float)x y:(float)y +{ + return [[[SPPoint alloc] initWithX:x y:y] autorelease]; +} + ++ (SPPoint*)point +{ + return [[[SPPoint alloc] init] autorelease]; +} + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone*)zone; +{ + return [[[self class] allocWithZone:zone] initWithX:mX y:mY]; +} + +#pragma mark SPPoolObject + ++ (SPPoolInfo *)poolInfo +{ + static SPPoolInfo poolInfo; + return &poolInfo; +} + +@end diff --git a/libs/sparrow/src/Classes/SPPoolObject.h b/libs/sparrow/src/Classes/SPPoolObject.h new file mode 100644 index 0000000..0656acb --- /dev/null +++ b/libs/sparrow/src/Classes/SPPoolObject.h @@ -0,0 +1,78 @@ +// +// SPPoolObject.h +// Sparrow +// +// Created by Daniel Sperl on 17.09.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +@class SPPoolObject; + +typedef struct +{ + Class poolClass; + SPPoolObject *lastElement; +} SPPoolInfo; + +#ifndef DISABLE_MEMORY_POOLING + + +/** ------------------------------------------------------------------------------------------------ + + The SPPoolObject class is an alternative to the base class `NSObject` that manages a pool of + objects. + + Subclasses of SPPoolObject do not deallocate object instances when the retain counter reaches + zero. Instead, the objects stay in memory and will be re-used when a new instance of the object + is requested. That way, object initialization is accelerated. You can release the memory of all + recycled objects anytime by calling the `purgePool` method. + + Sparrow uses this class for `SPPoint`, `SPRectangle` and `SPMatrix`, as they are created very often + as helper objects. + + To use memory pooling for another class, you just have to inherit from SPPoolObject and implement + the following method: + + + (SPPoolInfo *) poolInfo + { + static SPPoolInfo poolInfo; + return &poolInfo; + } + + ------------------------------------------------------------------------------------------------- */ + +@interface SPPoolObject : NSObject +{ + SPPoolObject *mPoolPredecessor; +} + +/// The pool info structure needed to access the pool. Needs to be implemented in any inheriting class. ++ (SPPoolInfo *)poolInfo; + +/// Purge all unused objects. ++ (int)purgePool; + +@end + +#else + +typedef NSObject SPPoolObject; + +/// Sparrow extensions for NSObject. +@interface NSObject (SPPoolObjectExtensions) + +/// Dummy implementation of SPPoolObject method to simplify switching between NSObject and SPPoolObject. ++ (SPPoolInfo *)poolInfo; + +/// Dummy implementation of SPPoolObject method to simplify switching between NSObject and SPPoolObject. ++ (int)purgePool; + +@end + + +#endif \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPPoolObject.m b/libs/sparrow/src/Classes/SPPoolObject.m new file mode 100644 index 0000000..289af57 --- /dev/null +++ b/libs/sparrow/src/Classes/SPPoolObject.m @@ -0,0 +1,113 @@ +// +// SPPoolObject.m +// Sparrow +// +// Created by Daniel Sperl on 17.09.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPPoolObject.h" +#import + +#ifndef DISABLE_MEMORY_POOLING + +#define COMPLAIN_MISSING_IMP @"Class %@ needs this code:\n\ ++ (SPPoolInfo *) poolInfo\n\ +{\n\ + static SPPoolInfo poolInfo;\n\ + return &poolInfo;\n\ +}" + +@implementation SPPoolObject + ++ (id)allocWithZone:(NSZone *)zone +{ + SPPoolInfo *poolInfo = [self poolInfo]; + if (!poolInfo->poolClass) // first allocation + { + poolInfo->poolClass = self; + poolInfo->lastElement = NULL; + } + else + { + if (poolInfo->poolClass != self) + [NSException raise:NSGenericException format:COMPLAIN_MISSING_IMP, self]; + } + + if (!poolInfo->lastElement) + { + // pool is empty -> allocate + return NSAllocateObject(self, 0, NULL); + } + else + { + // recycle element, update poolInfo + SPPoolObject *object = poolInfo->lastElement; + poolInfo->lastElement = object->mPoolPredecessor; + + // zero out memory. (do not overwrite isa & mPoolPredecessor, thus "+2" and "-8") + memset((id)object + 2, 0, malloc_size(object) - 8); + + return object; + } +} + +- (void)dealloc +{ + SPPoolInfo *poolInfo = [isa poolInfo]; + self->mPoolPredecessor = poolInfo->lastElement; + poolInfo->lastElement = self; + + if (0) [super dealloc]; // just to shut down a compiler warning ... +} + +- (void)purge +{ + [super dealloc]; +} + ++ (int)purgePool +{ + SPPoolInfo *poolInfo = [self poolInfo]; + SPPoolObject *lastElement; + + int count=0; + while (lastElement = poolInfo->lastElement) + { + ++count; + poolInfo->lastElement = lastElement->mPoolPredecessor; + [lastElement purge]; + } + + return count; +} + ++ (SPPoolInfo *)poolInfo +{ + [NSException raise:NSGenericException format:COMPLAIN_MISSING_IMP, self]; + return 0; +} + +@end + +#else + +@implementation NSObject (SPPoolObjectExtensions) + ++ (SPPoolInfo *)poolInfo +{ + return nil; +} + ++ (int)purgePool +{ + return 0; +} + +@end + + +#endif \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPQuad.h b/libs/sparrow/src/Classes/SPQuad.h new file mode 100644 index 0000000..3120155 --- /dev/null +++ b/libs/sparrow/src/Classes/SPQuad.h @@ -0,0 +1,97 @@ +// +// SPQuad.h +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPDisplayObject.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPQuad displays a single, colored rectangle. + It renders a rectangular area with a single color or a color gradient. + + You can set one color per vertex. The colors will smoothly fade into each other over the area + of the quad. To display a simple linear color gradient, assign one color to vertices 0 and 1 and + another color to vertices 2 and 3. + + The indices of the vertices are arranged like this: + + 0 - 1 + | / | + 2 - 3 + + *Colors* + + Colors in Sparrow are defined as unsigned integers, that's exactly 8 bit per color. The easiest + way to define a color is by writing it as a hexadecimal number. A color has the following + structure: + + 0xRRGGBB + + That means that you can create the base colors like this: + + 0xFF0000 -> red + 0x00FF00 -> green + 0x0000FF -> blue + + Other simple colors: + + 0x000000 or 0x0 -> black + 0xFFFFFF -> white + 0x808080 -> 50% gray + + If you're not comfortable with that, there is also a utility macro available that takes the + values for R, G and B separately: + + uint red = SP_COLOR(255, 0, 0) + +------------------------------------------------------------------------------------------------- */ + +@interface SPQuad : SPDisplayObject +{ + @protected + float mVertexCoords[8]; + uint mVertexColors[4]; +} + +/// -------------------- +/// @name Initialization +/// -------------------- + +/// Initializes a quad with a certain size and color. _Designated Initializer_. +- (id)initWithWidth:(float)width height:(float)height color:(uint)color; + +/// Initializes a white quad with a certain size. +- (id)initWithWidth:(float)width height:(float)height; + +/// ------------- +/// @name Methods +/// ------------- + +/// Sets the color of a vertex. +- (void)setColor:(uint)color ofVertex:(int)vertexID; + +/// Returns the color of a vertex. +- (uint)colorOfVertex:(int)vertexID; + +/// Factory method. ++ (SPQuad*)quadWithWidth:(float)width height:(float)height; + +/// Factory method. ++ (SPQuad*)quadWithWidth:(float)width height:(float)height color:(uint)color; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// Sets the colors of all vertices simultaneously. Returns the color of vertex '0'. +@property (nonatomic, assign) uint color; + +@end diff --git a/libs/sparrow/src/Classes/SPQuad.m b/libs/sparrow/src/Classes/SPQuad.m new file mode 100644 index 0000000..134bf01 --- /dev/null +++ b/libs/sparrow/src/Classes/SPQuad.m @@ -0,0 +1,104 @@ +// +// SPQuad.m +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPQuad.h" +#import "SPRectangle.h" +#import "SPMacros.h" +#import "SPPoint.h" + +@implementation SPQuad + +- (id)initWithWidth:(float)width height:(float)height color:(uint)color +{ + if (self = [super init]) + { + mVertexCoords[2] = width; + mVertexCoords[5] = height; + mVertexCoords[6] = width; + mVertexCoords[7] = height; + + self.color = color; + } + return self; +} + +- (id)initWithWidth:(float)width height:(float)height +{ + return [self initWithWidth:width height:height color:SP_WHITE]; +} + +- (id)init +{ + return [self initWithWidth:32 height:32]; +} + +- (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetCoordinateSpace +{ + if (targetCoordinateSpace == self) // optimization + return [SPRectangle rectangleWithX:0 y:0 width:mVertexCoords[6] height:mVertexCoords[7]]; + + SPMatrix *transformationMatrix = [self transformationMatrixToSpace:targetCoordinateSpace]; + SPPoint *point = [[SPPoint alloc] init]; + + float minX = FLT_MAX, maxX = -FLT_MAX, minY = FLT_MAX, maxY = -FLT_MAX; + for (int i=0; i<4; ++i) + { + point.x = mVertexCoords[2*i]; + point.y = mVertexCoords[2*i+1]; + SPPoint *transformedPoint = [transformationMatrix transformPoint:point]; + float tfX = transformedPoint.x; + float tfY = transformedPoint.y; + minX = MIN(minX, tfX); + maxX = MAX(maxX, tfX); + minY = MIN(minY, tfY); + maxY = MAX(maxY, tfY); + } + [point release]; + return [SPRectangle rectangleWithX:minX y:minY width:maxX-minX height:maxY-minY]; +} + +- (void)setColor:(uint)color ofVertex:(int)vertexID +{ + if (vertexID < 0 || vertexID > 3) + [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"invalid vertex id"]; + + mVertexColors[vertexID] = color; +} + +- (uint)colorOfVertex:(int)vertexID +{ + if (vertexID < 0 || vertexID > 3) + [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"invalid vertex id"]; + + return mVertexColors[vertexID]; +} + +- (void)setColor:(uint)color +{ + for (int i=0; i<4; ++i) [self setColor:color ofVertex:i]; +} + +- (uint)color +{ + return [self colorOfVertex:0]; +} + ++ (SPQuad*)quadWithWidth:(float)width height:(float)height +{ + return [[[SPQuad alloc] initWithWidth:width height:height] autorelease]; +} + ++ (SPQuad*)quadWithWidth:(float)width height:(float)height color:(uint)color +{ + return [[[SPQuad alloc] initWithWidth:width height:height color:color] autorelease]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPRectangle.h b/libs/sparrow/src/Classes/SPRectangle.h new file mode 100644 index 0000000..a5d1b2f --- /dev/null +++ b/libs/sparrow/src/Classes/SPRectangle.h @@ -0,0 +1,77 @@ +// +// SPRectangle.h +// Sparrow +// +// Created by Daniel Sperl on 21.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPPoolObject.h" +#import "SPPoint.h" + +/** The SPRectangle class describes a rectangle by its top-left corner point (x, y) and by + its width and height. */ + +@interface SPRectangle : SPPoolObject +{ + @private + float mX; + float mY; + float mWidth; + float mHeight; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a rectangle with the specified components. _Designated Initializer_. +- (id)initWithX:(float)x y:(float)y width:(float)width height:(float)height; + +/// Factory method. ++ (SPRectangle*)rectangleWithX:(float)x y:(float)y width:(float)width height:(float)height; + +/// ------------- +/// @name Methods +/// ------------- + +/// Determines if a point is within the rectangle. +- (BOOL)containsX:(float)x y:(float)y; + +/// Determines if a point is within the rectangle. +- (BOOL)containsPoint:(SPPoint*)point; + +/// Determines if another rectangle is within the rectangle. +- (BOOL)containsRectangle:(SPRectangle*)rectangle; + +/// Determines if another rectangle contains or intersects the rectangle. +- (BOOL)intersectsRectangle:(SPRectangle*)rectangle; + +/// If the specified rectangle intersects with the rectangle, returns the area of intersection. +- (SPRectangle*)intersectionWithRectangle:(SPRectangle*)rectangle; + +/// Adds two rectangles together to create a new Rectangle object (by filling in the space between +/// the two rectangles). +- (SPRectangle*)uniteWithRectangle:(SPRectangle*)rectangle; + +/// Sets width and height components to zero. +- (void)setEmpty; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// Rectangle component. +@property (nonatomic, assign) float x; +@property (nonatomic, assign) float y; +@property (nonatomic, assign) float width; +@property (nonatomic, assign) float height; + +/// Determines if a rectangle has an empty area. +@property (nonatomic, readonly) BOOL isEmpty; + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPRectangle.m b/libs/sparrow/src/Classes/SPRectangle.m new file mode 100644 index 0000000..0a00da7 --- /dev/null +++ b/libs/sparrow/src/Classes/SPRectangle.m @@ -0,0 +1,143 @@ +// +// SPRectangle.m +// Sparrow +// +// Created by Daniel Sperl on 21.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPRectangle.h" +#import "SPMacros.h" + +@implementation SPRectangle + +@synthesize x = mX; +@synthesize y = mY; +@synthesize width = mWidth; +@synthesize height = mHeight; + +- (id)initWithX:(float)x y:(float)y width:(float)width height:(float)height +{ + if (self = [super init]) + { + mX = x; + mY = y; + mWidth = width; + mHeight = height; + } + + return self; +} + +- (id)init +{ + return [self initWithX:0.0f y:0.0f width:0.0f height:0.0f]; +} + +- (BOOL)containsX:(float)x y:(float)y +{ + return x >= mX && y >= mY && x <= mX + mWidth && y <= mY + mHeight; +} + +- (BOOL)containsPoint:(SPPoint*)point +{ + return [self containsX:point.x y:point.y]; +} + +- (BOOL)containsRectangle:(SPRectangle*)rectangle +{ + float rX = rectangle->mX; + float rY = rectangle->mY; + float rWidth = rectangle->mWidth; + float rHeight = rectangle->mHeight; + + return rX >= mX && rX + rWidth <= mX + mWidth && + rY >= mY && rY + rHeight <= mY + mHeight; +} + +- (BOOL)intersectsRectangle:(SPRectangle*)rectangle +{ + float rX = rectangle->mX; + float rY = rectangle->mY; + float rWidth = rectangle->mWidth; + float rHeight = rectangle->mHeight; + + BOOL outside = + (rX <= mX && rX + rWidth <= mX) || (rX >= mX + mWidth && rX + rWidth >= mX + mWidth) || + (rY <= mY && rY + rHeight <= mY) || (rY >= mY + mHeight && rY + rHeight >= mY + mHeight); + return !outside; +} + +- (SPRectangle*)intersectionWithRectangle:(SPRectangle*)rectangle +{ + float left = MAX(mX, rectangle->mX); + float right = MIN(mX + mWidth, rectangle->mX + rectangle->mWidth); + float top = MAX(mY, rectangle->mY); + float bottom = MIN(mY + mHeight, rectangle->mY + rectangle->mHeight); + + if (left > right || top > bottom) + return [SPRectangle rectangleWithX:0 y:0 width:0 height:0]; + else + return [SPRectangle rectangleWithX:left y:top width:right-left height:bottom-top]; +} + +- (SPRectangle*)uniteWithRectangle:(SPRectangle*)rectangle +{ + float left = MIN(mX, rectangle->mX); + float right = MAX(mX + mWidth, rectangle->mX + rectangle->mWidth); + float top = MIN(mY, rectangle->mY); + float bottom = MAX(mY + mHeight, rectangle->mY + rectangle->mHeight); + return [SPRectangle rectangleWithX:left y:top width:right-left height:bottom-top]; +} + +- (void)setEmpty +{ + mX = mY = mWidth = mHeight = 0; +} + +- (BOOL)isEmpty +{ + return mWidth == 0 || mHeight == 0; +} + +- (BOOL)isEqual:(id)other +{ + if (other == self) return YES; + else if (!other || ![other isKindOfClass:[self class]]) return NO; + else + { + SPRectangle *rect = (SPRectangle*)other; + return SP_IS_FLOAT_EQUAL(mX, rect->mX) && SP_IS_FLOAT_EQUAL(mY, rect->mY) && + SP_IS_FLOAT_EQUAL(mWidth, rect->mWidth) && SP_IS_FLOAT_EQUAL(mHeight, rect->mHeight); + } +} + +- (NSString*)description +{ + return [NSString stringWithFormat:@"(x: %f, y: %f, width: %f, height: %f)", mX, mY, mWidth, mHeight]; +} + ++ (SPRectangle*)rectangleWithX:(float)x y:(float)y width:(float)width height:(float)height +{ + return [[[SPRectangle alloc] initWithX:x y:y width:width height:height] autorelease]; +} + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone*)zone; +{ + return [[[self class] allocWithZone:zone] initWithX:mX y:mY width:mWidth height:mHeight]; +} + +#pragma mark SPPoolObject + ++ (SPPoolInfo *)poolInfo +{ + static SPPoolInfo poolInfo; + return &poolInfo; +} + +@end diff --git a/libs/sparrow/src/Classes/SPRenderSupport.h b/libs/sparrow/src/Classes/SPRenderSupport.h new file mode 100644 index 0000000..e79951f --- /dev/null +++ b/libs/sparrow/src/Classes/SPRenderSupport.h @@ -0,0 +1,69 @@ +// +// SPRenderSupport.h +// Sparrow +// +// Created by Daniel Sperl on 28.09.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +@class SPTexture; +@class SPDisplayObject; + +/** ------------------------------------------------------------------------------------------------ + + A class that contains helper methods simplifying OpenGL rendering. + + An SPRenderSupport instance is passed to any render: method. It saves information about the currently + bound texture, which allows it to avoid unecessary texture switches. + + Furthermore, several static helper methods can be used for different needs whenever some + OpenGL processing is required. + +------------------------------------------------------------------------------------------------- */ + +@interface SPRenderSupport : NSObject +{ + @private + uint mBoundTextureID; + BOOL mPremultipliedAlpha; +} + +/// ------------- +/// @name Methods +/// ------------- + +/// Binds a texture if it is not already bound. Pass `nil` to unbind any texture. +- (void)bindTexture:(SPTexture *)texture; + +/// Converts color and alpha into the format needed by OpenGL. Premultiplies alpha depending on state. +- (uint)convertColor:(uint)color alpha:(float)alpha; + +/// Converts color and alpha into the format needed by OpenGL, optionally premultiplying alpha values. ++ (uint)convertColor:(uint)color alpha:(float)alpha premultiplyAlpha:(BOOL)pma; + +/// Clears OpenGL's color buffer. ++ (void)clearWithColor:(uint)color alpha:(float)alpha; + +/// Transforms OpenGL's matrix into the local coordinate system of the object. ++ (void)transformMatrixForObject:(SPDisplayObject *)object; + +/// Sets up OpenGL's projection matrix for 2D rendering. ++ (void)setupOrthographicRenderingWithLeft:(float)left right:(float)right + bottom:(float)bottom top:(float)top; + +/// Checks for an OpenGL error. If there is one, it is logged an the error code is returned. ++ (uint)checkForOpenGLError; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// Indicates if the bound texture has its alpha channel premultiplied. +@property (nonatomic, readonly) BOOL usingPremultipliedAlpha; + +@end diff --git a/libs/sparrow/src/Classes/SPRenderSupport.m b/libs/sparrow/src/Classes/SPRenderSupport.m new file mode 100644 index 0000000..13103ec --- /dev/null +++ b/libs/sparrow/src/Classes/SPRenderSupport.m @@ -0,0 +1,125 @@ +// +// SPRenderSupport.m +// Sparrow +// +// Created by Daniel Sperl on 28.09.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPRenderSupport.h" +#import "SPDisplayObject.h" +#import "SPTexture.h" +#import "SPMacros.h" + +#import +#import +#import + +@implementation SPRenderSupport + +@synthesize usingPremultipliedAlpha = mPremultipliedAlpha; + +- (id)init +{ + if (self = [super init]) + { + mBoundTextureID = UINT_MAX; + mPremultipliedAlpha = YES; + [self bindTexture:nil]; + } + return self; +} + +- (void)bindTexture:(SPTexture *)texture +{ + uint newTextureID = texture.textureID; + BOOL newPMA = texture.hasPremultipliedAlpha; + + if (newTextureID != mBoundTextureID) + glBindTexture(GL_TEXTURE_2D, newTextureID); + + if (newPMA != mPremultipliedAlpha || !mBoundTextureID) + { + if (newPMA) glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + else glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + mBoundTextureID = newTextureID; + mPremultipliedAlpha = newPMA; +} + +- (uint)convertColor:(uint)color alpha:(float)alpha +{ + return [SPRenderSupport convertColor:color alpha:alpha premultiplyAlpha:mPremultipliedAlpha]; +} + ++ (uint)convertColor:(uint)color alpha:(float)alpha premultiplyAlpha:(BOOL)pma +{ + if (pma) + { + return (GLubyte)(SP_COLOR_PART_RED(color) * alpha) | + (GLubyte)(SP_COLOR_PART_GREEN(color) * alpha) << 8 | + (GLubyte)(SP_COLOR_PART_BLUE(color) * alpha) << 16 | + (GLubyte)(alpha * 255) << 24; + } + else + { + return (GLubyte)SP_COLOR_PART_RED(color) | + (GLubyte)SP_COLOR_PART_GREEN(color) << 8 | + (GLubyte)SP_COLOR_PART_BLUE(color) << 16 | + (GLubyte)(alpha * 255) << 24; + } +} + ++ (void)clearWithColor:(uint)color alpha:(float)alpha; +{ + float red = SP_COLOR_PART_RED(color); + float green = SP_COLOR_PART_GREEN(color); + float blue = SP_COLOR_PART_BLUE(color); + + glClearColor(red, green, blue, alpha); + glClear(GL_COLOR_BUFFER_BIT); +} + ++ (void)transformMatrixForObject:(SPDisplayObject *)object +{ + float x = object.x; + float y = object.y; + float rotation = object.rotation; + float scaleX = object.scaleX; + float scaleY = object.scaleY; + + if (x != 0.0f || y != 0.0f) glTranslatef(x, y, 0); + if (rotation != 0.0f) glRotatef(SP_R2D(rotation), 0.0f, 0.0f, 1.0f); + if (scaleX != 0.0f || scaleY != 0.0f) glScalef(scaleX, scaleY, 1.0f); +} + ++ (void)setupOrthographicRenderingWithLeft:(float)left right:(float)right + bottom:(float)bottom top:(float)top +{ + glDisable(GL_CULL_FACE); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthof(left, right, bottom, top, -1.0f, 1.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + ++ (uint)checkForOpenGLError +{ + GLenum error = glGetError(); + if (error != 0) NSLog(@"Warning: There was an OpenGL error: #%d", error); + return error; +} + +@end diff --git a/libs/sparrow/src/Classes/SPRenderTexture.h b/libs/sparrow/src/Classes/SPRenderTexture.h new file mode 100644 index 0000000..6655c54 --- /dev/null +++ b/libs/sparrow/src/Classes/SPRenderTexture.h @@ -0,0 +1,91 @@ +// +// SPRenderTexture.h +// Sparrow +// +// Created by Daniel Sperl on 04.12.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import +#import +#import + +#import "SPDisplayObject.h" +#import "SPTexture.h" +#import "SPRenderSupport.h" + +typedef void (^SPDrawingBlock)(); + +/** ------------------------------------------------------------------------------------------------ + + An SPRenderTexture is a dynamic texture on which you can draw any display object. + + After creating a render texture, just call the `drawObject:` method to render an object directly + onto the texture. The object will be drawn onto the texture at its current position, adhering + its current rotation, scale and alpha properties. + + Drawing is done very efficiently, as it is happening directly in graphics memory. After you have + drawn objects on the texture, the performance will be just like that of a normal texture - no + matter how many objects you have drawn. + + If you draw lots of objects at once, it is recommended to bundle the drawing calls in a block + via the `bundleDrawCalls:` method, like shown below. That will speed it up immensely, allowing + you to draw hundreds of objects very quickly. + + [renderTexture bundleDrawCalls:^ + { + for (int i=0; i +#import +#import + +@implementation SPStage (Rendering) + +- (void)render:(SPRenderSupport *)support; +{ + [SPRenderSupport clearWithColor:0x0 alpha:1.0f]; + [SPRenderSupport setupOrthographicRenderingWithLeft:0 right:mWidth bottom:mHeight top:0]; + + [super render:support]; + + #if DEBUG + [SPRenderSupport checkForOpenGLError]; + #endif +} + +@end + +@implementation SPDisplayObjectContainer (Rendering) + +- (void)render:(SPRenderSupport *)support; +{ + float alpha = self.alpha; + + for (SPDisplayObject *child in mChildren) + { + float childAlpha = child.alpha; + if (childAlpha != 0.0f && child.visible) + { + glPushMatrix(); + + [SPRenderSupport transformMatrixForObject:child]; + + child.alpha *= alpha; + [child render:support]; + child.alpha = childAlpha; + + glPopMatrix(); + } + } +} + +@end + +@implementation SPQuad (Rendering) + +- (void)render:(SPRenderSupport *)support +{ + static uint colors[4]; + float alpha = self.alpha; + + [support bindTexture:nil]; + + for (int i=0; i<4; ++i) + colors[i] = [support convertColor:mVertexColors[i] alpha:alpha]; + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + glVertexPointer(2, GL_FLOAT, 0, mVertexCoords); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); +} + +@end + +@implementation SPImage (Rendering) + +- (void)render:(SPRenderSupport *)support; +{ + static float texCoords[8]; + static uint colors[4]; + float alpha = self.alpha; + + [support bindTexture:mTexture]; + [mTexture adjustTextureCoordinates:mTexCoords saveAtTarget:texCoords numVertices:4]; + + for (int i=0; i<4; ++i) + colors[i] = [support convertColor:mVertexColors[i] alpha:alpha]; + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + glVertexPointer(2, GL_FLOAT, 0, mVertexCoords); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + // Rendering was tested with vertex buffers, too -- but for simple quads and images like these, + // the overhead seems to outweigh the benefit. The "glDrawArrays"-approach is faster here. +} + +@end diff --git a/libs/sparrow/src/Classes/SPSound.h b/libs/sparrow/src/Classes/SPSound.h new file mode 100644 index 0000000..0e90616 --- /dev/null +++ b/libs/sparrow/src/Classes/SPSound.h @@ -0,0 +1,72 @@ +// +// SPSound.h +// Sparrow +// +// Created by Daniel Sperl on 14.11.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +@class SPSoundChannel; + +/** ------------------------------------------------------------------------------------------------ + + The SPSound class contains audio data that is ready for playback. + + Just as SPTexture contains image data, SPSound contains audio data. It loads audio files in + different formats and keeps them in memory (or streams them if the format supports it). + + You can use SPSound to play a sound directly, but this won't give you much control. + If you want to control the playback and volume of the sound, you you have to create an + SPSoundChannel first (via `createChannel`). + + If you need to play a sound repeatedly, create the SPSound object only once and keep it in + memory. Then call play or create a channel when you want to play the sound. + + Your sounds will automatically be paused when the application + is disrupted (e.g. by a phone call), and will continue playback where they stopped. + + Behind the scenes, the SPSound class will choose the appropriate technology for playback: + uncompressed files will use OpenAL, compressed sound will be handled by Apple’s AVAudioPlayer. + You don’t have to care. + +------------------------------------------------------------------------------------------------- */ + +@interface SPSound : NSObject +{ + @private + NSMutableSet *mPlayingChannels; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a sound +- (id)initWithContentsOfFile:(NSString *)path; + +/// Factory method. ++ (SPSound *)soundWithContentsOfFile:(NSString *)path; + +/// ------------- +/// @name Methods +/// ------------- + +/// Starts playback of the sound. +- (void)play; + +/// Creates an audio channel that gives you more control over playback. Don't forget to retain it! +- (SPSoundChannel *)createChannel; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The duration of the sound in seconds. +@property (nonatomic, readonly) double duration; + +@end diff --git a/libs/sparrow/src/Classes/SPSound.m b/libs/sparrow/src/Classes/SPSound.m new file mode 100644 index 0000000..d137e68 --- /dev/null +++ b/libs/sparrow/src/Classes/SPSound.m @@ -0,0 +1,204 @@ +// +// SPSound.m +// Sparrow +// +// Created by Daniel Sperl on 14.11.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPSound.h" +#import "SPSoundChannel.h" +#import "SPMacros.h" +#import "SPEvent.h" +#import "SPALSound.h" +#import "SPAVSound.h" + +#import + +@implementation SPSound + +- (id)init +{ + if ([self isMemberOfClass:[SPSound class]]) + { + [self release]; + [NSException raise:SP_EXC_ABSTRACT_CLASS + format:@"Attempting to initialize abstract class SPSound."]; + return nil; + } + + return [super init]; +} + +- (id)initWithContentsOfFile:(NSString *)path +{ + // SPSound is a class factory! We'll return a subclass, thus we don't need 'self' anymore. + [self release]; + + NSString *fullPath = [path isAbsolutePath] ? + path : [[NSBundle mainBundle] pathForResource:path ofType:nil]; + + if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath]) + { + [self release]; + [NSException raise:SP_EXC_FILE_NOT_FOUND format:@"file %@ not found", fullPath]; + } + + NSString *error = nil; + + AudioFileID fileID = 0; + void *soundBuffer = NULL; + int soundSize = 0; + int soundChannels = 0; + int soundFrequency = 0; + double soundDuration = 0.0; + + do + { + OSStatus result = noErr; + + result = AudioFileOpenURL((CFURLRef) [NSURL fileURLWithPath:fullPath], + kAudioFileReadPermission, 0, &fileID); + if (result != noErr) + { + error = [NSString stringWithFormat:@"could not read audio file (%x)", result]; + break; + } + + AudioStreamBasicDescription fileFormat; + UInt32 propertySize = sizeof(fileFormat); + result = AudioFileGetProperty(fileID, kAudioFilePropertyDataFormat, &propertySize, &fileFormat); + if (result != noErr) + { + error = [NSString stringWithFormat:@"could not read file format info (%x)", result]; + break; + } + + propertySize = sizeof(soundDuration); + result = AudioFileGetProperty(fileID, kAudioFilePropertyEstimatedDuration, + &propertySize, &soundDuration); + if (result != noErr) + { + error = [NSString stringWithFormat:@"could not read sound duration (%x)", result]; + break; + } + + if (fileFormat.mFormatID != kAudioFormatLinearPCM) + { + error = @"sound file not linear PCM"; + break; + } + + if (fileFormat.mChannelsPerFrame > 2) + { + error = @"more than two channels in sound file"; + break; + } + + if (!TestAudioFormatNativeEndian(fileFormat)) + { + error = @"sounds must be little-endian"; + break; + } + + if (!(fileFormat.mBitsPerChannel == 8 || fileFormat.mBitsPerChannel == 16)) + { + error = @"only files with 8 or 16 bits per channel supported"; + break; + } + + UInt64 fileSize = 0; + propertySize = sizeof(fileSize); + result = AudioFileGetProperty(fileID, kAudioFilePropertyAudioDataByteCount, + &propertySize, &fileSize); + if (result != noErr) + { + error = [NSString stringWithFormat:@"could not read sound file size (%x)", result]; + break; + } + + UInt32 dataSize = (UInt32)fileSize; + soundBuffer = malloc(dataSize); + if (!soundBuffer) + { + error = @"could not allocate memory for sound data"; + break; + } + + result = AudioFileReadBytes(fileID, false, 0, &dataSize, soundBuffer); + if (result == noErr) + { + soundSize = (int) dataSize; + soundChannels = fileFormat.mChannelsPerFrame; + soundFrequency = fileFormat.mSampleRate; + } + else + { + error = [NSString stringWithFormat:@"could not read sound data (%x)", result]; + break; + } + } + while (NO); + + if (fileID) AudioFileClose(fileID); + + if (!error) + { + self = [[SPALSound alloc] initWithData:soundBuffer size:soundSize channels:soundChannels + frequency:soundFrequency duration:soundDuration]; + } + else + { + NSLog(@"Sound '%@' will be played with AVAudioPlayer [Reason: %@]", path, error); + self = [[SPAVSound alloc] initWithContentsOfFile:fullPath duration:soundDuration]; + } + + free(soundBuffer); + return self; +} + +- (void)play +{ + SPSoundChannel *channel = [self createChannel]; + [channel addEventListener:@selector(onSoundCompleted:) atObject:self + forType:SP_EVENT_TYPE_SOUND_COMPLETED]; + [channel play]; + + if (!mPlayingChannels) mPlayingChannels = [[NSMutableSet alloc] init]; + [mPlayingChannels addObject:channel]; +} + +- (void)onSoundCompleted:(SPEvent *)event +{ + SPSoundChannel *channel = (SPSoundChannel *)event.target; + [channel stop]; + [mPlayingChannels removeObject:channel]; +} + +- (SPSoundChannel *)createChannel +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return nil; +} + +- (double)duration +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return 0.0; +} + ++ (SPSound *)soundWithContentsOfFile:(NSString *)path +{ + return [[[SPSound alloc] initWithContentsOfFile:path] autorelease]; +} + +- (void)dealloc +{ + [mPlayingChannels release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPSoundChannel.h b/libs/sparrow/src/Classes/SPSoundChannel.h new file mode 100644 index 0000000..d1b1d83 --- /dev/null +++ b/libs/sparrow/src/Classes/SPSoundChannel.h @@ -0,0 +1,67 @@ +// +// SPSoundChannel.h +// Sparrow +// +// Created by Daniel Sperl on 14.11.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPSound.h" +#import "SPEventDispatcher.h" + +#define SP_EVENT_TYPE_SOUND_COMPLETED @"soundCompleted" + +/** ------------------------------------------------------------------------------------------------ + + An SPSoundChannel represents an audio source. Use this class to control sound playback. + + Sound channels are created with the method [SPSound createChannel]. They allow control over + playback (`play`, `pause`, `stop`) and properties as the volume or if the sound should loop. + + Furthermore, it will dispatch events of type `SP_EVENT_TYPE_SOUND_COMPLETED` when the sound + is finished. + +------------------------------------------------------------------------------------------------- */ + +@interface SPSoundChannel : SPEventDispatcher + +/// ------------- +/// @name Methods +/// ------------- + +/// Starts playback. +- (void)play; + +/// Stops playback. Sound will start from the beginning on `play`. +- (void)stop; + +/// Pauses the sound. Call `play` again to continue. +- (void)pause; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// Indicates if the sound is currently playing. +@property (nonatomic, readonly) BOOL isPlaying; + +/// Indicates if the sound is currently paused. +@property (nonatomic, readonly) BOOL isPaused; + +/// Indicates if the sound was stopped. +@property (nonatomic, readonly) BOOL isStopped; + +/// The duration of the sound in seconds. +@property (nonatomic, readonly) double duration; + +/// The volume of the sound. Range: [0.0 - 1.0] +@property (nonatomic, assign) float volume; + +/// Indicates if the sound should loop. Looping sounds don't dispatch SOUND_COMPLETED events. +@property (nonatomic, assign) BOOL loop; + +@end diff --git a/libs/sparrow/src/Classes/SPSoundChannel.m b/libs/sparrow/src/Classes/SPSoundChannel.m new file mode 100644 index 0000000..8965284 --- /dev/null +++ b/libs/sparrow/src/Classes/SPSoundChannel.m @@ -0,0 +1,91 @@ +// +// SPSoundChannel.m +// Sparrow +// +// Created by Daniel Sperl on 14.11.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPSoundChannel.h" +#import "SPMacros.h" + +@implementation SPSoundChannel + +- (id)init +{ + if ([self isMemberOfClass:[SPSoundChannel class]]) + { + [self release]; + [NSException raise:SP_EXC_ABSTRACT_CLASS + format:@"Attempting to initialize abstract class SPSoundChannel."]; + return nil; + } + + return [super init]; +} + +- (void)play +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; +} + +- (void)pause +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; +} + +- (void)stop +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; +} + +- (BOOL)isPlaying +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return NO; +} + +- (BOOL)isPaused +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return NO; +} + +- (BOOL)isStopped +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return NO; +} + +- (BOOL)loop +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return NO; +} + +- (void)setLoop:(BOOL)value +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; +} + +- (float)volume +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return 1.0f; +} + +- (void)setVolume:(float)value +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; +} + +- (double)duration +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return 0.0; +} + +@end diff --git a/libs/sparrow/src/Classes/SPSprite.h b/libs/sparrow/src/Classes/SPSprite.h new file mode 100644 index 0000000..643748c --- /dev/null +++ b/libs/sparrow/src/Classes/SPSprite.h @@ -0,0 +1,48 @@ +// +// SPSprite.h +// Sparrow +// +// Created by Daniel Sperl on 21.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPDisplayObjectContainer.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPSprite is the most lightweight, non-abstract container class. + + Use it as a simple means of grouping objects together in one coordinate system. + + SPSprite *sprite = [SPSprite sprite]; + + // create children + SPImage *venus = [SPImage imageWithContentsOfFile:@"venus.png"]; + SPImage *mars = [SPImage imageWithContentsOfFile:@"mars.png"]; + + // move children to some relative positions + venus.x = 50; + mars.x = -20; + + // add children to the sprite + [sprite addChild:venus]; + [sprite addChild:mars]; + + // calculate total width of all children + float totalWidth = sprite.width; + + // rotate the whole group + sprite.rotation = PI; + +------------------------------------------------------------------------------------------------- */ + +@interface SPSprite : SPDisplayObjectContainer + +/// Create a new, empty sprite. ++ (SPSprite*)sprite; + +@end diff --git a/libs/sparrow/src/Classes/SPSprite.m b/libs/sparrow/src/Classes/SPSprite.m new file mode 100644 index 0000000..0e1c317 --- /dev/null +++ b/libs/sparrow/src/Classes/SPSprite.m @@ -0,0 +1,22 @@ +// +// SPSprite.m +// Sparrow +// +// Created by Daniel Sperl on 21.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPSprite.h" + + +@implementation SPSprite + ++ (SPSprite*)sprite +{ + return [[[SPSprite alloc] init] autorelease]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPStage.h b/libs/sparrow/src/Classes/SPStage.h new file mode 100644 index 0000000..efade55 --- /dev/null +++ b/libs/sparrow/src/Classes/SPStage.h @@ -0,0 +1,97 @@ +// +// SPStage.h +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPDisplayObjectContainer.h" + +@class SPTouchProcessor; +@class SPJuggler; + +/** ------------------------------------------------------------------------------------------------ + + An SPStage is the root of the display tree. It represents the rendering area of the application. + + To create a Sparrow application, you create a class that inherits from SPStage and populates + the display tree. + + The stage allows you to access the native view object it is drawing its content to. (Currently, + this is always an SPView). Furthermore, you can change the framerate in which the contents is + rendered. + + A stage also contains a default juggler which you can use for your animations. It is advanced + automatically once per frame. You can access this juggler from any display object by calling + + self.stage.juggler + + You have to take care, however, that the display object you are making this call from is already + connected to the stage - otherwise, the method will return `nil`, and you won't have access to + the juggler. + +------------------------------------------------------------------------------------------------- */ + +@interface SPStage : SPDisplayObjectContainer +{ + @private + float mWidth; + float mHeight; + + // helpers + SPTouchProcessor *mTouchProcessor; + SPJuggler *mJuggler; + + id mNativeView; +} + +/// -------------------- +/// @name Initialization +/// -------------------- + +/// Initializes a stage with a certain size in points. +- (id)initWithWidth:(float)width height:(float)height; + +/// Dispatches an enter frame event on all children and advances the juggler. +- (void)advanceTime:(double)seconds; + +/// Process a new set up touches. Dispatches touch events on affected children. +- (void)processTouches:(NSSet*)touches; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The requested number of frames per second. Must be a divisor of 60 (like 30, 20, 15, 12, 10, etc.). +/// The actual frame rate might be lower if there is too much to process. +@property (nonatomic, assign) float frameRate; + +/// A juggler that is automatically advanced once per frame. +@property (nonatomic, readonly) SPJuggler *juggler; + +/// The native view the stage is connected to. Normally an SPView. +@property (nonatomic, readonly) id nativeView; + +@end + + +@interface SPStage (HDSupport) + +/// Enables support for high resolutions (aka retina displays). ++ (void)setSupportHighResolutions:(BOOL)value; + +/// Determines if high resolution support is activated. ++ (BOOL)supportHighResolutions; + +/// Sets the content scale factor, which determines the relationship between points and pixels. ++ (void)setContentScaleFactor:(float)value; + +/// The current content scale factor. ++ (float)contentScaleFactor; + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPStage.m b/libs/sparrow/src/Classes/SPStage.m new file mode 100644 index 0000000..f767c3e --- /dev/null +++ b/libs/sparrow/src/Classes/SPStage.m @@ -0,0 +1,234 @@ +// +// SPStage.m +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPStage.h" +#import "SPStage_Internal.h" +#import "SPDisplayObject_Internal.h" +#import "SPMacros.h" +#import "SPEnterFrameEvent.h" +#import "SPTouchProcessor.h" +#import "SPJuggler.h" + +#import + +// --- static members ------------------------------------------------------------------------------ + +static BOOL supportHighResolutions = NO; +static float contentScaleFactor = -1; +static NSMutableArray *stages = NULL; + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPStage + +@synthesize width = mWidth; +@synthesize height = mHeight; +@synthesize juggler = mJuggler; +@synthesize nativeView = mNativeView; + +- (id)initWithWidth:(float)width height:(float)height +{ + if (self = [super init]) + { + // Save existing stages to have access to them in "SPStage setSupportHighResolutions:". + // We use a CFArray to avoid that 'self' is retained -> that would cause a memory leak! + if (!stages) stages = (NSMutableArray *)CFArrayCreateMutable(NULL, 0, NULL); + [stages addObject:self]; + + mWidth = width; + mHeight = height; + mTouchProcessor = [[SPTouchProcessor alloc] initWithRoot:self]; + mJuggler = [[SPJuggler alloc] init]; + } + return self; +} + +- (id)init +{ + CGSize screenSize = [UIScreen mainScreen].bounds.size; + return [self initWithWidth:screenSize.width height:screenSize.height]; +} + +- (void)advanceTime:(double)seconds +{ + // advance juggler + [mJuggler advanceTime:seconds]; + + // dispatch EnterFrameEvent + SPEnterFrameEvent *enterFrameEvent = [[SPEnterFrameEvent alloc] + initWithType:SP_EVENT_TYPE_ENTER_FRAME passedTime:seconds]; + [self dispatchEventOnChildren:enterFrameEvent]; + [enterFrameEvent release]; +} + +- (void)processTouches:(NSSet*)touches +{ + [mTouchProcessor processTouches:touches]; +} + +- (SPDisplayObject*)hitTestPoint:(SPPoint*)localPoint forTouch:(BOOL)isTouch; +{ + if (isTouch && (!self.visible || !self.touchable)) + return nil; + + SPDisplayObject *target = [super hitTestPoint:localPoint forTouch:isTouch]; + + // different to other containers, the stage should acknowledge touches even in empty parts. + if (!target) + { + SPRectangle *bounds = [SPRectangle rectangleWithX:self.x y:self.y + width:self.width height:self.height]; + if ([bounds containsPoint:localPoint]) + target = self; + } + return target; +} + +- (float)width +{ + return mWidth; +} + +- (void)setWidth:(float)width +{ + [NSException raise:SP_EXC_INVALID_OPERATION format:@"cannot set width of stage"]; +} + +- (float)height +{ + return mHeight; +} + +- (void)setHeight:(float)height +{ + [NSException raise:SP_EXC_INVALID_OPERATION format:@"cannot set height of stage"]; +} + +- (void)setX:(float)value +{ + [NSException raise:SP_EXC_INVALID_OPERATION format:@"cannot set x-coordinate of stage"]; +} + +- (void)setY:(float)value +{ + [NSException raise:SP_EXC_INVALID_OPERATION format:@"cannot set y-coordinate of stage"]; +} + +- (void)setScaleX:(float)value +{ + [NSException raise:SP_EXC_INVALID_OPERATION format:@"cannot scale stage"]; +} + +- (void)setScaleY:(float)value +{ + [NSException raise:SP_EXC_INVALID_OPERATION format:@"cannot scale stage"]; +} + +- (void)setRotation:(float)value +{ + [NSException raise:SP_EXC_INVALID_OPERATION format:@"cannot rotate stage"]; +} + +- (void)setFrameRate:(float)value +{ + [mNativeView setFrameRate:value]; +} + +- (float)frameRate +{ + return [mNativeView frameRate]; +} + +- (void)dealloc +{ + [SPPoint purgePool]; + [SPRectangle purgePool]; + [SPMatrix purgePool]; + + [mTouchProcessor release]; + [mJuggler release]; + + [stages removeObject:self]; + if (stages.count == 0) { [stages release]; stages = NULL; } + + [super dealloc]; +} + +@end + +// ------------------------------------------------------------------------------------------------- + +@implementation SPStage (HDSupport) + ++ (void)updateNativeViews +{ + for (SPStage *stage in stages) + { + if ([stage.nativeView respondsToSelector:@selector(contentScaleFactor)]) + { + [stage.nativeView setContentScaleFactor:[SPStage contentScaleFactor]]; + [stage.nativeView layoutSubviews]; + } + } +} + ++ (void)setSupportHighResolutions:(BOOL)value +{ + if (value != supportHighResolutions) + { + supportHighResolutions = value; + [SPStage updateNativeViews]; + } +} + ++ (BOOL)supportHighResolutions +{ + return supportHighResolutions; +} + ++ (void)setContentScaleFactor:(float)value +{ + if (value != contentScaleFactor) + { + contentScaleFactor = value; + [SPStage updateNativeViews]; + } +} + ++ (float)contentScaleFactor +{ + if (supportHighResolutions && + [UIScreen instancesRespondToSelector:@selector(scale)] && + [UIImage instancesRespondToSelector:@selector(scale)]) + { + return contentScaleFactor == -1 ? [[UIScreen mainScreen] scale] : contentScaleFactor; + } + else + { + return 1.0f; + } +} + +@end + +// ------------------------------------------------------------------------------------------------- + +@implementation SPStage (Internal) + +- (void)setNativeView:(id)nativeView +{ + if ([nativeView respondsToSelector:@selector(setContentScaleFactor:)]) + [nativeView setContentScaleFactor:[SPStage contentScaleFactor]]; + + mNativeView = nativeView; +} + +@end diff --git a/libs/sparrow/src/Classes/SPStage_Internal.h b/libs/sparrow/src/Classes/SPStage_Internal.h new file mode 100644 index 0000000..20c89cb --- /dev/null +++ b/libs/sparrow/src/Classes/SPStage_Internal.h @@ -0,0 +1,19 @@ +// +// SPStage_Internal.h +// Sparrow +// +// Created by Daniel Sperl on 30.08.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPStage.h" + +@interface SPStage (Internal) + +- (void)setNativeView:(id)nativeView; + +@end diff --git a/libs/sparrow/src/Classes/SPSubTexture.h b/libs/sparrow/src/Classes/SPSubTexture.h new file mode 100644 index 0000000..36106c9 --- /dev/null +++ b/libs/sparrow/src/Classes/SPSubTexture.h @@ -0,0 +1,52 @@ +// +// SPSubTexture.h +// Sparrow +// +// Created by Daniel Sperl on 27.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPTexture.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPSubTexture represents a section of another texture. This is achived solely by + manipulation of texture coordinates, making the class very efficient. + + It is allowed to create subtextures of subtextures. + +------------------------------------------------------------------------------------------------- */ + +@interface SPSubTexture : SPTexture +{ + @private + SPTexture *mBaseTexture; + SPRectangle *mClipping; + SPRectangle *mRootClipping; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a subtexture with a region (in points) of another texture. +- (id)initWithRegion:(SPRectangle*)region ofTexture:(SPTexture*)texture; + +/// Factory method. ++ (SPSubTexture*)textureWithRegion:(SPRectangle*)region ofTexture:(SPTexture*)texture; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The texture which the subtexture is based on. +@property (nonatomic, readonly) SPTexture *baseTexture; + +/// The clipping rectangle, which is the region provided on initialization, scaled into [0.0, 1.0]. +@property (nonatomic, copy) SPRectangle *clipping; + +@end diff --git a/libs/sparrow/src/Classes/SPSubTexture.m b/libs/sparrow/src/Classes/SPSubTexture.m new file mode 100644 index 0000000..b5478f1 --- /dev/null +++ b/libs/sparrow/src/Classes/SPSubTexture.m @@ -0,0 +1,118 @@ +// +// SPSubTexture.m +// Sparrow +// +// Created by Daniel Sperl on 27.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPSubTexture.h" +#import "SPRectangle.h" + +@implementation SPSubTexture + +@synthesize baseTexture = mBaseTexture; +@synthesize clipping = mClipping; + +- (id)initWithRegion:(SPRectangle*)region ofTexture:(SPTexture*)texture +{ + if (self = [super init]) + { + mBaseTexture = [texture retain]; + + // convert region to clipping rectangle (which has values between 0 and 1) + self.clipping = [SPRectangle rectangleWithX:region.x/texture.width + y:region.y/texture.height + width:region.width/texture.width + height:region.height/texture.height]; + } + return self; +} + +- (id)init +{ + [self release]; + return nil; +} + +- (void)setClipping:(SPRectangle *)clipping +{ + [mClipping release]; + mClipping = [clipping copy]; + + // if the base texture is a sub texture as well, calculate clipping + // in reference to the root texture + [mRootClipping release]; + mRootClipping = [mClipping copy]; + SPTexture *baseTexture = mBaseTexture; + while ([baseTexture isKindOfClass:[SPSubTexture class]]) + { + SPSubTexture *baseSubTexture = (SPSubTexture *)baseTexture; + SPRectangle *baseClipping = baseSubTexture->mClipping; + + mRootClipping.x = baseClipping.x + mRootClipping.x * baseClipping.width; + mRootClipping.y = baseClipping.y + mRootClipping.y * baseClipping.height; + mRootClipping.width *= baseClipping.width; + mRootClipping.height *= baseClipping.height; + + baseTexture = baseSubTexture.baseTexture; + } +} + +- (void)adjustTextureCoordinates:(const float *)texCoords saveAtTarget:(float *)targetTexCoords + numVertices:(int)numVertices +{ + float clipX = mRootClipping.x; + float clipY = mRootClipping.y; + float clipWidth = mRootClipping.width; + float clipHeight = mRootClipping.height; + + for (int i=0; i +#import "SPDisplayObjectContainer.h" +#import "SPMacros.h" + +@class SPTexture; +@class SPQuad; + +#define SP_DEFAULT_FONT_NAME @"Helvetica" +#define SP_DEFAULT_FONT_SIZE 14.0f +#define SP_DEFAULT_FONT_COLOR SP_BLACK + +#define SP_NATIVE_FONT_SIZE -1.0f + +/// Horizontal Alignment +typedef enum +{ + SPHAlignLeft = 0, + SPHAlignCenter, + SPHAlignRight +} SPHAlign; + +/// Vertical Alignment +typedef enum +{ + SPVAlignTop = 0, + SPVAlignCenter, + SPVAlignBottom +} SPVAlign; + +/** ------------------------------------------------------------------------------------------------ + + An SPTextField displays text, either using standard iOS fonts or a custom bitmap font. + + You can set all properties you are used to, like the font name and size, a color, the horizontal + and vertical alignment, etc. The border property is helpful during development, because it lets + you see the bounds of the textfield. + + There are two types of fonts that can be displayed: + + * Standard iOS fonts. This renders the text with standard iOS fonts like Verdana or Arial. Use this + method if you want to keep it simple, and if the text changes not too often. Simply pass the + font name to the corresponding property. + * Bitmap fonts. If you need speed or fancy font effects, use a bitmap font instead. That is a + font that has its glyphs rendered to a texture atlas. To use it, first register the font with + the method registerBitmapFontFromFile:, and then pass the font name to the corresponding + property of the text field. + + For the latter, we recommend the Angel Code Bitmap Font Generator. Export the font data as an XML + file and the texture as a png with white characters on a transparent background (32 bit). + + -> http://www.angelcode.com/products/bmfont/ + + Here is a sample with a standard font: + + SPTextField *textField = [SPTextField textFieldWithWidth:300 height:100 text:@"Hello world!"]; + textField.hAlign = SPHAlignCenter; + textField.vAlign = SPVAlignCenter; + textField.fontSize = 18; + textField.fontName = @"Georgia-Bold"; + + And now we use a bitmap font: + + // Register the font; the returned font name is the one that is defined in the font XML. + NSString *fontName = [SPTextField registerBitmapFontFromFile:@"bitmap_font.fnt"]; + + SPTextField *textField = [SPTextField textFieldWithWidth:300 height:100 text:@"Hello world!"]; + textField.fontName = fontName; + +------------------------------------------------------------------------------------------------- */ + +@interface SPTextField : SPDisplayObjectContainer +{ + @private + float mFontSize; + uint mColor; + NSString *mText; + NSString *mFontName; + SPHAlign mHAlign; + SPVAlign mVAlign; + BOOL mBorder; + BOOL mRequiresRedraw; + BOOL mIsRenderedText; + + SPQuad *mHitArea; + SPQuad *mTextArea; + SPDisplayObject *mContents; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initialize a text field with all important font properties. _Designated Initializer_. +- (id)initWithWidth:(float)width height:(float)height text:(NSString*)text fontName:(NSString*)name + fontSize:(float)size color:(uint)color; + +/// Initialize a text field with default settings (Helvetica, 14pt, black). +- (id)initWithWidth:(float)width height:(float)height text:(NSString*)text; + +/// Initialize a 128x128 textField (Helvetica, 14pt, black). +- (id)initWithText:(NSString *)text; + +/// Factory method. ++ (SPTextField *)textFieldWithWidth:(float)width height:(float)height text:(NSString*)text + fontName:(NSString*)name fontSize:(float)size color:(uint)color; + +/// Factory method. ++ (SPTextField *)textFieldWithWidth:(float)width height:(float)height text:(NSString*)text; + +/// Factory method. ++ (SPTextField *)textFieldWithText:(NSString *)text; + +/// ------------- +/// @name Methods +/// ------------- + +/// Makes a bitmap font available at any text field, manually providing the texture. +/// +/// @return The name of the font as defined in the font XML. ++ (NSString *)registerBitmapFontFromFile:(NSString*)path texture:(SPTexture *)texture; + +/// Makes a bitmap font available at any text field, using the texture defined in the file. +/// +/// @return The name of the font as defined in the font XML. ++ (NSString *)registerBitmapFontFromFile:(NSString*)path; + +/// Releases the bitmap font. ++ (void)unregisterBitmapFont:(NSString *)name; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The displayed text. +@property (nonatomic, copy) NSString *text; + +/// The name of the font. +@property (nonatomic, copy) NSString *fontName; + +/// The size of the font. For bitmap fonts, use `SP_NATIVE_FONT_SIZE` for the original size. +@property (nonatomic, assign) float fontSize; + +/// The horizontal alignment of the text. +@property (nonatomic, assign) SPHAlign hAlign; + +/// The vertical alignment of the text. +@property (nonatomic, assign) SPVAlign vAlign; + +/// Allows displaying a border around the edges of the text field. Useful for visual debugging. +@property (nonatomic, assign) BOOL border; + +/// The color of the text. +@property (nonatomic, assign) uint color; + +/// The bounds of the actual characters inside the text field. +@property (nonatomic, readonly) SPRectangle *textBounds; + +@end diff --git a/libs/sparrow/src/Classes/SPTextField.m b/libs/sparrow/src/Classes/SPTextField.m new file mode 100644 index 0000000..482f399 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTextField.m @@ -0,0 +1,329 @@ +// +// SPTextField.m +// Sparrow +// +// Created by Daniel Sperl on 29.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPTextField.h" +#import "SPImage.h" +#import "SPTexture.h" +#import "SPSubTexture.h" +#import "SPGLTexture.h" +#import "SPEnterFrameEvent.h" +#import "SPQuad.h" +#import "SPBitmapFont.h" +#import "SPStage.h" + +#import + +static NSMutableDictionary *bitmapFonts = nil; + +// --- private interface --------------------------------------------------------------------------- + +@interface SPTextField() + +- (void)redrawContents; +- (SPDisplayObject *)createRenderedContents; +- (SPDisplayObject *)createComposedContents; + +@end + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPTextField + +@synthesize text = mText; +@synthesize fontName = mFontName; +@synthesize fontSize = mFontSize; +@synthesize hAlign = mHAlign; +@synthesize vAlign = mVAlign; +@synthesize border = mBorder; +@synthesize color = mColor; + +- (id)initWithWidth:(float)width height:(float)height text:(NSString*)text fontName:(NSString*)name + fontSize:(float)size color:(uint)color +{ + if (self = [super init]) + { + mText = [text copy]; + mFontSize = size; + mColor = color; + mHAlign = SPHAlignCenter; + mVAlign = SPVAlignCenter; + mBorder = NO; + self.fontName = name; + + mHitArea = [[SPQuad alloc] initWithWidth:width height:height]; + mHitArea.alpha = 0.0f; + [self addChild:mHitArea]; + [mHitArea release]; + + mTextArea = [[SPQuad alloc] initWithWidth:width height:height]; + mTextArea.visible = NO; + [self addChild:mTextArea]; + [mTextArea release]; + + mRequiresRedraw = YES; + } + return self; +} + +- (id)initWithWidth:(float)width height:(float)height text:(NSString*)text; +{ + return [self initWithWidth:width height:height text:text fontName:SP_DEFAULT_FONT_NAME + fontSize:SP_DEFAULT_FONT_SIZE color:SP_DEFAULT_FONT_COLOR]; +} + +- (id)initWithWidth:(float)width height:(float)height +{ + return [self initWithWidth:width height:height text:@""]; +} + +- (id)initWithText:(NSString *)text +{ + return [self initWithWidth:128 height:128 text:text]; +} + +- (id)init +{ + return [self initWithText:@""]; +} + +- (void)render:(SPRenderSupport *)support +{ + if (mRequiresRedraw) [self redrawContents]; + [super render:support]; +} + +- (void)redrawContents +{ + [mContents removeFromParent]; + + mContents = mIsRenderedText ? [self createRenderedContents] : [self createComposedContents]; + mContents.touchable = NO; + mRequiresRedraw = NO; + + [self addChild:mContents]; +} + +- (SPDisplayObject *)createRenderedContents +{ + float width = mHitArea.width; + float height = mHitArea.height; + float fontSize = mFontSize == SP_NATIVE_FONT_SIZE ? SP_DEFAULT_FONT_SIZE : mFontSize; + + UILineBreakMode lbm = UILineBreakModeTailTruncation; + CGSize textSize = [mText sizeWithFont:[UIFont fontWithName:mFontName size:fontSize] + constrainedToSize:CGSizeMake(width, height) lineBreakMode:lbm]; + + float xOffset = 0; + if (mHAlign == SPHAlignCenter) xOffset = (width - textSize.width) / 2.0f; + else if (mHAlign == SPHAlignRight) xOffset = width - textSize.width; + + float yOffset = 0; + if (mVAlign == SPVAlignCenter) yOffset = (height - textSize.height) / 2.0f; + else if (mVAlign == SPVAlignBottom) yOffset = height - textSize.height; + + mTextArea.x = xOffset; + mTextArea.y = yOffset; + mTextArea.width = textSize.width; + mTextArea.height = textSize.height; + + SPTexture *texture = [[SPTexture alloc] initWithWidth:width height:height + scale:[SPStage contentScaleFactor] + colorSpace:SPColorSpaceAlpha + draw:^(CGContextRef context) + { + if (mBorder) + { + CGContextSetGrayStrokeColor(context, 1.0f, 1.0f); + CGContextSetLineWidth(context, 1.0f); + CGContextStrokeRect(context, CGRectMake(0.5f, 0.5f, width-1, height-1)); + } + + CGContextSetGrayFillColor(context, 1.0f, 1.0f); + + [mText drawInRect:CGRectMake(0, yOffset, width, height) + withFont:[UIFont fontWithName:mFontName size:fontSize] + lineBreakMode:lbm alignment:mHAlign]; + }]; + + SPImage *image = [SPImage imageWithTexture:texture]; + image.color = mColor; + [texture release]; + + return image; +} + +- (SPDisplayObject *)createComposedContents +{ + SPBitmapFont *bitmapFont = [bitmapFonts objectForKey:mFontName]; + if (!bitmapFont) + [NSException raise:SP_EXC_INVALID_OPERATION + format:@"bitmap font %@ not registered!", mFontName]; + + SPDisplayObject *contents = [bitmapFont createDisplayObjectWithWidth:mHitArea.width + height:mHitArea.height text:mText fontSize:mFontSize color:mColor + hAlign:mHAlign vAlign:mVAlign border:mBorder]; + + SPRectangle *textBounds = [(SPDisplayObjectContainer *)contents childAtIndex:0].bounds; + mTextArea.x = textBounds.x; mTextArea.y = textBounds.y; + mTextArea.width = textBounds.width; mTextArea.height = textBounds.height; + + return contents; +} + +- (SPRectangle *)textBounds +{ + if (mRequiresRedraw) [self redrawContents]; + return [mTextArea boundsInSpace:self.parent]; +} + +- (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetCoordinateSpace +{ + return [mHitArea boundsInSpace:targetCoordinateSpace]; +} + +- (void)setWidth:(float)width +{ + // other than in SPDisplayObject, changing the size of the object should not change the scaling; + // changing the size should just make the texture bigger/smaller, + // keeping the size of the text/font unchanged. (this applies to setHeight:, as well.) + + mHitArea.width = width; + mRequiresRedraw = YES; +} + +- (void)setHeight:(float)height +{ + mHitArea.height = height; + mRequiresRedraw = YES; +} + +- (void)setText:(NSString *)text +{ + if (![text isEqualToString:mText]) + { + [mText release]; + mText = [text copy]; + mRequiresRedraw = YES; + } +} + +- (void)setFontName:(NSString *)fontName +{ + if (![fontName isEqualToString:mFontName]) + { + [mFontName release]; + mFontName = [fontName copy]; + mRequiresRedraw = YES; + mIsRenderedText = ![bitmapFonts objectForKey:mFontName]; + } +} + +- (void)setFontSize:(float)fontSize +{ + if (fontSize != mFontSize) + { + mFontSize = fontSize; + mRequiresRedraw = YES; + } +} + +- (void)setBorder:(BOOL)border +{ + if (border != mBorder) + { + mBorder = border; + mRequiresRedraw = YES; + } +} + +- (void)setHAlign:(SPHAlign)hAlign +{ + if (hAlign != mHAlign) + { + mHAlign = hAlign; + mRequiresRedraw = YES; + } +} + +- (void)setVAlign:(SPVAlign)vAlign +{ + if (vAlign != mVAlign) + { + mVAlign = vAlign; + mRequiresRedraw = YES; + } +} + +- (void)setColor:(uint)color +{ + if (color != mColor) + { + mColor = color; + if (mIsRenderedText) + [(SPImage *)mContents setColor:color]; + else + mRequiresRedraw = YES; + } +} + ++ (SPTextField*)textFieldWithWidth:(float)width height:(float)height text:(NSString*)text + fontName:(NSString*)name fontSize:(float)size color:(uint)color +{ + return [[[SPTextField alloc] initWithWidth:width height:height text:text fontName:name + fontSize:size color:color] autorelease]; +} + ++ (SPTextField*)textFieldWithWidth:(float)width height:(float)height text:(NSString*)text +{ + return [[[SPTextField alloc] initWithWidth:width height:height text:text] autorelease]; +} + ++ (SPTextField*)textFieldWithText:(NSString*)text +{ + return [[[SPTextField alloc] initWithText:text] autorelease]; +} + ++ (NSString *)registerBitmapFontFromFile:(NSString*)path texture:(SPTexture *)texture +{ + if (!bitmapFonts) bitmapFonts = [[NSMutableDictionary alloc] init]; + + SPBitmapFont *bitmapFont = [[SPBitmapFont alloc] initWithContentsOfFile:path texture:texture]; + NSString *fontName = bitmapFont.name; + [bitmapFonts setObject:bitmapFont forKey:fontName]; + [bitmapFont release]; + + return fontName; +} + ++ (NSString *)registerBitmapFontFromFile:(NSString *)path +{ + return [SPTextField registerBitmapFontFromFile:path texture:nil]; +} + ++ (void)unregisterBitmapFont:(NSString *)name +{ + [bitmapFonts removeObjectForKey:name]; + + if (bitmapFonts.count == 0) + { + [bitmapFonts release]; + bitmapFonts = nil; + } +} + +- (void)dealloc +{ + [mText release]; + [mFontName release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPTexture.h b/libs/sparrow/src/Classes/SPTexture.h new file mode 100644 index 0000000..d570053 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTexture.h @@ -0,0 +1,133 @@ +// +// SPTexture.h +// Sparrow +// +// Created by Daniel Sperl on 19.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import +#import + +@class SPRectangle; + +typedef enum +{ + SPColorSpaceRGBA, + SPColorSpaceAlpha +} SPColorSpace; + +typedef void (^SPTextureDrawingBlock)(CGContextRef context); + +/** ------------------------------------------------------------------------------------------------ + + A texture stores the information that represents an image. It cannot be displayed directly, + but has to be mapped onto a display object. In Sparrow, that display object is `SPImage`. + + *Image formats* + + Sparrow supports different file formats for textures. The most common formats are `PNG`, which + contains an alpha channel, and `JPG` (without an alpha channel). You can also load files in + the `PVR` format (compressed or uncompressed). That's a special format of the graphics chip of + iOS devices that is very efficient. + + *HD textures* + + Furthermore, Sparrow supports development in multiple resolutions, i.e. creating a game + simultaneously for normal and retina displays. If HD texture support is activated (via + `[SPStage setSupportHighResolutions:]`) and you load a texture like this: + + SPTexture *texture = [[SPTexture alloc] initWithContentsOfFile:@"image.png"]; + + Sparrow will check if it finds the file `"image@2x.png"`, and will load that instead (provided that + it is available and the application is running on a HD device). The texture object will then + return values for width and height that are the original number of pixels divided by 2 + (by setting their scale-factor to `2.0`). That way, you will always work with the same values + for width and height - regardless of the device type. + + *Drawing API* + + Sparrow lets you create custom graphics directly at run-time by using the `Core Graphics` API. + You access the drawing API with a special init-method of SPTexture, which takes a `block`-parameter + you can fill with your drawing code. + + SPTexture *customTexture = [[SPTexture alloc] initWithWidth:200 height:100 + draw:^(CGContextRef context) + { + // draw a string + CGContextSetGrayFillColor(context, 1.0f, 1.0f); + NSString *string = @"Hello Core Graphics"; + [string drawAtPoint:CGPointMake(20.0f, 20.0f) + withFont:[UIFont fontWithName:@"Arial" size:25]]; + }]; + +------------------------------------------------------------------------------------------------- */ + +@interface SPTexture : NSObject + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a texture with a certain size (in points) and a block containing Core Graphics commands. +/// The texture will have the current scale factor of the stage and an RGBA color space. +- (id)initWithWidth:(float)width height:(float)height draw:(SPTextureDrawingBlock)drawingBlock; + +/// Initializes a texture with size and color properties, as well as a block containing +/// Core Graphics commands. +- (id)initWithWidth:(float)width height:(float)height scale:(float)scale + colorSpace:(SPColorSpace)colorSpace draw:(SPTextureDrawingBlock)drawingBlock; + +/// Initializes a texture with the contents of a file. Recommended formats: png, jpg, pvr. +- (id)initWithContentsOfFile:(NSString *)path; + +/// Initializes a texture with the contents of a UIImage. +- (id)initWithContentsOfImage:(UIImage *)image; + +/// Initializes a texture with a region (in points) of another texture. +- (id)initWithRegion:(SPRectangle*)region ofTexture:(SPTexture*)texture; + +/// Factory method. ++ (SPTexture *)textureWithContentsOfFile:(NSString*)path; + +/// Factory method. ++ (SPTexture *)textureWithRegion:(SPRectangle *)region ofTexture:(SPTexture *)texture; + +/// Factory method. ++ (SPTexture *)textureWithWidth:(float)width height:(float)height draw:(SPTextureDrawingBlock)drawingBlock; + +/// Factory method. Creates an empty (white) texture. ++ (SPTexture *)emptyTexture; + +/// ------------- +/// @name Methods +/// ------------- + +/// Convertes texture coordinates in the format required for rendering. +- (void)adjustTextureCoordinates:(const float *)texCoords saveAtTarget:(float *)targetTexCoords + numVertices:(int)numVertices; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The width of the image in points. +@property (nonatomic, readonly) float width; + +/// The height of the image in points. +@property (nonatomic, readonly) float height; + +/// The OpenGL texture ID. +@property (nonatomic, readonly) uint textureID; + +/// Indicates if the alpha values are premultiplied into the RGB values. +@property (nonatomic, readonly) BOOL hasPremultipliedAlpha; + +/// The scale factor, which influences `width` and `height` properties. +@property (nonatomic, readonly) float scale; + +@end diff --git a/libs/sparrow/src/Classes/SPTexture.m b/libs/sparrow/src/Classes/SPTexture.m new file mode 100644 index 0000000..3a8aeaf --- /dev/null +++ b/libs/sparrow/src/Classes/SPTexture.m @@ -0,0 +1,306 @@ +// +// SPTexture.m +// Sparrow +// +// Created by Daniel Sperl on 19.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPTexture.h" +#import "SPMacros.h" +#import "SPUtils.h" +#import "SPRectangle.h" +#import "SPGLTexture.h" +#import "SPSubTexture.h" +#import "SPNSExtensions.h" +#import "SPStage.h" + +// --- PVRTC structs & enums ----------------------------------------------------------------------- + +typedef struct +{ + uint headerSize; // size of the structure + uint height; // height of surface to be created + uint width; // width of input surface + uint numMipmaps; // number of mip-map levels requested + uint pfFlags; // pixel format flags + uint textureDataSize; // total size in bytes + uint bitCount; // number of bits per pixel + uint rBitMask; // mask for red bit + uint gBitMask; // mask for green bits + uint bBitMask; // mask for blue bits + uint alphaBitMask; // mask for alpha channel + uint pvr; // magic number identifying pvr file + uint numSurfs; // number of surfaces present in the pvr +} PVRTextureHeader; + +enum PVRPixelType +{ + OGL_RGBA_4444 = 0x10, + OGL_RGBA_5551, + OGL_RGBA_8888, + OGL_RGB_565, + OGL_RGB_555, + OGL_RGB_888, + OGL_I_8, + OGL_AI_88, + OGL_PVRTC2, + OGL_PVRTC4 +}; + +// --- private interface --------------------------------------------------------------------------- + +@interface SPTexture () + +- (id)initWithContentsOfPvrFile:(NSString *)path; + +@end + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPTexture + +- (id)init +{ + if ([self isMemberOfClass:[SPTexture class]]) + { + [self release]; + [NSException raise:SP_EXC_ABSTRACT_CLASS + format:@"Attempting to initialize abstract class SPTexture."]; + return nil; + } + + return [super init]; +} + +- (id)initWithContentsOfFile:(NSString *)path +{ + float contentScaleFactor = [SPStage contentScaleFactor]; + + NSString *fullPath = [path isAbsolutePath] ? + path : [[NSBundle mainBundle] pathForResource:path withScaleFactor:contentScaleFactor]; + + if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath]) + { + [self release]; + [NSException raise:SP_EXC_FILE_NOT_FOUND format:@"file '%@' not found", path]; + } + + NSString *imgType = [[path pathExtension] lowercaseString]; + if ([imgType rangeOfString:@"pvr"].location == 0) + return [self initWithContentsOfPvrFile:fullPath]; + else + return [self initWithContentsOfImage:[UIImage imageWithContentsOfFile:fullPath]]; +} + +- (id)initWithWidth:(float)width height:(float)height draw:(SPTextureDrawingBlock)drawingBlock +{ + return [self initWithWidth:width height:height scale:[SPStage contentScaleFactor] + colorSpace:SPColorSpaceRGBA draw:drawingBlock]; +} + +- (id)initWithWidth:(float)width height:(float)height scale:(float)scale + colorSpace:(SPColorSpace)colorSpace draw:(SPTextureDrawingBlock)drawingBlock +{ + [self release]; // class factory - we'll return a subclass! + + // only textures with sides that are powers of 2 are allowed by OpenGL ES. + int legalWidth = [SPUtils nextPowerOfTwo:width * scale]; + int legalHeight = [SPUtils nextPowerOfTwo:height * scale]; + + SPTextureFormat textureFormat; + CGColorSpaceRef cgColorSpace; + CGBitmapInfo bitmapInfo; + BOOL premultipliedAlpha; + int bytesPerPixel; + + if (colorSpace == SPColorSpaceRGBA) + { + bytesPerPixel = 4; + textureFormat = SPTextureFormatRGBA; + cgColorSpace = CGColorSpaceCreateDeviceRGB(); + bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast; + premultipliedAlpha = YES; + } + else + { + bytesPerPixel = 1; + textureFormat = SPTextureFormatAlpha; + cgColorSpace = CGColorSpaceCreateDeviceGray(); + bitmapInfo = kCGImageAlphaNone; + premultipliedAlpha = NO; + } + + void *imageData = calloc(legalWidth * legalHeight * bytesPerPixel, 1); + CGContextRef context = CGBitmapContextCreate(imageData, legalWidth, legalHeight, 8, + bytesPerPixel * legalWidth, cgColorSpace, + bitmapInfo); + CGColorSpaceRelease(cgColorSpace); + + // UIKit referential is upside down - we flip it and apply the scale factor + CGContextTranslateCTM(context, 0.0f, legalHeight); + CGContextScaleCTM(context, scale, -scale); + + if (drawingBlock) + { + UIGraphicsPushContext(context); + drawingBlock(context); + UIGraphicsPopContext(); + } + + SPTextureProperties properties = { + .format = textureFormat, + .width = legalWidth, + .height = legalHeight, + .generateMipmaps = YES, + .premultipliedAlpha = premultipliedAlpha + }; + + SPGLTexture *glTexture = [[SPGLTexture alloc] initWithData:imageData properties:properties]; + glTexture.scale = scale; + + CGContextRelease(context); + free(imageData); + + SPRectangle *region = [SPRectangle rectangleWithX:0 y:0 width:width height:height]; + SPTexture *subTexture = [[SPTexture alloc] initWithRegion:region ofTexture:glTexture]; + [glTexture release]; + return subTexture; +} + +- (id)initWithContentsOfImage:(UIImage *)image +{ + float scale = [image respondsToSelector:@selector(scale)] ? [image scale] : 1.0f; + + return [self initWithWidth:image.size.width height:image.size.height + scale:scale colorSpace:SPColorSpaceRGBA draw:^(CGContextRef context) + { + [image drawAtPoint:CGPointMake(0, 0)]; + }]; +} + +- (id)initWithContentsOfPvrFile:(NSString*)path +{ + [self release]; // class factory - we'll return a subclass! + + NSData *fileData = [[NSData alloc] initWithContentsOfFile:path]; + PVRTextureHeader *header = (PVRTextureHeader *)[fileData bytes]; + bool hasAlpha = header->alphaBitMask ? YES : NO; + + SPTextureProperties properties = { + .width = header->width, + .height = header->height, + .numMipmaps = header->numMipmaps, + .premultipliedAlpha = NO + }; + + switch (header->pfFlags & 0xff) + { + case OGL_RGB_565: + properties.format = SPTextureFormat565; + break; + case OGL_RGBA_5551: + properties.format = SPTextureFormat5551; + break; + case OGL_RGBA_4444: + properties.format = SPTextureFormat4444; + break; + case OGL_PVRTC2: + properties.format = hasAlpha ? SPTextureFormatPvrtcRGBA2 : SPTextureFormatPvrtcRGB2; + break; + case OGL_PVRTC4: + properties.format = hasAlpha ? SPTextureFormatPvrtcRGBA4 : SPTextureFormatPvrtcRGB4; + break; + default: + [fileData release]; + [NSException raise:SP_EXC_INVALID_OPERATION format:@"Unsupported PRV image format"]; + return nil; + } + + void *imageData = (unsigned char *)header + header->headerSize; + + SPGLTexture *glTexture = [[SPGLTexture alloc] initWithData:imageData properties:properties]; + [fileData release]; + + NSString *baseFilename = [[path lastPathComponent] stringByDeletingPathExtension]; + if ([baseFilename rangeOfString:@"@2x"].location == baseFilename.length - 3) + glTexture.scale = 2.0f; + + return glTexture; +} + +- (id)initWithRegion:(SPRectangle*)region ofTexture:(SPTexture*)texture +{ + [self release]; // class factory - we'll return a subclass! + + if (region.x == 0.0f && region.y == 0.0f && + region.width == texture.width && region.height == texture.height) + { + return [texture retain]; + } + else + { + return [[SPSubTexture alloc] initWithRegion:region ofTexture:texture]; + } +} + ++ (SPTexture *)emptyTexture +{ + return [[[SPGLTexture alloc] init] autorelease]; +} + ++ (SPTexture *)textureWithContentsOfFile:(NSString *)path +{ + return [[[SPTexture alloc] initWithContentsOfFile:path] autorelease]; +} + ++ (SPTexture *)textureWithRegion:(SPRectangle *)region ofTexture:(SPTexture *)texture +{ + return [[[SPTexture alloc] initWithRegion:region ofTexture:texture] autorelease]; +} + ++ (SPTexture *)textureWithWidth:(float)width height:(float)height draw:(SPTextureDrawingBlock)drawingBlock +{ + return [[[SPTexture alloc] initWithWidth:width height:height draw:drawingBlock] autorelease]; +} + +- (void)adjustTextureCoordinates:(const float *)texCoords saveAtTarget:(float *)targetTexCoords + numVertices:(int)numVertices +{ + memcpy(targetTexCoords, texCoords, numVertices * 2 * sizeof(float)); +} + +- (float)width +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return 0; +} + +- (float)height +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return 0; +} + +- (uint)textureID +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return 0; +} + +- (BOOL)hasPremultipliedAlpha +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return NO; +} + +- (float)scale +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD format:@"Override this method in subclasses."]; + return 1.0f; +} + +@end diff --git a/libs/sparrow/src/Classes/SPTextureAtlas.h b/libs/sparrow/src/Classes/SPTextureAtlas.h new file mode 100644 index 0000000..3981fc8 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTextureAtlas.h @@ -0,0 +1,103 @@ +// +// SPTextureAtlas.h +// Sparrow +// +// Created by Daniel Sperl on 27.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +@class SPTexture; +@class SPRectangle; + +/** ------------------------------------------------------------------------------------------------ + + A texture atlas is a collection of many smaller textures in one big image. The class + `SPTextureAtlas` is used to access textures from such an atlas. + + Using a texture atlas for your textures has two main advantages: + + * In OpenGL, there’s always one texture active at a given moment. Whenever you change the active + texture, a "texture-switch" has to be executed, and that switch takes time. + * To use a texture in OpenGL, its height and width must each be a power of 2. Sparrow hides this + limitation from you, but you will nevertheless use more memory if you do not follow that rule. + + By using a texture atlas, you avoid both texture switches and the power-of-two limitation. All + textures are within one big "super-texture", and Sparrow takes care that the correct part of this + texture is displayed. + + There are several ways to create a texture atlas. One is to use the atlas generator script that + is provided with Sparrow. Here is a sample on how to use it: + + # creates "atlas.xml" and "atlas.png" from the provided images + ./generate_atlas.rb input/*.png output/atlas.xml + + The atlas generator can be found in the 'utils' directory in the Sparrow package. A README file + shows you how to install and use it. If you want to have more control over your atlas, you will + find great alternative tools on the Internet (e.g. "Texture Packer"). + + Whatever tool you use, Sparrow expects the following file format: + + + + + + +------------------------------------------------------------------------------------------------- */ + +#ifdef __IPHONE_4_0 +@interface SPTextureAtlas : NSObject +#else +@interface SPTextureAtlas : NSObject +#endif +{ + @private + SPTexture *mAtlasTexture; + NSMutableDictionary *mTextureRegions; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a texture atlas from an XML file and a custom texture. _Designated Initializer_. +- (id)initWithContentsOfFile:(NSString *)path texture:(SPTexture *)texture; + +/// Initializes a texture atlas from an XML file, loading the texture that is specified in the XML. +- (id)initWithContentsOfFile:(NSString *)path; + +/// Initializes a teture atlas from a texture. Add the regions manually with `addName:forRegion:`. +- (id)initWithTexture:(SPTexture *)texture; + +/// Factory Method. ++ (SPTextureAtlas *)atlasWithContentsOfFile:(NSString *)path; + +/// ------------- +/// @name Methods +/// ------------- + +/// Retrieve a subtexture by name. Returns `nil` if it is not found. +- (SPTexture *)textureByName:(NSString *)name; + +/// Returns all textures that start with a certain string, sorted alphabetically +/// (especially useful for `SPMovieClip`). +- (NSArray *)texturesStartingWith:(NSString *)name; + +/// Creates a region for a subtexture and gives it a name. +- (void)addRegion:(SPRectangle *)region withName:(NSString *)name; + +/// Removes a region with a certain name. +- (void)removeRegion:(NSString *)name; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The number of available subtextures. +@property (nonatomic, readonly) int count; + +@end diff --git a/libs/sparrow/src/Classes/SPTextureAtlas.m b/libs/sparrow/src/Classes/SPTextureAtlas.m new file mode 100644 index 0000000..74e0f20 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTextureAtlas.m @@ -0,0 +1,160 @@ +// +// SPTextureAtlas.m +// Sparrow +// +// Created by Daniel Sperl on 27.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPTextureAtlas.h" +#import "SPMacros.h" +#import "SPTexture.h" +#import "SPSubTexture.h" +#import "SPGLTexture.h" +#import "SPRectangle.h" +#import "SPNSExtensions.h" +#import "SPStage.h" + +// --- private interface --------------------------------------------------------------------------- + +@interface SPTextureAtlas() + +- (void)parseAtlasXml:(NSString*)path; + +@end + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPTextureAtlas + +- (id)initWithContentsOfFile:(NSString *)path texture:(SPTexture *)texture +{ + if (self = [super init]) + { + mTextureRegions = [[NSMutableDictionary alloc] init]; + mAtlasTexture = [texture retain]; + [self parseAtlasXml:path]; + } + return self; +} + +- (id)initWithContentsOfFile:(NSString *)path +{ + return [self initWithContentsOfFile:path texture:nil]; +} + +- (id)initWithTexture:(SPTexture *)texture +{ + return [self initWithContentsOfFile:nil texture:(SPTexture *)texture]; +} + +- (id)init +{ + return [self initWithContentsOfFile:nil texture:nil]; +} + +- (void)parseAtlasXml:(NSString *)path +{ + SP_CREATE_POOL(pool); + + if (!path) return; + + float scale = [SPStage contentScaleFactor]; + NSString *fullPath = [[NSBundle mainBundle] pathForResource:path withScaleFactor:scale]; + NSURL *xmlUrl = [NSURL fileURLWithPath:fullPath]; + NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlUrl]; + xmlParser.delegate = self; + BOOL success = [xmlParser parse]; + + SP_RELEASE_POOL(pool); + + if (!success) + [NSException raise:SP_EXC_FILE_INVALID + format:@"could not parse texture atlas %@. Error code: %d, domain: %@", + path, xmlParser.parserError.code, xmlParser.parserError.domain]; + + [xmlParser release]; +} + +- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName + namespaceURI:(NSString *)namespaceURI + qualifiedName:(NSString *)qName + attributes:(NSDictionary *)attributeDict +{ + if ([elementName isEqualToString:@"SubTexture"]) + { + float scale = mAtlasTexture.scale; + + NSString *name = [attributeDict valueForKey:@"name"]; + float x = [[attributeDict valueForKey:@"x"] floatValue] / scale; + float y = [[attributeDict valueForKey:@"y"] floatValue] / scale; + float width = [[attributeDict valueForKey:@"width"] floatValue] / scale; + float height = [[attributeDict valueForKey:@"height"] floatValue] / scale; + + [self addRegion:[SPRectangle rectangleWithX:x y:y width:width height:height] withName:name]; + } + else if ([elementName isEqualToString:@"TextureAtlas"] && !mAtlasTexture) + { + // load atlas texture + NSString *imagePath = [attributeDict valueForKey:@"imagePath"]; + mAtlasTexture = [[SPTexture alloc] initWithContentsOfFile:imagePath]; + } +} + +- (int)count +{ + return [mTextureRegions count]; +} + +- (SPTexture *)textureByName:(NSString *)name +{ + SPRectangle *region = [mTextureRegions objectForKey:name]; + if (!region) return nil; + return [SPSubTexture textureWithRegion:region ofTexture:mAtlasTexture]; +} + +- (NSArray *)texturesStartingWith:(NSString *)name +{ + NSMutableArray *textureNames = [[NSMutableArray alloc] init]; + + for (NSString *textureName in mTextureRegions) + if ([textureName rangeOfString:name].location == 0) + [textureNames addObject:textureName]; + + // note: when switching to iOS 4, 'localizedStandardCompare:' would be preferable + [textureNames sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + + NSMutableArray *textures = [NSMutableArray arrayWithCapacity:textureNames.count]; + for (NSString *textureName in textureNames) + [textures addObject:[self textureByName:textureName]]; + + [textureNames release]; + return textures; +} + +- (void)addRegion:(SPRectangle *)region withName:(NSString *)name +{ + [mTextureRegions setObject:region forKey:name]; +} + +- (void)removeRegion:(NSString *)name +{ + [mTextureRegions removeObjectForKey:name]; +} + ++ (SPTextureAtlas *)atlasWithContentsOfFile:(NSString *)path +{ + return [[[SPTextureAtlas alloc] initWithContentsOfFile:path] autorelease]; +} + +- (void)dealloc +{ + [mAtlasTexture release]; + [mTextureRegions release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPTouch.h b/libs/sparrow/src/Classes/SPTouch.h new file mode 100644 index 0000000..573ae73 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTouch.h @@ -0,0 +1,105 @@ +// +// SPTouch.h +// Sparrow +// +// Created by Daniel Sperl on 01.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +@class SPDisplayObject; +@class SPPoint; + +/// SPTouchPhase describes the phases in the life-cycle of a touch. +typedef enum +{ + SPTouchPhaseBegan, /// The finger just touched the screen. + SPTouchPhaseMoved, /// The finger moves around. + SPTouchPhaseStationary, /// The finger has not moved since the last frame. + SPTouchPhaseEnded, /// The finger was lifted from the screen. + SPTouchPhaseCancelled /// The touch was aborted by the system (e.g. because of an AlertBox popping up) +} SPTouchPhase; + +/** ------------------------------------------------------------------------------------------------ + + An SPTouch contains information about the presence or movement of a finger on the screen. + + You receive objects of this type via an SPTouchEvent. When such an event is triggered, you can + query it for all touches that are currently present on the screen. One SPTouch object contains + information about a single touch. + + *The phase of a touch* + + Each touch normally moves through the following phases in its life: + + `Began -> Moved -> Ended` + + Furthermore, a touch can enter a `Stationary` phase. That phase will not trigger an event itself, + but you might receive it when another touch does so. Finally, there's the `Cancelled` phase, + which happens when the system aborts a touch (e.g. because of an AlertBox that pops up). + + *The position of a touch* + + You can get the current and last position on the screen with corresponding properties. However, + you'll want to have the position in a different coordinate system most of the time. + For this reason, there are methods that convert the current and previous touches into the local + coordinate system of any object. + +------------------------------------------------------------------------------------------------- */ + +@interface SPTouch : NSObject +{ + @private + double mTimestamp; + float mGlobalX; + float mGlobalY; + float mPreviousGlobalX; + float mPreviousGlobalY; + int mTapCount; + SPTouchPhase mPhase; + SPDisplayObject *mTarget; +} + +/// ------------- +/// @name Methods +/// ------------- + +/// Converts the current location of a touch to the local coordinate system of a display object. +- (SPPoint*)locationInSpace:(SPDisplayObject*)space; + +/// Converts the previous location of a touch to the local coordinate system of a display object. +- (SPPoint*)previousLocationInSpace:(SPDisplayObject*)space; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The moment the event occurred (in seconds since application start). +@property (nonatomic, readonly) double timestamp; + +/// The x-position of the touch in screen coordinates +@property (nonatomic, readonly) float globalX; + +/// The y-position of the touch in screen coordinates +@property (nonatomic, readonly) float globalY; + +/// The previous x-position of the touch in screen coordinates +@property (nonatomic, readonly) float previousGlobalX; + +/// The previous y-position of the touch in screen coordinates +@property (nonatomic, readonly) float previousGlobalY; + +/// The number of taps the finger made in a short amount of time. Use this to detect double-taps, etc. +@property (nonatomic, readonly) int tapCount; + +/// The current phase the touch is in. +@property (nonatomic, readonly) SPTouchPhase phase; + +/// The display object at which the touch occurred. +@property (nonatomic, readonly) SPDisplayObject *target; + +@end diff --git a/libs/sparrow/src/Classes/SPTouch.m b/libs/sparrow/src/Classes/SPTouch.m new file mode 100644 index 0000000..7e04083 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTouch.m @@ -0,0 +1,109 @@ +// +// SPTouch.m +// Sparrow +// +// Created by Daniel Sperl on 01.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPTouch.h" +#import "SPTouch_Internal.h" +#import "SPDisplayObject.h" +#import "SPPoint.h" + +@implementation SPTouch + +@synthesize timestamp = mTimestamp; +@synthesize globalX = mGlobalX; +@synthesize globalY = mGlobalY; +@synthesize previousGlobalX = mPreviousGlobalX; +@synthesize previousGlobalY = mPreviousGlobalY; +@synthesize tapCount = mTapCount; +@synthesize phase = mPhase; +@synthesize target = mTarget; + +- (id)init +{ + return [super init]; +} + +- (SPPoint*)locationInSpace:(SPDisplayObject*)space +{ + SPPoint *point = [SPPoint pointWithX:mGlobalX y:mGlobalY]; + SPMatrix *transformationMatrix = [mTarget.root transformationMatrixToSpace:space]; + return [transformationMatrix transformPoint:point]; +} + +- (SPPoint*)previousLocationInSpace:(SPDisplayObject*)space +{ + SPPoint *point = [SPPoint pointWithX:mPreviousGlobalX y:mPreviousGlobalY]; + SPMatrix *transformationMatrix = [mTarget.root transformationMatrixToSpace:space]; + return [transformationMatrix transformPoint:point]; +} + +- (void)dealloc +{ + [mTarget release]; + [super dealloc]; +} + +@end + +// ------------------------------------------------------------------------------------------------- + +@implementation SPTouch (Internal) + +- (void)setTimestamp:(double)timestamp +{ + mTimestamp = timestamp; +} + +- (void)setGlobalX:(float)x +{ + mGlobalX = x; +} + +- (void)setGlobalY:(float)y +{ + mGlobalY = y; +} + +- (void)setPreviousGlobalX:(float)x +{ + mPreviousGlobalX = x; +} + +- (void)setPreviousGlobalY:(float)y +{ + mPreviousGlobalY = y; +} + +- (void)setTapCount:(int)tapCount +{ + mTapCount = tapCount; +} + +- (void)setPhase:(SPTouchPhase)phase +{ + mPhase = phase; +} + +- (void)setTarget:(SPDisplayObject*)target +{ + if (target != mTarget) + { + [mTarget release]; + mTarget = [target retain]; + } +} + ++ (SPTouch*)touch +{ + return [[[SPTouch alloc] init] autorelease]; +} + +@end + diff --git a/libs/sparrow/src/Classes/SPTouchEvent.h b/libs/sparrow/src/Classes/SPTouchEvent.h new file mode 100644 index 0000000..5ca2225 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTouchEvent.h @@ -0,0 +1,98 @@ +// +// SPTouchEvent.h +// Sparrow +// +// Created by Daniel Sperl on 02.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPEvent.h" +#import "SPTouch.h" + +@class SPDisplayObject; + +#define SP_EVENT_TYPE_TOUCH @"touch" + +/** ------------------------------------------------------------------------------------------------ + + When one or more fingers touch the screen, move around or are raised, an SPTouchEvent is triggered. + + The event contains a list of all touches that are currently present. Since you are normally only + interested in those touches that occurred on top of certain objects, you can query the event for + touches with certain targets. + + In this context, the target of a touch is not only the object that was touched (e.g. an SPImage), + but also each of its parents - e.g. the container that holds that image. + + Here is an example of how to react on touch events at 'self', which could be a subclass of SPSprite: + + // e.g. in 'init' + [self addEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH]; + + // the corresponding listener: + - (void)onTouch:(SPTouchEvent*)event + { + // query all touches that are currently moving on top of 'self' + NSArray *touches = [[event touchesWithTarget:self andPhase:SPTouchPhaseMoved] allObjects]; + + if (touches.count == 1) + { + // one finger touching + SPTouch *touch = [touches objectAtIndex:0]; + SPPoint *currentPos = [touch locationInSpace:self.parent]; + SPPoint *previousPos = [touch previousLocationInSpace:self.parent]; + // ... + } + else if (touches.count >= 2) + { + // two fingers touching + // ... + } + } + +------------------------------------------------------------------------------------------------- */ + +@interface SPTouchEvent : SPEvent +{ + @private + NSSet *mTouches; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Creates a touch with a set of touches. _Designated Initializer_. +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles touches:(NSSet*)touches; + +/// Creates a touch with a set of touches. +- (id)initWithType:(NSString*)type touches:(NSSet*)touches; + +/// Factory method. ++ (SPTouchEvent*)eventWithType:(NSString*)type touches:(NSSet*)touches; + +/// ------------- +/// @name Methods +/// ------------- + +/// Gets a set of SPTouch objects that originated over a certain target. +- (NSSet*)touchesWithTarget:(SPDisplayObject*)target; + +/// Gets a set of SPTouch objects that originated over a certain target and are in a certain phase. +- (NSSet*)touchesWithTarget:(SPDisplayObject*)target andPhase:(SPTouchPhase)phase; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// All touches that are currently happening. +@property (nonatomic, readonly) NSSet *touches; + +/// The time the event occurred (in seconds since application launch). +@property (nonatomic, readonly) double timestamp; + +@end diff --git a/libs/sparrow/src/Classes/SPTouchEvent.m b/libs/sparrow/src/Classes/SPTouchEvent.m new file mode 100644 index 0000000..5d895a5 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTouchEvent.m @@ -0,0 +1,92 @@ +// +// SPTouchEvent.m +// Sparrow +// +// Created by Daniel Sperl on 02.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPTouchEvent.h" +#import "SPDisplayObject.h" +#import "SPDisplayObjectContainer.h" +#import "SPEvent_Internal.h" + +@implementation SPTouchEvent + +@synthesize touches = mTouches; + +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles touches:(NSSet*)touches +{ + if (self = [super initWithType:type bubbles:bubbles]) + { + mTouches = [touches retain]; + } + return self; +} + +- (id)initWithType:(NSString*)type touches:(NSSet*)touches +{ + return [self initWithType:type bubbles:YES touches:touches]; +} + +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles +{ + return [self initWithType:type bubbles:bubbles touches:[NSSet set]]; +} + +- (SPEvent*)clone +{ + return [SPTouchEvent eventWithType:self.type touches:self.touches]; +} + +- (double)timestamp +{ + return [[mTouches anyObject] timestamp]; +} + +- (NSSet*)touchesWithTarget:(SPDisplayObject*)target +{ + NSMutableSet *touchesFound = [NSMutableSet set]; + for (SPTouch *touch in mTouches) + { + if ([target isEqual:touch.target] || + ([target isKindOfClass:[SPDisplayObjectContainer class]] && + [(SPDisplayObjectContainer*)target containsChild:touch.target])) + { + [touchesFound addObject: touch]; + } + } + return touchesFound; +} + +- (NSSet*)touchesWithTarget:(SPDisplayObject*)target andPhase:(SPTouchPhase)phase +{ + NSMutableSet *touchesFound = [NSMutableSet set]; + for (SPTouch *touch in mTouches) + { + if (touch.phase == phase && + ([target isEqual:touch.target] || + ([target isKindOfClass:[SPDisplayObjectContainer class]] && + [(SPDisplayObjectContainer*)target containsChild:touch.target]))) + { + [touchesFound addObject: touch]; + } + } + return touchesFound; +} + +- (void)dealloc +{ + [mTouches release]; + [super dealloc]; +} + ++ (SPTouchEvent*)eventWithType:(NSString*)type touches:(NSSet*)touches +{ + return [[[SPTouchEvent alloc] initWithType:type touches:touches] autorelease]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPTouchProcessor.h b/libs/sparrow/src/Classes/SPTouchProcessor.h new file mode 100644 index 0000000..ed3ac24 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTouchProcessor.h @@ -0,0 +1,52 @@ +// +// SPTouchProcessor.h +// Sparrow +// +// Created by Daniel Sperl on 03.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +@class SPDisplayObjectContainer; + +/** ------------------------------------------------------------------------------------------------ + + The SPTouchProcesser processes raw touch information and dispatches it on display objects. + + _This is an internal class. You do not have to use it manually._ + +------------------------------------------------------------------------------------------------- */ + +@interface SPTouchProcessor : NSObject +{ + @private + SPDisplayObjectContainer *mRoot; + NSMutableSet *mCurrentTouches; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a touch processor with a certain root object. +- (id)initWithRoot:(SPDisplayObjectContainer*)root; + +/// ------------- +/// @name Methods +/// ------------- + +/// @name Processes raw touches and dispatches events on the touched display objects. +- (void)processTouches:(NSSet*)touches; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The root display container to check for touched targets. +@property (nonatomic, assign) SPDisplayObjectContainer *root; + +@end diff --git a/libs/sparrow/src/Classes/SPTouchProcessor.m b/libs/sparrow/src/Classes/SPTouchProcessor.m new file mode 100644 index 0000000..b5e0907 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTouchProcessor.m @@ -0,0 +1,121 @@ +// +// SPTouchProcessor.m +// Sparrow +// +// Created by Daniel Sperl on 03.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPTouchProcessor.h" +#import "SPMacros.h" +#import "SPTouchEvent.h" +#import "SPTouch.h" +#import "SPTouch_Internal.h" +#import "SPPoint.h" +#import "SPMatrix.h" +#import "SPDisplayObjectContainer.h" + +#define MULTITAP_TIME 0.25f +#define MULTITAP_DIST 25 + +@implementation SPTouchProcessor + +@synthesize root = mRoot; + +- (id)initWithRoot:(SPDisplayObjectContainer*)root +{ + if (self = [super init]) + { + mRoot = root; + mCurrentTouches = [[NSMutableSet alloc] initWithCapacity:2]; + } + return self; +} + +- (id)init +{ + return [self initWithRoot:nil]; +} + +- (void)processTouches:(NSSet*)touches +{ + SP_CREATE_POOL(pool); + + NSMutableSet *processedTouches = [[NSMutableSet alloc] init]; + + // process new touches + for (SPTouch *touch in touches) + { + SPTouch *currentTouch = nil; + + for (SPTouch *existingTouch in mCurrentTouches) + { + if ((existingTouch.globalX == touch.previousGlobalX && + existingTouch.globalY == touch.previousGlobalY) || + (existingTouch.globalX == touch.globalX && + existingTouch.globalY == touch.globalY)) + { + // existing touch; update values + existingTouch.timestamp = touch.timestamp; + existingTouch.previousGlobalX = touch.previousGlobalX; + existingTouch.previousGlobalY = touch.previousGlobalY; + existingTouch.globalX = touch.globalX; + existingTouch.globalY = touch.globalY; + existingTouch.phase = touch.phase; + existingTouch.tapCount = touch.tapCount; + + if (!existingTouch.target.stage) + { + // target could have been removed from stage -> find new target in that case + SPPoint *touchPosition = [SPPoint pointWithX:touch.globalX y:touch.globalY]; + existingTouch.target = [mRoot hitTestPoint:touchPosition forTouch:YES]; + } + + currentTouch = existingTouch; + break; + } + } + + if (!currentTouch) + { + // new touch! + currentTouch = [SPTouch touch]; + currentTouch.timestamp = touch.timestamp; + currentTouch.globalX = touch.globalX; + currentTouch.globalY = touch.globalY; + currentTouch.previousGlobalX = touch.previousGlobalX; + currentTouch.previousGlobalY = touch.previousGlobalY; + currentTouch.phase = touch.phase; + currentTouch.tapCount = touch.tapCount; + SPPoint *touchPosition = [SPPoint pointWithX:touch.globalX y:touch.globalY]; + currentTouch.target = [mRoot hitTestPoint:touchPosition forTouch:YES]; + } + + [processedTouches addObject:currentTouch]; + } + + // dispatch events + for (SPTouch *touch in processedTouches) + { + SPTouchEvent *touchEvent = [[SPTouchEvent alloc] initWithType:SP_EVENT_TYPE_TOUCH + touches:processedTouches]; + [touch.target dispatchEvent:touchEvent]; + [touchEvent release]; + } + + [mCurrentTouches release]; + mCurrentTouches = processedTouches; + + SP_RELEASE_POOL(pool); +} + +- (void) dealloc +{ + [mCurrentTouches release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPTouch_Internal.h b/libs/sparrow/src/Classes/SPTouch_Internal.h new file mode 100644 index 0000000..b36fe30 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTouch_Internal.h @@ -0,0 +1,28 @@ +// +// SPTouch_Internal.h +// Sparrow +// +// Created by Daniel Sperl on 03.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPTouch.h" + +@interface SPTouch (Internal) + +- (void)setTimestamp:(double)timestamp; +- (void)setGlobalX:(float)x; +- (void)setGlobalY:(float)y; +- (void)setPreviousGlobalX:(float)x; +- (void)setPreviousGlobalY:(float)y; +- (void)setTapCount:(int)tapCount; +- (void)setPhase:(SPTouchPhase)phase; +- (void)setTarget:(SPDisplayObject*)target; + ++ (SPTouch*)touch; + +@end diff --git a/libs/sparrow/src/Classes/SPTransitions.h b/libs/sparrow/src/Classes/SPTransitions.h new file mode 100644 index 0000000..b720b7f --- /dev/null +++ b/libs/sparrow/src/Classes/SPTransitions.h @@ -0,0 +1,75 @@ +// +// SPTransitions.h +// Sparrow +// +// Created by Daniel Sperl on 11.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +#define SP_TRANSITION_LINEAR @"linear" +#define SP_TRANSITION_RANDOMIZE @"randomize" + +#define SP_TRANSITION_EASE_IN @"easeIn" +#define SP_TRANSITION_EASE_OUT @"easeOut" +#define SP_TRANSITION_EASE_IN_OUT @"easeInOut" +#define SP_TRANSITION_EASE_OUT_IN @"easeOutIn" + +#define SP_TRANSITION_EASE_IN_BACK @"easeInBack" +#define SP_TRANSITION_EASE_OUT_BACK @"easeOutBack" +#define SP_TRANSITION_EASE_IN_OUT_BACK @"easeInOutBack" +#define SP_TRANSITION_EASE_OUT_IN_BACK @"easeOutInBack" + +#define SP_TRANSITION_EASE_IN_ELASTIC @"easeInElastic" +#define SP_TRANSITION_EASE_OUT_ELASTIC @"easeOutElastic" +#define SP_TRANSITION_EASE_IN_OUT_ELASTIC @"easeInOutElastic" +#define SP_TRANSITION_EASE_OUT_IN_ELASTIC @"easeOutInElastic" + +#define SP_TRANSITION_EASE_IN_BOUNCE @"easeInBounce" +#define SP_TRANSITION_EASE_OUT_BOUNCE @"easeOutBounce" +#define SP_TRANSITION_EASE_IN_OUT_BOUNCE @"easeInOutBounce" +#define SP_TRANSITION_EASE_OUT_IN_BOUNCE @"easeOutInBounce" + +/** ------------------------------------------------------------------------------------------------ + + The SPTransitions class contains static methods that define easing functions. Those functions + will be used by SPTween to execute animations. + + Find a visual representation of the transitions at this URL:
+ http://www.sparrow-framework.org/wp-content/uploads/2010/06/transitions.png + + You can define your own transitions by extending this class. The name of the method you declare + acts as the key that is used to identify the transition when you create the tween. + +------------------------------------------------------------------------------------------------- */ + +@interface SPTransitions : NSObject + ++ (float)linear:(float)ratio; ++ (float)randomize:(float)ratio; + ++ (float)easeIn:(float)ratio; ++ (float)easeOut:(float)ratio; ++ (float)easeInOut:(float)ratio; ++ (float)easeOutIn:(float)ratio; + ++ (float)easeInBack:(float)ratio; ++ (float)easeOutBack:(float)ratio; ++ (float)easeInOutBack:(float)ratio; ++ (float)easeOutInBack:(float)ratio; + ++ (float)easeInElastic:(float)ratio; ++ (float)easeOutElastic:(float)ratio; ++ (float)easeInOutElastic:(float)ratio; ++ (float)easeOutInElastic:(float)ratio; + ++ (float)easeInBounce:(float)ratio; ++ (float)easeOutBounce:(float)ratio; ++ (float)easeInOutBounce:(float)ratio; ++ (float)easeOutInBounce:(float)ratio; + +@end diff --git a/libs/sparrow/src/Classes/SPTransitions.m b/libs/sparrow/src/Classes/SPTransitions.m new file mode 100644 index 0000000..f1e6fcc --- /dev/null +++ b/libs/sparrow/src/Classes/SPTransitions.m @@ -0,0 +1,171 @@ +// +// SPTransitions.m +// Sparrow +// +// Created by Daniel Sperl on 11.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// +// The easing functions were thankfully taken from http://dojotoolkit.org +// and http://www.robertpenner.com/easing +// + +#import "SPTransitions.h" +#import "SPMacros.h" +#import "SPUtils.h" + +@implementation SPTransitions + +- (id)init +{ + [self release]; + [NSException raise:NSGenericException format:@"Static class - do not initialize!"]; + return nil; +} + ++ (float)linear:(float)ratio +{ + return ratio; +} + ++ (float)randomize:(float)ratio +{ + return [SPUtils randomFloat]; +} + ++ (float)easeIn:(float)ratio +{ + return ratio * ratio * ratio; +} + ++ (float)easeOut:(float)ratio +{ + float invRatio = ratio - 1.0f; + return invRatio * invRatio * invRatio + 1.0f; +} + ++ (float)easeInOut:(float)ratio +{ + if (ratio < 0.5f) return 0.5f * [SPTransitions easeIn:ratio*2.0f]; + else return 0.5f * [SPTransitions easeOut:(ratio-0.5f)*2.0f] + 0.5f; +} + ++ (float)easeOutIn:(float)ratio +{ + if (ratio < 0.5f) return 0.5f * [SPTransitions easeOut:ratio*2.0f]; + else return 0.5f * [SPTransitions easeIn:(ratio-0.5f)*2.0f] + 0.5f; +} + ++ (float)easeInBack:(float)ratio +{ + float s = 1.70158f; + return powf(ratio, 2.0f) * ((s + 1.0f)*ratio - s); +} + ++ (float)easeOutBack:(float)ratio +{ + float invRatio = ratio - 1.0f; + float s = 1.70158f; + return powf(invRatio, 2.0f) * ((s + 1.0f)*invRatio + s) + 1.0f; +} + ++ (float)easeInOutBack:(float)ratio +{ + if (ratio < 0.5f) return 0.5f * [SPTransitions easeInBack:ratio*2.0f]; + else return 0.5f * [SPTransitions easeOutBack:(ratio-0.5f)*2.0f] + 0.5f; +} + ++ (float)easeOutInBack:(float)ratio +{ + if (ratio < 0.5f) return 0.5f * [SPTransitions easeOutBack:ratio*2.0f]; + else return 0.5f * [SPTransitions easeInBack:(ratio-0.5f)*2.0f] + 0.5f; +} + ++ (float)easeInElastic:(float)ratio +{ + if (ratio == 0.0f || ratio == 1.0f) return ratio; + else + { + float p = 0.3f; + float s = p / 4.0f; + float invRatio = ratio - 1.0f; + return -1.0f * powf(2.0f, 10.0f*invRatio) * sinf((invRatio-s)*TWO_PI/p); + } +} + ++ (float)easeOutElastic:(float)ratio +{ + if (ratio == 0.0f || ratio == 1.0f) return ratio; + else + { + float p = 0.3f; + float s = p / 4.0f; + return powf(2.0f, -10.0f*ratio) * sinf((ratio-s)*TWO_PI/p) + 1.0f; + } +} + ++ (float)easeInOutElastic:(float)ratio +{ + if (ratio < 0.5f) return 0.5f * [SPTransitions easeInElastic:ratio*2.0f]; + else return 0.5f * [SPTransitions easeOutElastic:(ratio-0.5f)*2.0f] + 0.5f; +} + ++ (float)easeOutInElastic:(float)ratio +{ + if (ratio < 0.5f) return 0.5f * [SPTransitions easeOutElastic:ratio*2.0f]; + else return 0.5f * [SPTransitions easeInElastic:(ratio-0.5f)*2.0f] + 0.5f; +} + ++ (float)easeInBounce:(float)ratio +{ + return 1.0f - [SPTransitions easeOutBounce:1.0f - ratio]; +} + ++ (float)easeOutBounce:(float)ratio +{ + float s = 7.5625f; + float p = 2.75f; + float l; + if (ratio < (1.0f/p)) + { + l = s * powf(ratio, 2.0f); + } + else + { + if (ratio < (2.0f/p)) + { + ratio -= 1.5f/p; + l = s * powf(ratio, 2.0f) + 0.75f; + } + else + { + if (ratio < 2.5f/p) + { + ratio -= 2.25f/p; + l = s * powf(ratio, 2.0f) + 0.9375f; + } + else + { + ratio -= 2.625f/p; + l = s * powf(ratio, 2.0f) + 0.984375f; + } + } + } + return l; +} + ++ (float)easeInOutBounce:(float)ratio +{ + if (ratio < 0.5f) return 0.5f * [SPTransitions easeInBounce:ratio*2.0f]; + else return 0.5f * [SPTransitions easeOutBounce:(ratio-0.5f)*2.0f] + 0.5f; +} + ++ (float)easeOutInBounce:(float)ratio +{ + if (ratio < 0.5f) return 0.5f * [SPTransitions easeOutBounce:ratio*2.0f]; + else return 0.5f * [SPTransitions easeInBounce:(ratio-0.5f)*2.0f] + 0.5f; +} + +@end diff --git a/libs/sparrow/src/Classes/SPTween.h b/libs/sparrow/src/Classes/SPTween.h new file mode 100644 index 0000000..54f6063 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTween.h @@ -0,0 +1,124 @@ +// +// SPTween.h +// Sparrow +// +// Created by Daniel Sperl on 09.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPEventDispatcher.h" +#import "SPAnimatable.h" +#import "SPTransitions.h" + +typedef enum +{ + SPLoopTypeNone, + SPLoopTypeRepeat, + SPLoopTypeReverse +} SPLoopType; + +#define SP_EVENT_TYPE_TWEEN_STARTED @"tweenStarted" +#define SP_EVENT_TYPE_TWEEN_UPDATED @"tweenUpdated" +#define SP_EVENT_TYPE_TWEEN_COMPLETED @"tweenCompleted" + +/** ------------------------------------------------------------------------------------------------ + + An SPTween animates numeric properties of objects. It uses different transition functions to give + the animations various styles. + + The primary use of this class is to do standard animations like movement, fading, rotation, etc. + But there are no limits on what to animate; as long as the property you want to animate is numeric + (`int`, `float`, `double`), the tween can handle it. For a list of available Transition types, + see `SPTransitions`. + + Here is an example of a tween that moves an object to the right, rotates it, and fades it out: + + SPTween *tween = [SPTween tweenWithTarget:object time:2.0 transition:SP_TRANSITION_EASE_IN_OUT]; + [tween animateProperty:@"x" targetValue:object.x + 50]; + [tween animateProperty:@"rotation" targetValue:object.rotation + SP_D2R(45)]; + [tween animateProperty:@"alpha" targetValue:0.0f]; + [self.stage.juggler addObject:tween]; + + Note that the object is added to a juggler at the end. A tween will only be executed if its + `advanceTime:` method is executed regularly - the juggler will do that for us, and will release + the tween when it is finished. + + Tweens dispatch events in certain phases of their life time: + + * `SP_EVENT_TYPE_TWEEN_STARTED`: Dispatched once when the tween starts + * `SP_EVENT_TYPE_TWEEN_UPDATED`: Dispatched every time it is advanced + * `SP_EVENT_TYPE_TWEEN_COMPLETED`: Dispatched when it reaches its target value (except when it loops). + + Tweens can loop in two ways: + + * `SPLoopTypeRepeat`: Starts the animation from the beginning when it's finished. + * `SPLoopTypeReverse`: Reverses the animation when it's finished, tweening back to the start value. + +------------------------------------------------------------------------------------------------- */ + +@interface SPTween : SPEventDispatcher +{ + @private + id mTarget; + SEL mTransition; + IMP mTransitionFunc; + NSMutableArray *mProperties; + + double mTotalTime; + double mCurrentTime; + double mDelay; + + SPLoopType mLoop; + BOOL mInvertTransition; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a tween with a target, duration (in seconds) and a transition function. +/// _Designated Initializer_. +- (id)initWithTarget:(id)target time:(double)time transition:(NSString*)transition; + +/// Initializes a tween with a target, a time (in seconds) and a linear transition +/// (`SP_TRANSITION_LINEAR`). +- (id)initWithTarget:(id)target time:(double)time; + +/// Factory method. ++ (SPTween *)tweenWithTarget:(id)target time:(double)time transition:(NSString *)transition; + +/// Factory method. ++ (SPTween *)tweenWithTarget:(id)target time:(double)time; + +/// ------------- +/// @name Methods +/// ------------- + +/// Animates the property of an object to a target value. You can call this method multiple times +/// on one tween. +- (void)animateProperty:(NSString*)property targetValue:(float)value; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The target object that is animated. +@property (nonatomic, readonly) id target; + +/// The transition method used for the animation. +@property (nonatomic, readonly) NSString *transition; + +/// The total time the tween will take (in seconds). +@property (nonatomic, readonly) double time; + +/// The delay before the tween is started. +@property (nonatomic, assign) double delay; + +/// The type of loop. (Default: SPLoopTypeNone) +@property (nonatomic, assign) SPLoopType loop; + +@end diff --git a/libs/sparrow/src/Classes/SPTween.m b/libs/sparrow/src/Classes/SPTween.m new file mode 100644 index 0000000..c345c82 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTween.m @@ -0,0 +1,169 @@ +// +// SPTween.m +// Sparrow +// +// Created by Daniel Sperl on 09.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPTween.h" +#import "SPTransitions.h" +#import "SPTweenedProperty.h" +#import "SPMacros.h" + +#define TRANS_SUFFIX @":" + +typedef float (*FnPtrTransition) (id, SEL, float); + +@implementation SPTween + +@synthesize time = mTotalTime; +@synthesize delay = mDelay; +@synthesize target = mTarget; +@synthesize loop = mLoop; + +- (id)initWithTarget:(id)target time:(double)time transition:(NSString*)transition +{ + if (self = [super init]) + { + mTarget = [target retain]; + mTotalTime = MAX(0.0001, time); // zero is not allowed + mCurrentTime = 0; + mDelay = 0; + mProperties = [[NSMutableArray alloc] init]; + mLoop = SPLoopTypeNone; + mInvertTransition = NO; + + // create function pointer for transition + NSString *transMethod = [transition stringByAppendingString:TRANS_SUFFIX]; + mTransition = NSSelectorFromString(transMethod); + if (![SPTransitions respondsToSelector:mTransition]) + [NSException raise:SP_EXC_INVALID_OPERATION + format:@"transition not found: '%@'", transition]; + mTransitionFunc = [SPTransitions methodForSelector:mTransition]; + } + return self; +} + +- (id)initWithTarget:(id)target time:(double)time +{ + return [self initWithTarget:target time:time transition:SP_TRANSITION_LINEAR]; +} + +- (void)animateProperty:(NSString*)property targetValue:(float)value +{ + if (!mTarget) return; // tweening nil just does nothing. + + SPTweenedProperty *tweenedProp = [[SPTweenedProperty alloc] + initWithTarget:mTarget name:property endValue:value]; + [mProperties addObject:tweenedProp]; + [tweenedProp release]; +} + +- (void)advanceTime:(double)seconds +{ + if (seconds == 0.0) return; // nothing to do + + double previousTime = mCurrentTime; + mCurrentTime = MIN(mTotalTime, mCurrentTime + seconds); + + if (mCurrentTime < 0 || previousTime >= mTotalTime) return; + + if (previousTime <= 0 && mCurrentTime >= 0 && + [self hasEventListenerForType:SP_EVENT_TYPE_TWEEN_STARTED]) + { + SPEvent *event = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_TWEEN_STARTED]; + [self dispatchEvent:event]; + [event release]; + } + + float ratio = mCurrentTime / mTotalTime; + FnPtrTransition transFunc = (FnPtrTransition) mTransitionFunc; + Class transClass = [SPTransitions class]; + + for (SPTweenedProperty *prop in mProperties) + { + if (previousTime <= 0 && mCurrentTime >= 0) + prop.startValue = prop.currentValue; + + float transitionValue = mInvertTransition ? + 1.0f - transFunc(transClass, mTransition, 1.0f - ratio) : + transFunc(transClass, mTransition, ratio); + + prop.currentValue = prop.startValue + prop.delta * transitionValue; + } + + if ([self hasEventListenerForType:SP_EVENT_TYPE_TWEEN_UPDATED]) + { + SPEvent *event = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_TWEEN_UPDATED]; + [self dispatchEvent:event]; + [event release]; + } + + if (previousTime < mTotalTime && mCurrentTime >= mTotalTime) + { + if (mLoop == SPLoopTypeRepeat) + { + for (SPTweenedProperty *prop in mProperties) + prop.currentValue = prop.startValue; + + mCurrentTime = 0; + } + else if (mLoop == SPLoopTypeReverse) + { + for (SPTweenedProperty *prop in mProperties) + { + prop.currentValue = prop.endValue; // since tweens not necessarily end with endValue + prop.endValue = prop.startValue; + mInvertTransition = !mInvertTransition; + } + + mCurrentTime = 0; + } + else if ([self hasEventListenerForType:SP_EVENT_TYPE_TWEEN_COMPLETED]) + { + SPEvent *event = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_TWEEN_COMPLETED]; + [self dispatchEvent:event]; + [event release]; + } + } +} + +- (NSString*)transition +{ + NSString *selectorName = NSStringFromSelector(mTransition); + return [selectorName substringToIndex:selectorName.length - [TRANS_SUFFIX length]]; +} + +- (BOOL)isComplete +{ + return mCurrentTime >= mTotalTime && mLoop == SPLoopTypeNone; +} + +- (void)setDelay:(double)delay +{ + mCurrentTime = mCurrentTime + mDelay - delay; + mDelay = delay; +} + ++ (SPTween*)tweenWithTarget:(id)target time:(double)time transition:(NSString*)transition +{ + return [[[SPTween alloc] initWithTarget:target time:time transition:transition] autorelease]; +} + ++ (SPTween*)tweenWithTarget:(id)target time:(double)time +{ + return [[[SPTween alloc] initWithTarget:target time:time] autorelease]; +} + +- (void)dealloc +{ + [mTarget release]; + [mProperties release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPTweenedProperty.h b/libs/sparrow/src/Classes/SPTweenedProperty.h new file mode 100644 index 0000000..f67d469 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTweenedProperty.h @@ -0,0 +1,62 @@ +// +// SPTweenedProperty.h +// Sparrow +// +// Created by Daniel Sperl on 17.10.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +/** ------------------------------------------------------------------------------------------------ + + An SPTweenedProperty stores the information about the tweening of a single property of an object. + Its `currentValue` property updates the specified property of the target object. + + _This is an internal class. You do not have to use it manually._ + +------------------------------------------------------------------------------------------------- */ + +@interface SPTweenedProperty : NSObject +{ + @private + id mTarget; + + SEL mGetter; + IMP mGetterFunc; + SEL mSetter; + IMP mSetterFunc; + + float mStartValue; + float mEndValue; + char mNumericType; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a tween property on a certain target. The start value will be zero. +- (id)initWithTarget:(id)target name:(NSString *)name endValue:(float)endValue; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The start value of the tween. +@property (nonatomic, assign) float startValue; + +/// The current value of the tween. Setting this property updates the target property. +@property (nonatomic, assign) float currentValue; + +/// The end value of the tween. +@property (nonatomic, assign) float endValue; + +/// The animation delta (endValue - startValue) +@property (nonatomic, readonly) float delta; + + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPTweenedProperty.m b/libs/sparrow/src/Classes/SPTweenedProperty.m new file mode 100644 index 0000000..ebe32f1 --- /dev/null +++ b/libs/sparrow/src/Classes/SPTweenedProperty.m @@ -0,0 +1,110 @@ +// +// SPTweenedProperty.m +// Sparrow +// +// Created by Daniel Sperl on 17.10.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPTweenedProperty.h" +#import "SPMacros.h" + +typedef float (*FnPtrGetterF) (id, SEL); +typedef double (*FnPtrGetterD) (id, SEL); +typedef int (*FnPtrGetterI) (id, SEL); + +typedef void (*FnPtrSetterF) (id, SEL, float); +typedef void (*FnPtrSetterD) (id, SEL, double); +typedef void (*FnPtrSetterI) (id, SEL, int); + +@implementation SPTweenedProperty + +@synthesize startValue = mStartValue; +@synthesize endValue = mEndValue; + +- (id)initWithTarget:(id)target name:(NSString *)name endValue:(float)endValue; +{ + if (self = [super init]) + { + mTarget = [target retain]; + mEndValue = endValue; + + mGetter = NSSelectorFromString(name); + mSetter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", + [[name substringToIndex:1] uppercaseString], + [name substringFromIndex:1]]); + + if (![mTarget respondsToSelector:mGetter] || ![mTarget respondsToSelector:mSetter]) + [NSException raise:SP_EXC_INVALID_OPERATION format:@"property not found or readonly: '%@'", + name]; + + // query argument type + NSMethodSignature *sig = [mTarget methodSignatureForSelector:mGetter]; + mNumericType = *[sig methodReturnType]; + if (mNumericType != 'f' && mNumericType != 'i' && mNumericType != 'd') + [NSException raise:SP_EXC_INVALID_OPERATION format:@"property not numeric: '%@'", name]; + + mGetterFunc = [mTarget methodForSelector:mGetter]; + mSetterFunc = [mTarget methodForSelector:mSetter]; + } + return self; +} + +- (id)init +{ + return [self initWithTarget:nil name:nil endValue:0.0f]; +} + +- (void)setCurrentValue:(float)value +{ + if (mNumericType == 'f') + { + FnPtrSetterF func = (FnPtrSetterF)mSetterFunc; + func(mTarget, mSetter, value); + } + else if (mNumericType == 'd') + { + FnPtrSetterD func = (FnPtrSetterD)mSetterFunc; + func(mTarget, mSetter, (double)value); + } + else + { + FnPtrSetterI func = (FnPtrSetterI)mSetterFunc; + func(mTarget, mSetter, (int)(value+0.5f)); + } +} + +- (float)currentValue +{ + if (mNumericType == 'f') + { + FnPtrGetterF func = (FnPtrGetterF)mGetterFunc; + return func(mTarget, mGetter); + } + else if (mNumericType == 'd') + { + FnPtrGetterD func = (FnPtrGetterD)mGetterFunc; + return func(mTarget, mGetter); + } + else + { + FnPtrGetterI func = (FnPtrGetterI)mGetterFunc; + return func(mTarget, mGetter); + } +} + +- (float)delta +{ + return mEndValue - mStartValue; +} + +- (void)dealloc +{ + [mTarget release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPUtils.h b/libs/sparrow/src/Classes/SPUtils.h new file mode 100644 index 0000000..caf8fcc --- /dev/null +++ b/libs/sparrow/src/Classes/SPUtils.h @@ -0,0 +1,27 @@ +// +// SPUtils.h +// Sparrow +// +// Created by Daniel Sperl on 04.01.11. +// Copyright 2011 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +/// The SPUtils class contains utility methods for different purposes. + +@interface SPUtils : NSObject + +/// Finds the next power of two equal to or above the specified number. ++ (int)nextPowerOfTwo:(int)number; + +/// Returns a random integer number between `minValue` (inclusive) and `maxValue` (exclusive). ++ (int)randomIntBetween:(int)minValue and:(int)maxValue; + +/// Returns a random float number between 0.0 and 1.0 ++ (float)randomFloat; + +@end diff --git a/libs/sparrow/src/Classes/SPUtils.m b/libs/sparrow/src/Classes/SPUtils.m new file mode 100644 index 0000000..2def1bc --- /dev/null +++ b/libs/sparrow/src/Classes/SPUtils.m @@ -0,0 +1,34 @@ +// +// SPUtils.m +// Sparrow +// +// Created by Daniel Sperl on 04.01.11. +// Copyright 2011 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPUtils.h" + + +@implementation SPUtils + ++ (int)nextPowerOfTwo:(int)number +{ + int result = 1; + while (result < number) result *= 2; + return result; +} + ++ (int)randomIntBetween:(int)minValue and:(int)maxValue +{ + return (int)(minValue + [self randomFloat] * (maxValue - minValue)); +} + ++ (float)randomFloat +{ + return (float) arc4random() / UINT_MAX; +} + +@end diff --git a/libs/sparrow/src/Classes/SPView.h b/libs/sparrow/src/Classes/SPView.h new file mode 100644 index 0000000..537f238 --- /dev/null +++ b/libs/sparrow/src/Classes/SPView.h @@ -0,0 +1,78 @@ +// +// EAGLView.h +// Sparrow +// +// Created by Daniel Sperl on 13.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + + +#import +#import +#import +#import + +@class SPStage; +@class SPRenderSupport; + +/** ------------------------------------------------------------------------------------------------ + + An SPView is the UIView object that Sparrow renders its content into. + + Add it to the UIKit display list like any other view. Beware that Sparrow will only receive + multitouch events if the multitouchEnabled property of the view is enabled. + + To start Sparrow, connect this class to your stage subclass and call the start method. When + the application ends or moves into the background, you should call the stop method. + +------------------------------------------------------------------------------------------------- */ + +@interface SPView : UIView +{ + @private + int mWidth; + int mHeight; + + SPStage *mStage; + SPRenderSupport *mRenderSupport; + + EAGLContext *mContext; + GLuint mRenderbuffer; + GLuint mFramebuffer; + + float mFrameRate; + NSTimer *mTimer; + id mDisplayLink; + BOOL mDisplayLinkSupported; + + double mLastFrameTimestamp; + double mLastTouchTimestamp; +} + +/// ---------------- +/// @name Properties +/// ---------------- + +/// Indicates if start was called. +@property (nonatomic, readonly) BOOL isStarted; + +/// Assigns the desired framerate. Only dividers of 60 are allowed (60, 30, 20, 15, 12, 10, etc.) +@property (nonatomic, assign) float frameRate; + +/// The stage object that will be processed. +@property (nonatomic, retain) SPStage *stage; + +/// ------------- +/// @name Methods +/// ------------- + +/// Starts rendering and event handling. +- (void)start; + +/// Stops rendering and event handling. Call this when the application moves into the background. +- (void)stop; + +@end diff --git a/libs/sparrow/src/Classes/SPView.m b/libs/sparrow/src/Classes/SPView.m new file mode 100644 index 0000000..811cd64 --- /dev/null +++ b/libs/sparrow/src/Classes/SPView.m @@ -0,0 +1,314 @@ +// +// EAGLView.m +// Sparrow +// +// Created by Daniel Sperl on 13.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import + +#import "SPStage.h" +#import "SPStage_Internal.h" +#import "SPView.h" +#import "SPMacros.h" +#import "SPTouch.h" +#import "SPTouch_Internal.h" +#import "SPRenderSupport.h" + +// --- private interface --------------------------------------------------------------------------- + +@interface SPView () + +@property (nonatomic, retain) NSTimer *timer; +@property (nonatomic, retain) id displayLink; + +- (void)setup; +- (void)createFramebuffer; +- (void)destroyFramebuffer; + +- (void)renderStage; +- (void)processTouchEvent:(UIEvent*)event; + +@end + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPView + +#define REFRESH_RATE 60 + +@synthesize stage = mStage; +@synthesize timer = mTimer; +@synthesize displayLink = mDisplayLink; +@synthesize frameRate = mFrameRate; + +- (id)initWithFrame:(CGRect)frame +{ + if ([super initWithFrame:frame]) + { + [self setup]; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + if ([super initWithCoder:decoder]) + { + [self setup]; + } + return self; +} + +- (void)setup +{ + if (mContext) return; // already initialized! + + // A system version of 3.1 or greater is required to use CADisplayLink. + NSString *currSysVer = [[UIDevice currentDevice] systemVersion]; + if ([currSysVer compare:@"3.1" options:NSNumericSearch] != NSOrderedAscending) + mDisplayLinkSupported = YES; + + self.frameRate = 30.0f; + + // get the layer + CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; + + eaglLayer.opaque = YES; + eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, + kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; + + mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; + + if (!mContext || ![EAGLContext setCurrentContext:mContext]) + NSLog(@"Could not create render context"); + + mRenderSupport = [[SPRenderSupport alloc] init]; +} + +- (void)layoutSubviews +{ + [self destroyFramebuffer]; // reset framebuffer (scale factor could have changed) + [self createFramebuffer]; + [self renderStage]; // fill buffer immediately to avoid flickering +} + +- (void)createFramebuffer +{ + glGenFramebuffersOES(1, &mFramebuffer); + glGenRenderbuffersOES(1, &mRenderbuffer); + + glBindFramebufferOES(GL_FRAMEBUFFER_OES, mFramebuffer); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, mRenderbuffer); + [mContext renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer]; + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, mRenderbuffer); + + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &mWidth); + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &mHeight); + + if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) + NSLog(@"failed to create framebuffer: %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); +} + +- (void)destroyFramebuffer +{ + glDeleteFramebuffersOES(1, &mFramebuffer); + mFramebuffer = 0; + glDeleteRenderbuffersOES(1, &mRenderbuffer); + mRenderbuffer = 0; +} + +- (void)renderStage +{ + if (mFramebuffer == 0 || mRenderbuffer == 0) + return; // buffers not yet initialized + + SP_CREATE_POOL(pool); + + double now = CACurrentMediaTime(); + double timePassed = now - mLastFrameTimestamp; + [mStage advanceTime:timePassed]; + mLastFrameTimestamp = now; + + [EAGLContext setCurrentContext:mContext]; + + glBindFramebufferOES(GL_FRAMEBUFFER_OES, mFramebuffer); + glViewport(0, 0, mWidth, mHeight); + + [mRenderSupport bindTexture:nil]; // old textures could have become invalid + [mStage render:mRenderSupport]; + + glBindRenderbufferOES(GL_RENDERBUFFER_OES, mRenderbuffer); + [mContext presentRenderbuffer:GL_RENDERBUFFER_OES]; + + SP_RELEASE_POOL(pool); +} + +- (void)setTimer:(NSTimer *)newTimer +{ + if (mTimer != newTimer) + { + [mTimer invalidate]; + mTimer = newTimer; + } +} + +- (void)setDisplayLink:(id)newDisplayLink +{ + if (mDisplayLink != newDisplayLink) + { + [mDisplayLink invalidate]; + mDisplayLink = newDisplayLink; + } +} + +- (void)setFrameRate:(float)value +{ + if (mDisplayLinkSupported) + { + int frameInterval = 1; + while (REFRESH_RATE / frameInterval > value) + ++frameInterval; + mFrameRate = REFRESH_RATE / frameInterval; + } + else + mFrameRate = value; + + if (self.isStarted) + { + [self stop]; + [self start]; + } +} + +- (BOOL)isStarted +{ + return mTimer || mDisplayLink; +} + +- (void)start +{ + if (self.isStarted) return; + if (mFrameRate > 0.0f) + { + mLastFrameTimestamp = CACurrentMediaTime(); + + if (mDisplayLinkSupported) + { + mDisplayLink = [NSClassFromString(@"CADisplayLink") + displayLinkWithTarget:self selector:@selector(renderStage)]; + + [mDisplayLink setFrameInterval: (int)(REFRESH_RATE / mFrameRate)]; + [mDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + } + else + { + // timer used as a fallback + self.timer = [NSTimer scheduledTimerWithTimeInterval:(1.0f / mFrameRate) + target:self selector:@selector(renderStage) userInfo:nil repeats:YES]; + } + } +} + +- (void)stop +{ + [self renderStage]; // draw last-moment changes + + self.timer = nil; + self.displayLink = nil; +} + +- (void)setStage:(SPStage*)stage +{ + if (mStage != stage) + { + mStage.nativeView = nil; + [mStage release]; + mStage = [stage retain]; + mStage.nativeView = self; + } +} + ++ (Class)layerClass +{ + return [CAEAGLLayer class]; +} + +- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event +{ + [self processTouchEvent:event]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + [self processTouchEvent:event]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [self processTouchEvent:event]; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + mLastTouchTimestamp -= 0.0001f; // cancelled touch events have an old timestamp -> workaround + [self processTouchEvent:event]; +} + +- (void)processTouchEvent:(UIEvent*)event +{ + if (self.isStarted && mLastTouchTimestamp != event.timestamp) + { + SP_CREATE_POOL(pool); + + CGSize viewSize = self.bounds.size; + float xConversion = mStage.width / viewSize.width; + float yConversion = mStage.height / viewSize.height; + + // convert to SPTouches and forward to stage + NSMutableSet *touches = [NSMutableSet set]; + double now = CACurrentMediaTime(); + for (UITouch *uiTouch in [event touchesForView:self]) + { + CGPoint location = [uiTouch locationInView:self]; + CGPoint previousLocation = [uiTouch previousLocationInView:self]; + SPTouch *touch = [SPTouch touch]; + touch.timestamp = now; // timestamp of uiTouch not compatible to Sparrow timestamp + touch.globalX = location.x * xConversion; + touch.globalY = location.y * yConversion; + touch.previousGlobalX = previousLocation.x * xConversion; + touch.previousGlobalY = previousLocation.y * yConversion; + touch.tapCount = uiTouch.tapCount; + touch.phase = (SPTouchPhase) uiTouch.phase; + [touches addObject:touch]; + } + [mStage processTouches:touches]; + mLastTouchTimestamp = event.timestamp; + + SP_RELEASE_POOL(pool); + } +} + +- (void)dealloc +{ + if ([EAGLContext currentContext] == mContext) + [EAGLContext setCurrentContext:nil]; + + [mContext release]; + [mStage release]; + [mRenderSupport release]; + [self destroyFramebuffer]; + + self.timer = nil; // invalidates timer + self.displayLink = nil; // invalidates displayLink + + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/Sparrow.h b/libs/sparrow/src/Classes/Sparrow.h new file mode 100644 index 0000000..549ae5f --- /dev/null +++ b/libs/sparrow/src/Classes/Sparrow.h @@ -0,0 +1,45 @@ +// +// Sparrow.h +// Sparrow +// +// Created by Daniel Sperl on 21.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#define SPARROW_VERSION @"1.1.0" + +#import "SPNSExtensions.h" +#import "SPEventDispatcher.h" +#import "SPDisplayObject.h" +#import "SPDisplayObjectContainer.h" +#import "SPQuad.h" +#import "SPImage.h" +#import "SPTextField.h" +#import "SPBitmapFont.h" +#import "SPButton.h" +#import "SPStage.h" +#import "SPSprite.h" +#import "SPMovieClip.h" +#import "SPTexture.h" +#import "SPSubTexture.h" +#import "SPRenderTexture.h" +#import "SPGLTexture.h" +#import "SPTextureAtlas.h" +#import "SPEvent.h" +#import "SPTouchEvent.h" +#import "SPEnterFrameEvent.h" +#import "SPJuggler.h" +#import "SPTransitions.h" +#import "SPTween.h" +#import "SPDelayedInvocation.h" +#import "SPRectangle.h" +#import "SPMacros.h" +#import "SPUtils.h" +#import "SPView.h" +#import "SPRenderSupport.h" +#import "SPAudioEngine.h" +#import "SPSound.h" +#import "SPSoundChannel.h" diff --git a/libs/sparrow/src/Sparrow.xcodeproj/project.pbxproj b/libs/sparrow/src/Sparrow.xcodeproj/project.pbxproj new file mode 100755 index 0000000..b08d747 --- /dev/null +++ b/libs/sparrow/src/Sparrow.xcodeproj/project.pbxproj @@ -0,0 +1,868 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + DE019C391026360B00ECB0AC /* SPTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DE7044760FB62080007F5ECC /* SPTween.m */; }; + DE019C3A1026361200ECB0AC /* SPDelayedInvocation.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFB1B94100926260022C117 /* SPDelayedInvocation.m */; }; + DE019C3B1026363D00ECB0AC /* SPNSExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = DE68EA160FBB5660004DBC95 /* SPNSExtensions.m */; }; + DE019C3C1026364800ECB0AC /* SPJuggler.m in Sources */ = {isa = PBXBuildFile; fileRef = DE7044270FB61506007F5ECC /* SPJuggler.m */; }; + DE13D18B12AADBF6000C77E6 /* SPRenderTexture.h in Headers */ = {isa = PBXBuildFile; fileRef = DE13D18912AADBF6000C77E6 /* SPRenderTexture.h */; }; + DE13D18C12AADBF6000C77E6 /* SPRenderTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = DE13D18A12AADBF6000C77E6 /* SPRenderTexture.m */; }; + DE14412E12421EA600FFA95A /* SPNSExtensionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE05748611E915A900F3A8A4 /* SPNSExtensionsTest.m */; }; + DE14413612421F4700FFA95A /* SPImageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE0853A40FEC286900DAF53C /* SPImageTest.m */; }; + DE14413712421F4800FFA95A /* SPDisplayObjectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE469D6E0F938FAB00F56E91 /* SPDisplayObjectTest.m */; }; + DE14413812421F4800FFA95A /* SPMovieClipTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC54F6211B7765500E439B0 /* SPMovieClipTest.m */; }; + DE14413912421F4900FFA95A /* SPRectangleTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DED67F7C0FA359F00050E779 /* SPRectangleTest.m */; }; + DE14413A12421F4900FFA95A /* SPDisplayObjectContainerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEB21CF80F93C9780080D5C2 /* SPDisplayObjectContainerTest.m */; }; + DE14413B12421F4A00FFA95A /* SPEventDispatcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE594490FA63BA800E3AEFC /* SPEventDispatcherTest.m */; }; + DE14413C12421F4B00FFA95A /* SPDelayedInvocationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE5286BA11F77C6200F916E8 /* SPDelayedInvocationTest.m */; }; + DE14413D12421F4B00FFA95A /* SPMatrixTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE8F1E2D0F7C1F3A0085E9E4 /* SPMatrixTest.m */; }; + DE14413E12421F4B00FFA95A /* SPPointTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DEABCF5B0F7AE187003B6C9D /* SPPointTest.m */; }; + DE14413F12421F4C00FFA95A /* SPQuadTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DED2B6F90FA0CF5900083578 /* SPQuadTest.m */; }; + DE14414012421F4C00FFA95A /* SPJugglerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE1F9446104704440084D470 /* SPJugglerTest.m */; }; + DE14414112421F4D00FFA95A /* SPTweenTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE75E8660FBDC57E00C64495 /* SPTweenTest.m */; }; + DE14414212421F4E00FFA95A /* SPStageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DED67F330FA3514C0050E779 /* SPStageTest.m */; }; + DE18FE4511B083B200D01F04 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE18FE4411B083B200D01F04 /* AVFoundation.framework */; }; + DE20D9CC10713B0C006658C9 /* SPRenderSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = DE20D9CA10713B0C006658C9 /* SPRenderSupport.m */; }; + DE33072512D2EBCD009CC5E7 /* SPUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = DE33072312D2EBCD009CC5E7 /* SPUtils.h */; }; + DE33072612D2EBCD009CC5E7 /* SPUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DE33072412D2EBCD009CC5E7 /* SPUtils.m */; }; + DE33072B12D2ED1A009CC5E7 /* SPUtilsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = DE33072812D2ECB1009CC5E7 /* SPUtilsTest.m */; }; + DED8B48711B0827E00A1766C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; + DED8B48811B0827E00A1766C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE9CF1EB0FED3128008A32FF /* CoreGraphics.framework */; }; + DED8B48911B0827E00A1766C /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD15070DC6FC5B0079059D /* QuartzCore.framework */; }; + DED8B48A11B0827E00A1766C /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD14FF0DC6FC520079059D /* OpenGLES.framework */; }; + DED8B48B11B0827E00A1766C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; + DED8B48C11B0827E00A1766C /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEE63A7111AED4FA00D60321 /* OpenAL.framework */; }; + DED8B48D11B0827E00A1766C /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEE63A8511AED51400D60321 /* AudioToolbox.framework */; }; + DED9B51E10629D9F00989853 /* SPPoolObject.m in Sources */ = {isa = PBXBuildFile; fileRef = DED9B51C10629D9F00989853 /* SPPoolObject.m */; }; + DEE09D7B108364A900ECC896 /* SPBitmapFont.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE09D79108364A900ECC896 /* SPBitmapFont.m */; }; + DEE09D7F108369AE00ECC896 /* SPBitmapChar.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE09D7D108369AE00ECC896 /* SPBitmapChar.m */; }; + DEE63A5211AED38100D60321 /* SPAudioEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE63A4C11AED38100D60321 /* SPAudioEngine.m */; }; + DEE63A5411AED38100D60321 /* SPSound.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE63A4E11AED38100D60321 /* SPSound.m */; }; + DEE63A5611AED38100D60321 /* SPSoundChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE63A5011AED38100D60321 /* SPSoundChannel.m */; }; + DEE6516B11F9DF200065207E /* SPCompiledSprite.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2CC36011EF75B600439B43 /* SPCompiledSprite.m */; }; + DEE94E8311B43DE60000FE20 /* SPMovieClip.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE94E8111B43DE60000FE20 /* SPMovieClip.m */; }; + DEEA933A101E32E10071DD21 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE9CF1EB0FED3128008A32FF /* CoreGraphics.framework */; }; + DEEA933B101E32E10071DD21 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; + DEEA933C101E32E10071DD21 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD14FF0DC6FC520079059D /* OpenGLES.framework */; }; + DEEA933D101E32E10071DD21 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD15070DC6FC5B0079059D /* QuartzCore.framework */; }; + DEEA933E101E32E10071DD21 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; + DEEA934E101E33F00071DD21 /* libSparrow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DEFE4BC2101B317600E22471 /* libSparrow.a */; }; + DEED173A108A50000071438F /* SPTweenedProperty.m in Sources */ = {isa = PBXBuildFile; fileRef = DEED1738108A50000071438F /* SPTweenedProperty.m */; }; + DEEDE3A111B29908009FD4C8 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEE63A7111AED4FA00D60321 /* OpenAL.framework */; }; + DEEDE3A211B29908009FD4C8 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE18FE4411B083B200D01F04 /* AVFoundation.framework */; }; + DEEDE3A311B29909009FD4C8 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEE63A8511AED51400D60321 /* AudioToolbox.framework */; }; + DEF1730F11B0645A00A11DD7 /* SPALSound.m in Sources */ = {isa = PBXBuildFile; fileRef = DEF1730D11B0645A00A11DD7 /* SPALSound.m */; }; + DEF1731311B0648B00A11DD7 /* SPALSoundChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = DEF1731111B0648B00A11DD7 /* SPALSoundChannel.m */; }; + DEF1735D11B075B000A11DD7 /* SPAVSound.m in Sources */ = {isa = PBXBuildFile; fileRef = DEF1735B11B075B000A11DD7 /* SPAVSound.m */; }; + DEF1736111B075BC00A11DD7 /* SPAVSoundChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = DEF1735F11B075BC00A11DD7 /* SPAVSoundChannel.m */; }; + DEFE4BCE101B31DF00E22471 /* SPTransitions.m in Sources */ = {isa = PBXBuildFile; fileRef = DED859440FB883EE00D3D7D2 /* SPTransitions.m */; }; + DEFE4BD1101B31DF00E22471 /* SPView.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC2B67A0F6D4CE90063AB1D /* SPView.m */; }; + DEFE4BD2101B31DF00E22471 /* SPEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE593D20FA6116B00E3AEFC /* SPEvent.m */; }; + DEFE4BD3101B31DF00E22471 /* SPEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC2B6810F6D51430063AB1D /* SPEventDispatcher.m */; }; + DEFE4BD4101B31DF00E22471 /* SPTouch.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE4449D0FABA7860085D36D /* SPTouch.m */; }; + DEFE4BD5101B31DF00E22471 /* SPTouchEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = DEE444A90FABABBB0085D36D /* SPTouchEvent.m */; }; + DEFE4BD6101B31DF00E22471 /* SPEnterFrameEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = DEEEB8C90FAA2C300059D72B /* SPEnterFrameEvent.m */; }; + DEFE4BD7101B31DF00E22471 /* SPDisplayObject.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2ED8050F6D52080012B6BA /* SPDisplayObject.m */; }; + DEFE4BD8101B31DF00E22471 /* SPDisplayObjectContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2ED8090F6D53020012B6BA /* SPDisplayObjectContainer.m */; }; + DEFE4BD9101B31DF00E22471 /* SPSprite.m in Sources */ = {isa = PBXBuildFile; fileRef = DE4D6AEB0F75913D0045CBF7 /* SPSprite.m */; }; + DEFE4BDA101B31DF00E22471 /* SPStage.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2ED8590F6D54AC0012B6BA /* SPStage.m */; }; + DEFE4BDB101B31DF00E22471 /* SPQuad.m in Sources */ = {isa = PBXBuildFile; fileRef = DE2ED8560F6D54900012B6BA /* SPQuad.m */; }; + DEFE4BDC101B31DF00E22471 /* SPImage.m in Sources */ = {isa = PBXBuildFile; fileRef = DE08535D0FEC21F500DAF53C /* SPImage.m */; }; + DEFE4BDD101B31DF00E22471 /* SPTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DED4EFCA0FF9439D0093AD29 /* SPTextField.m */; }; + DEFE4BDE101B31DF00E22471 /* SPTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = DE0853F90FEC2CFF00DAF53C /* SPTexture.m */; }; + DEFE4BDF101B31DF00E22471 /* SPGLTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = DECF84320FF649D50026A4ED /* SPGLTexture.m */; }; + DEFE4BE0101B31DF00E22471 /* SPSubTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = DECF84BA0FF681BA0026A4ED /* SPSubTexture.m */; }; + DEFE4BE1101B31DF00E22471 /* SPButton.m in Sources */ = {isa = PBXBuildFile; fileRef = DED85312100BC9DA0014F729 /* SPButton.m */; }; + DEFE4BE2101B31DF00E22471 /* SPMatrix.m in Sources */ = {isa = PBXBuildFile; fileRef = DE469D260F9386FD00F56E91 /* SPMatrix.m */; }; + DEFE4BE3101B31DF00E22471 /* SPPoint.m in Sources */ = {isa = PBXBuildFile; fileRef = DE469D280F9386FD00F56E91 /* SPPoint.m */; }; + DEFE4BE4101B31DF00E22471 /* SPRectangle.m in Sources */ = {isa = PBXBuildFile; fileRef = DE469D2A0F9386FD00F56E91 /* SPRectangle.m */; }; + DEFE4BE6101B31DF00E22471 /* SPTextureAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = DECF84270FF619150026A4ED /* SPTextureAtlas.m */; }; + DEFE4C38101B5FB100E22471 /* SPRendering.m in Sources */ = {isa = PBXBuildFile; fileRef = DE9CF1BC0FED2678008A32FF /* SPRendering.m */; }; + DEFE4C3A101B5FB100E22471 /* SPTouchProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = DEDCD3AD0FADEE280022011C /* SPTouchProcessor.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + DEEA934C101E33ED0071DD21 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = DEFE4BC1101B317600E22471; + remoteInfo = SparrowLib; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 28FD14FF0DC6FC520079059D /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; + 28FD15070DC6FC5B0079059D /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + DE05748611E915A900F3A8A4 /* SPNSExtensionsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPNSExtensionsTest.m; sourceTree = ""; }; + DE08535C0FEC21F500DAF53C /* SPImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPImage.h; sourceTree = ""; }; + DE08535D0FEC21F500DAF53C /* SPImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPImage.m; sourceTree = ""; }; + DE0853A40FEC286900DAF53C /* SPImageTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPImageTest.m; sourceTree = ""; }; + DE0853F80FEC2CFF00DAF53C /* SPTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTexture.h; sourceTree = ""; }; + DE0853F90FEC2CFF00DAF53C /* SPTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTexture.m; sourceTree = ""; }; + DE13D18912AADBF6000C77E6 /* SPRenderTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPRenderTexture.h; sourceTree = ""; }; + DE13D18A12AADBF6000C77E6 /* SPRenderTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPRenderTexture.m; sourceTree = ""; }; + DE18FE4411B083B200D01F04 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + DE1F9446104704440084D470 /* SPJugglerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPJugglerTest.m; sourceTree = ""; }; + DE20D9C910713B0C006658C9 /* SPRenderSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPRenderSupport.h; sourceTree = ""; }; + DE20D9CA10713B0C006658C9 /* SPRenderSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPRenderSupport.m; sourceTree = ""; }; + DE2AD849104A84F1001AF0A0 /* SPStage_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPStage_Internal.h; sourceTree = ""; }; + DE2CC35F11EF75B600439B43 /* SPCompiledSprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPCompiledSprite.h; sourceTree = ""; }; + DE2CC36011EF75B600439B43 /* SPCompiledSprite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPCompiledSprite.m; sourceTree = ""; }; + DE2ED8040F6D52080012B6BA /* SPDisplayObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDisplayObject.h; sourceTree = ""; }; + DE2ED8050F6D52080012B6BA /* SPDisplayObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDisplayObject.m; sourceTree = ""; }; + DE2ED8080F6D53020012B6BA /* SPDisplayObjectContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDisplayObjectContainer.h; sourceTree = ""; }; + DE2ED8090F6D53020012B6BA /* SPDisplayObjectContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDisplayObjectContainer.m; sourceTree = ""; }; + DE2ED8550F6D54900012B6BA /* SPQuad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPQuad.h; sourceTree = ""; }; + DE2ED8560F6D54900012B6BA /* SPQuad.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPQuad.m; sourceTree = ""; }; + DE2ED8580F6D54AC0012B6BA /* SPStage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPStage.h; sourceTree = ""; }; + DE2ED8590F6D54AC0012B6BA /* SPStage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPStage.m; sourceTree = ""; }; + DE33072312D2EBCD009CC5E7 /* SPUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPUtils.h; sourceTree = ""; }; + DE33072412D2EBCD009CC5E7 /* SPUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPUtils.m; sourceTree = ""; }; + DE33072812D2ECB1009CC5E7 /* SPUtilsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPUtilsTest.m; sourceTree = ""; }; + DE469D240F9386FD00F56E91 /* SPMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPMacros.h; sourceTree = ""; }; + DE469D250F9386FD00F56E91 /* SPMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPMatrix.h; sourceTree = ""; }; + DE469D260F9386FD00F56E91 /* SPMatrix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMatrix.m; sourceTree = ""; }; + DE469D270F9386FD00F56E91 /* SPPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPoint.h; sourceTree = ""; }; + DE469D280F9386FD00F56E91 /* SPPoint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPoint.m; sourceTree = ""; }; + DE469D290F9386FD00F56E91 /* SPRectangle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPRectangle.h; sourceTree = ""; }; + DE469D2A0F9386FD00F56E91 /* SPRectangle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPRectangle.m; sourceTree = ""; }; + DE469D6E0F938FAB00F56E91 /* SPDisplayObjectTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDisplayObjectTest.m; sourceTree = ""; }; + DE4D6AEA0F75913D0045CBF7 /* SPSprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSprite.h; sourceTree = ""; }; + DE4D6AEB0F75913D0045CBF7 /* SPSprite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSprite.m; sourceTree = ""; }; + DE5286BA11F77C6200F916E8 /* SPDelayedInvocationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDelayedInvocationTest.m; sourceTree = ""; }; + DE68EA150FBB5660004DBC95 /* SPNSExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPNSExtensions.h; sourceTree = ""; }; + DE68EA160FBB5660004DBC95 /* SPNSExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPNSExtensions.m; sourceTree = ""; }; + DE7044260FB61506007F5ECC /* SPJuggler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPJuggler.h; sourceTree = ""; }; + DE7044270FB61506007F5ECC /* SPJuggler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPJuggler.m; sourceTree = ""; }; + DE70442A0FB618AE007F5ECC /* SPAnimatable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPAnimatable.h; sourceTree = ""; }; + DE7044750FB62080007F5ECC /* SPTween.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTween.h; sourceTree = ""; }; + DE7044760FB62080007F5ECC /* SPTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTween.m; sourceTree = ""; }; + DE75E8660FBDC57E00C64495 /* SPTweenTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTweenTest.m; sourceTree = ""; }; + DE8F1E2D0F7C1F3A0085E9E4 /* SPMatrixTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMatrixTest.m; sourceTree = ""; }; + DE9CF1BC0FED2678008A32FF /* SPRendering.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPRendering.m; sourceTree = ""; }; + DE9CF1EB0FED3128008A32FF /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + DEABCF5B0F7AE187003B6C9D /* SPPointTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPointTest.m; sourceTree = ""; }; + DEB21CF80F93C9780080D5C2 /* SPDisplayObjectContainerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPDisplayObjectContainerTest.m; sourceTree = ""; }; + DEC2B6790F6D4CE90063AB1D /* SPView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPView.h; sourceTree = ""; }; + DEC2B67A0F6D4CE90063AB1D /* SPView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPView.m; sourceTree = ""; }; + DEC2B6800F6D51430063AB1D /* SPEventDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEventDispatcher.h; sourceTree = ""; }; + DEC2B6810F6D51430063AB1D /* SPEventDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEventDispatcher.m; sourceTree = ""; }; + DEC54F6211B7765500E439B0 /* SPMovieClipTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMovieClipTest.m; sourceTree = ""; }; + DECF84260FF619150026A4ED /* SPTextureAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTextureAtlas.h; sourceTree = ""; }; + DECF84270FF619150026A4ED /* SPTextureAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTextureAtlas.m; sourceTree = ""; }; + DECF84310FF649D50026A4ED /* SPGLTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPGLTexture.h; sourceTree = ""; }; + DECF84320FF649D50026A4ED /* SPGLTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPGLTexture.m; sourceTree = ""; }; + DECF84B90FF681BA0026A4ED /* SPSubTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSubTexture.h; sourceTree = ""; }; + DECF84BA0FF681BA0026A4ED /* SPSubTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSubTexture.m; sourceTree = ""; }; + DED2B6F90FA0CF5900083578 /* SPQuadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPQuadTest.m; sourceTree = ""; }; + DED4EFC90FF9439D0093AD29 /* SPTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTextField.h; sourceTree = ""; }; + DED4EFCA0FF9439D0093AD29 /* SPTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTextField.m; sourceTree = ""; }; + DED67F330FA3514C0050E779 /* SPStageTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPStageTest.m; sourceTree = ""; }; + DED67F7C0FA359F00050E779 /* SPRectangleTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPRectangleTest.m; sourceTree = ""; }; + DED85311100BC9DA0014F729 /* SPButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPButton.h; sourceTree = ""; }; + DED85312100BC9DA0014F729 /* SPButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPButton.m; sourceTree = ""; }; + DED859430FB883EE00D3D7D2 /* SPTransitions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTransitions.h; sourceTree = ""; }; + DED859440FB883EE00D3D7D2 /* SPTransitions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTransitions.m; sourceTree = ""; }; + DED9B51B10629D9F00989853 /* SPPoolObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPoolObject.h; sourceTree = ""; }; + DED9B51C10629D9F00989853 /* SPPoolObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPoolObject.m; sourceTree = ""; }; + DEDCD3AC0FADEE280022011C /* SPTouchProcessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTouchProcessor.h; sourceTree = ""; }; + DEDCD3AD0FADEE280022011C /* SPTouchProcessor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTouchProcessor.m; sourceTree = ""; }; + DEDCD3CF0FADF52B0022011C /* SPTouch_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTouch_Internal.h; sourceTree = ""; }; + DEDCD44A0FADFF250022011C /* SPEvent_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEvent_Internal.h; sourceTree = ""; }; + DEDCD44E0FADFFA40022011C /* SPDisplayObject_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPDisplayObject_Internal.h; sourceTree = ""; }; + DEE09D78108364A900ECC896 /* SPBitmapFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPBitmapFont.h; sourceTree = ""; }; + DEE09D79108364A900ECC896 /* SPBitmapFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPBitmapFont.m; sourceTree = ""; }; + DEE09D7C108369AE00ECC896 /* SPB