Fixes the mess with sparrow.
authordsc <david.schoonover@gmail.com>
Thu, 5 May 2011 02:58:51 +0000 (19:58 -0700)
committerdsc <david.schoonover@gmail.com>
Thu, 5 May 2011 02:58:51 +0000 (19:58 -0700)
126 files changed:
.gitignore
libs/sparrow/BUILDING [new file with mode: 0644]
libs/sparrow/CHANGELOG [new file with mode: 0644]
libs/sparrow/LICENSE [new file with mode: 0644]
libs/sparrow/README [new file with mode: 0644]
libs/sparrow/doc/generate.sh [new file with mode: 0755]
libs/sparrow/src/Classes/SPALSound.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPALSound.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPALSoundChannel.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPALSoundChannel.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPAVSound.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPAVSound.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPAVSoundChannel.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPAVSoundChannel.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPAnimatable.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPAudioEngine.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPAudioEngine.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPBitmapChar.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPBitmapChar.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPBitmapFont.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPBitmapFont.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPButton.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPButton.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPCompiledSprite.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPCompiledSprite.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPDelayedInvocation.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPDelayedInvocation.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPDisplayObject.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPDisplayObject.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPDisplayObjectContainer.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPDisplayObjectContainer.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPDisplayObject_Internal.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPEnterFrameEvent.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPEnterFrameEvent.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPEvent.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPEvent.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPEventDispatcher.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPEventDispatcher.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPEvent_Internal.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPGLTexture.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPGLTexture.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPImage.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPImage.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPJuggler.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPJuggler.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPMacros.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPMatrix.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPMatrix.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPMovieClip.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPMovieClip.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPNSExtensions.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPNSExtensions.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPPoint.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPPoint.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPPoolObject.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPPoolObject.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPQuad.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPQuad.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPRectangle.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPRectangle.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPRenderSupport.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPRenderSupport.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPRenderTexture.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPRenderTexture.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPRendering.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPSound.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPSound.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPSoundChannel.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPSoundChannel.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPSprite.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPSprite.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPStage.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPStage.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPStage_Internal.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPSubTexture.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPSubTexture.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPTextField.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPTextField.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPTexture.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPTexture.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPTextureAtlas.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPTextureAtlas.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPTouch.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPTouch.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPTouchEvent.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPTouchEvent.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPTouchProcessor.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPTouchProcessor.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPTouch_Internal.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPTransitions.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPTransitions.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPTween.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPTween.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPTweenedProperty.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPTweenedProperty.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPUtils.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPUtils.m [new file with mode: 0644]
libs/sparrow/src/Classes/SPView.h [new file with mode: 0644]
libs/sparrow/src/Classes/SPView.m [new file with mode: 0644]
libs/sparrow/src/Classes/Sparrow.h [new file with mode: 0644]
libs/sparrow/src/Sparrow.xcodeproj/project.pbxproj [new file with mode: 0755]
libs/sparrow/src/UnitTests-Info.plist [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPDelayedInvocationTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPDisplayObjectContainerTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPDisplayObjectTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPEventDispatcherTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPImageTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPJugglerTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPMatrixTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPMovieClipTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPNSExtensionsTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPPointTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPQuadTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPRectangleTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPStageTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPTweenTest.m [new file with mode: 0644]
libs/sparrow/src/UnitTests/SPUtilsTest.m [new file with mode: 0644]
libs/sparrow/util/atlas_generator/README [new file with mode: 0644]
libs/sparrow/util/atlas_generator/generate_atlas.rb [new file with mode: 0755]
libs/sparrow/util/hiero2sparrow/README [new file with mode: 0644]
libs/sparrow/util/hiero2sparrow/hiero2sparrow.rb [new file with mode: 0755]
libs/sparrow/util/packer2sparrow/README [new file with mode: 0644]
libs/sparrow/util/packer2sparrow/packer2sparrow.rb [new file with mode: 0644]
libs/sparrow/util/texture_scaler/README [new file with mode: 0644]
libs/sparrow/util/texture_scaler/scale_textures.rb [new file with mode: 0755]
tanks/tanks.xcodeproj/project.pbxproj

index edc082c..9fac711 100644 (file)
@@ -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 (file)
index 0000000..2f3e629
--- /dev/null
@@ -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 (file)
index 0000000..8aeb392
--- /dev/null
@@ -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 (file)
index 0000000..7164bd5
--- /dev/null
@@ -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 (file)
index 0000000..9d9715c
--- /dev/null
@@ -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 (executable)
index 0000000..c6bb5e6
--- /dev/null
@@ -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 (file)
index 0000000..20e250c
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#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 (file)
index 0000000..801960a
--- /dev/null
@@ -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 <OpenAL/al.h>
+#import <OpenAL/alc.h>
+
+@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 (file)
index 0000000..957277c
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#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 (file)
index 0000000..2ce4410
--- /dev/null
@@ -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 <QuartzCore/QuartzCore.h> // for CACurrentMediaTime
+#import <OpenAL/al.h>
+#import <OpenAL/alc.h>
+
+// --- 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 (file)
index 0000000..ce66c0b
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#import <AVFoundation/AVFoundation.h>
+
+#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 (file)
index 0000000..5b917e7
--- /dev/null
@@ -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 (file)
index 0000000..8d722ae
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#import <AVFoundation/AVFoundation.h>
+
+#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 <AVAudioPlayerDelegate> 
+{
+  @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 (file)
index 0000000..81c411c
--- /dev/null
@@ -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 (file)
index 0000000..4668d71
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+/** ------------------------------------------------------------------------------------------------
+ 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 (file)
index 0000000..d59fe5d
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+
+#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 (file)
index 0000000..3f8bd06
--- /dev/null
@@ -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 <AudioToolbox/AudioToolbox.h> 
+#import <OpenAL/al.h>
+#import <OpenAL/alc.h>
+
+@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 (file)
index 0000000..07804fa
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#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 <NSCopying> 
+{
+  @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 (file)
index 0000000..e3124c2
--- /dev/null
@@ -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 (file)
index 0000000..402ef57
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#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:
+       <font>
+         <info face="BranchingMouse" size="40" />
+         <common lineHeight="40" />
+         <pages>  <!-- currently, only one page is supported -->
+           <page id="0" file="texture.png" />
+         </pages>
+         <chars>
+           <char id="32" x="60" y="29" width="1" height="1" xoffset="0" yoffset="27" xadvance="8" />
+           <char id="33" x="155" y="144" width="9" height="21" xoffset="0" yoffset="6" xadvance="9" />
+         </chars>
+       </font>
+  
+ _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 <NSXMLParserDelegate>
+#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 (file)
index 0000000..ea81f05
--- /dev/null
@@ -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<text.length; i++)
+    {        
+        BOOL lineFull = NO;
+
+        int charID = [text characterAtIndex:i];    
+        if (charID == CHAR_NEWLINE)        
+        {
+            lineFull = YES;
+        }            
+        else 
+        {        
+            if (charID == CHAR_SPACE || charID == CHAR_TAB)        
+                lastWhiteSpace = i;        
+            
+            SPBitmapChar *bitmapChar = [self charByID:charID];
+            if (!bitmapChar) bitmapChar = [self charByID:CHAR_SPACE];
+            
+            bitmapChar.x = currentX + bitmapChar.xOffset;
+            bitmapChar.y = bitmapChar.yOffset;
+            bitmapChar.color = color;
+            [currentLine addChild:bitmapChar];
+            
+            currentX += bitmapChar.xAdvance;
+            
+            if (currentX > 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<numCharsToRemove; ++i)
+                    [currentLine removeChildAtIndex:removeIndex];
+                
+                if (currentLine.numChildren == 0)
+                    break;
+                
+                SPDisplayObject *lastChar = [currentLine childAtIndex:currentLine.numChildren-1];
+                currentX = lastChar.x + lastChar.width;
+                
+                i -= numCharsToRemove;
+                lineFull = YES;                
+            }
+        }
+        
+        if (lineFull || i == text.length - 1)
+        {
+            float nextLineY = currentLine.y + mLineHeight;             
+            [lineContainer addChild:currentLine];                        
+            
+            if (nextLineY + mLineHeight <= containerHeight)
+            {
+                currentLine = [SPSprite sprite];
+                currentLine.y = nextLineY;            
+                lastWhiteSpace = -1;
+                currentX = 0;
+            }
+            else
+            {
+                break;
+            }
+        }
+    }
+    
+    // hAlign
+    if (hAlign != SPHAlignLeft)
+    {
+        for (SPSprite *line in lineContainer)
+        {
+            SPDisplayObject *lastChar = [line childAtIndex:line.numChildren-1];
+            float lineWidth = lastChar.x + lastChar.width;
+            float widthDiff = containerWidth - lineWidth;
+            line.x = (int) (hAlign == SPHAlignRight ? widthDiff : widthDiff / 2);
+        }
+    }
+    
+    SPSprite *outerContainer = [SPCompiledSprite sprite];
+    [outerContainer addChild:lineContainer];    
+    
+    if (vAlign != SPVAlignTop)
+    {
+        float contentHeight = lineContainer.numChildren * mLineHeight * scale;
+        float heightDiff = height - contentHeight;
+        lineContainer.y = (int)(vAlign == SPVAlignBottom ? heightDiff : heightDiff / 2.0f);
+    }
+    
+    if (border)
+    {
+        SPQuad *topBorder = [SPQuad quadWithWidth:width height:1];
+        SPQuad *bottomBorder = [SPQuad quadWithWidth:width height:1];
+        SPQuad *leftBorder = [SPQuad quadWithWidth:1 height:height-2];
+        SPQuad *rightBorder = [SPQuad quadWithWidth:1 height:height-2];
+        
+        topBorder.color = bottomBorder.color = leftBorder.color = rightBorder.color = color;
+        bottomBorder.y = height - 1;
+        leftBorder.y = rightBorder.y = 1;
+        rightBorder.x = width - 1;
+        
+        [outerContainer addChild:topBorder];
+        [outerContainer addChild:bottomBorder];
+        [outerContainer addChild:leftBorder];
+        [outerContainer addChild:rightBorder];        
+    }    
+    
+    return outerContainer;
+}
+
+- (void)dealloc
+{
+    [mFontTexture release];
+    [mChars release];
+    [mName release];
+    [super dealloc];
+}
+
+@end
diff --git a/libs/sparrow/src/Classes/SPButton.h b/libs/sparrow/src/Classes/SPButton.h
new file mode 100644 (file)
index 0000000..f8c0214
--- /dev/null
@@ -0,0 +1,112 @@
+//
+//  SPButton.h
+//  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 <Foundation/Foundation.h>
+#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 (file)
index 0000000..c2cb93a
--- /dev/null
@@ -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 (file)
index 0000000..81bcce0
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#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 (file)
index 0000000..d400f74
--- /dev/null
@@ -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 <OpenGLES/EAGL.h>
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+
+// --- 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<numQuads; ++i)
+        {
+            indices[pos++] = (GLushort)(i*4);
+            for (int j=0; j<4; ++j) indices[pos++] = (GLushort)(i*4 + j);
+            indices[pos++] = (GLushort)(i*4 + 3);
+        }
+        
+        // index buffer
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSize * sizeof(GLushort), indices, GL_STATIC_DRAW);                
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+        free(indices);
+        
+        // vertex buffer
+        glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
+        glBufferData(GL_ARRAY_BUFFER, vertexData.length, vertexData.bytes, GL_STATIC_DRAW);
+
+        // color buffer
+        glBindBuffer(GL_ARRAY_BUFFER, mColorBuffer);
+        glBufferData(GL_ARRAY_BUFFER, colorData.length, colorData.bytes, GL_DYNAMIC_DRAW);
+                
+        // texture coordinate buffer
+        glBindBuffer(GL_ARRAY_BUFFER, mTexCoordBuffer);
+        glBufferData(GL_ARRAY_BUFFER, texCoordData.length, texCoordData.bytes, GL_STATIC_DRAW);        
+        
+        glBindBuffer(GL_ARRAY_BUFFER, 0);
+    }
+    else
+    {
+        [textures release];
+        [colorData release];
+        textures = nil;
+        colorData = nil;
+    }
+    
+    mTextureSwitches = textures;
+    mColorData = colorData;
+    
+    [matrices release];
+    [vertexData release];    
+    [texCoordData release];
+    
+    free(scratchBuffer);    
+    
+    SP_RELEASE_POOL(pool);    
+    return success;
+}
+
+- (BOOL)processVerticesOfObject:(SPDisplayObject *)object withMatrices:(NSMutableArray *)matrices
+                     vertexData:(NSMutableData *)vertexData buffer:(void *)buffer
+{
+    if (object.alpha == 0.0f || !object.visible) return YES;
+    SPMatrix *currentMatrix = [matrices lastObject]; 
+    BOOL success = YES;        
+        
+    if ([object isKindOfClass:[SPDisplayObjectContainer class]])
+    {
+        for (SPDisplayObject *child in (SPDisplayObjectContainer *)object)
+        {
+            SPMatrix *childMatrix = child.transformationMatrix;
+            [childMatrix concatMatrix:currentMatrix];
+            [matrices addObject:childMatrix];
+            
+            success = [self processVerticesOfObject:child withMatrices:matrices vertexData:vertexData
+                                             buffer:buffer];
+            [matrices removeLastObject];            
+            if (!success) break;
+        }
+    }
+    else if ([object isKindOfClass:[SPQuad class]])
+    {
+        SPQuad *quad = (SPQuad *)object;
+        float *vertexCoords = (float *)buffer;
+        [quad copyVertexCoords:vertexCoords colors:NULL textureCoords:NULL];            
+        
+        for (int i=0; i<4; ++i)
+        {
+            float x = vertexCoords[2*i];
+            float y = vertexCoords[2*i+1];                    
+            SPPoint *vertex = [currentMatrix transformPoint:[SPPoint pointWithX:x y:y]];
+            vertexCoords[2*i  ] = vertex.x;
+            vertexCoords[2*i+1] = vertex.y;            
+        }
+        
+        [vertexData appendBytes:buffer length:8 * sizeof(float)];
+    }
+    else
+    {
+        NSLog(@"Objects of type '%@' are not supported for compilation", [object class]);
+        success = NO;
+    }
+    
+    return success;
+}
+
+- (BOOL)processColorsOfObject:(SPDisplayObject *)object withAlpha:(float)alpha
+                    colorData:(NSMutableData *)colorData buffer:(void *)buffer
+{
+    if (alpha == 0.0f || !object.visible) return YES;
+    BOOL success = YES;
+    
+    if ([object isKindOfClass:[SPDisplayObjectContainer class]])
+    {
+        for (SPDisplayObject *child in (SPDisplayObjectContainer *)object)
+        {
+            success = [self processColorsOfObject:child withAlpha:alpha * child.alpha
+                                        colorData:colorData buffer:buffer];
+            if (!success) break;            
+        }        
+    }
+    else if ([object isKindOfClass:[SPQuad class]])
+    {
+        SPQuad *quad = (SPQuad *)object;
+        uint *colors = (uint *)buffer;            
+        [quad copyVertexCoords:NULL colors:colors textureCoords:NULL];
+        uint alphaBits = (GLubyte)(alpha * 255) << 24;
+        
+        // add alpha information
+        for (int i=0; i<4; ++i)        
+            colors[i] |= alphaBits;            
+        
+        [colorData appendBytes:colors length:4 * sizeof(uint)];
+    }
+    else
+    {            
+        success = NO;
+    }    
+    
+    return success;
+}
+
+- (BOOL)processTexturesOfObject:(SPDisplayObject *)object withTextures:(NSMutableArray *)textures
+                   texCoordData:(NSMutableData *)texCoordData buffer:(void *)buffer
+{
+    if (object.alpha == 0.0f || !object.visible) return YES;
+    BOOL success = YES;    
+        
+    if ([object isKindOfClass:[SPDisplayObjectContainer class]])
+    {
+        for (SPDisplayObject *child in (SPDisplayObjectContainer *)object)
+        {
+            success = [self processTexturesOfObject:child withTextures:textures
+                                       texCoordData:texCoordData buffer:buffer];
+        }
+    }
+    else if ([object isKindOfClass:[SPQuad class]])
+    {
+        SPQuad *quad = (SPQuad *)object;
+        SPImage *image = [object isKindOfClass:[SPImage class]] ? (SPImage *)object : nil;
+                
+        // process texture switches
+        // (textureData contains "texture, vertexCount, texture, vertexCount, etc.")
+        
+        id texture = image.texture;
+        id lastTexture = [textures objectAtIndex:textures.count-2];
+        if ([lastTexture isKindOfClass:[NSNull class]]) lastTexture = nil;
+        
+        uint textureID = [texture textureID];
+        uint lastTextureID = [lastTexture textureID];
+        uint lastTextureVertexCount = [[textures lastObject] intValue];
+        
+        if (textureID != lastTextureID)
+        {
+            [textures addObject:texture ? texture : [NSNull null]];
+            [textures addObject:[NSNumber numberWithInt:4]];
+        }
+        else
+        {
+            [textures removeLastObject];
+            [textures addObject:[NSNumber numberWithInt:lastTextureVertexCount + 4]];
+        }
+        
+        float *texCoords = (float *)buffer;
+        [quad copyVertexCoords:NULL colors:NULL textureCoords:texCoords];
+        
+        if (textureID)
+            [texture adjustTextureCoordinates:texCoords saveAtTarget:texCoords numVertices:4];
+        
+        [texCoordData appendBytes:texCoords length:8 * sizeof(float)];
+    }
+    else
+    {            
+        success = NO;
+    }
+        
+    return success;    
+}
+
+- (void)setAlpha:(float)value
+{
+    if (value != self.alpha)
+    {
+        mAlphaChanged = YES;
+        [super setAlpha:value];
+    }
+}
+
+- (void)render:(SPRenderSupport *)support
+{
+    if (!mTextureSwitches) [self compile];
+    if (!mCurrentColors || mAlphaChanged) [self updateColorData];
+    
+    int vertexOffset = 0;
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
+    
+    for (int i=0; i<mTextureSwitches.count; i+=2)
+    {
+        int numVertices = [[mTextureSwitches objectAtIndex:i+1] intValue];        
+        if (!numVertices) continue;
+        
+        id texture = [mTextureSwitches objectAtIndex:i];
+        if ([texture isKindOfClass:[NSNull class]]) texture = nil;
+        
+        int renderedVertices = (numVertices / 4) * 6;        
+        [support bindTexture:texture];        
+        
+        if (texture)
+        {        
+            glBindBuffer(GL_ARRAY_BUFFER, mTexCoordBuffer);
+            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+            glTexCoordPointer(2, GL_FLOAT, 0, 0);
+        }
+        
+        glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
+        glEnableClientState(GL_VERTEX_ARRAY);
+        glVertexPointer(2, GL_FLOAT, 0, 0);
+        
+        glBindBuffer(GL_ARRAY_BUFFER, mColorBuffer);
+        glEnableClientState(GL_COLOR_ARRAY);
+        glColorPointer(4, GL_UNSIGNED_BYTE, 0, 0);                
+        
+        glDrawElements(GL_TRIANGLE_STRIP, renderedVertices, GL_UNSIGNED_SHORT, 
+                       (void *)(vertexOffset * sizeof(GLushort)));
+        
+        glDisableClientState(GL_VERTEX_ARRAY);
+        glDisableClientState(GL_COLOR_ARRAY);
+        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+        
+        vertexOffset += renderedVertices;
+    }
+    
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+- (void)updateColorData
+{
+    if (!mCurrentColors) 
+        mCurrentColors = malloc(mColorData.length * sizeof(uint));        
+    
+    const uint *origColors = (const uint *)mColorData.bytes;
+    uint *newColors = mCurrentColors;
+    float alpha = self.alpha;
+    
+    for (int i=0; i<mTextureSwitches.count; i+=2)
+    {
+        int numVertices = [[mTextureSwitches objectAtIndex:i+1] intValue];        
+        if (!numVertices) continue;
+        
+        id texture = [mTextureSwitches objectAtIndex:i];
+        if ([texture isKindOfClass:[NSNull class]]) texture = nil;
+        BOOL pma = [texture hasPremultipliedAlpha];              
+        
+        for (int i=0; i<numVertices; ++i)
+        {
+            uint origColor = *origColors;
+            float vertexAlpha = (origColor >> 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 (file)
index 0000000..9dbea9f
--- /dev/null
@@ -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 <Foundation/Foundation.h>
+#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 <SPAnimatable>
+{
+  @private
+    id mTarget;
+    NSMutableSet *mInvocations;
+    double mTotalTime;
+    double mCurrentTime;</