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 chil