From 54df41362c0a83ac44051dc64a9ed5981c9b8e5b Mon Sep 17 00:00:00 2001 From: dsc Date: Wed, 4 May 2011 19:58:51 -0700 Subject: [PATCH] Fixes the mess with sparrow. --- .gitignore | 2 +- libs/sparrow/BUILDING | 46 + libs/sparrow/CHANGELOG | 110 +++ libs/sparrow/LICENSE | 33 + libs/sparrow/README | 26 + libs/sparrow/doc/generate.sh | 33 + libs/sparrow/src/Classes/SPALSound.h | 45 + libs/sparrow/src/Classes/SPALSound.m | 94 +++ libs/sparrow/src/Classes/SPALSoundChannel.h | 46 + libs/sparrow/src/Classes/SPALSoundChannel.m | 217 +++++ libs/sparrow/src/Classes/SPAVSound.h | 46 + libs/sparrow/src/Classes/SPAVSound.m | 56 ++ libs/sparrow/src/Classes/SPAVSoundChannel.h | 43 + libs/sparrow/src/Classes/SPAVSoundChannel.m | 136 +++ libs/sparrow/src/Classes/SPAnimatable.h | 29 + libs/sparrow/src/Classes/SPAudioEngine.h | 64 ++ libs/sparrow/src/Classes/SPAudioEngine.m | 174 ++++ libs/sparrow/src/Classes/SPBitmapChar.h | 57 ++ libs/sparrow/src/Classes/SPBitmapChar.m | 55 ++ libs/sparrow/src/Classes/SPBitmapFont.h | 94 +++ libs/sparrow/src/Classes/SPBitmapFont.m | 289 +++++++ libs/sparrow/src/Classes/SPButton.h | 112 +++ libs/sparrow/src/Classes/SPButton.m | 261 ++++++ libs/sparrow/src/Classes/SPCompiledSprite.h | 61 ++ libs/sparrow/src/Classes/SPCompiledSprite.m | 436 ++++++++++ libs/sparrow/src/Classes/SPDelayedInvocation.h | 58 ++ libs/sparrow/src/Classes/SPDelayedInvocation.m | 93 +++ libs/sparrow/src/Classes/SPDisplayObject.h | 172 ++++ libs/sparrow/src/Classes/SPDisplayObject.m | 317 +++++++ .../sparrow/src/Classes/SPDisplayObjectContainer.h | 100 +++ .../sparrow/src/Classes/SPDisplayObjectContainer.m | 250 ++++++ .../sparrow/src/Classes/SPDisplayObject_Internal.h | 20 + libs/sparrow/src/Classes/SPEnterFrameEvent.h | 54 ++ libs/sparrow/src/Classes/SPEnterFrameEvent.m | 48 ++ libs/sparrow/src/Classes/SPEvent.h | 93 +++ libs/sparrow/src/Classes/SPEvent.m | 104 +++ libs/sparrow/src/Classes/SPEventDispatcher.h | 82 ++ libs/sparrow/src/Classes/SPEventDispatcher.m | 140 ++++ libs/sparrow/src/Classes/SPEvent_Internal.h | 23 + libs/sparrow/src/Classes/SPGLTexture.h | 82 ++ libs/sparrow/src/Classes/SPGLTexture.m | 183 ++++ libs/sparrow/src/Classes/SPImage.h | 72 ++ libs/sparrow/src/Classes/SPImage.m | 80 ++ libs/sparrow/src/Classes/SPJuggler.h | 88 ++ libs/sparrow/src/Classes/SPJuggler.m | 96 +++ libs/sparrow/src/Classes/SPMacros.h | 56 ++ libs/sparrow/src/Classes/SPMatrix.h | 99 +++ libs/sparrow/src/Classes/SPMatrix.m | 165 ++++ libs/sparrow/src/Classes/SPMovieClip.h | 136 +++ libs/sparrow/src/Classes/SPMovieClip.m | 274 ++++++ libs/sparrow/src/Classes/SPNSExtensions.h | 39 + libs/sparrow/src/Classes/SPNSExtensions.m | 50 ++ libs/sparrow/src/Classes/SPPoint.h | 82 ++ libs/sparrow/src/Classes/SPPoint.m | 142 ++++ libs/sparrow/src/Classes/SPPoolObject.h | 78 ++ libs/sparrow/src/Classes/SPPoolObject.m | 113 +++ libs/sparrow/src/Classes/SPQuad.h | 97 +++ libs/sparrow/src/Classes/SPQuad.m | 104 +++ libs/sparrow/src/Classes/SPRectangle.h | 77 ++ libs/sparrow/src/Classes/SPRectangle.m | 143 ++++ libs/sparrow/src/Classes/SPRenderSupport.h | 69 ++ libs/sparrow/src/Classes/SPRenderSupport.m | 125 +++ libs/sparrow/src/Classes/SPRenderTexture.h | 91 ++ libs/sparrow/src/Classes/SPRenderTexture.m | 210 +++++ libs/sparrow/src/Classes/SPRendering.m | 125 +++ libs/sparrow/src/Classes/SPSound.h | 72 ++ libs/sparrow/src/Classes/SPSound.m | 204 +++++ libs/sparrow/src/Classes/SPSoundChannel.h | 67 ++ libs/sparrow/src/Classes/SPSoundChannel.m | 91 ++ libs/sparrow/src/Classes/SPSprite.h | 48 ++ libs/sparrow/src/Classes/SPSprite.m | 22 + libs/sparrow/src/Classes/SPStage.h | 97 +++ libs/sparrow/src/Classes/SPStage.m | 234 ++++++ libs/sparrow/src/Classes/SPStage_Internal.h | 19 + libs/sparrow/src/Classes/SPSubTexture.h | 52 ++ libs/sparrow/src/Classes/SPSubTexture.m | 118 +++ libs/sparrow/src/Classes/SPTextField.h | 169 ++++ libs/sparrow/src/Classes/SPTextField.m | 329 ++++++++ libs/sparrow/src/Classes/SPTexture.h | 133 +++ libs/sparrow/src/Classes/SPTexture.m | 306 +++++++ libs/sparrow/src/Classes/SPTextureAtlas.h | 103 +++ libs/sparrow/src/Classes/SPTextureAtlas.m | 160 ++++ libs/sparrow/src/Classes/SPTouch.h | 105 +++ libs/sparrow/src/Classes/SPTouch.m | 109 +++ libs/sparrow/src/Classes/SPTouchEvent.h | 98 +++ libs/sparrow/src/Classes/SPTouchEvent.m | 92 ++ libs/sparrow/src/Classes/SPTouchProcessor.h | 52 ++ libs/sparrow/src/Classes/SPTouchProcessor.m | 121 +++ libs/sparrow/src/Classes/SPTouch_Internal.h | 28 + libs/sparrow/src/Classes/SPTransitions.h | 75 ++ libs/sparrow/src/Classes/SPTransitions.m | 171 ++++ libs/sparrow/src/Classes/SPTween.h | 124 +++ libs/sparrow/src/Classes/SPTween.m | 169 ++++ libs/sparrow/src/Classes/SPTweenedProperty.h | 62 ++ libs/sparrow/src/Classes/SPTweenedProperty.m | 110 +++ libs/sparrow/src/Classes/SPUtils.h | 27 + libs/sparrow/src/Classes/SPUtils.m | 34 + libs/sparrow/src/Classes/SPView.h | 78 ++ libs/sparrow/src/Classes/SPView.m | 314 +++++++ libs/sparrow/src/Classes/Sparrow.h | 45 + libs/sparrow/src/Sparrow.xcodeproj/project.pbxproj | 868 ++++++++++++++++++++ libs/sparrow/src/UnitTests-Info.plist | 20 + .../src/UnitTests/SPDelayedInvocationTest.m | 121 +++ .../src/UnitTests/SPDisplayObjectContainerTest.m | 416 ++++++++++ libs/sparrow/src/UnitTests/SPDisplayObjectTest.m | 256 ++++++ libs/sparrow/src/UnitTests/SPEventDispatcherTest.m | 155 ++++ libs/sparrow/src/UnitTests/SPImageTest.m | 58 ++ libs/sparrow/src/UnitTests/SPJugglerTest.m | 109 +++ libs/sparrow/src/UnitTests/SPMatrixTest.m | 164 ++++ libs/sparrow/src/UnitTests/SPMovieClipTest.m | 176 ++++ libs/sparrow/src/UnitTests/SPNSExtensionsTest.m | 38 + libs/sparrow/src/UnitTests/SPPointTest.m | 226 +++++ libs/sparrow/src/UnitTests/SPQuadTest.m | 70 ++ libs/sparrow/src/UnitTests/SPRectangleTest.m | 103 +++ libs/sparrow/src/UnitTests/SPStageTest.m | 47 ++ libs/sparrow/src/UnitTests/SPTweenTest.m | 254 ++++++ libs/sparrow/src/UnitTests/SPUtilsTest.m | 65 ++ libs/sparrow/util/atlas_generator/README | 54 ++ .../sparrow/util/atlas_generator/generate_atlas.rb | 255 ++++++ libs/sparrow/util/hiero2sparrow/README | 16 + libs/sparrow/util/hiero2sparrow/hiero2sparrow.rb | 82 ++ libs/sparrow/util/packer2sparrow/README | 11 + libs/sparrow/util/packer2sparrow/packer2sparrow.rb | 47 ++ libs/sparrow/util/texture_scaler/README | 35 + libs/sparrow/util/texture_scaler/scale_textures.rb | 96 +++ tanks/tanks.xcodeproj/project.pbxproj | 85 ++- 126 files changed, 14847 insertions(+), 13 deletions(-) create mode 100644 libs/sparrow/BUILDING create mode 100644 libs/sparrow/CHANGELOG create mode 100644 libs/sparrow/LICENSE create mode 100644 libs/sparrow/README create mode 100755 libs/sparrow/doc/generate.sh create mode 100644 libs/sparrow/src/Classes/SPALSound.h create mode 100644 libs/sparrow/src/Classes/SPALSound.m create mode 100644 libs/sparrow/src/Classes/SPALSoundChannel.h create mode 100644 libs/sparrow/src/Classes/SPALSoundChannel.m create mode 100644 libs/sparrow/src/Classes/SPAVSound.h create mode 100644 libs/sparrow/src/Classes/SPAVSound.m create mode 100644 libs/sparrow/src/Classes/SPAVSoundChannel.h create mode 100644 libs/sparrow/src/Classes/SPAVSoundChannel.m create mode 100644 libs/sparrow/src/Classes/SPAnimatable.h create mode 100644 libs/sparrow/src/Classes/SPAudioEngine.h create mode 100644 libs/sparrow/src/Classes/SPAudioEngine.m create mode 100644 libs/sparrow/src/Classes/SPBitmapChar.h create mode 100644 libs/sparrow/src/Classes/SPBitmapChar.m create mode 100644 libs/sparrow/src/Classes/SPBitmapFont.h create mode 100644 libs/sparrow/src/Classes/SPBitmapFont.m create mode 100644 libs/sparrow/src/Classes/SPButton.h create mode 100644 libs/sparrow/src/Classes/SPButton.m create mode 100644 libs/sparrow/src/Classes/SPCompiledSprite.h create mode 100644 libs/sparrow/src/Classes/SPCompiledSprite.m create mode 100644 libs/sparrow/src/Classes/SPDelayedInvocation.h create mode 100644 libs/sparrow/src/Classes/SPDelayedInvocation.m create mode 100644 libs/sparrow/src/Classes/SPDisplayObject.h create mode 100644 libs/sparrow/src/Classes/SPDisplayObject.m create mode 100644 libs/sparrow/src/Classes/SPDisplayObjectContainer.h create mode 100644 libs/sparrow/src/Classes/SPDisplayObjectContainer.m create mode 100644 libs/sparrow/src/Classes/SPDisplayObject_Internal.h create mode 100644 libs/sparrow/src/Classes/SPEnterFrameEvent.h create mode 100644 libs/sparrow/src/Classes/SPEnterFrameEvent.m create mode 100644 libs/sparrow/src/Classes/SPEvent.h create mode 100644 libs/sparrow/src/Classes/SPEvent.m create mode 100644 libs/sparrow/src/Classes/SPEventDispatcher.h create mode 100644 libs/sparrow/src/Classes/SPEventDispatcher.m create mode 100644 libs/sparrow/src/Classes/SPEvent_Internal.h create mode 100644 libs/sparrow/src/Classes/SPGLTexture.h create mode 100644 libs/sparrow/src/Classes/SPGLTexture.m create mode 100644 libs/sparrow/src/Classes/SPImage.h create mode 100644 libs/sparrow/src/Classes/SPImage.m create mode 100644 libs/sparrow/src/Classes/SPJuggler.h create mode 100644 libs/sparrow/src/Classes/SPJuggler.m create mode 100644 libs/sparrow/src/Classes/SPMacros.h create mode 100644 libs/sparrow/src/Classes/SPMatrix.h create mode 100644 libs/sparrow/src/Classes/SPMatrix.m create mode 100644 libs/sparrow/src/Classes/SPMovieClip.h create mode 100644 libs/sparrow/src/Classes/SPMovieClip.m create mode 100644 libs/sparrow/src/Classes/SPNSExtensions.h create mode 100644 libs/sparrow/src/Classes/SPNSExtensions.m create mode 100644 libs/sparrow/src/Classes/SPPoint.h create mode 100644 libs/sparrow/src/Classes/SPPoint.m create mode 100644 libs/sparrow/src/Classes/SPPoolObject.h create mode 100644 libs/sparrow/src/Classes/SPPoolObject.m create mode 100644 libs/sparrow/src/Classes/SPQuad.h create mode 100644 libs/sparrow/src/Classes/SPQuad.m create mode 100644 libs/sparrow/src/Classes/SPRectangle.h create mode 100644 libs/sparrow/src/Classes/SPRectangle.m create mode 100644 libs/sparrow/src/Classes/SPRenderSupport.h create mode 100644 libs/sparrow/src/Classes/SPRenderSupport.m create mode 100644 libs/sparrow/src/Classes/SPRenderTexture.h create mode 100644 libs/sparrow/src/Classes/SPRenderTexture.m create mode 100644 libs/sparrow/src/Classes/SPRendering.m create mode 100644 libs/sparrow/src/Classes/SPSound.h create mode 100644 libs/sparrow/src/Classes/SPSound.m create mode 100644 libs/sparrow/src/Classes/SPSoundChannel.h create mode 100644 libs/sparrow/src/Classes/SPSoundChannel.m create mode 100644 libs/sparrow/src/Classes/SPSprite.h create mode 100644 libs/sparrow/src/Classes/SPSprite.m create mode 100644 libs/sparrow/src/Classes/SPStage.h create mode 100644 libs/sparrow/src/Classes/SPStage.m create mode 100644 libs/sparrow/src/Classes/SPStage_Internal.h create mode 100644 libs/sparrow/src/Classes/SPSubTexture.h create mode 100644 libs/sparrow/src/Classes/SPSubTexture.m create mode 100644 libs/sparrow/src/Classes/SPTextField.h create mode 100644 libs/sparrow/src/Classes/SPTextField.m create mode 100644 libs/sparrow/src/Classes/SPTexture.h create mode 100644 libs/sparrow/src/Classes/SPTexture.m create mode 100644 libs/sparrow/src/Classes/SPTextureAtlas.h create mode 100644 libs/sparrow/src/Classes/SPTextureAtlas.m create mode 100644 libs/sparrow/src/Classes/SPTouch.h create mode 100644 libs/sparrow/src/Classes/SPTouch.m create mode 100644 libs/sparrow/src/Classes/SPTouchEvent.h create mode 100644 libs/sparrow/src/Classes/SPTouchEvent.m create mode 100644 libs/sparrow/src/Classes/SPTouchProcessor.h create mode 100644 libs/sparrow/src/Classes/SPTouchProcessor.m create mode 100644 libs/sparrow/src/Classes/SPTouch_Internal.h create mode 100644 libs/sparrow/src/Classes/SPTransitions.h create mode 100644 libs/sparrow/src/Classes/SPTransitions.m create mode 100644 libs/sparrow/src/Classes/SPTween.h create mode 100644 libs/sparrow/src/Classes/SPTween.m create mode 100644 libs/sparrow/src/Classes/SPTweenedProperty.h create mode 100644 libs/sparrow/src/Classes/SPTweenedProperty.m create mode 100644 libs/sparrow/src/Classes/SPUtils.h create mode 100644 libs/sparrow/src/Classes/SPUtils.m create mode 100644 libs/sparrow/src/Classes/SPView.h create mode 100644 libs/sparrow/src/Classes/SPView.m create mode 100644 libs/sparrow/src/Classes/Sparrow.h create mode 100755 libs/sparrow/src/Sparrow.xcodeproj/project.pbxproj create mode 100644 libs/sparrow/src/UnitTests-Info.plist create mode 100644 libs/sparrow/src/UnitTests/SPDelayedInvocationTest.m create mode 100644 libs/sparrow/src/UnitTests/SPDisplayObjectContainerTest.m create mode 100644 libs/sparrow/src/UnitTests/SPDisplayObjectTest.m create mode 100644 libs/sparrow/src/UnitTests/SPEventDispatcherTest.m create mode 100644 libs/sparrow/src/UnitTests/SPImageTest.m create mode 100644 libs/sparrow/src/UnitTests/SPJugglerTest.m create mode 100644 libs/sparrow/src/UnitTests/SPMatrixTest.m create mode 100644 libs/sparrow/src/UnitTests/SPMovieClipTest.m create mode 100644 libs/sparrow/src/UnitTests/SPNSExtensionsTest.m create mode 100644 libs/sparrow/src/UnitTests/SPPointTest.m create mode 100644 libs/sparrow/src/UnitTests/SPQuadTest.m create mode 100644 libs/sparrow/src/UnitTests/SPRectangleTest.m create mode 100644 libs/sparrow/src/UnitTests/SPStageTest.m create mode 100644 libs/sparrow/src/UnitTests/SPTweenTest.m create mode 100644 libs/sparrow/src/UnitTests/SPUtilsTest.m create mode 100644 libs/sparrow/util/atlas_generator/README create mode 100755 libs/sparrow/util/atlas_generator/generate_atlas.rb create mode 100644 libs/sparrow/util/hiero2sparrow/README create mode 100755 libs/sparrow/util/hiero2sparrow/hiero2sparrow.rb create mode 100644 libs/sparrow/util/packer2sparrow/README create mode 100644 libs/sparrow/util/packer2sparrow/packer2sparrow.rb create mode 100644 libs/sparrow/util/texture_scaler/README create mode 100755 libs/sparrow/util/texture_scaler/scale_textures.rb diff --git a/.gitignore b/.gitignore index edc082c..9fac711 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Xcode -build/* +*/build/* *.pbxuser !default.pbxuser *.mode1v3 diff --git a/libs/sparrow/BUILDING b/libs/sparrow/BUILDING new file mode 100644 index 0000000..2f3e629 --- /dev/null +++ b/libs/sparrow/BUILDING @@ -0,0 +1,46 @@ +---------------------------------------------------------------------------------------------------- +Sparrow: Tips for building an app +---------------------------------------------------------------------------------------------------- + +--- A First look at Sparrow ------------------------------------------------------------------------ + +In the folder 'samples/demo', you will find an Xcode project that shows the most basic Sparrow +features and how to use them. Just open the project in Xcode, compile and run - everything should +work out of the box. + +--- Creating a new Xcode project that uses Sparrow ------------------------------------------------- + +In the folder 'samples/scaffold', you will find an Xcode project that contains a bare-bone Sparrow +application. We recommend that you use this project as a starting point for your game. Please follow +this steps to use this Scaffold: + +Preconditions: + +Sparrow is linked to your application via Xcode project references. This has the advantage that it's +easy to update Sparrow (just download a new release and overwrite the old one in the same directory) +and that you can easily step into Sparrow source code, I case you want to do so. + +This has to be done only once: + +1. Set up a shared build output directory that will be shared by all Xcode projects. + * In the Xcode preferences, tab: "Building", set "Place Build Products in" to + "Customized location" and specify a common build directory (anywhere you want). + * Set "Place Intermediate Build Files in" to "With build products." +2. Add a "Source Tree" variable that Xcode can use to dynamically find Sparrow. + * In the Xcode preferences, tab: "Source Trees", create a new Source Tree variable. + * For Sparrow, create SPARROW_SRC and let it point to /some_valid_path/sparrow/src/ + +Creating your new project: + +1. Copy the "scaffold"-folder to the place where you want to have your game project. +2. Open "AppScaffold.xcodeproj" +3. Build and run - just to see if everything works fine. If it does not work, check if you have + created the SPARROW_SRC variable in Xcode, and if it points to the right place. +4. In Xcodes menu, click on "Project" -> "Rename ..." +5. Enter the name of your game. +6. That's it! Now you can start to develop your game with Sparrow. + +Selecting target hardware: iPhone / iPod Touch / iPad + +1. Enter the project Settings, tab: "Build" +2. Select the target of your choice for the setting "Targeted Device Family" \ No newline at end of file diff --git a/libs/sparrow/CHANGELOG b/libs/sparrow/CHANGELOG new file mode 100644 index 0000000..8aeb392 --- /dev/null +++ b/libs/sparrow/CHANGELOG @@ -0,0 +1,110 @@ +---------------------------------------------------------------------------------------------------- +Sparrow: Changelog +---------------------------------------------------------------------------------------------------- + +version 1.1 - 2011-01-12 + +- added SPRenderTexture +- added SPUtils class for easy random number generation (more to come) +- added support for looping and reversing tweens (thanks, Shilo!) +- added new transition method: 'randomize' +- added support for uncompressed PVR texture formats (565, 5551, 4444) +- added simple way to use HD textures on the iPad +- added support for creating dynamic texture atlases (add/remove regions on the fly) +- added methods to access the glyphs of SPBitmapFont directly +- added new init method to SPImage: 'initWithContentsOfImage:(UIImage *)image' +- added more factory methods to different classes +- added support for changing the fps of SPMovieClip at runtime (thanks Shilo!) +- added AppleDoc inline API documentation +- added bash script that generates API documentation +- updated SPView class to be more robust +- updated general rendering code (moved more OpenGL calls to SPRenderTexture) +- fixed bug with canceled touch events +- fixed bug that could cause a breakdown of the touch handling (thanks Kodi!) +- fixed 'isEqual' method of SPMatrix (thanks Matt!) +- fixed bug that could cut the outermost pixels of HD textures +- Special thanks to numerous forum members for bug reports, suggestions and feedback! + +version 1.0 - 2010-10-24 + +- added support for PVRTC-textures +- added new init method to SPTexture to allow simple use of Core Graphics drawings +- added new init method to SPMovieClip that uses an NSArray containing the frames +- added method 'childByName:' to SPDisplayObjectContainer +- added method 'texturesStartingWith:' to SPTextureAtlas +- added method 'scaleBy:' to SPPoint +- added method 'interpolateFromPoint:toPoint:ratio:' to SPPoint +- added property 'textBounds' to SPTextField +- added property 'name' to SPDisplayObject +- added workaround for unit test problem in Xcode 3.2.4 +- updated SPCompiledSprite-class to be public +- updated texture utilities for sharper output +- updated scaffold so that it is easier to create an iPad application +- fixed bug that classes inheriting directly from SPQuad were not rendered +- fixed that last-moment changes were not displayed when pausing stage +- fixed bug that could lead to a white flash when textures were released +- Special thanks to numerous forum members for bug reports, suggestions and feedback! + +version 0.9 - 2010-07-30 + +- !!! interface change !!! + The IDs of vertices in SPQuad and SPImage have changed. ("colorOfVertex:", "texCoordsOfVertex:") + Before: 0 = top left, 1 = top right, 2 = bottom right, 3 = bottom left + AFTER: 0 = top left, 1 = top right, 2 = bottom left, 3 = bottom right +- greatly improved rendering speed of SPTextField when used with bitmap fonts +- greatly improved performance of touch event analysis +- added Sparrow atlas generator (sparrow/util/atlas_generator) +- added Sparrow texture resizer (sparrow/util/texture_scaler) +- added support for high resolution screens (aka iPhone4's retina display) +- added support for high resolution textures ("texture@2x.png") +- added support for high resolution texture atlases ("atlas@2x.xml") +- added support for high resolution bitmap fonts ("font@2x.xml") +- added support for loading textures that are not inside the application bundle +- added support for loading sounds that are not inside the application bundle +- changed base SDK to iOS 4.0 +- added new event: SP_EVENT_TYPE_MOVIE_COMPLETED in SPMovieClip class +- added experimental feature: SPCompiledSprite +- added some missing "@private" declarations +- fixed memory access violation when object was destroyed within an enter frame event listener +- fixed bug that alpha values were only used when a texture was active +- fixed bug that SPDelayedInvocation (aka SPJuggler::delayInvocationAtTarget) did not retain + its arguments +- fixed bug that cancelled touch events would inhibit further user input +- code cleanup (especially concerning designated initializers) +- Special thanks to: Mike, Baike, Paolo, Jule and Alex_H for bug reports, suggestions and feedback! + +version 0.8 - 2010-06-05 + +- added audio classes +- added SPMovieClip class +- added new transition functions: + - easeInBack / easeOutBack / easeInOutBack / easeOutInBack + - easeInElastic / easeOutElastic / easeInOutElastic / easeOutInElastic + - easeInBounce / easeOutBounce / easeInOutBounce / easeOutInBounce +- added 'removeTweensWithTarget:'-method to juggler +- added 'removeAllChildren'-method to SPDisplayObjectContainer +- added new text-only constructor to SPTextField +- added NSXMLParserDelegate protocol statement for iPhone SDK 4+ +- added packer2sparrow utility +- added hiero2sparrow utility +- changed transition function signature (removed delta) +- changed rotation handling: angles now clamped from -180 to +180 degrees. + this should make most rotation tweens more intuitive. +- changed license text to allow easy AppStore distribution +- changed scaffold project to support audio +- changed demo project + - new design + - new scene: sound + - new scene: movie +- fixed touch issues when view size != stage size +- fixed exception that occurred when the same object was added to a container twice +- fixed flickering at application start +- fixed method signatures in SPTexture.m +- 'removeChild'-method in SPDisplayObjectContainer no longer throws an exception when the + object is not a child, but now silently ignores the failure. +- the stage property is now accessible in the REMOVED_FROM_STAGE event +- disabled unit test execution in iPhone SDK < 3 (unit tests are only supported by iPhone SDK 3+) + +version 0.7 - 2010-01-14 + +- first public version diff --git a/libs/sparrow/LICENSE b/libs/sparrow/LICENSE new file mode 100644 index 0000000..7164bd5 --- /dev/null +++ b/libs/sparrow/LICENSE @@ -0,0 +1,33 @@ +---------------------------------------------------------------------------------------------- +Sparrow: Simplified BSD License +---------------------------------------------------------------------------------------------- + +Copyright 2010 incognitek. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + 3. Redistributions via the Apple App Store constitute an exception to section 2. It is + sufficient to add a copy of this license to the application's internet page (if available). + The content of the following disclaimer is still in effect. + +THIS SOFTWARE IS PROVIDED BY INCOGNITEK ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INCOGNITEK OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of incognitek. \ No newline at end of file diff --git a/libs/sparrow/README b/libs/sparrow/README new file mode 100644 index 0000000..9d9715c --- /dev/null +++ b/libs/sparrow/README @@ -0,0 +1,26 @@ +---------------------------------------------------------------------------------------------------- +Sparrow: an Open Source Framework for iPhone game development +---------------------------------------------------------------------------------------------------- + +--- What is Sparrow? ------------------------------------------------------------------------------- + +Sparrow is a pure Objective C library targeted on making game development as easy and hassle-free +as possible. Sparrow makes it possible to write fast OpenGL applications without having to touch +OpenGL or pure C (but easily allowing to do so, for those who wish). It uses a tried and tested +API that is easy to use and hard to misuse. + +--- Who is Sparrow for? ---------------------------------------------------------------------------- + +Obviously Sparrow is for iPhone developers, especially those involved in game development. You +will need to have a basic understanding of Objective C – but there’s no way around that on the +iPhone anyway. + +If you have already worked with Adobe™ Flash/Flex technology, you will immediately befriend with +Sparrow since it uses lots of similar concepts and naming schemes. That said, everything is +designed to be as intuitive as possible, so any Java™ or .Net™ developer will get the hang of it +quickly as well. + +--- How to start? ---------------------------------------------------------------------------------- + +* Read through the file 'BUILDING' for a quick start with Sparrow. +* Visit --> http://www.sparrow-framework.org <-- \ No newline at end of file diff --git a/libs/sparrow/doc/generate.sh b/libs/sparrow/doc/generate.sh new file mode 100755 index 0000000..c6bb5e6 --- /dev/null +++ b/libs/sparrow/doc/generate.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# This script creates a nice API reference documentation for the Sparrow source +# and installs it in Xcode. +# +# To execute it, you need the "AppleDoc"-tool. Download it here: +# http://www.gentlebytes.com/home/appledocapp/ + +echo "Please enter the version number (like '1.0'), followed by [ENTER]:" +read version + +appledoc \ + --project-name "Sparrow Framework" \ + --project-company "Incognitek" \ + --company-id com.incognitek \ + --project-version "$version" \ + --ignore ".m" \ + --ignore "_Internal.h" \ + --keep-undocumented-objects \ + --keep-undocumented-members \ + --keep-intermediate-files \ + --docset-bundle-id "org.sparrow-framework.docset" \ + --docset-bundle-name "Sparrow Framework API Documentation" \ + --docset-atom-filename "docset.atom" \ + --docset-feed-url "http://doc.sparrow-framework.org/core/feed/%DOCSETATOMFILENAME" \ + --docset-package-url "http://doc.sparrow-framework.org/core/feed/%DOCSETPACKAGEFILENAME" \ + --install-docset \ + --publish-docset \ + --output . \ + ../src/Classes + +echo +echo "Finished." diff --git a/libs/sparrow/src/Classes/SPALSound.h b/libs/sparrow/src/Classes/SPALSound.h new file mode 100644 index 0000000..20e250c --- /dev/null +++ b/libs/sparrow/src/Classes/SPALSound.h @@ -0,0 +1,45 @@ +// +// SPALSound.h +// Sparrow +// +// Created by Daniel Sperl on 28.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPSound.h" + +/** ------------------------------------------------------------------------------------------------ + + The SPALSound class is a concrete implementation of SPSound that uses OpenAL internally. + + Don't create instances of this class manually. Use [SPSound initWithContentsOfFile:] instead. + +------------------------------------------------------------------------------------------------- */ + +@interface SPALSound : SPSound +{ + @private + uint mBufferID; + double mDuration; +} + +/// -------------------- +/// @name Initialization +/// -------------------- + +/// Initializes a sound with its known properties. +- (id)initWithData:(const void *)data size:(int)size channels:(int)channels frequency:(int)frequency + duration:(double)duration; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The OpenAL buffer ID of the sound. +@property (nonatomic, readonly) uint bufferID; + +@end diff --git a/libs/sparrow/src/Classes/SPALSound.m b/libs/sparrow/src/Classes/SPALSound.m new file mode 100644 index 0000000..801960a --- /dev/null +++ b/libs/sparrow/src/Classes/SPALSound.m @@ -0,0 +1,94 @@ +// +// SPALSound.m +// Sparrow +// +// Created by Daniel Sperl on 28.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPALSound.h" +#import "SPALSoundChannel.h" +#import "SPAudioEngine.h" + +#import +#import + +@implementation SPALSound + +@synthesize duration = mDuration; +@synthesize bufferID = mBufferID; + +- (id)init +{ + [self release]; + return nil; +} + +- (id)initWithData:(void *)data size:(int)size channels:(int)channels frequency:(int)frequency + duration:(double)duration +{ + if (self = [super init]) + { + BOOL success = NO; + mDuration = duration; + + do + { + [SPAudioEngine start]; + + ALCcontext *const currentContext = alcGetCurrentContext(); + if (!currentContext) + { + NSLog(@"Could not get current OpenAL context"); + break; + } + + ALenum errorCode; + + alGenBuffers(1, &mBufferID); + errorCode = alGetError(); + if (errorCode != AL_NO_ERROR) + { + NSLog(@"Could not allocate OpenAL buffer (%x)", errorCode); + break; + } + + int format = (channels > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; + + alBufferData(mBufferID, format, data, size, frequency); + errorCode = alGetError(); + if (errorCode != AL_NO_ERROR) + { + NSLog(@"Could not fill OpenAL buffer (%x)", errorCode); + break; + } + + success = YES; + } + while (NO); + + if (!success) + { + [self release]; + return nil; + } + } + return self; +} + +- (SPSoundChannel *)createChannel +{ + return [[[SPALSoundChannel alloc] initWithSound:self] autorelease]; +} + +- (void) dealloc +{ + alDeleteBuffers(1, &mBufferID); + mBufferID = 0; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPALSoundChannel.h b/libs/sparrow/src/Classes/SPALSoundChannel.h new file mode 100644 index 0000000..957277c --- /dev/null +++ b/libs/sparrow/src/Classes/SPALSoundChannel.h @@ -0,0 +1,46 @@ +// +// SPALSoundChannel.h +// Sparrow +// +// Created by Daniel Sperl on 28.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPSoundChannel.h" + +@class SPALSound; + +/** ------------------------------------------------------------------------------------------------ + + The SPALSoundChannel class is a concrete implementation of SPSoundChannel that uses + OpenAL internally. + + Don't create instances of this class manually. Use [SPSound createChannel] instead. + +------------------------------------------------------------------------------------------------- */ + +@interface SPALSoundChannel : SPSoundChannel +{ + @private + SPALSound *mSound; + uint mSourceID; + float mVolume; + BOOL mLoop; + + double mStartMoment; + double mPauseMoment; + BOOL mInterrupted; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a sound channel from an SPALSound object. +- (id)initWithSound:(SPALSound *)sound; + +@end diff --git a/libs/sparrow/src/Classes/SPALSoundChannel.m b/libs/sparrow/src/Classes/SPALSoundChannel.m new file mode 100644 index 0000000..2ce4410 --- /dev/null +++ b/libs/sparrow/src/Classes/SPALSoundChannel.m @@ -0,0 +1,217 @@ +// +// SPALSoundChannel.m +// Sparrow +// +// Created by Daniel Sperl on 28.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPALSoundChannel.h" +#import "SPALSound.h" +#import "SPAudioEngine.h" +#import "SPMacros.h" + +#import // for CACurrentMediaTime +#import +#import + +// --- private interface --------------------------------------------------------------------------- + +@interface SPALSoundChannel () + +- (void)scheduleSoundCompletedEvent; +- (void)revokeSoundCompletedEvent; + +@end + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPALSoundChannel + +@synthesize volume = mVolume; +@synthesize loop = mLoop; + +- (id)init +{ + [self release]; + return nil; +} + +- (id)initWithSound:(SPALSound *)sound +{ + if (self = [super init]) + { + mSound = [sound retain]; + mVolume = 1.0f; + mLoop = NO; + mInterrupted = NO; + mStartMoment = 0.0; + mPauseMoment = 0.0; + + alGenSources(1, &mSourceID); + alSourcei(mSourceID, AL_BUFFER, sound.bufferID); + ALenum errorCode = alGetError(); + if (errorCode != AL_NO_ERROR) + { + NSLog(@"Could not create OpenAL source (%x)", errorCode); + [self release]; + return nil; + } + + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc addObserver:self selector:@selector(onInterruptionBegan:) + name:SP_NOTIFICATION_AUDIO_INTERRUPTION_BEGAN object:nil]; + [nc addObserver:self selector:@selector(onInterruptionEnded:) + name:SP_NOTIFICATION_AUDIO_INTERRUPTION_ENDED object:nil]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + alSourceStop(mSourceID); + alSourcei(mSourceID, AL_BUFFER, 0); + alDeleteSources(1, &mSourceID); + mSourceID = 0; + [mSound release]; + [super dealloc]; +} + +- (void)play +{ + if (!self.isPlaying) + { + double now = CACurrentMediaTime(); + + if (mPauseMoment != 0.0) // paused + { + mStartMoment += now - mPauseMoment; + mPauseMoment = 0.0; + } + else // stopped + { + mStartMoment = now; + } + + [self scheduleSoundCompletedEvent]; + alSourcePlay(mSourceID); + } +} + +- (void)pause +{ + if (self.isPlaying) + { + [self revokeSoundCompletedEvent]; + mPauseMoment = CACurrentMediaTime(); + alSourcePause(mSourceID); + } +} + +- (void)stop +{ + [self revokeSoundCompletedEvent]; + mStartMoment = mPauseMoment = 0.0; + alSourceStop(mSourceID); +} + +- (BOOL)isPlaying +{ + ALint state; + alGetSourcei(mSourceID, AL_SOURCE_STATE, &state); + return state == AL_PLAYING; +} + +- (BOOL)isPaused +{ + ALint state; + alGetSourcei(mSourceID, AL_SOURCE_STATE, &state); + return state == AL_PAUSED; +} + +- (BOOL)isStopped +{ + ALint state; + alGetSourcei(mSourceID, AL_SOURCE_STATE, &state); + return state == AL_STOPPED; +} + +- (void)setLoop:(BOOL)value +{ + if (value != mLoop) + { + mLoop = value; + alSourcei(mSourceID, AL_LOOPING, mLoop); + } +} + +- (void)setVolume:(float)value +{ + if (value != mVolume) + { + mVolume = value; + alSourcef(mSourceID, AL_GAIN, mVolume); + } +} + +- (double)duration +{ + return [mSound duration]; +} + +- (void)scheduleSoundCompletedEvent +{ + if (mStartMoment != 0.0) + { + double remainingTime = mSound.duration - (CACurrentMediaTime() - mStartMoment); + [self revokeSoundCompletedEvent]; + if (remainingTime >= 0.0) + { + [self performSelector:@selector(dispatchSoundCompletedEvent) withObject:nil + afterDelay:remainingTime]; + } + } +} + +- (void)revokeSoundCompletedEvent +{ + [NSObject cancelPreviousPerformRequestsWithTarget:self + selector:@selector(dispatchSoundCompletedEvent) object:nil]; +} + +- (void)dispatchSoundCompletedEvent +{ + if (!mLoop) + { + SPEvent *event = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_SOUND_COMPLETED]; + [self dispatchEvent:event]; + [event release]; + } +} + +- (void)onInterruptionBegan:(NSNotification *)notification +{ + if (self.isPlaying) + { + [self revokeSoundCompletedEvent]; + mInterrupted = YES; + mPauseMoment = CACurrentMediaTime(); + } +} + +- (void)onInterruptionEnded:(NSNotification *)notification +{ + if (mInterrupted) + { + mStartMoment += CACurrentMediaTime() - mPauseMoment; + mPauseMoment = 0.0; + mInterrupted = NO; + [self scheduleSoundCompletedEvent]; + } +} + +@end diff --git a/libs/sparrow/src/Classes/SPAVSound.h b/libs/sparrow/src/Classes/SPAVSound.h new file mode 100644 index 0000000..ce66c0b --- /dev/null +++ b/libs/sparrow/src/Classes/SPAVSound.h @@ -0,0 +1,46 @@ +// +// SPAVSound.h +// Sparrow +// +// Created by Daniel Sperl on 29.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import + +#import "SPSound.h" + +/** ------------------------------------------------------------------------------------------------ + + The SPAVSound class is a concrete implementation of SPSound that uses AVAudioPlayer internally. + + Don't create instances of this class manually. Use [SPSound initWithContentsOfFile:] instead. + + */ + +@interface SPAVSound : SPSound +{ + @private + NSData *mSoundData; + double mDuration; +} + +/// -------------------- +/// @name Initialization +/// -------------------- + +/// Initializes a sound with the contents of a file and the known duration. +- (id)initWithContentsOfFile:(NSString *)path duration:(double)duration; + +/// ------------- +/// @name methods +/// ------------- + +/// Creates an AVAudioPlayer object from the sound. +- (AVAudioPlayer *)createPlayer; + +@end diff --git a/libs/sparrow/src/Classes/SPAVSound.m b/libs/sparrow/src/Classes/SPAVSound.m new file mode 100644 index 0000000..5b917e7 --- /dev/null +++ b/libs/sparrow/src/Classes/SPAVSound.m @@ -0,0 +1,56 @@ +// +// SPAVSound.m +// Sparrow +// +// Created by Daniel Sperl on 29.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPAVSound.h" +#import "SPAVSoundChannel.h" + +@implementation SPAVSound + +@synthesize duration = mDuration; + +- (id)init +{ + [self release]; + return nil; +} + +- (id)initWithContentsOfFile:(NSString *)path duration:(double)duration +{ + if (self = [super init]) + { + NSString *fullPath = [path isAbsolutePath] ? + path : [[NSBundle mainBundle] pathForResource:path ofType:nil]; + mSoundData = [[NSData alloc] initWithContentsOfMappedFile:fullPath]; + mDuration = duration; + } + return self; +} + +- (void)dealloc +{ + [mSoundData release]; + [super dealloc]; +} + +- (SPSoundChannel *)createChannel +{ + return [[[SPAVSoundChannel alloc] initWithSound:self] autorelease]; +} + +- (AVAudioPlayer *)createPlayer +{ + NSError *error = nil; + AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithData:mSoundData error:&error]; + if (error) NSLog(@"Could not create AVAudioPlayer: %@", [error description]); + return [player autorelease]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPAVSoundChannel.h b/libs/sparrow/src/Classes/SPAVSoundChannel.h new file mode 100644 index 0000000..8d722ae --- /dev/null +++ b/libs/sparrow/src/Classes/SPAVSoundChannel.h @@ -0,0 +1,43 @@ +// +// SPAVSoundChannel.h +// Sparrow +// +// Created by Daniel Sperl on 29.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import + +#import "SPSoundChannel.h" +#import "SPAVSound.h" + +/** ------------------------------------------------------------------------------------------------ + + The SPAVSoundChannel class is a concrete implementation of SPSoundChannel that uses AVAudioPlayer + internally. + + Don't create instances of this class manually. Use [SPSound createChannel] instead. + +------------------------------------------------------------------------------------------------- */ + +@interface SPAVSoundChannel : SPSoundChannel +{ + @private + SPAVSound *mSound; + AVAudioPlayer *mPlayer; + BOOL mPaused; + float mVolume; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a sound channel from an SPAVSound object. +- (id)initWithSound:(SPAVSound *)sound; + +@end diff --git a/libs/sparrow/src/Classes/SPAVSoundChannel.m b/libs/sparrow/src/Classes/SPAVSoundChannel.m new file mode 100644 index 0000000..81c411c --- /dev/null +++ b/libs/sparrow/src/Classes/SPAVSoundChannel.m @@ -0,0 +1,136 @@ +// +// SPAVSoundChannel.m +// Sparrow +// +// Created by Daniel Sperl on 29.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPAVSoundChannel.h" +#import "SPAudioEngine.h" +#import "SPMacros.h" + +@implementation SPAVSoundChannel + +- (id)init +{ + [self release]; + return nil; +} + +- (id)initWithSound:(SPAVSound *)sound +{ + if (self = [super init]) + { + mVolume = 1.0f; + mSound = [sound retain]; + mPlayer = [[sound createPlayer] retain]; + mPlayer.delegate = self; + [mPlayer prepareToPlay]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(onMasterVolumeChanged:) + name:SP_NOTIFICATION_MASTER_VOLUME_CHANGED object:nil]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [mPlayer release]; + [mSound release]; + [super dealloc]; +} + +- (void)play +{ + mPaused = NO; + [mPlayer play]; +} + +- (void)pause +{ + mPaused = YES; + [mPlayer pause]; +} + +- (void)stop +{ + mPaused = NO; + [mPlayer stop]; + mPlayer.currentTime = 0; +} + +- (BOOL)isPlaying +{ + return mPlayer.playing; +} + +- (BOOL)isPaused +{ + return mPaused && !mPlayer.playing; +} + +- (BOOL)isStopped +{ + return !mPaused && !mPlayer.playing; +} + +- (BOOL)loop +{ + return mPlayer.numberOfLoops < 0; +} + +- (void)setLoop:(BOOL)value +{ + mPlayer.numberOfLoops = value ? -1 : 0; +} + +- (float)volume +{ + return mVolume; +} + +- (void)setVolume:(float)value +{ + mVolume = value; + mPlayer.volume = value * [SPAudioEngine masterVolume]; +} + +- (double)duration +{ + return mPlayer.duration; +} + +- (void)onMasterVolumeChanged:(NSNotification *)notification +{ + self.volume = mVolume; +} + +#pragma mark AVAudioPlayerDelegate + +- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag +{ + [self dispatchEvent:[SPEvent eventWithType:SP_EVENT_TYPE_SOUND_COMPLETED]]; +} + +- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error +{ + NSLog(@"Error during sound decoding: %@", [error description]); +} + +- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player +{ + [player pause]; +} + +- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player +{ + [player play]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPAnimatable.h b/libs/sparrow/src/Classes/SPAnimatable.h new file mode 100644 index 0000000..4668d71 --- /dev/null +++ b/libs/sparrow/src/Classes/SPAnimatable.h @@ -0,0 +1,29 @@ +// +// SPAnimatable.h +// Sparrow +// +// Created by Daniel Sperl on 09.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +/** ------------------------------------------------------------------------------------------------ + + The SPAnimatable protocol describes objects that are animated depending on the passed time. + Any object that implements this protocol can be added to the SPJuggler. + +------------------------------------------------------------------------------------------------- */ + +@protocol SPAnimatable + +/// Advance the animation by a number of seconds. +- (void)advanceTime:(double)seconds; + +/// Indicates if the animation is finished. (The juggler will purge the object.) +@property (nonatomic, readonly) BOOL isComplete; + +@end diff --git a/libs/sparrow/src/Classes/SPAudioEngine.h b/libs/sparrow/src/Classes/SPAudioEngine.h new file mode 100644 index 0000000..d59fe5d --- /dev/null +++ b/libs/sparrow/src/Classes/SPAudioEngine.h @@ -0,0 +1,64 @@ +// +// SPAudioEngine.h +// Sparrow +// +// Created by Daniel Sperl on 14.11.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +#define SP_NOTIFICATION_MASTER_VOLUME_CHANGED @"masterVolumeChanged" +#define SP_NOTIFICATION_AUDIO_INTERRUPTION_BEGAN @"audioInterruptionBegan" +#define SP_NOTIFICATION_AUDIO_INTERRUPTION_ENDED @"audioInterruptionEnded" + +typedef enum { + SPAudioSessionCategory_AmbientSound = 'ambi', + SPAudioSessionCategory_SoloAmbientSound = 'solo', + SPAudioSessionCategory_MediaPlayback = 'medi', + SPAudioSessionCategory_RecordAudio = 'reca', + SPAudioSessionCategory_PlayAndRecord = 'plar', + SPAudioSessionCategory_AudioProcessing = 'proc' +} SPAudioSessionCategory; + +/** ------------------------------------------------------------------------------------------------ + + The SPAudioEngine prepares the system for audio playback and controls global volume. + + Before you play sounds, you should start an audio session. The type of the audio session + defines how iOS will handle audio processing and how iPod music will mix with your audio. + + * `SPAudioSessionCategory_AmbientSound:` iPod music mixes with your audio, audio silences on mute + * `SPAudioSessionCategory_SoloAmbientSound:` iPod music is silenced, audio silences on mute + * `SPAudioSessionCategory_MediaPlayback:` iPod music is silenced, audio continues on mute + * `SPAudioSessionCategory_RecordAudio:` iPod music is silenced, used for audio recording + * `SPAudioSessionCategory_PlayAndRecord:` iPod music is silenced, for simultaneous in- and output + * `SPAudioSessionCategory_AudioProcessing:` For using an audio hardware codec or signal processor + + */ + +@interface SPAudioEngine : NSObject + +/// ------------- +/// @name Methods +/// ------------- + +/// Starts an audio session with a specified category. Call this at the start of your application. ++ (void)start:(SPAudioSessionCategory)category; + +/// Starts an audio session with with the category 'SoloAmbientSound'. ++ (void)start; + +/// Stops the audio session. Call this before the application shuts down. ++ (void)stop; + +/// The master volume for all audio. Default: 1.0 ++ (float)masterVolume; + +/// Set the master volume for all audio. Range: [0.0 - 1.0] ++ (void)setMasterVolume:(float)volume; + +@end diff --git a/libs/sparrow/src/Classes/SPAudioEngine.m b/libs/sparrow/src/Classes/SPAudioEngine.m new file mode 100644 index 0000000..3f8bd06 --- /dev/null +++ b/libs/sparrow/src/Classes/SPAudioEngine.m @@ -0,0 +1,174 @@ +// +// SPAudioEngine.m +// Sparrow +// +// Created by Daniel Sperl on 14.11.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPAudioEngine.h" + +#import +#import +#import + +@interface SPAudioEngine () + ++ (BOOL)initAudioSession:(SPAudioSessionCategory)category; ++ (BOOL)initOpenAL; + ++ (void)beginInterruption; ++ (void)endInterruption; + ++ (void)postNotification:(NSString *)name object:(id)object; + +@end + +@implementation SPAudioEngine + +// --- C functions --- + +void interruptionCallback (void *inUserData, UInt32 interruptionState) +{ + if (interruptionState == kAudioSessionBeginInterruption) + [SPAudioEngine beginInterruption]; + else if (interruptionState == kAudioSessionEndInterruption) + [SPAudioEngine endInterruption]; +} + +// --- static members --- + +static ALCdevice *device = NULL; +static ALCcontext *context = NULL; +static float masterVolume = 1.0f; + +// --- + +- (id)init +{ + [self release]; + [NSException raise:NSGenericException format:@"Static class - do not initialize!"]; + return nil; +} + ++ (void)start:(SPAudioSessionCategory)category +{ + if (!device) + { + if ([SPAudioEngine initAudioSession:category]) + [SPAudioEngine initOpenAL]; + } +} + ++ (void)start +{ + [SPAudioEngine start:SPAudioSessionCategory_SoloAmbientSound]; +} + ++ (void)stop +{ + alcMakeContextCurrent(NULL); + alcDestroyContext(context); + alcCloseDevice(device); + + device = NULL; + context = NULL; + + AudioSessionSetActive(NO); +} + ++ (BOOL)initAudioSession:(SPAudioSessionCategory)category +{ + static BOOL sessionInitialized = NO; + OSStatus result; + + if (!sessionInitialized) + { + result = AudioSessionInitialize(NULL, NULL, interruptionCallback, NULL); + if (result != kAudioSessionNoError) + { + NSLog(@"Could not initialize audio session: %x", result); + return NO; + } + sessionInitialized = YES; + } + + UInt32 sessionCategory = category; + AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, + sizeof(sessionCategory), &sessionCategory); + + result = AudioSessionSetActive(YES); + if (result != kAudioSessionNoError) + { + NSLog(@"Could not activate audio session: %x", result); + return NO; + } + + return YES; +} + ++ (BOOL)initOpenAL +{ + alGetError(); // reset any errors + + device = alcOpenDevice(NULL); + if (!device) + { + NSLog(@"Could not open default OpenAL device"); + return NO; + } + + context = alcCreateContext(device, 0); + if (!context) + { + NSLog(@"Could not create OpenAL context for default device"); + return NO; + } + + BOOL success = alcMakeContextCurrent(context); + if (!success) + { + NSLog(@"Could not set current OpenAL context"); + return NO; + } + + return YES; +} + ++ (void)beginInterruption +{ + [SPAudioEngine postNotification:SP_NOTIFICATION_AUDIO_INTERRUPTION_BEGAN object:nil]; + alcMakeContextCurrent(NULL); + AudioSessionSetActive(NO); +} + ++ (void)endInterruption +{ + AudioSessionSetActive(YES); + alcMakeContextCurrent(context); + alcProcessContext(context); + [SPAudioEngine postNotification:SP_NOTIFICATION_AUDIO_INTERRUPTION_ENDED object:nil]; +} + ++ (float)masterVolume +{ + return masterVolume; +} + ++ (void)setMasterVolume:(float)volume +{ + masterVolume = volume; + alListenerf(AL_GAIN, volume); + [SPAudioEngine postNotification:SP_NOTIFICATION_MASTER_VOLUME_CHANGED object:nil]; +} + ++ (void)postNotification:(NSString *)name object:(id)object +{ + [[NSNotificationCenter defaultCenter] postNotification: + [NSNotification notificationWithName:name object:object]]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPBitmapChar.h b/libs/sparrow/src/Classes/SPBitmapChar.h new file mode 100644 index 0000000..07804fa --- /dev/null +++ b/libs/sparrow/src/Classes/SPBitmapChar.h @@ -0,0 +1,57 @@ +// +// SPBitmapChar.h +// Sparrow +// +// Created by Daniel Sperl on 12.10.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPImage.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPBitmapChar is an image that contains one char of a bitmap font. Its properties contain all + the information that is needed to arrange the char in a text. + + _You don't have to use this class directly in most cases._ + +------------------------------------------------------------------------------------------------- */ + +@interface SPBitmapChar : SPImage +{ + @private + int mCharID; + float mXOffset; + float mYOffset; + float mXAdvance; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a char with a texture and his properties. +- (id)initWithID:(int)charID texture:(SPTexture *)texture + xOffset:(float)xOffset yOffset:(float)yOffset xAdvance:(float)xAdvance; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The unicode ID of the char. +@property (nonatomic, readonly) int charID; + +/// The number of pixels to move the char in x direction on character arrangement. +@property (nonatomic, readonly) float xOffset; + +/// The number of pixels to move the char in y direction on character arrangement. +@property (nonatomic, readonly) float yOffset; + +/// The number of pixels the cursor has to be moved to the right for the next char. +@property (nonatomic, readonly) float xAdvance; + +@end diff --git a/libs/sparrow/src/Classes/SPBitmapChar.m b/libs/sparrow/src/Classes/SPBitmapChar.m new file mode 100644 index 0000000..e3124c2 --- /dev/null +++ b/libs/sparrow/src/Classes/SPBitmapChar.m @@ -0,0 +1,55 @@ +// +// SPBitmapChar.m +// Sparrow +// +// Created by Daniel Sperl on 12.10.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPBitmapChar.h" +#import "SPTexture.h" + +@implementation SPBitmapChar + +@synthesize charID = mCharID; +@synthesize xOffset = mXOffset; +@synthesize yOffset = mYOffset; +@synthesize xAdvance = mXAdvance; + +- (id)initWithID:(int)charID texture:(SPTexture *)texture + xOffset:(float)xOffset yOffset:(float)yOffset xAdvance:(float)xAdvance; +{ + if (self = [super initWithTexture:texture]) + { + mCharID = charID; + mXOffset = xOffset; + mYOffset = yOffset; + mXAdvance = xAdvance; + } + return self; +} + +- (id)initWithTexture:(SPTexture *)texture +{ + return [self initWithID:0 texture:texture xOffset:0 yOffset:0 xAdvance:texture.width]; +} + +- (id)init +{ + [self release]; + return nil; +} + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone*)zone; +{ + return [[[self class] allocWithZone:zone] initWithID:mCharID texture:self.texture + xOffset:mXOffset yOffset:mYOffset + xAdvance:mXAdvance]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPBitmapFont.h b/libs/sparrow/src/Classes/SPBitmapFont.h new file mode 100644 index 0000000..402ef57 --- /dev/null +++ b/libs/sparrow/src/Classes/SPBitmapFont.h @@ -0,0 +1,94 @@ +// +// SPBitmapFont.h +// Sparrow +// +// Created by Daniel Sperl on 12.10.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPBitmapChar.h" +#import "SPTextField.h" +#import "SPMacros.h" + +@class SPTexture; +@class SPDisplayObject; + +/** ------------------------------------------------------------------------------------------------ + + The SPBitmapFont class parses bitmap font files and arranges the glyphs in the form of a text. + + The class parses the XML format as it is used in the AngelCode Bitmap Font Generator. This is what + the file format looks like: + + + + + + + + + + + + + + _You don't have to use this class directly in most cases. SPTextField contains methods that + handle bitmap fonts for you._ + +------------------------------------------------------------------------------------------------- */ + +#ifdef __IPHONE_4_0 +@interface SPBitmapFont : NSObject +#else +@interface SPBitmapFont : NSObject +#endif +{ + @private + SPTexture *mFontTexture; + NSMutableDictionary *mChars; + NSString *mName; + float mSize; + float mLineHeight; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a bitmap font by parsing an XML file and uses the specified texture. +- (id)initWithContentsOfFile:(NSString *)path texture:(SPTexture *)texture; + +/// Initializes a bitmap font by parsing an XML file and loads the texture that is specified there. +- (id)initWithContentsOfFile:(NSString *)path; + +/// ------------- +/// @name Methods +/// ------------- + +/// Creates a single bitmap char with a certain character ID. +- (SPBitmapChar *)charByID:(int)charID; + +/// Creates a display object that contains the given text by arranging individual chars. +- (SPDisplayObject *)createDisplayObjectWithWidth:(float)width height:(float)height + text:(NSString *)text fontSize:(float)size color:(uint)color + hAlign:(SPHAlign)hAlign vAlign:(SPVAlign)vAlign + border:(BOOL)border; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The name of the font as it was parsed from the font file. +@property (nonatomic, readonly) NSString *name; + +/// The native size of the font. +@property (nonatomic, readonly) float size; + +/// The height of one line in pixels. +@property (nonatomic, assign) float lineHeight; + +@end diff --git a/libs/sparrow/src/Classes/SPBitmapFont.m b/libs/sparrow/src/Classes/SPBitmapFont.m new file mode 100644 index 0000000..ea81f05 --- /dev/null +++ b/libs/sparrow/src/Classes/SPBitmapFont.m @@ -0,0 +1,289 @@ +// +// SPBitmapFont.m +// Sparrow +// +// Created by Daniel Sperl on 12.10.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPBitmapFont.h" +#import "SPBitmapChar.h" +#import "SPTexture.h" +#import "SPRectangle.h" +#import "SPSubTexture.h" +#import "SPDisplayObject.h" +#import "SPSprite.h" +#import "SPImage.h" +#import "SPTextField.h" +#import "SPStage.h" +#import "SPNSExtensions.h" +#import "SPCompiledSprite.h" + +#define CHAR_SPACE 32 +#define CHAR_TAB 9 +#define CHAR_NEWLINE 10 + +// --- private interface --------------------------------------------------------------------------- + +@interface SPBitmapFont () + +- (void)parseFontXml:(NSString*)path; + +@end + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPBitmapFont + +@synthesize name = mName; +@synthesize lineHeight = mLineHeight; +@synthesize size = mSize; + +- (id)initWithContentsOfFile:(NSString *)path texture:(SPTexture *)texture +{ + if (self = [super init]) + { + mName = [[NSString alloc] initWithString:@"unknown"]; + mLineHeight = mSize = SP_DEFAULT_FONT_SIZE; + mFontTexture = [texture retain]; + + [self parseFontXml:path]; + } + return self; +} + +- (id)initWithContentsOfFile:(NSString *)path +{ + return [self initWithContentsOfFile:path texture:nil]; +} + +- (id)init +{ + [self release]; + return nil; +} + +- (void)parseFontXml:(NSString*)path +{ + SP_CREATE_POOL(pool); + + [mChars release]; + mChars = [[NSMutableDictionary alloc] init]; + + if (!path) return; + + float scale = [SPStage contentScaleFactor]; + NSString *fullPath = [[NSBundle mainBundle] pathForResource:path withScaleFactor:scale]; + NSURL *xmlUrl = [NSURL fileURLWithPath:fullPath]; + NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlUrl]; + xmlParser.delegate = self; + BOOL success = [xmlParser parse]; + + SP_RELEASE_POOL(pool); + + if (!success) + [NSException raise:SP_EXC_FILE_INVALID + format:@"could not parse bitmap font xml %@. Error code: %d, domain: %@", + path, xmlParser.parserError.code, xmlParser.parserError.domain]; + + [xmlParser release]; +} + +- (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName + namespaceURI:(NSString*)namespaceURI + qualifiedName:(NSString*)qName + attributes:(NSDictionary*)attributeDict +{ + if ([elementName isEqualToString:@"char"]) + { + int charID = [[attributeDict valueForKey:@"id"] intValue]; + float scale = mFontTexture.scale; + + SPRectangle *region = [[SPRectangle alloc] init]; + region.x = [[attributeDict valueForKey:@"x"] floatValue] / scale; + region.y = [[attributeDict valueForKey:@"y"] floatValue] / scale; + region.width = [[attributeDict valueForKey:@"width"] floatValue] / scale; + region.height = [[attributeDict valueForKey:@"height"] floatValue] / scale; + SPSubTexture *texture = [[SPSubTexture alloc] initWithRegion:region ofTexture:mFontTexture]; + [region release]; + + float xOffset = [[attributeDict valueForKey:@"xoffset"] floatValue] / scale; + float yOffset = [[attributeDict valueForKey:@"yoffset"] floatValue] / scale; + float xAdvance = [[attributeDict valueForKey:@"xadvance"] floatValue] / scale; + + SPBitmapChar *bitmapChar = [[SPBitmapChar alloc] initWithID:charID texture:texture + xOffset:xOffset yOffset:yOffset + xAdvance:xAdvance]; + [texture release]; + + [mChars setObject:bitmapChar forKey:[NSNumber numberWithInt:charID]]; + [bitmapChar release]; + } + else if ([elementName isEqualToString:@"info"]) + { + [mName release]; + mName = [[attributeDict valueForKey:@"face"] copy]; + mSize = [[attributeDict valueForKey:@"size"] floatValue]; + } + else if ([elementName isEqualToString:@"common"]) + { + mLineHeight = [[attributeDict valueForKey:@"lineHeight"] floatValue]; + } + else if ([elementName isEqualToString:@"page"]) + { + int id = [[attributeDict valueForKey:@"id"] intValue]; + if (id != 0) [NSException raise:SP_EXC_FILE_INVALID + format:@"Bitmap fonts with multiple pages are not supported"]; + if (!mFontTexture) + { + NSString *filename = [attributeDict valueForKey:@"file"]; + mFontTexture = [[SPTexture alloc] initWithContentsOfFile:filename]; + + // update sizes, now that we know the scale setting + mSize /= mFontTexture.scale; + mLineHeight /= mFontTexture.scale; + } + } +} + +- (SPBitmapChar *)charByID:(int)charID +{ + SPBitmapChar *bitmapChar = (SPBitmapChar *)[mChars objectForKey:[NSNumber numberWithInt:charID]]; + return [[bitmapChar copy] autorelease]; +} + +- (SPDisplayObject *)createDisplayObjectWithWidth:(float)width height:(float)height + text:(NSString *)text fontSize:(float)size color:(uint)color + hAlign:(SPHAlign)hAlign vAlign:(SPVAlign)vAlign + border:(BOOL)border +{ + SPSprite *lineContainer = [SPSprite sprite]; + + if (size == SP_NATIVE_FONT_SIZE) size = mSize; + float scale = size / mSize; + lineContainer.scaleX = lineContainer.scaleY = scale; + float containerWidth = width / scale; + float containerHeight = height / scale; + + int lastWhiteSpace = -1; + float currentX = 0; + SPSprite *currentLine = [SPSprite sprite]; + + for (int i=0; i containerWidth) + { + // remove characters and add them again to next line + int numCharsToRemove = lastWhiteSpace == -1 ? 1 : i - lastWhiteSpace; + int removeIndex = currentLine.numChildren - numCharsToRemove; + + for (int i=0; i +#import "SPDisplayObjectContainer.h" + +@class SPTexture; +@class SPImage; +@class SPTextField; +@class SPSprite; + +#define SP_EVENT_TYPE_TRIGGERED @"triggered" + +/** ------------------------------------------------------------------------------------------------ + + An SPButton is a simple button composed of an image and, optionally, text. + + You can pass a texture for up- and downstate of the button. If you do not provide a down stage, + the button is simply scaled a little when it is touched. + + In addition, you can overlay a text on the button. To customize the text, almost the same options + as those of SPTextField are provided. In addition, you can move the text to a certain position + with the help of the `textBounds` property. + + To react on touches on a button, there is special event type: `SP_EVENT_TYPE_TRIGGERED`. Use + this event instead of normal touch events - that way, the button will behave just like standard + iOS interface buttons. + +------------------------------------------------------------------------------------------------- */ + +@interface SPButton : SPDisplayObjectContainer +{ + @private + SPTexture *mUpState; + SPTexture *mDownState; + + SPSprite *mContents; + SPImage *mBackground; + SPTextField *mTextField; + SPRectangle *mTextBounds; + + float mScaleWhenDown; + float mAlphaWhenDisabled; + BOOL mEnabled; + BOOL mIsDown; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a button with textures for up- and down-state. _Designated Initializer_. +- (id)initWithUpState:(SPTexture*)upState downState:(SPTexture*)downState; + +/// Initializes a button with an up state texture and text. +- (id)initWithUpState:(SPTexture*)upState text:(NSString*)text; + +/// Initializes a button only with an up state. +- (id)initWithUpState:(SPTexture*)upState; + +/// Factory method. ++ (SPButton*)buttonWithUpState:(SPTexture*)upState downState:(SPTexture*)downState; + +/// Factory method. ++ (SPButton*)buttonWithUpState:(SPTexture*)upState text:(NSString*)text; + +/// Factory method. ++ (SPButton*)buttonWithUpState:(SPTexture*)upState; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The scale factor of the button on touch. Per default, a button with a down state texture won't scale. +@property (nonatomic, assign) float scaleWhenDown; + +/// The alpha value of the button when it is disabled. +@property (nonatomic, assign) float alphaWhenDisabled; + +/// Indicates if the button can be triggered. +@property (nonatomic, assign) BOOL enabled; + +/// The text that is displayed on the button. +@property (nonatomic, copy) NSString *text; + +/// The name of the font displayed on the button. May be a system font or a registered bitmap font. +@property (nonatomic, copy) NSString *fontName; + +/// The size of the font. +@property (nonatomic, assign) float fontSize; + +/// The color of the font. +@property (nonatomic, assign) uint fontColor; + +/// The texture that is displayed when the button is not being touched. +@property (nonatomic, retain) SPTexture *upState; + +/// The texture that is displayed while the button is touched. +@property (nonatomic, retain) SPTexture *downState; + +/// The bounds of the textfield on the button. Allows moving the text to a custom position. +@property (nonatomic, copy) SPRectangle *textBounds; + +@end diff --git a/libs/sparrow/src/Classes/SPButton.m b/libs/sparrow/src/Classes/SPButton.m new file mode 100644 index 0000000..c2cb93a --- /dev/null +++ b/libs/sparrow/src/Classes/SPButton.m @@ -0,0 +1,261 @@ +// +// SPButton.m +// Sparrow +// +// Created by Daniel Sperl on 13.07.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPButton.h" +#import "SPTouchEvent.h" +#import "SPTexture.h" +#import "SPGLTexture.h" +#import "SPImage.h" +#import "SPStage.h" +#import "SPSprite.h" +#import "SPTextField.h" + +// --- private interface --------------------------------------------------------------------------- + +@interface SPButton() + +- (void)resetContents; +- (void)createTextField; + +@end + + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPButton + +@synthesize scaleWhenDown = mScaleWhenDown; +@synthesize alphaWhenDisabled = mAlphaWhenDisabled; +@synthesize enabled = mEnabled; +@synthesize upState = mUpState; +@synthesize downState = mDownState; +@synthesize textBounds = mTextBounds; + +#define MAX_DRAG_DIST 40 + +- (id)initWithUpState:(SPTexture*)upState downState:(SPTexture*)downState; +{ + if (self = [super init]) + { + mUpState = [upState retain]; + mDownState = [downState retain]; + mContents = [[SPSprite alloc] init]; + mBackground = [[SPImage alloc] initWithTexture:upState]; + mTextField = nil; + mScaleWhenDown = 1.0f; + mAlphaWhenDisabled = 0.5f; + mEnabled = YES; + mIsDown = NO; + mTextBounds = [[SPRectangle alloc] initWithX:0 y:0 width:mUpState.width height:mUpState.height]; + + [mContents addChild:mBackground]; + [self addChild:mContents]; + [self addEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH]; + } + return self; +} + +- (id)initWithUpState:(SPTexture*)upState text:(NSString*)text +{ + self = [self initWithUpState:upState]; + self.text = text; + return self; +} + +- (id)initWithUpState:(SPTexture*)upState; +{ + self = [self initWithUpState:upState downState:upState]; + mScaleWhenDown = 0.9f; + return self; +} + +- (id)init +{ + SPTexture *texture = [[[SPGLTexture alloc] init] autorelease]; + return [self initWithUpState:texture]; +} + +- (void)onTouch:(SPTouchEvent*)touchEvent +{ + if (!mEnabled) return; + SPTouch *touch = [[touchEvent touchesWithTarget:self] anyObject]; + + if (touch.phase == SPTouchPhaseBegan) + { + mBackground.texture = mDownState; + mContents.scaleX = mContents.scaleY = mScaleWhenDown; + mContents.x = (1.0f - mScaleWhenDown) / 2.0f * mDownState.width; + mContents.y = (1.0f - mScaleWhenDown) / 2.0f * mDownState.height; + mIsDown = YES; + } + else if (touch.phase == SPTouchPhaseMoved && mIsDown) + { + // reset button when user dragged to far away after pushing + SPRectangle *buttonRect = [self boundsInSpace:self.stage]; + if (touch.globalX < buttonRect.x - MAX_DRAG_DIST || + touch.globalY < buttonRect.y - MAX_DRAG_DIST || + touch.globalX > buttonRect.x + buttonRect.width + MAX_DRAG_DIST || + touch.globalY > buttonRect.y + buttonRect.height + MAX_DRAG_DIST) + { + [self resetContents]; + } + } + else if (touch.phase == SPTouchPhaseEnded && mIsDown) + { + [self resetContents]; + [self dispatchEvent:[SPEvent eventWithType:SP_EVENT_TYPE_TRIGGERED]]; + } + else if (touch.phase == SPTouchPhaseCancelled && mIsDown) + { + [self resetContents]; + } +} + +- (void)resetContents +{ + mIsDown = NO; + mBackground.texture = mUpState; + mContents.x = mContents.y = 0; + mContents.scaleX = mContents.scaleY = 1.0f; +} + +- (void)setEnabled:(BOOL)value +{ + mEnabled = value; + if (mEnabled) + { + mContents.alpha = 1.0f; + } + else + { + mContents.alpha = mAlphaWhenDisabled; + [self resetContents]; + } +} + +- (void)setUpState:(SPTexture*)upState +{ + if (upState != mUpState) + { + [mUpState release]; + mUpState = [upState retain]; + if (!mIsDown) mBackground.texture = upState; + } +} + +- (void)setDownState:(SPTexture*)downState +{ + if (downState != mDownState) + { + [mDownState release]; + mDownState = [downState retain]; + if (mIsDown) mBackground.texture = downState; + } +} + +- (void)createTextField +{ + if (!mTextField) + { + mTextField = [[SPTextField alloc] initWithWidth:100 height:100 text:@""]; + mTextField.vAlign = SPVAlignCenter; + mTextField.hAlign = SPHAlignCenter; + [mContents addChild:mTextField]; + } + + mTextField.width = mTextBounds.width; + mTextField.height = mTextBounds.height; + mTextField.x = mTextBounds.x; + mTextField.y = mTextBounds.y; +} + +- (NSString*)text +{ + if (mTextField) return mTextField.text; + else return @""; +} + +- (void)setText:(NSString*)value +{ + [self createTextField]; + mTextField.text = value; +} + +- (void)setTextBounds:(SPRectangle *)value +{ + mTextBounds = [value copy]; + [self createTextField]; +} + +- (NSString*)fontName +{ + if (mTextField) return mTextField.fontName; + else return SP_DEFAULT_FONT_NAME; +} + +- (void)setFontName:(NSString*)value +{ + [self createTextField]; + mTextField.fontName = value; +} + +- (float)fontSize +{ + if (mTextField) return mTextField.fontSize; + else return SP_DEFAULT_FONT_SIZE; +} + +- (void)setFontSize:(float)value +{ + [self createTextField]; + mTextField.fontSize = value; +} + +- (uint)fontColor +{ + if (mTextField) return mTextField.color; + else return SP_DEFAULT_FONT_COLOR; +} + +- (void)setFontColor:(uint)value +{ + [self createTextField]; + mTextField.color = value; +} + ++ (SPButton*)buttonWithUpState:(SPTexture*)upState downState:(SPTexture*)downState +{ + return [[[SPButton alloc] initWithUpState:upState downState:downState] autorelease]; +} + ++ (SPButton*)buttonWithUpState:(SPTexture*)upState text:(NSString*)text +{ + return [[[SPButton alloc] initWithUpState:upState text:text] autorelease]; +} + ++ (SPButton*)buttonWithUpState:(SPTexture*)upState +{ + return [[[SPButton alloc] initWithUpState:upState] autorelease]; +} + +- (void)dealloc +{ + [self removeEventListenersAtObject:self forType:SP_EVENT_TYPE_TOUCH]; + [mTextBounds release]; + [mUpState release]; + [mDownState release]; + [mBackground release]; + [mTextField release]; + [mContents release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPCompiledSprite.h b/libs/sparrow/src/Classes/SPCompiledSprite.h new file mode 100644 index 0000000..81bcce0 --- /dev/null +++ b/libs/sparrow/src/Classes/SPCompiledSprite.h @@ -0,0 +1,61 @@ +// +// SPCompiledContainer.h +// Sparrow +// +// Created by Daniel Sperl on 15.07.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPSprite.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPCompiledSprite allows you to optimize the rendering of static parts of your display list. + + It analyzes the tree of children attached to it and optimizes the OpenGL rendering calls in a + way that makes rendering them extremely fast. The downside is that you will no longe see any + changes in the properties of the childs (position, rotation, alpha, etc.). To update the object + after changes have happened, simply call `compile` again. + + With the exception of this peculiarity, a compiled sprite can be use just like any other sprite. + + SPCompiledSprite *sprite = [SPCompiledSprite sprite]; + [sprite addChild:object1]; + [sprite addChild:object2]; + + [sprite compile]; // this call is optional, it will be done on rendering automatically. + +------------------------------------------------------------------------------------------------- */ + +@interface SPCompiledSprite : SPSprite +{ + @private + NSArray *mTextureSwitches; + NSMutableData *mColorData; + uint *mCurrentColors; + BOOL mAlphaChanged; + + uint mIndexBuffer; + uint mVertexBuffer; + uint mColorBuffer; + uint mTexCoordBuffer; +} + +/// ------------- +/// @name Methods +/// ------------- + +/// Compiles the children of the sprite to optimize rendering. After compilation, no changes in +/// the children will show up. Call the method again to make changes visible. +/// +/// @return Returns `YES` if compilation was successful. On error, it will `NSLog` the problem. +- (BOOL)compile; + +/// Factory method. ++ (SPCompiledSprite *)sprite; + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPCompiledSprite.m b/libs/sparrow/src/Classes/SPCompiledSprite.m new file mode 100644 index 0000000..d400f74 --- /dev/null +++ b/libs/sparrow/src/Classes/SPCompiledSprite.m @@ -0,0 +1,436 @@ +// +// SPCompiledContainer.m +// Sparrow +// +// Created by Daniel Sperl on 15.07.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPCompiledSprite.h" +#import "SPDisplayObjectContainer.h" +#import "SPPoint.h" +#import "SPMatrix.h" +#import "SPImage.h" +#import "SPQuad.h" +#import "SPTexture.h" +#import "SPRenderSupport.h" +#import "SPMacros.h" + +#import +#import +#import + +// --- SPQuad extension ---------------------------------------------------------------------------- + +@interface SPQuad (SPCompiledSprite_Extension) + +- (void)copyVertexCoords:(float *)vertexCoords colors:(uint *)colors textureCoords:(float *)texCoords; + +@end + +@implementation SPQuad (SPCompiledSprite_Extension) + +- (void)copyVertexCoords:(float *)vertexCoords colors:(uint *)colors textureCoords:(float *)texCoords +{ + if (vertexCoords) memcpy(vertexCoords, mVertexCoords, 8 * sizeof(float)); + if (colors) memcpy(colors, mVertexColors, 4 * sizeof(uint)); +} + +@end + +// --- SPImage extension --------------------------------------------------------------------------- + +@implementation SPImage (SPCompiledSprite_Extension) + +- (void)copyVertexCoords:(float *)vertexCoords colors:(uint *)colors textureCoords:(float *)texCoords +{ + [super copyVertexCoords:vertexCoords colors:colors textureCoords:texCoords]; + if (texCoords) memcpy(texCoords, mTexCoords, 8 * sizeof(float)); +} + +@end + +// --- private interface --------------------------------------------------------------------------- + +@interface SPCompiledSprite () + +- (BOOL)processVerticesOfObject:(SPDisplayObject *)object withMatrices:(NSMutableArray *)matrices + vertexData:(NSMutableData *)vertexData buffer:(void *)buffer; +- (BOOL)processColorsOfObject:(SPDisplayObject *)object withAlpha:(float)alpha + colorData:(NSMutableData *)colorData buffer:(void *)buffer; +- (BOOL)processTexturesOfObject:(SPDisplayObject *)object withTextures:(NSMutableArray *)textures + texCoordData:(NSMutableData *)texCoordData buffer:(void *)buffer; +- (void)updateColorData; +- (void)deleteBuffers; + +@end + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPCompiledSprite + +- (id)init +{ + return [super init]; +} + ++ (SPCompiledSprite *)sprite +{ + return [[[SPCompiledSprite alloc] init] autorelease]; +} + +- (void)dealloc +{ + free(mCurrentColors); + [mTextureSwitches release]; + [mColorData release]; + [self deleteBuffers]; + [super dealloc]; +} + +- (BOOL)compile +{ + SP_CREATE_POOL(pool); + + [self deleteBuffers]; + [mTextureSwitches release]; + [mColorData release]; + + free(mCurrentColors); + mCurrentColors = nil; + + void *scratchBuffer = malloc(MAX(8 * sizeof(float), 4 * sizeof(uint))); + + NSMutableData *vertexData = [[NSMutableData alloc] init]; + NSMutableData *colorData = [[NSMutableData alloc] init]; + NSMutableData *texCoordData = [[NSMutableData alloc] init]; + + NSMutableArray *matrices = [[NSMutableArray alloc] initWithObjects: + [SPMatrix matrixWithIdentity], nil]; + NSMutableArray *textures = [[NSMutableArray alloc] initWithObjects: + [NSNull null], [NSNumber numberWithInt:0], nil]; + + BOOL success; + + do + { + // compilation is done with an alpha of 1.0f, to get unscaled color data + float originalAlpha = self.alpha; + self.alpha = 1.0f; + + success = [self processVerticesOfObject:self withMatrices:matrices + vertexData:vertexData buffer:scratchBuffer]; + if (!success) break; + + success = [self processColorsOfObject:self withAlpha:self.alpha + colorData:colorData buffer:scratchBuffer]; + if (!success) break; + + success = [self processTexturesOfObject:self withTextures:textures + texCoordData:texCoordData buffer:scratchBuffer]; + + self.alpha = originalAlpha; + + } while (NO); + + if (success) + { + glGenBuffers(4, &mIndexBuffer); + + int numVertices = [vertexData length] / sizeof(float) / 2; + int numQuads = numVertices / 4; + int indexBufferSize = numQuads * 6; // 4 + 2 for degenerate triangles + GLushort *indices = malloc(indexBufferSize * sizeof(GLushort)); + + int pos = 0; + for (int i=0; i> 24) / 255.0f * alpha; + *newColors = [SPRenderSupport convertColor:origColor alpha:vertexAlpha premultiplyAlpha:pma]; + ++origColors; + ++newColors; + } + } + + // update buffer + glBindBuffer(GL_ARRAY_BUFFER, mColorBuffer); + glBufferSubData(GL_ARRAY_BUFFER, 0, mColorData.length, mCurrentColors); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + mAlphaChanged = NO; +} + +- (void)deleteBuffers +{ + glDeleteBuffers(4, &mIndexBuffer); + mIndexBuffer = mVertexBuffer = mColorBuffer = mTexCoordBuffer = 0; +} + +@end diff --git a/libs/sparrow/src/Classes/SPDelayedInvocation.h b/libs/sparrow/src/Classes/SPDelayedInvocation.h new file mode 100644 index 0000000..9dbea9f --- /dev/null +++ b/libs/sparrow/src/Classes/SPDelayedInvocation.h @@ -0,0 +1,58 @@ +// +// SPDelayedInvocation.h +// Sparrow +// +// Created by Daniel Sperl on 11.07.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPAnimatable.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPDelayedInvocation is a proxy object that will forward any methods that are called on it + to a certain target - but only after a certain time has passed. + + The easiest way to delay an invocation is by calling [SPJuggler delayInvocationAtTarget:byTime:]. + This method will create a delayed invocation for you, adding it to the juggler right away. + +------------------------------------------------------------------------------------------------- */ + + +@interface SPDelayedInvocation : NSObject +{ + @private + id mTarget; + NSMutableSet *mInvocations; + double mTotalTime; + double mCurrentTime; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a delayed invocation. +- (id)initWithTarget:(id)target delay:(double)time; + +/// Factory method. ++ (SPDelayedInvocation*)invocationWithTarget:(id)target delay:(double)time; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The target object to which messages will be forwarded. +@property (nonatomic, readonly) id target; + +/// The time messages will be delayed (in seconds). +@property (nonatomic, readonly) double totalTime; + +/// The time that has already passed (in seconds). +@property (nonatomic, assign) double currentTime; + +@end diff --git a/libs/sparrow/src/Classes/SPDelayedInvocation.m b/libs/sparrow/src/Classes/SPDelayedInvocation.m new file mode 100644 index 0000000..ba8e762 --- /dev/null +++ b/libs/sparrow/src/Classes/SPDelayedInvocation.m @@ -0,0 +1,93 @@ +// +// SPDelayedInvocation.m +// Sparrow +// +// Created by Daniel Sperl on 11.07.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPDelayedInvocation.h" + + +@implementation SPDelayedInvocation + +@synthesize totalTime = mTotalTime; +@synthesize currentTime = mCurrentTime; +@synthesize target = mTarget; + +- (id)initWithTarget:(id)target delay:(double)time +{ + if (!target) + { + [self release]; + return nil; + } + + if (self = [super init]) + { + mTotalTime = MAX(0.0001, time); // zero is not allowed + mCurrentTime = 0; + mTarget = [target retain]; + mInvocations = [[NSMutableSet alloc] init]; + } + return self; +} + +- (id)init +{ + [self release]; + return nil; +} + +- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector +{ + NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:aSelector]; + if (!sig) sig = [mTarget methodSignatureForSelector:aSelector]; + return sig; +} + +- (void)forwardInvocation:(NSInvocation*)anInvocation +{ + if ([mTarget respondsToSelector:[anInvocation selector]]) + { + anInvocation.target = mTarget; + [anInvocation retainArguments]; + [mInvocations addObject:anInvocation]; + } +} + +- (void)advanceTime:(double)seconds +{ + self.currentTime = mCurrentTime + seconds; +} + +- (void)setCurrentTime:(double)currentTime +{ + double previousTime = mCurrentTime; + mCurrentTime = MIN(mTotalTime, currentTime); + + if (previousTime < mTotalTime && mCurrentTime >= mTotalTime) + [mInvocations makeObjectsPerformSelector:@selector(invoke)]; +} + +- (BOOL)isComplete +{ + return mCurrentTime >= mTotalTime; +} + ++ (SPDelayedInvocation*)invocationWithTarget:(id)target delay:(double)time +{ + return [[[SPDelayedInvocation alloc] initWithTarget:target delay:time] autorelease]; +} + +- (void)dealloc +{ + [mTarget release]; + [mInvocations release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPDisplayObject.h b/libs/sparrow/src/Classes/SPDisplayObject.h new file mode 100644 index 0000000..2e85055 --- /dev/null +++ b/libs/sparrow/src/Classes/SPDisplayObject.h @@ -0,0 +1,172 @@ +// +// SPDisplayObject.h +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPEventDispatcher.h" +#import "SPRectangle.h" +#import "SPMatrix.h" + +@class SPDisplayObjectContainer; +@class SPStage; +@class SPRenderSupport; + +/** ------------------------------------------------------------------------------------------------ + + The SPDisplayObject class is the base class for all objects that are rendered on the screen. + + In Sparrow, all displayable objects are organized in a display tree. Only objects that are part of + the display tree will be displayed (rendered). + + The display tree consists of leaf nodes (SPImage, SPQuad) that will be rendered directly to + the screen, and of container nodes (subclasses of SPDisplayObjectContainer, like SPSprite). + A container is simply a display object that has child nodes - which can, again, be either leaf + nodes or other containers. + + At the root of the display tree, there is the SPStage, which is a container, too. To create a + Sparrow application, you let your main class inherit from SPStage, and build up your display + tree from there. + + A display object has properties that define its position in relation to its parent + (`x`, `y`), as well as its rotation and scaling factors (`scaleX`, `scaleY`). Use the `alpha` and + `visible` properties to make an object translucent or invisible. + + Every display object may be the target of touch events. If you don't want an object to be + touchable, you can disable the `touchable` property. When it's disabled, neither the object + nor its childs will receive any more touch events. + + *Points vs. Pixels* + + All sizes and distances are measured in points. What this means in pixels depends on the + contentScaleFactor of the stage. On a low resolution device (up to iPhone 3GS), one point is one + pixel. On devices with a retina display, one point may be 2 pixels. + + *Transforming coordinates* + + Within the display tree, each object has its own local coordinate system. If you rotate a container, + you rotate that coordinate system - and thus all the children of the container. + + Sometimes you need to know where a certain point lies relative to another coordinate system. + That's the purpose of the method `transformationMatrixToSpace:`. It will create a matrix that + represents the transformation of a point in one coordinate system to another. + + *Subclassing SPDisplayObject* + + As SPDisplayObject is an abstract class, you can't instantiate it directly, but have to use one of + its subclasses instead. There are already a lot of them available, and most of the time they will + suffice. + + However, you can create custom subclasses as well. That's especially useful when you want to + create an object with a custom render function. + + You will need to implement the following methods when you subclass SPDisplayObject: + + - (void)render:(SPRenderSupport*)support; + - (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetCoordinateSpace; + + Have a look at SPQuad for a sample implementation of those methods. + +------------------------------------------------------------------------------------------------- */ + +@interface SPDisplayObject : SPEventDispatcher +{ + @private + float mX; + float mY; + float mScaleX; + float mScaleY; + float mRotationZ; + float mAlpha; + BOOL mVisible; + BOOL mTouchable; + + SPDisplayObjectContainer *mParent; + double mLastTouchTimestamp; + NSString *mName; +} + +/// ------------- +/// @name Methods +/// ------------- + +/// Renders the display object with the help of a support object. +- (void)render:(SPRenderSupport*)support; + +/// Removes the object from its parent, if it has one. +- (void)removeFromParent; + +/// Creates a matrix that represents the transformation from the local coordinate system to another. +- (SPMatrix*)transformationMatrixToSpace:(SPDisplayObject*)targetCoordinateSpace; + +/// Returns a rectangle that completely encloses the object as it appears in another coordinate system. +- (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetCoordinateSpace; + +/// Transforms a point from the local coordinate system to global (stage) coordinates. +- (SPPoint*)localToGlobal:(SPPoint*)localPoint; + +/// Transforms a point from global (stage) coordinates to the local coordinate system. +- (SPPoint*)globalToLocal:(SPPoint*)globalPoint; + +/// Returns the object that is found topmost on a point in local coordinates, or nil if the test fails. +- (SPDisplayObject*)hitTestPoint:(SPPoint*)localPoint forTouch:(BOOL)isTouch; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The x coordinates of the object relative to the local coordinates of the parent. +@property (nonatomic, assign) float x; + +/// The y coordinates of the object relative to the local coordinates of the parent. +@property (nonatomic, assign) float y; + +/// The horizontal scale factor. "1" means no scale, negative values flip the object. +@property (nonatomic, assign) float scaleX; + +/// The vertical scale factor. "1" means no scale, negative values flip the object. +@property (nonatomic, assign) float scaleY; + +/// The width of the object in points. +@property (nonatomic, assign) float width; + +/// The height of the object in points. +@property (nonatomic, assign) float height; + +/// The rotation of the object in radians. (In Sparrow, all angles are measured in radians.) +@property (nonatomic, assign) float rotation; + +/// The opacity of the object. 0 = transparent, 1 = opaque. +@property (nonatomic, assign) float alpha; + +/// The visibility of the object. An invisible object will be untouchable. +@property (nonatomic, assign) BOOL visible; + +/// Indicates if this object (and its children) will receive touch events. +@property (nonatomic, assign) BOOL touchable; + +/// The bounds of the object relative to the local coordinates of the parent. +@property (nonatomic, readonly) SPRectangle *bounds; + +/// The display object container that contains this display object. +@property (nonatomic, readonly) SPDisplayObjectContainer *parent; + +/// The topmost object in the display tree the object is part of. +@property (nonatomic, readonly) SPDisplayObject *root; + +/// The stage the display object is connected to, or nil if it is not connected to a stage. +@property (nonatomic, readonly) SPStage *stage; + +/// The transformation matrix of the object relative to its parent. +@property (nonatomic, readonly) SPMatrix *transformationMatrix; + +/// The name of the display object (default: nil). Used by `childByName:` of display object containers. +@property (nonatomic, copy) NSString *name; + +@end diff --git a/libs/sparrow/src/Classes/SPDisplayObject.m b/libs/sparrow/src/Classes/SPDisplayObject.m new file mode 100644 index 0000000..6eab8f6 --- /dev/null +++ b/libs/sparrow/src/Classes/SPDisplayObject.m @@ -0,0 +1,317 @@ +// +// SPDisplayObject.m +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPDisplayObject.h" +#import "SPDisplayObject_Internal.h" +#import "SPDisplayObjectContainer.h" +#import "SPStage.h" +#import "SPMacros.h" +#import "SPTouchEvent.h" + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPDisplayObject + +@synthesize x = mX; +@synthesize y = mY; +@synthesize scaleX = mScaleX; +@synthesize scaleY = mScaleY; +@synthesize rotation = mRotationZ; +@synthesize parent = mParent; +@synthesize alpha = mAlpha; +@synthesize visible = mVisible; +@synthesize touchable = mTouchable; +@synthesize name = mName; + +- (id)init +{ + #ifdef DEBUG + if ([self isMemberOfClass:[SPDisplayObject class]]) + { + [self release]; + [NSException raise:SP_EXC_ABSTRACT_CLASS + format:@"Attempting to initialize abstract class SPDisplayObject."]; + return nil; + } + #endif + + if (self = [super init]) + { + mAlpha = 1.0f; + mScaleX = 1.0f; + mScaleY = 1.0f; + mVisible = YES; + mTouchable = YES; + } + return self; +} + +- (void)dealloc +{ + [mName release]; + [super dealloc]; +} + +- (void)render:(SPRenderSupport*)support +{ + // override in subclass +} + +- (void)removeFromParent +{ + [mParent removeChild:self]; +} + +- (SPMatrix*)transformationMatrixToSpace:(SPDisplayObject*)targetCoordinateSpace +{ + if (targetCoordinateSpace == self) + { + return [SPMatrix matrixWithIdentity]; + } + else if (!targetCoordinateSpace) + { + // targetCoordinateSpace 'nil' represents the target coordinate of the root object. + // -> move up from self to root + SPMatrix *selfMatrix = [[SPMatrix alloc] init]; + SPDisplayObject *currentObject = self; + while (currentObject) + { + [selfMatrix concatMatrix:currentObject.transformationMatrix]; + currentObject = currentObject->mParent; + } + return [selfMatrix autorelease]; + } + else if (targetCoordinateSpace->mParent == self) // optimization + { + SPMatrix *targetMatrix = targetCoordinateSpace.transformationMatrix; + [targetMatrix invert]; + return targetMatrix; + } + else if (mParent == targetCoordinateSpace) // optimization + { + return self.transformationMatrix; + } + + // 1.: Find a common parent of self and the target coordinate space. + // + // This method is used very often during touch testing, so we optimized the code. + // Instead of using an NSSet or NSArray (which would make the code much cleaner), we + // use a C array here to save the ancestors. + + static SPDisplayObject *ancestors[SP_MAX_DISPLAY_TREE_DEPTH]; + + int count = 0; + SPDisplayObject *commonParent = nil; + SPDisplayObject *currentObject = self; + while (currentObject && count < SP_MAX_DISPLAY_TREE_DEPTH) + { + ancestors[count++] = currentObject; + currentObject = currentObject->mParent; + } + + currentObject = targetCoordinateSpace; + while (currentObject && !commonParent) + { + for (int i=0; imParent; + } + + if (!commonParent) + [NSException raise:SP_EXC_NOT_RELATED format:@"Object not connected to target"]; + + // 2.: Move up from self to common parent + SPMatrix *selfMatrix = [[SPMatrix alloc] init]; + currentObject = self; + while (currentObject != commonParent) + { + [selfMatrix concatMatrix:currentObject.transformationMatrix]; + currentObject = currentObject->mParent; + } + + // 3.: Now move up from target until we reach the common parent + SPMatrix *targetMatrix = [[SPMatrix alloc] init]; + currentObject = targetCoordinateSpace; + while (currentObject && currentObject != commonParent) + { + [targetMatrix concatMatrix:currentObject.transformationMatrix]; + currentObject = currentObject->mParent; + } + + // 4.: Combine the two matrices + [targetMatrix invert]; + [selfMatrix concatMatrix:targetMatrix]; + [targetMatrix release]; + + return [selfMatrix autorelease]; +} + +- (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetCoordinateSpace +{ + [NSException raise:SP_EXC_ABSTRACT_METHOD + format:@"Method needs to be implemented in subclass"]; + return nil; +} + +- (SPRectangle*)bounds +{ + return [self boundsInSpace:mParent]; +} + +- (SPDisplayObject*)hitTestPoint:(SPPoint*)localPoint forTouch:(BOOL)isTouch; +{ + // on a touch test, invisible or untouchable objects cause the test to fail + if (isTouch && (!mVisible || !mTouchable)) return nil; + + // otherwise, check bounding box + if ([[self boundsInSpace:self] containsPoint:localPoint]) return self; + else return nil; +} + +- (SPPoint*)localToGlobal:(SPPoint*)localPoint +{ + // move up until parent is nil + SPMatrix *transformationMatrix = [[SPMatrix alloc] init]; + SPDisplayObject *currentObject = self; + while (currentObject) + { + [transformationMatrix concatMatrix:currentObject.transformationMatrix]; + currentObject = [currentObject parent]; + } + + SPPoint *globalPoint = [transformationMatrix transformPoint:localPoint]; + [transformationMatrix release]; + return globalPoint; +} + +- (SPPoint*)globalToLocal:(SPPoint*)globalPoint +{ + // move up until parent is nil, then invert matrix + SPMatrix *transformationMatrix = [[SPMatrix alloc] init]; + SPDisplayObject *currentObject = self; + while (currentObject) + { + [transformationMatrix concatMatrix:currentObject.transformationMatrix]; + currentObject = [currentObject parent]; + } + + [transformationMatrix invert]; + SPPoint *localPoint = [transformationMatrix transformPoint:globalPoint]; + [transformationMatrix release]; + return localPoint; +} + +- (void)dispatchEvent:(SPEvent*)event +{ + // on one given moment, there is only one set of touches -- thus, + // we process only one touch event with a certain timestamp + if ([event isKindOfClass:[SPTouchEvent class]]) + { + SPTouchEvent *touchEvent = (SPTouchEvent*)event; + if (touchEvent.timestamp == mLastTouchTimestamp) return; + else mLastTouchTimestamp = touchEvent.timestamp; + } + + [super dispatchEvent:event]; +} + +- (float)width +{ + return [self boundsInSpace:mParent].width; +} + +- (void)setWidth:(float)value +{ + // this method calls 'self.scaleX' instead of changing mScaleX directly. + // that way, subclasses reacting on size changes need to override only the scaleX method. + + mScaleX = 1.0f; + float actualWidth = self.width; + if (actualWidth != 0.0f) self.scaleX = value / actualWidth; + else self.scaleX = 1.0f; +} + +- (float)height +{ + return [self boundsInSpace:mParent].height; +} + +- (void)setHeight:(float)value +{ + mScaleY = 1.0f; + float actualHeight = self.height; + if (actualHeight != 0.0f) self.scaleY = value / actualHeight; + else self.scaleY = 1.0f; +} + +- (void)setRotation:(float)value +{ + // clamp between [-180 deg, +180 deg] + while (value < -PI) value += TWO_PI; + while (value > PI) value -= TWO_PI; + mRotationZ = value; +} + +- (void)setAlpha:(float)value +{ + mAlpha = MAX(0.0f, MIN(1.0f, value)); +} + +- (SPDisplayObject*)root +{ + SPDisplayObject *currentObject = self; + while (currentObject->mParent) + currentObject = currentObject->mParent; + return currentObject; +} + +- (SPStage*)stage +{ + SPDisplayObject *root = self.root; + if ([root isKindOfClass:[SPStage class]]) return (SPStage*) root; + else return nil; +} + +- (SPMatrix*)transformationMatrix +{ + SPMatrix *matrix = [[SPMatrix alloc] init]; + + if (mScaleX != 1.0f || mScaleY != 1.0f) [matrix scaleXBy:mScaleX yBy:mScaleY]; + if (mRotationZ != 0.0f) [matrix rotateBy:mRotationZ]; + if (mX != 0.0f || mY != 0.0f) [matrix translateXBy:mX yBy:mY]; + + return [matrix autorelease]; +} + +@end + +// ------------------------------------------------------------------------------------------------- + +@implementation SPDisplayObject (Internal) + +- (void)setParent:(SPDisplayObjectContainer*)parent +{ + // only assigned, not retained -- otherwise, we would create a circular reference. + mParent = parent; +} + +- (void)dispatchEventOnChildren:(SPEvent *)event +{ + [self dispatchEvent:event]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPDisplayObjectContainer.h b/libs/sparrow/src/Classes/SPDisplayObjectContainer.h new file mode 100644 index 0000000..4fc405f --- /dev/null +++ b/libs/sparrow/src/Classes/SPDisplayObjectContainer.h @@ -0,0 +1,100 @@ +// +// SPDisplayObjectContainer.h +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPDisplayObject.h" + +/** ------------------------------------------------------------------------------------------------ + + An SPDisplayObjectContainer represents a collection of display objects. + + It is the base class of all display objects that act as a container for other objects. By + maintaining an ordered list of children, it defines the back-to-front positioning of the children + within the display tree. + + A container does not have size in itself. The width and height properties represent the extents + of its children. Changing those properties will scale all children accordingly. + + As this is an abstract class, you can't instantiate it directly, but have to + use a subclass instead. The most lightweight container class is SPSprite. + + *Adding and removing children* + + The class defines methods that allow you to add or remove children. When you add a child, it will + be added at the foremost position, possibly occluding a child that was added before. You can access + the children via an index. The first child will have index 0, the second child index 1, etc. + + Adding and removing objects from a container triggers non-bubbling events. + + * `SP_EVENT_TYPE_ADDED`: the object was added to a parent. + * `SP_EVENT_TYPE_ADDED_TO_STAGE`: the object was added to a parent that is connected to the stage, + thus becoming visible now. + * `SP_EVENT_TYPE_REMOVED`: the object was removed from a parent. + * `SP_EVENT_TYPE_REMOVED_FROM_STAGE`: the object was removed from a parent that is connected to + the stage, thus becoming invisible now. + + Especially the `ADDED_TO_STAGE` event is very helpful, as it allows you to automatically execute + some logic (e.g. start an animation) when an object is rendered the first time. + +------------------------------------------------------------------------------------------------- */ + +@interface SPDisplayObjectContainer : SPDisplayObject +{ + @private + NSMutableArray *mChildren; +} + +/// ------------- +/// @name Methods +/// ------------- + +/// Adds a child to the container. It will be at the topmost position. +- (void)addChild:(SPDisplayObject *)child; + +/// Adds a child to the container at a certain index. +- (void)addChild:(SPDisplayObject *)child atIndex:(int)index; + +/// Determines if a certain object is a child of the container (recursively). +- (BOOL)containsChild:(SPDisplayObject *)child; + +/// Returns a child object at a certain index. +- (SPDisplayObject *)childAtIndex:(int)index; + +/// Returns a child object with a certain name (non-recursively). +- (SPDisplayObject *)childByName:(NSString *)name; + +/// Returns the index of a child within the container. +- (int)childIndex:(SPDisplayObject *)child; + +/// Removes a child from the container. If the object is not a child, nothing happens. +- (void)removeChild:(SPDisplayObject *)child; + +/// Removes a child at a certain index. Children above the child will move down. +- (void)removeChildAtIndex:(int)index; + +/// Removes all children from the container. +- (void)removeAllChildren; + +/// Swaps the indexes of two children. +- (void)swapChild:(SPDisplayObject*)child1 withChild:(SPDisplayObject*)child2; + +/// Swaps the indexes of two children. +- (void)swapChildAtIndex:(int)index1 withChildAtIndex:(int)index2; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The number of children of this container. +@property (nonatomic, readonly) int numChildren; + + +@end diff --git a/libs/sparrow/src/Classes/SPDisplayObjectContainer.m b/libs/sparrow/src/Classes/SPDisplayObjectContainer.m new file mode 100644 index 0000000..f9420c1 --- /dev/null +++ b/libs/sparrow/src/Classes/SPDisplayObjectContainer.m @@ -0,0 +1,250 @@ +// +// SPDisplayObjectContainer.m +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPDisplayObjectContainer.h" +#import "SPEnterFrameEvent.h" +#import "SPDisplayObject_Internal.h" +#import "SPMacros.h" + +// --- C functions --------------------------------------------------------------------------------- + +static void getChildEventListeners(SPDisplayObject *object, NSString *eventType, + NSMutableArray *listeners) +{ + // some events (ENTER_FRAME, ADDED_TO_STAGE, etc.) are dispatched very often and traverse + // the entire display tree -- thus, it pays off handling them in their own c function. + + if ([object hasEventListenerForType:eventType]) + [listeners addObject:object]; + + if ([object isKindOfClass:[SPDisplayObjectContainer class]]) + for (SPDisplayObject *child in (SPDisplayObjectContainer *)object) + getChildEventListeners(child, eventType, listeners); +} + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPDisplayObjectContainer + +- (id)init +{ + #if DEBUG + if ([[self class] isEqual:[SPDisplayObjectContainer class]]) + { + [NSException raise:SP_EXC_ABSTRACT_CLASS + format:@"Attempting to instantiate SPDisplayObjectContainer directly."]; + [self release]; + return nil; + } + #endif + + if (self = [super init]) + { + mChildren = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)addChild:(SPDisplayObject *)child +{ + [self addChild:child atIndex:[mChildren count]]; +} + +- (void)addChild:(SPDisplayObject *)child atIndex:(int)index +{ + if (index >= 0 && index <= [mChildren count]) + { + [child retain]; + [child removeFromParent]; + [mChildren insertObject:child atIndex:MIN(mChildren.count, index)]; + child.parent = self; + + SPEvent *addedEvent = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_ADDED]; + [child dispatchEvent:addedEvent]; + [addedEvent release]; + + if (self.stage) + { + SPEvent *addedToStageEvent = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_ADDED_TO_STAGE]; + [child dispatchEventOnChildren:addedToStageEvent]; + [addedToStageEvent release]; + } + + [child release]; + } + else [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"Invalid child index"]; +} + +- (BOOL)containsChild:(SPDisplayObject *)child +{ + if ([self isEqual:child]) return YES; + + for (SPDisplayObject *currentChild in mChildren) + { + if ([currentChild isKindOfClass:[SPDisplayObjectContainer class]]) + { + if ([(SPDisplayObjectContainer *)currentChild containsChild:child]) return YES; + } + else + { + if (currentChild == child) return YES; + } + } + + return NO; +} + +- (SPDisplayObject *)childAtIndex:(int)index +{ + return [mChildren objectAtIndex:index]; +} + +- (SPDisplayObject *)childByName:(NSString *)name +{ + for (SPDisplayObject *currentChild in mChildren) + if ([currentChild.name isEqualToString:name]) return currentChild; + + return nil; +} + +- (int)childIndex:(SPDisplayObject *)child +{ + int index = [mChildren indexOfObject:child]; + if (index == NSNotFound) return SP_NOT_FOUND; + else return index; +} + +- (void)removeChild:(SPDisplayObject *)child +{ + int childIndex = [self childIndex:child]; + if (childIndex != SP_NOT_FOUND) + [self removeChildAtIndex:childIndex]; +} + +- (void)removeChildAtIndex:(int)index +{ + if (index >= 0 && index < [mChildren count]) + { + SPDisplayObject *child = [[mChildren objectAtIndex:index] retain]; + + SPEvent *remEvent = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_REMOVED]; + [child dispatchEvent:remEvent]; + [remEvent release]; + + if (self.stage) + { + SPEvent *remFromStageEvent = [[SPEvent alloc] initWithType:SP_EVENT_TYPE_REMOVED_FROM_STAGE]; + [child dispatchEventOnChildren:remFromStageEvent]; + [remFromStageEvent release]; + } + + [mChildren removeObjectAtIndex:index]; + child.parent = nil; + + [child release]; + } + else [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"Invalid child index"]; +} + +- (void)swapChild:(SPDisplayObject*)child1 withChild:(SPDisplayObject*)child2 +{ + int index1 = [self childIndex:child1]; + int index2 = [self childIndex:child2]; + [self swapChildAtIndex:index1 withChildAtIndex:index2]; +} + +- (void)swapChildAtIndex:(int)index1 withChildAtIndex:(int)index2 +{ + int numChildren = [mChildren count]; + if (index1 < 0 || index1 >= numChildren || index2 < 0 || index2 >= numChildren) + [NSException raise:SP_EXC_INVALID_OPERATION format:@"invalid child indices"]; + [mChildren exchangeObjectAtIndex:index1 withObjectAtIndex:index2]; +} + +- (void)removeAllChildren +{ + for (int i=mChildren.count-1; i>=0; --i) + [self removeChildAtIndex:i]; +} + +- (int)numChildren +{ + return [mChildren count]; +} + +- (SPRectangle*)boundsInSpace:(SPDisplayObject*)targetCoordinateSpace +{ + int numChildren = [mChildren count]; + + if (numChildren == 0) + return [SPRectangle rectangleWithX:0 y:0 width:0 height:0]; + else if (numChildren == 1) + return [[mChildren objectAtIndex:0] boundsInSpace:targetCoordinateSpace]; + else + { + float minX = FLT_MAX, maxX = -FLT_MAX, minY = FLT_MAX, maxY = -FLT_MAX; + for (SPDisplayObject *child in mChildren) + { + SPRectangle *childBounds = [child boundsInSpace:targetCoordinateSpace]; + minX = MIN(minX, childBounds.x); + maxX = MAX(maxX, childBounds.x + childBounds.width); + minY = MIN(minY, childBounds.y); + maxY = MAX(maxY, childBounds.y + childBounds.height); + } + return [SPRectangle rectangleWithX:minX y:minY width:maxX-minX height:maxY-minY]; + } +} + +- (SPDisplayObject*)hitTestPoint:(SPPoint*)localPoint forTouch:(BOOL)isTouch; +{ + if (isTouch && (!self.visible || !self.touchable)) + return nil; + + for (int i=[mChildren count]-1; i>=0; --i) // front to back! + { + SPDisplayObject *child = [mChildren objectAtIndex:i]; + SPMatrix *transformationMatrix = [self transformationMatrixToSpace:child]; + SPPoint *transformedPoint = [transformationMatrix transformPoint:localPoint]; + SPDisplayObject *target = [child hitTestPoint:transformedPoint forTouch:isTouch]; + if (target) return target; + } + + return nil; +} + +- (void)dispatchEventOnChildren:(SPEvent *)event +{ + // the event listeners might modify the display tree, which could make the loop crash. + // thus, we collect them in a list and iterate over that list instead. + + NSMutableArray *listeners = [[NSMutableArray alloc] init]; + getChildEventListeners(self, event.type, listeners); + [listeners makeObjectsPerformSelector:@selector(dispatchEvent:) withObject:event]; + [listeners release]; +} + +- (void)dealloc +{ + // 'self' is becoming invalid; thus, we have to remove any references to it. + [mChildren makeObjectsPerformSelector:@selector(setParent:) withObject:nil]; + [mChildren release]; + [super dealloc]; +} + +#pragma mark NSFastEnumeration + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf + count:(NSUInteger)len +{ + return [mChildren countByEnumeratingWithState:state objects:stackbuf count:len]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPDisplayObject_Internal.h b/libs/sparrow/src/Classes/SPDisplayObject_Internal.h new file mode 100644 index 0000000..6ee335b --- /dev/null +++ b/libs/sparrow/src/Classes/SPDisplayObject_Internal.h @@ -0,0 +1,20 @@ +// +// SPDisplayObject_Internal.h +// Sparrow +// +// Created by Daniel Sperl on 03.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPDisplayObject.h" + +@interface SPDisplayObject (Internal) + +- (void)setParent:(SPDisplayObjectContainer*)parent; +- (void)dispatchEventOnChildren:(SPEvent *)event; + +@end diff --git a/libs/sparrow/src/Classes/SPEnterFrameEvent.h b/libs/sparrow/src/Classes/SPEnterFrameEvent.h new file mode 100644 index 0000000..535bc86 --- /dev/null +++ b/libs/sparrow/src/Classes/SPEnterFrameEvent.h @@ -0,0 +1,54 @@ +// +// SPEnterFrameEvent.h +// Sparrow +// +// Created by Daniel Sperl on 30.04.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPEvent.h" + +#define SP_EVENT_TYPE_ENTER_FRAME @"enterFrame" + +/** ------------------------------------------------------------------------------------------------ + + An SPEnterFrameEvent is triggered once per frame and is dispatched to all objects in the + display tree. + + It contains information about the time that has passed since the last frame. That way, you + can easily make animations that are independet of the frame rate, but take the passed time + into account. + +------------------------------------------------------------------------------------------------- */ + +@interface SPEnterFrameEvent : SPEvent +{ + @private + double mPassedTime; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes an enter frame event with the passed time. _Designated Initializer_. +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles passedTime:(double)seconds; + +/// Initializes an enter frame event that does not bubble (recommended). +- (id)initWithType:(NSString*)type passedTime:(double)seconds; + +/// Factory method. ++ (SPEnterFrameEvent*)eventWithType:(NSString*)type passedTime:(double)seconds; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The time that has passed since the last frame (in seconds). +@property (nonatomic, readonly) double passedTime; + +@end diff --git a/libs/sparrow/src/Classes/SPEnterFrameEvent.m b/libs/sparrow/src/Classes/SPEnterFrameEvent.m new file mode 100644 index 0000000..50aa213 --- /dev/null +++ b/libs/sparrow/src/Classes/SPEnterFrameEvent.m @@ -0,0 +1,48 @@ +// +// SPEnterFrameEvent.m +// Sparrow +// +// Created by Daniel Sperl on 30.04.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPEnterFrameEvent.h" + + +@implementation SPEnterFrameEvent + +@synthesize passedTime = mPassedTime; + +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles passedTime:(double)seconds +{ + if (self = [super initWithType:type bubbles:bubbles]) + { + mPassedTime = seconds; + } + return self; +} + +- (id)initWithType:(NSString*)type passedTime:(double)seconds; +{ + return [self initWithType:type bubbles:NO passedTime:seconds]; +} + +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles +{ + return [self initWithType:type bubbles:bubbles passedTime:0.0f]; +} + ++ (SPEnterFrameEvent*)eventWithType:(NSString*)type passedTime:(double)seconds +{ + return [[[SPEnterFrameEvent alloc] initWithType:type passedTime:seconds] autorelease]; +} + +- (void)dealloc +{ + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPEvent.h b/libs/sparrow/src/Classes/SPEvent.h new file mode 100644 index 0000000..a4c2a90 --- /dev/null +++ b/libs/sparrow/src/Classes/SPEvent.h @@ -0,0 +1,93 @@ +// +// SPEvent.h +// Sparrow +// +// Created by Daniel Sperl on 27.04.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +#define SP_EVENT_TYPE_ADDED @"added" +#define SP_EVENT_TYPE_ADDED_TO_STAGE @"addedToStage" +#define SP_EVENT_TYPE_REMOVED @"removed" +#define SP_EVENT_TYPE_REMOVED_FROM_STAGE @"removedFromStage" + +@class SPEventDispatcher; + +/** ------------------------------------------------------------------------------------------------ + + The SPEvent class contains data that describes an event. + + `SPEventDispatcher`s create instances of this class and send them to registered listeners. An event + contains information that characterizes an event, most importantly the event type and if the event + bubbles. The target of an event is the object that dispatched it. + + For some event types, this information is sufficient; other events may need additional information + to be carried to the listener. + In that case, you can subclass SPEvent and add properties with all the information you require. + The SPEnterFrameEvent is an example for this practice; it adds a property about the time that + has passed since the last frame. + + Furthermore, the event class contains methods that can stop the event from being processed by + other listeners - either completely or at the next bubble stage. + +------------------------------------------------------------------------------------------------- */ + +@interface SPEvent : NSObject +{ + @private + SPEventDispatcher *mTarget; + SPEventDispatcher *mCurrentTarget; + NSString *mType; + BOOL mStopsImmediatePropagation; + BOOL mStopsPropagation; + BOOL mBubbles; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes an event object that can be passed to listeners. _Designated Initializer_. +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles; + +/// Initializes a non-bubbling event. +- (id)initWithType:(NSString*)type; + +/// Factory method. ++ (SPEvent*)eventWithType:(NSString*)type bubbles:(BOOL)bubbles; + +/// Factory method. ++ (SPEvent*)eventWithType:(NSString*)type; + +/// ------------- +/// @name Methods +/// ------------- + +/// Prevents any other listeners from receiving the event. +- (void)stopImmediatePropagation; + +/// Prevents listeners at the next bubble stage from receiving the event. +- (void)stopPropagation; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// A string that identifies the event. +@property (nonatomic, readonly) NSString *type; + +/// Indicates if event will bubble. +@property (nonatomic, readonly) BOOL bubbles; + +/// The object that dispatched the event. +@property (nonatomic, readonly) SPEventDispatcher *target; + +/// The object the event is currently bubbling at. +@property (nonatomic, readonly) SPEventDispatcher *currentTarget; + +@end diff --git a/libs/sparrow/src/Classes/SPEvent.m b/libs/sparrow/src/Classes/SPEvent.m new file mode 100644 index 0000000..66b0b81 --- /dev/null +++ b/libs/sparrow/src/Classes/SPEvent.m @@ -0,0 +1,104 @@ +// +// SPEvent.m +// Sparrow +// +// Created by Daniel Sperl on 27.04.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPEvent.h" +#import "SPEvent_Internal.h" + +@implementation SPEvent + +@synthesize target = mTarget; +@synthesize currentTarget = mCurrentTarget; +@synthesize type = mType; +@synthesize bubbles = mBubbles; + +- (id)initWithType:(NSString*)type bubbles:(BOOL)bubbles +{ + if (self = [super init]) + { + mType = [[NSString alloc] initWithString:type]; + mBubbles = bubbles; + } + return self; +} + +- (id)initWithType:(NSString*)type +{ + return [self initWithType:type bubbles:NO]; +} + +- (id)init +{ + return [self initWithType:@"undefined"]; +} + +- (void)stopImmediatePropagation +{ + mStopsImmediatePropagation = YES; +} + +- (void)stopPropagation +{ + mStopsPropagation = YES; +} + ++ (SPEvent*)eventWithType:(NSString*)type bubbles:(BOOL)bubbles +{ + return [[[SPEvent alloc] initWithType:type bubbles:bubbles] autorelease]; +} + ++ (SPEvent*)eventWithType:(NSString*)type +{ + return [[[SPEvent alloc] initWithType:type] autorelease]; +} + +- (void)dealloc +{ + [mType release]; + [mTarget release]; + [mCurrentTarget release]; + [super dealloc]; +} + +@end + +// ------------------------------------------------------------------------------------------------- + +@implementation SPEvent (Internal) + +- (BOOL)stopsImmediatePropagation +{ + return mStopsImmediatePropagation; +} + +- (BOOL)stopsPropagation +{ + return mStopsPropagation; +} + +- (void)setTarget:(SPEventDispatcher*)target +{ + if (target != mTarget) + { + [mTarget release]; + mTarget = [target retain]; + } +} + +- (void)setCurrentTarget:(SPEventDispatcher*)currentTarget +{ + if (currentTarget != mCurrentTarget) + { + [mCurrentTarget release]; + mCurrentTarget = [currentTarget retain]; + } +} + +@end diff --git a/libs/sparrow/src/Classes/SPEventDispatcher.h b/libs/sparrow/src/Classes/SPEventDispatcher.h new file mode 100644 index 0000000..264e9da --- /dev/null +++ b/libs/sparrow/src/Classes/SPEventDispatcher.h @@ -0,0 +1,82 @@ +// +// SPEventDispatcher.h +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPEvent.h" + +/** ------------------------------------------------------------------------------------------------ + + The SPEventDispatcher class is the base for all classes that dispatch events. + + The event mechanism is a key feature of Sparrow's architecture. Objects can communicate with + each other over events. + + An event dispatcher can dispatch events (objects of type SPEvent or one of its subclasses) + to objects that have registered themselves as listeners. A string (the event type) is used to + identify different events. + + Here is a sample: + + // dispatching an event + [self dispatchEvent:[SPEvent eventWithType:@"eventType"]]; + + // listening to an event from 'object' + [object addEventListener:@selector(onEvent:) atObject:self forType:@"eventType"]; + + // the corresponding event listener + - (void)onEvent:(SPEvent *)event + { + // an event was triggered + } + + As SPDisplayObject, the base object of all rendered objects, inherits from SPEventDispatcher, + the event mechanism is tightly bound to the display list. Events that have their `bubbles`-property + enabled will rise up the display list until they reach its root (normally the stage). That means + that a listener can register for the event type not only on the object that will dispatch it, but + on any object that is a direct or indirect parent of the dispatcher. + + Different to _Adobe Flash_, events in Sparrow do not have a capture-phase. + + @see [SPEvent] + @see [SPDisplayObject] + +------------------------------------------------------------------------------------------------- */ + +@interface SPEventDispatcher : NSObject +{ + @private + NSMutableDictionary *mEventListeners; +} + +/// ------------- +/// @name Methods +/// ------------- + +/// Registers an event listener at a certain object. +- (void)addEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType + retainObject:(BOOL)retain; + +/// Registers an event listener at a certain object without retaining it (recommended). +- (void)addEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType; + +/// Removes an event listener at an object. +- (void)removeEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType; + +/// Removes all event listeners at an objct that have a certain type. +- (void)removeEventListenersAtObject:(id)object forType:(NSString*)eventType; + +/// Dispatches an event to all objects that have registered for events of the same type. +- (void)dispatchEvent:(SPEvent*)event; + +/// Returns if there are listeners registered for a certain event type. +- (BOOL)hasEventListenerForType:(NSString*)eventType; + +@end diff --git a/libs/sparrow/src/Classes/SPEventDispatcher.m b/libs/sparrow/src/Classes/SPEventDispatcher.m new file mode 100644 index 0000000..9721b9d --- /dev/null +++ b/libs/sparrow/src/Classes/SPEventDispatcher.m @@ -0,0 +1,140 @@ +// +// SPEventDispatcher.m +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPEventDispatcher.h" +#import "SPDisplayObject.h" +#import "SPEvent_Internal.h" +#import "SPMacros.h" +#import "SPNSExtensions.h" + +@implementation SPEventDispatcher + +- (void)addEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType + retainObject:(BOOL)doRetain +{ + if (!mEventListeners) + mEventListeners = [[NSMutableDictionary alloc] init]; + + NSInvocation *invocation = [NSInvocation invocationWithTarget:object selector:listener]; + if (doRetain) [invocation retainArguments]; + + // When an event listener is added or removed, a new NSArray object is created, instead of + // changing the array. The reason for this is that we can avoid creating a copy of the NSArray + // in the "dispatchEvent"-method, which is called far more often than + // "add"- and "removeEventListener". + + NSArray *listeners = [mEventListeners objectForKey:eventType]; + if (!listeners) + { + listeners = [[NSArray alloc] initWithObjects:invocation, nil]; + [mEventListeners setObject:listeners forKey:eventType]; + [listeners release]; + } + else + { + listeners = [listeners arrayByAddingObject:invocation]; + [mEventListeners setObject:listeners forKey:eventType]; + } +} + +- (void)addEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType +{ + [self addEventListener:listener atObject:object forType:eventType retainObject:NO]; +} + +- (void)removeEventListener:(SEL)listener atObject:(id)object forType:(NSString*)eventType +{ + NSArray *listeners = [mEventListeners objectForKey:eventType]; + if (listeners) + { + NSMutableArray *remainingListeners = [[NSMutableArray alloc] init]; + for (NSInvocation *inv in listeners) + { + if (inv.target != object || (listener != nil && inv.selector != listener)) + [remainingListeners addObject:inv]; + } + + if (remainingListeners.count == 0) [mEventListeners removeObjectForKey:eventType]; + else [mEventListeners setObject:remainingListeners forKey:eventType]; + + [remainingListeners release]; + } +} + +- (void)removeEventListenersAtObject:(id)object forType:(NSString*)eventType +{ + [self removeEventListener:nil atObject:object forType:eventType]; +} + +- (BOOL)hasEventListenerForType:(NSString*)eventType +{ + return [mEventListeners objectForKey:eventType] != nil; +} + +- (void)dispatchEvent:(SPEvent*)event +{ + NSMutableArray *listeners = [mEventListeners objectForKey:event.type]; + if (!event.bubbles && !listeners) return; // no need to do anything. + + // if the event already has a current target, it was re-dispatched by user -> we change the + // target to 'self' for now, but undo that later on (instead of creating a copy, which could + // lead to the creation of a huge amount of objects). + SPEventDispatcher *previousTarget = event.target; + if (!event.target || event.currentTarget) event.target = self; + event.currentTarget = self; + + [self retain]; // the event listener could release 'self', so we have to make sure that it + // stays valid while we're here. + + BOOL stopImmediatPropagation = NO; + if (listeners.count != 0) + { + // we can enumerate directly over the array, since "add"- and "removeEventListener" won't + // change it, but instead always create a new array. + [listeners retain]; + for (NSInvocation *inv in listeners) + { + [inv setArgument:&event atIndex:2]; + [inv invoke]; + if (event.stopsImmediatePropagation) + { + stopImmediatPropagation = YES; + break; + } + } + [listeners release]; + } + + if (!stopImmediatPropagation) + { + event.currentTarget = nil; // this is how we can find out later if the event was redispatched + if (event.bubbles && !event.stopsPropagation && [self isKindOfClass:[SPDisplayObject class]]) + { + SPDisplayObject *target = (SPDisplayObject*)self; + [target.parent dispatchEvent:event]; + } + } + + if (previousTarget) event.target = previousTarget; + + // we use autorelease instead of release to avoid having to make additional "retain"-calls + // in calling methods (like "dispatchEventsOnChildren"). Those methods might be called very + // often, so we save some time by avoiding that. + [self autorelease]; +} + +- (void)dealloc +{ + [mEventListeners release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPEvent_Internal.h b/libs/sparrow/src/Classes/SPEvent_Internal.h new file mode 100644 index 0000000..acace14 --- /dev/null +++ b/libs/sparrow/src/Classes/SPEvent_Internal.h @@ -0,0 +1,23 @@ +// +// SPEvent_Internal.h +// Sparrow +// +// Created by Daniel Sperl on 03.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPEvent.h" + +@interface SPEvent (Internal) + +- (BOOL)stopsImmediatePropagation; +- (BOOL)stopsPropagation; +- (void)setTarget:(SPEventDispatcher*)target; +- (void)setCurrentTarget:(SPEventDispatcher*)currentTarget; + +@end + diff --git a/libs/sparrow/src/Classes/SPGLTexture.h b/libs/sparrow/src/Classes/SPGLTexture.h new file mode 100644 index 0000000..71b2e44 --- /dev/null +++ b/libs/sparrow/src/Classes/SPGLTexture.h @@ -0,0 +1,82 @@ +// +// SPGLTexture.h +// Sparrow +// +// Created by Daniel Sperl on 27.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import + +#import "SPTexture.h" +#import "SPMacros.h" + +@class SPRectangle; + +typedef enum +{ + SPTextureFormatRGBA, + SPTextureFormatAlpha, + SPTextureFormatPvrtcRGB2, + SPTextureFormatPvrtcRGBA2, + SPTextureFormatPvrtcRGB4, + SPTextureFormatPvrtcRGBA4, + SPTextureFormat565, + SPTextureFormat5551, + SPTextureFormat4444 +} SPTextureFormat; + +typedef struct +{ + SPTextureFormat format; + int width; + int height; + int numMipmaps; + BOOL generateMipmaps; + BOOL premultipliedAlpha; +} SPTextureProperties; + +/** ------------------------------------------------------------------------------------------------ + + The SPGLTexture class is a concrete implementation of the abstract class SPTexture, + containing a standard 2D OpenGL texture. + + Don't use this class directly, but load textures with the init-methods of SPTexture instead. + +------------------------------------------------------------------------------------------------- */ + +@interface SPGLTexture : SPTexture +{ + @private + uint mTextureID; + float mWidth; + float mHeight; + float mScale; + BOOL mRepeat; + BOOL mPremultipliedAlpha; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a texture with raw pixel data and a set of properties. +- (id)initWithData:(const void *)imgData properties:(SPTextureProperties)properties; + +/// Factory method. ++ (SPGLTexture*)textureWithData:(const void *)imgData properties:(SPTextureProperties)properties; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// Indicates if the texture should repeat like a wallpaper or stretch the outermost pixels. +@property (nonatomic, assign) BOOL repeat; + +/// The scale factor, which influences `width` and `height` properties. +@property (nonatomic, assign) float scale; + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPGLTexture.m b/libs/sparrow/src/Classes/SPGLTexture.m new file mode 100644 index 0000000..5ff43a4 --- /dev/null +++ b/libs/sparrow/src/Classes/SPGLTexture.m @@ -0,0 +1,183 @@ +// +// SPGLTexture.m +// Sparrow +// +// Created by Daniel Sperl on 27.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPGLTexture.h" +#import "SPMacros.h" +#import "SPRectangle.h" + +#import +#import + +@implementation SPGLTexture + +@synthesize textureID = mTextureID; +@synthesize repeat = mRepeat; +@synthesize hasPremultipliedAlpha = mPremultipliedAlpha; +@synthesize scale = mScale; + +- (id)initWithData:(const void*)imgData properties:(SPTextureProperties)properties +{ + if (self = [super init]) + { + mWidth = properties.width; + mHeight = properties.height; + mRepeat = NO; + mPremultipliedAlpha = properties.premultipliedAlpha; + mScale = 1.0f; + + GLenum glTexType = GL_UNSIGNED_BYTE; + GLenum glTexFormat; + int bitsPerPixel; + BOOL compressed = NO; + + switch (properties.format) + { + default: + case SPTextureFormatRGBA: + bitsPerPixel = 8; + glTexFormat = GL_RGBA; + break; + case SPTextureFormatAlpha: + bitsPerPixel = 8; + glTexFormat = GL_ALPHA; + break; + case SPTextureFormatPvrtcRGBA2: + compressed = YES; + bitsPerPixel = 2; + glTexFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + break; + case SPTextureFormatPvrtcRGB2: + compressed = YES; + bitsPerPixel = 2; + glTexFormat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + break; + case SPTextureFormatPvrtcRGBA4: + compressed = YES; + bitsPerPixel = 4; + glTexFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + break; + case SPTextureFormatPvrtcRGB4: + compressed = YES; + bitsPerPixel = 4; + glTexFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + break; + case SPTextureFormat565: + bitsPerPixel = 16; + glTexFormat = GL_RGB; + glTexType = GL_UNSIGNED_SHORT_5_6_5; + break; + case SPTextureFormat5551: + bitsPerPixel = 16; + glTexFormat = GL_RGBA; + glTexType = GL_UNSIGNED_SHORT_5_5_5_1; + break; + case SPTextureFormat4444: + bitsPerPixel = 16; + glTexFormat = GL_RGBA; + glTexType = GL_UNSIGNED_SHORT_4_4_4_4; + break; + } + + glGenTextures(1, &mTextureID); + glBindTexture(GL_TEXTURE_2D, mTextureID); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + + if (!compressed) + { + if (properties.numMipmaps > 0 || properties.generateMipmaps) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + else + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + if (properties.numMipmaps == 0 && properties.generateMipmaps) + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + + int levelWidth = mWidth; + int levelHeight = mHeight; + unsigned char *levelData = (unsigned char *)imgData; + + for (int level=0; level<=properties.numMipmaps; ++level) + { + int size = levelWidth * levelHeight * bitsPerPixel / 8; + glTexImage2D(GL_TEXTURE_2D, level, glTexFormat, levelWidth, levelHeight, + 0, glTexFormat, glTexType, levelData); + levelData += size; + levelWidth /= 2; + levelHeight /= 2; + } + } + else + { + // 'generateMipmaps' not supported for compressed textures + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, properties.numMipmaps == 0 ? + GL_LINEAR : GL_LINEAR_MIPMAP_NEAREST); + + int levelWidth = mWidth; + int levelHeight = mHeight; + unsigned char *levelData = (unsigned char *)imgData; + + for (int level=0; level<=properties.numMipmaps; ++level) + { + int size = MAX(32, levelWidth * levelHeight * bitsPerPixel / 8); + glCompressedTexImage2D(GL_TEXTURE_2D, level, glTexFormat, + levelWidth, levelHeight, 0, size, levelData); + levelData += size; + levelWidth /= 2; + levelHeight /= 2; + } + } + + glBindTexture(GL_TEXTURE_2D, 0); + + } + return self; +} + +- (id)init +{ + return [self initWithData:NULL properties:(SPTextureProperties){ .width = 32, .height = 32 }]; +} + +- (float)width +{ + return mWidth / mScale; +} + +- (float)height +{ + return mHeight / mScale; +} + +- (void)setRepeat:(BOOL)value +{ + mRepeat = value; + glBindTexture(GL_TEXTURE_2D, mTextureID); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); +} + ++ (SPGLTexture*)textureWithData:(const void *)imgData properties:(SPTextureProperties)properties +{ + return [[[SPGLTexture alloc] initWithData:imgData properties:properties] autorelease]; +} + +- (void)dealloc +{ + glDeleteTextures(1, &mTextureID); + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPImage.h b/libs/sparrow/src/Classes/SPImage.h new file mode 100644 index 0000000..06e4d3b --- /dev/null +++ b/libs/sparrow/src/Classes/SPImage.h @@ -0,0 +1,72 @@ +// +// SPImage.h +// Sparrow +// +// Created by Daniel Sperl on 19.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPQuad.h" + +@class SPTexture; +@class SPPoint; + +/** ------------------------------------------------------------------------------------------------ + + An SPImage displays a quad with a texture mapped onto it. + + Sparrow uses the SPTexture class to represent textures. To display a texture, you have to map + it on a quad - and that's what SPImage is for. + + As SPImage inherits from SPQuad, you can also give it a color. The resulting color is then the + result of the multiplication between the color of the texture and the color of the quad. That way, + you can easily tint textures with a certain color. Furthermore, SPImage allows the manipulation of + texture coordinates. + +------------------------------------------------------------------------------------------------- */ + +@interface SPImage : SPQuad +{ + @protected + SPTexture *mTexture; + float mTexCoords[8]; +} + +/// -------------------- +/// @name Initialization +/// -------------------- + +/// Initialize a quad with a texture mapped onto it. _Designated Initializer_. +- (id)initWithTexture:(SPTexture*)texture; + +/// Initialize a quad with a texture loaded from a file. +- (id)initWithContentsOfFile:(NSString*)path; + +/// Factory method. ++ (SPImage*)imageWithTexture:(SPTexture*)texture; + +/// Factory method. ++ (SPImage*)imageWithContentsOfFile:(NSString*)path; + +/// ------------- +/// @name Methods +/// ------------- + +/// Sets the texture coordinates of a vertex. Coordinates are in the range [0, 1]. +- (void)setTexCoords:(SPPoint*)coords ofVertex:(int)vertexID; + +/// Gets the texture coordinates of a vertex. +- (SPPoint*)texCoordsOfVertex:(int)vertexID; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The texture that is displayed on the quad. +@property (nonatomic, retain) SPTexture *texture; + +@end diff --git a/libs/sparrow/src/Classes/SPImage.m b/libs/sparrow/src/Classes/SPImage.m new file mode 100644 index 0000000..732a432 --- /dev/null +++ b/libs/sparrow/src/Classes/SPImage.m @@ -0,0 +1,80 @@ +// +// SPImage.m +// Sparrow +// +// Created by Daniel Sperl on 19.06.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPImage.h" +#import "SPPoint.h" +#import "SPTexture.h" +#import "SPGLTexture.h" + +@implementation SPImage + +@synthesize texture = mTexture; + +- (id)initWithTexture:(SPTexture*)texture; +{ + if (!texture) [NSException raise:SP_EXC_INVALID_OPERATION format:@"texture cannot be nil!"]; + + if (self = [super initWithWidth:texture.width height:texture.height]) + { + self.texture = texture; + mTexCoords[0] = 0.0f; mTexCoords[1] = 0.0f; + mTexCoords[2] = 1.0f; mTexCoords[3] = 0.0f; + mTexCoords[4] = 0.0f; mTexCoords[5] = 1.0f; + mTexCoords[6] = 1.0f; mTexCoords[7] = 1.0f; + } + return self; +} + +- (id)initWithContentsOfFile:(NSString*)path +{ + return [self initWithTexture:[SPTexture textureWithContentsOfFile:path]]; +} + +- (id)initWithWidth:(float)width height:(float)height +{ + SPTextureProperties properties = { .width = width, .height = height }; + return [self initWithTexture:[SPGLTexture textureWithData:NULL properties:properties]]; +} + +- (void)setTexCoords:(SPPoint*)coords ofVertex:(int)vertexID +{ + if (vertexID < 0 || vertexID > 3) + [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"invalid vertex id"]; + + mTexCoords[2*vertexID ] = coords.x; + mTexCoords[2*vertexID+1] = coords.y; +} + +- (SPPoint*)texCoordsOfVertex:(int)vertexID +{ + if (vertexID < 0 || vertexID > 3) + [NSException raise:SP_EXC_INDEX_OUT_OF_BOUNDS format:@"invalid vertex id"]; + + return [SPPoint pointWithX:mTexCoords[vertexID*2] y:mTexCoords[vertexID*2+1]]; +} + ++ (SPImage*)imageWithTexture:(SPTexture*)texture +{ + return [[[SPImage alloc] initWithTexture:texture] autorelease]; +} + ++ (SPImage*)imageWithContentsOfFile:(NSString*)path +{ + return [[[SPImage alloc] initWithContentsOfFile:path] autorelease]; +} + +- (void)dealloc +{ + [mTexture release]; + [super dealloc]; +} + +@end \ No newline at end of file diff --git a/libs/sparrow/src/Classes/SPJuggler.h b/libs/sparrow/src/Classes/SPJuggler.h new file mode 100644 index 0000000..c7b48da --- /dev/null +++ b/libs/sparrow/src/Classes/SPJuggler.h @@ -0,0 +1,88 @@ +// +// SPJuggler.h +// Sparrow +// +// Created by Daniel Sperl on 09.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPAnimatable.h" + +/** ------------------------------------------------------------------------------------------------ + + The SPJuggler takes objects that implement SPAnimatable (e.g. `SPTween`s) and executes them. + + A juggler is a simple object. It does no more than saving a list of objects implementing + `SPAnimatable` and advancing their time if he is told to do so (by calling its own `advanceTime:` + method). When an animation is completed, it throws it away. + + There is a default juggler in every stage. You can access it by calling + + SPJuggler *juggler = self.stage.juggler; + + in any object that has access to the stage. You can, however, create juggler objects yourself, too. + That way, you can group your game into logical components that handle their animations independently. + + A cool feature of the juggler is to delay method calls. Say you want to remove an object from its + parent 2 seconds from now. Call: + + [[juggler delayInvocationAtTarget:object byTime:2.0] removeFromParent]; + + This line of code will execute the following method 2 seconds in the future: + + [object removeFromParent]; + + The Sparrow blog contains three extensive articles about the juggler: + + * http://www.sparrow-framework.org/2010/08/tweens-jugglers-animating-your-stage/ + * http://www.sparrow-framework.org/2010/09/tweens-jugglers-an-in-depth-look-at-the-juggler/ + * http://www.sparrow-framework.org/2010/10/tweens-jugglers-unleashed/ + +------------------------------------------------------------------------------------------------- */ + +@interface SPJuggler : NSObject +{ + @private + NSMutableSet *mObjects; + double mElapsedTime; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Factory method. ++ (SPJuggler *)juggler; + +/// ------------- +/// @name Methods +/// ------------- + +/// Adds an object to the juggler. +- (void)addObject:(id)object; + +/// Removes an object from the juggler. +- (void)removeObject:(id)object; + +/// Removes all objects at once. +- (void)removeAllObjects; + +/// Removes all objects of type `SPTween` that have a certain target. +- (void)removeTweensWithTarget:(id)object; + +/// Delays the execution of a certain method. Returns a proxy object on which to call the method +/// instead. Execution will be delayed until `time` has passed. +- (id)delayInvocationAtTarget:(id)target byTime:(double)time; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The total life time of the juggler. +@property (nonatomic, readonly) double elapsedTime; + +@end diff --git a/libs/sparrow/src/Classes/SPJuggler.m b/libs/sparrow/src/Classes/SPJuggler.m new file mode 100644 index 0000000..831919a --- /dev/null +++ b/libs/sparrow/src/Classes/SPJuggler.m @@ -0,0 +1,96 @@ +// +// SPJuggler.m +// Sparrow +// +// Created by Daniel Sperl on 09.05.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPJuggler.h" +#import "SPAnimatable.h" +#import "SPDelayedInvocation.h" + +@implementation SPJuggler + +@synthesize elapsedTime = mElapsedTime; + +- (id)init +{ + if (self = [super init]) + { + mObjects = [[NSMutableSet alloc] init]; + mElapsedTime = 0.0; + } + return self; +} + +- (BOOL)isComplete +{ + return NO; +} + +- (void)advanceTime:(double)seconds +{ + mElapsedTime += seconds; + + // we need work with a copy, since user-code could modify the collection during the enumeration + for (id object in [mObjects allObjects]) + { + [object advanceTime:seconds]; + if (object.isComplete) [self removeObject:object]; + } +} + +- (void)addObject:(id)object +{ + if (object) + [mObjects addObject:object]; +} + +- (void)removeObject:(id)object +{ + [mObjects removeObject:object]; +} + +- (void)removeAllObjects; +{ + [mObjects removeAllObjects]; +} + +- (void)removeTweensWithTarget:(id)object +{ + SEL targetSel = @selector(target); + NSMutableSet *remainingObjects = [[NSMutableSet alloc] init]; + + for (id currentObject in mObjects) + { + if (![currentObject respondsToSelector:targetSel] || ![[currentObject target] isEqual:object]) + [remainingObjects addObject:currentObject]; + } + + [mObjects release]; + mObjects = remainingObjects; +} + +- (id)delayInvocationAtTarget:(id)target byTime:(double)time +{ + SPDelayedInvocation *delayedInvoc = [SPDelayedInvocation invocationWithTarget:target delay:time]; + [self addObject:delayedInvoc]; + return delayedInvoc; +} + ++ (SPJuggler *)juggler +{ + return [[[SPJuggler alloc] init] autorelease]; +} + +- (void)dealloc +{ + [mObjects release]; + [super dealloc]; +} + +@end diff --git a/libs/sparrow/src/Classes/SPMacros.h b/libs/sparrow/src/Classes/SPMacros.h new file mode 100644 index 0000000..aea1b3e --- /dev/null +++ b/libs/sparrow/src/Classes/SPMacros.h @@ -0,0 +1,56 @@ +// +// SPMacros.h +// Sparrow +// +// Created by Daniel Sperl on 15.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import + +// constants + +#define PI 3.14159265359f +#define PI_HALF 1.57079632679f +#define TWO_PI 6.28318530718f + +#define SP_FLOAT_EPSILON 0.0001f + +#define SP_WHITE 0xFFFFFF +#define SP_BLACK 0x000000 + +#define SP_NOT_FOUND -1 +#define SP_MAX_DISPLAY_TREE_DEPTH 16 + +// exceptions + +#define SP_EXC_ABSTRACT_CLASS @"AbstractClass" +#define SP_EXC_ABSTRACT_METHOD @"AbstractMethod" +#define SP_EXC_NOT_RELATED @"NotRelated" +#define SP_EXC_INDEX_OUT_OF_BOUNDS @"IndexOutOfBounds" +#define SP_EXC_INVALID_OPERATION @"InvalidOperation" +#define SP_EXC_FILE_NOT_FOUND @"FileNotFound" +#define SP_EXC_FILE_INVALID @"FileInvalid" + +// macros + +#define SP_CREATE_POOL(pool) NSAutoreleasePool *(pool) = [[NSAutoreleasePool alloc] init]; +#define SP_RELEASE_POOL(pool) [(pool) release]; + +#define SP_R2D(rad) ((rad) / PI * 180.0f) +#define SP_D2R(deg) ((deg) / 180.0f * PI) + +#define SP_COLOR_PART_ALPHA(color) (((color) >> 24) & 0xff) +#define SP_COLOR_PART_RED(color) (((color) >> 16) & 0xff) +#define SP_COLOR_PART_GREEN(color) (((color) >> 8) & 0xff) +#define SP_COLOR_PART_BLUE(color) ( (color) & 0xff) + +#define SP_COLOR(r, g, b) (((r) << 16) | ((g) << 8) | (b)) + +#define SP_IS_FLOAT_EQUAL(f1, f2) (fabsf((f1)-(f2)) < SP_FLOAT_EPSILON) + + diff --git a/libs/sparrow/src/Classes/SPMatrix.h b/libs/sparrow/src/Classes/SPMatrix.h new file mode 100644 index 0000000..7ca72df --- /dev/null +++ b/libs/sparrow/src/Classes/SPMatrix.h @@ -0,0 +1,99 @@ +// +// SPMatrix.h +// Sparrow +// +// Created by Daniel Sperl on 26.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPPoolObject.h" + +@class SPPoint; + +/** ------------------------------------------------------------------------------------------------ + + The SPMatrix class describes an affine, 2D transformation Matrix. It provides methods to + manipulate the matrix in convenient ways, and can be used to transform points. + + The matrix has the following form: + + |a c tx| + |b d ty| + |0 0 1| + +------------------------------------------------------------------------------------------------- */ + +@interface SPMatrix : SPPoolObject +{ + @private + float mA, mB, mC, mD; + float mTx, mTy; +} + +/// ----------------- +/// @name Intializers +/// ----------------- + +/// Initializes a matrix with the specified components. _Designated Initializer_. +- (id)initWithA:(float)a b:(float)b c:(float)c d:(float)d tx:(float)tx ty:(float)ty; + +/// Initializes an identity matrix. +- (id)init; + +/// Factory method. ++ (SPMatrix*)matrixWithA:(float)a b:(float)b c:(float)c d:(float)d tx:(float)tx ty:(float)ty; + +/// Factory method. ++ (SPMatrix*)matrixWithIdentity; + +/// ------------- +/// @name Methods +/// ------------- + +/// Compares to matrices. +- (BOOL)isEqual:(id)other; + +/// Concatenates a matrix with the current matrix, combining the geometric effects of the two. +- (void)concatMatrix:(SPMatrix*)matrix; + +/// Translates the matrix along the x and y axes. +- (void)translateXBy:(float)dx yBy:(float)dy; + +/// Applies a scaling transformation to the matrix. +- (void)scaleXBy:(float)sx yBy:(float)sy; + +/// Applies a uniform scaling transformation to the matrix. +- (void)scaleBy:(float)scale; + +/// Applies a rotation on the matrix (angle in RAD). +- (void)rotateBy:(float)angle; + +/// Sets each matrix property to a value that causes a null transformation. +- (void)identity; + +/// Performs the opposite transformation of the matrix. +- (void)invert; + +/// Applies the geometric transformation represented by the matrix to the specified point. +- (SPPoint*)transformPoint:(SPPoint*)point; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// Matrix component. +@property (nonatomic, assign) float a; +@property (nonatomic, assign) float b; +@property (nonatomic, assign) float c; +@property (nonatomic, assign) float d; +@property (nonatomic, assign) float tx; +@property (nonatomic, assign) float ty; + +/// The determinant of the matrix. +@property (nonatomic, readonly) float determinant; + +@end diff --git a/libs/sparrow/src/Classes/SPMatrix.m b/libs/sparrow/src/Classes/SPMatrix.m new file mode 100644 index 0000000..2e83df0 --- /dev/null +++ b/libs/sparrow/src/Classes/SPMatrix.m @@ -0,0 +1,165 @@ +// +// SPMatrix.m +// Sparrow +// +// Created by Daniel Sperl on 26.03.09. +// Copyright 2009 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPMatrix.h" +#import "SPPoint.h" +#import "SPMacros.h" + +#define U 0 +#define V 0 +#define W 1 + +@implementation SPMatrix + +@synthesize a=mA, b=mB, c=mC, d=mD, tx=mTx, ty=mTy; + +// --- c functions --- + +static void setValues(SPMatrix *matrix, float a, float b, float c, float d, float tx, float ty) +{ + matrix->mA = a; + matrix->mB = b; + matrix->mC = c; + matrix->mD = d; + matrix->mTx = tx; + matrix->mTy = ty; +} + +// --- + +- (id)initWithA:(float)a b:(float)b c:(float)c d:(float)d tx:(float)tx ty:(float)ty +{ + if (self = [super init]) + { + mA = a; mB = b; mC = c; mD = d; + mTx = tx; mTy = ty; + } + return self; +} + +- (id)init +{ + return [self initWithA:1 b:0 c:0 d:1 tx:0 ty:0]; +} + +- (void)setValuesA:(float)a b:(float)b c:(float)c d:(float)d tx:(float)tx ty:(float)ty +{ + mA = a; mB = b; mC = c; mD = d; + mTx = tx; mTy = ty; +} + +- (float)determinant +{ + return mA * mD - mC * mB; +} + +- (void)concatMatrix:(SPMatrix*)matrix +{ + setValues(self, matrix->mA * mA + matrix->mC * mB, + matrix->mB * mA + matrix->mD * mB, + matrix->mA * mC + matrix->mC * mD, + matrix->mB * mC + matrix->mD * mD, + matrix->mA * mTx + matrix->mC * mTy + matrix->mTx * W, + matrix->mB * mTx + matrix->mD * mTy + matrix->mTy * W); +} + +- (void)translateXBy:(float)dx yBy:(float)dy +{ + mTx += dx; + mTy += dy; +} + +- (void)scaleXBy:(float)sx yBy:(float)sy +{ + mA *= sx; + mB *= sy; + mC *= sx; + mD *= sy; + mTx *= sx; + mTy *= sy; +} + +- (void)scaleBy:(float)scale +{ + [self scaleXBy:scale yBy:scale]; +} + +- (void)rotateBy:(float)angle +{ + SPMatrix *rotMatrix = [[SPMatrix alloc] initWithA:cosf(angle) b:sinf(angle) + c:-sinf(angle) d:cosf(angle) tx:0 ty:0]; + [self concatMatrix:rotMatrix]; + [rotMatrix release]; +} + +- (void)identity +{ + setValues(self, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); +} + +- (SPPoint*)transformPoint:(SPPoint*)point +{ + return [SPPoint pointWithX:mA*point.x + mC*point.y + mTx + y:mB*point.x + mD*point.y + mTy]; +} + +- (void)invert +{ + float det = self.determinant; + setValues(self, mD/det, -mB/det, -mC/det, mA/det, (mC*mTy-mD*mTx)/det, (mB*mTx-mA*mTy)/det); +} + +- (BOOL)isEqual:(id)other +{ + if (other == self) return YES; + else if (!other || ![other isKindOfClass:[self class]]) return NO; + else + { + SPMatrix *matrix = (SPMatrix*)other; + return SP_IS_FLOAT_EQUAL(mA, matrix->mA) && SP_IS_FLOAT_EQUAL(mB, matrix->mB) && + SP_IS_FLOAT_EQUAL(mC, matrix->mC) && SP_IS_FLOAT_EQUAL(mD, matrix->mD) && + SP_IS_FLOAT_EQUAL(mTx, matrix->mTx) && SP_IS_FLOAT_EQUAL(mTy, matrix->mTy); + } +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"(a=%f, b=%f, c=%f, d=%f, tx=%f, ty=%f)", + mA, mB, mC, mD, mTx, mTy]; +} + ++ (SPMatrix*)matrixWithA:(float)a b:(float)b c:(float)c d:(float)d tx:(float)tx ty:(float)ty +{ + return [[[SPMatrix alloc] initWithA:a b:b c:c d:d tx:tx ty:ty] autorelease]; +} + ++ (SPMatrix*)matrixWithIdentity +{ + return [[[SPMatrix alloc] init] autorelease]; +} + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone*)zone; +{ + return [[[self class] allocWithZone:zone] initWithA:mA b:mB c:mC d:mD + tx:mTx ty:mTy]; +} + +#pragma mark SPPoolObject + ++ (SPPoolInfo *)poolInfo +{ + static SPPoolInfo poolInfo; + return &poolInfo; +} + +@end diff --git a/libs/sparrow/src/Classes/SPMovieClip.h b/libs/sparrow/src/Classes/SPMovieClip.h new file mode 100644 index 0000000..c3e85c8 --- /dev/null +++ b/libs/sparrow/src/Classes/SPMovieClip.h @@ -0,0 +1,136 @@ +// +// SPMovieClip.h +// Sparrow +// +// Created by Daniel Sperl on 01.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import +#import "SPSprite.h" +#import "SPTexture.h" +#import "SPAnimatable.h" +#import "SPImage.h" +#import "SPSoundChannel.h" + +#define SP_EVENT_TYPE_MOVIE_COMPLETED @"movieCompleted" + +/** ------------------------------------------------------------------------------------------------ + + An SPMovieClip is a simple way to display an animation depicted by a number of textures. + + You can add the frames one by one or pass them all at once (in an array) at initialization time. + The movie clip will have the width and height of the first frame. + + At initialization, you can specify the desired framerate. You can, however, manually give each + frame a custom duration. You can also play a sound whenever a certain frame appears. + + The methods `play` and `pause` control playback of the movie. You will receive an event of type + `SP_EVENT_TYPE_MOVIE_COMPLETED` when the movie finished playback (except when you enabled looping). + + As any animated object, a movie clip has to be added to a juggler (or have its `advanceTime:` + method called regularly) to run. + +------------------------------------------------------------------------------------------------- */ + +@interface SPMovieClip : SPImage +{ + @private + NSMutableArray *mFrames; + NSMutableArray *mSounds; + NSMutableArray *mFrameDurations; + + double mDefaultFrameDuration; + double mTotalDuration; + double mElapsedTime; + BOOL mLoop; + BOOL mPlaying; + int mCurrentFrame; +} + +/// ------------------ +/// @name Initializers +/// ------------------ + +/// Initializes a movie with the first frame and the default number of frames per second. _Designated initializer_. +- (id)initWithFrame:(SPTexture *)texture fps:(float)fps; // designated initializer + +/// Initializes a movie with an array of textures and the default number of frames per second. +- (id)initWithFrames:(NSArray *)textures fps:(float)fps; + +/// Factory method. ++ (SPMovieClip *)movieWithFrame:(SPTexture *)texture fps:(float)fps; + +/// Factory method. ++ (SPMovieClip *)movieWithFrames:(NSArray *)textures fps:(float)fps; + +/// -------------------------------- +/// @name Frame Manipulation Methods +/// -------------------------------- + +/// Adds a frame with the default duration. +- (int)addFrame:(SPTexture *)texture; + +/// Adds a frame with a specified duration. +- (int)addFrame:(SPTexture *)texture withDuration:(double)duration; + +/// Inserts a frame at the index specified. +- (void)insertFrame:(SPTexture *)texture atIndex:(int)frameID; + +/// Removes the frame at the index specified. +- (void)removeFrameAtIndex:(int)frameID; + +/// Sets the texture of a certain frame. +- (void)setFrame:(SPTexture *)texture atIndex:(int)frameID; + +/// Sets the sound that will be played back when a certain frame is active. +- (void)setSound:(SPSoundChannel *)sound atIndex:(int)frameID; + +/// Sets the duration of a certain frame in seconds. +- (void)setDuration:(double)duration atIndex:(int)frameID; + +/// Returns the texture of a frame at a certain index. +- (SPTexture *)frameAtIndex:(int)frameID; + +/// Returns the sound of a frame at a certain index. +- (SPSoundChannel *)soundAtIndex:(int)frameID; + +/// Returns the duration (in seconds) of a frame at a certain index. +- (double)durationAtIndex:(int)frameID; + +/// ---------------------- +/// @name Playback Methods +/// ---------------------- + +/// Start playback. Beware that the clip has to be added to a juggler, too! +- (void)play; + +/// Pause playback. +- (void)pause; + +/// ---------------- +/// @name Properties +/// ---------------- + +/// The number of frames of the clip. +@property (nonatomic, readonly) int numFrames; + +/// The accumulated duration of all frames. +@property (nonatomic, readonly) double duration; + +/// Indicates if the movie is currently playing. +@property (nonatomic, readonly) BOOL isPlaying; + +/// Indicates if the movie is looping. +@property (nonatomic, assign) BOOL loop; + +/// The ID of the frame that is currently displayed. +@property (nonatomic, assign) int currentFrame; + +/// The default frames per second. Used when you add a frame without specifying a duration. +@property (nonatomic, assign) float fps; + +@end diff --git a/libs/sparrow/src/Classes/SPMovieClip.m b/libs/sparrow/src/Classes/SPMovieClip.m new file mode 100644 index 0000000..bbabe34 --- /dev/null +++ b/libs/sparrow/src/Classes/SPMovieClip.m @@ -0,0 +1,274 @@ +// +// SPMovieClip.m +// Sparrow +// +// Created by Daniel Sperl on 01.05.10. +// Copyright 2010 Incognitek. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the Simplified BSD License. +// + +#import "SPMovieClip.h" +#import "SPMacros.h" + +// --- private interface --------------------------------------------------------------------------- + +@interface SPMovieClip () + +- (void)updateCurrentFrame; +- (void)playCurrentSound; +- (void)checkIndex:(int)frameID; + +@end + + +// --- class implementation ------------------------------------------------------------------------ + +@implementation SPMovieClip + +@synthesize loop = mLoop; +@synthesize isPlaying = mPlaying; +@synthesize currentFrame = mCurrentFrame; +@synthesize duration = mTotalDuration; + +- (id)initWithFrame:(SPTexture *)texture fps:(float)fps +{ + if (self = [super initWithTexture:texture]) + { + self.fps = fps; + mLoop = YES; + mPlaying = YES; + mTotalDuration = 0.0; + mElapsedTime = 0.0; + mCurrentFrame = 0; +