From 139e9f43a60c141c8c5d772f630684af175d990f Mon Sep 17 00:00:00 2001 From: Greg Harding Date: Thu, 6 Sep 2012 23:36:01 +1200 Subject: [PATCH 1/3] replaced cocos2d 1.x with cocos2d 2.0 --- cocos2d/FontLabel/FontLabel.h | 44 - cocos2d/FontLabel/FontLabel.m | 195 ---- cocos2d/FontLabel/FontLabelStringDrawing.h | 69 -- cocos2d/FontLabel/FontLabelStringDrawing.m | 892 ---------------- cocos2d/FontLabel/FontManager.h | 85 -- cocos2d/FontLabel/FontManager.m | 123 --- cocos2d/FontLabel/ZAttributedString.h | 77 -- cocos2d/FontLabel/ZAttributedString.m | 597 ----------- cocos2d/FontLabel/ZAttributedStringPrivate.h | 24 - cocos2d/FontLabel/ZFont.h | 47 - cocos2d/FontLabel/ZFont.m | 170 --- cocos2d/LICENSE_FontLabel.txt | 79 -- cocos2d/LICENSE_cocos2d.txt | 0 cocos2d/cocos2d/CCAction.h | 28 +- cocos2d/cocos2d/CCAction.m | 53 +- cocos2d/cocos2d/CCActionCamera.h | 14 +- cocos2d/cocos2d/CCActionCamera.m | 28 +- cocos2d/cocos2d/CCActionCatmullRom.h | 139 +++ cocos2d/cocos2d/CCActionCatmullRom.m | 363 +++++++ cocos2d/cocos2d/CCActionEase.h | 4 +- cocos2d/cocos2d/CCActionEase.m | 52 +- cocos2d/cocos2d/CCActionGrid.h | 4 +- cocos2d/cocos2d/CCActionGrid.m | 36 +- cocos2d/cocos2d/CCActionGrid3D.h | 11 +- cocos2d/cocos2d/CCActionGrid3D.m | 119 +-- cocos2d/cocos2d/CCActionInstant.h | 34 +- cocos2d/cocos2d/CCActionInstant.m | 108 +- cocos2d/cocos2d/CCActionInterval.h | 84 +- cocos2d/cocos2d/CCActionInterval.m | 525 ++++++---- cocos2d/cocos2d/CCActionManager.h | 38 +- cocos2d/cocos2d/CCActionManager.m | 114 +- cocos2d/cocos2d/CCActionPageTurn3D.h | 6 +- cocos2d/cocos2d/CCActionPageTurn3D.m | 24 +- cocos2d/cocos2d/CCActionProgressTimer.h | 4 +- cocos2d/cocos2d/CCActionProgressTimer.m | 8 +- cocos2d/cocos2d/CCActionTiledGrid.h | 4 +- cocos2d/cocos2d/CCActionTiledGrid.m | 120 +-- cocos2d/cocos2d/CCActionTween.h | 20 +- cocos2d/cocos2d/CCActionTween.m | 12 +- cocos2d/cocos2d/CCAnimation.h | 113 +- cocos2d/cocos2d/CCAnimation.m | 145 ++- cocos2d/cocos2d/CCAnimationCache.h | 22 +- cocos2d/cocos2d/CCAnimationCache.m | 167 ++- cocos2d/cocos2d/CCAtlasNode.h | 17 +- cocos2d/cocos2d/CCAtlasNode.m | 92 +- cocos2d/cocos2d/CCCamera.h | 23 +- cocos2d/cocos2d/CCCamera.m | 73 +- cocos2d/cocos2d/CCConfiguration.h | 48 +- cocos2d/cocos2d/CCConfiguration.m | 110 +- cocos2d/cocos2d/CCDirector.h | 179 ++-- cocos2d/cocos2d/CCDirector.m | 384 ++++--- cocos2d/cocos2d/CCDrawingPrimitives.h | 63 +- cocos2d/cocos2d/CCDrawingPrimitives.m | 408 +++++--- cocos2d/cocos2d/CCGLProgram.h | 150 +++ cocos2d/cocos2d/CCGLProgram.m | 395 +++++++ cocos2d/cocos2d/CCGrabber.h | 9 +- cocos2d/cocos2d/CCGrabber.m | 48 +- cocos2d/cocos2d/CCGrid.h | 13 +- cocos2d/cocos2d/CCGrid.m | 321 +++--- cocos2d/cocos2d/CCLabelAtlas.h | 33 +- cocos2d/cocos2d/CCLabelAtlas.m | 111 +- cocos2d/cocos2d/CCLabelBMFont.h | 118 ++- cocos2d/cocos2d/CCLabelBMFont.m | 561 +++++++--- cocos2d/cocos2d/CCLabelTTF.h | 86 +- cocos2d/cocos2d/CCLabelTTF.m | 264 ++++- cocos2d/cocos2d/CCLayer.h | 85 +- cocos2d/cocos2d/CCLayer.m | 244 +++-- cocos2d/cocos2d/CCMenu.h | 41 +- cocos2d/cocos2d/CCMenu.m | 223 ++-- cocos2d/cocos2d/CCMenuItem.h | 208 ++-- cocos2d/cocos2d/CCMenuItem.m | 561 +++++----- cocos2d/cocos2d/CCMotionStreak.h | 88 +- cocos2d/cocos2d/CCMotionStreak.m | 286 ++++- .../{CCBlockSupport.m => CCNode+Debug.h} | 33 +- cocos2d/cocos2d/CCNode+Debug.m | 71 ++ cocos2d/cocos2d/CCNode.h | 264 +++-- cocos2d/cocos2d/CCNode.m | 773 +++++++------- cocos2d/cocos2d/CCParallaxNode.h | 10 +- cocos2d/cocos2d/CCParallaxNode.m | 28 +- cocos2d/cocos2d/CCParticleBatchNode.h | 99 ++ cocos2d/cocos2d/CCParticleBatchNode.m | 473 +++++++++ cocos2d/cocos2d/CCParticleExamples.h | 46 +- cocos2d/cocos2d/CCParticleExamples.m | 258 ++--- cocos2d/cocos2d/CCParticleSystem.h | 104 +- cocos2d/cocos2d/CCParticleSystem.m | 647 +++++++----- cocos2d/cocos2d/CCParticleSystemPoint.h | 65 -- cocos2d/cocos2d/CCParticleSystemPoint.m | 211 ---- cocos2d/cocos2d/CCParticleSystemQuad.h | 23 +- cocos2d/cocos2d/CCParticleSystemQuad.m | 385 +++++-- cocos2d/cocos2d/CCProgressTimer.h | 69 +- cocos2d/cocos2d/CCProgressTimer.m | 559 +++++----- cocos2d/cocos2d/CCProtocols.h | 27 +- cocos2d/cocos2d/CCRenderTexture.h | 83 +- cocos2d/cocos2d/CCRenderTexture.m | 440 +++++--- cocos2d/cocos2d/CCRibbon.h | 117 --- cocos2d/cocos2d/CCRibbon.m | 383 ------- cocos2d/cocos2d/CCScene.h | 10 +- cocos2d/cocos2d/CCScene.m | 12 +- cocos2d/cocos2d/CCScheduler.h | 83 +- cocos2d/cocos2d/CCScheduler.m | 358 ++++--- cocos2d/cocos2d/CCShaderCache.h | 60 ++ cocos2d/cocos2d/CCShaderCache.m | 214 ++++ cocos2d/cocos2d/CCSprite.h | 124 +-- cocos2d/cocos2d/CCSprite.m | 746 +++++++------ cocos2d/cocos2d/CCSpriteBatchNode.h | 24 +- cocos2d/cocos2d/CCSpriteBatchNode.m | 455 +++++--- cocos2d/cocos2d/CCSpriteFrame.h | 48 +- cocos2d/cocos2d/CCSpriteFrame.m | 100 +- cocos2d/cocos2d/CCSpriteFrameCache.h | 27 +- cocos2d/cocos2d/CCSpriteFrameCache.m | 261 +++-- cocos2d/cocos2d/CCTMXLayer.h | 54 +- cocos2d/cocos2d/CCTMXLayer.m | 430 ++++---- cocos2d/cocos2d/CCTMXObjectGroup.h | 6 +- cocos2d/cocos2d/CCTMXObjectGroup.m | 12 +- cocos2d/cocos2d/CCTMXTiledMap.h | 27 +- cocos2d/cocos2d/CCTMXTiledMap.m | 118 ++- cocos2d/cocos2d/CCTMXXMLParser.h | 85 +- cocos2d/cocos2d/CCTMXXMLParser.m | 331 +++--- cocos2d/cocos2d/CCTexture2D.h | 123 ++- cocos2d/cocos2d/CCTexture2D.m | 985 +++++++++++------- cocos2d/cocos2d/CCTextureAtlas.h | 63 +- cocos2d/cocos2d/CCTextureAtlas.m | 339 ++++-- cocos2d/cocos2d/CCTextureCache.h | 45 +- cocos2d/cocos2d/CCTextureCache.m | 595 +++++------ cocos2d/cocos2d/CCTexturePVR.h | 14 +- cocos2d/cocos2d/CCTexturePVR.m | 216 ++-- cocos2d/cocos2d/CCTileMapAtlas.h | 20 +- cocos2d/cocos2d/CCTileMapAtlas.m | 69 +- cocos2d/cocos2d/CCTransition.h | 14 +- cocos2d/cocos2d/CCTransition.m | 222 ++-- cocos2d/cocos2d/CCTransitionPageTurn.h | 14 +- cocos2d/cocos2d/CCTransitionPageTurn.m | 14 +- ...nsitionRadial.h => CCTransitionProgress.h} | 36 +- cocos2d/cocos2d/CCTransitionProgress.m | 276 +++++ cocos2d/cocos2d/CCTransitionRadial.m | 115 -- cocos2d/cocos2d/Platforms/CCGL.h | 71 +- cocos2d/cocos2d/Platforms/CCNS.h | 36 +- cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.h | 27 +- cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.m | 432 +++++--- .../cocos2d/Platforms/Mac/CCEventDispatcher.h | 56 +- .../cocos2d/Platforms/Mac/CCEventDispatcher.m | 307 +++--- .../Platforms/Mac/{MacGLView.h => CCGLView.h} | 42 +- .../Platforms/Mac/{MacGLView.m => CCGLView.m} | 141 ++- .../Platforms/Mac/{MacWindow.h => CCWindow.h} | 15 +- .../Platforms/Mac/{MacWindow.m => CCWindow.m} | 21 +- cocos2d/cocos2d/Platforms/iOS/CCDirectorIOS.h | 181 +--- cocos2d/cocos2d/Platforms/iOS/CCDirectorIOS.m | 722 +++++-------- .../iOS/{ES1Renderer.h => CCES2Renderer.h} | 43 +- .../iOS/{ES1Renderer.m => CCES2Renderer.m} | 231 ++-- .../iOS/{ESRenderer.h => CCESRenderer.h} | 14 +- .../Platforms/iOS/{EAGLView.h => CCGLView.h} | 51 +- .../Platforms/iOS/{EAGLView.m => CCGLView.m} | 143 +-- .../Platforms/iOS/CCTouchDelegateProtocol.h | 18 +- .../cocos2d/Platforms/iOS/CCTouchDispatcher.h | 27 +- .../cocos2d/Platforms/iOS/CCTouchDispatcher.m | 118 +-- .../cocos2d/Platforms/iOS/CCTouchHandler.h | 10 +- .../cocos2d/Platforms/iOS/CCTouchHandler.m | 20 +- cocos2d/cocos2d/Platforms/iOS/glu.c | 113 -- cocos2d/cocos2d/Platforms/iOS/glu.h | 29 - cocos2d/cocos2d/Support/CCArray.h | 25 +- cocos2d/cocos2d/Support/CCArray.m | 156 ++- cocos2d/cocos2d/Support/CCFileUtils.h | 160 ++- cocos2d/cocos2d/Support/CCFileUtils.m | 404 +++++-- cocos2d/cocos2d/Support/CCProfiling.h | 60 +- cocos2d/cocos2d/Support/CCProfiling.m | 145 ++- .../{CCBlockSupport.h => Support/CCVertex.h} | 41 +- cocos2d/cocos2d/Support/CCVertex.m | 134 +++ cocos2d/cocos2d/Support/CGPointExtension.h | 46 +- cocos2d/cocos2d/Support/CGPointExtension.m | 10 +- .../cocos2d/Support/NSThread+performBlock.h | 22 + .../cocos2d/Support/NSThread+performBlock.m | 76 ++ cocos2d/cocos2d/Support/OpenGL_Internal.h | 8 +- cocos2d/cocos2d/Support/TGAlib.h | 6 +- cocos2d/cocos2d/Support/TGAlib.m | 80 +- cocos2d/cocos2d/Support/TransformUtils.h | 15 +- cocos2d/cocos2d/Support/TransformUtils.m | 9 +- cocos2d/cocos2d/Support/ZipUtils.h | 22 +- cocos2d/cocos2d/Support/ZipUtils.m | 79 +- cocos2d/cocos2d/Support/base64.c | 14 +- cocos2d/cocos2d/Support/base64.h | 12 +- cocos2d/cocos2d/Support/ccCArray.h | 372 ++----- cocos2d/cocos2d/Support/ccCArray.m | 542 ++++++++++ cocos2d/cocos2d/Support/ccUtils.c | 2 +- cocos2d/cocos2d/Support/ccUtils.h | 13 +- cocos2d/cocos2d/Support/uthash.h | 72 +- cocos2d/cocos2d/Support/utlist.h | 16 +- cocos2d/cocos2d/ccConfig.h | 311 ++---- cocos2d/cocos2d/ccDeprecated.h | 300 ++++++ cocos2d/cocos2d/ccDeprecated.m | 422 ++++++++ cocos2d/cocos2d/ccGLStateCache.h | 138 +++ cocos2d/cocos2d/ccGLStateCache.m | 227 ++++ cocos2d/cocos2d/ccMacros.h | 278 +++-- cocos2d/cocos2d/ccShader_PositionColor_frag.h | 12 + cocos2d/cocos2d/ccShader_PositionColor_vert.h | 17 + .../ccShader_PositionTextureA8Color_frag.h | 16 + .../ccShader_PositionTextureA8Color_vert.h | 21 + ...hader_PositionTextureColorAlphaTest_frag.h | 23 + .../ccShader_PositionTextureColor_frag.h | 14 + .../ccShader_PositionTextureColor_vert.h | 22 + .../cocos2d/ccShader_PositionTexture_frag.h | 13 + .../ccShader_PositionTexture_uColor_frag.h | 16 + .../ccShader_PositionTexture_uColor_vert.h | 18 + .../cocos2d/ccShader_PositionTexture_vert.h | 17 + .../cocos2d/ccShader_Position_uColor_frag.h | 12 + .../cocos2d/ccShader_Position_uColor_vert.h | 19 + cocos2d/cocos2d/ccShaders.h | 45 + cocos2d/cocos2d/ccShaders.m | 65 ++ cocos2d/cocos2d/ccTypes.h | 105 +- cocos2d/cocos2d/cocos2d.h | 51 +- cocos2d/cocos2d/cocos2d.m | 10 +- 210 files changed, 17108 insertions(+), 12429 deletions(-) delete mode 100644 cocos2d/FontLabel/FontLabel.h delete mode 100644 cocos2d/FontLabel/FontLabel.m delete mode 100644 cocos2d/FontLabel/FontLabelStringDrawing.h delete mode 100644 cocos2d/FontLabel/FontLabelStringDrawing.m delete mode 100644 cocos2d/FontLabel/FontManager.h delete mode 100644 cocos2d/FontLabel/FontManager.m delete mode 100644 cocos2d/FontLabel/ZAttributedString.h delete mode 100644 cocos2d/FontLabel/ZAttributedString.m delete mode 100644 cocos2d/FontLabel/ZAttributedStringPrivate.h delete mode 100644 cocos2d/FontLabel/ZFont.h delete mode 100644 cocos2d/FontLabel/ZFont.m delete mode 100644 cocos2d/LICENSE_FontLabel.txt mode change 100644 => 100755 cocos2d/LICENSE_cocos2d.txt mode change 100644 => 100755 cocos2d/cocos2d/CCAction.h mode change 100644 => 100755 cocos2d/cocos2d/CCAction.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionCamera.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionCamera.m create mode 100755 cocos2d/cocos2d/CCActionCatmullRom.h create mode 100755 cocos2d/cocos2d/CCActionCatmullRom.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionEase.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionEase.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionGrid.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionGrid.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionGrid3D.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionGrid3D.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionInstant.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionInstant.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionInterval.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionInterval.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionManager.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionManager.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionPageTurn3D.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionPageTurn3D.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionProgressTimer.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionProgressTimer.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionTiledGrid.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionTiledGrid.m mode change 100644 => 100755 cocos2d/cocos2d/CCActionTween.h mode change 100644 => 100755 cocos2d/cocos2d/CCActionTween.m mode change 100644 => 100755 cocos2d/cocos2d/CCAnimation.h mode change 100644 => 100755 cocos2d/cocos2d/CCAnimation.m mode change 100644 => 100755 cocos2d/cocos2d/CCAnimationCache.h mode change 100644 => 100755 cocos2d/cocos2d/CCAnimationCache.m mode change 100644 => 100755 cocos2d/cocos2d/CCAtlasNode.h mode change 100644 => 100755 cocos2d/cocos2d/CCAtlasNode.m mode change 100644 => 100755 cocos2d/cocos2d/CCCamera.h mode change 100644 => 100755 cocos2d/cocos2d/CCCamera.m mode change 100644 => 100755 cocos2d/cocos2d/CCConfiguration.h mode change 100644 => 100755 cocos2d/cocos2d/CCConfiguration.m mode change 100644 => 100755 cocos2d/cocos2d/CCDirector.h mode change 100644 => 100755 cocos2d/cocos2d/CCDirector.m mode change 100644 => 100755 cocos2d/cocos2d/CCDrawingPrimitives.h mode change 100644 => 100755 cocos2d/cocos2d/CCDrawingPrimitives.m create mode 100755 cocos2d/cocos2d/CCGLProgram.h create mode 100755 cocos2d/cocos2d/CCGLProgram.m mode change 100644 => 100755 cocos2d/cocos2d/CCGrabber.h mode change 100644 => 100755 cocos2d/cocos2d/CCGrabber.m mode change 100644 => 100755 cocos2d/cocos2d/CCGrid.h mode change 100644 => 100755 cocos2d/cocos2d/CCGrid.m mode change 100644 => 100755 cocos2d/cocos2d/CCLabelAtlas.h mode change 100644 => 100755 cocos2d/cocos2d/CCLabelAtlas.m mode change 100644 => 100755 cocos2d/cocos2d/CCLabelBMFont.h mode change 100644 => 100755 cocos2d/cocos2d/CCLabelBMFont.m mode change 100644 => 100755 cocos2d/cocos2d/CCLabelTTF.h mode change 100644 => 100755 cocos2d/cocos2d/CCLabelTTF.m mode change 100644 => 100755 cocos2d/cocos2d/CCLayer.h mode change 100644 => 100755 cocos2d/cocos2d/CCLayer.m mode change 100644 => 100755 cocos2d/cocos2d/CCMenu.h mode change 100644 => 100755 cocos2d/cocos2d/CCMenu.m mode change 100644 => 100755 cocos2d/cocos2d/CCMenuItem.h mode change 100644 => 100755 cocos2d/cocos2d/CCMenuItem.m mode change 100644 => 100755 cocos2d/cocos2d/CCMotionStreak.h mode change 100644 => 100755 cocos2d/cocos2d/CCMotionStreak.m rename cocos2d/cocos2d/{CCBlockSupport.m => CCNode+Debug.h} (78%) mode change 100644 => 100755 create mode 100755 cocos2d/cocos2d/CCNode+Debug.m mode change 100644 => 100755 cocos2d/cocos2d/CCNode.h mode change 100644 => 100755 cocos2d/cocos2d/CCNode.m mode change 100644 => 100755 cocos2d/cocos2d/CCParallaxNode.h mode change 100644 => 100755 cocos2d/cocos2d/CCParallaxNode.m create mode 100755 cocos2d/cocos2d/CCParticleBatchNode.h create mode 100755 cocos2d/cocos2d/CCParticleBatchNode.m mode change 100644 => 100755 cocos2d/cocos2d/CCParticleExamples.h mode change 100644 => 100755 cocos2d/cocos2d/CCParticleExamples.m mode change 100644 => 100755 cocos2d/cocos2d/CCParticleSystem.h mode change 100644 => 100755 cocos2d/cocos2d/CCParticleSystem.m delete mode 100644 cocos2d/cocos2d/CCParticleSystemPoint.h delete mode 100644 cocos2d/cocos2d/CCParticleSystemPoint.m mode change 100644 => 100755 cocos2d/cocos2d/CCParticleSystemQuad.h mode change 100644 => 100755 cocos2d/cocos2d/CCParticleSystemQuad.m mode change 100644 => 100755 cocos2d/cocos2d/CCProgressTimer.h mode change 100644 => 100755 cocos2d/cocos2d/CCProgressTimer.m mode change 100644 => 100755 cocos2d/cocos2d/CCProtocols.h mode change 100644 => 100755 cocos2d/cocos2d/CCRenderTexture.h mode change 100644 => 100755 cocos2d/cocos2d/CCRenderTexture.m delete mode 100644 cocos2d/cocos2d/CCRibbon.h delete mode 100644 cocos2d/cocos2d/CCRibbon.m mode change 100644 => 100755 cocos2d/cocos2d/CCScene.h mode change 100644 => 100755 cocos2d/cocos2d/CCScene.m mode change 100644 => 100755 cocos2d/cocos2d/CCScheduler.h mode change 100644 => 100755 cocos2d/cocos2d/CCScheduler.m create mode 100755 cocos2d/cocos2d/CCShaderCache.h create mode 100755 cocos2d/cocos2d/CCShaderCache.m mode change 100644 => 100755 cocos2d/cocos2d/CCSprite.h mode change 100644 => 100755 cocos2d/cocos2d/CCSprite.m mode change 100644 => 100755 cocos2d/cocos2d/CCSpriteBatchNode.h mode change 100644 => 100755 cocos2d/cocos2d/CCSpriteBatchNode.m mode change 100644 => 100755 cocos2d/cocos2d/CCSpriteFrame.h mode change 100644 => 100755 cocos2d/cocos2d/CCSpriteFrame.m mode change 100644 => 100755 cocos2d/cocos2d/CCSpriteFrameCache.h mode change 100644 => 100755 cocos2d/cocos2d/CCSpriteFrameCache.m mode change 100644 => 100755 cocos2d/cocos2d/CCTMXLayer.h mode change 100644 => 100755 cocos2d/cocos2d/CCTMXLayer.m mode change 100644 => 100755 cocos2d/cocos2d/CCTMXObjectGroup.h mode change 100644 => 100755 cocos2d/cocos2d/CCTMXObjectGroup.m mode change 100644 => 100755 cocos2d/cocos2d/CCTMXTiledMap.h mode change 100644 => 100755 cocos2d/cocos2d/CCTMXTiledMap.m mode change 100644 => 100755 cocos2d/cocos2d/CCTMXXMLParser.h mode change 100644 => 100755 cocos2d/cocos2d/CCTMXXMLParser.m mode change 100644 => 100755 cocos2d/cocos2d/CCTexture2D.h mode change 100644 => 100755 cocos2d/cocos2d/CCTexture2D.m mode change 100644 => 100755 cocos2d/cocos2d/CCTextureAtlas.h mode change 100644 => 100755 cocos2d/cocos2d/CCTextureAtlas.m mode change 100644 => 100755 cocos2d/cocos2d/CCTextureCache.h mode change 100644 => 100755 cocos2d/cocos2d/CCTextureCache.m mode change 100644 => 100755 cocos2d/cocos2d/CCTexturePVR.h mode change 100644 => 100755 cocos2d/cocos2d/CCTexturePVR.m mode change 100644 => 100755 cocos2d/cocos2d/CCTileMapAtlas.h mode change 100644 => 100755 cocos2d/cocos2d/CCTileMapAtlas.m mode change 100644 => 100755 cocos2d/cocos2d/CCTransition.h mode change 100644 => 100755 cocos2d/cocos2d/CCTransition.m mode change 100644 => 100755 cocos2d/cocos2d/CCTransitionPageTurn.h mode change 100644 => 100755 cocos2d/cocos2d/CCTransitionPageTurn.m rename cocos2d/cocos2d/{CCTransitionRadial.h => CCTransitionProgress.h} (67%) mode change 100644 => 100755 create mode 100755 cocos2d/cocos2d/CCTransitionProgress.m delete mode 100644 cocos2d/cocos2d/CCTransitionRadial.m mode change 100644 => 100755 cocos2d/cocos2d/Platforms/CCGL.h mode change 100644 => 100755 cocos2d/cocos2d/Platforms/CCNS.h mode change 100644 => 100755 cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.h mode change 100644 => 100755 cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.m mode change 100644 => 100755 cocos2d/cocos2d/Platforms/Mac/CCEventDispatcher.h mode change 100644 => 100755 cocos2d/cocos2d/Platforms/Mac/CCEventDispatcher.m rename cocos2d/cocos2d/Platforms/Mac/{MacGLView.h => CCGLView.h} (80%) mode change 100644 => 100755 rename cocos2d/cocos2d/Platforms/Mac/{MacGLView.m => CCGLView.m} (67%) mode change 100644 => 100755 rename cocos2d/cocos2d/Platforms/Mac/{MacWindow.h => CCWindow.h} (87%) mode change 100644 => 100755 rename cocos2d/cocos2d/Platforms/Mac/{MacWindow.m => CCWindow.m} (90%) mode change 100644 => 100755 rename cocos2d/cocos2d/Platforms/iOS/{ES1Renderer.h => CCES2Renderer.h} (78%) rename cocos2d/cocos2d/Platforms/iOS/{ES1Renderer.m => CCES2Renderer.m} (50%) rename cocos2d/cocos2d/Platforms/iOS/{ESRenderer.h => CCESRenderer.h} (92%) rename cocos2d/cocos2d/Platforms/iOS/{EAGLView.h => CCGLView.h} (84%) rename cocos2d/cocos2d/Platforms/iOS/{EAGLView.m => CCGLView.m} (85%) mode change 100644 => 100755 cocos2d/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h mode change 100644 => 100755 cocos2d/cocos2d/Platforms/iOS/CCTouchDispatcher.h mode change 100644 => 100755 cocos2d/cocos2d/Platforms/iOS/CCTouchDispatcher.m mode change 100644 => 100755 cocos2d/cocos2d/Platforms/iOS/CCTouchHandler.h mode change 100644 => 100755 cocos2d/cocos2d/Platforms/iOS/CCTouchHandler.m delete mode 100755 cocos2d/cocos2d/Platforms/iOS/glu.c delete mode 100644 cocos2d/cocos2d/Platforms/iOS/glu.h mode change 100644 => 100755 cocos2d/cocos2d/Support/CCArray.h mode change 100644 => 100755 cocos2d/cocos2d/Support/CCArray.m mode change 100644 => 100755 cocos2d/cocos2d/Support/CCFileUtils.h mode change 100644 => 100755 cocos2d/cocos2d/Support/CCFileUtils.m mode change 100644 => 100755 cocos2d/cocos2d/Support/CCProfiling.h mode change 100644 => 100755 cocos2d/cocos2d/Support/CCProfiling.m rename cocos2d/cocos2d/{CCBlockSupport.h => Support/CCVertex.h} (68%) mode change 100644 => 100755 create mode 100755 cocos2d/cocos2d/Support/CCVertex.m mode change 100644 => 100755 cocos2d/cocos2d/Support/CGPointExtension.h mode change 100644 => 100755 cocos2d/cocos2d/Support/CGPointExtension.m create mode 100755 cocos2d/cocos2d/Support/NSThread+performBlock.h create mode 100755 cocos2d/cocos2d/Support/NSThread+performBlock.m mode change 100644 => 100755 cocos2d/cocos2d/Support/OpenGL_Internal.h mode change 100644 => 100755 cocos2d/cocos2d/Support/TGAlib.h mode change 100644 => 100755 cocos2d/cocos2d/Support/TGAlib.m mode change 100644 => 100755 cocos2d/cocos2d/Support/TransformUtils.h mode change 100644 => 100755 cocos2d/cocos2d/Support/TransformUtils.m mode change 100644 => 100755 cocos2d/cocos2d/Support/ZipUtils.h mode change 100644 => 100755 cocos2d/cocos2d/Support/ZipUtils.m mode change 100644 => 100755 cocos2d/cocos2d/Support/base64.c mode change 100644 => 100755 cocos2d/cocos2d/Support/base64.h mode change 100644 => 100755 cocos2d/cocos2d/Support/ccCArray.h create mode 100755 cocos2d/cocos2d/Support/ccCArray.m mode change 100644 => 100755 cocos2d/cocos2d/Support/ccUtils.c mode change 100644 => 100755 cocos2d/cocos2d/Support/ccUtils.h mode change 100644 => 100755 cocos2d/cocos2d/Support/uthash.h mode change 100644 => 100755 cocos2d/cocos2d/Support/utlist.h mode change 100644 => 100755 cocos2d/cocos2d/ccConfig.h create mode 100755 cocos2d/cocos2d/ccDeprecated.h create mode 100755 cocos2d/cocos2d/ccDeprecated.m create mode 100755 cocos2d/cocos2d/ccGLStateCache.h create mode 100755 cocos2d/cocos2d/ccGLStateCache.m mode change 100644 => 100755 cocos2d/cocos2d/ccMacros.h create mode 100755 cocos2d/cocos2d/ccShader_PositionColor_frag.h create mode 100755 cocos2d/cocos2d/ccShader_PositionColor_vert.h create mode 100755 cocos2d/cocos2d/ccShader_PositionTextureA8Color_frag.h create mode 100755 cocos2d/cocos2d/ccShader_PositionTextureA8Color_vert.h create mode 100755 cocos2d/cocos2d/ccShader_PositionTextureColorAlphaTest_frag.h create mode 100755 cocos2d/cocos2d/ccShader_PositionTextureColor_frag.h create mode 100755 cocos2d/cocos2d/ccShader_PositionTextureColor_vert.h create mode 100755 cocos2d/cocos2d/ccShader_PositionTexture_frag.h create mode 100755 cocos2d/cocos2d/ccShader_PositionTexture_uColor_frag.h create mode 100755 cocos2d/cocos2d/ccShader_PositionTexture_uColor_vert.h create mode 100755 cocos2d/cocos2d/ccShader_PositionTexture_vert.h create mode 100755 cocos2d/cocos2d/ccShader_Position_uColor_frag.h create mode 100755 cocos2d/cocos2d/ccShader_Position_uColor_vert.h create mode 100755 cocos2d/cocos2d/ccShaders.h create mode 100755 cocos2d/cocos2d/ccShaders.m mode change 100644 => 100755 cocos2d/cocos2d/ccTypes.h mode change 100644 => 100755 cocos2d/cocos2d/cocos2d.h mode change 100644 => 100755 cocos2d/cocos2d/cocos2d.m diff --git a/cocos2d/FontLabel/FontLabel.h b/cocos2d/FontLabel/FontLabel.h deleted file mode 100644 index 6de9c2c..0000000 --- a/cocos2d/FontLabel/FontLabel.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// FontLabel.h -// FontLabel -// -// Created by Kevin Ballard on 5/8/09. -// Copyright © 2009 Zynga Game Networks -// -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import -#import - -@class ZFont; -@class ZAttributedString; - -@interface FontLabel : UILabel { - void *reserved; // works around a bug in UILabel - ZFont *zFont; - ZAttributedString *zAttributedText; -} -@property (nonatomic, setter=setCGFont:) CGFontRef cgFont __AVAILABILITY_INTERNAL_DEPRECATED; -@property (nonatomic, assign) CGFloat pointSize __AVAILABILITY_INTERNAL_DEPRECATED; -@property (nonatomic, retain, setter=setZFont:) ZFont *zFont; -// if attributedText is nil, fall back on using the inherited UILabel properties -// if attributedText is non-nil, the font/text/textColor -// in addition, adjustsFontSizeToFitWidth does not work with attributed text -@property (nonatomic, copy) ZAttributedString *zAttributedText; -// -initWithFrame:fontName:pointSize: uses FontManager to look up the font name -- (id)initWithFrame:(CGRect)frame fontName:(NSString *)fontName pointSize:(CGFloat)pointSize; -- (id)initWithFrame:(CGRect)frame zFont:(ZFont *)font; -- (id)initWithFrame:(CGRect)frame font:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED; -@end diff --git a/cocos2d/FontLabel/FontLabel.m b/cocos2d/FontLabel/FontLabel.m deleted file mode 100644 index 58975b1..0000000 --- a/cocos2d/FontLabel/FontLabel.m +++ /dev/null @@ -1,195 +0,0 @@ -// -// FontLabel.m -// FontLabel -// -// Created by Kevin Ballard on 5/8/09. -// Copyright © 2009 Zynga Game Networks -// -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "FontLabel.h" -#import "FontManager.h" -#import "FontLabelStringDrawing.h" -#import "ZFont.h" - -@interface ZFont (ZFontPrivate) -@property (nonatomic, readonly) CGFloat ratio; -@end - -@implementation FontLabel -@synthesize zFont; -@synthesize zAttributedText; - -- (id)initWithFrame:(CGRect)frame fontName:(NSString *)fontName pointSize:(CGFloat)pointSize { - return [self initWithFrame:frame zFont:[[FontManager sharedManager] zFontWithName:fontName pointSize:pointSize]]; -} - -- (id)initWithFrame:(CGRect)frame zFont:(ZFont *)font { - if ((self = [super initWithFrame:frame])) { - zFont = [font retain]; - } - return self; -} - -- (id)initWithFrame:(CGRect)frame font:(CGFontRef)font pointSize:(CGFloat)pointSize { - return [self initWithFrame:frame zFont:[ZFont fontWithCGFont:font size:pointSize]]; -} - -- (CGFontRef)cgFont { - return self.zFont.cgFont; -} - -- (void)setCGFont:(CGFontRef)font { - if (self.zFont.cgFont != font) { - self.zFont = [ZFont fontWithCGFont:font size:self.zFont.pointSize]; - } -} - -- (CGFloat)pointSize { - return self.zFont.pointSize; -} - -- (void)setPointSize:(CGFloat)pointSize { - if (self.zFont.pointSize != pointSize) { - self.zFont = [ZFont fontWithCGFont:self.zFont.cgFont size:pointSize]; - } -} - -- (void)setZAttributedText:(ZAttributedString *)attStr { - if (zAttributedText != attStr) { - [zAttributedText release]; - zAttributedText = [attStr copy]; - [self setNeedsDisplay]; - } -} - -- (void)drawTextInRect:(CGRect)rect { - if (self.zFont == NULL && self.zAttributedText == nil) { - [super drawTextInRect:rect]; - return; - } - - if (self.zAttributedText == nil) { - // this method is documented as setting the text color for us, but that doesn't appear to be the case - if (self.highlighted) { - [(self.highlightedTextColor ?: [UIColor whiteColor]) setFill]; - } else { - [(self.textColor ?: [UIColor blackColor]) setFill]; - } - - ZFont *actualFont = self.zFont; - CGSize origSize = rect.size; - if (self.numberOfLines == 1) { - origSize.height = actualFont.leading; - CGPoint point = CGPointMake(rect.origin.x, - rect.origin.y + roundf(((rect.size.height - actualFont.leading) / 2.0f))); - CGSize size = [self.text sizeWithZFont:actualFont]; - if (self.adjustsFontSizeToFitWidth && self.minimumFontSize < actualFont.pointSize) { - if (size.width > origSize.width) { - CGFloat desiredRatio = (origSize.width * actualFont.ratio) / size.width; - CGFloat desiredPointSize = desiredRatio * actualFont.pointSize / actualFont.ratio; - actualFont = [actualFont fontWithSize:MAX(MAX(desiredPointSize, self.minimumFontSize), 1.0f)]; - size = [self.text sizeWithZFont:actualFont]; - } - if (!CGSizeEqualToSize(origSize, size)) { - switch (self.baselineAdjustment) { - case UIBaselineAdjustmentAlignCenters: - point.y += roundf((origSize.height - size.height) / 2.0f); - break; - case UIBaselineAdjustmentAlignBaselines: - point.y += (self.zFont.ascender - actualFont.ascender); - break; - case UIBaselineAdjustmentNone: - break; - } - } - } - size.width = MIN(size.width, origSize.width); - // adjust the point for alignment - switch (self.textAlignment) { - case UITextAlignmentLeft: - break; - case UITextAlignmentCenter: - point.x += (origSize.width - size.width) / 2.0f; - break; - case UITextAlignmentRight: - point.x += origSize.width - size.width; - break; - } - [self.text drawAtPoint:point forWidth:size.width withZFont:actualFont lineBreakMode:self.lineBreakMode]; - } else { - CGSize size = [self.text sizeWithZFont:actualFont constrainedToSize:origSize lineBreakMode:self.lineBreakMode numberOfLines:self.numberOfLines]; - CGPoint point = rect.origin; - point.y += roundf((rect.size.height - size.height) / 2.0f); - rect = (CGRect){point, CGSizeMake(rect.size.width, size.height)}; - [self.text drawInRect:rect withZFont:actualFont lineBreakMode:self.lineBreakMode alignment:self.textAlignment numberOfLines:self.numberOfLines]; - } - } else { - ZAttributedString *attStr = self.zAttributedText; - if (self.highlighted) { - // modify the string to change the base color - ZMutableAttributedString *mutStr = [[attStr mutableCopy] autorelease]; - NSRange activeRange = NSMakeRange(0, attStr.length); - while (activeRange.length > 0) { - NSRange effective; - UIColor *color = [attStr attribute:ZForegroundColorAttributeName atIndex:activeRange.location - longestEffectiveRange:&effective inRange:activeRange]; - if (color == nil) { - [mutStr addAttribute:ZForegroundColorAttributeName value:[UIColor whiteColor] range:effective]; - } - activeRange.location += effective.length, activeRange.length -= effective.length; - } - attStr = mutStr; - } - CGSize size = [attStr sizeConstrainedToSize:rect.size lineBreakMode:self.lineBreakMode numberOfLines:self.numberOfLines]; - CGPoint point = rect.origin; - point.y += roundf((rect.size.height - size.height) / 2.0f); - rect = (CGRect){point, CGSizeMake(rect.size.width, size.height)}; - [attStr drawInRect:rect withLineBreakMode:self.lineBreakMode alignment:self.textAlignment numberOfLines:self.numberOfLines]; - } -} - -- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines { - if (self.zFont == NULL && self.zAttributedText == nil) { - return [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines]; - } - - if (numberOfLines == 1) { - // if numberOfLines == 1 we need to use the version that converts spaces - CGSize size; - if (self.zAttributedText == nil) { - size = [self.text sizeWithZFont:self.zFont]; - } else { - size = [self.zAttributedText size]; - } - bounds.size.width = MIN(bounds.size.width, size.width); - bounds.size.height = MIN(bounds.size.height, size.height); - } else { - if (numberOfLines > 0) bounds.size.height = MIN(bounds.size.height, self.zFont.leading * numberOfLines); - if (self.zAttributedText == nil) { - bounds.size = [self.text sizeWithZFont:self.zFont constrainedToSize:bounds.size lineBreakMode:self.lineBreakMode]; - } else { - bounds.size = [self.zAttributedText sizeConstrainedToSize:bounds.size lineBreakMode:self.lineBreakMode]; - } - } - return bounds; -} - -- (void)dealloc { - [zFont release]; - [zAttributedText release]; - [super dealloc]; -} -@end diff --git a/cocos2d/FontLabel/FontLabelStringDrawing.h b/cocos2d/FontLabel/FontLabelStringDrawing.h deleted file mode 100644 index 821da22..0000000 --- a/cocos2d/FontLabel/FontLabelStringDrawing.h +++ /dev/null @@ -1,69 +0,0 @@ -// -// FontLabelStringDrawing.h -// FontLabel -// -// Created by Kevin Ballard on 5/5/09. -// Copyright © 2009 Zynga Game Networks -// -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import -#import "ZAttributedString.h" - -@class ZFont; - -@interface NSString (FontLabelStringDrawing) -// CGFontRef-based methods -- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED; -- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size __AVAILABILITY_INTERNAL_DEPRECATED; -- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size - lineBreakMode:(UILineBreakMode)lineBreakMode __AVAILABILITY_INTERNAL_DEPRECATED; -- (CGSize)drawAtPoint:(CGPoint)point withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED; -- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize __AVAILABILITY_INTERNAL_DEPRECATED; -- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize - lineBreakMode:(UILineBreakMode)lineBreakMode __AVAILABILITY_INTERNAL_DEPRECATED; -- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize - lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment __AVAILABILITY_INTERNAL_DEPRECATED; - -// ZFont-based methods -- (CGSize)sizeWithZFont:(ZFont *)font; -- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size; -- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode; -- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode - numberOfLines:(NSUInteger)numberOfLines; -- (CGSize)drawAtPoint:(CGPoint)point withZFont:(ZFont *)font; -- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode; -- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font; -- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode; -- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode - alignment:(UITextAlignment)alignment; -- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode - alignment:(UITextAlignment)alignment numberOfLines:(NSUInteger)numberOfLines; -@end - -@interface ZAttributedString (ZAttributedStringDrawing) -- (CGSize)size; -- (CGSize)sizeConstrainedToSize:(CGSize)size; -- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode; -- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode - numberOfLines:(NSUInteger)numberOfLines; -- (CGSize)drawAtPoint:(CGPoint)point; -- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode; -- (CGSize)drawInRect:(CGRect)rect; -- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode; -- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment; -- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment - numberOfLines:(NSUInteger)numberOfLines; -@end diff --git a/cocos2d/FontLabel/FontLabelStringDrawing.m b/cocos2d/FontLabel/FontLabelStringDrawing.m deleted file mode 100644 index 2907372..0000000 --- a/cocos2d/FontLabel/FontLabelStringDrawing.m +++ /dev/null @@ -1,892 +0,0 @@ -// -// FontLabelStringDrawing.m -// FontLabel -// -// Created by Kevin Ballard on 5/5/09. -// Copyright © 2009 Zynga Game Networks -// -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "FontLabelStringDrawing.h" -#import "ZFont.h" -#import "ZAttributedStringPrivate.h" - -@interface ZFont (ZFontPrivate) -@property (nonatomic, readonly) CGFloat ratio; -@end - -#define kUnicodeHighSurrogateStart 0xD800 -#define kUnicodeHighSurrogateEnd 0xDBFF -#define kUnicodeHighSurrogateMask kUnicodeHighSurrogateStart -#define kUnicodeLowSurrogateStart 0xDC00 -#define kUnicodeLowSurrogateEnd 0xDFFF -#define kUnicodeLowSurrogateMask kUnicodeLowSurrogateStart -#define kUnicodeSurrogateTypeMask 0xFC00 -#define UnicharIsHighSurrogate(c) ((c & kUnicodeSurrogateTypeMask) == kUnicodeHighSurrogateMask) -#define UnicharIsLowSurrogate(c) ((c & kUnicodeSurrogateTypeMask) == kUnicodeLowSurrogateMask) -#define ConvertSurrogatePairToUTF32(high, low) ((UInt32)((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000)) - -typedef enum { - kFontTableFormat4 = 4, - kFontTableFormat12 = 12, -} FontTableFormat; - -typedef struct fontTable { - NSUInteger retainCount; - CFDataRef cmapTable; - FontTableFormat format; - union { - struct { - UInt16 segCountX2; - UInt16 *endCodes; - UInt16 *startCodes; - UInt16 *idDeltas; - UInt16 *idRangeOffsets; - } format4; - struct { - UInt32 nGroups; - struct { - UInt32 startCharCode; - UInt32 endCharCode; - UInt32 startGlyphCode; - } *groups; - } format12; - } cmap; -} fontTable; - -static FontTableFormat supportedFormats[] = { kFontTableFormat4, kFontTableFormat12 }; -static size_t supportedFormatsCount = sizeof(supportedFormats) / sizeof(FontTableFormat); - -static fontTable *newFontTable(CFDataRef cmapTable, FontTableFormat format) { - fontTable *table = (struct fontTable *)malloc(sizeof(struct fontTable)); - table->retainCount = 1; - table->cmapTable = CFRetain(cmapTable); - table->format = format; - return table; -} - -static fontTable *retainFontTable(fontTable *table) { - if (table != NULL) { - table->retainCount++; - } - return table; -} - -static void releaseFontTable(fontTable *table) { - if (table != NULL) { - if (table->retainCount <= 1) { - CFRelease(table->cmapTable); - free(table); - } else { - table->retainCount--; - } - } -} - -static const void *fontTableRetainCallback(CFAllocatorRef allocator, const void *value) { - return retainFontTable((fontTable *)value); -} - -static void fontTableReleaseCallback(CFAllocatorRef allocator, const void *value) { - releaseFontTable((fontTable *)value); -} - -static const CFDictionaryValueCallBacks kFontTableDictionaryValueCallBacks = { - .version = 0, - .retain = &fontTableRetainCallback, - .release = &fontTableReleaseCallback, - .copyDescription = NULL, - .equal = NULL -}; - -// read the cmap table from the font -// we only know how to understand some of the table formats at the moment -static fontTable *readFontTableFromCGFont(CGFontRef font) { - CFDataRef cmapTable = CGFontCopyTableForTag(font, 'cmap'); - NSCAssert1(cmapTable != NULL, @"CGFontCopyTableForTag returned NULL for 'cmap' tag in font %@", - (font ? [(id)CFCopyDescription(font) autorelease] : @"(null)")); - const UInt8 * const bytes = CFDataGetBytePtr(cmapTable); - NSCAssert1(OSReadBigInt16(bytes, 0) == 0, @"cmap table for font %@ has bad version number", - (font ? [(id)CFCopyDescription(font) autorelease] : @"(null)")); - UInt16 numberOfSubtables = OSReadBigInt16(bytes, 2); - const UInt8 *unicodeSubtable = NULL; - //UInt16 unicodeSubtablePlatformID; - UInt16 unicodeSubtablePlatformSpecificID; - FontTableFormat unicodeSubtableFormat; - const UInt8 * const encodingSubtables = &bytes[4]; - for (UInt16 i = 0; i < numberOfSubtables; i++) { - const UInt8 * const encodingSubtable = &encodingSubtables[8 * i]; - UInt16 platformID = OSReadBigInt16(encodingSubtable, 0); - UInt16 platformSpecificID = OSReadBigInt16(encodingSubtable, 2); - // find the best subtable - // best is defined by a combination of encoding and format - // At the moment we only support format 4, so ignore all other format tables - // We prefer platformID == 0, but we will also accept Microsoft's unicode format - if (platformID == 0 || (platformID == 3 && platformSpecificID == 1)) { - BOOL preferred = NO; - if (unicodeSubtable == NULL) { - preferred = YES; - } else if (platformID == 0 && platformSpecificID > unicodeSubtablePlatformSpecificID) { - preferred = YES; - } - if (preferred) { - UInt32 offset = OSReadBigInt32(encodingSubtable, 4); - const UInt8 *subtable = &bytes[offset]; - UInt16 format = OSReadBigInt16(subtable, 0); - for (size_t i = 0; i < supportedFormatsCount; i++) { - if (format == supportedFormats[i]) { - if (format >= 8) { - // the version is a fixed-point - UInt16 formatFrac = OSReadBigInt16(subtable, 2); - if (formatFrac != 0) { - // all the current formats with a Fixed version are always *.0 - continue; - } - } - unicodeSubtable = subtable; - //unicodeSubtablePlatformID = platformID; - unicodeSubtablePlatformSpecificID = platformSpecificID; - unicodeSubtableFormat = format; - break; - } - } - } - } - } - fontTable *table = NULL; - if (unicodeSubtable != NULL) { - table = newFontTable(cmapTable, unicodeSubtableFormat); - switch (unicodeSubtableFormat) { - case kFontTableFormat4: - // subtable format 4 - //UInt16 length = OSReadBigInt16(unicodeSubtable, 2); - //UInt16 language = OSReadBigInt16(unicodeSubtable, 4); - table->cmap.format4.segCountX2 = OSReadBigInt16(unicodeSubtable, 6); - //UInt16 searchRange = OSReadBigInt16(unicodeSubtable, 8); - //UInt16 entrySelector = OSReadBigInt16(unicodeSubtable, 10); - //UInt16 rangeShift = OSReadBigInt16(unicodeSubtable, 12); - table->cmap.format4.endCodes = (UInt16*)&unicodeSubtable[14]; - table->cmap.format4.startCodes = (UInt16*)&((UInt8*)table->cmap.format4.endCodes)[table->cmap.format4.segCountX2+2]; - table->cmap.format4.idDeltas = (UInt16*)&((UInt8*)table->cmap.format4.startCodes)[table->cmap.format4.segCountX2]; - table->cmap.format4.idRangeOffsets = (UInt16*)&((UInt8*)table->cmap.format4.idDeltas)[table->cmap.format4.segCountX2]; - //UInt16 *glyphIndexArray = &idRangeOffsets[segCountX2]; - break; - case kFontTableFormat12: - table->cmap.format12.nGroups = OSReadBigInt32(unicodeSubtable, 12); - table->cmap.format12.groups = (void *)&unicodeSubtable[16]; - break; - default: - releaseFontTable(table); - table = NULL; - } - } - CFRelease(cmapTable); - return table; -} - -// outGlyphs must be at least size n -static void mapCharactersToGlyphsInFont(const fontTable *table, unichar characters[], size_t charLen, CGGlyph outGlyphs[], size_t *outGlyphLen) { - if (table != NULL) { - NSUInteger j = 0; - switch (table->format) { - case kFontTableFormat4: { - for (NSUInteger i = 0; i < charLen; i++, j++) { - unichar c = characters[i]; - UInt16 segOffset; - BOOL foundSegment = NO; - for (segOffset = 0; segOffset < table->cmap.format4.segCountX2; segOffset += 2) { - UInt16 endCode = OSReadBigInt16(table->cmap.format4.endCodes, segOffset); - if (endCode >= c) { - foundSegment = YES; - break; - } - } - if (!foundSegment) { - // no segment - // this is an invalid font - outGlyphs[j] = 0; - } else { - UInt16 startCode = OSReadBigInt16(table->cmap.format4.startCodes, segOffset); - if (!(startCode <= c)) { - // the code falls in a hole between segments - outGlyphs[j] = 0; - } else { - UInt16 idRangeOffset = OSReadBigInt16(table->cmap.format4.idRangeOffsets, segOffset); - if (idRangeOffset == 0) { - UInt16 idDelta = OSReadBigInt16(table->cmap.format4.idDeltas, segOffset); - outGlyphs[j] = (c + idDelta) % 65536; - } else { - // use the glyphIndexArray - UInt16 glyphOffset = idRangeOffset + 2 * (c - startCode); - outGlyphs[j] = OSReadBigInt16(&((UInt8*)table->cmap.format4.idRangeOffsets)[segOffset], glyphOffset); - } - } - } - } - break; - } - case kFontTableFormat12: { - UInt32 lastSegment = UINT32_MAX; - for (NSUInteger i = 0; i < charLen; i++, j++) { - unichar c = characters[i]; - UInt32 c32 = c; - if (UnicharIsHighSurrogate(c)) { - if (i+1 < charLen) { // do we have another character after this one? - unichar cc = characters[i+1]; - if (UnicharIsLowSurrogate(cc)) { - c32 = ConvertSurrogatePairToUTF32(c, cc); - i++; - } - } - } - // Start the heuristic search - // If this is an ASCII char, just do a linear search - // Otherwise do a hinted, modified binary search - // Start the first pivot at the last range found - // And when moving the pivot, limit the movement by increasing - // powers of two. This should help with locality - __typeof__(table->cmap.format12.groups[0]) *foundGroup = NULL; - if (c32 <= 0x7F) { - // ASCII - for (UInt32 idx = 0; idx < table->cmap.format12.nGroups; idx++) { - __typeof__(table->cmap.format12.groups[idx]) *group = &table->cmap.format12.groups[idx]; - if (c32 < OSSwapBigToHostInt32(group->startCharCode)) { - // we've fallen into a hole - break; - } else if (c32 <= OSSwapBigToHostInt32(group->endCharCode)) { - // this is the range - foundGroup = group; - break; - } - } - } else { - // heuristic search - UInt32 maxJump = (lastSegment == UINT32_MAX ? UINT32_MAX / 2 : 8); - UInt32 lowIdx = 0, highIdx = table->cmap.format12.nGroups; // highIdx is the first invalid idx - UInt32 pivot = (lastSegment == UINT32_MAX ? lowIdx + (highIdx - lowIdx) / 2 : lastSegment); - while (highIdx > lowIdx) { - __typeof__(table->cmap.format12.groups[pivot]) *group = &table->cmap.format12.groups[pivot]; - if (c32 < OSSwapBigToHostInt32(group->startCharCode)) { - highIdx = pivot; - } else if (c32 > OSSwapBigToHostInt32(group->endCharCode)) { - lowIdx = pivot + 1; - } else { - // we've hit the range - foundGroup = group; - break; - } - if (highIdx - lowIdx > maxJump * 2) { - if (highIdx == pivot) { - pivot -= maxJump; - } else { - pivot += maxJump; - } - maxJump *= 2; - } else { - pivot = lowIdx + (highIdx - lowIdx) / 2; - } - } - if (foundGroup != NULL) lastSegment = pivot; - } - if (foundGroup == NULL) { - outGlyphs[j] = 0; - } else { - outGlyphs[j] = (CGGlyph)(OSSwapBigToHostInt32(foundGroup->startGlyphCode) + - (c32 - OSSwapBigToHostInt32(foundGroup->startCharCode))); - } - } - break; - } - } - if (outGlyphLen != NULL) *outGlyphLen = j; - } else { - // we have no table, so just null out the glyphs - bzero(outGlyphs, charLen*sizeof(CGGlyph)); - if (outGlyphLen != NULL) *outGlyphLen = 0; - } -} - -static BOOL mapGlyphsToAdvancesInFont(ZFont *font, size_t n, CGGlyph glyphs[], CGFloat outAdvances[]) { - int advances[n]; - if (CGFontGetGlyphAdvances(font.cgFont, glyphs, n, advances)) { - CGFloat ratio = font.ratio; - - for (size_t i = 0; i < n; i++) { - outAdvances[i] = advances[i]*ratio; - } - return YES; - } else { - bzero(outAdvances, n*sizeof(CGFloat)); - } - return NO; -} - -static id getValueOrDefaultForRun(ZAttributeRun *run, NSString *key) { - id value = [run.attributes objectForKey:key]; - if (value == nil) { - static NSDictionary *defaultValues = nil; - if (defaultValues == nil) { - defaultValues = [[NSDictionary alloc] initWithObjectsAndKeys: - [ZFont fontWithUIFont:[UIFont systemFontOfSize:12]], ZFontAttributeName, - [UIColor blackColor], ZForegroundColorAttributeName, - [UIColor clearColor], ZBackgroundColorAttributeName, - [NSNumber numberWithInt:ZUnderlineStyleNone], ZUnderlineStyleAttributeName, - nil]; - } - value = [defaultValues objectForKey:key]; - } - return value; -} - -static void readRunInformation(NSArray *attributes, NSUInteger len, CFMutableDictionaryRef fontTableMap, - NSUInteger index, ZAttributeRun **currentRun, NSUInteger *nextRunStart, - ZFont **currentFont, fontTable **currentTable) { - *currentRun = [attributes objectAtIndex:index]; - *nextRunStart = ([attributes count] > index+1 ? [[attributes objectAtIndex:index+1] index] : len); - *currentFont = getValueOrDefaultForRun(*currentRun, ZFontAttributeName); - if (!CFDictionaryGetValueIfPresent(fontTableMap, (*currentFont).cgFont, (const void **)currentTable)) { - *currentTable = readFontTableFromCGFont((*currentFont).cgFont); - CFDictionarySetValue(fontTableMap, (*currentFont).cgFont, *currentTable); - releaseFontTable(*currentTable); - } -} - -static CGSize drawOrSizeTextConstrainedToSize(BOOL performDraw, NSString *string, NSArray *attributes, CGSize constrainedSize, NSUInteger maxLines, - UILineBreakMode lineBreakMode, UITextAlignment alignment, BOOL ignoreColor) { - NSUInteger len = [string length]; - NSUInteger idx = 0; - CGPoint drawPoint = CGPointZero; - CGSize retValue = CGSizeZero; - CGContextRef ctx = (performDraw ? UIGraphicsGetCurrentContext() : NULL); - - BOOL convertNewlines = (maxLines == 1); - - // Extract the characters from the string - // Convert newlines to spaces if necessary - unichar *characters = (unichar *)malloc(sizeof(unichar) * len); - if (convertNewlines) { - NSCharacterSet *charset = [NSCharacterSet newlineCharacterSet]; - NSRange range = NSMakeRange(0, len); - size_t cIdx = 0; - while (range.length > 0) { - NSRange newlineRange = [string rangeOfCharacterFromSet:charset options:0 range:range]; - if (newlineRange.location == NSNotFound) { - [string getCharacters:&characters[cIdx] range:range]; - cIdx += range.length; - break; - } else { - NSUInteger delta = newlineRange.location - range.location; - if (newlineRange.location > range.location) { - [string getCharacters:&characters[cIdx] range:NSMakeRange(range.location, delta)]; - } - cIdx += delta; - characters[cIdx] = (unichar)' '; - cIdx++; - delta += newlineRange.length; - range.location += delta, range.length -= delta; - if (newlineRange.length == 1 && range.length >= 1 && - [string characterAtIndex:newlineRange.location] == (unichar)'\r' && - [string characterAtIndex:range.location] == (unichar)'\n') { - // CRLF sequence, skip the LF - range.location += 1, range.length -= 1; - } - } - } - len = cIdx; - } else { - [string getCharacters:characters range:NSMakeRange(0, len)]; - } - - // Create storage for glyphs and advances - CGGlyph *glyphs; - CGFloat *advances; - { - NSUInteger maxRunLength = 0; - ZAttributeRun *a = [attributes objectAtIndex:0]; - for (NSUInteger i = 1; i < [attributes count]; i++) { - ZAttributeRun *b = [attributes objectAtIndex:i]; - maxRunLength = MAX(maxRunLength, b.index - a.index); - a = b; - } - maxRunLength = MAX(maxRunLength, len - a.index); - maxRunLength++; // for a potential ellipsis - glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * maxRunLength); - advances = (CGFloat *)malloc(sizeof(CGFloat) * maxRunLength); - } - - // Use this table to cache all fontTable objects - CFMutableDictionaryRef fontTableMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, - &kFontTableDictionaryValueCallBacks); - - // Fetch initial style values - NSUInteger currentRunIdx = 0; - ZAttributeRun *currentRun; - NSUInteger nextRunStart; - ZFont *currentFont; - fontTable *currentTable; - -#define READ_RUN() readRunInformation(attributes, len, fontTableMap, \ - currentRunIdx, ¤tRun, &nextRunStart, \ - ¤tFont, ¤tTable) - - READ_RUN(); - - // fetch the glyphs for the first run - size_t glyphCount; - NSUInteger glyphIdx; - -#define READ_GLYPHS() do { \ - mapCharactersToGlyphsInFont(currentTable, &characters[currentRun.index], (nextRunStart - currentRun.index), glyphs, &glyphCount); \ - mapGlyphsToAdvancesInFont(currentFont, (nextRunStart - currentRun.index), glyphs, advances); \ - glyphIdx = 0; \ - } while (0) - - READ_GLYPHS(); - - NSMutableCharacterSet *alphaCharset = [NSMutableCharacterSet alphanumericCharacterSet]; - [alphaCharset addCharactersInString:@"([{'\"\u2019\u02BC"]; - - // scan left-to-right looking for newlines or until we hit the width constraint - // When we hit a wrapping point, calculate truncation as follows: - // If we have room to draw at least one more character on the next line, no truncation - // Otherwise apply the truncation algorithm to the current line. - // After calculating any truncation, draw. - // Each time we hit the end of an attribute run, calculate the new font and make sure - // it fits (vertically) within the size constraint. If not, truncate this line. - // When we draw, iterate over the attribute runs for this line and draw each run separately - BOOL lastLine = NO; // used to indicate truncation and to stop the iterating - NSUInteger lineCount = 1; - while (idx < len && !lastLine) { - if (maxLines > 0 && lineCount == maxLines) { - lastLine = YES; - } - // scan left-to-right - struct { - NSUInteger index; - NSUInteger glyphIndex; - NSUInteger currentRunIdx; - } indexCache = { idx, glyphIdx, currentRunIdx }; - CGSize lineSize = CGSizeMake(0, currentFont.leading); - CGFloat lineAscender = currentFont.ascender; - struct { - NSUInteger index; - NSUInteger glyphIndex; - NSUInteger currentRunIdx; - CGSize lineSize; - } lastWrapCache = {0, 0, 0, CGSizeZero}; - BOOL inAlpha = NO; // used for calculating wrap points - - BOOL finishLine = NO; - for (;idx <= len && !finishLine;) { - NSUInteger skipCount = 0; - if (idx == len) { - finishLine = YES; - lastLine = YES; - } else { - if (idx >= nextRunStart) { - // cycle the font and table and grab the next set of glyphs - do { - currentRunIdx++; - READ_RUN(); - } while (idx >= nextRunStart); - READ_GLYPHS(); - // re-scan the characters to synchronize the glyph index - for (NSUInteger j = currentRun.index; j < idx; j++) { - if (UnicharIsHighSurrogate(characters[j]) && j+1 lineSize.height) { - lineSize.height = currentFont.leading; - if (retValue.height + currentFont.ascender > constrainedSize.height) { - lastLine = YES; - finishLine = YES; - } - } - lineAscender = MAX(lineAscender, currentFont.ascender); - } - unichar c = characters[idx]; - // Mark a wrap point before spaces and after any stretch of non-alpha characters - BOOL markWrap = NO; - if (c == (unichar)' ') { - markWrap = YES; - } else if ([alphaCharset characterIsMember:c]) { - if (!inAlpha) { - markWrap = YES; - inAlpha = YES; - } - } else { - inAlpha = NO; - } - if (markWrap) { - lastWrapCache = (__typeof__(lastWrapCache)){ - .index = idx, - .glyphIndex = glyphIdx, - .currentRunIdx = currentRunIdx, - .lineSize = lineSize - }; - } - // process the line - if (c == (unichar)'\n' || c == 0x0085) { // U+0085 is the NEXT_LINE unicode character - finishLine = YES; - skipCount = 1; - } else if (c == (unichar)'\r') { - finishLine = YES; - // check for CRLF - if (idx+1 < len && characters[idx+1] == (unichar)'\n') { - skipCount = 2; - } else { - skipCount = 1; - } - } else if (lineSize.width + advances[glyphIdx] > constrainedSize.width) { - finishLine = YES; - if (retValue.height + lineSize.height + currentFont.ascender > constrainedSize.height) { - lastLine = YES; - } - // walk backwards if wrapping is necessary - if (lastWrapCache.index > indexCache.index && lineBreakMode != UILineBreakModeCharacterWrap && - (!lastLine || lineBreakMode != UILineBreakModeClip)) { - // we're doing some sort of word wrapping - idx = lastWrapCache.index; - lineSize = lastWrapCache.lineSize; - if (!lastLine) { - // re-check if this is the last line - if (lastWrapCache.currentRunIdx != currentRunIdx) { - currentRunIdx = lastWrapCache.currentRunIdx; - READ_RUN(); - READ_GLYPHS(); - } - if (retValue.height + lineSize.height + currentFont.ascender > constrainedSize.height) { - lastLine = YES; - } - } - glyphIdx = lastWrapCache.glyphIndex; - // skip any spaces - for (NSUInteger j = idx; j < len && characters[j] == (unichar)' '; j++) { - skipCount++; - } - } - } - } - if (finishLine) { - // TODO: support head/middle truncation - if (lastLine && idx < len && lineBreakMode == UILineBreakModeTailTruncation) { - // truncate - unichar ellipsis = 0x2026; // ellipsis (…) - CGGlyph ellipsisGlyph; - mapCharactersToGlyphsInFont(currentTable, &ellipsis, 1, &ellipsisGlyph, NULL); - CGFloat ellipsisWidth; - mapGlyphsToAdvancesInFont(currentFont, 1, &ellipsisGlyph, &ellipsisWidth); - while ((idx - indexCache.index) > 1 && lineSize.width + ellipsisWidth > constrainedSize.width) { - // we have more than 1 character and we're too wide, so back up - idx--; - if (UnicharIsHighSurrogate(characters[idx]) && UnicharIsLowSurrogate(characters[idx+1])) { - idx--; - } - if (idx < currentRun.index) { - ZFont *oldFont = currentFont; - do { - currentRunIdx--; - READ_RUN(); - } while (idx < currentRun.index); - READ_GLYPHS(); - glyphIdx = glyphCount-1; - if (oldFont != currentFont) { - mapCharactersToGlyphsInFont(currentTable, &ellipsis, 1, &ellipsisGlyph, NULL); - mapGlyphsToAdvancesInFont(currentFont, 1, &ellipsisGlyph, &ellipsisWidth); - } - } else { - glyphIdx--; - } - lineSize.width -= advances[glyphIdx]; - } - // skip any spaces before truncating - while ((idx - indexCache.index) > 1 && characters[idx-1] == (unichar)' ') { - idx--; - if (idx < currentRun.index) { - currentRunIdx--; - READ_RUN(); - READ_GLYPHS(); - glyphIdx = glyphCount-1; - } else { - glyphIdx--; - } - lineSize.width -= advances[glyphIdx]; - } - lineSize.width += ellipsisWidth; - glyphs[glyphIdx] = ellipsisGlyph; - idx++; - glyphIdx++; - } - retValue.width = MAX(retValue.width, lineSize.width); - retValue.height += lineSize.height; - - // draw - if (performDraw) { - switch (alignment) { - case UITextAlignmentLeft: - drawPoint.x = 0; - break; - case UITextAlignmentCenter: - drawPoint.x = (constrainedSize.width - lineSize.width) / 2.0f; - break; - case UITextAlignmentRight: - drawPoint.x = constrainedSize.width - lineSize.width; - break; - } - NSUInteger stopGlyphIdx = glyphIdx; - NSUInteger lastRunIdx = currentRunIdx; - NSUInteger stopCharIdx = idx; - idx = indexCache.index; - if (currentRunIdx != indexCache.currentRunIdx) { - currentRunIdx = indexCache.currentRunIdx; - READ_RUN(); - READ_GLYPHS(); - } - glyphIdx = indexCache.glyphIndex; - for (NSUInteger drawIdx = currentRunIdx; drawIdx <= lastRunIdx; drawIdx++) { - if (drawIdx != currentRunIdx) { - currentRunIdx = drawIdx; - READ_RUN(); - READ_GLYPHS(); - } - NSUInteger numGlyphs; - if (drawIdx == lastRunIdx) { - numGlyphs = stopGlyphIdx - glyphIdx; - idx = stopCharIdx; - } else { - numGlyphs = glyphCount - glyphIdx; - idx = nextRunStart; - } - CGContextSetFont(ctx, currentFont.cgFont); - CGContextSetFontSize(ctx, currentFont.pointSize); - // calculate the fragment size - CGFloat fragmentWidth = 0; - for (NSUInteger g = 0; g < numGlyphs; g++) { - fragmentWidth += advances[glyphIdx + g]; - } - - if (!ignoreColor) { - UIColor *foregroundColor = getValueOrDefaultForRun(currentRun, ZForegroundColorAttributeName); - UIColor *backgroundColor = getValueOrDefaultForRun(currentRun, ZBackgroundColorAttributeName); - if (backgroundColor != nil && ![backgroundColor isEqual:[UIColor clearColor]]) { - [backgroundColor setFill]; - UIRectFillUsingBlendMode((CGRect){ drawPoint, { fragmentWidth, lineSize.height } }, kCGBlendModeNormal); - } - [foregroundColor setFill]; - } - - CGContextShowGlyphsAtPoint(ctx, drawPoint.x, drawPoint.y + lineAscender, &glyphs[glyphIdx], numGlyphs); - NSNumber *underlineStyle = getValueOrDefaultForRun(currentRun, ZUnderlineStyleAttributeName); - if ([underlineStyle integerValue] & ZUnderlineStyleMask) { - // we only support single for the time being - UIRectFill(CGRectMake(drawPoint.x, drawPoint.y + lineAscender, fragmentWidth, 1)); - } - drawPoint.x += fragmentWidth; - glyphIdx += numGlyphs; - } - drawPoint.y += lineSize.height; - } - idx += skipCount; - glyphIdx += skipCount; - lineCount++; - } else { - lineSize.width += advances[glyphIdx]; - glyphIdx++; - idx++; - if (idx < len && UnicharIsHighSurrogate(characters[idx-1]) && UnicharIsLowSurrogate(characters[idx])) { - // skip the second half of the surrogate pair - idx++; - } - } - } - } - CFRelease(fontTableMap); - free(glyphs); - free(advances); - free(characters); - -#undef READ_GLYPHS -#undef READ_RUN - - return retValue; -} - -static NSArray *attributeRunForFont(ZFont *font) { - return [NSArray arrayWithObject:[ZAttributeRun attributeRunWithIndex:0 - attributes:[NSDictionary dictionaryWithObject:font - forKey:ZFontAttributeName]]]; -} - -static CGSize drawTextInRect(CGRect rect, NSString *text, NSArray *attributes, UILineBreakMode lineBreakMode, - UITextAlignment alignment, NSUInteger numberOfLines, BOOL ignoreColor) { - CGContextRef ctx = UIGraphicsGetCurrentContext(); - - CGContextSaveGState(ctx); - - // flip it upside-down because our 0,0 is upper-left, whereas ttfs are for screens where 0,0 is lower-left - CGAffineTransform textTransform = CGAffineTransformMake(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); - CGContextSetTextMatrix(ctx, textTransform); - - CGContextTranslateCTM(ctx, rect.origin.x, rect.origin.y); - - CGContextSetTextDrawingMode(ctx, kCGTextFill); - CGSize size = drawOrSizeTextConstrainedToSize(YES, text, attributes, rect.size, numberOfLines, lineBreakMode, alignment, ignoreColor); - - CGContextRestoreGState(ctx); - - return size; -} - -@implementation NSString (FontLabelStringDrawing) -// CGFontRef-based methods -- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize { - return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize]]; -} - -- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size { - return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize] constrainedToSize:size]; -} - -- (CGSize)sizeWithCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize constrainedToSize:(CGSize)size - lineBreakMode:(UILineBreakMode)lineBreakMode { - return [self sizeWithZFont:[ZFont fontWithCGFont:font size:pointSize] constrainedToSize:size lineBreakMode:lineBreakMode]; -} - -- (CGSize)drawAtPoint:(CGPoint)point withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize { - return [self drawAtPoint:point withZFont:[ZFont fontWithCGFont:font size:pointSize]]; -} - -- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize { - return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize]]; -} - -- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize lineBreakMode:(UILineBreakMode)lineBreakMode { - return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize] lineBreakMode:lineBreakMode]; -} - -- (CGSize)drawInRect:(CGRect)rect withCGFont:(CGFontRef)font pointSize:(CGFloat)pointSize - lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment { - return [self drawInRect:rect withZFont:[ZFont fontWithCGFont:font size:pointSize] lineBreakMode:lineBreakMode alignment:alignment]; -} - -// ZFont-based methods -- (CGSize)sizeWithZFont:(ZFont *)font { - CGSize size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), 1, - UILineBreakModeClip, UITextAlignmentLeft, YES); - return CGSizeMake(ceilf(size.width), ceilf(size.height)); -} - -- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size { - return [self sizeWithZFont:font constrainedToSize:size lineBreakMode:UILineBreakModeWordWrap]; -} - -/* - According to experimentation with UIStringDrawing, this can actually return a CGSize whose height is greater - than the one passed in. The two cases are as follows: - 1. If the given size parameter's height is smaller than a single line, the returned value will - be the height of one line. - 2. If the given size parameter's height falls between multiples of a line height, and the wrapped string - actually extends past the size.height, and the difference between size.height and the previous multiple - of a line height is >= the font's ascender, then the returned size's height is extended to the next line. - To put it simply, if the baseline point of a given line falls in the given size, the entire line will - be present in the output size. - */ -- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode { - size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), size, 0, lineBreakMode, UITextAlignmentLeft, YES); - return CGSizeMake(ceilf(size.width), ceilf(size.height)); -} - -- (CGSize)sizeWithZFont:(ZFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode - numberOfLines:(NSUInteger)numberOfLines { - size = drawOrSizeTextConstrainedToSize(NO, self, attributeRunForFont(font), size, numberOfLines, lineBreakMode, UITextAlignmentLeft, YES); - return CGSizeMake(ceilf(size.width), ceilf(size.height)); -} - -- (CGSize)drawAtPoint:(CGPoint)point withZFont:(ZFont *)font { - return [self drawAtPoint:point forWidth:CGFLOAT_MAX withZFont:font lineBreakMode:UILineBreakModeClip]; -} - -- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode { - return drawTextInRect((CGRect){ point, { width, CGFLOAT_MAX } }, self, attributeRunForFont(font), lineBreakMode, UITextAlignmentLeft, 1, YES); -} - -- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font { - return [self drawInRect:rect withZFont:font lineBreakMode:UILineBreakModeWordWrap]; -} - -- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode { - return [self drawInRect:rect withZFont:font lineBreakMode:lineBreakMode alignment:UITextAlignmentLeft]; -} - -- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode - alignment:(UITextAlignment)alignment { - return drawTextInRect(rect, self, attributeRunForFont(font), lineBreakMode, alignment, 0, YES); -} - -- (CGSize)drawInRect:(CGRect)rect withZFont:(ZFont *)font lineBreakMode:(UILineBreakMode)lineBreakMode - alignment:(UITextAlignment)alignment numberOfLines:(NSUInteger)numberOfLines { - return drawTextInRect(rect, self, attributeRunForFont(font), lineBreakMode, alignment, numberOfLines, YES); -} -@end - -@implementation ZAttributedString (ZAttributedStringDrawing) -- (CGSize)size { - CGSize size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX), 1, - UILineBreakModeClip, UITextAlignmentLeft, NO); - return CGSizeMake(ceilf(size.width), ceilf(size.height)); -} - -- (CGSize)sizeConstrainedToSize:(CGSize)size { - return [self sizeConstrainedToSize:size lineBreakMode:UILineBreakModeWordWrap]; -} - -- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode { - size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, size, 0, lineBreakMode, UITextAlignmentLeft, NO); - return CGSizeMake(ceilf(size.width), ceilf(size.height)); -} - -- (CGSize)sizeConstrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode - numberOfLines:(NSUInteger)numberOfLines { - size = drawOrSizeTextConstrainedToSize(NO, self.string, self.attributes, size, numberOfLines, lineBreakMode, UITextAlignmentLeft, NO); - return CGSizeMake(ceilf(size.width), ceilf(size.height)); -} - -- (CGSize)drawAtPoint:(CGPoint)point { - return [self drawAtPoint:point forWidth:CGFLOAT_MAX lineBreakMode:UILineBreakModeClip]; -} - -- (CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode { - return drawTextInRect((CGRect){ point, { width, CGFLOAT_MAX } }, self.string, self.attributes, lineBreakMode, UITextAlignmentLeft, 1, NO); -} - -- (CGSize)drawInRect:(CGRect)rect { - return [self drawInRect:rect withLineBreakMode:UILineBreakModeWordWrap]; -} - -- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode { - return [self drawInRect:rect withLineBreakMode:lineBreakMode alignment:UITextAlignmentLeft]; -} - -- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment { - return drawTextInRect(rect, self.string, self.attributes, lineBreakMode, alignment, 0, NO); -} - -- (CGSize)drawInRect:(CGRect)rect withLineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment - numberOfLines:(NSUInteger)numberOfLines { - return drawTextInRect(rect, self.string, self.attributes, lineBreakMode, alignment, numberOfLines, NO); -} -@end diff --git a/cocos2d/FontLabel/FontManager.h b/cocos2d/FontLabel/FontManager.h deleted file mode 100644 index 1592b8a..0000000 --- a/cocos2d/FontLabel/FontManager.h +++ /dev/null @@ -1,85 +0,0 @@ -// -// FontManager.h -// FontLabel -// -// Created by Kevin Ballard on 5/5/09. -// Copyright © 2009 Zynga Game Networks -// -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import -#import - -@class ZFont; - -@interface FontManager : NSObject { - CFMutableDictionaryRef fonts; - NSMutableDictionary *urls; -} -+ (FontManager *)sharedManager; -/*! - @method - @abstract Loads a TTF font from the main bundle - @param filename The name of the font file to load (with or without extension). - @return YES if the font was loaded, NO if an error occurred - @discussion If the font has already been loaded, this method does nothing and returns YES. - This method first attempts to load the font by appending .ttf to the filename. - If that file does not exist, it tries the filename exactly as given. -*/ -- (BOOL)loadFont:(NSString *)filename; -/*! - @method - @abstract Loads a font from the given file URL - @param url A file URL that points to a font file - @return YES if the font was loaded, NO if an error occurred - @discussion If the font has already been loaded, this method does nothing and returns YES. -*/ -- (BOOL)loadFontURL:(NSURL *)url; -/*! - @method - @abstract Returns the loaded font with the given filename - @param filename The name of the font file that was given to -loadFont: - @return A CGFontRef, or NULL if the specified font cannot be found - @discussion If the font has not been loaded yet, -loadFont: will be - called with the given name first. -*/ -- (CGFontRef)fontWithName:(NSString *)filename __AVAILABILITY_INTERNAL_DEPRECATED; -/*! - @method - @abstract Returns a ZFont object corresponding to the loaded font with the given filename and point size - @param filename The name of the font file that was given to -loadFont: - @param pointSize The point size of the font - @return A ZFont, or NULL if the specified font cannot be found - @discussion If the font has not been loaded yet, -loadFont: will be - called with the given name first. -*/ -- (ZFont *)zFontWithName:(NSString *)filename pointSize:(CGFloat)pointSize; -/*! - @method - @abstract Returns a ZFont object corresponding to the loaded font with the given file URL and point size - @param url A file URL that points to a font file - @param pointSize The point size of the font - @return A ZFont, or NULL if the specified font cannot be loaded - @discussion If the font has not been loaded yet, -loadFontURL: will be called with the given URL first. -*/ -- (ZFont *)zFontWithURL:(NSURL *)url pointSize:(CGFloat)pointSize; -/*! - @method - @abstract Returns a CFArrayRef of all loaded CGFont objects - @return A CFArrayRef of all loaded CGFont objects - @description You are responsible for releasing the CFArrayRef -*/ -- (CFArrayRef)copyAllFonts; -@end diff --git a/cocos2d/FontLabel/FontManager.m b/cocos2d/FontLabel/FontManager.m deleted file mode 100644 index 12eac2d..0000000 --- a/cocos2d/FontLabel/FontManager.m +++ /dev/null @@ -1,123 +0,0 @@ -// -// FontManager.m -// FontLabel -// -// Created by Kevin Ballard on 5/5/09. -// Copyright © 2009 Zynga Game Networks -// -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "FontManager.h" -#import "ZFont.h" - -static FontManager *sharedFontManager = nil; - -@implementation FontManager -+ (FontManager *)sharedManager { - @synchronized(self) { - if (sharedFontManager == nil) { - sharedFontManager = [[self alloc] init]; - } - } - return sharedFontManager; -} - -- (id)init { - if ((self = [super init])) { - fonts = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - urls = [[NSMutableDictionary alloc] init]; - } - return self; -} - -- (BOOL)loadFont:(NSString *)filename { - NSString *fontPath = [[NSBundle mainBundle] pathForResource:filename ofType:@"ttf"]; - if (fontPath == nil) { - fontPath = [[NSBundle mainBundle] pathForResource:filename ofType:nil]; - } - if (fontPath == nil) return NO; - - NSURL *url = [NSURL fileURLWithPath:fontPath]; - if ([self loadFontURL:url]) { - [urls setObject:url forKey:filename]; - return YES; - } - return NO; -} - -- (BOOL)loadFontURL:(NSURL *)url { - CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((CFURLRef)url); - if (fontDataProvider == NULL) return NO; - CGFontRef newFont = CGFontCreateWithDataProvider(fontDataProvider); - CGDataProviderRelease(fontDataProvider); - if (newFont == NULL) return NO; - - CFDictionarySetValue(fonts, url, newFont); - CGFontRelease(newFont); - return YES; -} - -- (CGFontRef)fontWithName:(NSString *)filename { - CGFontRef font = NULL; - NSURL *url = [urls objectForKey:filename]; - if (url == nil && [self loadFont:filename]) { - url = [urls objectForKey:filename]; - } - if (url != nil) { - font = (CGFontRef)CFDictionaryGetValue(fonts, url); - } - return font; -} - -- (ZFont *)zFontWithName:(NSString *)filename pointSize:(CGFloat)pointSize { - NSURL *url = [urls objectForKey:filename]; - if (url == nil && [self loadFont:filename]) { - url = [urls objectForKey:filename]; - } - if (url != nil) { - CGFontRef cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url); - if (cgFont != NULL) { - return [ZFont fontWithCGFont:cgFont size:pointSize]; - } - } - return nil; -} - -- (ZFont *)zFontWithURL:(NSURL *)url pointSize:(CGFloat)pointSize { - CGFontRef cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url); - if (cgFont == NULL && [self loadFontURL:url]) { - cgFont = (CGFontRef)CFDictionaryGetValue(fonts, url); - } - if (cgFont != NULL) { - return [ZFont fontWithCGFont:cgFont size:pointSize]; - } - return nil; -} - -- (CFArrayRef)copyAllFonts { - CFIndex count = CFDictionaryGetCount(fonts); - CGFontRef *values = (CGFontRef *)malloc(sizeof(CGFontRef) * count); - CFDictionaryGetKeysAndValues(fonts, NULL, (const void **)values); - CFArrayRef array = CFArrayCreate(NULL, (const void **)values, count, &kCFTypeArrayCallBacks); - free(values); - return array; -} - -- (void)dealloc { - CFRelease(fonts); - [urls release]; - [super dealloc]; -} -@end diff --git a/cocos2d/FontLabel/ZAttributedString.h b/cocos2d/FontLabel/ZAttributedString.h deleted file mode 100644 index e194c81..0000000 --- a/cocos2d/FontLabel/ZAttributedString.h +++ /dev/null @@ -1,77 +0,0 @@ -// -// ZAttributedString.h -// FontLabel -// -// Created by Kevin Ballard on 9/22/09. -// Copyright 2009 Zynga Game Networks. All rights reserved. -// - -#import - -#if NS_BLOCKS_AVAILABLE -#define Z_BLOCKS 1 -#else -// set this to 1 if you are using PLBlocks -#define Z_BLOCKS 0 -#endif - -#if Z_BLOCKS -enum { - ZAttributedStringEnumerationReverse = (1UL << 1), - ZAttributedStringEnumerationLongestEffectiveRangeNotRequired = (1UL << 20) -}; -typedef NSUInteger ZAttributedStringEnumerationOptions; -#endif - -@interface ZAttributedString : NSObject { - NSMutableString *_buffer; - NSMutableArray *_attributes; -} -@property (nonatomic, readonly) NSUInteger length; -@property (nonatomic, readonly) NSString *string; -- (id)initWithAttributedString:(ZAttributedString *)attr; -- (id)initWithString:(NSString *)str; -- (id)initWithString:(NSString *)str attributes:(NSDictionary *)attributes; -- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange; -- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit; -- (ZAttributedString *)attributedSubstringFromRange:(NSRange)aRange; -- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange; -- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit; -#if Z_BLOCKS -- (void)enumerateAttribute:(NSString *)attrName inRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts - usingBlock:(void (^)(id value, NSRange range, BOOL *stop))block; -- (void)enumerateAttributesInRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts - usingBlock:(void (^)(NSDictionary *attrs, NSRange range, BOOL *stop))block; -#endif -- (BOOL)isEqualToAttributedString:(ZAttributedString *)otherString; -@end - -@interface ZMutableAttributedString : ZAttributedString { -} -- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range; -- (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range; -- (void)appendAttributedString:(ZAttributedString *)str; -- (void)deleteCharactersInRange:(NSRange)range; -- (void)insertAttributedString:(ZAttributedString *)str atIndex:(NSUInteger)idx; -- (void)removeAttribute:(NSString *)name range:(NSRange)range; -- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(ZAttributedString *)str; -- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str; -- (void)setAttributedString:(ZAttributedString *)str; -- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range; -@end - -extern NSString * const ZFontAttributeName; -extern NSString * const ZForegroundColorAttributeName; -extern NSString * const ZBackgroundColorAttributeName; -extern NSString * const ZUnderlineStyleAttributeName; - -enum { - ZUnderlineStyleNone = 0x00, - ZUnderlineStyleSingle = 0x01 -}; -#define ZUnderlineStyleMask 0x00FF - -enum { - ZUnderlinePatternSolid = 0x0000 -}; -#define ZUnderlinePatternMask 0xFF00 diff --git a/cocos2d/FontLabel/ZAttributedString.m b/cocos2d/FontLabel/ZAttributedString.m deleted file mode 100644 index a4163bc..0000000 --- a/cocos2d/FontLabel/ZAttributedString.m +++ /dev/null @@ -1,597 +0,0 @@ -// -// ZAttributedString.m -// FontLabel -// -// Created by Kevin Ballard on 9/22/09. -// Copyright 2009 Zynga Game Networks. All rights reserved. -// - -#import "ZAttributedString.h" -#import "ZAttributedStringPrivate.h" - -@interface ZAttributedString () -- (NSUInteger)indexOfEffectiveAttributeRunForIndex:(NSUInteger)index; -- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange uniquingOnName:(NSString *)attributeName; -- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange - inRange:(NSRange)rangeLimit uniquingOnName:(NSString *)attributeName; -@end - -@interface ZAttributedString () -@property (nonatomic, readonly) NSArray *attributes; -@end - -@implementation ZAttributedString -@synthesize string = _buffer; -@synthesize attributes = _attributes; - -- (id)initWithAttributedString:(ZAttributedString *)attr { - NSParameterAssert(attr != nil); - if ((self = [super init])) { - _buffer = [attr->_buffer mutableCopy]; - _attributes = [[NSMutableArray alloc] initWithArray:attr->_attributes copyItems:YES]; - } - return self; -} - -- (id)initWithString:(NSString *)str { - return [self initWithString:str attributes:nil]; -} - -- (id)initWithString:(NSString *)str attributes:(NSDictionary *)attributes { - if ((self = [super init])) { - _buffer = [str mutableCopy]; - _attributes = [[NSMutableArray alloc] initWithObjects:[ZAttributeRun attributeRunWithIndex:0 attributes:attributes], nil]; - } - return self; -} - -- (id)init { - return [self initWithString:@"" attributes:nil]; -} - -- (id)initWithCoder:(NSCoder *)decoder { - if ((self = [super init])) { - _buffer = [[decoder decodeObjectForKey:@"buffer"] mutableCopy]; - _attributes = [[decoder decodeObjectForKey:@"attributes"] mutableCopy]; - } - return self; -} - -- (void)encodeWithCoder:(NSCoder *)aCoder { - [aCoder encodeObject:_buffer forKey:@"buffer"]; - [aCoder encodeObject:_attributes forKey:@"attributes"]; -} - -- (id)copyWithZone:(NSZone *)zone { - return [self retain]; -} - -- (id)mutableCopyWithZone:(NSZone *)zone { - return [(ZMutableAttributedString *)[ZMutableAttributedString allocWithZone:zone] initWithAttributedString:self]; -} - -- (NSUInteger)length { - return [_buffer length]; -} - -- (NSString *)description { - NSMutableArray *components = [NSMutableArray arrayWithCapacity:[_attributes count]*2]; - NSRange range = NSMakeRange(0, 0); - for (NSUInteger i = 0; i <= [_attributes count]; i++) { - range.location = NSMaxRange(range); - ZAttributeRun *run; - if (i < [_attributes count]) { - run = [_attributes objectAtIndex:i]; - range.length = run.index - range.location; - } else { - run = nil; - range.length = [_buffer length] - range.location; - } - if (range.length > 0) { - [components addObject:[NSString stringWithFormat:@"\"%@\"", [_buffer substringWithRange:range]]]; - } - if (run != nil) { - NSMutableArray *attrDesc = [NSMutableArray arrayWithCapacity:[run.attributes count]]; - for (id key in run.attributes) { - [attrDesc addObject:[NSString stringWithFormat:@"%@: %@", key, [run.attributes objectForKey:key]]]; - } - [components addObject:[NSString stringWithFormat:@"{%@}", [attrDesc componentsJoinedByString:@", "]]]; - } - } - return [NSString stringWithFormat:@"%@", [components componentsJoinedByString:@" "]]; -} - -- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange { - NSParameterAssert(attributeName != nil); - return [[self attributesAtIndex:index effectiveRange:aRange uniquingOnName:attributeName] objectForKey:attributeName]; -} - -- (id)attribute:(NSString *)attributeName atIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit { - NSParameterAssert(attributeName != nil); - return [[self attributesAtIndex:index longestEffectiveRange:aRange inRange:rangeLimit uniquingOnName:attributeName] objectForKey:attributeName]; -} - -- (ZAttributedString *)attributedSubstringFromRange:(NSRange)aRange { - if (NSMaxRange(aRange) > [_buffer length]) { - @throw [NSException exceptionWithName:NSRangeException reason:@"range was outisde of the attributed string" userInfo:nil]; - } - ZMutableAttributedString *newStr = [self mutableCopy]; - if (aRange.location > 0) { - [newStr deleteCharactersInRange:NSMakeRange(0, aRange.location)]; - } - if (NSMaxRange(aRange) < [_buffer length]) { - [newStr deleteCharactersInRange:NSMakeRange(aRange.length, [_buffer length] - NSMaxRange(aRange))]; - } - return [newStr autorelease]; -} - -- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange { - return [NSDictionary dictionaryWithDictionary:[self attributesAtIndex:index effectiveRange:aRange uniquingOnName:nil]]; -} - -- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange inRange:(NSRange)rangeLimit { - return [NSDictionary dictionaryWithDictionary:[self attributesAtIndex:index longestEffectiveRange:aRange inRange:rangeLimit uniquingOnName:nil]]; -} - -#if Z_BLOCKS -// Warning: this code has not been tested. The only guarantee is that it compiles. -- (void)enumerateAttribute:(NSString *)attrName inRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts - usingBlock:(void (^)(id, NSRange, BOOL*))block { - if (opts & ZAttributedStringEnumerationLongestEffectiveRangeNotRequired) { - [self enumerateAttributesInRange:enumerationRange options:opts usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { - id value = [attrs objectForKey:attrName]; - if (value != nil) { - block(value, range, stop); - } - }]; - } else { - __block id oldValue = nil; - __block NSRange effectiveRange = NSMakeRange(0, 0); - [self enumerateAttributesInRange:enumerationRange options:opts usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { - id value = [attrs objectForKey:attrName]; - if (oldValue == nil) { - oldValue = value; - effectiveRange = range; - } else if (value != nil && [oldValue isEqual:value]) { - // combine the attributes - effectiveRange = NSUnionRange(effectiveRange, range); - } else { - BOOL innerStop = NO; - block(oldValue, effectiveRange, &innerStop); - if (innerStop) { - *stop = YES; - oldValue = nil; - } else { - oldValue = value; - } - } - }]; - if (oldValue != nil) { - BOOL innerStop = NO; // necessary for the block, but unused - block(oldValue, effectiveRange, &innerStop); - } - } -} - -- (void)enumerateAttributesInRange:(NSRange)enumerationRange options:(ZAttributedStringEnumerationOptions)opts - usingBlock:(void (^)(NSDictionary*, NSRange, BOOL*))block { - // copy the attributes so we can mutate the string if necessary during enumeration - // also clip the array during copy to only the subarray of attributes that cover the requested range - NSArray *attrs; - if (NSEqualRanges(enumerationRange, NSMakeRange(0, 0))) { - attrs = [NSArray arrayWithArray:_attributes]; - } else { - // in this binary search, last is the first run after the range - NSUInteger first = 0, last = [_attributes count]; - while (last > first+1) { - NSUInteger pivot = (last + first) / 2; - ZAttributeRun *run = [_attributes objectAtIndex:pivot]; - if (run.index < enumerationRange.location) { - first = pivot; - } else if (run.index >= NSMaxRange(enumerationRange)) { - last = pivot; - } - } - attrs = [_attributes subarrayWithRange:NSMakeRange(first, last-first)]; - } - if (opts & ZAttributedStringEnumerationReverse) { - NSUInteger end = [_buffer length]; - for (ZAttributeRun *run in [attrs reverseObjectEnumerator]) { - BOOL stop = NO; - NSUInteger start = run.index; - // clip to enumerationRange - start = MAX(start, enumerationRange.location); - end = MIN(end, NSMaxRange(enumerationRange)); - block(run.attributes, NSMakeRange(start, end - start), &stop); - if (stop) break; - end = run.index; - } - } else { - NSUInteger start = 0; - ZAttributeRun *run = [attrs objectAtIndex:0]; - NSInteger offset = 0; - NSInteger oldLength = [_buffer length]; - for (NSUInteger i = 1;;i++) { - NSUInteger end; - if (i >= [attrs count]) { - end = oldLength; - } else { - end = [[attrs objectAtIndex:i] index]; - } - BOOL stop = NO; - NSUInteger clippedStart = MAX(start, enumerationRange.location); - NSUInteger clippedEnd = MIN(end, NSMaxRange(enumerationRange)); - block(run.attributes, NSMakeRange(clippedStart + offset, clippedEnd - start), &stop); - if (stop || i >= [attrs count]) break; - start = end; - NSUInteger newLength = [_buffer length]; - offset += (newLength - oldLength); - oldLength = newLength; - } - } -} -#endif - -- (BOOL)isEqualToAttributedString:(ZAttributedString *)otherString { - return ([_buffer isEqualToString:otherString->_buffer] && [_attributes isEqualToArray:otherString->_attributes]); -} - -- (BOOL)isEqual:(id)object { - return [object isKindOfClass:[ZAttributedString class]] && [self isEqualToAttributedString:(ZAttributedString *)object]; -} - -#pragma mark - - -- (NSUInteger)indexOfEffectiveAttributeRunForIndex:(NSUInteger)index { - NSUInteger first = 0, last = [_attributes count]; - while (last > first + 1) { - NSUInteger pivot = (last + first) / 2; - ZAttributeRun *run = [_attributes objectAtIndex:pivot]; - if (run.index > index) { - last = pivot; - } else if (run.index < index) { - first = pivot; - } else { - first = pivot; - break; - } - } - return first; -} - -- (NSDictionary *)attributesAtIndex:(NSUInteger)index effectiveRange:(NSRangePointer)aRange uniquingOnName:(NSString *)attributeName { - if (index >= [_buffer length]) { - @throw [NSException exceptionWithName:NSRangeException reason:@"index beyond range of attributed string" userInfo:nil]; - } - NSUInteger runIndex = [self indexOfEffectiveAttributeRunForIndex:index]; - ZAttributeRun *run = [_attributes objectAtIndex:runIndex]; - if (aRange != NULL) { - aRange->location = run.index; - runIndex++; - if (runIndex < [_attributes count]) { - aRange->length = [[_attributes objectAtIndex:runIndex] index] - aRange->location; - } else { - aRange->length = [_buffer length] - aRange->location; - } - } - return run.attributes; -} -- (NSDictionary *)attributesAtIndex:(NSUInteger)index longestEffectiveRange:(NSRangePointer)aRange - inRange:(NSRange)rangeLimit uniquingOnName:(NSString *)attributeName { - if (index >= [_buffer length]) { - @throw [NSException exceptionWithName:NSRangeException reason:@"index beyond range of attributed string" userInfo:nil]; - } else if (NSMaxRange(rangeLimit) > [_buffer length]) { - @throw [NSException exceptionWithName:NSRangeException reason:@"rangeLimit beyond range of attributed string" userInfo:nil]; - } - NSUInteger runIndex = [self indexOfEffectiveAttributeRunForIndex:index]; - ZAttributeRun *run = [_attributes objectAtIndex:runIndex]; - if (aRange != NULL) { - if (attributeName != nil) { - id value = [run.attributes objectForKey:attributeName]; - NSUInteger endRunIndex = runIndex+1; - runIndex--; - // search backwards - while (1) { - if (run.index <= rangeLimit.location) { - break; - } - ZAttributeRun *prevRun = [_attributes objectAtIndex:runIndex]; - id prevValue = [prevRun.attributes objectForKey:attributeName]; - if (prevValue == value || (value != nil && [prevValue isEqual:value])) { - runIndex--; - run = prevRun; - } else { - break; - } - } - // search forwards - ZAttributeRun *endRun = nil; - while (endRunIndex < [_attributes count]) { - ZAttributeRun *nextRun = [_attributes objectAtIndex:endRunIndex]; - if (nextRun.index >= NSMaxRange(rangeLimit)) { - endRun = nextRun; - break; - } - id nextValue = [nextRun.attributes objectForKey:attributeName]; - if (nextValue == value || (value != nil && [nextValue isEqual:value])) { - endRunIndex++; - } else { - endRun = nextRun; - break; - } - } - aRange->location = MAX(run.index, rangeLimit.location); - aRange->length = MIN((endRun ? endRun.index : [_buffer length]), NSMaxRange(rangeLimit)) - aRange->location; - } else { - // with no attribute name, we don't need to do any real searching, - // as we already guarantee each run has unique attributes. - // just make sure to clip the range to the rangeLimit - aRange->location = MAX(run.index, rangeLimit.location); - ZAttributeRun *endRun = (runIndex+1 < [_attributes count] ? [_attributes objectAtIndex:runIndex+1] : nil); - aRange->length = MIN((endRun ? endRun.index : [_buffer length]), NSMaxRange(rangeLimit)) - aRange->location; - } - } - return run.attributes; -} - -- (void)dealloc { - [_buffer release]; - [_attributes release]; - [super dealloc]; -} -@end - -@interface ZMutableAttributedString () -- (void)cleanupAttributesInRange:(NSRange)range; -- (NSRange)rangeOfAttributeRunsForRange:(NSRange)range; -- (void)offsetRunsInRange:(NSRange )range byOffset:(NSInteger)offset; -@end - -@implementation ZMutableAttributedString -- (id)copyWithZone:(NSZone *)zone { - return [(ZAttributedString *)[ZAttributedString allocWithZone:zone] initWithAttributedString:self]; -} - -- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range { - range = [self rangeOfAttributeRunsForRange:range]; - for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) { - [run.attributes setObject:value forKey:name]; - } - [self cleanupAttributesInRange:range]; -} - -- (void)addAttributes:(NSDictionary *)attributes range:(NSRange)range { - range = [self rangeOfAttributeRunsForRange:range]; - for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) { - [run.attributes addEntriesFromDictionary:attributes]; - } - [self cleanupAttributesInRange:range]; -} - -- (void)appendAttributedString:(ZAttributedString *)str { - [self insertAttributedString:str atIndex:[_buffer length]]; -} - -- (void)deleteCharactersInRange:(NSRange)range { - NSRange runRange = [self rangeOfAttributeRunsForRange:range]; - [_buffer replaceCharactersInRange:range withString:@""]; - [_attributes removeObjectsInRange:runRange]; - for (NSUInteger i = runRange.location; i < [_attributes count]; i++) { - ZAttributeRun *run = [_attributes objectAtIndex:i]; - ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:(run.index - range.length) attributes:run.attributes]; - [_attributes replaceObjectAtIndex:i withObject:newRun]; - [newRun release]; - } - [self cleanupAttributesInRange:NSMakeRange(runRange.location, 0)]; -} - -- (void)insertAttributedString:(ZAttributedString *)str atIndex:(NSUInteger)idx { - [self replaceCharactersInRange:NSMakeRange(idx, 0) withAttributedString:str]; -} - -- (void)removeAttribute:(NSString *)name range:(NSRange)range { - range = [self rangeOfAttributeRunsForRange:range]; - for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) { - [run.attributes removeObjectForKey:name]; - } - [self cleanupAttributesInRange:range]; -} - -- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(ZAttributedString *)str { - NSRange replaceRange = [self rangeOfAttributeRunsForRange:range]; - NSInteger offset = [str->_buffer length] - range.length; - [_buffer replaceCharactersInRange:range withString:str->_buffer]; - [_attributes replaceObjectsInRange:replaceRange withObjectsFromArray:str->_attributes]; - NSRange newRange = NSMakeRange(replaceRange.location, [str->_attributes count]); - [self offsetRunsInRange:newRange byOffset:range.location]; - [self offsetRunsInRange:NSMakeRange(NSMaxRange(newRange), [_attributes count] - NSMaxRange(newRange)) byOffset:offset]; - [self cleanupAttributesInRange:NSMakeRange(newRange.location, 0)]; - [self cleanupAttributesInRange:NSMakeRange(NSMaxRange(newRange), 0)]; -} - -- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str { - [self replaceCharactersInRange:range withAttributedString:[[[ZAttributedString alloc] initWithString:str] autorelease]]; -} - -- (void)setAttributedString:(ZAttributedString *)str { - [_buffer release], _buffer = [str->_buffer mutableCopy]; - [_attributes release], _attributes = [str->_attributes mutableCopy]; -} - -- (void)setAttributes:(NSDictionary *)attributes range:(NSRange)range { - range = [self rangeOfAttributeRunsForRange:range]; - for (ZAttributeRun *run in [_attributes subarrayWithRange:range]) { - [run.attributes setDictionary:attributes]; - } - [self cleanupAttributesInRange:range]; -} - -#pragma mark - - -// splits the existing runs to provide one or more new runs for the given range -- (NSRange)rangeOfAttributeRunsForRange:(NSRange)range { - NSParameterAssert(NSMaxRange(range) <= [_buffer length]); - - // find (or create) the first run - NSUInteger first = 0; - ZAttributeRun *lastRun = nil; - for (;;first++) { - if (first >= [_attributes count]) { - // we didn't find a run - first = [_attributes count]; - ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:range.location attributes:lastRun.attributes]; - [_attributes addObject:newRun]; - [newRun release]; - break; - } - ZAttributeRun *run = [_attributes objectAtIndex:first]; - if (run.index == range.location) { - break; - } else if (run.index > range.location) { - ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:range.location attributes:lastRun.attributes]; - [_attributes insertObject:newRun atIndex:first]; - [newRun release]; - break; - } - lastRun = run; - } - - if (((ZAttributeRun *)[_attributes lastObject]).index < NSMaxRange(range)) { - NSRange subrange = NSMakeRange(first, [_attributes count] - first); - if (NSMaxRange(range) < [_buffer length]) { - ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:NSMaxRange(range) - attributes:(NSDictionary*)[(ZAttributeRun *)[_attributes lastObject] attributes]]; - [_attributes addObject:newRun]; - [newRun release]; - } - return subrange; - } else { - // find the last run within and the first run after the range - NSUInteger lastIn = first, firstAfter = [_attributes count]-1; - while (firstAfter > lastIn + 1) { - NSUInteger idx = (firstAfter + lastIn) / 2; - ZAttributeRun *run = [_attributes objectAtIndex:idx]; - if (run.index < range.location) { - lastIn = idx; - } else if (run.index > range.location) { - firstAfter = idx; - } else { - // this is definitively the first run after the range - firstAfter = idx; - break; - } - } - if ([[_attributes objectAtIndex:firstAfter] index] > NSMaxRange(range)) { - // the first after is too far after, insert another run! - ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:NSMaxRange(range) - attributes:[(ZAttributeRun *)[_attributes objectAtIndex:firstAfter-1] attributes]]; - [_attributes insertObject:newRun atIndex:firstAfter]; - [newRun release]; - } - return NSMakeRange(lastIn, firstAfter - lastIn); - } -} - -- (void)cleanupAttributesInRange:(NSRange)range { - // expand the range to include one surrounding attribute on each side - if (range.location > 0) { - range.location -= 1; - range.length += 1; - } - if (NSMaxRange(range) < [_attributes count]) { - range.length += 1; - } else { - // make sure the range is capped to the attributes count - range.length = [_attributes count] - range.location; - } - if (range.length == 0) return; - ZAttributeRun *lastRun = [_attributes objectAtIndex:range.location]; - for (NSUInteger i = range.location+1; i < NSMaxRange(range);) { - ZAttributeRun *run = [_attributes objectAtIndex:i]; - if ([lastRun.attributes isEqualToDictionary:run.attributes]) { - [_attributes removeObjectAtIndex:i]; - range.length -= 1; - } else { - lastRun = run; - i++; - } - } -} - -- (void)offsetRunsInRange:(NSRange)range byOffset:(NSInteger)offset { - for (NSUInteger i = range.location; i < NSMaxRange(range); i++) { - ZAttributeRun *run = [_attributes objectAtIndex:i]; - ZAttributeRun *newRun = [[ZAttributeRun alloc] initWithIndex:run.index + offset attributes:run.attributes]; - [_attributes replaceObjectAtIndex:i withObject:newRun]; - [newRun release]; - } -} -@end - -@implementation ZAttributeRun -@synthesize index = _index; -@synthesize attributes = _attributes; - -+ (id)attributeRunWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs { - return [[[self alloc] initWithIndex:idx attributes:attrs] autorelease]; -} - -- (id)initWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs { - NSParameterAssert(idx >= 0); - if ((self = [super init])) { - _index = idx; - if (attrs == nil) { - _attributes = [[NSMutableDictionary alloc] init]; - } else { - _attributes = [attrs mutableCopy]; - } - } - return self; -} - -- (id)initWithCoder:(NSCoder *)decoder { - if ((self = [super init])) { - _index = [[decoder decodeObjectForKey:@"index"] unsignedIntegerValue]; - _attributes = [[decoder decodeObjectForKey:@"attributes"] mutableCopy]; - } - return self; -} - -- (id)init { - return [self initWithIndex:0 attributes:[NSDictionary dictionary]]; -} - -- (id)copyWithZone:(NSZone *)zone { - return [[ZAttributeRun allocWithZone:zone] initWithIndex:_index attributes:_attributes]; -} - -- (void)encodeWithCoder:(NSCoder *)aCoder { - [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:_index] forKey:@"index"]; - [aCoder encodeObject:_attributes forKey:@"attributes"]; -} - -- (NSString *)description { - NSMutableArray *components = [NSMutableArray arrayWithCapacity:[_attributes count]]; - for (id key in _attributes) { - [components addObject:[NSString stringWithFormat:@"%@=%@", key, [_attributes objectForKey:key]]]; - } - return [NSString stringWithFormat:@"<%@: %p index=%lu attributes={%@}>", - NSStringFromClass([self class]), self, (unsigned long)_index, [components componentsJoinedByString:@" "]]; -} - -- (BOOL)isEqual:(id)object { - if (![object isKindOfClass:[ZAttributeRun class]]) return NO; - ZAttributeRun *other = (ZAttributeRun *)object; - return _index == other->_index && [_attributes isEqualToDictionary:other->_attributes]; -} - -- (void)dealloc { - [_attributes release]; - [super dealloc]; -} -@end - -NSString * const ZFontAttributeName = @"ZFontAttributeName"; -NSString * const ZForegroundColorAttributeName = @"ZForegroundColorAttributeName"; -NSString * const ZBackgroundColorAttributeName = @"ZBackgroundColorAttributeName"; -NSString * const ZUnderlineStyleAttributeName = @"ZUnderlineStyleAttributeName"; diff --git a/cocos2d/FontLabel/ZAttributedStringPrivate.h b/cocos2d/FontLabel/ZAttributedStringPrivate.h deleted file mode 100644 index 1021d7b..0000000 --- a/cocos2d/FontLabel/ZAttributedStringPrivate.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// ZAttributedStringPrivate.h -// FontLabel -// -// Created by Kevin Ballard on 9/23/09. -// Copyright 2009 Zynga Game Networks. All rights reserved. -// - -#import -#import "ZAttributedString.h" - -@interface ZAttributeRun : NSObject { - NSUInteger _index; - NSMutableDictionary *_attributes; -} -@property (nonatomic, readonly) NSUInteger index; -@property (nonatomic, readonly) NSMutableDictionary *attributes; -+ (id)attributeRunWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs; -- (id)initWithIndex:(NSUInteger)idx attributes:(NSDictionary *)attrs; -@end - -@interface ZAttributedString (ZAttributedStringPrivate) -@property (nonatomic, readonly) NSArray *attributes; -@end diff --git a/cocos2d/FontLabel/ZFont.h b/cocos2d/FontLabel/ZFont.h deleted file mode 100644 index 05ae823..0000000 --- a/cocos2d/FontLabel/ZFont.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// ZFont.h -// FontLabel -// -// Created by Kevin Ballard on 7/2/09. -// Copyright © 2009 Zynga Game Networks -// -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import -#import - -@interface ZFont : NSObject { - CGFontRef _cgFont; - CGFloat _pointSize; - CGFloat _ratio; - NSString *_familyName; - NSString *_fontName; - NSString *_postScriptName; -} -@property (nonatomic, readonly) CGFontRef cgFont; -@property (nonatomic, readonly) CGFloat pointSize; -@property (nonatomic, readonly) CGFloat ascender; -@property (nonatomic, readonly) CGFloat descender; -@property (nonatomic, readonly) CGFloat leading; -@property (nonatomic, readonly) CGFloat xHeight; -@property (nonatomic, readonly) CGFloat capHeight; -@property (nonatomic, readonly) NSString *familyName; -@property (nonatomic, readonly) NSString *fontName; -@property (nonatomic, readonly) NSString *postScriptName; -+ (ZFont *)fontWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize; -+ (ZFont *)fontWithUIFont:(UIFont *)uiFont; -- (id)initWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize; -- (ZFont *)fontWithSize:(CGFloat)fontSize; -@end diff --git a/cocos2d/FontLabel/ZFont.m b/cocos2d/FontLabel/ZFont.m deleted file mode 100644 index 793b13a..0000000 --- a/cocos2d/FontLabel/ZFont.m +++ /dev/null @@ -1,170 +0,0 @@ -// -// ZFont.m -// FontLabel -// -// Created by Kevin Ballard on 7/2/09. -// Copyright © 2009 Zynga Game Networks -// -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "ZFont.h" - -@interface ZFont () -@property (nonatomic, readonly) CGFloat ratio; -- (NSString *)copyNameTableEntryForID:(UInt16)nameID; -@end - -@implementation ZFont -@synthesize cgFont=_cgFont, pointSize=_pointSize, ratio=_ratio; - -+ (ZFont *)fontWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize { - return [[[self alloc] initWithCGFont:cgFont size:fontSize] autorelease]; -} - -+ (ZFont *)fontWithUIFont:(UIFont *)uiFont { - NSParameterAssert(uiFont != nil); - CGFontRef cgFont = CGFontCreateWithFontName((CFStringRef)uiFont.fontName); - ZFont *zFont = [[self alloc] initWithCGFont:cgFont size:uiFont.pointSize]; - CGFontRelease(cgFont); - return [zFont autorelease]; -} - -- (id)initWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize { - if ((self = [super init])) { - _cgFont = CGFontRetain(cgFont); - _pointSize = fontSize; - _ratio = fontSize/CGFontGetUnitsPerEm(cgFont); - } - return self; -} - -- (id)init { - NSAssert(NO, @"-init is not valid for ZFont"); - return nil; -} - -- (CGFloat)ascender { - return ceilf(self.ratio * CGFontGetAscent(self.cgFont)); -} - -- (CGFloat)descender { - return floorf(self.ratio * CGFontGetDescent(self.cgFont)); -} - -- (CGFloat)leading { - return (self.ascender - self.descender); -} - -- (CGFloat)capHeight { - return ceilf(self.ratio * CGFontGetCapHeight(self.cgFont)); -} - -- (CGFloat)xHeight { - return ceilf(self.ratio * CGFontGetXHeight(self.cgFont)); -} - -- (NSString *)familyName { - if (_familyName == nil) { - _familyName = [self copyNameTableEntryForID:1]; - } - return _familyName; -} - -- (NSString *)fontName { - if (_fontName == nil) { - _fontName = [self copyNameTableEntryForID:4]; - } - return _fontName; -} - -- (NSString *)postScriptName { - if (_postScriptName == nil) { - _postScriptName = [self copyNameTableEntryForID:6]; - } - return _postScriptName; -} - -- (ZFont *)fontWithSize:(CGFloat)fontSize { - if (fontSize == self.pointSize) return self; - NSParameterAssert(fontSize > 0.0); - return [[[ZFont alloc] initWithCGFont:self.cgFont size:fontSize] autorelease]; -} - -- (BOOL)isEqual:(id)object { - if (![object isKindOfClass:[ZFont class]]) return NO; - ZFont *font = (ZFont *)object; - return (font.cgFont == self.cgFont && font.pointSize == self.pointSize); -} - -- (NSString *)copyNameTableEntryForID:(UInt16)aNameID { - CFDataRef nameTable = CGFontCopyTableForTag(self.cgFont, 'name'); - NSAssert1(nameTable != NULL, @"CGFontCopyTableForTag returned NULL for 'name' tag in font %@", - [(id)CFCopyDescription(self.cgFont) autorelease]); - const UInt8 * const bytes = CFDataGetBytePtr(nameTable); - NSAssert1(OSReadBigInt16(bytes, 0) == 0, @"name table for font %@ has bad version number", - [(id)CFCopyDescription(self.cgFont) autorelease]); - const UInt16 count = OSReadBigInt16(bytes, 2); - const UInt16 stringOffset = OSReadBigInt16(bytes, 4); - const UInt8 * const nameRecords = &bytes[6]; - UInt16 nameLength = 0; - UInt16 nameOffset = 0; - NSStringEncoding encoding = 0; - for (UInt16 idx = 0; idx < count; idx++) { - const uintptr_t recordOffset = 12 * idx; - const UInt16 nameID = OSReadBigInt16(nameRecords, recordOffset + 6); - if (nameID != aNameID) continue; - const UInt16 platformID = OSReadBigInt16(nameRecords, recordOffset + 0); - const UInt16 platformSpecificID = OSReadBigInt16(nameRecords, recordOffset + 2); - encoding = 0; - // for now, we only support a subset of encodings - switch (platformID) { - case 0: // Unicode - encoding = NSUTF16StringEncoding; - break; - case 1: // Macintosh - switch (platformSpecificID) { - case 0: - encoding = NSMacOSRomanStringEncoding; - break; - } - case 3: // Microsoft - switch (platformSpecificID) { - case 1: - encoding = NSUTF16StringEncoding; - break; - } - } - if (encoding == 0) continue; - nameLength = OSReadBigInt16(nameRecords, recordOffset + 8); - nameOffset = OSReadBigInt16(nameRecords, recordOffset + 10); - break; - } - NSString *result = nil; - if (nameOffset > 0) { - const UInt8 *nameBytes = &bytes[stringOffset + nameOffset]; - result = [[NSString alloc] initWithBytes:nameBytes length:nameLength encoding:encoding]; - } - CFRelease(nameTable); - return result; -} - -- (void)dealloc { - CGFontRelease(_cgFont); - [_familyName release]; - [_fontName release]; - [_postScriptName release]; - [super dealloc]; -} -@end diff --git a/cocos2d/LICENSE_FontLabel.txt b/cocos2d/LICENSE_FontLabel.txt deleted file mode 100644 index a45b29a..0000000 --- a/cocos2d/LICENSE_FontLabel.txt +++ /dev/null @@ -1,79 +0,0 @@ -FontLabel ---------- - -Copyright © 2009 Zynga Game Networks. - - -License -------- - -Apache License, Version 2.0 - -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and - 2. You must cause any modified files to carry prominent notices stating that You changed the files; and - 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/cocos2d/LICENSE_cocos2d.txt b/cocos2d/LICENSE_cocos2d.txt old mode 100644 new mode 100755 diff --git a/cocos2d/cocos2d/CCAction.h b/cocos2d/cocos2d/CCAction.h old mode 100644 new mode 100755 index 51bad8e..f1da6b8 --- a/cocos2d/cocos2d/CCAction.h +++ b/cocos2d/cocos2d/CCAction.h @@ -10,10 +10,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -76,10 +76,10 @@ enum { //! called after the action has finished. It will set the 'target' to nil. //! IMPORTANT: You should never call "[action stop]" manually. Instead, use: "[target stopAction:action];" -(void) stop; -//! called every frame with it's delta time. DON'T override unless you know what you are doing. +//! called every frame with its delta time. DON'T override unless you know what you are doing. -(void) step: (ccTime) dt; //! called once per frame. time a value between 0 and 1 -//! For example: +//! For example: //! * 0 means that the action just started //! * 0.5 means that the action is in the middle //! * 1 means that the action is over @@ -124,8 +124,8 @@ enum { -(id) initWithAction: (CCActionInterval*) action; @end -/** Changes the speed of an action, making it take longer (speed>1) - or less (speed<1) time. +/** Changes the speed of an action, making it take longer (speed<1) + or less (speed>1) time. Useful to simulate 'slow motion' or 'fast forward' effect. @warning This action can't be Sequenceable because it is not an CCIntervalAction */ @@ -140,17 +140,17 @@ enum { @property (nonatomic, readwrite, retain) CCActionInterval *innerAction; /** creates the action */ -+(id) actionWithAction: (CCActionInterval*) action speed:(float)rate; ++(id) actionWithAction: (CCActionInterval*) action speed:(float)value; /** initializes the action */ --(id) initWithAction: (CCActionInterval*) action speed:(float)rate; +-(id) initWithAction: (CCActionInterval*) action speed:(float)value; @end @class CCNode; /** CCFollow is an action that "follows" a node. - + Eg: [layer runAction: [CCFollow actionWithTarget:hero]]; - + Instead of using CCCamera as a "follower", use this action instead. @since v0.99.2 */ @@ -158,17 +158,17 @@ enum { { /* node to follow */ CCNode *followedNode_; - + /* whether camera should be limited to certain area */ BOOL boundarySet; - + /* if screensize is bigger than the boundary - update not needed */ BOOL boundaryFullyCovered; - + /* fast access to the screen dimensions */ CGPoint halfScreenSize; CGPoint fullScreenSize; - + /* world boundaries */ float leftBoundary; float rightBoundary; diff --git a/cocos2d/cocos2d/CCAction.m b/cocos2d/cocos2d/CCAction.m old mode 100644 new mode 100755 index 27db20b..7202e3c --- a/cocos2d/cocos2d/CCAction.m +++ b/cocos2d/cocos2d/CCAction.m @@ -10,10 +10,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,8 +25,6 @@ */ - -#import #import "CCDirector.h" #import "ccMacros.h" #import "CCAction.h" @@ -49,7 +47,7 @@ +(id) action -(id) init { - if( (self=[super init]) ) { + if( (self=[super init]) ) { originalTarget_ = target_ = nil; tag_ = kCCActionTagInvalid; } @@ -64,7 +62,7 @@ -(void) dealloc -(NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_]; + return [NSString stringWithFormat:@"<%@ = %p | Tag = %ld>", [self class], self, (long)tag_]; } -(id) copyWithZone: (NSZone*) zone @@ -91,12 +89,12 @@ -(BOOL) isDone -(void) step: (ccTime) dt { - NSLog(@"[Action step]. override me"); + CCLOG(@"[Action step]. override me"); } -(void) update: (ccTime) time { - NSLog(@"[Action update]. override me"); + CCLOG(@"[Action update]. override me"); } @end @@ -130,7 +128,7 @@ +(id) actionWithAction: (CCActionInterval*) action -(id) initWithAction: (CCActionInterval*) action { - if( (self=[super init]) ) + if( (self=[super init]) ) self.innerAction = action; return self; @@ -158,10 +156,11 @@ -(void) step:(ccTime) dt { [innerAction_ step: dt]; if( [innerAction_ isDone] ) { - ccTime diff = dt + innerAction_.duration - innerAction_.elapsed; + ccTime diff = innerAction_.elapsed - innerAction_.duration; [innerAction_ startWithTarget:target_]; - - // to prevent jerk. issue #390 + + // to prevent jerk. issue #390, 1247 + [innerAction_ step: 0.0f]; [innerAction_ step: diff]; } } @@ -187,16 +186,16 @@ @implementation CCSpeed @synthesize speed=speed_; @synthesize innerAction=innerAction_; -+(id) actionWithAction: (CCActionInterval*) action speed:(float)r ++(id) actionWithAction: (CCActionInterval*) action speed:(float)value { - return [[[self alloc] initWithAction: action speed:r] autorelease]; + return [[[self alloc] initWithAction: action speed:value] autorelease]; } --(id) initWithAction: (CCActionInterval*) action speed:(float)r +-(id) initWithAction: (CCActionInterval*) action speed:(float)value { if( (self=[super init]) ) { self.innerAction = action; - speed_ = r; + speed_ = value; } return self; } @@ -263,36 +262,36 @@ +(id) actionWithTarget:(CCNode *) fNode worldBoundary:(CGRect)rect -(id) initWithTarget:(CCNode *)fNode { if( (self=[super init]) ) { - + followedNode_ = [fNode retain]; boundarySet = FALSE; boundaryFullyCovered = FALSE; - + CGSize s = [[CCDirector sharedDirector] winSize]; fullScreenSize = CGPointMake(s.width, s.height); halfScreenSize = ccpMult(fullScreenSize, .5f); } - + return self; } -(id) initWithTarget:(CCNode *)fNode worldBoundary:(CGRect)rect { if( (self=[super init]) ) { - + followedNode_ = [fNode retain]; boundarySet = TRUE; boundaryFullyCovered = FALSE; - + CGSize winSize = [[CCDirector sharedDirector] winSize]; fullScreenSize = CGPointMake(winSize.width, winSize.height); halfScreenSize = ccpMult(fullScreenSize, .5f); - + leftBoundary = -((rect.origin.x+rect.size.width) - fullScreenSize.x); rightBoundary = -rect.origin.x ; topBoundary = -rect.origin.y; bottomBoundary = -((rect.origin.y+rect.size.height) - fullScreenSize.y); - + if(rightBoundary < leftBoundary) { // screen width is larger than world's boundary width @@ -305,11 +304,11 @@ -(id) initWithTarget:(CCNode *)fNode worldBoundary:(CGRect)rect //set both in the middle of the world topBoundary = bottomBoundary = (topBoundary + bottomBoundary) / 2; } - + if( (topBoundary == bottomBoundary) && (leftBoundary == rightBoundary) ) boundaryFullyCovered = TRUE; } - + return self; } @@ -327,14 +326,12 @@ -(void) step:(ccTime) dt // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased if(boundaryFullyCovered) return; - + CGPoint tempPos = ccpSub( halfScreenSize, followedNode_.position); [target_ setPosition:ccp(clampf(tempPos.x,leftBoundary,rightBoundary), clampf(tempPos.y,bottomBoundary,topBoundary))]; } else [target_ setPosition:ccpSub( halfScreenSize, followedNode_.position )]; - -#undef CLAMP } diff --git a/cocos2d/cocos2d/CCActionCamera.h b/cocos2d/cocos2d/CCActionCamera.h old mode 100644 new mode 100755 index 1ea83a7..3c3934c --- a/cocos2d/cocos2d/CCActionCamera.h +++ b/cocos2d/cocos2d/CCActionCamera.h @@ -10,10 +10,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -31,15 +31,15 @@ /** Base class for CCCamera actions */ @interface CCActionCamera : CCActionInterval -{ +{ float centerXOrig_; float centerYOrig_; float centerZOrig_; - + float eyeXOrig_; float eyeYOrig_; float eyeZOrig_; - + float upXOrig_; float upYOrig_; float upZOrig_; @@ -57,12 +57,12 @@ float deltaAngleZ_; float angleX_; float deltaAngleX_; - + float radZ_; float radDeltaZ_; float radX_; float radDeltaX_; - + } /** creates a CCOrbitCamera action with radius, delta-radius, z, deltaZ, x, deltaX */ +(id) actionWithDuration:(float) t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx; diff --git a/cocos2d/cocos2d/CCActionCamera.m b/cocos2d/cocos2d/CCActionCamera.m old mode 100644 new mode 100755 index 4dafc4e..c46f239 --- a/cocos2d/cocos2d/CCActionCamera.m +++ b/cocos2d/cocos2d/CCActionCamera.m @@ -10,10 +10,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -65,7 +65,7 @@ -(id) copyWithZone: (NSZone*) zone -(id) initWithDuration:(float)t radius:(float)r deltaRadius:(float) dr angleZ:(float)z deltaAngleZ:(float)dz angleX:(float)x deltaAngleX:(float)dx { if((self=[super initWithDuration:t]) ) { - + radius_ = r; deltaRadius_ = dr; angleZ_ = z; @@ -76,7 +76,7 @@ -(id) initWithDuration:(float)t radius:(float)r deltaRadius:(float) dr angleZ:(f radDeltaZ_ = (CGFloat)CC_DEGREES_TO_RADIANS(dz); radDeltaX_ = (CGFloat)CC_DEGREES_TO_RADIANS(dx); } - + return self; } @@ -84,16 +84,16 @@ -(void) startWithTarget:(id)aTarget { [super startWithTarget:aTarget]; float r, zenith, azimuth; - + [self sphericalRadius: &r zenith:&zenith azimuth:&azimuth]; - + #if 0 // isnan() is not supported on the simulator, and isnan() always returns false. if( isnan(radius_) ) radius_ = r; - + if( isnan( angleZ_) ) angleZ_ = (CGFloat)CC_RADIANS_TO_DEGREES(zenith); - + if( isnan( angleX_ ) ) angleX_ = (CGFloat)CC_RADIANS_TO_DEGREES(azimuth); #endif @@ -112,7 +112,7 @@ -(void) update: (ccTime) dt float j = sinf(za) * sinf(xa) * r + centerYOrig_; float k = cosf(za) * r + centerZOrig_; - [[target_ camera] setEyeX:i eyeY:j eyeZ:k]; + [[target_ camera] setEyeX:i eyeY:j eyeZ:k]; } -(void) sphericalRadius:(float*) newRadius zenith:(float*) zenith azimuth:(float*) azimuth @@ -120,15 +120,15 @@ -(void) sphericalRadius:(float*) newRadius zenith:(float*) zenith azimuth:(float float ex, ey, ez, cx, cy, cz, x, y, z; float r; // radius float s; - + CCCamera *camera = [target_ camera]; [camera eyeX:&ex eyeY:&ey eyeZ:&ez]; [camera centerX:&cx centerY:&cy centerZ:&cz]; - + x = ex-cx; y = ey-cy; z = ez-cz; - + r = sqrtf( x*x + y*y + z*z); s = sqrtf( x*x + y*y); if(s==0.0f) @@ -141,7 +141,7 @@ -(void) sphericalRadius:(float*) newRadius zenith:(float*) zenith azimuth:(float *azimuth = (float)M_PI - asinf(y/s); else *azimuth = asinf(y/s); - - *newRadius = r / [CCCamera getZEye]; + + *newRadius = r / [CCCamera getZEye]; } @end diff --git a/cocos2d/cocos2d/CCActionCatmullRom.h b/cocos2d/cocos2d/CCActionCatmullRom.h new file mode 100755 index 0000000..1218913 --- /dev/null +++ b/cocos2d/cocos2d/CCActionCatmullRom.h @@ -0,0 +1,139 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008 Radu Gruian + * + * Copyright (c) 2011 Vit Valentin + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * + * Orignal code by Radu Gruian: http://www.codeproject.com/Articles/30838/Overhauser-Catmull-Rom-Splines-for-Camera-Animatio.So + * + * Adapted to cocos2d-x by Vit Valentin + * + * Adapted from cocos2d-x to cocos2d-iphone by Ricardo Quesada + */ + + +#import "CCActionInterval.h" + +/** An Array that contain control points. + Used by CCCardinalSplineTo and (By) and CCCatmullRomTo (and By) actions. + */ +@interface CCPointArray : NSObject +{ + NSMutableArray *controlPoints_; +} + +/** Array that contains the control points */ +@property (nonatomic,readwrite,retain) NSMutableArray *controlPoints; + +/** creates and initializes a Points array with capacity */ + +(id) arrayWithCapacity:(NSUInteger)capacity; + +/** initializes a Catmull Rom config with a capacity hint */ +-(id) initWithCapacity:(NSUInteger)capacity; + +/** appends a control point */ +-(void) addControlPoint:(CGPoint)controlPoint; + +/** inserts a controlPoint at index */ +-(void) insertControlPoint:(CGPoint)controlPoint atIndex:(NSUInteger)index; + +/** replaces an existing controlPoint at index */ +-(void) replaceControlPoint:(CGPoint)controlPoint atIndex:(NSUInteger)index; + +/** get the value of a controlPoint at a given index */ +-(CGPoint) getControlPointAtIndex:(NSInteger)index; + +/** deletes a control point at a given index */ +-(void) removeControlPointAtIndex:(NSUInteger)index; + +/** returns the number of objects of the control point array */ +-(NSUInteger) count; + +/** returns a new copy of the array reversed. User is responsible for releasing this copy */ +-(CCPointArray*) reverse; + +/** reverse the current control point array inline, without generating a new one */ +-(void) reverseInline; +@end + +/** Cardinal Spline path. + http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline + */ +@interface CCCardinalSplineTo : CCActionInterval +{ + CCPointArray *points_; + CGFloat deltaT_; + CGFloat tension_; +} + +/** Array of control points */ + @property (nonatomic,readwrite,retain) CCPointArray *points; + +/** creates an action with a Cardinal Spline array of points and tension */ ++(id) actionWithDuration:(ccTime)duration points:(CCPointArray*)points tension:(CGFloat)tension; + +/** initializes the action with a duration and an array of points */ +-(id) initWithDuration:(ccTime)duration points:(CCPointArray*)points tension:(CGFloat)tension; + +@end + +/** Cardinal Spline path. + http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline + */ +@interface CCCardinalSplineBy : CCCardinalSplineTo +{ + CGPoint startPosition_; +} +@end + +/** An action that moves the target with a CatmullRom curve to a destination point. + A Catmull Rom is a Cardinal Spline with a tension of 0.5. + http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline + */ +@interface CCCatmullRomTo : CCCardinalSplineTo +{ +} +/** creates an action with a Cardinal Spline array of points and tension */ ++(id) actionWithDuration:(ccTime)dt points:(CCPointArray*)points; + +/** initializes the action with a duration and an array of points */ +-(id) initWithDuration:(ccTime)dt points:(CCPointArray*)points; +@end + +/** An action that moves the target with a CatmullRom curve by a certain distance. + A Catmull Rom is a Cardinal Spline with a tension of 0.5. + http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline + */ +@interface CCCatmullRomBy : CCCardinalSplineBy +{ +} +/** creates an action with a Cardinal Spline array of points and tension */ ++(id) actionWithDuration:(ccTime)dt points:(CCPointArray*)points; + +/** initializes the action with a duration and an array of points */ +-(id) initWithDuration:(ccTime)dt points:(CCPointArray*)points; +@end + +/** Returns the Cardinal Spline position for a given set of control points, tension and time */ + CGPoint ccCardinalSplineAt( CGPoint p0, CGPoint p1, CGPoint p2, CGPoint p3, CGFloat tension, ccTime t ); diff --git a/cocos2d/cocos2d/CCActionCatmullRom.m b/cocos2d/cocos2d/CCActionCatmullRom.m new file mode 100755 index 0000000..6f0fe25 --- /dev/null +++ b/cocos2d/cocos2d/CCActionCatmullRom.m @@ -0,0 +1,363 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008 Radu Gruian + * + * Copyright (c) 2011 Vit Valentin + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * + * Orignal code by Radu Gruian: http://www.codeproject.com/Articles/30838/Overhauser-Catmull-Rom-Splines-for-Camera-Animatio.So + * + * Adapted to cocos2d-x by Vit Valentin + * + * Adapted from cocos2d-x to cocos2d-iphone by Ricardo Quesada + */ + + +#import "ccMacros.h" +#import "Support/CGPointExtension.h" +#import "CCActionCatmullRom.h" + +#pragma mark - CCPointArray + +@implementation CCPointArray + +@synthesize controlPoints = controlPoints_; + ++(id) arrayWithCapacity:(NSUInteger)capacity +{ + return [[[self alloc] initWithCapacity:capacity] autorelease]; +} + +-(id) init +{ + return [self initWithCapacity:50]; +} + +// designated initializer +-(id) initWithCapacity:(NSUInteger)capacity +{ + if( (self=[super init])) { + controlPoints_ = [[NSMutableArray alloc] initWithCapacity:capacity]; + } + + return self; +} + +-(id) copyWithZone:(NSZone *)zone +{ + NSMutableArray *newArray = [controlPoints_ mutableCopy]; + CCPointArray *points = [[[self class] allocWithZone:zone] initWithCapacity:10]; + points.controlPoints = newArray; + [newArray release]; + + return points; +} + +-(void) dealloc +{ + [controlPoints_ release]; + + [super dealloc]; +} + +-(void) addControlPoint:(CGPoint)controlPoint +{ +#ifdef __CC_PLATFORM_MAC + NSValue *value = [NSValue valueWithPoint:NSPointFromCGPoint(controlPoint)]; +#elif defined(__CC_PLATFORM_IOS) + NSValue *value = [NSValue valueWithCGPoint:controlPoint]; +#endif + + [controlPoints_ addObject:value]; +} + +-(void) insertControlPoint:(CGPoint)controlPoint atIndex:(NSUInteger)index +{ +#ifdef __CC_PLATFORM_MAC + NSValue *value = [NSValue valueWithPoint:NSPointFromCGPoint(controlPoint)]; +#elif defined(__CC_PLATFORM_IOS) + NSValue *value = [NSValue valueWithCGPoint:controlPoint]; +#endif + + [controlPoints_ insertObject:value atIndex:index]; + +} + +-(CGPoint) getControlPointAtIndex:(NSInteger)index +{ + index = MIN([controlPoints_ count]-1, MAX(index, 0)); + + NSValue *value = [controlPoints_ objectAtIndex:index]; + +#ifdef __CC_PLATFORM_MAC + CGPoint point = NSPointToCGPoint([value pointValue]); +#elif defined(__CC_PLATFORM_IOS) + CGPoint point = [value CGPointValue]; +#endif + + return point; +} + +-(void) replaceControlPoint:(CGPoint)controlPoint atIndex:(NSUInteger)index +{ +#ifdef __CC_PLATFORM_MAC + NSValue *value = [NSValue valueWithPoint:NSPointFromCGPoint(controlPoint)]; +#elif defined(__CC_PLATFORM_IOS) + NSValue *value = [NSValue valueWithCGPoint:controlPoint]; +#endif + + [controlPoints_ replaceObjectAtIndex:index withObject:value]; +} + +-(void) removeControlPointAtIndex:(NSUInteger)index +{ + [controlPoints_ removeObjectAtIndex:index]; +} + +-(NSUInteger) count +{ + return [controlPoints_ count]; +} + +-(CCPointArray*) reverse +{ + NSMutableArray *newArray = [[NSMutableArray alloc] initWithCapacity:[controlPoints_ count]]; + NSEnumerator *enumerator = [controlPoints_ reverseObjectEnumerator]; + for (id element in enumerator) + [newArray addObject:element]; + + CCPointArray *config = [[[self class] alloc] initWithCapacity:0]; + config.controlPoints = newArray; + + [newArray release]; + + return [config autorelease]; +} + +-(void) reverseInline +{ + NSUInteger l = [controlPoints_ count]; + for( NSUInteger i=0; i 0, @"Invalid configuration. It must at least have one control point"); + + if( (self=[super initWithDuration:duration]) ) + { + self.points = points; + tension_ = tension; + } + + return self; +} + +- (void)dealloc +{ + [points_ release]; + [super dealloc]; +} + +-(void) startWithTarget:(id)target +{ + [super startWithTarget:target]; + + deltaT_ = (CGFloat) 1 / [points_ count]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [[[self class] allocWithZone: zone] initWithDuration:[self duration] points:points_ tension:tension_]; + return copy; +} + +-(void) update:(ccTime) dt +{ + NSUInteger p; + CGFloat lt; + + // border + if( dt == 1 ) { + p = [points_ count] - 1; + lt = 1; + } else { + p = dt / deltaT_; + lt = (dt - deltaT_ * (CGFloat)p) / deltaT_; + } + + // Interpolate + CGPoint pp0 = [points_ getControlPointAtIndex:p-1]; + CGPoint pp1 = [points_ getControlPointAtIndex:p+0]; + CGPoint pp2 = [points_ getControlPointAtIndex:p+1]; + CGPoint pp3 = [points_ getControlPointAtIndex:p+2]; + + CGPoint newPos = ccCardinalSplineAt( pp0, pp1, pp2, pp3, tension_, lt ); + + [self updatePosition:newPos]; +} + +-(void) updatePosition:(CGPoint)newPos +{ + [target_ setPosition:newPos]; +} + +-(CCActionInterval*) reverse +{ + CCPointArray *reverse = [points_ reverse]; + + return [[self class] actionWithDuration:duration_ points:reverse tension:tension_]; +} +@end + +#pragma mark - CCCardinalSplineBy + +@implementation CCCardinalSplineBy + +-(void) startWithTarget:(id)target +{ + [super startWithTarget:target]; + + startPosition_ = [(CCNode*)target position]; +} + +-(void) updatePosition:(CGPoint)newPos +{ + [target_ setPosition:ccpAdd(newPos, startPosition_)]; +} + +-(CCActionInterval*) reverse +{ + CCPointArray *copyConfig = [points_ copy]; + + // + // convert "absolutes" to "diffs" + // + CGPoint p = [copyConfig getControlPointAtIndex:0]; + for( NSUInteger i=1; i < [copyConfig count];i++ ) { + + CGPoint current = [copyConfig getControlPointAtIndex:i]; + CGPoint diff = ccpSub(current,p); + [copyConfig replaceControlPoint:diff atIndex:i]; + + p = current; + } + + + // convert to "diffs" to "reverse absolute" + + CCPointArray *reverse = [copyConfig reverse]; + [copyConfig release]; + + // 1st element (which should be 0,0) should be here too + p = [reverse getControlPointAtIndex: [reverse count]-1]; + [reverse removeControlPointAtIndex:[reverse count]-1]; + + p = ccpNeg(p); + [reverse insertControlPoint:p atIndex:0]; + + for( NSUInteger i=1; i < [reverse count];i++ ) { + + CGPoint current = [reverse getControlPointAtIndex:i]; + current = ccpNeg(current); + CGPoint abs = ccpAdd( current, p); + [reverse replaceControlPoint:abs atIndex:i]; + + p = abs; + } + + return [[self class] actionWithDuration:duration_ points:reverse tension:tension_]; +} +@end + +@implementation CCCatmullRomTo ++(id) actionWithDuration:(ccTime)dt points:(CCPointArray *)points +{ + return [[[self alloc] initWithDuration:dt points:points] autorelease]; +} + +-(id) initWithDuration:(ccTime)dt points:(CCPointArray *)points +{ + if( (self=[super initWithDuration:dt points:points tension:0.5f]) ) { + + } + + return self; +} +@end + +@implementation CCCatmullRomBy ++(id) actionWithDuration:(ccTime)dt points:(CCPointArray *)points +{ + return [[[self alloc] initWithDuration:dt points:points] autorelease]; +} + +-(id) initWithDuration:(ccTime)dt points:(CCPointArray *)points +{ + if( (self=[super initWithDuration:dt points:points tension:0.5f]) ) { + + } + + return self; +} +@end diff --git a/cocos2d/cocos2d/CCActionEase.h b/cocos2d/cocos2d/CCActionEase.h old mode 100644 new mode 100755 index fced701..4c91e56 --- a/cocos2d/cocos2d/CCActionEase.h +++ b/cocos2d/cocos2d/CCActionEase.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/cocos2d/cocos2d/CCActionEase.m b/cocos2d/cocos2d/CCActionEase.m old mode 100644 new mode 100755 index f28be11..25b1cf0 --- a/cocos2d/cocos2d/CCActionEase.m +++ b/cocos2d/cocos2d/CCActionEase.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -52,10 +52,10 @@ +(id) actionWithAction: (CCActionInterval*) action -(id) initWithAction: (CCActionInterval*) action { NSAssert( action!=nil, @"Ease: arguments must be non-nil"); - + if( (self=[super initWithDuration: action.duration]) ) other = [action retain]; - + return self; } @@ -112,7 +112,7 @@ -(id) initWithAction: (CCActionInterval*) action rate:(float)aRate { if( (self=[super initWithAction:action ]) ) self.rate = aRate; - + return self; } @@ -158,16 +158,14 @@ -(void) update: (ccTime) t // @implementation CCEaseInOut -(void) update: (ccTime) t -{ - int sign =1; - int r = (int) rate; - if (r % 2 == 0) - sign = -1; +{ t *= 2; - if (t < 1) + if (t < 1) { [other update: 0.5f * powf (t, rate)]; - else - [other update: sign*0.5f * (powf (t-2, rate) + sign*2)]; + } + else { + [other update: 1.0f - 0.5f * powf(2-t, rate)]; + } } // InOut and OutIn are symmetrical @@ -222,7 +220,7 @@ -(void) update: (ccTime) t t = 0.5f * powf(2, 10 * (t - 1)); else t = 0.5f * (-powf(2, -10 * (t -1) ) + 2); - + [other update:t]; } @end @@ -300,7 +298,7 @@ -(id) initWithAction: (CCActionInterval*) action period:(float)period { if( (self=[super initWithAction:action]) ) period_ = period; - + return self; } @@ -324,11 +322,11 @@ -(CCActionInterval*) reverse @implementation CCEaseElasticIn -(void) update: (ccTime) t -{ +{ ccTime newT = 0; if (t == 0 || t == 1) newT = t; - + else { float s = period_ / 4; t = t - 1; @@ -350,11 +348,11 @@ - (CCActionInterval*) reverse @implementation CCEaseElasticOut -(void) update: (ccTime) t -{ +{ ccTime newT = 0; if (t == 0 || t == 1) { newT = t; - + } else { float s = period_ / 4; newT = powf(2, -10 * t) * sinf( (t-s) *M_PI_X_2 / period_) + 1; @@ -376,7 +374,7 @@ @implementation CCEaseElasticInOut -(void) update: (ccTime) t { ccTime newT = 0; - + if( t == 0 || t == 1 ) newT = t; else { @@ -384,14 +382,14 @@ -(void) update: (ccTime) t if(! period_ ) period_ = 0.3f * 1.5f; ccTime s = period_ / 4; - + t = t -1; if( t < 0 ) newT = -0.5f * powf(2, 10 * t) * sinf((t - s) * M_PI_X_2 / period_); else newT = powf(2, -10 * t) * sinf((t - s) * M_PI_X_2 / period_) * 0.5f + 1; } - [other update:newT]; + [other update:newT]; } - (CCActionInterval*) reverse @@ -435,7 +433,7 @@ @implementation CCEaseBounceIn -(void) update: (ccTime) t { - ccTime newT = 1 - [self bounceTime:1-t]; + ccTime newT = 1 - [self bounceTime:1-t]; [other update:newT]; } @@ -450,7 +448,7 @@ @implementation CCEaseBounceOut -(void) update: (ccTime) t { - ccTime newT = [self bounceTime:t]; + ccTime newT = [self bounceTime:t]; [other update:newT]; } @@ -471,7 +469,7 @@ -(void) update: (ccTime) t newT = (1 - [self bounceTime:1-t] ) * 0.5f; } else newT = [self bounceTime:t * 2 - 1] * 0.5f + 0.5f; - + [other update:newT]; } @end @@ -503,7 +501,7 @@ @implementation CCEaseBackOut -(void) update: (ccTime) t { ccTime overshoot = 1.70158f; - + t = t - 1; [other update: t * t * ((overshoot + 1) * t + overshoot) + 1]; } @@ -522,7 +520,7 @@ @implementation CCEaseBackInOut -(void) update: (ccTime) t { ccTime overshoot = 1.70158f * 1.525f; - + t = t * 2; if (t < 1) [other update: (t * t * ((overshoot + 1) * t - overshoot)) / 2]; diff --git a/cocos2d/cocos2d/CCActionGrid.h b/cocos2d/cocos2d/CCActionGrid.h old mode 100644 new mode 100755 index 6b31179..244ee79 --- a/cocos2d/cocos2d/CCActionGrid.h +++ b/cocos2d/cocos2d/CCActionGrid.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/cocos2d/cocos2d/CCActionGrid.m b/cocos2d/cocos2d/CCActionGrid.m old mode 100644 new mode 100755 index 638e27d..fd87ac8 --- a/cocos2d/cocos2d/CCActionGrid.m +++ b/cocos2d/cocos2d/CCActionGrid.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -45,19 +45,19 @@ -(id) initWithSize:(ccGridSize)gSize duration:(ccTime)d { gridSize_ = gSize; } - + return self; } --(void)startWithTarget:(id)aTarget +-(void)startWithTarget:(id)target { - [super startWithTarget:aTarget]; + [super startWithTarget:target]; CCGridBase *newgrid = [self grid]; - - CCNode *t = (CCNode*) target_; + + CCNode *t = (CCNode*) target; CCGridBase *targetGrid = [t grid]; - + if ( targetGrid && targetGrid.reuseGrid > 0 ) { if ( targetGrid.active && targetGrid.gridSize.x == gridSize_.x && targetGrid.gridSize.y == gridSize_.y && [targetGrid isKindOfClass:[newgrid class]] ) @@ -69,10 +69,10 @@ -(void)startWithTarget:(id)aTarget { if ( targetGrid && targetGrid.active ) targetGrid.active = NO; - - t.grid = newgrid; + + [t setGrid: newgrid]; t.grid.active = YES; - } + } } -(CCGridBase *)grid @@ -197,7 +197,7 @@ -(id)initWithAction:(CCAction *)action duration:(ccTime)d rate_ = 1.0f; other_ = (CCActionInterval*)[action retain]; } - + return self; } @@ -216,13 +216,13 @@ -(void)startWithTarget:(id)aTarget -(void) update: (ccTime) time { float f = time*2; - + if (f > 1) { f -= 1; f = 1 - f; } - + [other_ setAmplitudeRate:powf(f, rate_)]; [other_ update:time]; } @@ -255,7 +255,7 @@ -(id)initWithAction:(CCAction *)action duration:(ccTime)d rate_ = 1.0f; other_ = (CCActionInterval*)[action retain]; } - + return self; } @@ -305,7 +305,7 @@ -(id)initWithAction:(CCAction *)action duration:(ccTime)d rate_ = 1.0f; other_ = (CCActionInterval*)[action retain]; } - + return self; } @@ -347,7 +347,7 @@ -(void)startWithTarget:(id)aTarget if ( [[self target] grid] && [[[self target] grid] active] ) { [[[self target] grid] setActive: NO]; - + // [[self target] setGrid: nil]; } } @@ -370,7 +370,7 @@ -(id)initWithTimes:(int)times { if ( (self = [super init]) ) t_ = times; - + return self; } diff --git a/cocos2d/cocos2d/CCActionGrid3D.h b/cocos2d/cocos2d/CCActionGrid3D.h old mode 100644 new mode 100755 index a8003f4..7e1c40e --- a/cocos2d/cocos2d/CCActionGrid3D.h +++ b/cocos2d/cocos2d/CCActionGrid3D.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -73,7 +73,6 @@ @interface CCLens3D : CCGrid3DAction { CGPoint position_; - CGPoint positionInPixels_; float radius_; float lensEffect_; BOOL dirty_; @@ -97,7 +96,6 @@ @interface CCRipple3D : CCGrid3DAction { CGPoint position_; - CGPoint positionInPixels_; float radius_; int waves_; float amplitude_; @@ -123,7 +121,7 @@ /** CCShaky3D action */ @interface CCShaky3D : CCGrid3DAction { - int randrange; + int randrange; BOOL shakeZ; } @@ -142,7 +140,7 @@ int waves; float amplitude; float amplitudeRate; - + } /** amplitude */ @@ -187,7 +185,6 @@ @interface CCTwirl : CCGrid3DAction { CGPoint position_; - CGPoint positionInPixels_; int twirls_; float amplitude_; float amplitudeRate_; diff --git a/cocos2d/cocos2d/CCActionGrid3D.m b/cocos2d/cocos2d/CCActionGrid3D.m old mode 100644 new mode 100755 index 1d4a783..e9fb707 --- a/cocos2d/cocos2d/CCActionGrid3D.m +++ b/cocos2d/cocos2d/CCActionGrid3D.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -49,7 +49,7 @@ -(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration amplitude = amp; amplitudeRate = 1.0f; } - + return self; } @@ -63,7 +63,7 @@ -(id) copyWithZone: (NSZone*) zone -(void)update:(ccTime)time { int i, j; - + for( i = 0; i < (gridSize_.x+1); i++ ) { for( j = 0; j < (gridSize_.y+1); j++ ) @@ -99,7 +99,7 @@ -(id)initWithSize:(ccGridSize)gSize duration:(ccTime)d { [NSException raise:@"FlipX3D" format:@"Grid size must be (1,1)"]; } - + return [super initWithSize:gSize duration:d]; } @@ -116,17 +116,17 @@ -(void)update:(ccTime)time CGFloat mz = sinf( angle ); angle = angle / 2.0f; // x calculates degrees from 0 to 90 CGFloat mx = cosf( angle ); - + ccVertex3F v0, v1, v, diff; - + v0 = [self originalVertex:ccg(1,1)]; v1 = [self originalVertex:ccg(0,0)]; - + CGFloat x0 = v0.x; CGFloat x1 = v1.x; CGFloat x; ccGridSize a, b, c, d; - + if ( x0 > x1 ) { // Normal Grid @@ -145,28 +145,28 @@ -(void)update:(ccTime)time b = ccg(1,1); x = x1; } - + diff.x = ( x - x * mx ); diff.z = fabsf( floorf( (x * mz) / 4.0f ) ); - + // bottom-left v = [self originalVertex:a]; v.x = diff.x; v.z += diff.z; [self setVertex:a vertex:v]; - + // upper-left v = [self originalVertex:b]; v.x = diff.x; v.z += diff.z; [self setVertex:b vertex:v]; - + // bottom-right v = [self originalVertex:c]; v.x -= diff.x; v.z -= diff.z; [self setVertex:c vertex:v]; - + // upper-right v = [self originalVertex:d]; v.x -= diff.x; @@ -189,17 +189,17 @@ -(void)update:(ccTime)time CGFloat mz = sinf( angle ); angle = angle / 2.0f; // x calculates degrees from 0 to 90 CGFloat my = cosf( angle ); - + ccVertex3F v0, v1, v, diff; - + v0 = [self originalVertex:ccg(1,1)]; v1 = [self originalVertex:ccg(0,0)]; - + CGFloat y0 = v0.y; CGFloat y1 = v1.y; CGFloat y; ccGridSize a, b, c, d; - + if ( y0 > y1 ) { // Normal Grid @@ -218,28 +218,28 @@ -(void)update:(ccTime)time c = ccg(1,1); y = y1; } - + diff.y = y - y * my; diff.z = fabsf( floorf( (y * mz) / 4.0f ) ); - + // bottom-left v = [self originalVertex:a]; v.y = diff.y; v.z += diff.z; [self setVertex:a vertex:v]; - + // upper-left v = [self originalVertex:b]; v.y -= diff.y; v.z -= diff.z; [self setVertex:b vertex:v]; - + // bottom-right v = [self originalVertex:c]; v.y = diff.y; v.z += diff.z; [self setVertex:c vertex:v]; - + // upper-right v = [self originalVertex:d]; v.y -= diff.y; @@ -273,7 +273,7 @@ -(id)initWithPosition:(CGPoint)pos radius:(float)r grid:(ccGridSize)gSize durati lensEffect_ = 0.7f; dirty_ = YES; } - + return self; } @@ -287,9 +287,6 @@ -(void) setPosition:(CGPoint)pos { if( ! CGPointEqualToPoint(pos, position_) ) { position_ = pos; - positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR(); - positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR(); - dirty_ = YES; } } @@ -298,21 +295,21 @@ -(CGPoint) position { return position_; } - + -(void)update:(ccTime)time { if ( dirty_ ) { int i, j; - + for( i = 0; i < gridSize_.x+1; i++ ) { for( j = 0; j < gridSize_.y+1; j++ ) { ccVertex3F v = [self originalVertex:ccg(i,j)]; - CGPoint vect = ccpSub(positionInPixels_, ccp(v.x,v.y)); + CGPoint vect = ccpSub(position_, ccp(v.x,v.y)); CGFloat r = ccpLength(vect); - + if ( r < radius_ ) { r = radius_ - r; @@ -320,7 +317,7 @@ -(void)update:(ccTime)time if ( pre_log == 0 ) pre_log = 0.001f; float l = logf(pre_log) * lensEffect_; float new_r = expf( l ) * radius_; - + if ( ccpLength(vect) > 0 ) { vect = ccpNormalize(vect); @@ -328,11 +325,11 @@ -(void)update:(ccTime)time v.z += ccpLength(new_vect) * lensEffect_; } } - + [self setVertex:ccg(i,j) vertex:v]; } } - + dirty_ = NO; } } @@ -364,7 +361,7 @@ -(id)initWithPosition:(CGPoint)pos radius:(float)r waves:(int)wav amplitude:(flo amplitude_ = amp; amplitudeRate_ = 1.0f; } - + return self; } @@ -376,8 +373,6 @@ -(CGPoint) position -(void) setPosition:(CGPoint)pos { position_ = pos; - positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR(); - positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR(); } -(id) copyWithZone: (NSZone*) zone @@ -390,22 +385,22 @@ -(id) copyWithZone: (NSZone*) zone -(void)update:(ccTime)time { int i, j; - + for( i = 0; i < (gridSize_.x+1); i++ ) { for( j = 0; j < (gridSize_.y+1); j++ ) { ccVertex3F v = [self originalVertex:ccg(i,j)]; - CGPoint vect = ccpSub(positionInPixels_, ccp(v.x,v.y)); + CGPoint vect = ccpSub(position_, ccp(v.x,v.y)); CGFloat r = ccpLength(vect); - + if ( r < radius_ ) { r = radius_ - r; CGFloat rate = powf( r / radius_, 2); v.z += (sinf( time*(CGFloat)M_PI*waves_*2 + r * 0.1f) * amplitude_ * amplitudeRate_ * rate ); } - + [self setVertex:ccg(i,j) vertex:v]; } } @@ -432,7 +427,7 @@ -(id)initWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gSize duration:(c randrange = range; shakeZ = sz; } - + return self; } @@ -445,7 +440,7 @@ -(id) copyWithZone: (NSZone*) zone -(void)update:(ccTime)time { int i, j; - + for( i = 0; i < (gridSize_.x+1); i++ ) { for( j = 0; j < (gridSize_.y+1); j++ ) @@ -455,7 +450,7 @@ -(void)update:(ccTime)time v.y += ( rand() % (randrange*2) ) - randrange; if( shakeZ ) v.z += ( rand() % (randrange*2) ) - randrange; - + [self setVertex:ccg(i,j) vertex:v]; } } @@ -486,14 +481,14 @@ -(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration amplitude = amp; amplitudeRate = 1.0f; } - + return self; } -(void)update:(ccTime)time { int i, j; - + for( i = 1; i < gridSize_.x; i++ ) { for( j = 1; j < gridSize_.y; j++ ) @@ -504,7 +499,7 @@ -(void)update:(ccTime)time [self setVertex:ccg(i,j) vertex:v]; } } -} +} -(id) copyWithZone: (NSZone*) zone { @@ -539,26 +534,26 @@ -(id)initWithWaves:(int)wav amplitude:(float)amp horizontal:(BOOL)h vertical:(BO horizontal = h; vertical = v; } - + return self; } -(void)update:(ccTime)time { int i, j; - + for( i = 0; i < (gridSize_.x+1); i++ ) { for( j = 0; j < (gridSize_.y+1); j++ ) { ccVertex3F v = [self originalVertex:ccg(i,j)]; - + if ( vertical ) v.x = (v.x + (sinf(time*(CGFloat)M_PI*waves*2 + v.y * .01f) * amplitude * amplitudeRate)); - + if ( horizontal ) v.y = (v.y + (sinf(time*(CGFloat)M_PI*waves*2 + v.x * .01f) * amplitude * amplitudeRate)); - + [self setVertex:ccg(i,j) vertex:v]; } } @@ -596,15 +591,13 @@ -(id)initWithPosition:(CGPoint)pos twirls:(int)t amplitude:(float)amp grid:(ccGr amplitude_ = amp; amplitudeRate_ = 1.0f; } - + return self; } -(void) setPosition:(CGPoint)pos { position_ = pos; - positionInPixels_.x = pos.x * CC_CONTENT_SCALE_FACTOR(); - positionInPixels_.y = pos.y * CC_CONTENT_SCALE_FACTOR(); } -(CGPoint) position @@ -615,31 +608,31 @@ -(CGPoint) position -(void)update:(ccTime)time { int i, j; - CGPoint c = positionInPixels_; - + CGPoint c = position_; + for( i = 0; i < (gridSize_.x+1); i++ ) { for( j = 0; j < (gridSize_.y+1); j++ ) { ccVertex3F v = [self originalVertex:ccg(i,j)]; - + CGPoint avg = ccp(i-(gridSize_.x/2.0f), j-(gridSize_.y/2.0f)); CGFloat r = ccpLength( avg ); - + CGFloat amp = 0.1f * amplitude_ * amplitudeRate_; CGFloat a = r * cosf( (CGFloat)M_PI/2.0f + time * (CGFloat)M_PI * twirls_ * 2 ) * amp; - + float cosA = cosf(a); float sinA = sinf(a); - + CGPoint d = { sinA * (v.y-c.y) + cosA * (v.x-c.x), cosA * (v.y-c.y) - sinA * (v.x-c.x) }; - + v.x = c.x + d.x; v.y = c.y + d.y; - + [self setVertex:ccg(i,j) vertex:v]; } } diff --git a/cocos2d/cocos2d/CCActionInstant.h b/cocos2d/cocos2d/CCActionInstant.h old mode 100644 new mode 100755 index 5a1bc2d..928732b --- a/cocos2d/cocos2d/CCActionInstant.h +++ b/cocos2d/cocos2d/CCActionInstant.h @@ -10,10 +10,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,7 +29,7 @@ /** Instant actions are immediate actions. They don't have a duration like the CCIntervalAction actions. -*/ +*/ @interface CCActionInstant : CCFiniteTimeAction { } @@ -156,8 +156,6 @@ typedef void (*CC_CALLBACK_ND)(id, SEL, id, void *); #pragma mark Blocks Support -#if NS_BLOCKS_AVAILABLE - /** Executes a callback using a block. */ @interface CCCallBlock : CCActionInstant @@ -202,4 +200,28 @@ typedef void (*CC_CALLBACK_ND)(id, SEL, id, void *); -(void) execute; @end -#endif +/** Executes a callback using a block with a single NSObject parameter. + @since v2.0 + */ +@interface CCCallBlockO : CCActionInstant +{ + void (^block_)(id object); + id object_; +} + +/** object to be passed to the block */ +@property (nonatomic,retain) id object; + +/** creates the action with the specified block, to be used as a callback. + The block will be "copied". + */ ++(id) actionWithBlock:(void(^)(id object))block object:(id)object; + +/** initialized the action with the specified block, to be used as a callback. + The block will be "copied". + */ +-(id) initWithBlock:(void(^)(id object))block object:(id)object; + +/** executes the callback */ +-(void) execute; +@end diff --git a/cocos2d/cocos2d/CCActionInstant.m b/cocos2d/cocos2d/CCActionInstant.m old mode 100644 new mode 100755 index e7f6fad..9fbda59 --- a/cocos2d/cocos2d/CCActionInstant.m +++ b/cocos2d/cocos2d/CCActionInstant.m @@ -10,10 +10,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,7 +25,6 @@ */ -#import "CCBlockSupport.h" #import "CCActionInstant.h" #import "CCNode.h" #import "CCSprite.h" @@ -40,9 +39,9 @@ @implementation CCActionInstant -(id) init { - if( (self=[super init]) ) + if( (self=[super init]) ) duration_ = 0; - + return self; } @@ -64,7 +63,7 @@ -(void) step: (ccTime) dt -(void) update: (ccTime) t { - // ignore + // nothing } -(CCFiniteTimeAction*) reverse @@ -79,9 +78,8 @@ -(CCFiniteTimeAction*) reverse #pragma mark CCShow @implementation CCShow --(void) startWithTarget:(id)aTarget +-(void) update:(ccTime)time { - [super startWithTarget:aTarget]; ((CCNode *)target_).visible = YES; } @@ -97,9 +95,8 @@ -(CCFiniteTimeAction*) reverse #pragma mark CCHide @implementation CCHide --(void) startWithTarget:(id)aTarget +-(void) update:(ccTime)time { - [super startWithTarget:aTarget]; ((CCNode *)target_).visible = NO; } @@ -115,9 +112,8 @@ -(CCFiniteTimeAction*) reverse #pragma mark CCToggleVisibility @implementation CCToggleVisibility --(void) startWithTarget:(id)aTarget +-(void) update:(ccTime)time { - [super startWithTarget:aTarget]; ((CCNode *)target_).visible = !((CCNode *)target_).visible; } @end @@ -137,14 +133,13 @@ -(id) initWithFlipX:(BOOL)x { if(( self=[super init])) flipX = x; - + return self; } --(void) startWithTarget:(id)aTarget +-(void) update:(ccTime)time { - [super startWithTarget:aTarget]; - [(CCSprite*)aTarget setFlipX:flipX]; + [(CCSprite*)target_ setFlipX:flipX]; } -(CCFiniteTimeAction*) reverse @@ -174,14 +169,13 @@ -(id) initWithFlipY:(BOOL)y { if(( self=[super init])) flipY = y; - + return self; } --(void) startWithTarget:(id)aTarget +-(void) update:(ccTime)time { - [super startWithTarget:aTarget]; - [(CCSprite*)aTarget setFlipY:flipY]; + [(CCSprite*)target_ setFlipY:flipY]; } -(CCFiniteTimeAction*) reverse @@ -212,7 +206,7 @@ -(id) initWithPosition: (CGPoint) pos { if( (self=[super init]) ) position = pos; - + return self; } @@ -222,9 +216,8 @@ -(id) copyWithZone: (NSZone*) zone return copy; } --(void) startWithTarget:(id)aTarget +-(void) update:(ccTime)time { - [super startWithTarget:aTarget]; ((CCNode *)target_).position = position; } @@ -255,11 +248,10 @@ -(id) initWithTarget: (id) t selector:(SEL) s -(NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i | target = %@ | selector = %@>", + return [NSString stringWithFormat:@"<%@ = %p | Tag = %ld | selector = %@>", [self class], self, - tag_, - [targetCallback_ class], + (long)tag_, NSStringFromSelector(selector_) ]; } @@ -276,9 +268,8 @@ -(id) copyWithZone: (NSZone*) zone return copy; } --(void) startWithTarget:(id)aTarget +-(void) update:(ccTime)time { - [super startWithTarget:aTarget]; [self execute]; } @@ -359,7 +350,7 @@ -(id) initWithTarget:(id) t selector:(SEL) s object:(id)object { if( (self=[super initWithTarget:t selector:s] ) ) self.object = object; - + return self; } @@ -387,8 +378,6 @@ -(void) execute #pragma mark - #pragma mark Blocks -#if NS_BLOCKS_AVAILABLE - #pragma mark CCCallBlock @implementation CCCallBlock @@ -402,7 +391,7 @@ -(id) initWithBlock:(void(^)())block { if ((self = [super init])) block_ = [block copy]; - + return self; } @@ -412,9 +401,8 @@ -(id) copyWithZone: (NSZone*) zone return copy; } --(void) startWithTarget:(id)aTarget +-(void) update:(ccTime)time { - [super startWithTarget:aTarget]; [self execute]; } @@ -444,7 +432,7 @@ -(id) initWithBlock:(void(^)(CCNode *node))block { if ((self = [super init])) block_ = [block copy]; - + return self; } @@ -454,9 +442,8 @@ -(id) copyWithZone: (NSZone*) zone return copy; } --(void) startWithTarget:(id)aTarget +-(void) update:(ccTime)time { - [super startWithTarget:aTarget]; [self execute]; } @@ -473,5 +460,50 @@ -(void) dealloc @end +#pragma mark CCCallBlockO + +@implementation CCCallBlockO + +@synthesize object=object_; + ++(id) actionWithBlock:(void(^)(id object))block object:(id)object +{ + return [[[self alloc] initWithBlock:block object:object] autorelease]; +} + +-(id) initWithBlock:(void(^)(id object))block object:(id)object +{ + if ((self = [super init])) { + block_ = [block copy]; + object_ = [object retain]; + } + + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCActionInstant *copy = [[[self class] allocWithZone: zone] initWithBlock:block_]; + return copy; +} + +-(void) update:(ccTime)time +{ + [self execute]; +} + +-(void) execute +{ + block_(object_); +} + +-(void) dealloc +{ + [object_ release]; + [block_ release]; + + [super dealloc]; +} + +@end -#endif // NS_BLOCKS_AVAILABLE diff --git a/cocos2d/cocos2d/CCActionInterval.h b/cocos2d/cocos2d/CCActionInterval.h old mode 100644 new mode 100755 index c667963..ac3b38d --- a/cocos2d/cocos2d/CCActionInterval.h +++ b/cocos2d/cocos2d/CCActionInterval.h @@ -10,10 +10,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -44,7 +44,7 @@ For example, you can simulate a Ping Pong effect running the action normally and then running it again in Reverse mode. Example: - + CCAction * pingPongAction = [CCSequence actions: action, [action reverse], nil]; */ @interface CCActionInterval: CCFiniteTimeAction @@ -77,7 +77,7 @@ then running it again in Reverse mode. /** helper contructor to create an array of sequenceable actions */ +(id) actions: (CCFiniteTimeAction*) action1, ... NS_REQUIRES_NIL_TERMINATION; /** helper contructor to create an array of sequenceable actions given an array */ -+(id) actionsWithArray: (NSArray*) actions; ++(id) actionWithArray: (NSArray*) arrayOfActions; /** creates the action */ +(id) actionOne:(CCFiniteTimeAction*)actionOne two:(CCFiniteTimeAction*)actionTwo; /** initializes the action */ @@ -92,13 +92,16 @@ then running it again in Reverse mode. { NSUInteger times_; NSUInteger total_; + ccTime nextDt_; + BOOL isActionInstant_; CCFiniteTimeAction *innerAction_; } /** Inner action */ @property (nonatomic,readwrite,retain) CCFiniteTimeAction *innerAction; -/** creates a CCRepeat action. Times is an unsigned integer between 1 and MAX_UINT */ +/** creates a CCRepeat action. Times is an unsigned integer between 1 and MAX_UINT. + */ +(id) actionWithAction:(CCFiniteTimeAction*)action times: (NSUInteger)times; /** initializes a CCRepeat action. Times is an unsigned integer between 1 and MAX_UINT */ -(id) initWithAction:(CCFiniteTimeAction*)action times: (NSUInteger)times; @@ -114,7 +117,7 @@ then running it again in Reverse mode. /** helper constructor to create an array of spawned actions */ +(id) actions: (CCFiniteTimeAction*) action1, ... NS_REQUIRES_NIL_TERMINATION; /** helper contructor to create an array of spawned actions given an array */ -+(id) actionsWithArray: (NSArray*) actions; ++(id) actionWithArray: (NSArray*) arrayOfActions; /** creates the Spawn action */ +(id) actionOne: (CCFiniteTimeAction*) one two:(CCFiniteTimeAction*) two; /** initializes the Spawn action with the 2 actions to spawn */ @@ -124,7 +127,7 @@ then running it again in Reverse mode. /** Rotates a CCNode object to a certain angle by modifying it's rotation attribute. The direction will be decided by the shortest angle. -*/ +*/ @interface CCRotateTo : CCActionInterval { float dstAngle_; @@ -137,7 +140,7 @@ then running it again in Reverse mode. -(id) initWithDuration:(ccTime)duration angle:(float)angle; @end -/** Rotates a CCNode object clockwise a number of degrees by modiying it's rotation attribute. +/** Rotates a CCNode object clockwise a number of degrees by modiying its rotation attribute. */ @interface CCRotateBy : CCActionInterval { @@ -150,7 +153,7 @@ then running it again in Reverse mode. -(id) initWithDuration:(ccTime)duration angle:(float)deltaAngle; @end -/** Moves a CCNode object to the position x,y. x and y are absolute coordinates by modifying it's position attribute. +/** Moves a CCNode object to the position x,y. x and y are absolute coordinates by modifying its position attribute. */ @interface CCMoveTo : CCActionInterval { @@ -164,10 +167,10 @@ then running it again in Reverse mode. -(id) initWithDuration:(ccTime)duration position:(CGPoint)position; @end -/** Moves a CCNode object x,y pixels by modifying it's position attribute. +/** Moves a CCNode object x,y pixels by modifying its position attribute. x and y are relative to the position of the object. Duration is is seconds. -*/ +*/ @interface CCMoveBy : CCMoveTo { } @@ -177,7 +180,7 @@ then running it again in Reverse mode. -(id) initWithDuration: (ccTime)duration position:(CGPoint)deltaPosition; @end -/** Skews a CCNode object to given angles by modifying it's skewX and skewY attributes +/** Skews a CCNode object to given angles by modifying its skewX and skewY attributes @since v1.0 */ @interface CCSkewTo : CCActionInterval @@ -205,7 +208,7 @@ then running it again in Reverse mode. } @end -/** Moves a CCNode object simulating a parabolic jump movement by modifying it's position attribute. +/** Moves a CCNode object simulating a parabolic jump movement by modifying its position attribute. */ @interface CCJumpBy : CCActionInterval { @@ -220,8 +223,8 @@ then running it again in Reverse mode. -(id) initWithDuration: (ccTime)duration position:(CGPoint)position height:(ccTime)height jumps:(NSUInteger)jumps; @end -/** Moves a CCNode object to a parabolic position simulating a jump movement by modifying it's position attribute. -*/ +/** Moves a CCNode object to a parabolic position simulating a jump movement by modifying its position attribute. +*/ @interface CCJumpTo : CCJumpBy { } @@ -261,7 +264,7 @@ typedef struct _ccBezierConfig { } @end -/** Scales a CCNode object to a zoom factor by modifying it's scale attribute. +/** Scales a CCNode object to a zoom factor by modifying its scale attribute. @warning This action doesn't support "reverse" */ @interface CCScaleTo : CCActionInterval @@ -285,14 +288,14 @@ typedef struct _ccBezierConfig { -(id) initWithDuration: (ccTime)duration scaleX:(float) sx scaleY:(float)sy; @end -/** Scales a CCNode object a zoom factor by modifying it's scale attribute. +/** Scales a CCNode object a zoom factor by modifying its scale attribute. */ @interface CCScaleBy : CCScaleTo { } @end -/** Blinks a CCNode object by modifying it's visible attribute +/** Blinks a CCNode object by modifying its visible attribute */ @interface CCBlink : CCActionInterval { @@ -371,7 +374,7 @@ typedef struct _ccBezierConfig { @end /** Executes an action in reverse order, from time=duration to time=0 - + @warning Use this action carefully. This action is not sequenceable. Use it as the default "reversed" method of your own actions, but using it outside the "reversed" @@ -393,29 +396,36 @@ typedef struct _ccBezierConfig { /** Animates a sprite given the name of an Animation */ @interface CCAnimate : CCActionInterval { - CCAnimation *animation_; - id origFrame_; - BOOL restoreOriginalFrame_; + NSMutableArray *splitTimes_; + NSInteger nextFrame_; + CCAnimation *animation_; + id origFrame_; + NSUInteger executedLoops_; } /** animation used for the animage */ @property (readwrite,nonatomic,retain) CCAnimation * animation; /** creates the action with an Animation and will restore the original frame when the animation is over */ -+(id) actionWithAnimation:(CCAnimation*) a; ++(id) actionWithAnimation:(CCAnimation*)animation; /** initializes the action with an Animation and will restore the original frame when the animtion is over */ --(id) initWithAnimation:(CCAnimation*) a; -/** creates the action with an Animation */ -+(id) actionWithAnimation:(CCAnimation*) a restoreOriginalFrame:(BOOL)b; -/** initializes the action with an Animation */ --(id) initWithAnimation:(CCAnimation*) a restoreOriginalFrame:(BOOL)b; -/** creates an action with a duration, animation and depending of the restoreOriginalFrame, it will restore the original frame or not. - The 'delay' parameter of the animation will be overrided by the duration parameter. - @since v0.99.0 - */ -+(id) actionWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)b; -/** initializes an action with a duration, animation and depending of the restoreOriginalFrame, it will restore the original frame or not. - The 'delay' parameter of the animation will be overrided by the duration parameter. - @since v0.99.0 +-(id) initWithAnimation:(CCAnimation*)animation; +@end + +/** Overrides the target of an action so that it always runs on the target + * specified at action creation rather than the one specified by runAction. */ --(id) initWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)b; +@interface CCTargetedAction : CCActionInterval +{ + id forcedTarget_; + CCFiniteTimeAction* action_; +} +/** This is the target that the action will be forced to run with */ +@property(readwrite,nonatomic,retain) id forcedTarget; + +/** Create an action with the specified action and forced target */ ++ (id) actionWithTarget:(id) target action:(CCFiniteTimeAction*) action; + +/** Init an action with the specified action and forced target */ +- (id) initWithTarget:(id) target action:(CCFiniteTimeAction*) action; + @end diff --git a/cocos2d/cocos2d/CCActionInterval.m b/cocos2d/cocos2d/CCActionInterval.m old mode 100644 new mode 100755 index 17bef40..b8a78d3 --- a/cocos2d/cocos2d/CCActionInterval.m +++ b/cocos2d/cocos2d/CCActionInterval.m @@ -10,10 +10,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,6 +27,7 @@ #import "CCActionInterval.h" +#import "CCActionInstant.h" #import "CCSprite.h" #import "CCSpriteFrame.h" #import "CCAnimation.h" @@ -36,8 +37,7 @@ // // IntervalAction // -#pragma mark - -#pragma mark IntervalAction +#pragma mark - CCIntervalAction @implementation CCActionInterval @synthesize elapsed = elapsed_; @@ -58,7 +58,7 @@ -(id) initWithDuration: (ccTime) d { if( (self=[super init]) ) { duration_ = d; - + // prevent division by 0 // This comparison could be in step:, but it might decrease the performance // by 3% in heavy based action games. @@ -89,7 +89,13 @@ -(void) step: (ccTime) dt } else elapsed_ += dt; - [self update: MIN(1, elapsed_/duration_)]; + + [self update: MAX(0, // needed for rewind. elapsed could be negative + MIN(1, elapsed_/ + MAX(duration_,FLT_EPSILON) // division by 0 + ) + ) + ]; } -(void) startWithTarget:(id)aTarget @@ -109,8 +115,7 @@ - (CCActionInterval*) reverse // // Sequence // -#pragma mark - -#pragma mark Sequence +#pragma mark - CCSequence @implementation CCSequence +(id) actions: (CCFiniteTimeAction*) action1, ... { @@ -131,7 +136,7 @@ +(id) actions: (CCFiniteTimeAction*) action1, ... return prev; } -+(id) actionsWithArray: (NSArray*) actions ++(id) actionWithArray: (NSArray*) actions { CCFiniteTimeAction *prev = [actions objectAtIndex:0]; @@ -142,7 +147,7 @@ +(id) actionsWithArray: (NSArray*) actions } +(id) actionOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two -{ +{ return [[[self alloc] initOne:one two:two ] autorelease]; } @@ -151,15 +156,15 @@ -(id) initOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two NSAssert( one!=nil && two!=nil, @"Sequence: arguments must be non-nil"); NSAssert( one!=actions_[0] && one!=actions_[1], @"Sequence: re-init using the same parameters is not supported"); NSAssert( two!=actions_[1] && two!=actions_[0], @"Sequence: re-init using the same parameters is not supported"); - + ccTime d = [one duration] + [two duration]; if( (self=[super initWithDuration: d]) ) { - + // XXX: Supports re-init without leaking. Fails if one==one_ || two==two_ [actions_[0] release]; [actions_[1] release]; - + actions_[0] = [one retain]; actions_[1] = [two retain]; } @@ -182,15 +187,17 @@ -(void) dealloc -(void) startWithTarget:(id)aTarget { - [super startWithTarget:aTarget]; - split_ = [actions_[0] duration] / duration_; + [super startWithTarget:aTarget]; + split_ = [actions_[0] duration] / MAX(duration_, FLT_EPSILON); last_ = -1; } -(void) stop { - [actions_[0] stop]; - [actions_[1] stop]; + // Issue #1305 + if( last_ != - 1) + [actions_[last_] stop]; + [super stop]; } @@ -199,33 +206,43 @@ -(void) update: (ccTime) t int found = 0; ccTime new_t = 0.0f; - if( t >= split_ ) { - found = 1; - if ( split_ == 1 ) - new_t = 1; - else - new_t = (t-split_) / (1 - split_ ); - } else { + if( t < split_ ) { + // action[0] found = 0; if( split_ != 0 ) new_t = t / split_; else new_t = 1; + + } else { + // action[1] + found = 1; + if ( split_ == 1 ) + new_t = 1; + else + new_t = (t-split_) / (1 - split_ ); } - if (last_ == -1 && found==1) { - [actions_[0] startWithTarget:target_]; - [actions_[0] update:1.0f]; - [actions_[0] stop]; - } - - if (last_ != found ) { - if( last_ != -1 ) { - [actions_[last_] update: 1.0f]; - [actions_[last_] stop]; + if ( found==1 ) { + + if( last_ == -1 ) { + // action[0] was skipped, execute it. + [actions_[0] startWithTarget:target_]; + [actions_[0] update:1.0f]; + [actions_[0] stop]; + } + else if( last_ == 0 ) + { + // switching to action 1. stop action 0. + [actions_[0] update: 1.0f]; + [actions_[0] stop]; } - [actions_[found] startWithTarget:target_]; } + + // New action. Start it. + if( found != last_ ) + [actions_[found] startWithTarget:target_]; + [actions_[found] update: new_t]; last_ = found; } @@ -239,8 +256,7 @@ - (CCActionInterval *) reverse // // Repeat // -#pragma mark - -#pragma mark CCRepeat +#pragma mark - CCRepeat @implementation CCRepeat @synthesize innerAction=innerAction_; @@ -256,7 +272,10 @@ -(id) initWithAction:(CCFiniteTimeAction*)action times:(NSUInteger)times if( (self=[super initWithDuration: d ]) ) { times_ = times; self.innerAction = action; + isActionInstant_ = ([action isKindOfClass:[CCActionInstant class]]) ? YES : NO; + //a instant action needs to be executed one time less in the update method since it uses startWithTarget to execute the action + if (isActionInstant_) times_ -=1; total_ = 0; } return self; @@ -277,49 +296,59 @@ -(void) dealloc -(void) startWithTarget:(id)aTarget { total_ = 0; + nextDt_ = [innerAction_ duration]/duration_; [super startWithTarget:aTarget]; [innerAction_ startWithTarget:aTarget]; } -(void) stop -{ +{ [innerAction_ stop]; [super stop]; } -// issue #80. Instead of hooking step:, hook update: since it can be called by any -// container action like Repeat, Sequence, AccelDeccel, etc.. +// issue #80. Instead of hooking step:, hook update: since it can be called by any +// container action like CCRepeat, CCSequence, CCEase, etc.. -(void) update:(ccTime) dt { - ccTime t = dt * times_; - if( t > total_+1 ) { - [innerAction_ update:1.0f]; - total_++; - [innerAction_ stop]; - [innerAction_ startWithTarget:target_]; - - // repeat is over ? - if( total_== times_ ) - // so, set it in the original position - [innerAction_ update:0]; - else { - // no ? start next repeat with the right update - // to prevent jerk (issue #390) - [innerAction_ update: t-total_]; - } + if (dt >= nextDt_) + { + while (dt > nextDt_ && total_ < times_) + { - } else { + [innerAction_ update:1.0f]; + total_++; + + [innerAction_ stop]; + [innerAction_ startWithTarget:target_]; + nextDt_ += [innerAction_ duration]/duration_; + } - float r = fmodf(t, 1.0f); + // fix for issue #1288, incorrect end value of repeat + if(dt >= 1.0f && total_ < times_) + { + total_++; + } - // fix last repeat position - // else it could be 0. - if( dt== 1.0f) { - r = 1.0f; - total_++; // this is the added line + // don't set a instantaction back or update it, it has no use because it has no duration + if (!isActionInstant_) + { + if (total_ == times_) + { + [innerAction_ update:1]; + [innerAction_ stop]; + } + else + { + // issue #390 prevent jerk, use right update + [innerAction_ update:dt - (nextDt_ - innerAction_.duration/duration_)]; + } } - [innerAction_ update: MIN(r,1)]; + } + else + { + [innerAction_ update:fmodf(dt * times_,1.0f)]; } } @@ -337,18 +366,17 @@ - (CCActionInterval *) reverse // // Spawn // -#pragma mark - -#pragma mark Spawn +#pragma mark - CCSpawn @implementation CCSpawn +(id) actions: (CCFiniteTimeAction*) action1, ... { va_list params; va_start(params,action1); - + CCFiniteTimeAction *now; CCFiniteTimeAction *prev = action1; - + while( action1 ) { now = va_arg(params,CCFiniteTimeAction*); if ( now ) @@ -360,18 +388,18 @@ +(id) actions: (CCFiniteTimeAction*) action1, ... return prev; } -+(id) actionsWithArray: (NSArray*) actions ++(id) actionWithArray: (NSArray*) actions { CCFiniteTimeAction *prev = [actions objectAtIndex:0]; - + for (NSUInteger i = 1; i < [actions count]; i++) prev = [self actionOne:prev two:[actions objectAtIndex:i]]; - + return prev; } +(id) actionOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two -{ +{ return [[[self alloc] initOne:one two:two ] autorelease]; } @@ -382,8 +410,8 @@ -(id) initOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two NSAssert( two!=two_ && two!=one_, @"Spawn: reinit using same parameters is not supported"); ccTime d1 = [one duration]; - ccTime d2 = [two duration]; - + ccTime d2 = [two duration]; + if( (self=[super initWithDuration: MAX(d1,d2)] ) ) { // XXX: Supports re-init without leaking. Fails if one==one_ || two==two_ @@ -397,7 +425,7 @@ -(id) initOne: (CCFiniteTimeAction*) one two: (CCFiniteTimeAction*) two two_ = [CCSequence actionOne:two two:[CCDelayTime actionWithDuration: (d1-d2)] ]; else if( d1 < d2) one_ = [CCSequence actionOne:one two: [CCDelayTime actionWithDuration: (d2-d1)] ]; - + [one_ retain]; [two_ retain]; } @@ -446,12 +474,11 @@ - (CCActionInterval *) reverse // // RotateTo // -#pragma mark - -#pragma mark RotateTo +#pragma mark - CCRotateTo @implementation CCRotateTo +(id) actionWithDuration: (ccTime) t angle:(float) a -{ +{ return [[[self alloc] initWithDuration:t angle:a ] autorelease]; } @@ -459,7 +486,7 @@ -(id) initWithDuration: (ccTime) t angle:(float) a { if( (self=[super initWithDuration: t]) ) dstAngle_ = a; - + return self; } @@ -472,13 +499,13 @@ -(id) copyWithZone: (NSZone*) zone -(void) startWithTarget:(CCNode *)aTarget { [super startWithTarget:aTarget]; - + startAngle_ = [target_ rotation]; if (startAngle_ > 0) startAngle_ = fmodf(startAngle_, 360.0f); else startAngle_ = fmodf(startAngle_, -360.0f); - + diffAngle_ =dstAngle_ - startAngle_; if (diffAngle_ > 180) diffAngle_ -= 360; @@ -495,12 +522,11 @@ -(void) update: (ccTime) t // // RotateBy // -#pragma mark - -#pragma mark RotateBy +#pragma mark - CCRotateBy @implementation CCRotateBy +(id) actionWithDuration: (ccTime) t angle:(float) a -{ +{ return [[[self alloc] initWithDuration:t angle:a ] autorelease]; } @@ -508,7 +534,7 @@ -(id) initWithDuration: (ccTime) t angle:(float) a { if( (self=[super initWithDuration: t]) ) angle_ = a; - + return self; } @@ -525,7 +551,7 @@ -(void) startWithTarget:(id)aTarget } -(void) update: (ccTime) t -{ +{ // XXX: shall I add % 360 [target_ setRotation: (startAngle_ +angle_ * t )]; } @@ -540,12 +566,11 @@ -(CCActionInterval*) reverse // // MoveTo // -#pragma mark - -#pragma mark MoveTo +#pragma mark - CCMoveTo @implementation CCMoveTo +(id) actionWithDuration: (ccTime) t position: (CGPoint) p -{ +{ return [[[self alloc] initWithDuration:t position:p ] autorelease]; } @@ -553,7 +578,7 @@ -(id) initWithDuration: (ccTime) t position: (CGPoint) p { if( (self=[super initWithDuration: t]) ) endPosition_ = p; - + return self; } @@ -571,7 +596,7 @@ -(void) startWithTarget:(CCNode *)aTarget } -(void) update: (ccTime) t -{ +{ [target_ setPosition: ccp( (startPosition_.x + delta_.x * t ), (startPosition_.y + delta_.y * t ) )]; } @end @@ -579,12 +604,11 @@ -(void) update: (ccTime) t // // MoveBy // -#pragma mark - -#pragma mark MoveBy +#pragma mark - CCMoveBy @implementation CCMoveBy +(id) actionWithDuration: (ccTime) t position: (CGPoint) p -{ +{ return [[[self alloc] initWithDuration:t position:p ] autorelease]; } @@ -592,7 +616,7 @@ -(id) initWithDuration: (ccTime) t position: (CGPoint) p { if( (self=[super initWithDuration: t]) ) delta_ = p; - + return self; } @@ -619,18 +643,17 @@ -(CCActionInterval*) reverse // // SkewTo // -#pragma mark - -#pragma mark SkewTo +#pragma mark - CCSkewTo @implementation CCSkewTo -+(id) actionWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy ++(id) actionWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy { return [[[self alloc] initWithDuration: t skewX:sx skewY:sy] autorelease]; } --(id) initWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy +-(id) initWithDuration:(ccTime)t skewX:(float)sx skewY:(float)sy { - if( (self=[super initWithDuration:t]) ) { + if( (self=[super initWithDuration:t]) ) { endSkewX_ = sx; endSkewY_ = sy; } @@ -646,32 +669,32 @@ -(id) copyWithZone: (NSZone*) zone -(void) startWithTarget:(CCNode *)aTarget { [super startWithTarget:aTarget]; - + startSkewX_ = [target_ skewX]; - + if (startSkewX_ > 0) startSkewX_ = fmodf(startSkewX_, 180.0f); else startSkewX_ = fmodf(startSkewX_, -180.0f); - + deltaX_ = endSkewX_ - startSkewX_; - + if ( deltaX_ > 180 ) { deltaX_ -= 360; } if ( deltaX_ < -180 ) { deltaX_ += 360; } - + startSkewY_ = [target_ skewY]; - + if (startSkewY_ > 0) startSkewY_ = fmodf(startSkewY_, 360.0f); else startSkewY_ = fmodf(startSkewY_, -360.0f); - + deltaY_ = endSkewY_ - startSkewY_; - + if ( deltaY_ > 180 ) { deltaY_ -= 360; } @@ -691,11 +714,13 @@ -(void) update: (ccTime) t // // CCSkewBy // +#pragma mark - CCSkewBy + @implementation CCSkewBy -(id) initWithDuration:(ccTime)t skewX:(float)deltaSkewX skewY:(float)deltaSkewY { - if( (self=[super initWithDuration:t skewX:deltaSkewX skewY:deltaSkewY]) ) { + if( (self=[super initWithDuration:t skewX:deltaSkewX skewY:deltaSkewY]) ) { skewX_ = deltaSkewX; skewY_ = deltaSkewY; } @@ -721,8 +746,7 @@ -(CCActionInterval*) reverse // // JumpBy // -#pragma mark - -#pragma mark JumpBy +#pragma mark - CCJumpBy @implementation CCJumpBy +(id) actionWithDuration: (ccTime) t position: (CGPoint) pos height: (ccTime) h jumps:(NSUInteger)j @@ -758,15 +782,15 @@ -(void) update: (ccTime) t // ccTime y = height * fabsf( sinf(t * (CGFloat)M_PI * jumps ) ); // y += delta.y * t; // ccTime x = delta.x * t; -// [target setPosition: ccp( startPosition.x + x, startPosition.y + y )]; - +// [target setPosition: ccp( startPosition.x + x, startPosition.y + y )]; + // parabolic jump (since v0.8.2) ccTime frac = fmodf( t * jumps_, 1.0f ); ccTime y = height_ * 4 * frac * (1 - frac); y += delta_.y * t; ccTime x = delta_.x * t; [target_ setPosition: ccp( startPosition_.x + x, startPosition_.y + y )]; - + } -(CCActionInterval*) reverse @@ -778,8 +802,7 @@ -(CCActionInterval*) reverse // // JumpTo // -#pragma mark - -#pragma mark JumpTo +#pragma mark - CCJumpTo @implementation CCJumpTo -(void) startWithTarget:(CCNode *)aTarget @@ -790,17 +813,16 @@ -(void) startWithTarget:(CCNode *)aTarget @end -#pragma mark - -#pragma mark BezierBy +#pragma mark - CCBezierBy // Bezier cubic formula: -// ((1 - t) + t)3 = 1 -// Expands to… -// (1 - t)3 + 3t(1-t)2 + 3t2(1 - t) + t3 = 1 -static inline float bezierat( float a, float b, float c, float d, ccTime t ) +// ((1 - t) + t)3 = 1 +// Expands to… +// (1 - t)3 + 3t(1-t)2 + 3t2(1 - t) + t3 = 1 +static inline CGFloat bezierat( float a, float b, float c, float d, ccTime t ) { - return (powf(1-t,3) * a + - 3*t*(powf(1-t,2))*b + + return (powf(1-t,3) * a + + 3*t*(powf(1-t,2))*b + 3*powf(t,2)*(1-t)*c + powf(t,3)*d ); } @@ -810,7 +832,7 @@ static inline float bezierat( float a, float b, float c, float d, ccTime t ) // @implementation CCBezierBy +(id) actionWithDuration: (ccTime) t bezier:(ccBezierConfig) c -{ +{ return [[[self alloc] initWithDuration:t bezier:c ] autorelease]; } @@ -836,18 +858,18 @@ -(void) startWithTarget:(id)aTarget -(void) update: (ccTime) t { - float xa = 0; - float xb = config_.controlPoint_1.x; - float xc = config_.controlPoint_2.x; - float xd = config_.endPosition.x; - - float ya = 0; - float yb = config_.controlPoint_1.y; - float yc = config_.controlPoint_2.y; - float yd = config_.endPosition.y; - - float x = bezierat(xa, xb, xc, xd, t); - float y = bezierat(ya, yb, yc, yd, t); + CGFloat xa = 0; + CGFloat xb = config_.controlPoint_1.x; + CGFloat xc = config_.controlPoint_2.x; + CGFloat xd = config_.endPosition.x; + + CGFloat ya = 0; + CGFloat yb = config_.controlPoint_1.y; + CGFloat yc = config_.controlPoint_2.y; + CGFloat yd = config_.endPosition.y; + + CGFloat x = bezierat(xa, xb, xc, xd, t); + CGFloat y = bezierat(ya, yb, yc, yd, t); [target_ setPosition: ccpAdd( startPosition_, ccp(x,y))]; } @@ -858,7 +880,7 @@ - (CCActionInterval*) reverse r.endPosition = ccpNeg(config_.endPosition); r.controlPoint_1 = ccpAdd(config_.controlPoint_2, ccpNeg(config_.endPosition)); r.controlPoint_2 = ccpAdd(config_.controlPoint_1, ccpNeg(config_.endPosition)); - + CCBezierBy *action = [[self class] actionWithDuration:[self duration] bezier:r]; return action; } @@ -867,8 +889,7 @@ - (CCActionInterval*) reverse // // BezierTo // -#pragma mark - -#pragma mark BezierTo +#pragma mark - CCBezierTo @implementation CCBezierTo -(void) startWithTarget:(id)aTarget { @@ -883,8 +904,7 @@ -(void) startWithTarget:(id)aTarget // // ScaleTo // -#pragma mark - -#pragma mark ScaleTo +#pragma mark - CCScaleTo @implementation CCScaleTo +(id) actionWithDuration: (ccTime) t scale:(float) s { @@ -900,14 +920,14 @@ -(id) initWithDuration: (ccTime) t scale:(float) s return self; } -+(id) actionWithDuration: (ccTime) t scaleX:(float)sx scaleY:(float)sy ++(id) actionWithDuration: (ccTime) t scaleX:(float)sx scaleY:(float)sy { return [[[self alloc] initWithDuration: t scaleX:sx scaleY:sy] autorelease]; } -(id) initWithDuration: (ccTime) t scaleX:(float)sx scaleY:(float)sy { - if( (self=[super initWithDuration: t]) ) { + if( (self=[super initWithDuration: t]) ) { endScaleX_ = sx; endScaleY_ = sy; } @@ -939,8 +959,7 @@ -(void) update: (ccTime) t // // ScaleBy // -#pragma mark - -#pragma mark ScaleBy +#pragma mark - CCScaleBy @implementation CCScaleBy -(void) startWithTarget:(CCNode *)aTarget { @@ -958,8 +977,7 @@ -(CCActionInterval*) reverse // // Blink // -#pragma mark - -#pragma mark Blink +#pragma mark - CCBlink @implementation CCBlink +(id) actionWithDuration: (ccTime) t blinks: (NSUInteger) b { @@ -970,7 +988,7 @@ -(id) initWithDuration: (ccTime) t blinks: (NSUInteger) b { if( (self=[super initWithDuration: t] ) ) times_ = b; - + return self; } @@ -999,8 +1017,7 @@ -(CCActionInterval*) reverse // // FadeIn // -#pragma mark - -#pragma mark FadeIn +#pragma mark - CCFadeIn @implementation CCFadeIn -(void) update: (ccTime) t { @@ -1016,8 +1033,7 @@ -(CCActionInterval*) reverse // // FadeOut // -#pragma mark - -#pragma mark FadeOut +#pragma mark - CCFadeOut @implementation CCFadeOut -(void) update: (ccTime) t { @@ -1033,8 +1049,7 @@ -(CCActionInterval*) reverse // // FadeTo // -#pragma mark - -#pragma mark FadeTo +#pragma mark - CCFadeTo @implementation CCFadeTo +(id) actionWithDuration: (ccTime) t opacity: (GLubyte) o { @@ -1045,7 +1060,7 @@ -(id) initWithDuration: (ccTime) t opacity: (GLubyte) o { if( (self=[super initWithDuration: t] ) ) toOpacity_ = o; - + return self; } @@ -1070,8 +1085,7 @@ -(void) update: (ccTime) t // // TintTo // -#pragma mark - -#pragma mark TintTo +#pragma mark - CCTintTo @implementation CCTintTo +(id) actionWithDuration:(ccTime)t red:(GLubyte)r green:(GLubyte)g blue:(GLubyte)b { @@ -1082,7 +1096,7 @@ -(id) initWithDuration: (ccTime) t red:(GLubyte)r green:(GLubyte)g blue:(GLubyte { if( (self=[super initWithDuration:t] ) ) to_ = ccc3(r,g,b); - + return self; } @@ -1095,7 +1109,7 @@ -(id) copyWithZone: (NSZone*) zone -(void) startWithTarget:(id)aTarget { [super startWithTarget:aTarget]; - + id tn = (id) target_; from_ = [tn color]; } @@ -1110,8 +1124,7 @@ -(void) update: (ccTime) t // // TintBy // -#pragma mark - -#pragma mark TintBy +#pragma mark - CCTintBy @implementation CCTintBy +(id) actionWithDuration:(ccTime)t red:(GLshort)r green:(GLshort)g blue:(GLshort)b { @@ -1136,7 +1149,7 @@ -(id) copyWithZone: (NSZone*) zone -(void) startWithTarget:(id)aTarget { [super startWithTarget:aTarget]; - + id tn = (id) target_; ccColor3B color = [tn color]; fromR_ = color.r; @@ -1159,8 +1172,7 @@ - (CCActionInterval*) reverse // // DelayTime // -#pragma mark - -#pragma mark DelayTime +#pragma mark - CCDelayTime @implementation CCDelayTime -(void) update: (ccTime) t { @@ -1176,13 +1188,12 @@ -(id)reverse // // ReverseTime // -#pragma mark - -#pragma mark ReverseTime +#pragma mark - CCReverseTime @implementation CCReverseTime +(id) actionWithAction: (CCFiniteTimeAction*) action { // casting to prevent warnings - CCReverseTime *a = [super alloc]; + CCReverseTime *a = [self alloc]; return [[a initWithAction:action] autorelease]; } @@ -1196,7 +1207,7 @@ -(id) initWithAction: (CCFiniteTimeAction*) action [other_ release]; other_ = [action retain]; } - + return self; } @@ -1238,55 +1249,42 @@ -(CCActionInterval*) reverse // Animate // -#pragma mark - -#pragma mark Animate +#pragma mark - CCAnimate @implementation CCAnimate @synthesize animation = animation_; +(id) actionWithAnimation: (CCAnimation*)anim { - return [[[self alloc] initWithAnimation:anim restoreOriginalFrame:YES] autorelease]; -} - -+(id) actionWithAnimation: (CCAnimation*)anim restoreOriginalFrame:(BOOL)b -{ - return [[[self alloc] initWithAnimation:anim restoreOriginalFrame:b] autorelease]; + return [[[self alloc] initWithAnimation:anim] autorelease]; } -+(id) actionWithDuration:(ccTime)duration animation: (CCAnimation*)anim restoreOriginalFrame:(BOOL)b -{ - return [[[self alloc] initWithDuration:duration animation:anim restoreOriginalFrame:b] autorelease]; -} - --(id) initWithAnimation: (CCAnimation*)anim -{ - NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil"); - return [self initWithAnimation:anim restoreOriginalFrame:YES]; -} - --(id) initWithAnimation: (CCAnimation*)anim restoreOriginalFrame:(BOOL) b +// delegate initializer +-(id) initWithAnimation:(CCAnimation*)anim { NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil"); + + float singleDuration = anim.duration; - if( (self=[super initWithDuration: [[anim frames] count] * [anim delay]]) ) { + if( (self=[super initWithDuration:singleDuration * anim.loops] ) ) { - restoreOriginalFrame_ = b; + nextFrame_ = 0; self.animation = anim; origFrame_ = nil; - } - return self; -} - --(id) initWithDuration:(ccTime)aDuration animation: (CCAnimation*)anim restoreOriginalFrame:(BOOL) b -{ - NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil"); - - if( (self=[super initWithDuration:aDuration] ) ) { + executedLoops_ = 0; - restoreOriginalFrame_ = b; - self.animation = anim; - origFrame_ = nil; + splitTimes_ = [[NSMutableArray alloc] initWithCapacity:anim.frames.count]; + + float accumUnitsOfTime = 0; + float newUnitOfTimeValue = singleDuration / anim.totalDelayUnits; + + for( CCAnimationFrame *frame in anim.frames ) { + + NSNumber *value = [NSNumber numberWithFloat: (accumUnitsOfTime * newUnitOfTimeValue) / singleDuration]; + accumUnitsOfTime += frame.delayUnits; + + [splitTimes_ addObject:value]; + } } return self; } @@ -1294,11 +1292,12 @@ -(id) initWithDuration:(ccTime)aDuration animation: (CCAnimation*)anim restoreOr -(id) copyWithZone: (NSZone*) zone { - return [[[self class] allocWithZone: zone] initWithDuration:duration_ animation:animation_ restoreOriginalFrame:restoreOriginalFrame_]; + return [[[self class] allocWithZone: zone] initWithAnimation:[[animation_ copy]autorelease] ]; } -(void) dealloc { + [splitTimes_ release]; [animation_ release]; [origFrame_ release]; [super dealloc]; @@ -1311,33 +1310,62 @@ -(void) startWithTarget:(id)aTarget [origFrame_ release]; - if( restoreOriginalFrame_ ) - origFrame_ = [[sprite displayedFrame] retain]; + if( animation_.restoreOriginalFrame ) + origFrame_ = [[sprite displayFrame] retain]; + + nextFrame_ = 0; + executedLoops_ = 0; } -(void) stop { - if( restoreOriginalFrame_ ) { + if( animation_.restoreOriginalFrame ) { CCSprite *sprite = target_; [sprite setDisplayFrame:origFrame_]; } - + [super stop]; } -(void) update: (ccTime) t { + + // if t==1, ignore. Animation should finish with t==1 + if( t < 1.0f ) { + t *= animation_.loops; + + // new loop? If so, reset frame counter + NSUInteger loopNumber = (NSUInteger)t; + if( loopNumber > executedLoops_ ) { + nextFrame_ = 0; + executedLoops_++; + } + + // new t for animations + t = fmodf(t, 1.0f); + } + NSArray *frames = [animation_ frames]; NSUInteger numberOfFrames = [frames count]; - - NSUInteger idx = t * numberOfFrames; + CCSpriteFrame *frameToDisplay = nil; - if( idx >= numberOfFrames ) - idx = numberOfFrames -1; - - CCSprite *sprite = target_; - if (! [sprite isFrameDisplayed: [frames objectAtIndex: idx]] ) - [sprite setDisplayFrame: [frames objectAtIndex:idx]]; + for( NSUInteger i=nextFrame_; i < numberOfFrames; i++ ) { + NSNumber *splitTime = [splitTimes_ objectAtIndex:i]; + + if( [splitTime floatValue] <= t ) { + CCAnimationFrame *frame = [frames objectAtIndex:i]; + frameToDisplay = [frame spriteFrame]; + [(CCSprite*)target_ setDisplayFrame: frameToDisplay]; + + NSDictionary *dict = [frame userInfo]; + if( dict ) + [[NSNotificationCenter defaultCenter] postNotificationName:CCAnimationFrameDisplayedNotification object:target_ userInfo:dict]; + + nextFrame_ = i+1; + + break; + } + } } - (CCActionInterval *) reverse @@ -1347,9 +1375,68 @@ - (CCActionInterval *) reverse NSEnumerator *enumerator = [oldArray reverseObjectEnumerator]; for (id element in enumerator) [newArray addObject:[[element copy] autorelease]]; - - CCAnimation *newAnim = [CCAnimation animationWithFrames:newArray delay:animation_.delay]; - return [[self class] actionWithDuration:duration_ animation:newAnim restoreOriginalFrame:restoreOriginalFrame_]; + + CCAnimation *newAnim = [CCAnimation animationWithAnimationFrames:newArray delayPerUnit:animation_.delayPerUnit loops:animation_.loops]; + newAnim.restoreOriginalFrame = animation_.restoreOriginalFrame; + return [[self class] actionWithAnimation:newAnim]; +} +@end + + +#pragma mark - CCTargetedAction + +@implementation CCTargetedAction + +@synthesize forcedTarget = forcedTarget_; + ++ (id) actionWithTarget:(id) target action:(CCFiniteTimeAction*) action +{ + return [[ (CCTargetedAction*)[self alloc] initWithTarget:target action:action] autorelease]; +} + +- (id) initWithTarget:(id) targetIn action:(CCFiniteTimeAction*) actionIn +{ + if((self = [super initWithDuration:actionIn.duration])) + { + forcedTarget_ = [targetIn retain]; + action_ = [actionIn retain]; + } + return self; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAction *copy = [ (CCTargetedAction*) [[self class] allocWithZone: zone] initWithTarget:target_ action:[[action_ copy] autorelease]]; + return copy; +} + +- (void) dealloc +{ + [forcedTarget_ release]; + [action_ release]; + [super dealloc]; +} + +//- (void) updateDuration:(id)aTarget +//{ +// [action updateDuration:forcedTarget]; +// duration_ = action.duration; +//} + +- (void) startWithTarget:(id)aTarget +{ + [super startWithTarget:forcedTarget_]; + [action_ startWithTarget:forcedTarget_]; +} + +- (void) stop +{ + [action_ stop]; +} + +- (void) update:(ccTime) time +{ + [action_ update:time]; } @end diff --git a/cocos2d/cocos2d/CCActionManager.h b/cocos2d/cocos2d/CCActionManager.h old mode 100644 new mode 100755 index 0eeda91..b9eb59c --- a/cocos2d/cocos2d/CCActionManager.h +++ b/cocos2d/cocos2d/CCActionManager.h @@ -12,10 +12,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,29 +28,30 @@ #import "CCAction.h" +#import "ccMacros.h" #import "Support/ccCArray.h" #import "Support/uthash.h" typedef struct _hashElement { struct ccArray *actions; - id target; NSUInteger actionIndex; - CCAction *currentAction; BOOL currentActionSalvaged; - BOOL paused; + BOOL paused; UT_hash_handle hh; + + CC_ARC_UNSAFE_RETAINED id target; + CC_ARC_UNSAFE_RETAINED CCAction *currentAction; } tHashElement; -/** CCActionManager is a singleton that manages all the actions. - Normally you won't need to use this singleton directly. 99% of the cases you will use the CCNode interface, - which uses this singleton. - But there are some cases where you might need to use this singleton. +/** CCActionManager the object that manages all the actions. + Normally you won't need to use this API directly. 99% of the cases you will use the CCNode interface, which uses this object. + But there are some cases where you might need to use this API dirctly: Examples: - - When you want to run an action where the target is different from a CCNode. + - When you want to run an action where the target is different from a CCNode. - When you want to pause / resume the actions - + @since v0.8 */ @interface CCActionManager : NSObject @@ -60,13 +61,6 @@ typedef struct _hashElement BOOL currentTargetSalvaged; } -/** returns a shared instance of the CCActionManager */ -+ (CCActionManager *)sharedManager; - -/** purges the shared action manager. It releases the retained instance. - @since v0.99.0 - */ -+(void)purgeSharedManager; // actions @@ -107,5 +101,13 @@ typedef struct _hashElement */ -(void) resumeTarget:(id)target; +/** Pauses all running actions, returning a list of targets whose actions were paused. + */ +-(NSSet *) pauseAllRunningActions; + +/** Resume a set of targets (convenience function to reverse a pauseAllRunningActions call) + */ +-(void) resumeTargets:(NSSet *)targetsToResume; + @end diff --git a/cocos2d/cocos2d/CCActionManager.m b/cocos2d/cocos2d/CCActionManager.m old mode 100644 new mode 100755 index 8bdfbb7..49fc86c --- a/cocos2d/cocos2d/CCActionManager.m +++ b/cocos2d/cocos2d/CCActionManager.m @@ -12,10 +12,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -31,12 +31,6 @@ #import "CCScheduler.h" #import "ccMacros.h" - -// -// singleton stuff -// -static CCActionManager *sharedManager_ = nil; - @interface CCActionManager (Private) -(void) removeActionAtIndex:(NSUInteger)index hashElement:(tHashElement*)element; -(void) deleteHashElement:(tHashElement*)element; @@ -46,45 +40,25 @@ -(void) actionAllocWithHashElement:(tHashElement*)element; @implementation CCActionManager -#pragma mark ActionManager - init -+ (CCActionManager *)sharedManager -{ - if (!sharedManager_) - sharedManager_ = [[self alloc] init]; - - return sharedManager_; -} - -+(id)alloc -{ - NSAssert(sharedManager_ == nil, @"Attempted to allocate a second instance of a singleton."); - return [super alloc]; -} - -+(void)purgeSharedManager -{ - [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self]; - [sharedManager_ release]; - sharedManager_ = nil; -} - -(id) init { if ((self=[super init]) ) { - [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:0 paused:NO]; targets = NULL; } - + return self; } +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %p>", [self class], self]; +} + - (void) dealloc { CCLOGINFO( @"cocos2d: deallocing %@", self); - - [self removeAllActions]; - sharedManager_ = nil; + [self removeAllActions]; [super dealloc]; } @@ -106,24 +80,24 @@ -(void) actionAllocWithHashElement:(tHashElement*)element if( element->actions == nil ) element->actions = ccArrayNew(4); else if( element->actions->num == element->actions->max ) - ccArrayDoubleCapacity(element->actions); + ccArrayDoubleCapacity(element->actions); } -(void) removeActionAtIndex:(NSUInteger)index hashElement:(tHashElement*)element -{ +{ id action = element->actions->arr[index]; if( action == element->currentAction && !element->currentActionSalvaged ) { [element->currentAction retain]; element->currentActionSalvaged = YES; } - + ccArrayRemoveObjectAtIndex(element->actions, index); // update actionIndex in case we are in tick:, looping over the actions if( element->actionIndex >= index ) element->actionIndex--; - + if( element->actions->num == 0 ) { if( currentTarget == element ) currentTargetSalvaged = YES; @@ -154,13 +128,33 @@ -(void) resumeTarget:(id)target // CCLOG(@"cocos2d: resumeAllActions: Target not found"); } +-(NSSet *) pauseAllRunningActions +{ + NSMutableSet* idsWithActions = [NSMutableSet setWithCapacity:50]; + + for(tHashElement *element=targets; element != NULL; element=element->hh.next) { + if( !element->paused ) { + element->paused = YES; + [idsWithActions addObject:element->target]; + } + } + return idsWithActions; +} + +-(void) resumeTargets:(NSSet *)targetsToResume +{ + for(id target in targetsToResume) { + [self resumeTarget:target]; + } +} + #pragma mark ActionManager - run -(void) addAction:(CCAction*)action target:(id)target paused:(BOOL)paused { NSAssert( action != nil, @"Argument action must be non-nil"); - NSAssert( target != nil, @"Argument target must be non-nil"); - + NSAssert( target != nil, @"Argument target must be non-nil"); + tHashElement *element = NULL; HASH_FIND_INT(targets, &target, element); if( ! element ) { @@ -171,12 +165,12 @@ -(void) addAction:(CCAction*)action target:(id)target paused:(BOOL)paused // CCLOG(@"cocos2d: ---- buckets: %d/%d - %@", targets->entries, targets->size, element->target); } - + [self actionAllocWithHashElement:element]; - NSAssert( !ccArrayContainsObject(element->actions, action), @"runAction: Action already running"); + NSAssert( !ccArrayContainsObject(element->actions, action), @"runAction: Action already running"); ccArrayAppendObject(element->actions, action); - + [action startWithTarget:target]; } @@ -184,7 +178,7 @@ -(void) addAction:(CCAction*)action target:(id)target paused:(BOOL)paused -(void) removeAllActions { - for(tHashElement *element=targets; element != NULL; ) { + for(tHashElement *element=targets; element != NULL; ) { id target = element->target; element = element->hh.next; [self removeAllActionsFromTarget:target]; @@ -195,7 +189,7 @@ -(void) removeAllActionsFromTarget:(id)target // explicit nil handling if( target == nil ) return; - + tHashElement *element = NULL; HASH_FIND_INT(targets, &target, element); if( element ) { @@ -219,7 +213,7 @@ -(void) removeAction: (CCAction*) action // explicit nil handling if (action == nil) return; - + tHashElement *element = NULL; id target = [action originalTarget]; HASH_FIND_INT(targets, &target, element ); @@ -237,15 +231,15 @@ -(void) removeActionByTag:(NSInteger)aTag target:(id)target { NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag"); NSAssert( target != nil, @"Target should be ! nil"); - + tHashElement *element = NULL; HASH_FIND_INT(targets, &target, element); - + if( element ) { NSUInteger limit = element->actions->num; for( NSUInteger i = 0; i < limit; i++) { CCAction *a = element->actions->arr[i]; - + if( a.tag == aTag && [a originalTarget]==target) { [self removeActionAtIndex:i hashElement:element]; break; @@ -269,9 +263,9 @@ -(CCAction*) getActionByTag:(NSInteger)aTag target:(id)target NSUInteger limit = element->actions->num; for( NSUInteger i = 0; i < limit; i++) { CCAction *a = element->actions->arr[i]; - + if( a.tag == aTag ) - return a; + return a; } } // CCLOG(@"cocos2d: getActionByTag: Action not found"); @@ -297,18 +291,18 @@ -(NSUInteger) numberOfRunningActionsInTarget:(id) target -(void) update: (ccTime) dt { - for(tHashElement *elt = targets; elt != NULL; ) { + for(tHashElement *elt = targets; elt != NULL; ) { currentTarget = elt; currentTargetSalvaged = NO; - + if( ! currentTarget->paused ) { - + // The 'actions' ccArray may change while inside this loop. for( currentTarget->actionIndex = 0; currentTarget->actionIndex < currentTarget->actions->num; currentTarget->actionIndex++) { currentTarget->currentAction = currentTarget->actions->arr[currentTarget->actionIndex]; currentTarget->currentActionSalvaged = NO; - + [currentTarget->currentAction step: dt]; if( currentTarget->currentActionSalvaged ) { @@ -319,13 +313,13 @@ -(void) update: (ccTime) dt } else if( [currentTarget->currentAction isDone] ) { [currentTarget->currentAction stop]; - + CCAction *a = currentTarget->currentAction; // Make currentAction nil to prevent removeAction from salvaging it. currentTarget->currentAction = nil; [self removeAction:a]; } - + currentTarget->currentAction = nil; } } @@ -333,12 +327,12 @@ -(void) update: (ccTime) dt // elt, at this moment, is still valid // so it is safe to ask this here (issue #490) elt = elt->hh.next; - + // only delete currentTarget if no actions were scheduled during the cycle (issue #481) if( currentTargetSalvaged && currentTarget->actions->num == 0 ) [self deleteHashElement:currentTarget]; } - + // issue #635 currentTarget = nil; } diff --git a/cocos2d/cocos2d/CCActionPageTurn3D.h b/cocos2d/cocos2d/CCActionPageTurn3D.h old mode 100644 new mode 100755 index 39eb31d..6199aff --- a/cocos2d/cocos2d/CCActionPageTurn3D.h +++ b/cocos2d/cocos2d/CCActionPageTurn3D.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -32,7 +32,7 @@ * * Based on an original paper by L Hong et al. * http://www.parc.com/publication/1638/turning-pages-of-3d-electronic-books.html - * + * * @since v0.8.2 */ @interface CCPageTurn3D : CCGrid3DAction diff --git a/cocos2d/cocos2d/CCActionPageTurn3D.m b/cocos2d/cocos2d/CCActionPageTurn3D.m old mode 100644 new mode 100755 index ee59500..cff9bc2 --- a/cocos2d/cocos2d/CCActionPageTurn3D.m +++ b/cocos2d/cocos2d/CCActionPageTurn3D.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -36,26 +36,26 @@ -(void)update:(ccTime)time float tt = MAX( 0, time - 0.25f ); float deltaAy = ( tt * tt * 500); float ay = -100 - deltaAy; - + float deltaTheta = - (float) M_PI_2 * sqrtf( time) ; float theta = /*0.01f*/ + (float) M_PI_2 +deltaTheta; - + float sinTheta = sinf(theta); float cosTheta = cosf(theta); - + for( int i = 0; i <=gridSize_.x; i++ ) { for( int j = 0; j <= gridSize_.y; j++ ) { // Get original vertex ccVertex3F p = [self originalVertex:ccg(i,j)]; - + float R = sqrtf(p.x*p.x + (p.y - ay) * (p.y - ay)); float r = R * sinTheta; float alpha = asinf( p.x / R ); float beta = alpha / sinTheta; float cosBeta = cosf( beta ); - + // If beta > PI then we've wrapped around the cone // Reduce the radius to stop these points interfering with others if( beta <= M_PI) @@ -66,18 +66,18 @@ -(void)update:(ccTime)time // points p.x = 0; } - + p.y = ( R + ay - ( r*(1 - cosBeta)*sinTheta)); - + // We scale z here to avoid the animation being // too much bigger than the screen due to perspectve transform p.z = (r * ( 1 - cosBeta ) * cosTheta) / 7; // "100" didn't work for - + // Stop z coord from dropping beneath underlying page in a transition - // issue #751 + // issue #751 if( p.z<0.5f ) p.z = 0.5f; - + // Set new coords [self setVertex:ccg(i,j) vertex:p]; } diff --git a/cocos2d/cocos2d/CCActionProgressTimer.h b/cocos2d/cocos2d/CCActionProgressTimer.h old mode 100644 new mode 100755 index 500631b..2b9e771 --- a/cocos2d/cocos2d/CCActionProgressTimer.h +++ b/cocos2d/cocos2d/CCActionProgressTimer.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/cocos2d/cocos2d/CCActionProgressTimer.m b/cocos2d/cocos2d/CCActionProgressTimer.m old mode 100644 new mode 100755 index c242570..d5e2993 --- a/cocos2d/cocos2d/CCActionProgressTimer.m +++ b/cocos2d/cocos2d/CCActionProgressTimer.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -38,7 +38,7 @@ -(id) initWithDuration: (ccTime) t percent: (float) v { if( (self=[super initWithDuration: t] ) ) to_ = v; - + return self; } @@ -52,7 +52,7 @@ -(void) startWithTarget:(id) aTarget; { [super startWithTarget:aTarget]; from_ = [(kProgressTimerCast)target_ percentage]; - + // XXX: Is this correct ? // Adding it to support CCRepeat if( from_ == 100) diff --git a/cocos2d/cocos2d/CCActionTiledGrid.h b/cocos2d/cocos2d/CCActionTiledGrid.h old mode 100644 new mode 100755 index d66132d..42b23a6 --- a/cocos2d/cocos2d/CCActionTiledGrid.h +++ b/cocos2d/cocos2d/CCActionTiledGrid.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/cocos2d/cocos2d/CCActionTiledGrid.m b/cocos2d/cocos2d/CCActionTiledGrid.m old mode 100644 new mode 100755 index 75965ec..f8d6a2d --- a/cocos2d/cocos2d/CCActionTiledGrid.m +++ b/cocos2d/cocos2d/CCActionTiledGrid.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -53,7 +53,7 @@ -(id)initWithRange:(int)range shakeZ:(BOOL)sz grid:(ccGridSize)gSize duration:(c randrange = range; shakeZ = sz; } - + return self; } @@ -67,7 +67,7 @@ -(id) copyWithZone: (NSZone*) zone -(void)update:(ccTime)time { int i, j; - + for( i = 0; i < gridSize_.x; i++ ) { for( j = 0; j < gridSize_.y; j++ ) @@ -92,7 +92,7 @@ -(void)update:(ccTime)time coords.tl.z += ( rand() % (randrange*2) ) - randrange; coords.tr.z += ( rand() % (randrange*2) ) - randrange; } - + [self setTile:ccg(i,j) coords:coords]; } } @@ -120,7 +120,7 @@ -(id)initWithRange:(int)range shatterZ:(BOOL)sz grid:(ccGridSize)gSize duration: randrange = range; shatterZ = sz; } - + return self; } @@ -134,7 +134,7 @@ -(id) copyWithZone: (NSZone*) zone -(void)update:(ccTime)time { int i, j; - + if ( once == NO ) { for( i = 0; i < gridSize_.x; i++ ) @@ -142,13 +142,13 @@ -(void)update:(ccTime)time for( j = 0; j < gridSize_.y; j++ ) { ccQuad3 coords = [self originalTile:ccg(i,j)]; - + // X coords.bl.x += ( rand() % (randrange*2) ) - randrange; coords.br.x += ( rand() % (randrange*2) ) - randrange; coords.tl.x += ( rand() % (randrange*2) ) - randrange; coords.tr.x += ( rand() % (randrange*2) ) - randrange; - + // Y coords.bl.y += ( rand() % (randrange*2) ) - randrange; coords.br.y += ( rand() % (randrange*2) ) - randrange; @@ -157,15 +157,15 @@ -(void)update:(ccTime)time if( shatterZ ) { coords.bl.z += ( rand() % (randrange*2) ) - randrange; - coords.br.z += ( rand() % (randrange*2) ) - randrange; + coords.br.z += ( rand() % (randrange*2) ) - randrange; coords.tl.z += ( rand() % (randrange*2) ) - randrange; coords.tr.z += ( rand() % (randrange*2) ) - randrange; } - + [self setTile:ccg(i,j) coords:coords]; } } - + once = YES; } } @@ -192,7 +192,7 @@ -(id)initWithSeed:(int)s grid:(ccGridSize)gSize duration:(ccTime)d tilesOrder = nil; tiles = nil; } - + return self; } @@ -225,19 +225,19 @@ -(void)shuffle:(int*)array count:(NSUInteger)len -(ccGridSize)getDelta:(ccGridSize)pos { CGPoint pos2; - + NSInteger idx = pos.x * gridSize_.y + pos.y; - + pos2.x = tilesOrder[idx] / (int)gridSize_.y; pos2.y = tilesOrder[idx] % (int)gridSize_.y; - + return ccg(pos2.x - pos.x, pos2.y - pos.y); } -(void)placeTile:(ccGridSize)pos tile:(Tile)t { ccQuad3 coords = [self originalTile:pos]; - + CGPoint step = [[target_ grid] step]; coords.bl.x += (int)(t.position.x * step.x); coords.bl.y += (int)(t.position.y * step.y); @@ -257,22 +257,22 @@ -(void)placeTile:(ccGridSize)pos tile:(Tile)t -(void)startWithTarget:(id)aTarget { [super startWithTarget:aTarget]; - + if ( seed != -1 ) srand(seed); - + tilesCount = gridSize_.x * gridSize_.y; tilesOrder = (int*)malloc(tilesCount*sizeof(int)); int i, j; - + for( i = 0; i < tilesCount; i++ ) tilesOrder[i] = i; - + [self shuffle:tilesOrder count:tilesCount]; - + tiles = malloc(tilesCount*sizeof(Tile)); Tile *tileArray = (Tile*)tiles; - + for( i = 0; i < gridSize_.x; i++ ) { for( j = 0; j < gridSize_.y; j++ ) @@ -288,9 +288,9 @@ -(void)startWithTarget:(id)aTarget -(void)update:(ccTime)time { int i, j; - + Tile *tileArray = (Tile*)tiles; - + for( i = 0; i < gridSize_.x; i++ ) { for( j = 0; j < gridSize_.y; j++ ) @@ -316,7 +316,7 @@ -(float)testFunc:(ccGridSize)pos time:(ccTime)time CGPoint n = ccpMult( ccp(gridSize_.x,gridSize_.y), time); if ( (n.x+n.y) == 0.0f ) return 1.0f; - + return powf( (pos.x+pos.y) / (n.x+n.y), 6 ); } @@ -327,7 +327,7 @@ -(void)turnOnTile:(ccGridSize)pos -(void)turnOffTile:(ccGridSize)pos { - ccQuad3 coords; + ccQuad3 coords; bzero(&coords, sizeof(ccQuad3)); [self setTile:pos coords:coords]; } @@ -336,7 +336,7 @@ -(void)transformTile:(ccGridSize)pos distance:(float)distance { ccQuad3 coords = [self originalTile:pos]; CGPoint step = [[target_ grid] step]; - + coords.bl.x += (step.x / 2) * (1.0f - distance); coords.bl.y += (step.y / 2) * (1.0f - distance); @@ -355,7 +355,7 @@ -(void)transformTile:(ccGridSize)pos distance:(float)distance -(void)update:(ccTime)time { int i, j; - + for( i = 0; i < gridSize_.x; i++ ) { for( j = 0; j < gridSize_.y; j++ ) @@ -385,7 +385,7 @@ -(float)testFunc:(ccGridSize)pos time:(ccTime)time CGPoint n = ccpMult(ccp(gridSize_.x, gridSize_.y), (1.0f-time)); if ( (pos.x+pos.y) == 0 ) return 1.0f; - + return powf( (n.x+n.y) / (pos.x+pos.y), 6 ); } @@ -403,7 +403,7 @@ -(float)testFunc:(ccGridSize)pos time:(ccTime)time CGPoint n = ccpMult(ccp(gridSize_.x, gridSize_.y), time); if ( n.y == 0 ) return 1.0f; - + return powf( pos.y / n.y, 6 ); } @@ -411,12 +411,12 @@ -(void)transformTile:(ccGridSize)pos distance:(float)distance { ccQuad3 coords = [self originalTile:pos]; CGPoint step = [[target_ grid] step]; - + coords.bl.y += (step.y / 2) * (1.0f - distance); coords.br.y += (step.y / 2) * (1.0f - distance); coords.tl.y -= (step.y / 2) * (1.0f - distance); coords.tr.y -= (step.y / 2) * (1.0f - distance); - + [self setTile:pos coords:coords]; } @@ -434,7 +434,7 @@ -(float)testFunc:(ccGridSize)pos time:(ccTime)time CGPoint n = ccpMult(ccp(gridSize_.x,gridSize_.y), (1.0f - time)); if ( pos.y == 0 ) return 1.0f; - + return powf( n.y / pos.y, 6 ); } @@ -459,7 +459,7 @@ -(id)initWithSeed:(int)s grid:(ccGridSize)gSize duration:(ccTime)d seed = s; tilesOrder = nil; } - + return self; } @@ -495,7 +495,7 @@ -(void)turnOnTile:(ccGridSize)pos -(void)turnOffTile:(ccGridSize)pos { ccQuad3 coords; - + bzero(&coords, sizeof(ccQuad3)); [self setTile:pos coords:coords]; } @@ -503,32 +503,32 @@ -(void)turnOffTile:(ccGridSize)pos -(void)startWithTarget:(id)aTarget { int i; - + [super startWithTarget:aTarget]; - + if ( seed != -1 ) srand(seed); - + tilesCount = gridSize_.x * gridSize_.y; tilesOrder = (int*)malloc(tilesCount*sizeof(int)); for( i = 0; i < tilesCount; i++ ) tilesOrder[i] = i; - + [self shuffle:tilesOrder count:tilesCount]; } -(void)update:(ccTime)time { int i, l, t; - + l = (int)(time * (float)tilesCount); - + for( i = 0; i < tilesCount; i++ ) { t = tilesOrder[i]; ccGridSize tilePos = ccg( t / gridSize_.y, t % gridSize_.y ); - + if ( i < l ) [self turnOffTile:tilePos]; else @@ -561,7 +561,7 @@ -(id)initWithWaves:(int)wav amplitude:(float)amp grid:(ccGridSize)gSize duration amplitude = amp; amplitudeRate = 1.0f; } - + return self; } @@ -575,18 +575,18 @@ -(id) copyWithZone: (NSZone*) zone -(void)update:(ccTime)time { int i, j; - + for( i = 0; i < gridSize_.x; i++ ) { for( j = 0; j < gridSize_.y; j++ ) { ccQuad3 coords = [self originalTile:ccg(i,j)]; - + coords.bl.z = (sinf(time*(CGFloat)M_PI*waves*2 + (coords.bl.y+coords.bl.x) * .01f) * amplitude * amplitudeRate ); coords.br.z = coords.bl.z; coords.tl.z = coords.bl.z; coords.tr.z = coords.bl.z; - + [self setTile:ccg(i,j) coords:coords]; } } @@ -616,7 +616,7 @@ -(id)initWithJumps:(int)j amplitude:(float)amp grid:(ccGridSize)gSize duration:( amplitude = amp; amplitudeRate = 1.0f; } - + return self; } @@ -630,16 +630,16 @@ -(id) copyWithZone: (NSZone*) zone -(void)update:(ccTime)time { int i, j; - + float sinz = (sinf((CGFloat)M_PI*time*jumps*2) * amplitude * amplitudeRate ); float sinz2 = (sinf((CGFloat)M_PI*(time*jumps*2 + 1)) * amplitude * amplitudeRate ); - + for( i = 0; i < gridSize_.x; i++ ) { for( j = 0; j < gridSize_.y; j++ ) { ccQuad3 coords = [self originalTile:ccg(i,j)]; - + if ( ((i+j) % 2) == 0 ) { coords.bl.z += sinz; @@ -654,7 +654,7 @@ -(void)update:(ccTime)time coords.tl.z += sinz2; coords.tr.z += sinz2; } - + [self setTile:ccg(i,j) coords:coords]; } } @@ -694,20 +694,20 @@ -(void)startWithTarget:(id)aTarget -(void)update:(ccTime)time { int j; - + for( j = 0; j < gridSize_.y; j++ ) { ccQuad3 coords = [self originalTile:ccg(0,j)]; float direction = 1; - + if ( (j % 2 ) == 0 ) direction = -1; - + coords.bl.x += direction * winSize.width * time; coords.br.x += direction * winSize.width * time; coords.tl.x += direction * winSize.width * time; coords.tr.x += direction * winSize.width * time; - + [self setTile:ccg(0,j) coords:coords]; } } @@ -747,20 +747,20 @@ -(void)startWithTarget:(id)aTarget -(void)update:(ccTime)time { int i; - + for( i = 0; i < gridSize_.x; i++ ) { ccQuad3 coords = [self originalTile:ccg(i,0)]; float direction = 1; - + if ( (i % 2 ) == 0 ) direction = -1; - + coords.bl.y += direction * winSize.height * time; coords.br.y += direction * winSize.height * time; coords.tl.y += direction * winSize.height * time; coords.tr.y += direction * winSize.height * time; - + [self setTile:ccg(i,0) coords:coords]; } } diff --git a/cocos2d/cocos2d/CCActionTween.h b/cocos2d/cocos2d/CCActionTween.h old mode 100644 new mode 100755 index 69fdea5..378a828 --- a/cocos2d/cocos2d/CCActionTween.h +++ b/cocos2d/cocos2d/CCActionTween.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,27 +28,27 @@ #import "CCActionInterval.h" /** CCActionTween - + CCActionTween is an action that lets you update any property of an object. - For example, if you want to modify the "width" property of a target from 200 to 300 in 2 senconds, then: - + For example, if you want to modify the "width" property of a target from 200 to 300 in 2 seconds, then: + id modifyWidth = [CCActionTween actionWithDuration:2 key:@"width" from:200 to:300]; [target runAction:modifyWidth]; - + Another example: CCScaleTo action could be rewriten using CCPropertyAction: - + // scaleA and scaleB are equivalents id scaleA = [CCScaleTo actionWithDuration:2 scale:3]; id scaleB = [CCActionTween actionWithDuration:2 key:@"scale" from:1 to:3]; - + @since v0.99.2 */ @interface CCActionTween : CCActionInterval { NSString *key_; - + float from_, to_; float delta_; } @@ -58,5 +58,5 @@ /** initializes the action with the property name (key), and the from and to parameters. */ - (id)initWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to; - + @end diff --git a/cocos2d/cocos2d/CCActionTween.m b/cocos2d/cocos2d/CCActionTween.m old mode 100644 new mode 100755 index 95ae572..a119d72 --- a/cocos2d/cocos2d/CCActionTween.m +++ b/cocos2d/cocos2d/CCActionTween.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -34,15 +34,15 @@ + (id)actionWithDuration:(ccTime)aDuration key:(NSString *)aKey from:(float)aFro } - (id)initWithDuration:(ccTime)aDuration key:(NSString *)key from:(float)from to:(float)to { - + if ((self = [super initWithDuration:aDuration])) { - + key_ = [key copy]; to_ = to; from_ = from; } - + return self; } @@ -59,7 +59,7 @@ - (void)startWithTarget:aTarget } - (void) update:(ccTime) dt -{ +{ [target_ setValue:[NSNumber numberWithFloat:to_ - delta_ * (1 - dt)] forKey:key_]; } diff --git a/cocos2d/cocos2d/CCAnimation.h b/cocos2d/cocos2d/CCAnimation.h old mode 100644 new mode 100755 index 622bc47..a20def3 --- a/cocos2d/cocos2d/CCAnimation.h +++ b/cocos2d/cocos2d/CCAnimation.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,71 +25,126 @@ */ #import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import #endif // IPHONE @class CCSpriteFrame; @class CCTexture2D; +@class CCSpriteFrame; -/** A CCAnimation object is used to perform animations on the CCSprite objects. +/** CCAnimationFrame + A frame of the animation. It contains information like: + - sprite frame name + - # of delay units. + - offset - The CCAnimation object contains CCSpriteFrame objects, and a possible delay between the frames. + @since v2.0 + */ +@interface CCAnimationFrame : NSObject +{ + CCSpriteFrame* spriteFrame_; + float delayUnits_; + NSDictionary *userInfo_; +} +/** CCSpriteFrameName to be used */ +@property (nonatomic, readwrite, retain) CCSpriteFrame* spriteFrame; + +/** how many units of time the frame takes */ +@property (nonatomic, readwrite) float delayUnits; + +/** A CCAnimationFrameDisplayedNotification notification will be broadcasted when the frame is displayed with this dictionary as UserInfo. If UserInfo is nil, then no notification will be broadcasted. */ +@property (nonatomic, readwrite, retain) NSDictionary *userInfo; + +/** initializes the animation frame with a spriteframe, number of delay units and a notification user info */ +-(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame delayUnits:(float)delayUnits userInfo:(NSDictionary*)userInfo; +@end + +/** A CCAnimation object is used to perform animations on the CCSprite objects. + + The CCAnimation object contains CCAnimationFrame objects, and a possible delay between the frames. You can animate a CCAnimation object by using the CCAnimate action. Example: - + [sprite runAction:[CCAnimate actionWithAnimation:animation]]; - + */ -@interface CCAnimation : NSObject +@interface CCAnimation : NSObject { - NSString *name_; - float delay_; - NSMutableArray *frames_; + NSMutableArray *frames_; + float totalDelayUnits_; + float delayPerUnit_; + BOOL restoreOriginalFrame_; + NSUInteger loops_; } -/** name of the animation */ -@property (nonatomic,readwrite,retain) NSString *name; -/** delay between frames in seconds. */ -@property (nonatomic,readwrite,assign) float delay; -/** array of frames */ +/** total Delay units of the CCAnimation. */ +@property (nonatomic, readonly) float totalDelayUnits; +/** Delay in seconds of the "delay unit" */ +@property (nonatomic, readwrite) float delayPerUnit; +/** duration in seconds of the whole animation. It is the result of totalDelayUnits * delayPerUnit */ +@property (nonatomic,readonly) float duration; +/** array of CCAnimationFrames */ @property (nonatomic,readwrite,retain) NSMutableArray *frames; +/** whether or not it shall restore the original frame when the animation finishes */ +@property (nonatomic,readwrite) BOOL restoreOriginalFrame; +/** how many times the animation is going to loop. 0 means animation is not animated. 1, animation is executed one time, ... */ +@property (nonatomic, readwrite) NSUInteger loops; /** Creates an animation @since v0.99.5 */ +(id) animation; -/** Creates an animation with frames. +/** Creates an animation with an array of CCSpriteFrame. + The frames will be created with one "delay unit". @since v0.99.5 */ -+(id) animationWithFrames:(NSArray*)frames; ++(id) animationWithSpriteFrames:(NSArray*)arrayOfSpriteFrameNames; -/* Creates an animation with frames and a delay between frames. +/* Creates an animation with an array of CCSpriteFrame and a delay between frames in seconds. + The frames will be added with one "delay unit". @since v0.99.5 */ -+(id) animationWithFrames:(NSArray*)frames delay:(float)delay; ++(id) animationWithSpriteFrames:(NSArray*)arrayOfSpriteFrameNames delay:(float)delay; -/** Initializes a CCAnimation with frames. +/* Creates an animation with an array of CCAnimationFrame, the delay per units in seconds and and how many times it should be executed. + @since v2.0 + */ ++(id) animationWithAnimationFrames:(NSArray*)arrayOfAnimationFrames delayPerUnit:(float)delayPerUnit loops:(NSUInteger)loops; + + +/** Initializes a CCAnimation with an array of CCSpriteFrame. + The frames will be added with one "delay unit". @since v0.99.5 */ --(id) initWithFrames:(NSArray*)frames; +-(id) initWithSpriteFrames:(NSArray*)arrayOfSpriteFrameNames; -/** Initializes a CCAnimation with frames and a delay between frames +/** Initializes a CCAnimation with an array of CCSpriteFrames and a delay between frames in seconds. + The frames will be added with one "delay unit". @since v0.99.5 */ --(id) initWithFrames:(NSArray *)frames delay:(float)delay; +-(id) initWithSpriteFrames:(NSArray *)arrayOfSpriteFrameNames delay:(float)delay; -/** Adds a frame to a CCAnimation. */ --(void) addFrame:(CCSpriteFrame*)frame; +/* Initializes an animation with an array of CCAnimationFrame and the delay per units in seconds. + @since v2.0 + */ +-(id) initWithAnimationFrames:(NSArray*)arrayOfAnimationFrames delayPerUnit:(float)delayPerUnit loops:(NSUInteger)loops; + +/** Adds a CCSpriteFrame to a CCAnimation. + The frame will be added with one "delay unit". +*/ +-(void) addSpriteFrame:(CCSpriteFrame*)frame; /** Adds a frame with an image filename. Internally it will create a CCSpriteFrame and it will add it. + The frame will be added with one "delay unit". Added to facilitate the migration from v0.8 to v0.9. */ --(void) addFrameWithFilename:(NSString*)filename; +-(void) addSpriteFrameWithFilename:(NSString*)filename; /** Adds a frame with a texture and a rect. Internally it will create a CCSpriteFrame and it will add it. + The frame will be added with one "delay unit". Added to facilitate the migration from v0.8 to v0.9. */ --(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect; +-(void) addSpriteFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect; @end diff --git a/cocos2d/cocos2d/CCAnimation.m b/cocos2d/cocos2d/CCAnimation.m old mode 100644 new mode 100755 index d98cdac..4a28e49 --- a/cocos2d/cocos2d/CCAnimation.m +++ b/cocos2d/cocos2d/CCAnimation.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,78 +30,171 @@ #import "CCTexture2D.h" #import "CCTextureCache.h" +#pragma mark - CCAnimationFrame +@implementation CCAnimationFrame + +@synthesize spriteFrame = spriteFrame_, delayUnits = delayUnits_, userInfo=userInfo_; + +-(id) initWithSpriteFrame:(CCSpriteFrame *)spriteFrame delayUnits:(float)delayUnits userInfo:(NSDictionary*)userInfo +{ + if( (self=[super init]) ) { + self.spriteFrame = spriteFrame; + self.delayUnits = delayUnits; + self.userInfo = userInfo; + } + + return self; +} + +-(void) dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@", self); + + [spriteFrame_ release]; + [userInfo_ release]; + + [super dealloc]; +} + +-(id) copyWithZone: (NSZone*) zone +{ + CCAnimationFrame *copy = [[[self class] allocWithZone: zone] initWithSpriteFrame:[[spriteFrame_ copy] autorelease] delayUnits:delayUnits_ userInfo:[[userInfo_ copy] autorelease] ]; + return copy; +} + +-(NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %p | SpriteFrame = %p, delayUnits = %0.2f >", [self class], self, spriteFrame_, delayUnits_ ]; +} +@end + + +#pragma mark - CCAnimation + @implementation CCAnimation -@synthesize name = name_, delay = delay_, frames = frames_; +@synthesize frames = frames_, totalDelayUnits=totalDelayUnits_, delayPerUnit=delayPerUnit_, restoreOriginalFrame=restoreOriginalFrame_, loops=loops_; +(id) animation { return [[[self alloc] init] autorelease]; } -+(id) animationWithFrames:(NSArray*)frames ++(id) animationWithSpriteFrames:(NSArray*)frames +{ + return [[[self alloc] initWithSpriteFrames:frames] autorelease]; +} + ++(id) animationWithSpriteFrames:(NSArray*)frames delay:(float)delay { - return [[[self alloc] initWithFrames:frames] autorelease]; + return [[[self alloc] initWithSpriteFrames:frames delay:delay] autorelease]; } -+(id) animationWithFrames:(NSArray*)frames delay:(float)delay ++(id) animationWithAnimationFrames:(NSArray*)arrayOfAnimationFrames delayPerUnit:(float)delayPerUnit loops:(NSUInteger)loops { - return [[[self alloc] initWithFrames:frames delay:delay] autorelease]; + return [[[self alloc] initWithAnimationFrames:arrayOfAnimationFrames delayPerUnit:delayPerUnit loops:loops] autorelease]; } -(id) init { - return [self initWithFrames:nil delay:0]; + return [self initWithSpriteFrames:nil delay:0]; } --(id) initWithFrames:(NSArray*)frames +-(id) initWithSpriteFrames:(NSArray*)frames { - return [self initWithFrames:frames delay:0]; + return [self initWithSpriteFrames:frames delay:0]; } --(id) initWithFrames:(NSArray*)array delay:(float)delay +-(id) initWithSpriteFrames:(NSArray*)array delay:(float)delay { - if( (self=[super init]) ) { + if( (self=[super init]) ) + { + loops_ = 1; + delayPerUnit_ = delay; + + self.frames = [NSMutableArray arrayWithCapacity:[array count]]; - delay_ = delay; - self.frames = [NSMutableArray arrayWithArray:array]; + for( CCSpriteFrame *frame in array ) { + CCAnimationFrame *animFrame = [[CCAnimationFrame alloc] initWithSpriteFrame:frame delayUnits:1 userInfo:nil]; + + [self.frames addObject:animFrame]; + [animFrame release]; + totalDelayUnits_++; + } + + } + return self; +} + +-(id) initWithAnimationFrames:(NSArray*)arrayOfAnimationFrames delayPerUnit:(float)delayPerUnit loops:(NSUInteger)loops +{ + if( ( self=[super init]) ) + { + delayPerUnit_ = delayPerUnit; + loops_ = loops; + + self.frames = [NSMutableArray arrayWithArray:arrayOfAnimationFrames]; + + for( CCAnimationFrame *animFrame in frames_ ) + totalDelayUnits_ += animFrame.delayUnits; } return self; } - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | frames=%d, delay:%f>", [self class], self, - [frames_ count], - delay_ + return [NSString stringWithFormat:@"<%@ = %p | frames=%lu, totalDelayUnits=%f, delayPerUnit=%f, loops=%lu>", [self class], self, + (unsigned long)[frames_ count], + totalDelayUnits_, + delayPerUnit_, + (unsigned long)loops_ ]; } +-(float) duration +{ + return totalDelayUnits_ * delayPerUnit_; +} + +- (id)copyWithZone:(NSZone *)zone +{ + CCAnimation *animation = [[[self class] allocWithZone: zone] initWithAnimationFrames:frames_ delayPerUnit:delayPerUnit_ loops:loops_]; + animation.restoreOriginalFrame = restoreOriginalFrame_; + + return animation; +} + -(void) dealloc { CCLOGINFO( @"cocos2d: deallocing %@",self); - [name_ release]; + [frames_ release]; [super dealloc]; } --(void) addFrame:(CCSpriteFrame*)frame +-(void) addSpriteFrame:(CCSpriteFrame*)frame { - [frames_ addObject:frame]; + CCAnimationFrame *animFrame = [[CCAnimationFrame alloc] initWithSpriteFrame:frame delayUnits:1 userInfo:nil]; + [frames_ addObject:animFrame]; + [animFrame release]; + + // update duration + totalDelayUnits_++; } --(void) addFrameWithFilename:(NSString*)filename +-(void) addSpriteFrameWithFilename:(NSString*)filename { CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:filename]; CGRect rect = CGRectZero; rect.size = texture.contentSize; - CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect]; - [frames_ addObject:frame]; + CCSpriteFrame *spriteFrame = [CCSpriteFrame frameWithTexture:texture rect:rect]; + + [self addSpriteFrame:spriteFrame]; } --(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect +-(void) addSpriteFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect { CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect]; - [frames_ addObject:frame]; + [self addSpriteFrame:frame]; } @end diff --git a/cocos2d/cocos2d/CCAnimationCache.h b/cocos2d/cocos2d/CCAnimationCache.h old mode 100644 new mode 100755 index 075c836..5488e8a --- a/cocos2d/cocos2d/CCAnimationCache.h +++ b/cocos2d/cocos2d/CCAnimationCache.h @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,11 +28,9 @@ @class CCAnimation; -/** Singleton that manages the Animations. +/** Singleton that manages the CCAnimation objects. It saves in a cache the animations. You should use this class if you want to save your animations in a cache. - Before v0.99.5, the recommend way was to save them on the CCSprite. Since v0.99.5, you should use this class instead. - @since v0.99.5 */ @interface CCAnimationCache : NSObject @@ -61,4 +59,16 @@ */ -(CCAnimation*) animationByName:(NSString*)name; +/** Adds an animation from an NSDictionary + Make sure that the frames were previously loaded in the CCSpriteFrameCache. + @since v1.1 + */ +-(void)addAnimationsWithDictionary:(NSDictionary *)dictionary; + +/** Adds an animation from a plist file. + Make sure that the frames were previously loaded in the CCSpriteFrameCache. + @since v1.1 + */ +-(void)addAnimationsWithFile:(NSString *)plist; + @end diff --git a/cocos2d/cocos2d/CCAnimationCache.m b/cocos2d/cocos2d/CCAnimationCache.m old mode 100644 new mode 100755 index f508227..443b878 --- a/cocos2d/cocos2d/CCAnimationCache.m +++ b/cocos2d/cocos2d/CCAnimationCache.m @@ -3,17 +3,20 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * + * Copyright (c) 2011 John Wordsworth + * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,10 +27,12 @@ * */ -#import "ccMacros.h" #import "CCAnimationCache.h" +#import "ccMacros.h" +#import "CCSpriteFrameCache.h" #import "CCAnimation.h" #import "CCSprite.h" +#import "Support/CCFileUtils.h" @implementation CCAnimationCache @@ -40,7 +45,7 @@ + (CCAnimationCache *)sharedAnimationCache { if (!sharedAnimationCache_) sharedAnimationCache_ = [[CCAnimationCache alloc] init]; - + return sharedAnimationCache_; } @@ -61,19 +66,19 @@ -(id) init if( (self=[super init]) ) { animations_ = [[NSMutableDictionary alloc] initWithCapacity: 20]; } - + return self; } - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | num of animations = %i>", [self class], self, [animations_ count]]; + return [NSString stringWithFormat:@"<%@ = %p | num of animations = %lu>", [self class], self, (unsigned long)[animations_ count]]; } -(void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); - + [animations_ release]; [super dealloc]; } @@ -89,7 +94,7 @@ -(void) removeAnimationByName:(NSString*)name { if( ! name ) return; - + [animations_ removeObjectForKey:name]; } @@ -98,4 +103,148 @@ -(CCAnimation*) animationByName:(NSString*)name return [animations_ objectForKey:name]; } +#pragma mark CCAnimationCache - from file + +-(void) parseVersion1:(NSDictionary*)animations +{ + NSArray* animationNames = [animations allKeys]; + CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache]; + + for( NSString *name in animationNames ) { + NSDictionary* animationDict = [animations objectForKey:name]; + NSArray *frameNames = [animationDict objectForKey:@"frames"]; + NSNumber *delay = [animationDict objectForKey:@"delay"]; + CCAnimation* animation = nil; + + if ( frameNames == nil ) { + CCLOG(@"cocos2d: CCAnimationCache: Animation '%@' found in dictionary without any frames - cannot add to animation cache.", name); + continue; + } + + NSMutableArray *frames = [NSMutableArray arrayWithCapacity:[frameNames count]]; + + for( NSString *frameName in frameNames ) { + CCSpriteFrame *spriteFrame = [frameCache spriteFrameByName:frameName]; + + if ( ! spriteFrame ) { + CCLOG(@"cocos2d: CCAnimationCache: Animation '%@' refers to frame '%@' which is not currently in the CCSpriteFrameCache. This frame will not be added to the animation.", name, frameName); + + continue; + } + + CCAnimationFrame *animFrame = [[CCAnimationFrame alloc] initWithSpriteFrame:spriteFrame delayUnits:1 userInfo:nil]; + [frames addObject:animFrame]; + [animFrame release]; + } + + if ( [frames count] == 0 ) { + CCLOG(@"cocos2d: CCAnimationCache: None of the frames for animation '%@' were found in the CCSpriteFrameCache. Animation is not being added to the Animation Cache.", name); + continue; + } else if ( [frames count] != [frameNames count] ) { + CCLOG(@"cocos2d: CCAnimationCache: An animation in your dictionary refers to a frame which is not in the CCSpriteFrameCache. Some or all of the frames for the animation '%@' may be missing.", name); + } + + animation = [CCAnimation animationWithAnimationFrames:frames delayPerUnit:[delay floatValue] loops:1 ]; + + [[CCAnimationCache sharedAnimationCache] addAnimation:animation name:name]; + } +} + +-(void) parseVersion2:(NSDictionary*)animations +{ + NSArray* animationNames = [animations allKeys]; + CCSpriteFrameCache *frameCache = [CCSpriteFrameCache sharedSpriteFrameCache]; + + for( NSString *name in animationNames ) + { + NSDictionary* animationDict = [animations objectForKey:name]; + + NSNumber *loops = [animationDict objectForKey:@"loops"]; + BOOL restoreOriginalFrame = [[animationDict objectForKey:@"restoreOriginalFrame"] boolValue]; + NSArray *frameArray = [animationDict objectForKey:@"frames"]; + + + if ( frameArray == nil ) { + CCLOG(@"cocos2d: CCAnimationCache: Animation '%@' found in dictionary without any frames - cannot add to animation cache.", name); + continue; + } + + // Array of AnimationFrames + NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:[frameArray count]]; + + for( NSDictionary *entry in frameArray ) { + NSString *spriteFrameName = [entry objectForKey:@"spriteframe"]; + CCSpriteFrame *spriteFrame = [frameCache spriteFrameByName:spriteFrameName]; + + if( ! spriteFrame ) { + CCLOG(@"cocos2d: CCAnimationCache: Animation '%@' refers to frame '%@' which is not currently in the CCSpriteFrameCache. This frame will not be added to the animation.", name, spriteFrameName); + + continue; + } + + float delayUnits = [[entry objectForKey:@"delayUnits"] floatValue]; + NSDictionary *userInfo = [entry objectForKey:@"notification"]; + + CCAnimationFrame *animFrame = [[CCAnimationFrame alloc] initWithSpriteFrame:spriteFrame delayUnits:delayUnits userInfo:userInfo]; + + [array addObject:animFrame]; + [animFrame release]; + } + + float delayPerUnit = [[animationDict objectForKey:@"delayPerUnit"] floatValue]; + CCAnimation *animation = [[CCAnimation alloc] initWithAnimationFrames:array delayPerUnit:delayPerUnit loops:(loops?[loops intValue]:1)]; + [array release]; + + [animation setRestoreOriginalFrame:restoreOriginalFrame]; + + [[CCAnimationCache sharedAnimationCache] addAnimation:animation name:name]; + [animation release]; + } +} + +-(void)addAnimationsWithDictionary:(NSDictionary *)dictionary +{ + NSDictionary *animations = [dictionary objectForKey:@"animations"]; + + if ( animations == nil ) { + CCLOG(@"cocos2d: CCAnimationCache: No animations were found in provided dictionary."); + return; + } + + NSUInteger version = 1; + NSDictionary *properties = [dictionary objectForKey:@"properties"]; + if( properties ) + version = [[properties objectForKey:@"format"] intValue]; + + NSArray *spritesheets = [properties objectForKey:@"spritesheets"]; + for( NSString *name in spritesheets ) + [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:name]; + + switch (version) { + case 1: + [self parseVersion1:animations]; + break; + case 2: + [self parseVersion2:animations]; + break; + default: + NSAssert(NO, @"Invalid animation format"); + } +} + + +/** Read an NSDictionary from a plist file and parse it automatically for animations */ +-(void)addAnimationsWithFile:(NSString *)plist +{ + NSAssert( plist, @"Invalid texture file name"); + + NSString *path = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:plist]; + NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; + + NSAssert1( dict, @"CCAnimationCache: File could not be found: %@", plist); + + + [self addAnimationsWithDictionary:dict]; +} + @end diff --git a/cocos2d/cocos2d/CCAtlasNode.h b/cocos2d/cocos2d/CCAtlasNode.h old mode 100644 new mode 100755 index c805812..5360e54 --- a/cocos2d/cocos2d/CCAtlasNode.h +++ b/cocos2d/cocos2d/CCAtlasNode.h @@ -4,17 +4,17 @@ * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. * - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -31,10 +31,10 @@ /** CCAtlasNode is a subclass of CCNode that implements the CCRGBAProtocol and CCTextureProtocol protocol - + It knows how to render a TextureAtlas object. If you are going to render a TextureAtlas consider subclassing CCAtlasNode (or a subclass of CCAtlasNode) - + All features from CCNode are valid, plus the following features: - opacity and RGB colors */ @@ -47,7 +47,7 @@ NSUInteger itemsPerRow_; // chars per column NSUInteger itemsPerColumn_; - + // width of each char NSUInteger itemWidth_; // height of each char @@ -59,11 +59,14 @@ // blend function ccBlendFunc blendFunc_; - // texture RGBA. + // texture RGBA. GLubyte opacity_; ccColor3B color_; ccColor3B colorUnmodified_; BOOL opacityModifyRGB_; + + // color uniform + GLint uniformColor_; } /** conforms to CCTextureProtocol protocol */ diff --git a/cocos2d/cocos2d/CCAtlasNode.m b/cocos2d/cocos2d/CCAtlasNode.m old mode 100644 new mode 100755 index 3f44633..5f16333 --- a/cocos2d/cocos2d/CCAtlasNode.m +++ b/cocos2d/cocos2d/CCAtlasNode.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,6 +26,14 @@ #import "CCAtlasNode.h" #import "ccMacros.h" +#import "CCGLProgram.h" +#import "CCShaderCache.h" +#import "ccGLStateCache.h" +#import "CCDirector.h" +#import "Support/TransformUtils.h" + +// external +#import "kazmath/GL/matrix.h" @interface CCAtlasNode () @@ -41,6 +49,12 @@ @implementation CCAtlasNode @synthesize quadsToDraw = quadsToDraw_; #pragma mark CCAtlasNode - Creation & Init +- (id) init +{ + NSAssert( NO, @"Not supported - Use initWtihTileFile instead"); + return self; +} + +(id) atlasWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c { return [[[self alloc] initWithTileFile:tile tileWidth:w tileHeight:h itemsToRender:c] autorelease]; @@ -49,35 +63,37 @@ +(id) atlasWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUI -(id) initWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUInteger)h itemsToRender: (NSUInteger) c { if( (self=[super init]) ) { - - itemWidth_ = w * CC_CONTENT_SCALE_FACTOR(); - itemHeight_ = h * CC_CONTENT_SCALE_FACTOR(); + + itemWidth_ = w; + itemHeight_ = h; opacity_ = 255; color_ = colorUnmodified_ = ccWHITE; opacityModifyRGB_ = YES; - + blendFunc_.src = CC_BLEND_SRC; blendFunc_.dst = CC_BLEND_DST; - - // double retain to avoid the autorelease pool - // also, using: self.textureAtlas supports re-initialization without leaking - self.textureAtlas = [[CCTextureAtlas alloc] initWithFile:tile capacity:c]; - [textureAtlas_ release]; + + CCTextureAtlas * newAtlas = [[CCTextureAtlas alloc] initWithFile:tile capacity:c]; + self.textureAtlas = newAtlas; + [newAtlas release]; if( ! textureAtlas_ ) { CCLOG(@"cocos2d: Could not initialize CCAtlasNode. Invalid Texture"); [self release]; return nil; } - + [self updateBlendFunc]; [self updateOpacityModifyRGB]; - + [self calculateMaxItems]; - + self.quadsToDraw = c; - + + // shader stuff + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTexture_uColor]; + uniformColor_ = glGetUniformLocation( shaderProgram_->program_, "u_color"); } return self; } @@ -85,7 +101,7 @@ -(id) initWithTileFile:(NSString*)tile tileWidth:(NSUInteger)w tileHeight:(NSUIn -(void) dealloc { [textureAtlas_ release]; - + [super dealloc]; } @@ -93,7 +109,7 @@ -(void) dealloc -(void) calculateMaxItems { - CGSize s = [[textureAtlas_ texture] contentSizeInPixels]; + CGSize s = [[textureAtlas_ texture] contentSize]; itemsPerColumn_ = s.height / itemHeight_; itemsPerRow_ = s.width / itemWidth_; } @@ -106,32 +122,14 @@ -(void) updateAtlasValues #pragma mark CCAtlasNode - draw - (void) draw { - [super draw]; - - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY - // Unneeded states: GL_COLOR_ARRAY - glDisableClientState(GL_COLOR_ARRAY); + CC_NODE_DRAW_SETUP(); - glColor4ub( color_.r, color_.g, color_.b, opacity_); - - BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; - if( newBlend ) - glBlendFunc( blendFunc_.src, blendFunc_.dst ); - - [textureAtlas_ drawNumberOfQuads:quadsToDraw_ fromIndex:0]; - - if( newBlend ) - glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + ccGLBlendFunc( blendFunc_.src, blendFunc_.dst ); - // is this chepear than saving/restoring color state ? - // XXX: There is no need to restore the color to (255,255,255,255). Objects should use the color - // XXX: that they need -// glColor4ub( 255, 255, 255, 255); - - // restore default GL state - glEnableClientState(GL_COLOR_ARRAY); + GLfloat colors[4] = {color_.r / 255.0f, color_.g / 255.0f, color_.b / 255.0f, opacity_ / 255.0f}; + [shaderProgram_ setUniformLocation:uniformColor_ with4fv:colors count:1]; + [textureAtlas_ drawNumberOfQuads:quadsToDraw_ fromIndex:0]; } #pragma mark CCAtlasNode - RGBA protocol @@ -140,19 +138,19 @@ - (ccColor3B) color { if(opacityModifyRGB_) return colorUnmodified_; - + return color_; } -(void) setColor:(ccColor3B)color3 { color_ = colorUnmodified_ = color3; - + if( opacityModifyRGB_ ){ color_.r = color3.r * opacity_/255; color_.g = color3.g * opacity_/255; color_.b = color3.b * opacity_/255; - } + } } -(GLubyte) opacity @@ -163,10 +161,10 @@ -(GLubyte) opacity -(void) setOpacity:(GLubyte) anOpacity { opacity_ = anOpacity; - + // special opacity for premultiplied textures if( opacityModifyRGB_ ) - [self setColor: colorUnmodified_]; + [self setColor: colorUnmodified_]; } -(void) setOpacityModifyRGB:(BOOL)modify @@ -186,7 +184,7 @@ -(void) updateOpacityModifyRGB opacityModifyRGB_ = [textureAtlas_.texture hasPremultipliedAlpha]; } -#pragma mark CCAtlasNode - CocosNodeTexture protocol +#pragma mark CCAtlasNode - CCNodeTexture protocol -(void) updateBlendFunc { diff --git a/cocos2d/cocos2d/CCCamera.h b/cocos2d/cocos2d/CCCamera.h old mode 100644 new mode 100755 index 19a7712..415caf7 --- a/cocos2d/cocos2d/CCCamera.h +++ b/cocos2d/cocos2d/CCCamera.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,8 +26,9 @@ #import "CCNode.h" +#import "kazmath/mat4.h" -/** +/** A CCCamera is used in every CCNode. Useful to look at the object from different views. The OpenGL gluLookAt() function is used to locate the @@ -35,19 +36,19 @@ If the object is transformed by any of the scale, rotation or position attributes, then they will override the camera. - + IMPORTANT: Either your use the camera or the rotation/scale/position properties. You can't use both. World coordinates won't work if you use the camera. Limitations: - + - Some nodes, like CCParallaxNode, CCParticle uses world node coordinates, and they won't work properly if you move them (or any of their ancestors) using the camera. - + - It doesn't work on batched nodes like CCSprite objects when they are parented to a CCSpriteBatchNode object. - + - It is recommended to use it ONLY if you are going to create 3D effects. For 2D effecs, use the action CCFollow or position/scale/rotate. - + */ @interface CCCamera : NSObject @@ -63,8 +64,10 @@ float upX_; float upY_; float upZ_; - + BOOL dirty_; + + kmMat4 lookupMatrix_; } /** whether of not the camera is dirty */ diff --git a/cocos2d/cocos2d/CCCamera.m b/cocos2d/cocos2d/CCCamera.m old mode 100644 new mode 100755 index 1ef6655..70d028d --- a/cocos2d/cocos2d/CCCamera.m +++ b/cocos2d/cocos2d/CCCamera.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,6 +28,7 @@ #import "CCCamera.h" #import "ccMacros.h" #import "CCDrawingPrimitives.h" +#import "kazmath/GL/matrix.h" @implementation CCCamera @@ -37,13 +38,13 @@ -(id) init { if( (self=[super init]) ) [self restore]; - + return self; } - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | center = (%.2f,%.2f,%.2f)>", [self class], self, centerX_, centerY_, centerZ_]; + return [NSString stringWithFormat:@"<%@ = %p | center = (%.2f,%.2f,%.2f)>", [self class], self, centerX_, centerY_, centerZ_]; } @@ -57,45 +58,60 @@ -(void) restore { eyeX_ = eyeY_ = 0; eyeZ_ = [CCCamera getZEye]; - + centerX_ = centerY_ = centerZ_ = 0; - + upX_ = 0.0f; upY_ = 1.0f; upZ_ = 0.0f; - + + kmMat4Identity( &lookupMatrix_ ); + dirty_ = NO; } -(void) locate { - if( dirty_ ) - gluLookAt( eyeX_, eyeY_, eyeZ_, - centerX_, centerY_, centerZ_, - upX_, upY_, upZ_ - ); + if( dirty_ ) { + + kmVec3 eye, center, up; + + kmVec3Fill( &eye, eyeX_, eyeY_ , eyeZ_ ); + kmVec3Fill( ¢er, centerX_, centerY_, centerZ_ ); + + kmVec3Fill( &up, upX_, upY_, upZ_); + kmMat4LookAt( &lookupMatrix_, &eye, ¢er, &up); + + dirty_ = NO; + + } + + kmGLMultMatrix( &lookupMatrix_ ); + } +(float) getZEye { return FLT_EPSILON; -// CGSize s = [[CCDirector sharedDirector] displaySize]; -// return ( s.height / 1.1566f ); + // CGSize s = [[CCDirector sharedDirector] displaySize]; + // return ( s.height / 1.1566f ); } -(void) setEyeX: (float)x eyeY:(float)y eyeZ:(float)z { - eyeX_ = x * CC_CONTENT_SCALE_FACTOR(); - eyeY_ = y * CC_CONTENT_SCALE_FACTOR(); - eyeZ_ = z * CC_CONTENT_SCALE_FACTOR(); - dirty_ = YES; + eyeX_ = x; + eyeY_ = y; + eyeZ_ = z; + + dirty_ = YES; } -(void) setCenterX: (float)x centerY:(float)y centerZ:(float)z { - centerX_ = x * CC_CONTENT_SCALE_FACTOR(); - centerY_ = y * CC_CONTENT_SCALE_FACTOR(); - centerZ_ = z * CC_CONTENT_SCALE_FACTOR(); + centerX_ = x; + centerY_ = y; + centerZ_ = z; + dirty_ = YES; } @@ -104,21 +120,22 @@ -(void) setUpX: (float)x upY:(float)y upZ:(float)z upX_ = x; upY_ = y; upZ_ = z; + dirty_ = YES; } -(void) eyeX: (float*)x eyeY:(float*)y eyeZ:(float*)z { - *x = eyeX_ / CC_CONTENT_SCALE_FACTOR(); - *y = eyeY_ / CC_CONTENT_SCALE_FACTOR(); - *z = eyeZ_ / CC_CONTENT_SCALE_FACTOR(); + *x = eyeX_; + *y = eyeY_; + *z = eyeZ_; } -(void) centerX: (float*)x centerY:(float*)y centerZ:(float*)z { - *x = centerX_ / CC_CONTENT_SCALE_FACTOR(); - *y = centerY_ / CC_CONTENT_SCALE_FACTOR(); - *z = centerZ_ / CC_CONTENT_SCALE_FACTOR(); + *x = centerX_; + *y = centerY_; + *z = centerZ_; } -(void) upX: (float*)x upY:(float*)y upZ:(float*)z diff --git a/cocos2d/cocos2d/CCConfiguration.h b/cocos2d/cocos2d/CCConfiguration.h old mode 100644 new mode 100755 index 04e1b55..1d728dd --- a/cocos2d/cocos2d/CCConfiguration.h +++ b/cocos2d/cocos2d/CCConfiguration.h @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,25 +30,25 @@ /** OS version definitions. Includes both iOS and Mac OS versions */ enum { - kCCiOSVersion_3_0 = 0x03000000, - kCCiOSVersion_3_1 = 0x03010000, - kCCiOSVersion_3_1_1 = 0x03010100, - kCCiOSVersion_3_1_2 = 0x03010200, - kCCiOSVersion_3_1_3 = 0x03010300, - kCCiOSVersion_3_2 = 0x03020000, - kCCiOSVersion_3_2_1 = 0x03020100, kCCiOSVersion_4_0 = 0x04000000, kCCiOSVersion_4_0_1 = 0x04000100, kCCiOSVersion_4_1 = 0x04010000, kCCiOSVersion_4_2 = 0x04020000, + kCCiOSVersion_4_2_1 = 0x04020100, kCCiOSVersion_4_3 = 0x04030000, kCCiOSVersion_4_3_1 = 0x04030100, kCCiOSVersion_4_3_2 = 0x04030200, kCCiOSVersion_4_3_3 = 0x04030300, - - kCCMacVersion_10_5 = 0x0a050000, + kCCiOSVersion_4_3_4 = 0x04030400, + kCCiOSVersion_4_3_5 = 0x04030500, + kCCiOSVersion_5_0 = 0x05000000, + kCCiOSVersion_5_0_1 = 0x05000100, + kCCiOSVersion_5_1_0 = 0x05010000, + kCCiOSVersion_6_0_0 = 0x06000000, + kCCMacVersion_10_6 = 0x0a060000, kCCMacVersion_10_7 = 0x0a070000, + kCCMacVersion_10_8 = 0x0a080000, }; /** @@ -63,8 +63,10 @@ enum { BOOL supportsNPOT_; BOOL supportsBGRA8888_; BOOL supportsDiscardFramebuffer_; + BOOL supportsShareableVAO_; unsigned int OSVersion_; GLint maxSamplesAllowed_; + GLint maxTextureUnits_; } /** OpenGL Max texture size. */ @@ -73,11 +75,14 @@ enum { /** OpenGL Max Modelview Stack Depth. */ @property (nonatomic, readonly) GLint maxModelviewStackDepth; +/** returns the maximum texture units + @since v2.0.0 + */ +@property (nonatomic, readonly) GLint maxTextureUnits; + /** Whether or not the GPU supports NPOT (Non Power Of Two) textures. - NPOT textures have the following limitations: - - They can't have mipmaps - - They only accept GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T} - + OpenGL ES 2.0 already supports NPOT (iOS). + @since v0.99.2 */ @property (nonatomic, readonly) BOOL supportsNPOT; @@ -86,21 +91,26 @@ enum { @property (nonatomic, readonly) BOOL supportsPVRTC; /** Whether or not BGRA8888 textures are supported. - + @since v0.99.2 */ @property (nonatomic, readonly) BOOL supportsBGRA8888; /** Whether or not glDiscardFramebufferEXT is supported - + @since v0.99.2 */ @property (nonatomic, readonly) BOOL supportsDiscardFramebuffer; +/** Whether or not shareable VAOs are supported. + @since v2.0.0 + */ +@property (nonatomic, readonly) BOOL supportsShareableVAO; + /** returns the OS version. - On iOS devices it returns the firmware version. - On Mac returns the OS version - + @since v0.99.5 */ @property (nonatomic, readonly) unsigned int OSVersion; diff --git a/cocos2d/cocos2d/CCConfiguration.m b/cocos2d/cocos2d/CCConfiguration.m old mode 100644 new mode 100755 index d51cd58..94fa945 --- a/cocos2d/cocos2d/CCConfiguration.m +++ b/cocos2d/cocos2d/CCConfiguration.m @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,14 +23,13 @@ * THE SOFTWARE. */ -#import +#import "ccMacros.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import // Needed for UIDevice #endif #import "Platforms/CCGL.h" -#import "CCBlockSupport.h" #import "CCConfiguration.h" #import "ccMacros.h" #import "ccConfig.h" @@ -38,12 +37,13 @@ @implementation CCConfiguration -@synthesize maxTextureSize = maxTextureSize_; +@synthesize maxTextureSize = maxTextureSize_, maxTextureUnits=maxTextureUnits_; @synthesize supportsPVRTC = supportsPVRTC_; @synthesize maxModelviewStackDepth = maxModelviewStackDepth_; @synthesize supportsNPOT = supportsNPOT_; @synthesize supportsBGRA8888 = supportsBGRA8888_; @synthesize supportsDiscardFramebuffer = supportsDiscardFramebuffer_; +@synthesize supportsShareableVAO = supportsShareableVAO_; @synthesize OSVersion = OSVersion_; // @@ -68,105 +68,100 @@ +(id)alloc } -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#ifdef __CC_PLATFORM_IOS +#elif defined(__CC_PLATFORM_MAC) - (NSString*)getMacVersion { SInt32 versionMajor, versionMinor, versionBugFix; Gestalt(gestaltSystemVersionMajor, &versionMajor); Gestalt(gestaltSystemVersionMinor, &versionMinor); Gestalt(gestaltSystemVersionBugFix, &versionBugFix); - + return [NSString stringWithFormat:@"%d.%d.%d", versionMajor, versionMinor, versionBugFix]; } -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_MAC -(id) init { if( (self=[super init])) { - + // Obtain iOS version OSVersion_ = 0; -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS NSString *OSVer = [[UIDevice currentDevice] systemVersion]; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) NSString *OSVer = [self getMacVersion]; #endif - NSArray *arr = [OSVer componentsSeparatedByString:@"."]; - int idx=0x01000000; + NSArray *arr = [OSVer componentsSeparatedByString:@"."]; + int idx = 0x01000000; for( NSString *str in arr ) { int value = [str intValue]; OSVersion_ += value * idx; idx = idx >> 8; } CCLOG(@"cocos2d: OS version: %@ (0x%08x)", OSVer, OSVersion_); - + CCLOG(@"cocos2d: GL_VENDOR: %s", glGetString(GL_VENDOR) ); CCLOG(@"cocos2d: GL_RENDERER: %s", glGetString ( GL_RENDERER ) ); CCLOG(@"cocos2d: GL_VERSION: %s", glGetString ( GL_VERSION ) ); - + glExtensions = (char*) glGetString(GL_EXTENSIONS); - + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_); - glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &maxModelviewStackDepth_); -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits_ ); + +#ifdef __CC_PLATFORM_IOS if( OSVersion_ >= kCCiOSVersion_4_0 ) glGetIntegerv(GL_MAX_SAMPLES_APPLE, &maxSamplesAllowed_); else maxSamplesAllowed_ = 0; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) glGetIntegerv(GL_MAX_SAMPLES, &maxSamplesAllowed_); #endif - + supportsPVRTC_ = [self checkForGLExtension:@"GL_IMG_texture_compression_pvrtc"]; -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - supportsNPOT_ = [self checkForGLExtension:@"GL_APPLE_texture_2D_limited_npot"]; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#ifdef __CC_PLATFORM_IOS + supportsNPOT_ = YES; +#elif defined(__CC_PLATFORM_MAC) supportsNPOT_ = [self checkForGLExtension:@"GL_ARB_texture_non_power_of_two"]; #endif // It seems that somewhere between firmware iOS 3.0 and 4.2 Apple renamed // GL_IMG_... to GL_APPLE.... So we should check both names - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +#ifdef __CC_PLATFORM_IOS BOOL bgra8a = [self checkForGLExtension:@"GL_IMG_texture_format_BGRA8888"]; BOOL bgra8b = [self checkForGLExtension:@"GL_APPLE_texture_format_BGRA8888"]; supportsBGRA8888_ = bgra8a | bgra8b; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) supportsBGRA8888_ = [self checkForGLExtension:@"GL_EXT_bgra"]; #endif + + supportsShareableVAO_ = [self checkForGLExtension:@"GL_APPLE_vertex_array_object"]; + supportsDiscardFramebuffer_ = [self checkForGLExtension:@"GL_EXT_discard_framebuffer"]; CCLOG(@"cocos2d: GL_MAX_TEXTURE_SIZE: %d", maxTextureSize_); - CCLOG(@"cocos2d: GL_MAX_MODELVIEW_STACK_DEPTH: %d",maxModelviewStackDepth_); + CCLOG(@"cocos2d: GL_MAX_TEXTURE_UNITS: %d", maxTextureUnits_); CCLOG(@"cocos2d: GL_MAX_SAMPLES: %d", maxSamplesAllowed_); CCLOG(@"cocos2d: GL supports PVRTC: %s", (supportsPVRTC_ ? "YES" : "NO") ); CCLOG(@"cocos2d: GL supports BGRA8888 textures: %s", (supportsBGRA8888_ ? "YES" : "NO") ); CCLOG(@"cocos2d: GL supports NPOT textures: %s", (supportsNPOT_ ? "YES" : "NO") ); CCLOG(@"cocos2d: GL supports discard_framebuffer: %s", (supportsDiscardFramebuffer_ ? "YES" : "NO") ); - CCLOG(@"cocos2d: compiled with NPOT support: %s", -#if CC_TEXTURE_NPOT_SUPPORT - "YES" -#else - "NO" -#endif - ); - CCLOG(@"cocos2d: compiled with VBO support in TextureAtlas : %s", -#if CC_USES_VBO - "YES" -#else - "NO" -#endif + CCLOG(@"cocos2d: GL supports shareable VAO: %s", (supportsShareableVAO_ ? "YES" : "NO") ); + +#ifdef __CC_PLATFORM_MAC + CCLOG(@"cocos2d: Director's thread: %@", +#if (CC_DIRECTOR_MAC_THREAD == CC_MAC_USE_MAIN_THREAD) + @"Main thread" +#elif (CC_DIRECTOR_MAC_THREAD == CC_MAC_USE_OWN_THREAD) + @"Own thread" +#elif (CC_DIRECTOR_MAC_THREAD == CC_MAC_USE_DISPLAY_LINK_THREAD) + @"DisplayLink thread" +#endif // ); +#endif // Mac - CCLOG(@"cocos2d: compiled with Affine Matrix transformation in CCNode : %s", -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - "YES" -#else - "NO" -#endif - ); - CCLOG(@"cocos2d: compiled with Profiling Support: %s", #if CC_ENABLE_PROFILERS @@ -175,10 +170,17 @@ -(id) init "NO" #endif ); - - CHECK_GL_ERROR(); + } - + +#if CC_ENABLE_GL_STATE_CACHE == 0 + printf("\n"); + NSLog(@"cocos2d: **** WARNING **** CC_ENABLE_GL_STATE_CACHE is disabled. To improve performance, enable it by editing ccConfig.h"); + printf("\n"); +#endif + + CHECK_GL_ERROR_DEBUG(); + return self; } diff --git a/cocos2d/cocos2d/CCDirector.h b/cocos2d/cocos2d/CCDirector.h old mode 100644 new mode 100755 index 5a4424f..087ce17 --- a/cocos2d/cocos2d/CCDirector.h +++ b/cocos2d/cocos2d/CCDirector.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,10 +26,11 @@ #import "ccConfig.h" #import "ccTypes.h" +#import "ccMacros.h" -// OpenGL related -#import "Platforms/CCGL.h" #import "CCProtocols.h" +#import "Platforms/CCGL.h" +#import "kazmath/mat4.h" /** @typedef ccDirectorProjection Possible OpenGL projections used by director @@ -37,111 +38,120 @@ typedef enum { /// sets a 2D projection (orthogonal projection). kCCDirectorProjection2D, - + /// sets a 3D projection with a fovy=60, znear=0.5f and zfar=1500. kCCDirectorProjection3D, - + /// it calls "updateProjection" on the projection delegate. kCCDirectorProjectionCustom, - + /// Detault projection is 3D projection kCCDirectorProjectionDefault = kCCDirectorProjection3D, - - // backward compatibility stuff - CCDirectorProjection2D = kCCDirectorProjection2D, - CCDirectorProjection3D = kCCDirectorProjection3D, - CCDirectorProjectionCustom = kCCDirectorProjectionCustom, } ccDirectorProjection; @class CCLabelAtlas; @class CCScene; +@class CCScheduler; +@class CCActionManager; + + +#ifdef __CC_PLATFORM_IOS +#define CC_VIEWCONTROLLER UIViewController +#elif defined(__CC_PLATFORM_MAC) +#define CC_VIEWCONTROLLER NSObject +#endif /**Class that creates and handle the main Window and manages how and when to execute the Scenes. - + The CCDirector is also resposible for: - initializing the OpenGL ES context - setting the OpenGL pixel format (default on is RGB565) - setting the OpenGL buffer depth (default one is 0-bit) - setting the projection (default one is 3D) - - setting the orientation (default one is Protrait) - + Since the CCDirector is a singleton, the standard way to use it is by calling: - [[CCDirector sharedDirector] methodName]; - + The CCDirector also sets the default OpenGL context: - GL_TEXTURE_2D is enabled - GL_VERTEX_ARRAY is enabled - GL_COLOR_ARRAY is enabled - GL_TEXTURE_COORD_ARRAY is enabled */ -@interface CCDirector : NSObject +@interface CCDirector : CC_VIEWCONTROLLER { - CC_GLVIEW *openGLView_; - // internal timer NSTimeInterval animationInterval_; - NSTimeInterval oldAnimationInterval_; - - /* display FPS ? */ - BOOL displayFPS_; + NSTimeInterval oldAnimationInterval_; + + /* stats */ + BOOL displayStats_; NSUInteger frames_; NSUInteger totalFrames_; + ccTime secondsPerFrame_; - ccTime accumDt_; - ccTime frameRate_; -#if CC_DIRECTOR_FAST_FPS + ccTime accumDt_; + ccTime frameRate_; CCLabelAtlas *FPSLabel_; -#endif - + CCLabelAtlas *SPFLabel_; + CCLabelAtlas *drawsLabel_; + /* is the running scene paused */ BOOL isPaused_; - + + /* Is the director running */ + BOOL isAnimating_; + /* The running scene */ CCScene *runningScene_; - + /* This object will be visited after the scene. Useful to hook a notification node */ id notificationNode_; - + /* will be the next 'runningScene' in the next frame nextScene is a weak reference. */ CCScene *nextScene_; - + /* If YES, then "old" scene will receive the cleanup message */ BOOL sendCleanupToScene_; /* scheduled scenes */ NSMutableArray *scenesStack_; - + /* last time the main loop was updated */ struct timeval lastUpdate_; /* delta time since last tick to main loop */ ccTime dt; /* whether or not the next delta time will be zero */ BOOL nextDeltaTimeZero_; - + /* projection used */ ccDirectorProjection projection_; - - /* Projection protocol delegate */ - id projectionDelegate_; + + /* CCDirector delegate */ + id delegate_; /* window size in points */ CGSize winSizeInPoints_; - + /* window size in pixels */ CGSize winSizeInPixels_; /* the cocos2d running thread */ NSThread *runningThread_; - // profiler -#if CC_ENABLE_PROFILERS - ccTime accumDtForProfiler_; -#endif + /* scheduler associated with this director */ + CCScheduler *scheduler_; + + /* action manager associated with this director */ + CCActionManager *actionManager_; + + /* OpenGLView. On iOS it is a copy of self.view */ + CCGLView *view_; } /** returns the cocos2d thread. @@ -154,20 +164,20 @@ and when to execute the Scenes. @property (nonatomic,readonly) CCScene* runningScene; /** The FPS value */ @property (nonatomic,readwrite, assign) NSTimeInterval animationInterval; -/** Whether or not to display the FPS on the bottom-left corner */ -@property (nonatomic,readwrite, assign) BOOL displayFPS; -/** The OpenGLView, where everything is rendered */ -@property (nonatomic,readwrite,retain) CC_GLVIEW *openGLView; +/** Whether or not to display director statistics */ +@property (nonatomic, readwrite, assign) BOOL displayStats; /** whether or not the next delta time will be zero */ @property (nonatomic,readwrite,assign) BOOL nextDeltaTimeZero; /** Whether or not the Director is paused */ @property (nonatomic,readonly) BOOL isPaused; -/** Sets an OpenGL projection - @since v0.8.2 - */ +/** Whether or not the Director is active (animating) */ +@property (nonatomic,readonly) BOOL isAnimating; +/** Sets an OpenGL projection */ @property (nonatomic,readwrite) ccDirectorProjection projection; /** How many frames were called since the director started */ @property (nonatomic,readonly) NSUInteger totalFrames; +/** seconds per frame */ +@property (nonatomic, readonly) ccTime secondsPerFrame; /** Whether or not the replaced scene will receive the cleanup message. If the new scene is pushed, then the old scene won't receive the "cleanup" message. @@ -183,32 +193,36 @@ and when to execute the Scenes. */ @property (nonatomic, readwrite, retain) id notificationNode; -/** This object will be called when the OpenGL projection is udpated and only when the kCCDirectorProjectionCustom projection is used. +/** CCDirector delegate. It shall implemente the CCDirectorDelegate protocol @since v0.99.5 */ -@property (nonatomic, readwrite, retain) id projectionDelegate; +@property (nonatomic, readwrite, retain) id delegate; -/** returns a shared instance of the director */ -+(CCDirector *)sharedDirector; +/** CCScheduler associated with this director + @since v2.0 + */ +@property (nonatomic,readwrite,retain) CCScheduler *scheduler; +/** CCActionManager associated with this director + @since v2.0 + */ +@property (nonatomic,readwrite,retain) CCActionManager *actionManager; +/** returns a shared instance of the director */ ++(CCDirector*)sharedDirector; -// Window size -/** returns the size of the OpenGL view in points. - It takes into account any possible rotation (device orientation) of the window - */ +#pragma mark Director - Stats + +#pragma mark Director - Win Size +/** returns the size of the OpenGL view in points */ - (CGSize) winSize; /** returns the size of the OpenGL view in pixels. - It takes into account any possible rotation (device orientation) of the window. On Mac winSize and winSizeInPixels return the same value. */ - (CGSize) winSizeInPixels; -/** returns the display size of the OpenGL view in pixels. - It doesn't take into account any possible rotation of the window. - */ --(CGSize) displaySizeInPixels; + /** changes the projection size */ -(void) reshapeProjection:(CGSize)newWindowSize; @@ -224,17 +238,19 @@ and when to execute the Scenes. /// XXX: missing description -(float) getZEye; -// Scene Management +#pragma mark Director - Scene Management -/**Enters the Director's main loop with the given Scene. +/**Enters the Director's main loop with the given Scene. * Call it to run only your FIRST scene. * Don't call it if there is already a running scene. + * + * It will call pushScene: and then it will call startAnimation */ - (void) runWithScene:(CCScene*) scene; /**Suspends the execution of the running scene, pushing it on the stack of suspended scenes. * The new scene will be executed. - * Try to avoid big stacks of pushed scenes to reduce memory allocation. + * Try to avoid big stacks of pushed scenes to reduce memory allocation. * ONLY call it if there is a running scene. */ - (void) pushScene:(CCScene*) scene; @@ -246,6 +262,13 @@ and when to execute the Scenes. */ - (void) popScene; +/**Pops out all scenes from the queue until the root scene in the queue. + * This scene will replace the running one. + * The running scene will be deleted. If there are no more scenes in the stack the execution is terminated. + * ONLY call it if there is a running scene. + */ +- (void) popToRootScene; + /** Replaces the running scene with a new one. The running scene is terminated. * ONLY call it if there is a running scene. */ @@ -284,26 +307,38 @@ and when to execute the Scenes. */ -(void) drawScene; -// Memory Helper + +// XXX: Hack. Should be placed on CCDirectorMac.h. Refactoring needed +#if defined(__CC_PLATFORM_MAC) +/** sets the openGL view */ +-(void) setView:(CCGLView*)view; + +/** returns the OpenGL view */ +-(CCGLView*) view; +#endif + +#pragma mark Director - Memory Helper /** Removes all the cocos2d data that was cached automatically. It will purge the CCTextureCache, CCLabelBMFont cache. IMPORTANT: The CCSpriteFrameCache won't be purged. If you want to purge it, you have to purge it manually. @since v0.99.3 */ --(void) purgeCachedData; +-(void) purgeCachedData; // OpenGL Helper /** sets the OpenGL default values */ -(void) setGLDefaultValues; - /** enables/disables OpenGL alpha blending */ - (void) setAlphaBlending: (BOOL) on; /** enables/disables OpenGL depth test */ - (void) setDepthTest: (BOOL) on; -// Profiler --(void) showProfilers; - +// helper +/** creates the Stats labels */ +-(void) createStatsLabel; @end + +// optimization. Should only be used to read it. Never to write it. +extern NSUInteger __ccNumberOfDraws; diff --git a/cocos2d/cocos2d/CCDirector.m b/cocos2d/cocos2d/CCDirector.m old mode 100644 new mode 100755 index 6640f37..3e3e253 --- a/cocos2d/cocos2d/CCDirector.m +++ b/cocos2d/cocos2d/CCDirector.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,7 +28,6 @@ */ #import -#import // cocos2d imports #import "CCDirector.h" @@ -44,6 +43,8 @@ #import "CCTexture2D.h" #import "CCLabelBMFont.h" #import "CCLayer.h" +#import "ccGLStateCache.h" +#import "CCShaderCache.h" // support imports #import "Platforms/CCGL.h" @@ -51,42 +52,55 @@ #import "Support/OpenGL_Internal.h" #import "Support/CGPointExtension.h" +#import "Support/CCProfiling.h" +#import "Support/CCFileUtils.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import "Platforms/iOS/CCDirectorIOS.h" -#define CC_DIRECTOR_DEFAULT CCDirectorTimer -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#define CC_DIRECTOR_DEFAULT CCDirectorDisplayLink +#elif defined(__CC_PLATFORM_MAC) #import "Platforms/Mac/CCDirectorMac.h" #define CC_DIRECTOR_DEFAULT CCDirectorDisplayLink #endif -#import "Support/CCProfiling.h" + +#pragma mark - +#pragma mark Director - global variables (optimization) + +// XXX it shoul be a Director ivar. Move it there once support for multiple directors is added +NSUInteger __ccNumberOfDraws = 0; #define kDefaultFPS 60.0 // 60 frames per second extern NSString * cocos2dVersion(void); - @interface CCDirector (Private) -(void) setNextScene; -// shows the FPS in the screen --(void) showFPS; +// shows the statistics +-(void) showStats; // calculates delta time since last time it was called -(void) calculateDeltaTime; +// calculates the milliseconds per frame from the start of the frame +-(void) calculateMPF; @end @implementation CCDirector @synthesize animationInterval = animationInterval_; @synthesize runningScene = runningScene_; -@synthesize displayFPS = displayFPS_; +@synthesize displayStats = displayStats_; @synthesize nextDeltaTimeZero = nextDeltaTimeZero_; @synthesize isPaused = isPaused_; +@synthesize isAnimating = isAnimating_; @synthesize sendCleanupToScene = sendCleanupToScene_; @synthesize runningThread = runningThread_; @synthesize notificationNode = notificationNode_; -@synthesize projectionDelegate = projectionDelegate_; +@synthesize delegate = delegate_; @synthesize totalFrames = totalFrames_; +@synthesize secondsPerFrame = secondsPerFrame_; +@synthesize scheduler = scheduler_; +@synthesize actionManager = actionManager_; + // // singleton stuff // @@ -97,14 +111,14 @@ + (CCDirector *)sharedDirector if (!_sharedDirector) { // - // Default Director is TimerDirector - // + // Default Director is DisplayLink + // if( [ [CCDirector class] isEqual:[self class]] ) _sharedDirector = [[CC_DIRECTOR_DEFAULT alloc] init]; else _sharedDirector = [[self alloc] init]; } - + return _sharedDirector; } @@ -115,102 +129,106 @@ +(id)alloc } - (id) init -{ +{ CCLOG(@"cocos2d: %@", cocos2dVersion() ); - if( (self=[super init]) ) { + if( (self=[super init] ) ) { CCLOG(@"cocos2d: Using Director Type:%@", [self class]); - + // scenes runningScene_ = nil; nextScene_ = nil; - + notificationNode_ = nil; - + oldAnimationInterval_ = animationInterval_ = 1.0 / kDefaultFPS; scenesStack_ = [[NSMutableArray alloc] initWithCapacity:10]; - + // Set default projection (3D) projection_ = kCCDirectorProjectionDefault; // projection delegate if "Custom" projection is used - projectionDelegate_ = nil; + delegate_ = nil; // FPS - displayFPS_ = NO; + displayStats_ = NO; totalFrames_ = frames_ = 0; - + // paused ? isPaused_ = NO; - + // running thread runningThread_ = nil; - + + // scheduler + scheduler_ = [[CCScheduler alloc] init]; + + // action manager + actionManager_ = [[CCActionManager alloc] init]; + [scheduler_ scheduleUpdateForTarget:actionManager_ priority:kCCPrioritySystem paused:NO]; + winSizeInPixels_ = winSizeInPoints_ = CGSizeZero; } return self; } +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %p | Size: %0.f x %0.f, view = %@>", [self class], self, winSizeInPoints_.width, winSizeInPoints_.height, view_]; +} + - (void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); -#if CC_DIRECTOR_FAST_FPS [FPSLabel_ release]; -#endif + [SPFLabel_ release]; + [drawsLabel_ release]; [runningScene_ release]; [notificationNode_ release]; [scenesStack_ release]; - - [projectionDelegate_ release]; - + [scheduler_ release]; + [actionManager_ release]; + [delegate_ release]; + _sharedDirector = nil; - + [super dealloc]; } -(void) setGLDefaultValues { - // This method SHOULD be called only after openGLView_ was initialized - NSAssert( openGLView_, @"openGLView_ must be initialized"); + // This method SHOULD be called only after view_ was initialized + NSAssert( view_, @"view_ must be initialized"); [self setAlphaBlending: YES]; - [self setDepthTest: YES]; + [self setDepthTest: view_.depthFormat]; [self setProjection: projection_]; - + // set other opengl default values glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - -#if CC_DIRECTOR_FAST_FPS - if (!FPSLabel_) { - CCTexture2DPixelFormat currentFormat = [CCTexture2D defaultAlphaPixelFormat]; - [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444]; - FPSLabel_ = [[CCLabelAtlas labelWithString:@"00.0" charMapFile:@"fps_images.png" itemWidth:16 itemHeight:24 startCharMap:'.'] retain]; - [CCTexture2D setDefaultAlphaPixelFormat:currentFormat]; - } -#endif // CC_DIRECTOR_FAST_FPS } // // Draw the Scene // - (void) drawScene -{ +{ // Override me } -(void) calculateDeltaTime { struct timeval now; - + if( gettimeofday( &now, NULL) != 0 ) { CCLOG(@"cocos2d: error in gettimeofday"); dt = 0; return; } - + // new delta time if( nextDeltaTimeZero_ ) { dt = 0; @@ -225,16 +243,17 @@ -(void) calculateDeltaTime if( dt > 0.2f ) dt = 1/60.0f; #endif - - lastUpdate_ = now; + + lastUpdate_ = now; } #pragma mark Director - Memory Helper -(void) purgeCachedData { - [CCLabelBMFont purgeCachedData]; - [[CCTextureCache sharedTextureCache] removeUnusedTextures]; + [CCLabelBMFont purgeCachedData]; + [[CCTextureCache sharedTextureCache] removeUnusedTextures]; + [[CCFileUtils sharedFileUtils] purgeCachedEntries]; } #pragma mark Director - Scene OpenGL Helper @@ -246,7 +265,7 @@ -(ccDirectorProjection) projection -(float) getZEye { - return ( winSizeInPixels_.height / 1.1566f ); + return ( winSizeInPixels_.height / 1.1566f / CC_CONTENT_SCALE_FACTOR() ); } -(void) setProjection:(ccDirectorProjection)projection @@ -257,46 +276,62 @@ -(void) setProjection:(ccDirectorProjection)projection - (void) setAlphaBlending: (BOOL) on { if (on) { - glEnable(GL_BLEND); - glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); - + ccGLEnable(CC_GL_BLEND); + ccGLBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + } else glDisable(GL_BLEND); + + CHECK_GL_ERROR_DEBUG(); } - (void) setDepthTest: (BOOL) on { if (on) { - ccglClearDepth(1.0f); + glClearDepth(1.0f); + glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); // glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); } else glDisable( GL_DEPTH_TEST ); + + CHECK_GL_ERROR_DEBUG(); } #pragma mark Director Integration with a UIKit view --(CC_GLVIEW*) openGLView +-(void) setView:(CCGLView*)view { - return openGLView_; -} +// NSAssert( view, @"OpenGLView must be non-nil"); --(void) setOpenGLView:(CC_GLVIEW *)view -{ - NSAssert( view, @"OpenGLView must be non-nil"); + if( view != view_ ) { + +#ifdef __CC_PLATFORM_IOS + [super setView:view]; +#endif + [view_ release]; + view_ = [view retain]; - if( view != openGLView_ ) { - [openGLView_ release]; - openGLView_ = [view retain]; - // set size - winSizeInPixels_ = winSizeInPoints_ = CCNSSizeToCGSize( [view bounds].size ); + winSizeInPixels_ = winSizeInPoints_ = CCNSSizeToCGSize( [view_ bounds].size ); - [self setGLDefaultValues]; + [self createStatsLabel]; + + // it could be nil + if( view ) + [self setGLDefaultValues]; + + CHECK_GL_ERROR_DEBUG(); } } +-(CCGLView*) view +{ + return view_; +} + + #pragma mark Director Scene Landscape -(CGPoint)convertToGL:(CGPoint)uiPoint @@ -321,11 +356,6 @@ -(CGSize)winSizeInPixels return winSizeInPixels_; } --(CGSize)displaySizeInPixels -{ - return winSizeInPixels_; -} - -(void) reshapeProjection:(CGSize)newWindowSize { winSizeInPixels_ = winSizeInPoints_ = newWindowSize; @@ -337,10 +367,9 @@ -(void) reshapeProjection:(CGSize)newWindowSize - (void)runWithScene:(CCScene*) scene { NSAssert( scene != nil, @"Argument must be non-nil"); - NSAssert( runningScene_ == nil, @"You can't run an scene if another Scene is running. Use replaceScene or pushScene instead"); - + [self pushScene:scene]; - [self startAnimation]; + [self startAnimation]; } -(void) replaceScene: (CCScene*) scene @@ -348,7 +377,7 @@ -(void) replaceScene: (CCScene*) scene NSAssert( scene != nil, @"Argument must be non-nil"); NSUInteger index = [scenesStack_ count]; - + sendCleanupToScene_ = YES; [scenesStack_ replaceObjectAtIndex:index-1 withObject:scene]; nextScene_ = scene; // nextScene_ is a weak ref @@ -365,12 +394,12 @@ - (void) pushScene: (CCScene*) scene } -(void) popScene -{ +{ NSAssert( runningScene_ != nil, @"A running Scene is needed"); [scenesStack_ removeLastObject]; NSUInteger c = [scenesStack_ count]; - + if( c == 0 ) [self end]; else { @@ -379,6 +408,29 @@ -(void) popScene } } +-(void) popToRootScene +{ + NSAssert(runningScene_ != nil, @"A running Scene is needed"); + NSUInteger c = [scenesStack_ count]; + + if (c == 1) { + [scenesStack_ removeLastObject]; + [self end]; + } else { + while (c > 1) { + CCScene *current = [scenesStack_ lastObject]; + if( [current isRunning] ) + [current onExit]; + [current cleanup]; + + [scenesStack_ removeLastObject]; + c--; + } + nextScene_ = [scenesStack_ lastObject]; + sendCleanupToScene_ = NO; + } +} + -(void) end { [runningScene_ onExit]; @@ -387,40 +439,44 @@ -(void) end runningScene_ = nil; nextScene_ = nil; - + // remove all objects, but don't release it. // runWithScene might be executed after 'end'. [scenesStack_ removeAllObjects]; - + [self stopAnimation]; - -#if CC_DIRECTOR_FAST_FPS + [FPSLabel_ release]; - FPSLabel_ = nil; -#endif + [SPFLabel_ release]; + [drawsLabel_ release]; + FPSLabel_ = nil, SPFLabel_=nil, drawsLabel_=nil; - [projectionDelegate_ release]; - projectionDelegate_ = nil; + [delegate_ release]; + delegate_ = nil; + + [self setView:nil]; // Purge bitmap cache [CCLabelBMFont purgeCachedData]; - // Purge all managers + // Purge all managers / caches [CCAnimationCache purgeSharedAnimationCache]; [CCSpriteFrameCache purgeSharedSpriteFrameCache]; - [CCScheduler purgeSharedScheduler]; - [CCActionManager purgeSharedManager]; [CCTextureCache purgeSharedTextureCache]; - - + [CCShaderCache purgeSharedShaderCache]; + [[CCFileUtils sharedFileUtils] purgeCachedEntries]; + // OpenGL view - + // Since the director doesn't attach the openglview to the window // it shouldn't remove it from the window too. // [openGLView_ removeFromSuperview]; - [openGLView_ release]; - openGLView_ = nil; + + // Invalidate GL state cache + ccGLInvalidateStateCache(); + + CHECK_GL_ERROR(); } -(void) setNextScene @@ -440,7 +496,7 @@ -(void) setNextScene } [runningScene_ release]; - + runningScene_ = [nextScene_ retain]; nextScene_ = nil; @@ -456,24 +512,30 @@ -(void) pause return; oldAnimationInterval_ = animationInterval_; - + // when paused, don't consume CPU [self setAnimationInterval:1/4.0]; + + [self willChangeValueForKey:@"isPaused"]; isPaused_ = YES; + [self didChangeValueForKey:@"isPaused"]; } -(void) resume { if( ! isPaused_ ) return; - + [self setAnimationInterval: oldAnimationInterval_]; if( gettimeofday( &lastUpdate_, NULL) != 0 ) { CCLOG(@"cocos2d: Director: Error in gettimeofday"); } - + + [self willChangeValueForKey:@"isPaused"]; isPaused_ = NO; + [self didChangeValueForKey:@"isPaused"]; + dt = 0; } @@ -492,73 +554,83 @@ - (void)setAnimationInterval:(NSTimeInterval)interval CCLOG(@"cocos2d: Director#setAnimationInterval. Override me"); } -#if CC_DIRECTOR_FAST_FPS -// display the FPS using a LabelAtlas -// updates the FPS every frame --(void) showFPS +// display statistics +-(void) showStats { frames_++; accumDt_ += dt; - - if ( accumDt_ > CC_DIRECTOR_FPS_INTERVAL) { - frameRate_ = frames_/accumDt_; - frames_ = 0; - accumDt_ = 0; - -// sprintf(format,"%.1f",frameRate); -// [FPSLabel setCString:format]; - NSString *str = [[NSString alloc] initWithFormat:@"%.1f", frameRate_]; - [FPSLabel_ setString:str]; - [str release]; + if( displayStats_ ) { + // Ms per Frame + + if( accumDt_ > CC_DIRECTOR_STATS_INTERVAL) + { + NSString *spfstr = [[NSString alloc] initWithFormat:@"%.3f", secondsPerFrame_]; + [SPFLabel_ setString:spfstr]; + [spfstr release]; + + frameRate_ = frames_/accumDt_; + frames_ = 0; + accumDt_ = 0; + +// sprintf(format,"%.1f",frameRate); +// [FPSLabel setCString:format]; + + NSString *fpsstr = [[NSString alloc] initWithFormat:@"%.1f", frameRate_]; + [FPSLabel_ setString:fpsstr]; + [fpsstr release]; + + NSString *draws = [[NSString alloc] initWithFormat:@"%4lu", (unsigned long)__ccNumberOfDraws]; + [drawsLabel_ setString:draws]; + [draws release]; + } + + [drawsLabel_ visit]; + [FPSLabel_ visit]; + [SPFLabel_ visit]; } - - [FPSLabel_ draw]; + + __ccNumberOfDraws = 0; } -#else -// display the FPS using a manually generated Texture (very slow) -// updates the FPS 3 times per second aprox. --(void) showFPS + +-(void) calculateMPF { - frames_++; - accumDt_ += dt; - - if ( accumDt_ > CC_DIRECTOR_FPS_INTERVAL) { - frameRate_ = frames_/accumDt_; - frames_ = 0; - accumDt_ = 0; - } - - NSString *str = [NSString stringWithFormat:@"%.2f",frameRate_]; - CCTexture2D *texture = [[CCTexture2D alloc] initWithString:str dimensions:CGSizeMake(100,30) alignment:CCTextAlignmentLeft fontName:@"Arial" fontSize:24]; + struct timeval now; + gettimeofday( &now, NULL); - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY - // Unneeded states: GL_COLOR_ARRAY - glDisableClientState(GL_COLOR_ARRAY); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glColor4ub(224,224,244,200); - [texture drawAtPoint: ccp(5,2)]; - [texture release]; - - glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); - - // restore default GL state - glEnableClientState(GL_COLOR_ARRAY); + secondsPerFrame_ = (now.tv_sec - lastUpdate_.tv_sec) + (now.tv_usec - lastUpdate_.tv_usec) / 1000000.0f; } -#endif -- (void) showProfilers { -#if CC_ENABLE_PROFILERS - accumDtForProfiler_ += dt; - if (accumDtForProfiler_ > 1.0f) { - accumDtForProfiler_ = 0; - [[CCProfiler sharedProfiler] displayTimers]; +#pragma mark Director - Helper + +-(void) createStatsLabel +{ + if( FPSLabel_ && SPFLabel_ ) { + CCTexture2D *texture = [FPSLabel_ texture]; + + [FPSLabel_ release]; + [SPFLabel_ release]; + [drawsLabel_ release]; + [[CCTextureCache sharedTextureCache ] removeTexture:texture]; + FPSLabel_ = nil; + SPFLabel_ = nil; + drawsLabel_ = nil; + + [[CCFileUtils sharedFileUtils] purgeCachedEntries]; } -#endif // CC_ENABLE_PROFILERS + + CCTexture2DPixelFormat currentFormat = [CCTexture2D defaultAlphaPixelFormat]; + [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444]; + FPSLabel_ = [[CCLabelAtlas alloc] initWithString:@"00.0" charMapFile:@"fps_images.png" itemWidth:12 itemHeight:32 startCharMap:'.']; + SPFLabel_ = [[CCLabelAtlas alloc] initWithString:@"0.000" charMapFile:@"fps_images.png" itemWidth:12 itemHeight:32 startCharMap:'.']; + drawsLabel_ = [[CCLabelAtlas alloc] initWithString:@"000" charMapFile:@"fps_images.png" itemWidth:12 itemHeight:32 startCharMap:'.']; + + [CCTexture2D setDefaultAlphaPixelFormat:currentFormat]; + + [drawsLabel_ setPosition: ccpAdd( ccp(0,34), CC_DIRECTOR_STATS_POSITION ) ]; + [SPFLabel_ setPosition: ccpAdd( ccp(0,17), CC_DIRECTOR_STATS_POSITION ) ]; + [FPSLabel_ setPosition: CC_DIRECTOR_STATS_POSITION ]; } @end diff --git a/cocos2d/cocos2d/CCDrawingPrimitives.h b/cocos2d/cocos2d/CCDrawingPrimitives.h old mode 100644 new mode 100755 index 8d1dbe5..e91ea54 --- a/cocos2d/cocos2d/CCDrawingPrimitives.h +++ b/cocos2d/cocos2d/CCDrawingPrimitives.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,17 +27,22 @@ #ifndef __CC_DRAWING_PRIMITIVES_H #define __CC_DRAWING_PRIMITIVES_H -#import #import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "ccTypes.h" +#import "ccMacros.h" + +#ifdef __CC_PLATFORM_IOS #import // for CGPoint #endif #ifdef __cplusplus extern "C" { -#endif +#endif + +@class CCPointArray; + /** @file @@ -51,10 +56,10 @@ extern "C" { You can change the color, width and other property by calling the glColor4ub(), glLineWidth(), glPointSize(). - + @warning These functions draws the Line, Point, Polygon, immediately. They aren't batched. If you are going to make a game that depends on these primitives, I suggest creating a batch. */ - + /** draws a point given x and y coordinate measured in points. */ void ccDrawPoint( CGPoint point ); @@ -67,24 +72,66 @@ void ccDrawPoints( const CGPoint *points, NSUInteger numberOfPoints ); /** draws a line given the origin and destination point measured in points. */ void ccDrawLine( CGPoint origin, CGPoint destination ); +/** draws a rectangle given the origin and destination point measured in points. */ +void ccDrawRect( CGPoint origin, CGPoint destination ); + +/** draws a solid rectangle given the origin and destination point measured in points. + @since 1.1 + */ +void ccDrawSolidRect( CGPoint origin, CGPoint destination, ccColor4F color ); + /** draws a poligon given a pointer to CGPoint coordiantes and the number of vertices measured in points. The polygon can be closed or open */ void ccDrawPoly( const CGPoint *vertices, NSUInteger numOfVertices, BOOL closePolygon ); +/** draws a solid polygon given a pointer to CGPoint coordiantes, the number of vertices measured in points, and a color. + */ +void ccDrawSolidPoly( const CGPoint *poli, NSUInteger numberOfPoints, ccColor4F color ); + /** draws a circle given the center, radius and number of segments measured in points */ void ccDrawCircle( CGPoint center, float radius, float angle, NSUInteger segments, BOOL drawLineToCenter); /** draws a quad bezier path measured in points. + @warning This function could be pretty slow. Use it only for debugging purposes. @since v0.8 */ void ccDrawQuadBezier(CGPoint origin, CGPoint control, CGPoint destination, NSUInteger segments); /** draws a cubic bezier path measured in points. + @warning This function could be pretty slow. Use it only for debugging purposes. @since v0.8 */ void ccDrawCubicBezier(CGPoint origin, CGPoint control1, CGPoint control2, CGPoint destination, NSUInteger segments); +/** draws a Catmull Rom path. + @warning This function could be pretty slow. Use it only for debugging purposes. + @since v2.0 + */ +void ccDrawCatmullRom( CCPointArray *arrayOfControlPoints, NSUInteger segments ); + +/** draws a Cardinal Spline path. + @warning This function could be pretty slow. Use it only for debugging purposes. + @since v2.0 + */ +void ccDrawCardinalSpline( CCPointArray *config, CGFloat tension, NSUInteger segments ); + +/** set the drawing color with 4 unsigned bytes + @since v2.0 + */ +void ccDrawColor4B( GLubyte r, GLubyte g, GLubyte b, GLubyte a ); + +/** set the drawing color with 4 floats + @since v2.0 + */ +void ccDrawColor4F( GLfloat r, GLfloat g, GLfloat b, GLfloat a ); + +/** set the point size in points. Default 1. + @since v2.0 + */ +void ccPointSize( GLfloat pointSize ); + + #ifdef __cplusplus } #endif diff --git a/cocos2d/cocos2d/CCDrawingPrimitives.m b/cocos2d/cocos2d/CCDrawingPrimitives.m old mode 100644 new mode 100755 index f7df2b6..03adf9b --- a/cocos2d/cocos2d/CCDrawingPrimitives.m +++ b/cocos2d/cocos2d/CCDrawingPrimitives.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,245 +28,355 @@ #import #import "CCDrawingPrimitives.h" -#import "ccTypes.h" #import "ccMacros.h" #import "Platforms/CCGL.h" +#import "ccGLStateCache.h" +#import "CCShaderCache.h" +#import "CCGLProgram.h" +#import "CCActionCatmullRom.h" +#import "Support/OpenGL_Internal.h" + + +static BOOL initialized = NO; +static CCGLProgram *shader_ = nil; +static int colorLocation_ = -1; +static ccColor4F color_ = {1,1,1,1}; +static int pointSizeLocation_ = -1; +static GLfloat pointSize_ = 1; + +static void lazy_init( void ) +{ + if( ! initialized ) { + + // + // Position and 1 color passed as a uniform (to similate glColor4ub ) + // + shader_ = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_Position_uColor]; + + colorLocation_ = glGetUniformLocation( shader_->program_, "u_color"); + pointSizeLocation_ = glGetUniformLocation( shader_->program_, "u_pointSize"); + + initialized = YES; + } + +} void ccDrawPoint( CGPoint point ) { - ccVertex2F p = (ccVertex2F) {point.x * CC_CONTENT_SCALE_FACTOR(), point.y * CC_CONTENT_SCALE_FACTOR() }; - - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_VERTEX_ARRAY, - // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY - glDisable(GL_TEXTURE_2D); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glVertexPointer(2, GL_FLOAT, 0, &p); - glDrawArrays(GL_POINTS, 0, 1); + lazy_init(); + + ccVertex2F p = (ccVertex2F) {point.x, point.y}; + + ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position ); + + [shader_ use]; + [shader_ setUniformForModelViewProjectionMatrix]; - // restore default state - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnable(GL_TEXTURE_2D); + [shader_ setUniformLocation:colorLocation_ with4fv:(GLfloat*) &color_.r count:1]; + [shader_ setUniformLocation:pointSizeLocation_ withF1:pointSize_]; + + glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, &p); + + glDrawArrays(GL_POINTS, 0, 1); + + CC_INCREMENT_GL_DRAWS(1); } void ccDrawPoints( const CGPoint *points, NSUInteger numberOfPoints ) { - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_VERTEX_ARRAY, - // Unneeded states: GL_TEXTURE_2D, GL_TEXTURE_COORD_ARRAY, GL_COLOR_ARRAY - glDisable(GL_TEXTURE_2D); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); + lazy_init(); - ccVertex2F newPoints[numberOfPoints]; + ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position ); - // iPhone and 32-bit machines optimization - if( sizeof(CGPoint) == sizeof(ccVertex2F) ) { + [shader_ use]; + [shader_ setUniformForModelViewProjectionMatrix]; + [shader_ setUniformLocation:colorLocation_ with4fv:(GLfloat*) &color_.r count:1]; + [shader_ setUniformLocation:pointSizeLocation_ withF1:pointSize_]; - // points ? - if( CC_CONTENT_SCALE_FACTOR() != 1 ) { - for( NSUInteger i=0; i +#import "ccMacros.h" +#import "Platforms/CCGL.h" + +enum { + kCCVertexAttrib_Position, + kCCVertexAttrib_Color, + kCCVertexAttrib_TexCoords, + + kCCVertexAttrib_MAX, +}; + +enum { + kCCUniformMVPMatrix, + kCCUniformSampler, + + kCCUniform_MAX, +}; + +#define kCCShader_PositionTextureColor @"ShaderPositionTextureColor" +#define kCCShader_PositionTextureColorAlphaTest @"ShaderPositionTextureColorAlphaTest" +#define kCCShader_PositionColor @"ShaderPositionColor" +#define kCCShader_PositionTexture @"ShaderPositionTexture" +#define kCCShader_PositionTexture_uColor @"ShaderPositionTexture_uColor" +#define kCCShader_PositionTextureA8Color @"ShaderPositionTextureA8Color" +#define kCCShader_Position_uColor @"ShaderPosition_uColor" + +// uniform names +#define kCCUniformMVPMatrix_s "u_MVPMatrix" +#define kCCUniformSampler_s "u_texture" +#define kCCUniformAlphaTestValue "u_alpha_value" + +// Attribute names +#define kCCAttributeNameColor @"a_color" +#define kCCAttributeNamePosition @"a_position" +#define kCCAttributeNameTexCoord @"a_texCoord" + + +struct _hashUniformEntry; + +/** CCGLProgram + Class that implements a glProgram + + + @since v2.0.0 + */ +@interface CCGLProgram : NSObject +{ + struct _hashUniformEntry *hashForUniforms_; + +@public + GLuint program_, + vertShader_, + fragShader_; + + GLint uniforms_[kCCUniform_MAX]; +} + +/** Initializes the CCGLProgram with a vertex and fragment with bytes array */ +- (id)initWithVertexShaderByteArray:(const GLchar*)vShaderByteArray fragmentShaderByteArray:(const GLchar*)fShaderByteArray; + +/** Initializes the CCGLProgram with a vertex and fragment with contents of filenames */ +- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename fragmentShaderFilename:(NSString *)fShaderFilename; + +/** It will add a new attribute to the shader */ +- (void)addAttribute:(NSString *)attributeName index:(GLuint)index; + +/** links the glProgram */ +- (BOOL)link; + +/** it will call glUseProgram() */ +- (void)use; + +/** It will create 3 uniforms: + - kCCUniformPMatrix + - kCCUniformMVMatrix + - kCCUniformSampler + + And it will bind "kCCUniformSampler" to 0 + */ +- (void) updateUniforms; + +/** calls glUniform1i only if the values are different than the previous call for this same shader program. */ +-(void) setUniformLocation:(NSUInteger)location withI1:(GLint)i1; + +/** calls glUniform1f only if the values are different than the previous call for this same shader program. */ +-(void) setUniformLocation:(NSUInteger)location withF1:(GLfloat)f1; + +/** calls glUniform2f only if the values are different than the previous call for this same shader program. */ +-(void) setUniformLocation:(NSUInteger)location withF1:(GLfloat)f1 f2:(GLfloat)f2; + +/** calls glUniform3f only if the values are different than the previous call for this same shader program. */ +-(void) setUniformLocation:(NSUInteger)location withF1:(GLfloat)f1 f2:(GLfloat)f2 f3:(GLfloat)f3; + +/** calls glUniform4f only if the values are different than the previous call for this same shader program. */ +-(void) setUniformLocation:(NSUInteger)location withF1:(GLfloat)f1 f2:(GLfloat)f2 f3:(GLfloat)f3 f4:(GLfloat)f4; + +/** calls glUniform2fv only if the values are different than the previous call for this same shader program. */ +-(void) setUniformLocation:(NSUInteger)location with2fv:(GLfloat*)floats count:(NSUInteger)numberOfArrays; + +/** calls glUniform3fv only if the values are different than the previous call for this same shader program. */ +-(void) setUniformLocation:(NSUInteger)location with3fv:(GLfloat*)floats count:(NSUInteger)numberOfArrays; + +/** calls glUniform4fv only if the values are different than the previous call for this same shader program. */ +-(void) setUniformLocation:(NSUInteger)location with4fv:(GLvoid*)floats count:(NSUInteger)numberOfArrays; + +/** calls glUniformMatrix4fv only if the values are different than the previous call for this same shader program. */ +-(void) setUniformLocation:(NSUInteger)location withMatrix4fv:(GLvoid*)matrix_array count:(NSUInteger)numberOfMatrix; + +/** will update the MVP matrix on the MVP uniform if it is different than the previous call for this same shader program. */ +-(void) setUniformForModelViewProjectionMatrix; + +/** returns the vertexShader error log */ +- (NSString *)vertexShaderLog; + +/** returns the fragmentShader error log */ +- (NSString *)fragmentShaderLog; + +/** returns the program error log */ +- (NSString *)programLog; +@end diff --git a/cocos2d/cocos2d/CCGLProgram.m b/cocos2d/cocos2d/CCGLProgram.m new file mode 100755 index 0000000..1c0f489 --- /dev/null +++ b/cocos2d/cocos2d/CCGLProgram.m @@ -0,0 +1,395 @@ +// +// Copyright 2011 Jeff Lamarche +// +// Copyright 2012 Goffredo Marocchi +// +// Copyright 2012 Ricardo Quesada +// +// +// 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. +// +// THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``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 THE FREEBSD PROJECT +// 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. + + +#import "CCGLProgram.h" +#import "ccGLStateCache.h" +#import "ccMacros.h" +#import "Support/CCFileUtils.h" +#import "Support/uthash.h" +#import "Support/OpenGL_Internal.h" + +// extern +#import "kazmath/GL/matrix.h" +#import "kazmath/kazmath.h" + + +typedef struct _hashUniformEntry +{ + GLvoid *value; // value + NSUInteger location; // Key + UT_hash_handle hh; // hash entry +} tHashUniformEntry; + + +#pragma mark Function Pointer Definitions +typedef void (*GLInfoFunction)(GLuint program, + GLenum pname, + GLint* params); +typedef void (*GLLogFunction) (GLuint program, + GLsizei bufsize, + GLsizei* length, + GLchar* infolog); +#pragma mark - +#pragma mark Private Extension Method Declaration + +@interface CCGLProgram() +- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type byteArray:(const GLchar*)byteArray; + +- (NSString *)logForOpenGLObject:(GLuint)object infoCallback:(GLInfoFunction)infoFunc logFunc:(GLLogFunction)logFunc; +@end + +#pragma mark - + +@implementation CCGLProgram +- (id)initWithVertexShaderByteArray:(const GLchar *)vShaderByteArray fragmentShaderByteArray:(const GLchar *)fShaderByteArray +{ + if ((self = [super init]) ) + { + program_ = glCreateProgram(); + + vertShader_ = fragShader_ = 0; + + if( vShaderByteArray ) { + + if (![self compileShader:&vertShader_ + type:GL_VERTEX_SHADER + byteArray:vShaderByteArray] ) + CCLOG(@"cocos2d: ERROR: Failed to compile vertex shader"); + } + + // Create and compile fragment shader + if( fShaderByteArray ) { + if (![self compileShader:&fragShader_ + type:GL_FRAGMENT_SHADER + byteArray:fShaderByteArray] ) + + CCLOG(@"cocos2d: ERROR: Failed to compile fragment shader"); + } + + if( vertShader_ ) + glAttachShader(program_, vertShader_); + + if( fragShader_ ) + glAttachShader(program_, fragShader_); + + hashForUniforms_ = NULL; + } + + return self; +} + +- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename fragmentShaderFilename:(NSString *)fShaderFilename +{ + + const GLchar * vertexSource = (GLchar*) [[NSString stringWithContentsOfFile:[[CCFileUtils sharedFileUtils] fullPathFromRelativePath:vShaderFilename] encoding:NSUTF8StringEncoding error:nil] UTF8String]; + const GLchar * fragmentSource = (GLchar*) [[NSString stringWithContentsOfFile:[[CCFileUtils sharedFileUtils] fullPathFromRelativePath:fShaderFilename] encoding:NSUTF8StringEncoding error:nil] UTF8String]; + + return [self initWithVertexShaderByteArray:vertexSource fragmentShaderByteArray:fragmentSource]; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %p | Program = %i, VertexShader = %i, FragmentShader = %i>", [self class], self, program_, vertShader_, fragShader_]; +} + + +- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type byteArray:(const GLchar *)source +{ + GLint status; + + if (!source) + return NO; + + *shader = glCreateShader(type); + glShaderSource(*shader, 1, &source, NULL); + glCompileShader(*shader); + + glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); + + if( ! status ) { + if( type == GL_VERTEX_SHADER ) + CCLOG(@"cocos2d: %@", [self vertexShaderLog] ); + else + CCLOG(@"cocos2d: %@", [self fragmentShaderLog] ); + + } + return ( status == GL_TRUE ); +} + +#pragma mark - + +- (void)addAttribute:(NSString *)attributeName index:(GLuint)index +{ + glBindAttribLocation(program_, + index, + [attributeName UTF8String]); +} + +-(void) updateUniforms +{ + // Since sample most probably won't change, set it to 0 now. + + uniforms_[kCCUniformMVPMatrix] = glGetUniformLocation(program_, kCCUniformMVPMatrix_s); + + uniforms_[kCCUniformSampler] = glGetUniformLocation(program_, kCCUniformSampler_s); + + [self use]; + + [self setUniformLocation:uniforms_[kCCUniformSampler] withI1:0]; +} + +#pragma mark - + +- (BOOL)link +{ + glLinkProgram(program_); + +#if DEBUG + GLint status; + glValidateProgram(program_); + + glGetProgramiv(program_, GL_LINK_STATUS, &status); + if (status == GL_FALSE) { + CCLOG(@"cocos2d: ERROR: Failed to link program: %i", program_); + if( vertShader_ ) + glDeleteShader( vertShader_ ); + if( fragShader_ ) + glDeleteShader( fragShader_ ); + ccGLDeleteProgram( program_ ); + vertShader_ = fragShader_ = program_ = 0; + return NO; + } +#endif + + if (vertShader_) + glDeleteShader(vertShader_); + if (fragShader_) + glDeleteShader(fragShader_); + + vertShader_ = fragShader_ = 0; + + return YES; +} + +- (void)use +{ + ccGLUseProgram(program_); +} + +#pragma mark - + +- (NSString *)logForOpenGLObject:(GLuint)object + infoCallback:(GLInfoFunction)infoFunc + logFunc:(GLLogFunction)logFunc +{ + GLint logLength = 0, charsWritten = 0; + + infoFunc(object, GL_INFO_LOG_LENGTH, &logLength); + if (logLength < 1) + return nil; + + char *logBytes = malloc(logLength); + logFunc(object, logLength, &charsWritten, logBytes); + NSString *log = [[[NSString alloc] initWithBytes:logBytes + length:logLength + encoding:NSUTF8StringEncoding] + autorelease]; + free(logBytes); + return log; +} + +- (NSString *)vertexShaderLog +{ + return [self logForOpenGLObject:vertShader_ + infoCallback:(GLInfoFunction)&glGetShaderiv + logFunc:(GLLogFunction)&glGetShaderInfoLog]; + +} + +- (NSString *)fragmentShaderLog +{ + return [self logForOpenGLObject:fragShader_ + infoCallback:(GLInfoFunction)&glGetShaderiv + logFunc:(GLLogFunction)&glGetShaderInfoLog]; +} + +- (NSString *)programLog +{ + return [self logForOpenGLObject:program_ + infoCallback:(GLInfoFunction)&glGetProgramiv + logFunc:(GLLogFunction)&glGetProgramInfoLog]; +} + +#pragma mark - Uniform cache + +-(BOOL) updateUniformLocation:(NSUInteger)location withData:(GLvoid*)data sizeOfData:(NSUInteger)bytes +{ + BOOL updated = YES; + tHashUniformEntry *element = NULL; + HASH_FIND_INT(hashForUniforms_, &location, element); + + if( ! element ) { + + element = malloc( sizeof(*element) ); + + // key + element->location = location; + + // value + element->value = malloc( bytes ); + memcpy(element->value, data, bytes ); + + HASH_ADD_INT(hashForUniforms_, location, element); + } + else + { + if( memcmp( element->value, data, bytes) == 0 ) + updated = NO; + else + memcpy( element->value, data, bytes ); + } + + return updated; +} + +-(void) setUniformLocation:(NSUInteger)location withI1:(GLint)i1 +{ + BOOL updated = [self updateUniformLocation:location withData:&i1 sizeOfData:sizeof(i1)*1]; + + if( updated ) + glUniform1i( (GLint)location, i1); +} + +-(void) setUniformLocation:(NSUInteger)location withF1:(GLfloat)f1 +{ + BOOL updated = [self updateUniformLocation:location withData:&f1 sizeOfData:sizeof(f1)*1]; + + if( updated ) + glUniform1f( (GLint)location, f1); +} + +-(void) setUniformLocation:(NSUInteger)location withF1:(GLfloat)f1 f2:(GLfloat)f2 +{ + GLfloat floats[2] = {f1,f2}; + BOOL updated = [self updateUniformLocation:location withData:floats sizeOfData:sizeof(floats)]; + + if( updated ) + glUniform2f( (GLint)location, f1, f2); +} + +-(void) setUniformLocation:(NSUInteger)location withF1:(GLfloat)f1 f2:(GLfloat)f2 f3:(GLfloat)f3 +{ + GLfloat floats[3] = {f1,f2,f3}; + BOOL updated = [self updateUniformLocation:location withData:floats sizeOfData:sizeof(floats)]; + + if( updated ) + glUniform3f( (GLint)location, f1, f2, f3); +} + +-(void) setUniformLocation:(NSUInteger)location withF1:(GLfloat)f1 f2:(GLfloat)f2 f3:(GLfloat)f3 f4:(GLfloat)f4 +{ + GLfloat floats[4] = {f1,f2,f3,f4}; + BOOL updated = [self updateUniformLocation:location withData:floats sizeOfData:sizeof(floats)]; + + if( updated ) + glUniform4f( (GLint)location, f1, f2, f3,f4); +} + +-(void) setUniformLocation:(NSUInteger)location with2fv:(GLfloat*)floats count:(NSUInteger)numberOfArrays +{ + BOOL updated = [self updateUniformLocation:location withData:floats sizeOfData:sizeof(float)*2*numberOfArrays]; + + if( updated ) + glUniform2fv( (GLint)location, (GLsizei)numberOfArrays, floats ); +} + +-(void) setUniformLocation:(NSUInteger)location with3fv:(GLfloat*)floats count:(NSUInteger)numberOfArrays +{ + BOOL updated = [self updateUniformLocation:location withData:floats sizeOfData:sizeof(float)*3*numberOfArrays]; + + if( updated ) + glUniform3fv( (GLint)location, (GLsizei)numberOfArrays, floats ); +} + +-(void) setUniformLocation:(NSUInteger)location with4fv:(GLvoid*)floats count:(NSUInteger)numberOfArrays +{ + BOOL updated = [self updateUniformLocation:location withData:floats sizeOfData:sizeof(float)*4*numberOfArrays]; + + if( updated ) + glUniform4fv( (GLint)location, (GLsizei)numberOfArrays, floats ); +} + + +-(void) setUniformLocation:(NSUInteger)location withMatrix4fv:(GLvoid*)matrixArray count:(NSUInteger)numberOfMatrices +{ + BOOL updated = [self updateUniformLocation:location withData:matrixArray sizeOfData:sizeof(float)*16*numberOfMatrices]; + + if( updated ) + glUniformMatrix4fv( (GLint)location, (GLsizei)numberOfMatrices, GL_FALSE, matrixArray); +} + +-(void) setUniformForModelViewProjectionMatrix +{ + kmMat4 matrixP; + kmMat4 matrixMV; + kmMat4 matrixMVP; + + kmGLGetMatrix(KM_GL_PROJECTION, &matrixP ); + kmGLGetMatrix(KM_GL_MODELVIEW, &matrixMV ); + + kmMat4Multiply(&matrixMVP, &matrixP, &matrixMV); + + [self setUniformLocation:uniforms_[kCCUniformMVPMatrix] withMatrix4fv:matrixMVP.mat count:1]; +} + + +#pragma mark - + +- (void)dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@", self); + + // there is no need to delete the shaders. They should have been already deleted. + NSAssert( vertShader_ == 0, @"Vertex Shaders should have been already deleted"); + NSAssert( fragShader_ == 0, @"Vertex Shaders should have been already deleted"); + + if (program_) + ccGLDeleteProgram(program_); + + tHashUniformEntry *current_element, *tmp; + + // Purge uniform hash + HASH_ITER(hh, hashForUniforms_, current_element, tmp) { + HASH_DEL(hashForUniforms_, current_element); + free(current_element->value); + free(current_element); + } + + [super dealloc]; +} +@end diff --git a/cocos2d/cocos2d/CCGrabber.h b/cocos2d/cocos2d/CCGrabber.h old mode 100644 new mode 100755 index f1ce6cb..5b086c6 --- a/cocos2d/cocos2d/CCGrabber.h +++ b/cocos2d/cocos2d/CCGrabber.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -32,8 +32,9 @@ /** FBO class that grabs the the contents of the screen */ @interface CCGrabber : NSObject { - GLuint fbo; - GLint oldFBO; + GLuint fbo_; + GLint oldFBO_; + GLfloat oldClearColor_[4]; } -(void)grab:(CCTexture2D*)texture; diff --git a/cocos2d/cocos2d/CCGrabber.m b/cocos2d/cocos2d/CCGrabber.m old mode 100644 new mode 100755 index a259091..5b12542 --- a/cocos2d/cocos2d/CCGrabber.m +++ b/cocos2d/cocos2d/CCGrabber.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -36,59 +36,63 @@ -(id) init { if(( self = [super init] )) { // generate FBO - ccglGenFramebuffers(1, &fbo); + glGenFramebuffers(1, &fbo_); } return self; } -(void)grab:(CCTexture2D*)texture { - glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO); - + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO_); + // bind - ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); // associate texture with FBO - ccglFramebufferTexture2D(CC_GL_FRAMEBUFFER, CC_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.name, 0); - + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.name, 0); + // check if it worked (probably worth doing :) ) - GLuint status = ccglCheckFramebufferStatus(CC_GL_FRAMEBUFFER); - if (status != CC_GL_FRAMEBUFFER_COMPLETE) + GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) [NSException raise:@"Frame Grabber" format:@"Could not attach texture to framebuffer"]; - - ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO); + + glBindFramebuffer(GL_FRAMEBUFFER, oldFBO_); } -(void)beforeRender:(CCTexture2D*)texture { - glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO); - ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo); - - // BUG XXX: doesn't work with RGB565. + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO_); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + // save clear color + glGetFloatv(GL_COLOR_CLEAR_VALUE,oldClearColor_); + // BUG XXX: doesn't work with RGB565. glClearColor(0,0,0,0); - + // BUG #631: To fix #631, uncomment the lines with #631 // Warning: But it CCGrabber won't work with 2 effects at the same time // glClearColor(0.0f,0.0f,0.0f,1.0f); // #631 - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // glColorMask(TRUE, TRUE, TRUE, FALSE); // #631 } -(void)afterRender:(CCTexture2D*)texture { - ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO); + glBindFramebuffer(GL_FRAMEBUFFER, oldFBO_); // glColorMask(TRUE, TRUE, TRUE, TRUE); // #631 + + // Restore clear color + glClearColor( oldClearColor_[0], oldClearColor_[1], oldClearColor_[2], oldClearColor_[3] ); } - (void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); - ccglDeleteFramebuffers(1, &fbo); + glDeleteFramebuffers(1, &fbo_); [super dealloc]; } diff --git a/cocos2d/cocos2d/CCGrid.h b/cocos2d/cocos2d/CCGrid.h old mode 100644 new mode 100755 index e5e77e8..df8fa34 --- a/cocos2d/cocos2d/CCGrid.h +++ b/cocos2d/cocos2d/CCGrid.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,9 +29,12 @@ #import "CCNode.h" #import "CCCamera.h" #import "ccTypes.h" +#import "CCDirector.h" +#import "kazmath/mat4.h" @class CCTexture2D; @class CCGrabber; +@class CCGLProgram; /** Base class for other */ @@ -44,6 +47,10 @@ CGPoint step_; CCGrabber *grabber_; BOOL isTextureFlipped_; + + CCGLProgram *shaderProgram_; + + ccDirectorProjection directorProjection_; } /** wheter or not the grid is active */ @@ -60,6 +67,8 @@ @property (nonatomic, retain) CCGrabber *grabber; /** is texture flipped */ @property (nonatomic, readwrite) BOOL isTextureFlipped; +/** shader program */ +@property (nonatomic, readwrite, assign) CCGLProgram *shaderProgram; +(id) gridWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped; +(id) gridWithSize:(ccGridSize)gridSize; diff --git a/cocos2d/cocos2d/CCGrid.m b/cocos2d/cocos2d/CCGrid.m old mode 100644 new mode 100755 index c2ed19d..cab291a --- a/cocos2d/cocos2d/CCGrid.m +++ b/cocos2d/cocos2d/CCGrid.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,21 +24,27 @@ */ -#import - #import "ccMacros.h" #import "CCGrid.h" #import "CCTexture2D.h" #import "CCDirector.h" #import "CCGrabber.h" +#import "CCGLProgram.h" +#import "CCShaderCache.h" +#import "ccGLStateCache.h" #import "Platforms/CCGL.h" #import "Support/CGPointExtension.h" #import "Support/ccUtils.h" +#import "Support/TransformUtils.h" +#import "Support/OpenGL_Internal.h" + +#import "kazmath/kazmath.h" +#import "kazmath/GL/matrix.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import "Platforms/iOS/CCDirectorIOS.h" -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS #pragma mark - #pragma mark CCGridBase @@ -50,6 +56,7 @@ @implementation CCGridBase @synthesize grabber = grabber_; @synthesize gridSize = gridSize_; @synthesize step = step_; +@synthesize shaderProgram = shaderProgram_; +(id) gridWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped { @@ -64,21 +71,23 @@ +(id) gridWithSize:(ccGridSize)gridSize -(id) initWithSize:(ccGridSize)gridSize texture:(CCTexture2D*)texture flippedTexture:(BOOL)flipped { if( (self=[super init]) ) { - + active_ = NO; reuseGrid_ = 0; gridSize_ = gridSize; self.texture = texture; isTextureFlipped_ = flipped; - - CGSize texSize = [texture_ contentSizeInPixels]; + + CGSize texSize = [texture_ contentSize]; step_.x = texSize.width / gridSize_.x; step_.y = texSize.height / gridSize_.y; - + grabber_ = [[CCGrabber alloc] init]; [grabber_ grab:texture_]; - + + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTexture]; + [self calculateVertexPoints]; } return self; @@ -88,26 +97,29 @@ -(id)initWithSize:(ccGridSize)gSize { CCDirector *director = [CCDirector sharedDirector]; CGSize s = [director winSizeInPixels]; - + + unsigned long POTWide = ccNextPOT(s.width); unsigned long POTHigh = ccNextPOT(s.height); - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - EAGLView *glview = [[CCDirector sharedDirector] openGLView]; + +#ifdef __CC_PLATFORM_IOS + CCGLView *glview = (CCGLView*)[[CCDirector sharedDirector] view]; NSString *pixelFormat = [glview pixelFormat]; CCTexture2DPixelFormat format = [pixelFormat isEqualToString: kEAGLColorFormatRGB565] ? kCCTexture2DPixelFormat_RGB565 : kCCTexture2DPixelFormat_RGBA8888; -#else +#elif defined(__CC_PLATFORM_MAC) CCTexture2DPixelFormat format = kCCTexture2DPixelFormat_RGBA8888; #endif - - void *data = calloc((int)(POTWide * POTHigh * 4), 1); + + int bpp = ( format == kCCTexture2DPixelFormat_RGB565 ? 2 : 4 ); + + void *data = calloc((size_t)(POTWide * POTHigh * bpp), 1); if( ! data ) { CCLOG(@"cocos2d: CCGrid: not enough memory"); [self release]; return nil; } - + CCTexture2D *texture = [[CCTexture2D alloc] initWithData:data pixelFormat:format pixelsWide:POTWide pixelsHigh:POTHigh contentSize:s]; free( data ); @@ -116,23 +128,23 @@ -(id)initWithSize:(ccGridSize)gSize [self release]; return nil; } - + self = [self initWithSize:gSize texture:texture flippedTexture:NO]; - + [texture release]; - + return self; } - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | Dimensions = %ix%i>", [self class], self, gridSize_.x, gridSize_.y]; + return [NSString stringWithFormat:@"<%@ = %p | Dimensions = %ldx%ld>", [self class], self, (long)gridSize_.x, (long)gridSize_.y]; } - (void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); - [self setActive: NO]; +// [self setActive: NO]; [texture_ release]; [grabber_ release]; @@ -168,104 +180,68 @@ -(void) setIsTextureFlipped:(BOOL)flipped } } -// This routine can be merged with Director -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED --(void)applyLandscape -{ +-(void)set2DProjection +{ CCDirector *director = [CCDirector sharedDirector]; - - CGSize winSize = [director displaySizeInPixels]; - float w = winSize.width / 2; - float h = winSize.height / 2; - - ccDeviceOrientation orientation = [director deviceOrientation]; - - switch (orientation) { - case CCDeviceOrientationLandscapeLeft: - glTranslatef(w,h,0); - glRotatef(-90,0,0,1); - glTranslatef(-h,-w,0); - break; - case CCDeviceOrientationLandscapeRight: - glTranslatef(w,h,0); - glRotatef(90,0,0,1); - glTranslatef(-h,-w,0); - break; - case CCDeviceOrientationPortraitUpsideDown: - glTranslatef(w,h,0); - glRotatef(180,0,0,1); - glTranslatef(-w,-h,0); - break; - default: - break; - } -} -#endif --(void)set2DProjection -{ - CGSize winSize = [[CCDirector sharedDirector] winSizeInPixels]; + CGSize size = [director winSizeInPixels]; - glLoadIdentity(); - glViewport(0, 0, winSize.width, winSize.height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - ccglOrtho(0, winSize.width, 0, winSize.height, -1024, 1024); - glMatrixMode(GL_MODELVIEW); -} - -// This routine can be merged with Director --(void)set3DProjection -{ - CCDirector *director = [CCDirector sharedDirector]; + glViewport(0, 0, size.width * CC_CONTENT_SCALE_FACTOR(), size.height * CC_CONTENT_SCALE_FACTOR() ); + kmGLMatrixMode(KM_GL_PROJECTION); + kmGLLoadIdentity(); - CGSize winSize = [director displaySizeInPixels]; + kmMat4 orthoMatrix; + kmMat4OrthographicProjection(&orthoMatrix, 0, size.width * CC_CONTENT_SCALE_FACTOR(), 0, size.height * CC_CONTENT_SCALE_FACTOR(), -1, 1); + kmGLMultMatrix( &orthoMatrix ); - glViewport(0, 0, winSize.width, winSize.height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(60, (GLfloat)winSize.width/winSize.height, 0.5f, 1500.0f); + kmGLMatrixMode(KM_GL_MODELVIEW); + kmGLLoadIdentity(); + - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - gluLookAt( winSize.width/2, winSize.height/2, [director getZEye], - winSize.width/2, winSize.height/2, 0, - 0.0f, 1.0f, 0.0f - ); + ccSetProjectionMatrixDirty(); } -(void)beforeDraw { + // save projection + CCDirector *director = [CCDirector sharedDirector]; + directorProjection_ = [director projection]; + + // 2d projection +// [director setProjection:kCCDirectorProjection2D]; [self set2DProjection]; + + [grabber_ beforeRender:texture_]; } + -(void)afterDraw:(CCNode *)target { [grabber_ afterRender:texture_]; - - [self set3DProjection]; -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - [self applyLandscape]; -#endif + + // restore projection + CCDirector *director = [CCDirector sharedDirector]; + [director setProjection: directorProjection_]; if( target.camera.dirty ) { - CGPoint offset = [target anchorPointInPixels]; + CGPoint offset = [target anchorPointInPoints]; // // XXX: Camera should be applied in the AnchorPoint // - ccglTranslate(offset.x, offset.y, 0); + kmGLTranslatef(offset.x, offset.y, 0); [target.camera locate]; - ccglTranslate(-offset.x, -offset.y, 0); + kmGLTranslatef(-offset.x, -offset.y, 0); } - - glBindTexture(GL_TEXTURE_2D, texture_.name); + + ccGLBindTexture2D( texture_.name ); [self blit]; } + -(void)blit { [NSException raise:@"GridBase" format:@"Abstract class needs implementation"]; @@ -301,18 +277,24 @@ -(void)dealloc -(void)blit { NSInteger n = gridSize_.x * gridSize_.y; - - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY - // Unneeded states: GL_COLOR_ARRAY - glDisableClientState(GL_COLOR_ARRAY); - - glVertexPointer(3, GL_FLOAT, 0, vertices); - glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates); + + ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); + [shaderProgram_ use]; + [shaderProgram_ setUniformForModelViewProjectionMatrix]; + + // + // Attributes + // + + // position + glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, 0, vertices); + + // texCoods + glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, texCoordinates); + glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices); - // restore GL default state - glEnableClientState(GL_COLOR_ARRAY); + CC_INCREMENT_GL_DRAWS(1); } -(void)calculateVertexPoints @@ -320,55 +302,62 @@ -(void)calculateVertexPoints float width = (float)texture_.pixelsWide; float height = (float)texture_.pixelsHigh; float imageH = texture_.contentSizeInPixels.height; - + int x, y, i; + + if (vertices) free(vertices); + if (originalVertices) free(originalVertices); + if (texCoordinates) free(texCoordinates); + if (indices) free(indices); - vertices = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F)); - originalVertices = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F)); - texCoordinates = malloc((gridSize_.x+1)*(gridSize_.y+1)*sizeof(CGPoint)); - indices = malloc(gridSize_.x*gridSize_.y*sizeof(GLushort)*6); + NSUInteger numOfPoints = (gridSize_.x+1) * (gridSize_.y+1); - float *vertArray = (float*)vertices; - float *texArray = (float*)texCoordinates; + vertices = malloc(numOfPoints * sizeof(ccVertex3F)); + originalVertices = malloc(numOfPoints * sizeof(ccVertex3F)); + texCoordinates = malloc(numOfPoints * sizeof(ccVertex2F)); + indices = malloc( (gridSize_.x * gridSize_.y) * sizeof(GLushort)*6); + + GLfloat *vertArray = (GLfloat*)vertices; + GLfloat *texArray = (GLfloat*)texCoordinates; GLushort *idxArray = (GLushort *)indices; - + for( x = 0; x < gridSize_.x; x++ ) { for( y = 0; y < gridSize_.y; y++ ) { NSInteger idx = (y * gridSize_.x) + x; - - float x1 = x * step_.x; - float x2 = x1 + step_.x; - float y1 = y * step_.y; - float y2 = y1 + step_.y; - + + GLfloat x1 = x * step_.x; + GLfloat x2 = x1 + step_.x; + GLfloat y1 = y * step_.y; + GLfloat y2 = y1 + step_.y; + GLushort a = x * (gridSize_.y+1) + y; GLushort b = (x+1) * (gridSize_.y+1) + y; GLushort c = (x+1) * (gridSize_.y+1) + (y+1); GLushort d = x * (gridSize_.y+1) + (y+1); - + GLushort tempidx[6] = { a, b, d, b, c, d }; - + memcpy(&idxArray[6*idx], tempidx, 6*sizeof(GLushort)); - + int l1[4] = { a*3, b*3, c*3, d*3 }; ccVertex3F e = {x1,y1,0}; ccVertex3F f = {x2,y1,0}; ccVertex3F g = {x2,y2,0}; ccVertex3F h = {x1,y2,0}; - + ccVertex3F l2[4] = { e, f, g, h }; - + int tex1[4] = { a*2, b*2, c*2, d*2 }; CGPoint tex2[4] = { ccp(x1, y1), ccp(x2, y1), ccp(x2, y2), ccp(x1, y2) }; - + for( i = 0; i < 4; i++ ) { vertArray[ l1[i] ] = l2[i].x; vertArray[ l1[i] + 1 ] = l2[i].y; vertArray[ l1[i] + 2 ] = l2[i].z; - + texArray[ tex1[i] ] = tex2[i].x / width; if( isTextureFlipped_ ) texArray[ tex1[i] + 1 ] = (imageH - tex2[i].y) / height; @@ -377,7 +366,7 @@ -(void)calculateVertexPoints } } } - + memcpy(originalVertices, vertices, (gridSize_.x+1)*(gridSize_.y+1)*sizeof(ccVertex3F)); } @@ -385,9 +374,9 @@ -(ccVertex3F)vertex:(ccGridSize)pos { NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3; float *vertArray = (float *)vertices; - + ccVertex3F vert = { vertArray[index], vertArray[index+1], vertArray[index+2] }; - + return vert; } @@ -395,9 +384,9 @@ -(ccVertex3F)originalVertex:(ccGridSize)pos { NSInteger index = (pos.x * (gridSize_.y+1) + pos.y) * 3; float *vertArray = (float *)originalVertices; - + ccVertex3F vert = { vertArray[index], vertArray[index+1], vertArray[index+2] }; - + return vert; } @@ -440,18 +429,25 @@ -(void)dealloc -(void)blit { NSInteger n = gridSize_.x * gridSize_.y; - - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY - // Unneeded states: GL_COLOR_ARRAY - glDisableClientState(GL_COLOR_ARRAY); - - glVertexPointer(3, GL_FLOAT, 0, vertices); - glTexCoordPointer(2, GL_FLOAT, 0, texCoordinates); - glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices); - // restore default GL state - glEnableClientState(GL_COLOR_ARRAY); + [shaderProgram_ use]; + [shaderProgram_ setUniformForModelViewProjectionMatrix]; + + + // + // Attributes + // + ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); + + // position + glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, 0, vertices); + + // texCoods + glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, texCoordinates); + + glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, indices); + + CC_INCREMENT_GL_DRAWS(1); } -(void)calculateVertexPoints @@ -459,20 +455,25 @@ -(void)calculateVertexPoints float width = (float)texture_.pixelsWide; float height = (float)texture_.pixelsHigh; float imageH = texture_.contentSizeInPixels.height; - + NSInteger numQuads = gridSize_.x * gridSize_.y; - - vertices = malloc(numQuads*12*sizeof(GLfloat)); - originalVertices = malloc(numQuads*12*sizeof(GLfloat)); - texCoordinates = malloc(numQuads*8*sizeof(GLfloat)); + + if (vertices) free(vertices); + if (originalVertices) free(originalVertices); + if (texCoordinates) free(texCoordinates); + if (indices) free(indices); + + vertices = malloc(numQuads*4*sizeof(ccVertex3F)); + originalVertices = malloc(numQuads*4*sizeof(ccVertex3F)); + texCoordinates = malloc(numQuads*4*sizeof(ccVertex2F)); indices = malloc(numQuads*6*sizeof(GLushort)); - - float *vertArray = (float*)vertices; - float *texArray = (float*)texCoordinates; + + GLfloat *vertArray = (GLfloat*)vertices; + GLfloat *texArray = (GLfloat*)texCoordinates; GLushort *idxArray = (GLushort *)indices; - + int x, y; - + for( x = 0; x < gridSize_.x; x++ ) { for( y = 0; y < gridSize_.y; y++ ) @@ -481,7 +482,7 @@ -(void)calculateVertexPoints float x2 = x1 + step_.x; float y1 = y * step_.y; float y2 = y1 + step_.y; - + *vertArray++ = x1; *vertArray++ = y1; *vertArray++ = 0; @@ -494,10 +495,10 @@ -(void)calculateVertexPoints *vertArray++ = x2; *vertArray++ = y2; *vertArray++ = 0; - + float newY1 = y1; float newY2 = y2; - + if( isTextureFlipped_ ) { newY1 = imageH - y1; newY2 = imageH - y2; @@ -513,18 +514,18 @@ -(void)calculateVertexPoints *texArray++ = newY2 / height; } } - + for( x = 0; x < numQuads; x++) { idxArray[x*6+0] = x*4+0; idxArray[x*6+1] = x*4+1; idxArray[x*6+2] = x*4+2; - + idxArray[x*6+3] = x*4+1; idxArray[x*6+4] = x*4+2; idxArray[x*6+5] = x*4+3; } - + memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat)); } @@ -539,10 +540,10 @@ -(ccQuad3)originalTile:(ccGridSize)pos { NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3; float *vertArray = (float*)originalVertices; - + ccQuad3 ret; memcpy(&ret, &vertArray[idx], sizeof(ccQuad3)); - + return ret; } @@ -550,10 +551,10 @@ -(ccQuad3)tile:(ccGridSize)pos { NSInteger idx = (gridSize_.y * pos.x + pos.y) * 4 * 3; float *vertArray = (float*)vertices; - + ccQuad3 ret; memcpy(&ret, &vertArray[idx], sizeof(ccQuad3)); - + return ret; } @@ -562,7 +563,7 @@ -(void)reuse if ( reuseGrid_ > 0 ) { NSInteger numQuads = gridSize_.x * gridSize_.y; - + memcpy(originalVertices, vertices, numQuads*12*sizeof(GLfloat)); reuseGrid_--; } diff --git a/cocos2d/cocos2d/CCLabelAtlas.h b/cocos2d/cocos2d/CCLabelAtlas.h old mode 100644 new mode 100755 index 7a1fe9a..caa2ff8 --- a/cocos2d/cocos2d/CCLabelAtlas.h +++ b/cocos2d/cocos2d/CCLabelAtlas.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,29 +29,40 @@ #import "CCTextureAtlas.h" /** CCLabelAtlas is a subclass of CCAtlasNode. - + It can be as a replacement of CCLabel since it is MUCH faster. - + CCLabelAtlas versus CCLabel: - CCLabelAtlas is MUCH faster than CCLabel - CCLabelAtlas "characters" have a fixed height and width - CCLabelAtlas "characters" can be anything you want since they are taken from an image file - + A more flexible class is CCLabelBMFont. It supports variable width characters and it also has a nice editor. */ @interface CCLabelAtlas : CCAtlasNode -{ +{ // string to render NSString *string_; - + // the first char in the charmap - unsigned char mapStartChar_; + NSUInteger mapStartChar_; } /** creates the CCLabelAtlas with a string, a char map file(the atlas), the width and height of each element in points and the starting char of the atlas */ -+(id) labelWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(NSUInteger)w itemHeight:(NSUInteger)h startCharMap:(unsigned char)c; ++(id) labelWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(NSUInteger)w itemHeight:(NSUInteger)h startCharMap:(NSUInteger)firstElement; + +/** creates the CCLabelAtlas with a string and a configuration file + @since v2.0 + */ ++(id) labelWithString:(NSString*) string fntFile:(NSString*)fontFile; /** initializes the CCLabelAtlas with a string, a char map file(the atlas), the width and height in points of each element and the starting char of the atlas */ --(id) initWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(NSUInteger)w itemHeight:(NSUInteger)h startCharMap:(unsigned char)c; +-(id) initWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(NSUInteger)w itemHeight:(NSUInteger)h startCharMap:(NSUInteger)firstElement; + +/** initializes the CCLabelAtlas with a string and a configuration file + @since v2.0 + */ +-(id) initWithString:(NSString*) string fntFile:(NSString*)fontFile; + @end diff --git a/cocos2d/cocos2d/CCLabelAtlas.m b/cocos2d/cocos2d/CCLabelAtlas.m old mode 100644 new mode 100755 index a8d4f43..40b94e8 --- a/cocos2d/cocos2d/CCLabelAtlas.m +++ b/cocos2d/cocos2d/CCLabelAtlas.m @@ -1,19 +1,19 @@ /* * cocos2d for iPhone: http://www.cocos2d-iphone.org * - * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2008-2011 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,24 +29,53 @@ #import "ccMacros.h" #import "CCDrawingPrimitives.h" #import "CCLabelAtlas.h" +#import "CCShaderCache.h" +#import "CCGLProgram.h" +#import "ccGLStateCache.h" +#import "CCDirector.h" #import "Support/CGPointExtension.h" +#import "Support/TransformUtils.h" +#import "Support/CCFileUtils.h" - +// external +#import "kazmath/GL/matrix.h" @implementation CCLabelAtlas #pragma mark CCLabelAtlas - Creation & Init -+(id) labelWithString:(NSString*)string charMapFile:(NSString*)charmapfile itemWidth:(NSUInteger)w itemHeight:(NSUInteger)h startCharMap:(unsigned char)c ++(id) labelWithString:(NSString*)string charMapFile:(NSString*)charmapfile itemWidth:(NSUInteger)w itemHeight:(NSUInteger)h startCharMap:(NSUInteger)c { return [[[self alloc] initWithString:string charMapFile:charmapfile itemWidth:w itemHeight:h startCharMap:c] autorelease]; } --(id) initWithString:(NSString*) theString charMapFile: (NSString*) charmapfile itemWidth:(NSUInteger)w itemHeight:(NSUInteger)h startCharMap:(unsigned char)c ++(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile { + return [[[self alloc] initWithString:string fntFile:fntFile] autorelease]; +} +-(id) initWithString:(NSString*) theString fntFile:(NSString*)fntFile +{ + NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[CCFileUtils sharedFileUtils] fullPathFromRelativePath:fntFile]]; + + NSAssert( [[dict objectForKey:@"version"] intValue] == 1, @"Unsupported version. Upgrade cocos2d version"); + + NSString *textureFilename = [dict objectForKey:@"textureFilename"]; + NSUInteger width = [[dict objectForKey:@"itemWidth"] unsignedIntValue] / CC_CONTENT_SCALE_FACTOR(); + NSUInteger height = [[dict objectForKey:@"itemHeight"] unsignedIntValue] / CC_CONTENT_SCALE_FACTOR(); + NSUInteger startChar = [[dict objectForKey:@"firstChar"] unsignedIntValue]; + + return [self initWithString:theString + charMapFile:textureFilename + itemWidth:width + itemHeight:height + startCharMap:startChar]; +} + +-(id) initWithString:(NSString*) theString charMapFile: (NSString*) charmapfile itemWidth:(NSUInteger)w itemHeight:(NSUInteger)h startCharMap:(NSUInteger)c +{ if ((self=[super initWithTileFile:charmapfile tileWidth:w tileHeight:h itemsToRender:[theString length] ]) ) { - mapStartChar_ = c; + mapStartChar_ = c; [self setString: theString]; } @@ -65,7 +94,7 @@ -(void) dealloc -(void) updateAtlasValues { NSUInteger n = [string_ length]; - + ccV3F_C4B_T2F_Quad quad; const unsigned char *s = (unsigned char*) [string_ UTF8String]; @@ -73,25 +102,29 @@ -(void) updateAtlasValues CCTexture2D *texture = [textureAtlas_ texture]; float textureWide = [texture pixelsWide]; float textureHigh = [texture pixelsHigh]; + float itemWidthInPixels = itemWidth_ * CC_CONTENT_SCALE_FACTOR(); + float itemHeightInPixels = itemHeight_ * CC_CONTENT_SCALE_FACTOR(); + - for( NSUInteger i=0; i textureAtlas_.capacity ) - [textureAtlas_ resizeCapacity:len]; + if( newString == string_ ) + return; - [string_ release]; - string_ = [newString copy]; - [self updateAtlasValues]; + if( [newString hash] != [string_ hash] ) { - CGSize s; - s.width = len * itemWidth_; - s.height = itemHeight_; - [self setContentSizeInPixels:s]; - - self.quadsToDraw = len; + NSUInteger len = [newString length]; + if( len > textureAtlas_.capacity ) + [textureAtlas_ resizeCapacity:len]; + + [string_ release]; + string_ = [newString copy]; + [self updateAtlasValues]; + + CGSize s = CGSizeMake(len * itemWidth_, itemHeight_); + [self setContentSize:s]; + + self.quadsToDraw = len; + } } -(NSString*) string @@ -156,7 +198,6 @@ - (void) draw ccp(s.width,s.height),ccp(0,s.height), }; ccDrawPoly(vertices, 4, YES); - } #endif // CC_LABELATLAS_DEBUG_DRAW diff --git a/cocos2d/cocos2d/CCLabelBMFont.h b/cocos2d/cocos2d/CCLabelBMFont.h old mode 100644 new mode 100755 index 2570bc0..fc07359 --- a/cocos2d/cocos2d/CCLabelBMFont.h +++ b/cocos2d/cocos2d/CCLabelBMFont.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,7 +25,7 @@ * Portions of this code are based and inspired on: * http://www.71squared.co.uk/2009/04/iphone-game-programming-tutorial-4-bitmap-font-class * by Michael Daley - * + * * Use any of these editors to generate BMFonts: * http://glyphdesigner.71squared.com/ (Commercial, Mac OS X) * http://www.n4te.com/hiero/hiero.jnlp (Free, Java) @@ -36,6 +36,10 @@ #import "CCSpriteBatchNode.h" #import "Support/uthash.h" +enum { + kCCLabelAutomaticWidth = -1, +}; + struct _KerningHashElement; /** @struct ccBMFontDef @@ -43,15 +47,15 @@ struct _KerningHashElement; */ typedef struct _BMFontDef { //! ID of the character - unsigned int charID; + unichar charID; //! origin and size of the font CGRect rect; //! The X amount the image should be offset when drawing the image (in pixels) - int xOffset; + short xOffset; //! The Y amount the image should be offset when drawing the image (in pixels) - int yOffset; + short yOffset; //! The amount to move the current position after drawing the character (in pixels) - int xAdvance; + short xAdvance; } ccBMFontDef; /** @struct ccBMFontPadding @@ -69,34 +73,34 @@ typedef struct _BMFontPadding { int bottom; } ccBMFontPadding; -enum { - // how many characters are supported - kCCBMFontMaxChars = 2048, //256, -}; /** CCBMFontConfiguration has parsed configuration of the the .fnt file @since v0.8 */ @interface CCBMFontConfiguration : NSObject { -// XXX: Creating a public interface so that the bitmapFontArray[] is accesible + // atlas name + NSString *atlasName_; + + // XXX: Creating a public interface so that the bitmapFontArray[] is accesible @public - // The characters building up the font - ccBMFontDef BMFontArray_[kCCBMFontMaxChars]; - - // FNTConfig: Common Height - NSUInteger commonHeight_; - + + // BMFont definitions + struct _FontDefHashElement *fontDefDictionary_; + + // FNTConfig: Common Height. Should be signed (issue #1343) + NSInteger commonHeight_; + // Padding ccBMFontPadding padding_; - - // atlas name - NSString *atlasName_; // values for kerning struct _KerningHashElement *kerningDictionary_; } +// atlasName +@property (nonatomic, readwrite, retain) NSString *atlasName; + /** allocates a CCBMFontConfiguration with a FNT file */ +(id) configurationWithFNTFile:(NSString*)FNTfile; /** initializes a CCBMFontConfiguration with a FNT file */ @@ -105,31 +109,33 @@ enum { /** CCLabelBMFont is a subclass of CCSpriteBatchNode - + Features: - Treats each character like a CCSprite. This means that each individual character can be: - - rotated - - scaled - - translated - - tinted - - chage the opacity + - rotated + - scaled + - translated + - tinted + - chage the opacity - It can be used as part of a menu item. - anchorPoint can be used to align the "label" - Supports AngelCode text format - + Limitations: - - All inner characters are using an anchorPoint of (0.5f, 0.5f) and it is not recommend to change it - because it might affect the rendering - + - All inner characters are using an anchorPoint of (0.5f, 0.5f) and it is not recommend to change it + because it might affect the rendering + CCLabelBMFont implements the protocol CCLabelProtocol, like CCLabel and CCLabelAtlas. CCLabelBMFont has the flexibility of CCLabel, the speed of CCLabelAtlas and all the features of CCSprite. If in doubt, use CCLabelBMFont instead of CCLabelAtlas / CCLabel. - + Supported editors: - - http://www.n4te.com/hiero/hiero.jnlp - - http://slick.cokeandcode.com/demos/hiero.jnlp - - http://www.angelcode.com/products/bmfont/ - + - http://glyphdesigner.71squared.com/ + - http://www.bmglyph.com/ + - http://www.n4te.com/hiero/hiero.jnlp + - http://slick.cokeandcode.com/demos/hiero.jnlp + - http://www.angelcode.com/products/bmfont/ + @since v0.8 */ @@ -137,13 +143,26 @@ enum { { // string to render NSString *string_; - + + // name of fntFile + NSString *fntFile_; + + // initial string without line breaks + NSString *initialString_; + // max width until a line break is added + float width_; + // alignment of all lines + CCTextAlignment alignment_; + CCBMFontConfiguration *configuration_; // texture RGBA GLubyte opacity_; ccColor3B color_; BOOL opacityModifyRGB_; + + // offset of the texture atlas + CGPoint imageOffset_; } /** Purges the cached data. @@ -152,24 +171,43 @@ enum { */ +(void) purgeCachedData; +/** alignment used for the label */ +@property (nonatomic,assign,readonly) CCTextAlignment alignment; +/** fntFile used for the font */ +@property (nonatomic,retain) NSString* fntFile; /** conforms to CCRGBAProtocol protocol */ @property (nonatomic,readwrite) GLubyte opacity; /** conforms to CCRGBAProtocol protocol */ @property (nonatomic,readwrite) ccColor3B color; -/** creates a BMFont label with an initial string and the FNT file */ +/** creates a BMFont label with an initial string and the FNT file. */ +(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile; +/** creates a BMFont label with an initial string, the FNT file, width, and alignment option */ ++(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment; +/** creates a BMFont label with an initial string, the FNT file, width, alignment option and the offset of where the glpyhs start on the .PNG image */ ++(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment imageOffset:(CGPoint)offset; /** init a BMFont label with an initial string and the FNT file */ -(id) initWithString:(NSString*)string fntFile:(NSString*)fntFile; +/** init a BMFont label with an initial string and the FNT file, width, and alignment option*/ +-(id) initWithString:(NSString*)string fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment; +/** init a BMFont label with an initial string and the FNT file, width, alignment option and the offset of where the glyphs start on the .PNG image */ +-(id) initWithString:(NSString*)string fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment imageOffset:(CGPoint)offset; /** updates the font chars based on the string to render */ -(void) createFontChars; + +/** set label width */ +- (void)setWidth:(float)width; + +/** set label alignment */ +- (void)setAlignment:(CCTextAlignment)alignment; + @end /** Free function that parses a FNT file a place it on the cache -*/ + */ CCBMFontConfiguration * FNTConfigLoadFile( NSString *file ); /** Purges the FNT config cache */ diff --git a/cocos2d/cocos2d/CCLabelBMFont.m b/cocos2d/cocos2d/CCLabelBMFont.m old mode 100644 new mode 100755 index 4313840..371f70c --- a/cocos2d/cocos2d/CCLabelBMFont.m +++ b/cocos2d/cocos2d/CCLabelBMFont.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -35,10 +35,12 @@ */ #import "ccConfig.h" +#import "ccMacros.h" #import "CCLabelBMFont.h" #import "CCSprite.h" #import "CCDrawingPrimitives.h" #import "CCConfiguration.h" +#import "CCTextureCache.h" #import "Support/CCFileUtils.h" #import "Support/CGPointExtension.h" #import "Support/uthash.h" @@ -50,16 +52,17 @@ CCBMFontConfiguration* FNTConfigLoadFile( NSString *fntFile) { CCBMFontConfiguration *ret = nil; - + if( configurations == nil ) configurations = [[NSMutableDictionary dictionaryWithCapacity:3] retain]; - + ret = [configurations objectForKey:fntFile]; if( ret == nil ) { ret = [CCBMFontConfiguration configurationWithFNTFile:fntFile]; - [configurations setObject:ret forKey:fntFile]; + if( ret ) + [configurations setObject:ret forKey:fntFile]; } - + return ret; } @@ -72,28 +75,40 @@ void FNTConfigRemoveCache( void ) // Equal function for targetSet. typedef struct _KerningHashElement -{ +{ int key; // key for the hash. 16-bit for 1st element, 16-bit for 2nd element int amount; UT_hash_handle hh; } tKerningHashElement; + #pragma mark - #pragma mark BitmapFontConfiguration +typedef struct _FontDefHashElement +{ + NSUInteger key; // key. Font Unicode value + ccBMFontDef fontDef; // font definition + UT_hash_handle hh; +} tFontDefHashElement; + -@interface CCBMFontConfiguration (Private) --(void) parseConfigFile:(NSString*)controlFile; +@interface CCBMFontConfiguration () +-(BOOL) parseConfigFile:(NSString*)controlFile; -(void) parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition; -(void) parseInfoArguments:(NSString*)line; -(void) parseCommonArguments:(NSString*)line; -(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile; --(void) parseKerningCapacity:(NSString*)line; -(void) parseKerningEntry:(NSString*)line; -(void) purgeKerningDictionary; +-(void) purgeFontDefDictionary; @end +#pragma mark - +#pragma mark CCBMFontConfiguration + @implementation CCBMFontConfiguration +@synthesize atlasName=atlasName_; +(id) configurationWithFNTFile:(NSString*)FNTfile { @@ -103,10 +118,14 @@ +(id) configurationWithFNTFile:(NSString*)FNTfile -(id) initWithFNTfile:(NSString*)fntFile { if((self=[super init])) { - + kerningDictionary_ = NULL; + fontDefDictionary_ = NULL; - [self parseConfigFile:fntFile]; + if( ! [self parseConfigFile:fntFile] ) { + [self release]; + return nil; + } } return self; } @@ -114,6 +133,7 @@ -(id) initWithFNTfile:(NSString*)fntFile - (void) dealloc { CCLOGINFO( @"cocos2d: deallocing %@", self); + [self purgeFontDefDictionary]; [self purgeKerningDictionary]; [atlasName_ release]; [super dealloc]; @@ -121,48 +141,61 @@ - (void) dealloc - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | Kernings:%d | Image = %@>", [self class], self, + return [NSString stringWithFormat:@"<%@ = %p | Glphys:%d Kernings:%d | Image = %@>", [self class], self, + HASH_COUNT(fontDefDictionary_), HASH_COUNT(kerningDictionary_), atlasName_]; } +-(void) purgeFontDefDictionary +{ + tFontDefHashElement *current, *tmp; + + HASH_ITER(hh, fontDefDictionary_, current, tmp) { + HASH_DEL(fontDefDictionary_, current); + free(current); + } +} + -(void) purgeKerningDictionary { tKerningHashElement *current; - + while(kerningDictionary_) { - current = kerningDictionary_; + current = kerningDictionary_; HASH_DEL(kerningDictionary_,current); free(current); } } -- (void)parseConfigFile:(NSString*)fntFile -{ - NSString *fullpath = [CCFileUtils fullPathFromRelativePath:fntFile]; +- (BOOL)parseConfigFile:(NSString*)fntFile +{ + NSString *fullpath = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:fntFile]; NSError *error; NSString *contents = [NSString stringWithContentsOfFile:fullpath encoding:NSUTF8StringEncoding error:&error]; - NSAssert1( contents, @"cocos2d: Error parsing FNTfile: %@", error); - - + if( ! contents ) { + NSLog(@"cocos2d: Error parsing FNTfile %@: %@", fntFile, error); + return NO; + } + // Move all lines in the string, which are denoted by \n, into an array NSArray *lines = [[NSArray alloc] initWithArray:[contents componentsSeparatedByString:@"\n"]]; - + // Create an enumerator which we can use to move through the lines read from the control file NSEnumerator *nse = [lines objectEnumerator]; - + // Create a holder for each line we are going to work with NSString *line; - + // Loop through all the lines in the lines array processing each one while( (line = [nse nextObject]) ) { // parse spacing / padding if([line hasPrefix:@"info face"]) { // XXX: info parsing is incomplete // Not needed for the Hiero editors, but needed for the AngelCode editor -// [self parseInfoArguments:line]; + // [self parseInfoArguments:line]; } // Check to see if the start of the line is something we are interested in else if([line hasPrefix:@"common lineHeight"]) { @@ -176,21 +209,24 @@ - (void)parseConfigFile:(NSString*)fntFile } else if([line hasPrefix:@"char"]) { // Parse the current line and create a new CharDef - ccBMFontDef characterDefinition; - [self parseCharacterDefinition:line charDef:&characterDefinition]; - - // Add the CharDef returned to the charArray - BMFontArray_[ characterDefinition.charID ] = characterDefinition; - } - else if([line hasPrefix:@"kernings count"]) { - [self parseKerningCapacity:line]; + tFontDefHashElement *element = malloc( sizeof(*element) ); + + [self parseCharacterDefinition:line charDef:&element->fontDef]; + + element->key = element->fontDef.charID; + HASH_ADD_INT(fontDefDictionary_, key, element); } +// else if([line hasPrefix:@"kernings count"]) { +// [self parseKerningCapacity:line]; +// } else if([line hasPrefix:@"kerning first"]) { [self parseKerningEntry:line]; } } // Finished with lines so release it [lines release]; + + return YES; } -(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile @@ -199,23 +235,23 @@ -(void) parseImageFileName:(NSString*)line fntFile:(NSString*)fntFile // Break the values for this line up using = NSArray *values = [line componentsSeparatedByString:@"="]; - + // Get the enumerator for the array of components which has been created NSEnumerator *nse = [values objectEnumerator]; - + // We need to move past the first entry in the array before we start assigning values [nse nextObject]; - + // page ID. Sanity check propertyValue = [nse nextObject]; NSAssert( [propertyValue intValue] == 0, @"XXX: LabelBMFont only supports 1 page"); - - // file + + // file propertyValue = [nse nextObject]; NSArray *array = [propertyValue componentsSeparatedByString:@"\""]; propertyValue = [array objectAtIndex:1]; NSAssert(propertyValue,@"LabelBMFont file could not be found"); - + // Supports subdirectories NSString *dir = [fntFile stringByDeletingLastPathComponent]; atlasName_ = [dir stringByAppendingPathComponent:propertyValue]; @@ -231,15 +267,15 @@ -(void) parseInfoArguments:(NSString*)line // info face="Cracked" size=36 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 // NSArray *values = [line componentsSeparatedByString:@"="]; - NSEnumerator *nse = [values objectEnumerator]; + NSEnumerator *nse = [values objectEnumerator]; NSString *propertyValue = nil; - + // We need to move past the first entry in the array before we start assigning values [nse nextObject]; - + // face (ignore) [nse nextObject]; - + // size (ignore) [nse nextObject]; @@ -248,7 +284,7 @@ -(void) parseInfoArguments:(NSString*)line // italic (ignore) [nse nextObject]; - + // charset (ignore) [nse nextObject]; @@ -260,20 +296,20 @@ -(void) parseInfoArguments:(NSString*)line // smooth (ignore) [nse nextObject]; - + // aa (ignore) [nse nextObject]; - + // padding (ignore) propertyValue = [nse nextObject]; { - + NSArray *paddingValues = [propertyValue componentsSeparatedByString:@","]; NSEnumerator *paddingEnum = [paddingValues objectEnumerator]; // padding top propertyValue = [paddingEnum nextObject]; padding_.top = [propertyValue intValue]; - + // padding right propertyValue = [paddingEnum nextObject]; padding_.right = [propertyValue intValue]; @@ -281,16 +317,16 @@ -(void) parseInfoArguments:(NSString*)line // padding bottom propertyValue = [paddingEnum nextObject]; padding_.bottom = [propertyValue intValue]; - + // padding left propertyValue = [paddingEnum nextObject]; padding_.left = [propertyValue intValue]; - + CCLOG(@"cocos2d: padding: %d,%d,%d,%d", padding_.left, padding_.top, padding_.right, padding_.bottom); } // spacing (ignore) - [nse nextObject]; + [nse nextObject]; } -(void) parseCommonArguments:(NSString*)line @@ -300,49 +336,48 @@ -(void) parseCommonArguments:(NSString*)line // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0 // NSArray *values = [line componentsSeparatedByString:@"="]; - NSEnumerator *nse = [values objectEnumerator]; + NSEnumerator *nse = [values objectEnumerator]; NSString *propertyValue = nil; - + // We need to move past the first entry in the array before we start assigning values [nse nextObject]; - + // Character ID propertyValue = [nse nextObject]; commonHeight_ = [propertyValue intValue]; - + // base (ignore) [nse nextObject]; - - + + // scaleW. sanity check - propertyValue = [nse nextObject]; + propertyValue = [nse nextObject]; NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported"); - + // scaleH. sanity check propertyValue = [nse nextObject]; NSAssert( [propertyValue intValue] <= [[CCConfiguration sharedConfiguration] maxTextureSize], @"CCLabelBMFont: page can't be larger than supported"); - + // pages. sanity check propertyValue = [nse nextObject]; NSAssert( [propertyValue intValue] == 1, @"CCBitfontAtlas: only supports 1 page"); - + // packed (ignore) What does this mean ?? } - (void)parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)characterDefinition -{ +{ // Break the values for this line up using = NSArray *values = [line componentsSeparatedByString:@"="]; - NSEnumerator *nse = [values objectEnumerator]; + NSEnumerator *nse = [values objectEnumerator]; NSString *propertyValue; - + // We need to move past the first entry in the array before we start assigning values [nse nextObject]; - + // Character ID propertyValue = [nse nextObject]; propertyValue = [propertyValue substringToIndex: [propertyValue rangeOfString: @" "].location]; characterDefinition->charID = [propertyValue intValue]; - NSAssert(characterDefinition->charID < kCCBMFontMaxChars, @"BitmpaFontAtlas: CharID bigger than supported"); // Character x propertyValue = [nse nextObject]; @@ -367,45 +402,23 @@ - (void)parseCharacterDefinition:(NSString*)line charDef:(ccBMFontDef*)character characterDefinition->xAdvance = [propertyValue intValue]; } --(void) parseKerningCapacity:(NSString*) line -{ - // When using uthash there is not need to parse the capacity. - -// NSAssert(!kerningDictionary, @"dictionary already initialized"); -// -// // Break the values for this line up using = -// NSArray *values = [line componentsSeparatedByString:@"="]; -// NSEnumerator *nse = [values objectEnumerator]; -// NSString *propertyValue; -// -// // We need to move past the first entry in the array before we start assigning values -// [nse nextObject]; -// -// // count -// propertyValue = [nse nextObject]; -// int capacity = [propertyValue intValue]; -// -// if( capacity != -1 ) -// kerningDictionary = ccHashSetNew(capacity, targetSetEql); -} - -(void) parseKerningEntry:(NSString*) line { NSArray *values = [line componentsSeparatedByString:@"="]; - NSEnumerator *nse = [values objectEnumerator]; + NSEnumerator *nse = [values objectEnumerator]; NSString *propertyValue; - + // We need to move past the first entry in the array before we start assigning values [nse nextObject]; - + // first propertyValue = [nse nextObject]; int first = [propertyValue intValue]; - + // second propertyValue = [nse nextObject]; int second = [propertyValue intValue]; - + // second propertyValue = [nse nextObject]; int amount = [propertyValue intValue]; @@ -421,17 +434,23 @@ -(void) parseKerningEntry:(NSString*) line #pragma mark - #pragma mark CCLabelBMFont -@interface CCLabelBMFont (Private) --(NSString*) atlasNameFromFntFile:(NSString*)fntFile; +@interface CCLabelBMFont () -(int) kerningAmountForFirst:(unichar)first second:(unichar)second; +-(void) updateLabel; +-(void) setString:(NSString*) newString updateLabel:(BOOL)update; @end +#pragma mark - +#pragma mark CCLabelBMFont + @implementation CCLabelBMFont +@synthesize alignment = alignment_; @synthesize opacity = opacity_, color = color_; + #pragma mark LabelBMFont - Purge Cache +(void) purgeCachedData { @@ -442,32 +461,74 @@ +(void) purgeCachedData +(id) labelWithString:(NSString *)string fntFile:(NSString *)fntFile { - return [[[self alloc] initWithString:string fntFile:fntFile] autorelease]; + return [[[self alloc] initWithString:string fntFile:fntFile width:kCCLabelAutomaticWidth alignment:kCCTextAlignmentLeft imageOffset:CGPointZero] autorelease]; +} + ++(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment +{ + return [[[self alloc] initWithString:string fntFile:fntFile width:width alignment:alignment imageOffset:CGPointZero] autorelease]; +} + ++(id) labelWithString:(NSString*)string fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment imageOffset:(CGPoint)offset +{ + return [[[self alloc] initWithString:string fntFile:fntFile width:width alignment:alignment imageOffset:offset] autorelease]; +} + +-(id) init +{ + return [self initWithString:nil fntFile:nil width:kCCLabelAutomaticWidth alignment:kCCTextAlignmentLeft imageOffset:CGPointZero]; } -(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile -{ +{ + return [self initWithString:theString fntFile:fntFile width:kCCLabelAutomaticWidth alignment:kCCTextAlignmentLeft]; +} + +-(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment +{ + return [self initWithString:theString fntFile:fntFile width:width alignment:alignment imageOffset:CGPointZero]; +} + +// designated initializer +-(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile width:(float)width alignment:(CCTextAlignment)alignment imageOffset:(CGPoint)offset +{ + NSAssert(!configuration_, @"re-init is no longer supported"); - [configuration_ release]; // allow re-init + // if theString && fntfile are both nil, then it is OK + NSAssert( (theString && fntFile) || (theString==nil && fntFile==nil), @"Invalid params for CCLabelBMFont"); + + CCTexture2D *texture = nil; - configuration_ = FNTConfigLoadFile(fntFile); - [configuration_ retain]; + if( fntFile ) { + CCBMFontConfiguration *newConf = FNTConfigLoadFile(fntFile); + NSAssert( newConf, @"CCLabelBMFont: Impossible to create font. Please check file: '%@'", fntFile ); - NSAssert( configuration_, @"Error creating config for LabelBMFont"); + configuration_ = [newConf retain]; + + fntFile_ = [fntFile retain]; - - if ((self=[super initWithFile:configuration_->atlasName_ capacity:[theString length]])) { + texture = [[CCTextureCache sharedTextureCache] addImage:configuration_.atlasName]; + + } else + texture = [[[CCTexture2D alloc] init] autorelease]; + + + if( (self=[super initWithTexture:texture capacity:[theString length]]) ) { + width_ = width; + alignment_ = alignment; opacity_ = 255; color_ = ccWHITE; - + contentSize_ = CGSizeZero; opacityModifyRGB_ = [[textureAtlas_ texture] hasPremultipliedAlpha]; - + anchorPoint_ = ccp(0.5f, 0.5f); - [self setString:theString]; + imageOffset_ = offset; + + [self setString:theString updateLabel:YES]; } return self; @@ -476,24 +537,170 @@ -(id) initWithString:(NSString*)theString fntFile:(NSString*)fntFile -(void) dealloc { [string_ release]; + [initialString_ release]; [configuration_ release]; + [fntFile_ release]; + [super dealloc]; } +#pragma mark LabelBMFont - Alignment + +- (void)updateLabel +{ + [self setString:initialString_ updateLabel:NO]; + + if (width_ > 0){ + //Step 1: Make multiline + + NSString *multilineString = @"", *lastWord = @""; + int line = 1, i = 0; + NSUInteger stringLength = [self.string length]; + float startOfLine = -1, startOfWord = -1; + int skip = 0; + //Go through each character and insert line breaks as necessary + for (int j = 0; j < [children_ count]; j++) { + CCSprite *characterSprite; + + while(!(characterSprite = (CCSprite *)[self getChildByTag:j+skip])) + skip++; + + if (!characterSprite.visible) continue; + + if (i >= stringLength || i < 0) + break; + + unichar character = [self.string characterAtIndex:i]; + + if (startOfWord == -1) + startOfWord = characterSprite.position.x - characterSprite.contentSize.width/2; + if (startOfLine == -1) + startOfLine = startOfWord; + + //Character is a line break + //Put lastWord on the current line and start a new line + //Reset lastWord + if ([[NSCharacterSet newlineCharacterSet] characterIsMember:character]) { + lastWord = [[lastWord stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] stringByAppendingFormat:@"%C", character]; + multilineString = [multilineString stringByAppendingString:lastWord]; + lastWord = @""; + startOfWord = -1; + line++; + startOfLine = -1; + i++; + + //CCLabelBMFont do not have a character for new lines, so do NOT "continue;" in the for loop. Process the next character + if (i >= stringLength || i < 0) + break; + character = [self.string characterAtIndex:i]; + + if (startOfWord == -1) + startOfWord = characterSprite.position.x - characterSprite.contentSize.width/2; + if (startOfLine == -1) + startOfLine = startOfWord; + } + + //Character is a whitespace + //Put lastWord on current line and continue on current line + //Reset lastWord + if ([[NSCharacterSet whitespaceCharacterSet] characterIsMember:character]) { + lastWord = [lastWord stringByAppendingFormat:@"%C", character]; + multilineString = [multilineString stringByAppendingString:lastWord]; + lastWord = @""; + startOfWord = -1; + i++; + continue; + } + + //Character is out of bounds + //Do not put lastWord on current line. Add "\n" to current line to start a new line + //Append to lastWord + if (characterSprite.position.x + characterSprite.contentSize.width/2 - startOfLine > width_) { + lastWord = [lastWord stringByAppendingFormat:@"%C", character]; + NSString *trimmedString = [multilineString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + multilineString = [trimmedString stringByAppendingString:@"\n"]; + line++; + startOfLine = -1; + i++; + continue; + } else { + //Character is normal + //Append to lastWord + lastWord = [lastWord stringByAppendingFormat:@"%C", character]; + i++; + continue; + } + } + + multilineString = [multilineString stringByAppendingFormat:@"%@", lastWord]; + + [self setString:multilineString updateLabel:NO]; + } + + //Step 2: Make alignment + + if (self.alignment != kCCTextAlignmentLeft) { + + int i = 0; + //Number of spaces skipped + int lineNumber = 0; + //Go through line by line + for (NSString *lineString in [string_ componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]) { + int lineWidth = 0; + + //Find index of last character in this line + NSInteger index = i + [lineString length] - 1 + lineNumber; + if (index < 0) + continue; + + //Find position of last character on the line + CCSprite *lastChar = (CCSprite *)[self getChildByTag:index]; + + lineWidth = lastChar.position.x + lastChar.contentSize.width/2; + + //Figure out how much to shift each character in this line horizontally + float shift = 0; + switch (self.alignment) { + case kCCTextAlignmentCenter: + shift = self.contentSize.width/2 - lineWidth/2; + break; + case kCCTextAlignmentRight: + shift = self.contentSize.width - lineWidth; + default: + break; + } + + if (shift != 0) { + int j = 0; + //For each character, shift it so that the line is center aligned + for (j = 0; j < [lineString length]; j++) { + index = i + j + lineNumber; + if (index < 0) + continue; + CCSprite *characterSprite = (CCSprite *)[self getChildByTag:index]; + characterSprite.position = ccpAdd(characterSprite.position, ccp(shift, 0)); + } + } + i += [lineString length]; + lineNumber++; + } + } +} + #pragma mark LabelBMFont - Atlas generation -(int) kerningAmountForFirst:(unichar)first second:(unichar)second { int ret = 0; unsigned int key = (first<<16) | (second & 0xffff); - + if( configuration_->kerningDictionary_ ) { tKerningHashElement *element = NULL; - HASH_FIND_INT(configuration_->kerningDictionary_, &key, element); + HASH_FIND_INT(configuration_->kerningDictionary_, &key, element); if(element) ret = element->amount; } - + return ret; } @@ -503,12 +710,12 @@ -(void) createFontChars NSInteger nextFontPositionY = 0; unichar prev = -1; NSInteger kerningAmount = 0; - + CGSize tmpSize = CGSizeZero; NSInteger longestLine = 0; NSUInteger totalHeight = 0; - + NSUInteger quantityOfLines = 1; NSUInteger stringLen = [string_ length]; @@ -522,14 +729,13 @@ -(void) createFontChars if( c=='\n') quantityOfLines++; } - + totalHeight = configuration_->commonHeight_ * quantityOfLines; nextFontPositionY = -(configuration_->commonHeight_ - configuration_->commonHeight_*quantityOfLines); - - for(NSUInteger i=0; icommonHeight_; @@ -537,34 +743,48 @@ -(void) createFontChars } kerningAmount = [self kerningAmountForFirst:prev second:c]; + - ccBMFontDef fontDef = configuration_->BMFontArray_[c]; + tFontDefHashElement *element = NULL; + // unichar is a short, and an int is needed on HASH_FIND_INT + NSUInteger key = (NSUInteger)c; + HASH_FIND_INT(configuration_->fontDefDictionary_ , &key, element); + NSAssert(element, @"FontDefinition could not be found!"); + + ccBMFontDef fontDef = element->fontDef; + CGRect rect = fontDef.rect; + rect = CC_RECT_PIXELS_TO_POINTS(rect); + rect.origin.x += imageOffset_.x; + rect.origin.y += imageOffset_.y; + CCSprite *fontChar; - + fontChar = (CCSprite*) [self getChildByTag:i]; if( ! fontChar ) { - fontChar = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; + fontChar = [[CCSprite alloc] initWithTexture:textureAtlas_.texture rect:rect]; [self addChild:fontChar z:0 tag:i]; [fontChar release]; } else { // reusing fonts - [fontChar setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size]; - + [fontChar setTextureRect:rect rotated:NO untrimmedSize:rect.size]; + // restore to default in case they were modified fontChar.visible = YES; fontChar.opacity = 255; } - - float yOffset = configuration_->commonHeight_ - fontDef.yOffset; - fontChar.positionInPixels = ccp( (float)nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width*0.5f + kerningAmount, - (float)nextFontPositionY + yOffset - rect.size.height*0.5f ); + // See issue 1343. cast( signed short + unsigned integer ) == unsigned integer (sign is lost!) + NSInteger yOffset = configuration_->commonHeight_ - fontDef.yOffset; + CGPoint fontPos = ccp( (CGFloat)nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width*0.5f + kerningAmount, + (CGFloat)nextFontPositionY + yOffset - rect.size.height*0.5f * CC_CONTENT_SCALE_FACTOR() ); + fontChar.position = CC_POINT_PIXELS_TO_POINTS(fontPos); + // update kerning - nextFontPositionX += configuration_->BMFontArray_[c].xAdvance + kerningAmount; + nextFontPositionX += fontDef.xAdvance + kerningAmount; prev = c; // Apply label properties @@ -584,22 +804,10 @@ -(void) createFontChars tmpSize.width = longestLine; tmpSize.height = totalHeight; - [self setContentSizeInPixels:tmpSize]; + [self setContentSize:CC_SIZE_PIXELS_TO_POINTS(tmpSize)]; } #pragma mark LabelBMFont - CCLabelProtocol protocol -- (void) setString:(NSString*) newString -{ - [string_ release]; - string_ = [newString copy]; - - CCNode *child; - CCARRAY_FOREACH(children_, child) - child.visible = NO; - - [self createFontChars]; -} - -(NSString*) string { return string_; @@ -607,7 +815,32 @@ -(NSString*) string -(void) setCString:(char*)label { - [self setString:[NSString stringWithUTF8String:label]]; + [self setString:[NSString stringWithUTF8String:label] ]; +} + +- (void) setString:(NSString*)newString +{ + [self setString:newString updateLabel:YES]; +} + +- (void) setString:(NSString*) newString updateLabel:(BOOL)update +{ + if( !update ) { + [string_ release]; + string_ = [newString copy]; + } else { + [initialString_ release]; + initialString_ = [newString copy]; + } + + CCSprite *child; + CCARRAY_FOREACH(children_, child) + child.visible = NO; + + [self createFontChars]; + + if (update) + [self updateLabel]; } #pragma mark LabelBMFont - CCRGBAProtocol protocol @@ -615,10 +848,10 @@ -(void) setCString:(char*)label -(void) setColor:(ccColor3B)color { color_ = color; - + CCSprite *child; CCARRAY_FOREACH(children_, child) - [child setColor:color_]; + [child setColor:color_]; } -(void) setOpacity:(GLubyte)opacity @@ -627,15 +860,15 @@ -(void) setOpacity:(GLubyte)opacity id child; CCARRAY_FOREACH(children_, child) - [child setOpacity:opacity_]; + [child setOpacity:opacity_]; } -(void) setOpacityModifyRGB:(BOOL)modify { opacityModifyRGB_ = modify; - + id child; CCARRAY_FOREACH(children_, child) - [child setOpacityModifyRGB:modify]; + [child setOpacityModifyRGB:modify]; } -(BOOL) doesOpacityModifyRGB @@ -652,6 +885,42 @@ -(void) setAnchorPoint:(CGPoint)point } } +#pragma mark LabelBMFont - Alignment +- (void)setWidth:(float)width { + width_ = width; + [self updateLabel]; +} + +- (void)setAlignment:(CCTextAlignment)alignment { + alignment_ = alignment; + [self updateLabel]; +} + +#pragma mark LabelBMFont - FntFile +- (void) setFntFile:(NSString*) fntFile +{ + if( fntFile != fntFile_ ) { + + CCBMFontConfiguration *newConf = FNTConfigLoadFile(fntFile); + + NSAssert( newConf, @"CCLabelBMFont: Impossible to create font. Please check file: '%@'", fntFile ); + + [fntFile_ release]; + fntFile_ = [fntFile retain]; + + [configuration_ release]; + configuration_ = [newConf retain]; + + [self setTexture:[[CCTextureCache sharedTextureCache] addImage:configuration_.atlasName]]; + [self createFontChars]; + } +} + +- (NSString*) fntFile +{ + return fntFile_; +} + #pragma mark LabelBMFont - Debug draw #if CC_LABELBMFONT_DEBUG_DRAW -(void) draw diff --git a/cocos2d/cocos2d/CCLabelTTF.h b/cocos2d/cocos2d/CCLabelTTF.h old mode 100644 new mode 100755 index 8e48ce1..46a047b --- a/cocos2d/cocos2d/CCLabelTTF.h +++ b/cocos2d/cocos2d/CCLabelTTF.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -40,38 +40,92 @@ @interface CCLabelTTF : CCSprite { CGSize dimensions_; - CCTextAlignment alignment_; + CCTextAlignment hAlignment_; + CCVerticalTextAlignment vAlignment_; NSString * fontName_; CGFloat fontSize_; CCLineBreakMode lineBreakMode_; NSString *string_; } -/** creates a CCLabel from a fontname, alignment, dimension in points, line break mode, and font size in points. +/** Font name used in the label */ +@property (nonatomic,retain) NSString* fontName; +/** Font size of the label */ +@property (nonatomic,assign) float fontSize; +/** Dimensions of the label in Points */ +@property (nonatomic,assign) CGSize dimensions; +/** The alignment of the label */ +@property (nonatomic,assign) CCTextAlignment horizontalAlignment; +/** The vertical alignment of the label */ +@property (nonatomic,assign) CCVerticalTextAlignment verticalAlignment; + + +/** creates a CCLabelTTF with a font name and font size in points*/ ++ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size; + +/** creates a CCLabelTTF from a fontname, horizontal alignment, dimension in points, and font size in points. Supported lineBreakModes: - iOS: all UILineBreakMode supported modes - Mac: Only NSLineBreakByWordWrapping is supported. @since v1.0 */ -+ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; -/** creates a CCLabel from a fontname, alignment, dimension in points and font size in points*/ -+ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; -/** creates a CCLabel from a fontname and font size in points*/ -+ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size; -/** initializes the CCLabel with a font name, alignment, dimension in points, line brea mode and font size in points. ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; + +/** creates a CCLabelTTF from a fontname, horizontal alignment, dimension in points, line break mode, and font size in points. Supported lineBreakModes: - iOS: all UILineBreakMode supported modes - Mac: Only NSLineBreakByWordWrapping is supported. @since v1.0 */ -- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; -/** initializes the CCLabel with a font name, alignment, dimension in points and font size in points */ -- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; -/** initializes the CCLabel with a font name and font size in points */ ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; + +/** creates a CCLabelTTF from a fontname, horizontal aligment, vertical alignment, dimension in points, line break mode, and font size in points. + Supported lineBreakModes: + - iOS: all UILineBreakMode supported modes + - Mac: Only NSLineBreakByWordWrapping is supported. + @since v1.0 + */ ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment)vertAlignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; + +/** creates a CCLabel from a fontname, alignment, dimension in points and font size in points*/ ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment)vertAlignment fontName:(NSString*)name fontSize:(CGFloat)size; + + +/** initializes the CCLabelTTF with a font name and font size in points */ - (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size; +/** initializes the CCLabelTTF with a font name, horizonal alignment, dimension in points, and font size in points. + Default verticalAlignment: kCCVerticalTextAlignmentTop + Default lineBreakMode: CCLineBreakModeWordWrap + @since v1.0 + */ +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; + +/** initializes the CCLabelTTF with a font name, horizontal alignment, dimension in points, line break mode and font size in points. + Default verticalAlignment: kCCVerticalTextAlignmentTop + + Supported lineBreakModes: + - iOS: all UILineBreakMode supported modes + - Mac: Only NSLineBreakByWordWrapping is supported. + @since v1.0 + */ +- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; + +/** initializes the CCLabelTTF with a font name, horiozntal alignment, vertical alignment, dimension in points and font size in points. + Default lineBreakMode: CCLineBreakModeWordWrap + */ +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment)vertAlignment fontName:(NSString*)name fontSize:(CGFloat)size; + +/** initializes the CCLabelTTF with a font name, horizontal alignment, vertical aligment, dimension in points, line break mode and font size in points. + Supported lineBreakModes: + - iOS: all UILineBreakMode supported modes + - Mac: Only NSLineBreakByWordWrapping is supported. + @since v2.0 + */ +- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment)vAlignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; + /** changes the string to render - * @warning Changing the string is as expensive as creating a new CCLabel. To obtain better performance use CCLabelAtlas + * @warning Changing the string is as expensive as creating a new CCLabelTTF. To obtain better performance use CCLabelAtlas or CCLabelBMFont. */ - (void) setString:(NSString*)str; diff --git a/cocos2d/cocos2d/CCLabelTTF.m b/cocos2d/cocos2d/CCLabelTTF.m old mode 100644 new mode 100755 index 18689fd..6eec6d8 --- a/cocos2d/cocos2d/CCLabelTTF.m +++ b/cocos2d/cocos2d/CCLabelTTF.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,103 +25,220 @@ */ -#import - #import "CCLabelTTF.h" #import "Support/CGPointExtension.h" #import "ccMacros.h" +#import "CCShaderCache.h" +#import "CCGLProgram.h" +#import "Support/CCFileUtils.h" +#import "ccDeprecated.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import "Platforms/iOS/CCDirectorIOS.h" #endif +#if CC_USE_LA88_LABELS +#define SHADER_PROGRAM kCCShader_PositionTextureColor +#else +#define SHADER_PROGRAM kCCShader_PositionTextureA8Color +#endif + +@interface CCLabelTTF () +-(void) updateTexture; +@end + @implementation CCLabelTTF +// - ++ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [[[self alloc] initWithString:string fontName:name fontSize:size]autorelease]; +} + +// hAlignment ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [[[self alloc] initWithString: string dimensions:dimensions hAlignment:alignment vAlignment:kCCVerticalTextAlignmentTop lineBreakMode:kCCLineBreakModeWordWrap fontName:name fontSize:size]autorelease]; +} + +// hAlignment, vAlignment ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment) vertAlignment fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [[[self alloc] initWithString: string dimensions:dimensions hAlignment:alignment vAlignment:vertAlignment fontName:name fontSize:size]autorelease]; +} + +// hAlignment, lineBreakMode ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; +{ + return [[[self alloc] initWithString: string dimensions:dimensions hAlignment:alignment vAlignment:kCCVerticalTextAlignmentTop lineBreakMode:lineBreakMode fontName:name fontSize:size]autorelease]; +} + +// hAlignment, vAlignment, lineBreakMode ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment) vertAlignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; +{ + return [[[self alloc] initWithString: string dimensions:dimensions hAlignment:alignment vAlignment:vertAlignment lineBreakMode:lineBreakMode fontName:name fontSize:size]autorelease]; +} + - (id) init { - NSAssert(NO, @"CCLabelTTF: Init not supported. Use initWithString"); - [self release]; - return nil; + return [self initWithString:@"" fontName:@"Helvetica" fontSize:12]; } -+ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; +- (id) initWithString:(NSString*)str fontName:(NSString*)name fontSize:(CGFloat)size { - return [[[self alloc] initWithString: string dimensions:dimensions alignment:alignment lineBreakMode:lineBreakMode fontName:name fontSize:size]autorelease]; + return [self initWithString:str dimensions:CGSizeZero hAlignment:kCCTextAlignmentLeft vAlignment:kCCVerticalTextAlignmentTop lineBreakMode:kCCLineBreakModeWordWrap fontName:name fontSize:size]; } -+ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size +// hAlignment +- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size { - return [[[self alloc] initWithString: string dimensions:dimensions alignment:alignment fontName:name fontSize:size]autorelease]; + return [self initWithString:str dimensions:dimensions hAlignment:alignment vAlignment:kCCVerticalTextAlignmentTop lineBreakMode:kCCLineBreakModeWordWrap fontName:name fontSize:size]; } -+ (id) labelWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size +// hAlignment, vAlignment +- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment) vertAlignment fontName:(NSString*)name fontSize:(CGFloat)size { - return [[[self alloc] initWithString: string fontName:name fontSize:size]autorelease]; + return [self initWithString:str dimensions:dimensions hAlignment:alignment vAlignment:vertAlignment lineBreakMode:kCCLineBreakModeWordWrap fontName:name fontSize:size]; } +// hAlignment, lineBreakMode +- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [self initWithString:str dimensions:dimensions hAlignment:alignment vAlignment:kCCVerticalTextAlignmentTop lineBreakMode:lineBreakMode fontName:name fontSize:size]; +} -- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size +// hAlignment, vAligment, lineBreakMode +- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment) vertAlignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size { if( (self=[super init]) ) { - dimensions_ = CGSizeMake( dimensions.width * CC_CONTENT_SCALE_FACTOR(), dimensions.height * CC_CONTENT_SCALE_FACTOR() ); - alignment_ = alignment; + // shader program + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:SHADER_PROGRAM]; + + dimensions_ = dimensions; + hAlignment_ = alignment; + vAlignment_ = vertAlignment; fontName_ = [name retain]; - fontSize_ = size * CC_CONTENT_SCALE_FACTOR(); + fontSize_ = size; lineBreakMode_ = lineBreakMode; - + [self setString:str]; } return self; } -- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size +- (void) setString:(NSString*)str { - return [self initWithString:str dimensions:dimensions alignment:alignment lineBreakMode:CCLineBreakModeWordWrap fontName:name fontSize:size]; + NSAssert( str, @"Invalid string" ); + + if( string_.hash != str.hash ) { + [string_ release]; + string_ = [str copy]; + + [self updateTexture]; + } } -- (id) initWithString:(NSString*)str fontName:(NSString*)name fontSize:(CGFloat)size +-(NSString*) string { - if( (self=[super init]) ) { + return string_; +} + +- (void)setFontName:(NSString*)fontName +{ + if( fontName.hash != fontName_.hash ) { + [fontName_ release]; + fontName_ = [fontName copy]; - dimensions_ = CGSizeZero; - fontName_ = [name retain]; - fontSize_ = size * CC_CONTENT_SCALE_FACTOR(); +#ifdef __CC_PLATFORM_MAC + if ([[fontName lowercaseString] hasSuffix:@".ttf"] || YES) + { + // This is a file, register font with font manager + NSString* fontFile = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:fontName]; + NSURL* fontURL = [NSURL fileURLWithPath:fontFile]; + CTFontManagerRegisterFontsForURL((CFURLRef)fontURL, kCTFontManagerScopeProcess, NULL); + NSString *newFontName = [[fontFile lastPathComponent] stringByDeletingPathExtension]; + + fontName_ = [newFontName copy]; + } +#endif + // Force update + if( string_ ) + [self updateTexture]; + } +} + +- (NSString*)fontName +{ + return fontName_; +} + +- (void) setFontSize:(float)fontSize +{ + if( fontSize != fontSize_ ) { + fontSize_ = fontSize; - [self setString:str]; + // Force update + if( string_ ) + [self updateTexture]; } - return self; } -- (void) setString:(NSString*)str +- (float) fontSize { - [string_ release]; - string_ = [str copy]; + return fontSize_; +} - CCTexture2D *tex; - if( CGSizeEqualToSize( dimensions_, CGSizeZero ) ) - tex = [[CCTexture2D alloc] initWithString:str - fontName:fontName_ - fontSize:fontSize_]; - else - tex = [[CCTexture2D alloc] initWithString:str - dimensions:dimensions_ - alignment:alignment_ - lineBreakMode:lineBreakMode_ - fontName:fontName_ - fontSize:fontSize_]; +-(void) setDimensions:(CGSize) dim +{ + if( dim.width != dimensions_.width || dim.height != dimensions_.height) + { + dimensions_ = dim; + + // Force update + if( string_ ) + [self updateTexture]; + } +} - [self setTexture:tex]; - [tex release]; +-(CGSize) dimensions +{ + return dimensions_; +} - CGRect rect = CGRectZero; - rect.size = [texture_ contentSize]; - [self setTextureRect: rect]; +-(void) setHorizontalAlignment:(CCTextAlignment)alignment +{ + if (alignment != hAlignment_) + { + hAlignment_ = alignment; + + // Force update + if( string_ ) + [self updateTexture]; + + } } --(NSString*) string +- (CCTextAlignment) horizontalAlignment { - return string_; + return hAlignment_; +} + +-(void) setVerticalAlignment:(CCVerticalTextAlignment)verticalAlignment +{ + if (vAlignment_ != verticalAlignment) + { + vAlignment_ = verticalAlignment; + + // Force update + if( string_ ) + [self updateTexture]; + } +} + +- (CCVerticalTextAlignment) verticalAlignment +{ + return vAlignment_; } - (void) dealloc @@ -136,6 +253,49 @@ - (NSString*) description { // XXX: string_, fontName_ can't be displayed here, since they might be already released - return [NSString stringWithFormat:@"<%@ = %08X | FontSize = %.1f>", [self class], self, fontSize_]; + return [NSString stringWithFormat:@"<%@ = %p | FontSize = %.1f>", [self class], self, fontSize_]; +} + +// Helper +- (void) updateTexture +{ + CCTexture2D *tex; + if( dimensions_.width == 0 || dimensions_.height == 0 ) + tex = [[CCTexture2D alloc] initWithString:string_ + fontName:fontName_ + fontSize:fontSize_ * CC_CONTENT_SCALE_FACTOR()]; + else + tex = [[CCTexture2D alloc] initWithString:string_ + dimensions:CC_SIZE_POINTS_TO_PIXELS(dimensions_) + hAlignment:hAlignment_ + vAlignment:vAlignment_ + lineBreakMode:lineBreakMode_ + fontName:fontName_ + fontSize:fontSize_ * CC_CONTENT_SCALE_FACTOR()]; + +#ifdef __CC_PLATFORM_IOS + // iPad ? + if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) { + if( CC_CONTENT_SCALE_FACTOR() == 2 ) + [tex setResolutionType:kCCResolutioniPadRetinaDisplay]; + else + [tex setResolutionType:kCCResolutioniPad]; + } + // iPhone ? + else + { + if( CC_CONTENT_SCALE_FACTOR() == 2 ) + [tex setResolutionType:kCCResolutioniPhoneRetinaDisplay]; + else + [tex setResolutionType:kCCResolutioniPhone]; + } +#endif + + [self setTexture:tex]; + [tex release]; + + CGRect rect = CGRectZero; + rect.size = [texture_ contentSize]; + [self setTextureRect: rect]; } @end diff --git a/cocos2d/cocos2d/CCLayer.h b/cocos2d/cocos2d/CCLayer.h old mode 100644 new mode 100755 index b456088..a2b08f5 --- a/cocos2d/cocos2d/CCLayer.h +++ b/cocos2d/cocos2d/CCLayer.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,11 +26,12 @@ -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#import // Needed for UIAccelerometerDelegate +#import "ccMacros.h" + +#ifdef __CC_PLATFORM_IOS +#import // Needed for UIAccelerometerDelegate #import "Platforms/iOS/CCTouchDelegateProtocol.h" // Touches only supported on iOS -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) #import "Platforms/Mac/CCEventDispatcher.h" #endif @@ -40,13 +41,13 @@ #pragma mark - #pragma mark CCLayer -/** CCLayer is a subclass of CCNode that implements the TouchEventsDelegate protocol. - +/** CCLayer is a subclass of CCNode that implements the CCTouchEventsDelegate protocol. + All features from CCNode are valid, plus the following new features: - It can receive iPhone Touches - It can receive Accelerometer input */ -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS @interface CCLayer : CCNode { BOOL isTouchEnabled_; @@ -54,23 +55,23 @@ } /** If isTouchEnabled, this method is called onEnter. Override it to change the way CCLayer receives touch events. - ( Default: [[TouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0] ) + ( Default: [touchDispatcher addStandardDelegate:self priority:0] ) Example: -(void) registerWithTouchDispatcher { - [[TouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES]; + [touchDispatcher addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES]; } - + Valid only on iOS. Not valid on Mac. - + @since v0.8.0 */ -(void) registerWithTouchDispatcher; /** whether or not it will receive Touch events. You can enable / disable touch events with this property. - Only the touches of this node will be affected. This "method" is not propagated to it's children. - + Only the touches of this node will be affected. This "method" is not propagated to its children. + Valid on iOS and Mac OS X v10.6 and later. @since v0.8.1 @@ -78,14 +79,14 @@ @property(nonatomic,assign) BOOL isTouchEnabled; /** whether or not it will receive Accelerometer events You can enable / disable accelerometer events with this property. - + Valid only on iOS. Not valid on Mac. @since v0.8.1 */ @property(nonatomic,assign) BOOL isAccelerometerEnabled; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) @interface CCLayer : CCNode @@ -96,19 +97,19 @@ } /** whether or not it will receive mouse events. - + Valind only Mac. Not valid on iOS */ @property (nonatomic, readwrite) BOOL isMouseEnabled; /** whether or not it will receive keyboard events. - + Valind only Mac. Not valid on iOS */ @property (nonatomic, readwrite) BOOL isKeyboardEnabled; /** whether or not it will receive touch events. - + Valid on iOS and Mac OS X v10.6 and later. */ @property (nonatomic, readwrite) BOOL isTouchEnabled; @@ -116,24 +117,24 @@ /** priority of the mouse event delegate. Default 0. Override this method to set another priority. - - Valind only Mac. Not valid on iOS + + Valind only Mac. Not valid on iOS */ -(NSInteger) mouseDelegatePriority; /** priority of the keyboard event delegate. Default 0. Override this method to set another priority. - - Valind only Mac. Not valid on iOS + + Valind only Mac. Not valid on iOS */ -(NSInteger) keyboardDelegatePriority; /** priority of the touch event delegate. Default 0. Override this method to set another priority. - - Valind only Mac. Not valid on iOS + + Valind only Mac. Not valid on iOS */ -(NSInteger) touchDelegatePriority; @@ -146,7 +147,7 @@ #pragma mark CCLayerColor /** CCLayerColor is a subclass of CCLayer that implements the CCRGBAProtocol protocol. - + All features from CCLayer are valid, plus the following new features: - opacity - RGB colors @@ -154,10 +155,10 @@ @interface CCLayerColor : CCLayer { GLubyte opacity_; - ccColor3B color_; + ccColor3B color_; ccVertex2F squareVertices_[4]; - ccColor4B squareColors_[4]; - + ccColor4F squareColors_[4]; + ccBlendFunc blendFunc_; } @@ -166,7 +167,9 @@ /** creates a CCLayer with color. Width and height are the window size. */ + (id) layerWithColor: (ccColor4B)color; -/** initializes a CCLayer with color, width and height in Points */ +/** initializes a CCLayer with color, width and height in Points. + This is the designated initializer. + */ - (id) initWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat)h; /** initializes a CCLayer with color. Width and height are the window size. */ - (id) initWithColor:(ccColor4B)color; @@ -198,17 +201,17 @@ the background. - direction - final color - interpolation mode - + Color is interpolated between the startColor and endColor along the given vector (starting at the origin, ending at the terminus). If no vector is supplied, it defaults to (0, -1) -- a fade from top to bottom. - + If 'compressedInterpolation' is disabled, you will not see either the start or end color for non-cardinal vectors; a smooth gradient implying both end points will be still be drawn, however. - + If ' compressedInterpolation' is enabled (default mode) you will see both the start and end colors of the gradient. - + @since v0.99.5 */ @interface CCLayerGradient : CCLayerColor @@ -244,13 +247,13 @@ the background. Default: YES */ @property (nonatomic, readwrite) BOOL compressedInterpolation; - + @end #pragma mark - #pragma mark CCLayerMultiplex -/** CCLayerMultiplex is a CCLayer with the ability to multiplex it's children. +/** CCLayerMultiplex is a CCLayer with the ability to multiplex its children. Features: - It supports one or more children - Only one children will be active a time @@ -265,12 +268,12 @@ the background. +(id) layerWithLayers: (CCLayer*) layer, ... NS_REQUIRES_NIL_TERMINATION; /** initializes a MultiplexLayer with one or more layers using a variable argument list. */ -(id) initWithLayers: (CCLayer*) layer vaList:(va_list) params; -/** switches to a certain layer indexed by n. - The current (old) layer will be removed from it's parent with 'cleanup:YES'. +/** switches to a certain layer indexed by n. + The current (old) layer will be removed from its parent with 'cleanup:YES'. */ -(void) switchTo: (unsigned int) n; /** release the current layer and switches to another layer indexed by n. - The current (old) layer will be removed from it's parent with 'cleanup:YES'. + The current (old) layer will be removed from its parent with 'cleanup:YES'. */ -(void) switchToAndReleaseMe: (unsigned int) n; @end diff --git a/cocos2d/cocos2d/CCLayer.m b/cocos2d/cocos2d/CCLayer.m old mode 100644 new mode 100755 index 80eede8..ee30bdb --- a/cocos2d/cocos2d/CCLayer.m +++ b/cocos2d/cocos2d/CCLayer.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -32,15 +32,23 @@ #import "CCLayer.h" #import "CCDirector.h" #import "ccMacros.h" +#import "CCShaderCache.h" +#import "CCGLProgram.h" +#import "ccGLStateCache.h" +#import "Support/TransformUtils.h" #import "Support/CGPointExtension.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import "Platforms/iOS/CCTouchDispatcher.h" #import "Platforms/iOS/CCDirectorIOS.h" -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) #import "Platforms/Mac/CCEventDispatcher.h" +#import "Platforms/Mac/CCDirectorMac.h" #endif +// extern +#import "kazmath/GL/matrix.h" + #pragma mark - #pragma mark Layer @@ -50,31 +58,32 @@ @implementation CCLayer -(id) init { if( (self=[super init]) ) { - + CGSize s = [[CCDirector sharedDirector] winSize]; anchorPoint_ = ccp(0.5f, 0.5f); [self setContentSize:s]; - self.isRelativeAnchorPoint = NO; + self.ignoreAnchorPointForPosition = YES; isTouchEnabled_ = NO; -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS isAccelerometerEnabled_ = NO; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) isMouseEnabled_ = NO; isKeyboardEnabled_ = NO; #endif } - + return self; } #pragma mark Layer - Touch and Accelerometer related -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS -(void) registerWithTouchDispatcher { - [[CCTouchDispatcher sharedDispatcher] addStandardDelegate:self priority:0]; + CCDirector *director = [CCDirector sharedDirector]; + [[director touchDispatcher] addStandardDelegate:self priority:0]; } -(BOOL) isAccelerometerEnabled @@ -107,13 +116,15 @@ -(void) setIsTouchEnabled:(BOOL)enabled if( isRunning_ ) { if( enabled ) [self registerWithTouchDispatcher]; - else - [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; + else { + CCDirector *director = [CCDirector sharedDirector]; + [[director touchDispatcher] removeDelegate:self]; + } } } } -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) #pragma mark CCLayer - Mouse, Keyboard & Touch events @@ -131,12 +142,13 @@ -(void) setIsMouseEnabled:(BOOL)enabled { if( isMouseEnabled_ != enabled ) { isMouseEnabled_ = enabled; - + if( isRunning_ ) { + CCDirector *director = [CCDirector sharedDirector]; if( enabled ) - [[CCEventDispatcher sharedDispatcher] addMouseDelegate:self priority:[self mouseDelegatePriority]]; + [[director eventDispatcher] addMouseDelegate:self priority:[self mouseDelegatePriority]]; else - [[CCEventDispatcher sharedDispatcher] removeMouseDelegate:self]; + [[director eventDispatcher] removeMouseDelegate:self]; } } } @@ -155,12 +167,13 @@ -(void) setIsKeyboardEnabled:(BOOL)enabled { if( isKeyboardEnabled_ != enabled ) { isKeyboardEnabled_ = enabled; - + if( isRunning_ ) { + CCDirector *director = [CCDirector sharedDirector]; if( enabled ) - [[CCEventDispatcher sharedDispatcher] addKeyboardDelegate:self priority:[self keyboardDelegatePriority] ]; + [[director eventDispatcher] addKeyboardDelegate:self priority:[self keyboardDelegatePriority] ]; else - [[CCEventDispatcher sharedDispatcher] removeKeyboardDelegate:self]; + [[director eventDispatcher] removeKeyboardDelegate:self]; } } } @@ -180,10 +193,11 @@ -(void) setIsTouchEnabled:(BOOL)enabled if( isTouchEnabled_ != enabled ) { isTouchEnabled_ = enabled; if( isRunning_ ) { + CCDirector *director = [CCDirector sharedDirector]; if( enabled ) - [[CCEventDispatcher sharedDispatcher] addTouchDelegate:self priority:[self touchDelegatePriority]]; + [[director eventDispatcher] addTouchDelegate:self priority:[self touchDelegatePriority]]; else - [[CCEventDispatcher sharedDispatcher] removeTouchDelegate:self]; + [[director eventDispatcher] removeTouchDelegate:self]; } } } @@ -195,24 +209,27 @@ -(void) setIsTouchEnabled:(BOOL)enabled #pragma mark Layer - Callbacks -(void) onEnter { -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS // register 'parent' nodes first // since events are propagated in reverse order if (isTouchEnabled_) [self registerWithTouchDispatcher]; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) + CCDirector *director = [CCDirector sharedDirector]; + CCEventDispatcher *eventDispatcher = [director eventDispatcher]; + if( isMouseEnabled_ ) - [[CCEventDispatcher sharedDispatcher] addMouseDelegate:self priority:[self mouseDelegatePriority]]; - + [eventDispatcher addMouseDelegate:self priority:[self mouseDelegatePriority]]; + if( isKeyboardEnabled_) - [[CCEventDispatcher sharedDispatcher] addKeyboardDelegate:self priority:[self keyboardDelegatePriority]]; + [eventDispatcher addKeyboardDelegate:self priority:[self keyboardDelegatePriority]]; if( isTouchEnabled_) - [[CCEventDispatcher sharedDispatcher] addTouchDelegate:self priority:[self touchDelegatePriority]]; + [eventDispatcher addTouchDelegate:self priority:[self touchDelegatePriority]]; #endif - + // then iterate over all the children [super onEnter]; } @@ -221,40 +238,43 @@ -(void) onEnter // Can't register mouse, touches here because of #issue #1018, and #1021 -(void) onEnterTransitionDidFinish { -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS if( isAccelerometerEnabled_ ) [[UIAccelerometer sharedAccelerometer] setDelegate:self]; #endif - + [super onEnterTransitionDidFinish]; } -(void) onExit { -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + CCDirector *director = [CCDirector sharedDirector]; + +#ifdef __CC_PLATFORM_IOS if( isTouchEnabled_ ) - [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; - + [[director touchDispatcher] removeDelegate:self]; + if( isAccelerometerEnabled_ ) [[UIAccelerometer sharedAccelerometer] setDelegate:nil]; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) + CCEventDispatcher *eventDispatcher = [director eventDispatcher]; if( isMouseEnabled_ ) - [[CCEventDispatcher sharedDispatcher] removeMouseDelegate:self]; - + [eventDispatcher removeMouseDelegate:self]; + if( isKeyboardEnabled_ ) - [[CCEventDispatcher sharedDispatcher] removeKeyboardDelegate:self]; + [eventDispatcher removeKeyboardDelegate:self]; if( isTouchEnabled_ ) - [[CCEventDispatcher sharedDispatcher] removeTouchDelegate:self]; + [eventDispatcher removeTouchDelegate:self]; #endif - + [super onExit]; } -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { NSAssert(NO, @"Layer#ccTouchBegan override me"); @@ -287,25 +307,34 @@ + (id) layerWithColor:(ccColor4B)color return [[(CCLayerColor*)[self alloc] initWithColor:color] autorelease]; } +-(id) init +{ + CGSize s = [[CCDirector sharedDirector] winSize]; + return [self initWithColor:ccc4(0,0,0,0) width:s.width height:s.height]; +} + +// Designated initializer - (id) initWithColor:(ccColor4B)color width:(GLfloat)w height:(GLfloat) h { if( (self=[super init]) ) { - + // default blend function - blendFunc_ = (ccBlendFunc) { CC_BLEND_SRC, CC_BLEND_DST }; + blendFunc_ = (ccBlendFunc) { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA }; color_.r = color.r; color_.g = color.g; color_.b = color.b; opacity_ = color.a; - + for (NSUInteger i = 0; i -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import "Platforms/iOS/CCDirectorIOS.h" #import "Platforms/iOS/CCTouchDispatcher.h" -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) -#import "Platforms/Mac/MacGLView.h" +#elif defined(__CC_PLATFORM_MAC) +#import "Platforms/Mac/CCGLView.h" #import "Platforms/Mac/CCDirectorMac.h" #endif @@ -44,69 +43,81 @@ kDefaultPadding = 5, }; +#pragma mark - CCMenu + @implementation CCMenu -@synthesize opacity = opacity_, color = color_; +@synthesize opacity = opacity_, color = color_, enabled=enabled_; -- (id) init ++(id) menuWithArray:(NSArray *)arrayOfItems { - NSAssert(NO, @"CCMenu: Init not supported."); - [self release]; - return nil; + return [[[self alloc] initWithArray:arrayOfItems] autorelease]; } +(id) menuWithItems: (CCMenuItem*) item, ... { va_list args; va_start(args,item); - + id s = [[[self alloc] initWithItems: item vaList:args] autorelease]; - + va_end(args); return s; } +-(id) init +{ + return [self initWithArray:nil]; +} + -(id) initWithItems: (CCMenuItem*) item vaList: (va_list) args { - if( (self=[super init]) ) { + NSMutableArray *array = nil; + if( item ) { + array = [NSMutableArray arrayWithObject:item]; + CCMenuItem *i = va_arg(args, CCMenuItem*); + while(i) { + [array addObject:i]; + i = va_arg(args, CCMenuItem*); + } + } -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + return [self initWithArray:array]; +} + +-(id) initWithArray:(NSArray *)arrayOfItems +{ + if( (self=[super init]) ) { +#ifdef __CC_PLATFORM_IOS self.isTouchEnabled = YES; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) self.isMouseEnabled = YES; #endif + enabled_ = YES; - // menu in the center of the screen + // by default, menu in the center of the screen CGSize s = [[CCDirector sharedDirector] winSize]; - self.isRelativeAnchorPoint = NO; + self.ignoreAnchorPointForPosition = YES; anchorPoint_ = ccp(0.5f, 0.5f); [self setContentSize:s]; // XXX: in v0.7, winSize should return the visible size // XXX: so the bar calculation should be done there -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS CGRect r = [[UIApplication sharedApplication] statusBarFrame]; - ccDeviceOrientation orientation = [[CCDirector sharedDirector] deviceOrientation]; - if( orientation == CCDeviceOrientationLandscapeLeft || orientation == CCDeviceOrientationLandscapeRight ) - s.height -= r.size.width; - else - s.height -= r.size.height; + s.height -= r.size.height; #endif self.position = ccp(s.width/2, s.height/2); - + int z=0; - if (item) { + for( CCMenuItem *item in arrayOfItems) { [self addChild: item z:z]; - CCMenuItem *i = va_arg(args, CCMenuItem*); - while(i) { - z++; - [self addChild: i z:z]; - i = va_arg(args, CCMenuItem*); - } + z++; } - // [self alignItemsVertically]; + +// [self alignItemsVertically]; selectedItem_ = nil; state_ = kCCMenuStateWaiting; @@ -133,35 +144,51 @@ - (void) onExit { if(state_ == kCCMenuStateTrackingTouch) { - [selectedItem_ unselected]; + [selectedItem_ unselected]; state_ = kCCMenuStateWaiting; selectedItem_ = nil; } [super onExit]; } - -#pragma mark Menu - Touches -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#pragma mark Menu - Events + +-(void) setHandlerPriority:(NSInteger)newPriority +{ +#ifdef __CC_PLATFORM_IOS + CCTouchDispatcher *dispatcher = [[CCDirector sharedDirector] touchDispatcher]; + [dispatcher setPriority:newPriority forDelegate:self]; + +#elif defined(__CC_PLATFORM_MAC) + CCEventDispatcher *dispatcher = [[CCDirector sharedDirector] eventDispatcher]; + [dispatcher removeMouseDelegate:self]; + [dispatcher addMouseDelegate:self priority:newPriority]; +#endif +} + +#pragma mark Menu - Events Touches + +#ifdef __CC_PLATFORM_IOS -(void) registerWithTouchDispatcher { - [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:kCCMenuTouchPriority swallowsTouches:YES]; + CCDirector *director = [CCDirector sharedDirector]; + [[director touchDispatcher] addTargetedDelegate:self priority:kCCMenuHandlerPriority swallowsTouches:YES]; } -(CCMenuItem *) itemForTouch: (UITouch *) touch { CGPoint touchLocation = [touch locationInView: [touch view]]; touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation]; - + CCMenuItem* item; CCARRAY_FOREACH(children_, item){ // ignore invisible and disabled items: issue #779, #866 if ( [item visible] && [item isEnabled] ) { - + CGPoint local = [item convertToNodeSpace:touchLocation]; CGRect r = [item rect]; r.origin = CGPointZero; - + if( CGRectContainsPoint( r, local ) ) return item; } @@ -171,16 +198,16 @@ -(CCMenuItem *) itemForTouch: (UITouch *) touch -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event { - if( state_ != kCCMenuStateWaiting || !visible_ ) + if( state_ != kCCMenuStateWaiting || !visible_ || ! enabled_) return NO; - + for( CCNode *c = self.parent; c != nil; c = c.parent ) if( c.visible == NO ) return NO; selectedItem_ = [self itemForTouch:touch]; [selectedItem_ selected]; - + if( selectedItem_ ) { state_ = kCCMenuStateTrackingTouch; return YES; @@ -191,28 +218,28 @@ -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event -(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event { NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchEnded] -- invalid state"); - + [selectedItem_ unselected]; [selectedItem_ activate]; - + state_ = kCCMenuStateWaiting; } -(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event { NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchCancelled] -- invalid state"); - + [selectedItem_ unselected]; - + state_ = kCCMenuStateWaiting; } -(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event { NSAssert(state_ == kCCMenuStateTrackingTouch, @"[Menu ccTouchMoved] -- invalid state"); - + CCMenuItem *currentItem = [self itemForTouch:touch]; - + if (currentItem != selectedItem_) { [selectedItem_ unselected]; selectedItem_ = currentItem; @@ -220,29 +247,29 @@ -(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event } } -#pragma mark Menu - Mouse +#pragma mark Menu - Events Mouse -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) -(NSInteger) mouseDelegatePriority { - return kCCMenuMousePriority+1; + return kCCMenuHandlerPriority+1; } -(CCMenuItem *) itemForMouseEvent: (NSEvent *) event { - CGPoint location = [(CCDirectorMac*)[CCDirector sharedDirector] convertEventToGL:event]; - + CGPoint location = [[CCDirector sharedDirector] convertEventToGL:event]; + CCMenuItem* item; CCARRAY_FOREACH(children_, item){ // ignore invisible and disabled items: issue #779, #866 if ( [item visible] && [item isEnabled] ) { - + CGPoint local = [item convertToNodeSpace:location]; - + CGRect r = [item rect]; r.origin = CGPointZero; - + if( CGRectContainsPoint( r, local ) ) return item; } @@ -252,7 +279,7 @@ -(CCMenuItem *) itemForMouseEvent: (NSEvent *) event -(BOOL) ccMouseUp:(NSEvent *)event { - if( ! visible_ ) + if( ! visible_ || ! enabled_) return NO; if(state_ == kCCMenuStateTrackingTouch) { @@ -261,7 +288,7 @@ -(BOOL) ccMouseUp:(NSEvent *)event [selectedItem_ activate]; } state_ = kCCMenuStateWaiting; - + return YES; } return NO; @@ -269,9 +296,9 @@ -(BOOL) ccMouseUp:(NSEvent *)event -(BOOL) ccMouseDown:(NSEvent *)event { - if( ! visible_ ) + if( ! visible_ || ! enabled_) return NO; - + selectedItem_ = [self itemForMouseEvent:event]; [selectedItem_ selected]; @@ -280,23 +307,23 @@ -(BOOL) ccMouseDown:(NSEvent *)event return YES; } - return NO; + return NO; } -(BOOL) ccMouseDragged:(NSEvent *)event { - if( ! visible_ ) + if( ! visible_ || ! enabled_) return NO; if(state_ == kCCMenuStateTrackingTouch) { CCMenuItem *currentItem = [self itemForMouseEvent:event]; - + if (currentItem != selectedItem_) { [selectedItem_ unselected]; selectedItem_ = currentItem; [selectedItem_ selected]; } - + return YES; } return NO; @@ -312,13 +339,13 @@ -(void) alignItemsVertically -(void) alignItemsVerticallyWithPadding:(float)padding { float height = -padding; - + CCMenuItem *item; CCARRAY_FOREACH(children_, item) height += item.contentSize.height * item.scaleY + padding; float y = height / 2.0f; - + CCARRAY_FOREACH(children_, item) { CGSize itemSize = item.contentSize; [item setPosition:ccp(0, y - itemSize.height * item.scaleY / 2.0f)]; @@ -333,14 +360,14 @@ -(void) alignItemsHorizontally -(void) alignItemsHorizontallyWithPadding:(float)padding { - + float width = -padding; CCMenuItem *item; CCARRAY_FOREACH(children_, item) width += item.contentSize.width * item.scaleX + padding; float x = -width / 2.0f; - + CCARRAY_FOREACH(children_, item){ CGSize itemSize = item.contentSize; [item setPosition:ccp(x + itemSize.width * item.scaleX / 2.0f, 0)]; @@ -352,9 +379,9 @@ -(void) alignItemsInColumns: (NSNumber *) columns, ... { va_list args; va_start(args, columns); - + [self alignItemsInColumns:columns vaList:args]; - + va_end(args); } @@ -366,19 +393,19 @@ -(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args [rows addObject:columns]; columns = va_arg(args, NSNumber*); } - + int height = -5; NSUInteger row = 0, rowHeight = 0, columnsOccupied = 0, rowColumns; CCMenuItem *item; CCARRAY_FOREACH(children_, item){ NSAssert( row < [rows count], @"Too many menu items for the amount of rows/columns."); - + rowColumns = [(NSNumber *) [rows objectAtIndex:row] unsignedIntegerValue]; NSAssert( rowColumns, @"Can't have zero columns on a row"); - + rowHeight = fmaxf(rowHeight, item.contentSize.height); ++columnsOccupied; - + if(columnsOccupied >= rowColumns) { height += rowHeight + 5; @@ -390,7 +417,7 @@ -(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args NSAssert( !columnsOccupied, @"Too many rows/columns for available menu items." ); CGSize winSize = [[CCDirector sharedDirector] winSize]; - + row = 0; rowHeight = 0; rowColumns = 0; float w, x, y = height / 2; CCARRAY_FOREACH(children_, item) { @@ -404,13 +431,13 @@ -(void) alignItemsInColumns: (NSNumber *) columns vaList: (va_list) args rowHeight = fmaxf(rowHeight, itemSize.height); [item setPosition:ccp(x - winSize.width / 2, y - itemSize.height / 2)]; - + x += w; ++columnsOccupied; - + if(columnsOccupied >= rowColumns) { y -= rowHeight + 5; - + columnsOccupied = 0; rowColumns = 0; rowHeight = 0; @@ -425,9 +452,9 @@ -(void) alignItemsInRows: (NSNumber *) rows, ... { va_list args; va_start(args, rows); - + [self alignItemsInRows:rows vaList:args]; - + va_end(args); } @@ -442,26 +469,26 @@ -(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args NSMutableArray *columnWidths = [[NSMutableArray alloc] init]; NSMutableArray *columnHeights = [[NSMutableArray alloc] init]; - + int width = -10, columnHeight = -5; NSUInteger column = 0, columnWidth = 0, rowsOccupied = 0, columnRows; CCMenuItem *item; CCARRAY_FOREACH(children_, item){ NSAssert( column < [columns count], @"Too many menu items for the amount of rows/columns."); - + columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue]; NSAssert( columnRows, @"Can't have zero rows on a column"); - + CGSize itemSize = item.contentSize; columnWidth = fmaxf(columnWidth, itemSize.width); columnHeight += itemSize.height + 5; ++rowsOccupied; - + if(rowsOccupied >= columnRows) { [columnWidths addObject:[NSNumber numberWithUnsignedInteger:columnWidth]]; [columnHeights addObject:[NSNumber numberWithUnsignedInteger:columnHeight]]; width += columnWidth + 10; - + rowsOccupied = 0; columnWidth = 0; columnHeight = -5; @@ -469,36 +496,36 @@ -(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args } } NSAssert( !rowsOccupied, @"Too many rows/columns for available menu items."); - + CGSize winSize = [[CCDirector sharedDirector] winSize]; - + column = 0; columnWidth = 0; columnRows = 0; float x = -width / 2, y; - + CCARRAY_FOREACH(children_, item){ if(columnRows == 0) { columnRows = [(NSNumber *) [columns objectAtIndex:column] unsignedIntegerValue]; y = ([(NSNumber *) [columnHeights objectAtIndex:column] intValue] + winSize.height) / 2; } - + CGSize itemSize = item.contentSize; columnWidth = fmaxf(columnWidth, itemSize.width); [item setPosition:ccp(x + [(NSNumber *) [columnWidths objectAtIndex:column] unsignedIntegerValue] / 2, y - winSize.height / 2)]; - + y -= itemSize.height + 10; ++rowsOccupied; - + if(rowsOccupied >= columnRows) { x += columnWidth + 5; - + rowsOccupied = 0; columnRows = 0; columnWidth = 0; ++column; } } - + [columns release]; [columnWidths release]; [columnHeights release]; @@ -510,7 +537,7 @@ -(void) alignItemsInRows: (NSNumber *) rows vaList: (va_list) args - (void) setOpacity:(GLubyte)newOpacity { opacity_ = newOpacity; - + id item; CCARRAY_FOREACH(children_, item) [item setOpacity:opacity_]; @@ -519,7 +546,7 @@ - (void) setOpacity:(GLubyte)newOpacity -(void) setColor:(ccColor3B)color { color_ = color; - + id item; CCARRAY_FOREACH(children_, item) [item setColor:color_]; diff --git a/cocos2d/cocos2d/CCMenuItem.h b/cocos2d/cocos2d/CCMenuItem.h old mode 100644 new mode 100755 index 2437394..6336534 --- a/cocos2d/cocos2d/CCMenuItem.h +++ b/cocos2d/cocos2d/CCMenuItem.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2011 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,12 +24,11 @@ * */ -#import "CCBlockSupport.h" - #import "CCNode.h" #import "CCProtocols.h" @class CCSprite; +@class CCSpriteFrame; #define kCCItemSize 32 @@ -41,12 +40,9 @@ */ @interface CCMenuItem : CCNode { - NSInvocation *invocation_; -#if NS_BLOCKS_AVAILABLE // used for menu items using a block void (^block_)(id sender); -#endif - + BOOL isEnabled_; BOOL isSelected_; } @@ -56,23 +52,24 @@ */ @property (nonatomic,readonly) BOOL isSelected; -/** Creates a CCMenuItem with a target/selector */ +/** Creates a CCMenuItem with a target/selector. + target/selector will be implemented using blocks. + "target" won't be retained. + */ +(id) itemWithTarget:(id)target selector:(SEL)selector; -/** Initializes a CCMenuItem with a target/selector */ --(id) initWithTarget:(id)target selector:(SEL)selector; - -#if NS_BLOCKS_AVAILABLE /** Creates a CCMenuItem with the specified block. The block will be "copied". */ +(id) itemWithBlock:(void(^)(id sender))block; +/** Initializes a CCMenuItem with a target/selector */ +-(id) initWithTarget:(id)target selector:(SEL)selector; + /** Initializes a CCMenuItem with the specified block. The block will be "copied". */ -(id) initWithBlock:(void(^)(id sender))block; -#endif /** Returns the outside box in points */ -(CGRect) rect; @@ -88,14 +85,30 @@ /** Enable or disabled the CCMenuItem */ -(void) setIsEnabled:(BOOL)enabled; + /** Returns whether or not the CCMenuItem is enabled */ -(BOOL) isEnabled; + +/** Sets the block that is called when the item is tapped. + The block will be "copied". + */ +-(void) setBlock:(void(^)(id sender))block; + +/** Sets the target and selector that is called when the item is tapped. + target/selector will be implemented using blocks. + "target" won't be retained. + */ +-(void) setTarget:(id)target selector:(SEL)selector; + +/** cleanup event. It will release the block and call [super cleanup] */ +-(void) cleanup; + @end #pragma mark - #pragma mark CCMenuItemLabel -/** An abstract class for "label" CCMenuItemLabel items +/** An abstract class for "label" CCMenuItemLabel items Any CCNode that supports the CCLabelProtocol protocol can be added. Supported nodes: - CCLabelBMFont @@ -116,26 +129,30 @@ /** Label that is rendered. It can be any CCNode that implements the CCLabelProtocol */ @property (nonatomic,readwrite,assign) CCNode* label; -/** creates a CCMenuItemLabel with a Label. Target and selector will be nill */ +/** creates a CCMenuItemLabel with a Label. Block will benil */ +(id) itemWithLabel:(CCNode*)label; -/** creates a CCMenuItemLabel with a Label, target and selector */ +/** creates a CCMenuItemLabel with a Label, target and selector. + The "target" won't be retained. + */ +(id) itemWithLabel:(CCNode*)label target:(id)target selector:(SEL)selector; -/** initializes a CCMenuItemLabel with a Label, target and selector */ --(id) initWithLabel:(CCNode*)label target:(id)target selector:(SEL)selector; - -#if NS_BLOCKS_AVAILABLE /** creates a CCMenuItemLabel with a Label and a block to execute. The block will be "copied". */ +(id) itemWithLabel:(CCNode*)label block:(void(^)(id sender))block; +/** initializes a CCMenuItemLabel with a Label, target and selector. + Internally it will create a block that executes the target/selector. + The "target" won't be retained. + */ +-(id) initWithLabel:(CCNode*)label target:(id)target selector:(SEL)selector; + /** initializes a CCMenuItemLabel with a Label and a block to execute. The block will be "copied". + This is the designated initializer. */ -(id) initWithLabel:(CCNode*)label block:(void(^)(id sender))block; -#endif /** sets a new string to the inner label */ -(void) setString:(NSString*)label; @@ -150,32 +167,34 @@ #pragma mark CCMenuItemAtlasFont /** A CCMenuItemAtlasFont - Helper class that creates a MenuItemLabel class with a LabelAtlas + Helper class that creates a CCMenuItemLabel class with a CCLabelAtlas */ @interface CCMenuItemAtlasFont : CCMenuItemLabel { } /** creates a menu item from a string and atlas with a target/selector */ -+(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap; ++(id) itemWithString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap; -/** creates a menu item from a string and atlas. Use it with MenuItemToggle */ -+(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb; +/** creates a menu item from a string and atlas. Use it with CCMenuItemToggle. + The "target" won't be retained. + */ ++(id) itemWithString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id)target selector:(SEL)selector; -/** initializes a menu item from a string and atlas with a target/selector */ --(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb; +/** initializes a menu item from a string and atlas with a target/selector. + The "target" won't be retained. + */ +-(id) initWithString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id)target selector:(SEL)selector; -#if NS_BLOCKS_AVAILABLE -/** creates a menu item from a string and atlas. Use it with MenuItemToggle. +/** creates a menu item from a string and atlas. Use it with CCMenuItemToggle. The block will be "copied". */ -+(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block; ++(id) itemWithString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block; /** initializes a menu item from a string and atlas with a block. The block will be "copied". */ --(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block; -#endif +-(id) initWithString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block; @end @@ -203,13 +222,22 @@ +(NSString*) fontName; /** creates a menu item from a string without target/selector. To be used with CCMenuItemToggle */ -+(id) itemFromString: (NSString*) value; ++(id) itemWithString: (NSString*) value; -/** creates a menu item from a string with a target/selector */ -+(id) itemFromString: (NSString*) value target:(id) r selector:(SEL) s; +/** creates a menu item from a string with a target/selector. + The "target" won't be retained. + */ ++(id) itemWithString: (NSString*) value target:(id) r selector:(SEL) s; -/** initializes a menu item from a string with a target/selector */ --(id) initFromString: (NSString*) value target:(id) r selector:(SEL) s; +/** creates a menu item from a string with the specified block. + The block will be "copied". + */ ++(id) itemWithString: (NSString*) value block:(void(^)(id sender))block; + +/** initializes a menu item from a string with a target/selector + The "target" won't be retained. + */ +-(id) initWithString: (NSString*) value target:(id) r selector:(SEL) s; /** set font size */ -(void) setFontSize: (NSUInteger) s; @@ -223,17 +251,11 @@ /** get the font name */ -(NSString*) fontName; -#if NS_BLOCKS_AVAILABLE -/** creates a menu item from a string with the specified block. - The block will be "copied". - */ -+(id) itemFromString: (NSString*) value block:(void(^)(id sender))block; - /** initializes a menu item from a string with the specified block. The block will be "copied". */ --(id) initFromString: (NSString*) value block:(void(^)(id sender))block; -#endif +-(id) initWithString: (NSString*) value block:(void(^)(id sender))block; + @end #pragma mark - @@ -244,7 +266,7 @@ - unselected image - selected image - disabled image - + @since v0.8.0 */ @interface CCMenuItemSprite : CCMenuItem @@ -262,28 +284,36 @@ @property (nonatomic,readwrite,assign) CCNode *disabledImage; /** creates a menu item with a normal and selected image*/ -+(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite; -/** creates a menu item with a normal and selected image with target/selector */ -+(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite target:(id)target selector:(SEL)selector; -/** creates a menu item with a normal,selected and disabled image with target/selector */ -+(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector; -/** initializes a menu item with a normal, selected and disabled image with target/selector */ --(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector; ++(id) itemWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite; +/** creates a menu item with a normal and selected image with target/selector. + The "target" won't be retained. + */ ++(id) itemWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite target:(id)target selector:(SEL)selector; + +/** creates a menu item with a normal,selected and disabled image with target/selector. + The "target" won't be retained. + */ ++(id) itemWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector; -#if NS_BLOCKS_AVAILABLE /** creates a menu item with a normal and selected image with a block. The block will be "copied". */ -+(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite block:(void(^)(id sender))block; ++(id) itemWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite block:(void(^)(id sender))block; + /** creates a menu item with a normal,selected and disabled image with a block. The block will be "copied". */ -+(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block; ++(id) itemWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block; + +/** initializes a menu item with a normal, selected and disabled image with target/selector. + The "target" won't be retained. + */ +-(id) initWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector; + /** initializes a menu item with a normal, selected and disabled image with a block. The block will be "copied". */ --(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block; -#endif +-(id) initWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block; @end @@ -295,7 +325,7 @@ - unselected image - selected image - disabled image - + For best results try that all images are of the same size */ @interface CCMenuItemImage : CCMenuItemSprite @@ -303,34 +333,52 @@ } /** creates a menu item with a normal and selected image*/ -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2; ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2; + /** creates a menu item with a normal and selected image with target/selector */ -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) r selector:(SEL) s; -/** creates a menu item with a normal,selected and disabled image with target/selector */ -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s; -/** initializes a menu item with a normal, selected and disabled image with target/selector */ --(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s; -#if NS_BLOCKS_AVAILABLE ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) r selector:(SEL) s; + +/** creates a menu item with a normal,selected and disabled image with target/selector. + The "target" won't be retained. + */ ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s; + /** creates a menu item with a normal and selected image with a block. The block will be "copied". */ -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block; ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block; + /** creates a menu item with a normal,selected and disabled image with a block. The block will be "copied". */ -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block; ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block; + +/** initializes a menu item with a normal, selected and disabled image with target/selector. + The "target" won't be retained. + */ +-(id) initWithNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s; + /** initializes a menu item with a normal, selected and disabled image with a block. The block will be "copied". */ --(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block; -#endif +-(id) initWithNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block; + +/** sets the sprite frame for the normal image */ +- (void) setNormalSpriteFrame:(CCSpriteFrame*)frame; + +/** sets the sprite frame for the selected image */ +- (void) setSelectedSpriteFrame:(CCSpriteFrame*)frame; + +/** sets the sprite frame for the disabled image */ +- (void) setDisabledSpriteFrame:(CCSpriteFrame*)frame; + @end #pragma mark - #pragma mark CCMenuItemToggle /** A CCMenuItemToggle - A simple container class that "toggles" it's inner items + A simple container class that "toggles" its inner items The inner itmes can be any MenuItem */ @interface CCMenuItemToggle : CCMenuItem @@ -354,22 +402,20 @@ @property (nonatomic,readwrite,retain) NSMutableArray *subItems; /** creates a menu item from a list of items with a target/selector */ -+(id) itemWithTarget:(id)t selector:(SEL)s items:(CCMenuItem*) item, ... NS_REQUIRES_NIL_TERMINATION; ++(id) itemWithTarget:(id)target selector:(SEL)selector items:(CCMenuItem*) item, ... NS_REQUIRES_NIL_TERMINATION; -/** initializes a menu item from a list of items with a target selector */ --(id) initWithTarget:(id)t selector:(SEL)s items:(CCMenuItem*) item vaList:(va_list) args; - -#if NS_BLOCKS_AVAILABLE /** creates a menu item from a list of items and executes the given block when the item is selected. The block will be "copied". */ -+(id) itemWithBlock:(void(^)(id sender))block items:(CCMenuItem*)item, ... NS_REQUIRES_NIL_TERMINATION; ++(id) itemWithItems:(NSArray*)arrayOfItems block:(void(^)(id sender))block; + +/** initializes a menu item from a list of items with a target selector */ +-(id) initWithTarget:(id)target selector:(SEL)selector items:(CCMenuItem*) item vaList:(va_list) args; /** initializes a menu item from a list of items with a block. The block will be "copied". */ --(id) initWithBlock:(void (^)(id))block items:(CCMenuItem*)item vaList:(va_list)args; -#endif +-(id) initWithItems:(NSArray*)arrayOfItems block:(void (^)(id))block; /** return the selected item */ -(CCMenuItem*) selectedItem; diff --git a/cocos2d/cocos2d/CCMenuItem.m b/cocos2d/cocos2d/CCMenuItem.m old mode 100644 new mode 100755 index f88c0e0..4a38b59 --- a/cocos2d/cocos2d/CCMenuItem.m +++ b/cocos2d/cocos2d/CCMenuItem.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2011 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,15 +30,14 @@ #import "CCActionInterval.h" #import "CCSprite.h" #import "Support/CGPointExtension.h" -#import "CCBlockSupport.h" - static NSUInteger _fontSize = kCCItemSize; +static NSUInteger _fontSize = kCCItemSize; static NSString *_fontName = @"Marker Felt"; static BOOL _fontNameRelease = NO; -const uint32_t kCurrentItem = 0xc0c05001; -const uint32_t kZoomActionTag = 0xc0c05002; +const NSInteger kCCCurrentItemTag = 0xc0c05001; +const NSInteger kCCZoomActionTag = 0xc0c05002; #pragma mark - @@ -47,69 +46,61 @@ @implementation CCMenuItem @synthesize isSelected=isSelected_; ++(id) itemWithTarget:(id) r selector:(SEL) s +{ + return [[[self alloc] initWithTarget:r selector:s] autorelease]; +} + ++(id) itemWithBlock:(void(^)(id sender))block { + return [[[self alloc] initWithBlock:block] autorelease]; +} + -(id) init { - NSAssert(NO, @"MenuItemInit: Init not supported."); - [self release]; - return nil; + return [self initWithBlock:nil]; } -+(id) itemWithTarget:(id) r selector:(SEL) s +-(id) initWithTarget:(id)target selector:(SEL)selector { - return [[[self alloc] initWithTarget:r selector:s] autorelease]; + // avoid retain cycle + __block id t = target; + return [self initWithBlock:^(id sender) { + + [t performSelector:selector withObject:sender]; + }]; + } --(id) initWithTarget:(id) rec selector:(SEL) cb + +// Designated initializer +-(id) initWithBlock:(void (^)(id))block { if((self=[super init]) ) { - + + if( block ) + block_ = [block copy]; + anchorPoint_ = ccp(0.5f, 0.5f); - NSMethodSignature * sig = nil; - - if( rec && cb ) { - sig = [rec methodSignatureForSelector:cb]; - - invocation_ = nil; - invocation_ = [NSInvocation invocationWithMethodSignature:sig]; - [invocation_ setTarget:rec]; - [invocation_ setSelector:cb]; -#if NS_BLOCKS_AVAILABLE - if ([sig numberOfArguments] == 3) -#endif - [invocation_ setArgument:&self atIndex:2]; - - [invocation_ retain]; - } - isEnabled_ = YES; isSelected_ = NO; + } - return self; } -#if NS_BLOCKS_AVAILABLE - -+(id) itemWithBlock:(void(^)(id sender))block { - return [[[self alloc] initWithBlock:block] autorelease]; -} +-(void) dealloc +{ + [block_ release]; --(id) initWithBlock:(void(^)(id sender))block { - block_ = [block copy]; - return [self initWithTarget:block_ selector:@selector(ccCallbackBlockWithSender:)]; + [super dealloc]; } -#endif // NS_BLOCKS_AVAILABLE - --(void) dealloc +-(void) cleanup { - [invocation_ release]; - -#if NS_BLOCKS_AVAILABLE [block_ release]; -#endif - - [super dealloc]; + block_ = nil; + + [super cleanup]; } -(void) selected @@ -124,8 +115,8 @@ -(void) unselected -(void) activate { - if(isEnabled_) - [invocation_ invoke]; + if(isEnabled_&& block_ ) + block_(self); } -(void) setIsEnabled: (BOOL)enabled @@ -142,7 +133,21 @@ -(CGRect) rect { return CGRectMake( position_.x - contentSize_.width*anchorPoint_.x, position_.y - contentSize_.height*anchorPoint_.y, - contentSize_.width, contentSize_.height); + contentSize_.width, contentSize_.height); +} + +-(void) setBlock:(void(^)(id sender))block +{ + [block_ release]; + block_ = [block copy]; +} + +-(void) setTarget:(id)target selector:(SEL)selector +{ + [self setBlock:^(id sender) { + + [target performSelector:selector withObject:sender]; + }]; } @end @@ -155,41 +160,48 @@ @implementation CCMenuItemLabel @synthesize disabledColor = disabledColor_; ++(id) itemWithLabel:(CCNode*)label +{ + return [[[self alloc] initWithLabel:label block:nil] autorelease]; +} + +(id) itemWithLabel:(CCNode*)label target:(id)target selector:(SEL)selector { return [[[self alloc] initWithLabel:label target:target selector:selector] autorelease]; } -+(id) itemWithLabel:(CCNode*)label -{ - return [[[self alloc] initWithLabel:label target:nil selector:NULL] autorelease]; ++(id) itemWithLabel:(CCNode*)label block:(void(^)(id sender))block { + return [[[self alloc] initWithLabel:label block:block] autorelease]; } + -(id) initWithLabel:(CCNode*)label target:(id)target selector:(SEL)selector { - if( (self=[super initWithTarget:target selector:selector]) ) { + // avoid retain cycle + __block id t = target; + + self = [self initWithLabel:label block: ^(id sender) { + [t performSelector:selector withObject:sender]; + } + ]; + return self; +} + +// +// Designated initializer +// +-(id) initWithLabel:(CCNode *)label block:(void (^)(id))block +{ + if( (self=[self initWithBlock:block]) ) { originalScale_ = 1; colorBackup = ccWHITE; disabledColor_ = ccc3( 126,126,126); self.label = label; - } - return self; -} -#if NS_BLOCKS_AVAILABLE - -+(id) itemWithLabel:(CCNode*)label block:(void(^)(id sender))block { - return [[[self alloc] initWithLabel:label block:block] autorelease]; -} - --(id) initWithLabel:(CCNode*)label block:(void(^)(id sender))block { - block_ = [block copy]; - return [self initWithLabel:label target:block_ selector:@selector(ccCallbackBlockWithSender:)]; + return self; } -#endif // NS_BLOCKS_AVAILABLE - -(CCNode*) label { return label_; @@ -199,7 +211,7 @@ -(void) setLabel:(CCNode*) label if( label != label_ ) { [self removeChild:label_ cleanup:YES]; [self addChild:label]; - + label_ = label; label_.anchorPoint = ccp(0,0); @@ -216,9 +228,9 @@ -(void) setString:(NSString *)string -(void) activate { if(isEnabled_) { [self stopAllActions]; - + self.scale = originalScale_; - + [super activate]; } } @@ -226,17 +238,17 @@ -(void) activate { -(void) selected { // subclass to change the default action - if(isEnabled_) { + if(isEnabled_) { [super selected]; - CCAction *action = [self getActionByTag:kZoomActionTag]; + CCAction *action = [self getActionByTag:kCCZoomActionTag]; if( action ) [self stopAction:action]; else originalScale_ = self.scale; CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:originalScale_ * 1.2f]; - zoomAction.tag = kZoomActionTag; + zoomAction.tag = kCCZoomActionTag; [self runAction:zoomAction]; } } @@ -246,9 +258,9 @@ -(void) unselected // subclass to change the default action if(isEnabled_) { [super unselected]; - [self stopActionByTag:kZoomActionTag]; + [self stopActionByTag:kCCZoomActionTag]; CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:originalScale_]; - zoomAction.tag = kZoomActionTag; + zoomAction.tag = kCCZoomActionTag; [self runAction:zoomAction]; } } @@ -263,7 +275,7 @@ -(void) setIsEnabled: (BOOL)enabled else [label_ setColor:colorBackup]; } - + [super setIsEnabled:enabled]; } @@ -285,45 +297,51 @@ -(ccColor3B) color } @end -#pragma mark - -#pragma mark CCMenuItemAtlasFont +#pragma mark - CCMenuItemAtlasFont @implementation CCMenuItemAtlasFont -+(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap ++(id) itemWithString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap { - return [CCMenuItemAtlasFont itemFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:nil selector:nil]; + return [CCMenuItemAtlasFont itemWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:nil selector:nil]; } -+(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb ++(id) itemWithString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id)target selector:(SEL)selector { - return [[[self alloc] initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:rec selector:cb] autorelease]; + return [[[self alloc] initWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:target selector:selector] autorelease]; } --(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb ++(id) itemWithString:(NSString*)value charMapFile:(NSString*)charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block { - NSAssert( [value length] != 0, @"value length must be greater than 0"); - - CCLabelAtlas *label = [[CCLabelAtlas alloc] initWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap]; - [label autorelease]; - - if((self=[super initWithLabel:label target:rec selector:cb]) ) { - // do something ? - } - - return self; + return [[[self alloc] initWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap block:block] autorelease]; } -#if NS_BLOCKS_AVAILABLE -+(id) itemFromString:(NSString*)value charMapFile:(NSString*)charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block { - return [[[self alloc] initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap block:block] autorelease]; +-(id) initWithString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id)target selector:(SEL)selector +{ + // avoid retain cycle + __block id t = target; + + return [self initWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap block:^(id sender) { + [t performSelector:selector withObject:sender]; + } ]; } --(id) initFromString:(NSString*)value charMapFile:(NSString*)charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block { - block_ = [block copy]; - return [self initFromString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:block_ selector:@selector(ccCallbackBlockWithSender:)]; +// +// Designated initializer +// +-(id) initWithString:(NSString*)value charMapFile:(NSString*)charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block +{ + NSAssert( [value length] > 0, @"value length must be greater than 0"); + + CCLabelAtlas *label = [[CCLabelAtlas alloc] initWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap]; + + id ret = [self initWithLabel:label block:block]; + + [label release]; + + return ret; + } -#endif // NS_BLOCKS_AVAILABLE -(void) dealloc { @@ -332,8 +350,7 @@ -(void) dealloc @end -#pragma mark - -#pragma mark CCMenuItemFont +#pragma mark - CCMenuItemFont @implementation CCMenuItemFont @@ -351,7 +368,7 @@ +(void) setFontName: (NSString*) n { if( _fontNameRelease ) [_fontName release]; - + _fontName = [n retain]; _fontNameRelease = YES; } @@ -361,36 +378,55 @@ +(NSString*) fontName return _fontName; } -+(id) itemFromString: (NSString*) value target:(id) r selector:(SEL) s ++(id) itemWithString: (NSString*) value target:(id) r selector:(SEL) s +{ + return [[[self alloc] initWithString: value target:r selector:s] autorelease]; +} + ++(id) itemWithString: (NSString*) value { - return [[[self alloc] initFromString: value target:r selector:s] autorelease]; + return [[[self alloc] initWithString: value target:nil selector:nil] autorelease]; } -+(id) itemFromString: (NSString*) value ++(id) itemWithString: (NSString*) value block:(void(^)(id sender))block { - return [[[self alloc] initFromString: value target:nil selector:nil] autorelease]; + return [[[self alloc] initWithString:value block:block] autorelease]; } --(id) initFromString: (NSString*) value target:(id) rec selector:(SEL) cb +-(id) initWithString: (NSString*) value target:(id)target selector:(SEL)selector +{ + // avoid retain cycle + __block id t = target; + + return [self initWithString:value block:^(id sender) { + [t performSelector:selector withObject:sender]; + }]; +} + +// +// Designated initializer +// +-(id) initWithString: (NSString*)string block:(void(^)(id sender))block { - NSAssert( [value length] != 0, @"Value length must be greater than 0"); - + NSAssert( [string length] > 0, @"Value length must be greater than 0"); + fontName_ = [_fontName copy]; fontSize_ = _fontSize; - - CCLabelTTF *label = [CCLabelTTF labelWithString:value fontName:fontName_ fontSize:fontSize_]; - if((self=[super initWithLabel:label target:rec selector:cb]) ) { + CCLabelTTF *label = [CCLabelTTF labelWithString:string fontName:fontName_ fontSize:fontSize_]; + + if((self=[super initWithLabel:label block:block]) ) { // do something ? } - + return self; } -(void) recreateLabel { - CCLabelTTF *label = [CCLabelTTF labelWithString:[label_ string] fontName:fontName_ fontSize:fontSize_]; + CCLabelTTF *label = [[CCLabelTTF alloc] initWithString:[label_ string] fontName:fontName_ fontSize:fontSize_]; self.label = label; + [label release]; } -(void) setFontSize: (NSUInteger) size @@ -417,79 +453,82 @@ -(NSString*) fontName { return fontName_; } +@end -#if NS_BLOCKS_AVAILABLE -+(id) itemFromString: (NSString*) value block:(void(^)(id sender))block -{ - return [[[self alloc] initFromString:value block:block] autorelease]; -} - --(id) initFromString: (NSString*) value block:(void(^)(id sender))block -{ - block_ = [block copy]; - return [self initFromString:value target:block_ selector:@selector(ccCallbackBlockWithSender:)]; -} -#endif // NS_BLOCKS_AVAILABLE +#pragma mark - CCMenuItemSprite +@interface CCMenuItemSprite() +-(void) updateImagesVisibility; @end -#pragma mark - -#pragma mark CCMenuItemSprite @implementation CCMenuItemSprite @synthesize normalImage=normalImage_, selectedImage=selectedImage_, disabledImage=disabledImage_; -+(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite ++(id) itemWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite { - return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:nil selector:nil]; + return [self itemWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:nil selector:nil]; } -+(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite target:(id)target selector:(SEL)selector + ++(id) itemWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite target:(id)target selector:(SEL)selector { - return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:target selector:selector]; + return [self itemWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil target:target selector:selector]; } -+(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector + ++(id) itemWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector { - return [[[self alloc] initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:target selector:selector] autorelease]; + return [[[self alloc] initWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:target selector:selector] autorelease]; } --(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector + ++(id) itemWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite block:(void(^)(id sender))block { - if( (self=[super initWithTarget:target selector:selector]) ) { - - self.normalImage = normalSprite; - self.selectedImage = selectedSprite; - self.disabledImage = disabledSprite; - - [self setContentSize: [normalImage_ contentSize]]; - } - return self; + return [self itemWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil block:block]; } -#if NS_BLOCKS_AVAILABLE -+(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite block:(void(^)(id sender))block { - return [self itemFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:nil block:block]; ++(id) itemWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block +{ + return [[[self alloc] initWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite block:block] autorelease]; } -+(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block { - return [[[self alloc] initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite block:block] autorelease]; -} +-(id) initWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector +{ + // avoid retain cycle + __block id t = target; --(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block { - block_ = [block copy]; - return [self initFromNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:block_ selector:@selector(ccCallbackBlockWithSender:)]; + return [self initWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite block:^(id sender) { + [t performSelector:selector withObject:sender]; + } ]; } -#endif // NS_BLOCKS_AVAILABLE +// +// Designated initializer +// +-(id) initWithNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block +{ + if ( (self = [super initWithBlock:block] ) ) { + + self.normalImage = normalSprite; + self.selectedImage = selectedSprite; + self.disabledImage = disabledSprite; + + [self setContentSize: [normalImage_ contentSize]]; + } + return self; +} -(void) setNormalImage:(CCNode *)image { if( image != normalImage_ ) { image.anchorPoint = ccp(0,0); - image.visible = YES; - + [self removeChild:normalImage_ cleanup:YES]; [self addChild:image]; - + normalImage_ = image; + + [self setContentSize: [normalImage_ contentSize]]; + + [self updateImagesVisibility]; } } @@ -497,12 +536,13 @@ -(void) setSelectedImage:(CCNode *)image { if( image != selectedImage_ ) { image.anchorPoint = ccp(0,0); - image.visible = NO; - + [self removeChild:selectedImage_ cleanup:YES]; [self addChild:image]; - + selectedImage_ = image; + + [self updateImagesVisibility]; } } @@ -510,16 +550,18 @@ -(void) setDisabledImage:(CCNode *)image { if( image != disabledImage_ ) { image.anchorPoint = ccp(0,0); - image.visible = NO; - + [self removeChild:disabledImage_ cleanup:YES]; [self addChild:image]; - + disabledImage_ = image; + + [self updateImagesVisibility]; } } -#pragma mark CCMenuItemImage - CCRGBAProtocol protocol +#pragma mark CCMenuItemSprite - CCRGBAProtocol protocol + - (void) setOpacity: (GLubyte)opacity { [normalImage_ setOpacity:opacity]; @@ -531,7 +573,7 @@ -(void) setColor:(ccColor3B)color { [normalImage_ setColor:color]; [selectedImage_ setColor:color]; - [disabledImage_ setColor:color]; + [disabledImage_ setColor:color]; } -(GLubyte) opacity @@ -552,12 +594,12 @@ -(void) selected [normalImage_ setVisible:NO]; [selectedImage_ setVisible:YES]; [disabledImage_ setVisible:NO]; - + } else { // there is not selected image - + [normalImage_ setVisible:YES]; [selectedImage_ setVisible:NO]; - [disabledImage_ setVisible:NO]; + [disabledImage_ setVisible:NO]; } } @@ -571,18 +613,27 @@ -(void) unselected -(void) setIsEnabled:(BOOL)enabled { - [super setIsEnabled:enabled]; + if( isEnabled_ != enabled ) { + [super setIsEnabled:enabled]; - if( enabled ) { + [self updateImagesVisibility]; + } +} + + +// Helper +-(void) updateImagesVisibility +{ + if( isEnabled_ ) { [normalImage_ setVisible:YES]; [selectedImage_ setVisible:NO]; [disabledImage_ setVisible:NO]; - + } else { if( disabledImage_ ) { [normalImage_ setVisible:NO]; [selectedImage_ setVisible:NO]; - [disabledImage_ setVisible:YES]; + [disabledImage_ setVisible:YES]; } else { [normalImage_ setVisible:YES]; [selectedImage_ setVisible:NO]; @@ -593,66 +644,89 @@ -(void) setIsEnabled:(BOOL)enabled @end -#pragma mark - -#pragma mark CCMenuItemImage +#pragma mark - CCMenuItemImage @implementation CCMenuItemImage -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2 +{ + return [self itemWithNormalImage:value selectedImage:value2 disabledImage: nil target:nil selector:nil]; +} + ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) t selector:(SEL) s { - return [self itemFromNormalImage:value selectedImage:value2 disabledImage: nil target:nil selector:nil]; + return [self itemWithNormalImage:value selectedImage:value2 disabledImage: nil target:t selector:s]; } -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) t selector:(SEL) s ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage: (NSString*) value3 { - return [self itemFromNormalImage:value selectedImage:value2 disabledImage: nil target:t selector:s]; + return [[[self alloc] initWithNormalImage:value selectedImage:value2 disabledImage:value3 target:nil selector:nil] autorelease]; } -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage: (NSString*) value3 ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage: (NSString*) value3 target:(id) t selector:(SEL) s { - return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:nil selector:nil] autorelease]; + return [[[self alloc] initWithNormalImage:value selectedImage:value2 disabledImage:value3 target:t selector:s] autorelease]; } -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage: (NSString*) value3 target:(id) t selector:(SEL) s ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block { - return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:t selector:s] autorelease]; + return [self itemWithNormalImage:value selectedImage:value2 disabledImage:nil block:block]; +} + ++(id) itemWithNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block +{ + return [[[self alloc] initWithNormalImage:value selectedImage:value2 disabledImage:value3 block:block] autorelease]; +} + +-(id) initWithNormalImage: (NSString*) normalI selectedImage:(NSString*)selectedI disabledImage: (NSString*) disabledI target:(id)target selector:(SEL)selector +{ + // avoid retain cycle + __block id t = target; + + return [self initWithNormalImage:normalI selectedImage:selectedI disabledImage:disabledI block:^(id sender) { + [t performSelector:selector withObject:sender]; + }]; } --(id) initFromNormalImage: (NSString*) normalI selectedImage:(NSString*)selectedI disabledImage: (NSString*) disabledI target:(id)t selector:(SEL)sel + +// +// Designated initializer +// +-(id) initWithNormalImage:(NSString*)normalI selectedImage:(NSString*)selectedI disabledImage:(NSString*)disabledI block:(void(^)(id sender))block { CCNode *normalImage = [CCSprite spriteWithFile:normalI]; CCNode *selectedImage = nil; CCNode *disabledImage = nil; if( selectedI ) - selectedImage = [CCSprite spriteWithFile:selectedI]; + selectedImage = [CCSprite spriteWithFile:selectedI]; if(disabledI) disabledImage = [CCSprite spriteWithFile:disabledI]; - return [self initFromNormalSprite:normalImage selectedSprite:selectedImage disabledSprite:disabledImage target:t selector:sel]; + return [super initWithNormalSprite:normalImage selectedSprite:selectedImage disabledSprite:disabledImage block:block]; } -#if NS_BLOCKS_AVAILABLE - -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block { - return [self itemFromNormalImage:value selectedImage:value2 disabledImage:nil block:block]; +// +// Setter of sprite frames +// +-(void) setNormalSpriteFrame:(CCSpriteFrame *)frame +{ + [self setNormalImage:[CCSprite spriteWithSpriteFrame:frame]]; } -+(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block { - return [[[self alloc] initFromNormalImage:value selectedImage:value2 disabledImage:value3 block:block] autorelease]; +-(void) setSelectedSpriteFrame:(CCSpriteFrame *)frame +{ + [self setSelectedImage:[CCSprite spriteWithSpriteFrame:frame]]; } --(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block { - block_ = [block copy]; - return [self initFromNormalImage:value selectedImage:value2 disabledImage:value3 target:block_ selector:@selector(ccCallbackBlockWithSender:)]; +-(void) setDisabledSpriteFrame:(CCSpriteFrame *)frame +{ + [self setDisabledImage:[CCSprite spriteWithSpriteFrame:frame]]; } -#endif // NS_BLOCKS_AVAILABLE - @end -#pragma mark - -#pragma mark CCMenuItemToggle +#pragma mark - CCMenuItemToggle // // MenuItemToggle @@ -666,52 +740,51 @@ +(id) itemWithTarget: (id)t selector: (SEL)sel items: (CCMenuItem*) item, ... { va_list args; va_start(args, item); - + id s = [[[self alloc] initWithTarget: t selector:sel items: item vaList:args] autorelease]; - + va_end(args); return s; } --(id) initWithTarget: (id)t selector: (SEL)sel items:(CCMenuItem*) item vaList: (va_list) args ++(id) itemWithItems:(NSArray*)arrayOfItems block:(void(^)(id))block { - if( (self=[super initWithTarget:t selector:sel]) ) { - - self.subItems = [NSMutableArray arrayWithCapacity:2]; - - int z = 0; - CCMenuItem *i = item; - while(i) { - z++; - [subItems_ addObject:i]; - i = va_arg(args, CCMenuItem*); - } + return [[[self alloc] initWithItems:arrayOfItems block:block] autorelease]; +} - selectedIndex_ = NSUIntegerMax; - [self setSelectedIndex:0]; +-(id) initWithTarget:(id)target selector:(SEL)selector items:(CCMenuItem*) item vaList: (va_list) args +{ + NSMutableArray *array = [NSMutableArray arrayWithCapacity:2]; + + int z = 0; + CCMenuItem *i = item; + while(i) { + z++; + [array addObject:i]; + i = va_arg(args, CCMenuItem*); } - - return self; -} -#if NS_BLOCKS_AVAILABLE - -+(id) itemWithBlock:(void(^)(id sender))block items:(CCMenuItem*)item, ... { - va_list args; - va_start(args, item); - - id s = [[[self alloc] initWithBlock:block items:item vaList:args] autorelease]; - - va_end(args); - return s; -} + // avoid retain cycle + __block id t = target; --(id) initWithBlock:(void (^)(id))block items:(CCMenuItem*)item vaList:(va_list)args { - block_ = [block copy]; - return [self initWithTarget:block_ selector:@selector(ccCallbackBlockWithSender:) items:item vaList:args]; + return [self initWithItems:array block:^(id sender) { + [t performSelector:selector withObject:sender]; + } + ]; } -#endif // NS_BLOCKS_AVAILABLE +-(id) initWithItems:(NSArray*)arrayOfItems block:(void(^)(id sender))block +{ + if( (self=[super initWithBlock:block] ) ) { + + self.subItems = [NSMutableArray arrayWithArray:arrayOfItems]; + + selectedIndex_ = NSUIntegerMax; + [self setSelectedIndex:0]; + } + + return self; +} -(void) dealloc { @@ -723,11 +796,13 @@ -(void)setSelectedIndex:(NSUInteger)index { if( index != selectedIndex_ ) { selectedIndex_=index; - [self removeChildByTag:kCurrentItem cleanup:NO]; + CCMenuItem *currentItem = (CCMenuItem*)[self getChildByTag:kCCCurrentItemTag]; + if( currentItem ) + [currentItem removeFromParentAndCleanup:NO]; CCMenuItem *item = [subItems_ objectAtIndex:selectedIndex_]; - [self addChild:item z:0 tag:kCurrentItem]; - + [self addChild:item z:0 tag:kCCCurrentItemTag]; + CGSize s = [item contentSize]; [self setContentSize: s]; item.position = ccp( s.width/2, s.height/2 ); @@ -766,9 +841,11 @@ -(void) activate -(void) setIsEnabled: (BOOL)enabled { - [super setIsEnabled:enabled]; - for(CCMenuItem* item in subItems_) - [item setIsEnabled:enabled]; + if( isEnabled_ != enabled ) { + [super setIsEnabled:enabled]; + for(CCMenuItem* item in subItems_) + [item setIsEnabled:enabled]; + } } -(CCMenuItem*) selectedItem diff --git a/cocos2d/cocos2d/CCMotionStreak.h b/cocos2d/cocos2d/CCMotionStreak.h old mode 100644 new mode 100755 index e017124..4404151 --- a/cocos2d/cocos2d/CCMotionStreak.h +++ b/cocos2d/cocos2d/CCMotionStreak.h @@ -1,18 +1,18 @@ /* * cocos2d for iPhone: http://www.cocos2d-iphone.org * - * Copyright (c) 2008, 2009 Jason Booth - * + * Copyright (c) 2011 ForzeField Studios S.L. http://forzefield.com + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,45 +23,65 @@ * */ + #import +#import "CCTexture2D.h" +#import "ccTypes.h" #import "CCNode.h" -#import "CCRibbon.h" -/** - * CCMotionStreak manages a Ribbon based on it's motion in absolute space. - * You construct it with a fadeTime, minimum segment size, texture path, texture - * length and color. The fadeTime controls how long it takes each vertex in - * the streak to fade out, the minimum segment size it how many pixels the - * streak will move before adding a new ribbon segement, and the texture - * length is the how many pixels the texture is stretched across. The texture - * is vertically aligned along the streak segemnts. - * - * Limitations: - * CCMotionStreak, by default, will use the GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA blending function. - * This blending function might not be the correct one for certain textures. - * But you can change it by using: - * [obj setBlendFunc: (ccBlendfunc) {new_src_blend_func, new_dst_blend_func}]; - * - * @since v0.8.1 +/** MotionStreak. + Creates a trailing path. */ -@interface CCMotionStreak : CCNode +@interface CCMotionStreak : CCNode { - CCRibbon* ribbon_; - float segThreshold_; - float width_; - CGPoint lastLocation_; + CCTexture2D *texture_; + CGPoint positionR_; + ccColor3B color_; + ccBlendFunc blendFunc_; + float stroke_; + float fadeDelta_; + float minSeg_; + + NSUInteger maxPoints_; + NSUInteger nuPoints_; + NSUInteger previousNuPoints_; + + /** Pointers */ + CGPoint *pointVertexes_; + float *pointState_; + + // Opengl + ccVertex2F *vertices_; + unsigned char *colorPointer_; + ccTex2F *texCoords_; + + BOOL fastMode_; + + BOOL startingPositionInitialized_; } +/** blending function */ +@property (nonatomic, readwrite, assign) ccBlendFunc blendFunc; + +/** When fast mode is enbled, new points are added faster but with lower precision */ +@property (nonatomic, readwrite, assign, getter = isFastMode) BOOL fastMode; + +/** texture used for the motion streak */ +@property (nonatomic, retain) CCTexture2D *texture; -/** Ribbon used by MotionStreak (weak reference) */ -@property (nonatomic,readonly) CCRibbon *ribbon; +/** creates and initializes a motion streak with fade in seconds, minimum segments, stroke's width, color, texture filename */ ++ (id) streakWithFade:(float)fade minSeg:(float)minSeg width:(float)stroke color:(ccColor3B)color textureFilename:(NSString*)path; +/** creates and initializes a motion streak with fade in seconds, minimum segments, stroke's width, color, texture */ ++ (id) streakWithFade:(float)fade minSeg:(float)minSeg width:(float)stroke color:(ccColor3B)color texture:(CCTexture2D*)texture; -/** creates the a MotionStreak. The image will be loaded using the TextureMgr. */ -+(id)streakWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color; +/** initializes a motion streak with fade in seconds, minimum segments, stroke's width, color and texture filename */ +- (id) initWithFade:(float)fade minSeg:(float)minSeg width:(float)stroke color:(ccColor3B)color textureFilename:(NSString*)path; +/** initializes a motion streak with fade in seconds, minimum segments, stroke's width, color and texture */ +- (id) initWithFade:(float)fade minSeg:(float)minSeg width:(float)stroke color:(ccColor3B)color texture:(CCTexture2D*)texture; -/** initializes a MotionStreak. The file will be loaded using the TextureMgr. */ --(id)initWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color; +/** color used for the tint */ +- (void) tintWithColor:(ccColor3B)colors; -/** polling function */ --(void)update:(ccTime)delta; +/** Remove all living segments of the ribbon */ +- (void) reset; @end diff --git a/cocos2d/cocos2d/CCMotionStreak.m b/cocos2d/cocos2d/CCMotionStreak.m old mode 100644 new mode 100755 index 42737b9..e299eef --- a/cocos2d/cocos2d/CCMotionStreak.m +++ b/cocos2d/cocos2d/CCMotionStreak.m @@ -1,18 +1,18 @@ /* * cocos2d for iPhone: http://www.cocos2d-iphone.org * - * Copyright (c) 2008, 2009 Jason Booth - * + * Copyright (c) 2011 ForzeField Studios S.L. http://forzefield.com + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -21,84 +21,270 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * - * - ********************************************************* - * - * Motion Streak manages a Ribbon based on it's motion in absolute space. - * You construct it with a fadeTime, minimum segment size, texture path, texture - * length and color. The fadeTime controls how long it takes each vertex in - * the streak to fade out, the minimum segment size it how many pixels the - * streak will move before adding a new ribbon segement, and the texture - * length is the how many pixels the texture is stretched across. The texture - * is vertically aligned along the streak segemnts. */ #import "CCMotionStreak.h" +#import "CCTextureCache.h" +#import "ccGLStateCache.h" +#import "CCGLProgram.h" +#import "CCShaderCache.h" +#import "ccMacros.h" + +#import "Support/CCVertex.h" #import "Support/CGPointExtension.h" + @implementation CCMotionStreak +@synthesize texture = texture_; +@synthesize blendFunc = blendFunc_; +@synthesize fastMode = fastMode_; -@synthesize ribbon = ribbon_; ++ (id) streakWithFade:(float)fade minSeg:(float)minSeg width:(float)stroke color:(ccColor3B)color textureFilename:(NSString*)path +{ + return [[[self alloc] initWithFade:fade minSeg:minSeg width:stroke color:color textureFilename:path] autorelease]; +} -+(id)streakWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color ++ (id) streakWithFade:(float)fade minSeg:(float)minSeg width:(float)stroke color:(ccColor3B)color texture:(CCTexture2D*)texture { - return [[[self alloc] initWithFade:(float)fade minSeg:seg image:path width:width length:length color:color] autorelease]; + return [[[self alloc] initWithFade:fade minSeg:minSeg width:stroke color:color texture:texture] autorelease]; } --(id)initWithFade:(float)fade minSeg:(float)seg image:(NSString*)path width:(float)width length:(float)length color:(ccColor4B)color +- (id) initWithFade:(float)fade minSeg:(float)minSeg width:(float)stroke color:(ccColor3B)color textureFilename:(NSString*)path { - if( (self=[super init])) { - segThreshold_ = seg; - width_ = width; - lastLocation_ = CGPointZero; - ribbon_ = [CCRibbon ribbonWithWidth:width_ image:path length:length color:color fade:fade]; - [self addChild:ribbon_]; - - // update ribbon position. Use schedule:interval and not scheduleUpdated. issue #1075 - [self schedule:@selector(update:) interval:0]; - } - return self; + NSAssert(path != nil, @"Invalid filename"); + + CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:path]; + return [self initWithFade:fade minSeg:minSeg width:stroke color:color texture:texture]; } --(void)update:(ccTime)delta +- (id) initWithFade:(float)fade minSeg:(float)minSeg width:(float)stroke color:(ccColor3B)color texture:(CCTexture2D*)texture { - CGPoint location = [self convertToWorldSpace:CGPointZero]; - [ribbon_ setPosition:ccp(-1*location.x, -1*location.y)]; - float len = ccpLength(ccpSub(lastLocation_, location)); - if (len > segThreshold_) - { - [ribbon_ addPointAt:location width:width_]; - lastLocation_ = location; - } - [ribbon_ update:delta]; + self = [super init]; + if (self) + { + [super setPosition:CGPointZero]; + [self setAnchorPoint:CGPointZero]; + [self setIgnoreAnchorPointForPosition:YES]; + + startingPositionInitialized_ = NO; + positionR_ = CGPointZero; + fastMode_ = YES; + minSeg_ = (minSeg == -1.0f) ? stroke/5.0f : minSeg; + minSeg_ *= minSeg_; + + stroke_ = stroke; + fadeDelta_ = 1.0f/fade; + + maxPoints_ = (int)(fade*60.0f)+2; + nuPoints_ = previousNuPoints_ = 0; + pointState_ = malloc(sizeof(float) * maxPoints_); + pointVertexes_ = malloc(sizeof(CGPoint) * maxPoints_); + + vertices_ = malloc(sizeof(ccVertex2F) * maxPoints_ * 2); + texCoords_ = malloc(sizeof(ccTex2F) * maxPoints_ * 2); + colorPointer_ = malloc(sizeof(GLubyte) * maxPoints_ * 2 * 4); + + // Set blend mode + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + + // shader program + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTextureColor]; + + [self setTexture:texture]; + [self setColor:color]; + [self scheduleUpdate]; + + } + return self; } +#pragma mark - --(void)dealloc +- (void) setPosition:(CGPoint)position { - [super dealloc]; + startingPositionInitialized_ = YES; + positionR_ = position; } -#pragma mark MotionStreak - CocosNodeTexture protocol +- (void) tintWithColor:(ccColor3B)colors +{ + [self setColor:colors]; + + // Fast assignation + for(int i = 0; i0) + { + // Move data + pointState_[newIdx] = pointState_[i]; + + // Move point + pointVertexes_[newIdx] = pointVertexes_[i]; + + // Move vertices + i2 = i*2; + newIdx2 = newIdx*2; + vertices_[newIdx2] = vertices_[i2]; + vertices_[newIdx2+1] = vertices_[i2+1]; + + // Move color + i2 *= 4; + newIdx2 *= 4; + colorPointer_[newIdx2+0] = colorPointer_[i2+0]; + colorPointer_[newIdx2+1] = colorPointer_[i2+1]; + colorPointer_[newIdx2+2] = colorPointer_[i2+2]; + colorPointer_[newIdx2+4] = colorPointer_[i2+4]; + colorPointer_[newIdx2+5] = colorPointer_[i2+5]; + colorPointer_[newIdx2+6] = colorPointer_[i2+6]; + }else + newIdx2 = newIdx*8; + + const GLubyte op = pointState_[newIdx] * 255.0f; + colorPointer_[newIdx2+3] = op; + colorPointer_[newIdx2+7] = op; + } + } + nuPoints_-=mov; + + // Append new point + BOOL appendNewPoint = YES; + if(nuPoints_ >= maxPoints_) + appendNewPoint = NO; + + else if(nuPoints_>0) + { + BOOL a1 = ccpDistanceSQ(pointVertexes_[nuPoints_-1], positionR_) < minSeg_; + BOOL a2 = (nuPoints_ == 1) ? NO : (ccpDistanceSQ(pointVertexes_[nuPoints_-2], positionR_) < (minSeg_ * 2.0f)); + if(a1 || a2) + appendNewPoint = NO; + } + + if(appendNewPoint) + { + pointVertexes_[nuPoints_] = positionR_; + pointState_[nuPoints_] = 1.0f; + + // Color asignation + const NSUInteger offset = nuPoints_*8; + *((ccColor3B*)(colorPointer_ + offset)) = color_; + *((ccColor3B*)(colorPointer_ + offset+4)) = color_; + + // Opacity + colorPointer_[offset+3] = 255; + colorPointer_[offset+7] = 255; + + // Generate polygon + if(nuPoints_ > 0 && fastMode_ ) + { + if(nuPoints_ > 1) + ccVertexLineToPolygon(pointVertexes_, stroke_, vertices_, nuPoints_, 1); + else + ccVertexLineToPolygon(pointVertexes_, stroke_, vertices_, 0, 2); + } + + nuPoints_ ++; + } + + if( ! fastMode_ ) + ccVertexLineToPolygon(pointVertexes_, stroke_, vertices_, 0, nuPoints_); + + + // Updated Tex Coords only if they are different than previous step + if( nuPoints_ && previousNuPoints_ != nuPoints_ ) { + float texDelta = 1.0f / nuPoints_; + for( i=0; i < nuPoints_; i++ ) { + texCoords_[i*2] = (ccTex2F) {0, texDelta*i}; + texCoords_[i*2+1] = (ccTex2F) {1, texDelta*i}; + } + + previousNuPoints_ = nuPoints_; + } +} + +- (void) reset +{ + nuPoints_ = 0; +} + +- (void) draw +{ + if(nuPoints_ <= 1) + return; + + CC_NODE_DRAW_SETUP(); + + ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex ); + ccGLBlendFunc( blendFunc_.src, blendFunc_.dst ); + + ccGLBindTexture2D( [texture_ name] ); + + glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices_); + glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, texCoords_); + glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colorPointer_); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nuPoints_*2); + + CC_INCREMENT_GL_DRAWS(1); +} + +- (void)dealloc +{ + [texture_ release]; + + free(pointState_); + free(pointVertexes_); + free(vertices_); + free(colorPointer_); + free(texCoords_); + + [super dealloc]; } @end diff --git a/cocos2d/cocos2d/CCBlockSupport.m b/cocos2d/cocos2d/CCNode+Debug.h old mode 100644 new mode 100755 similarity index 78% rename from cocos2d/cocos2d/CCBlockSupport.m rename to cocos2d/cocos2d/CCNode+Debug.h index 9ac99b3..f2e08fe --- a/cocos2d/cocos2d/CCBlockSupport.m +++ b/cocos2d/cocos2d/CCNode+Debug.h @@ -1,18 +1,18 @@ /* * cocos2d for iPhone: http://www.cocos2d-iphone.org * - * Copyright (c) 2010 Stuart Carnie - * + * Copyright (c) 2012 Zynga Inc. + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -20,27 +20,20 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - * */ +#import "CCNode.h" -#import "CCBlockSupport.h" +#ifdef DEBUG -#if NS_BLOCKS_AVAILABLE - -@implementation NSObject(CCBlocksAdditions) - -- (void)ccCallbackBlock { - void (^block)(void) = (id)self; - block(); -} - -- (void)ccCallbackBlockWithSender:(id)sender { - void (^block)(id) = (id)self; - block(sender); -} +/** Debugging extensions of CCNode. + They are available when the DEBUG macro is defined at compile time + */ +@interface CCNode (Debug) +/** prints on the debug console the scene graph */ +-(void) walkSceneGraph:(NSUInteger)level; @end -#endif +#endif // DEBUG diff --git a/cocos2d/cocos2d/CCNode+Debug.m b/cocos2d/cocos2d/CCNode+Debug.m new file mode 100755 index 0000000..3e84954 --- /dev/null +++ b/cocos2d/cocos2d/CCNode+Debug.m @@ -0,0 +1,71 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2012 Zynga Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import "CCNode+Debug.h" + +#ifdef DEBUG + +@implementation CCNode (Debug) + +-(void) walkSceneGraph:(NSUInteger)level +{ + char buf[64]; + NSUInteger i=0; + for( i=0; idata; + i = 0; + + // draw children zOrder < 0 + for( ; i < arrayData->num; i++ ) { + CCNode *child = arrayData->arr[i]; + if ( [child zOrder] < 0 ) + [child walkSceneGraph:level+1]; + else + break; + } + + // self draw + NSLog(@"walk tree: %s> %@ %p", buf, self, self); + + // draw children zOrder >= 0 + for( ; i < arrayData->num; i++ ) { + CCNode *child = arrayData->arr[i]; + [child walkSceneGraph:level+1]; + } + + } else + NSLog(@"walk tree: %s> %@ %p", buf, self, self); + +} +@end + +#endif // DEBUG diff --git a/cocos2d/cocos2d/CCNode.h b/cocos2d/cocos2d/CCNode.h old mode 100644 new mode 100755 index 64acdc5..a4ab100 --- a/cocos2d/cocos2d/CCNode.h +++ b/cocos2d/cocos2d/CCNode.h @@ -5,17 +5,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,15 +25,13 @@ * THE SOFTWARE. */ -#import - #import "Platforms/CCGL.h" -#import "CCAction.h" #import "ccTypes.h" -#import "CCTexture2D.h" #import "CCProtocols.h" #import "ccConfig.h" +#import "ccGLStateCache.h" #import "Support/CCArray.h" +#import "kazmath/kazmath.h" enum { kCCNodeTagInvalid = -1, @@ -41,22 +39,26 @@ enum { @class CCCamera; @class CCGridBase; +@class CCGLProgram; +@class CCScheduler; +@class CCActionManager; +@class CCAction; /** CCNode is the main element. Anything thats gets drawn or contains things that get drawn is a CCNode. The most popular CCNodes are: CCScene, CCLayer, CCSprite, CCMenu. - + The main features of a CCNode are: - They can contain other CCNode nodes (addChild, getChildByTag, removeChild, etc) - They can schedule periodic callback (schedule, unschedule, etc) - They can execute actions (runAction, stopAction, etc) - + Some CCNode nodes provide extra functionality for them or their children. - + Subclassing a CCNode usually means (one/all) of: - overriding init to initialize resources and schedule callbacks - create callbacks to handle the advancement of time - overriding draw to render the node - + Features of CCNode: - position - scale (x, y) @@ -68,108 +70,118 @@ enum { - visible - z-order - openGL z position - + Default values: - rotation: 0 - position: (x=0,y=0) - scale: (x=1,y=1) - contentSize: (x=0,y=0) - anchorPoint: (x=0,y=0) - + Limitations: - A CCNode is a "void" object. It doesn't have a texture - + Order in transformations with grid disabled -# The node will be translated (position) -# The node will be rotated (rotation) - -# The node will be scaled (scale) + -# The node will be skewed (skewX, skewY) + -# The node will be scaled (scale, scaleX, scaleY) -# The node will be moved according to the camera values (camera) - + Order in transformations with grid enabled -# The node will be translated (position) -# The node will be rotated (rotation) - -# The node will be scaled (scale) + -# The node will be skewed (skewX, skewY) + -# The node will be scaled (scale, scaleX, scaleY) -# The grid will capture the screen -# The node will be moved according to the camera values (camera) -# The grid will render the captured screen - + Camera: - Each node has a camera. By default it points to the center of the CCNode. - */ + */ @interface CCNode : NSObject -{ +{ // rotation angle - float rotation_; - + float rotation_; + // scaling factors float scaleX_, scaleY_; - + + // openGL real Z vertex + float vertexZ_; + // position of the node CGPoint position_; - CGPoint positionInPixels_; - + // skew angles float skewX_, skewY_; - // is visible - BOOL visible_; - - // anchor point in pixels - CGPoint anchorPointInPixels_; - // anchor point normalized - CGPoint anchorPoint_; - // If YES the transformtions will be relative to (-transform.x, -transform.y). - // Sprites, Labels and any other "small" object uses it. - // Scenes, Layers and other "whole screen" object don't use it. - BOOL isRelativeAnchorPoint_; - + // anchor point in points + CGPoint anchorPointInPoints_; + // anchor point normalized (NOT in points) + CGPoint anchorPoint_; + // untransformed size of the node CGSize contentSize_; - CGSize contentSizeInPixels_; - + // transform CGAffineTransform transform_, inverse_; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - GLfloat transformGL_[16]; -#endif - // openGL real Z vertex - float vertexZ_; - // a Camera CCCamera *camera_; - + // a Grid CCGridBase *grid_; - + // z-order value NSInteger zOrder_; - + // array of children CCArray *children_; - + // weakref to parent CCNode *parent_; - + // a tag. any number you want to assign to the node NSInteger tag_; - + // user data field void *userData_; + id userObject_; + + // Shader + CCGLProgram *shaderProgram_; + + // Server side state + ccGLServerState glServerState_; + + // used to preserve sequence while sorting children with the same zOrder + NSUInteger orderOfArrival_; + + // scheduler used to schedule timers and updates + CCScheduler *scheduler_; + + // ActionManager used to handle all the actions + CCActionManager *actionManager_; // Is running BOOL isRunning_; - // To reduce memory, place BOOLs that are not properties here: - BOOL isTransformDirty_:1; - BOOL isInverseDirty_:1; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - BOOL isTransformGLDirty_:1; -#endif + BOOL isTransformDirty_; + BOOL isInverseDirty_; + + // is visible + BOOL visible_; + // If YES, the Anchor Point will be (0,0) when you position the CCNode. + // Used by CCLayer and CCScene + BOOL ignoreAnchorPointForPosition_; + + BOOL isReorderChildDirty_; } -/** The z order of the node relative to it's "brothers": children of the same parent */ -@property(nonatomic,readonly) NSInteger zOrder; +/** The z order of the node relative to its "siblings": children of the same parent */ +@property(nonatomic,assign) NSInteger zOrder; /** The real openGL Z vertex. Differences between openGL Z vertex and cocos2d Z order: - OpenGL Z modifies the Z vertex, and not the Z order in the relation between parent-children @@ -203,10 +215,7 @@ enum { @property(nonatomic,readwrite,assign) float scaleY; /** Position (x,y) of the node in points. (0,0) is the left-bottom corner. */ @property(nonatomic,readwrite,assign) CGPoint position; -/** Position (x,y) of the node in points. (0,0) is the left-bottom corner. */ -@property(nonatomic,readwrite,assign) CGPoint positionInPixels; -/** A CCCamera object that lets you move the node using a gluLookAt -*/ +/** A CCCamera object that lets you move the node using a gluLookAt */ @property(nonatomic,readonly) CCCamera* camera; /** Array of children */ @property(nonatomic,readonly) CCArray *children; @@ -225,7 +234,7 @@ enum { /** The anchorPoint in absolute pixels. Since v0.8 you can only read it. If you wish to modify it, use anchorPoint instead */ -@property(nonatomic,readonly) CGPoint anchorPointInPixels; +@property(nonatomic,readonly) CGPoint anchorPointInPoints; /** The untransformed size of the node in Points The contentSize remains the same no matter the node is scaled or rotated. @@ -234,26 +243,45 @@ enum { */ @property (nonatomic,readwrite) CGSize contentSize; -/** The untransformed size of the node in Pixels - The contentSize remains the same no matter the node is scaled or rotated. - All nodes has a size. Layer and Scene has the same size of the screen. - @since v0.8 - */ -@property (nonatomic,readwrite) CGSize contentSizeInPixels; - /** whether or not the node is running */ @property(nonatomic,readonly) BOOL isRunning; /** A weak reference to the parent */ @property(nonatomic,readwrite,assign) CCNode* parent; -/** If YES the transformtions will be relative to it's anchor point. - * Sprites, Labels and any other sizeble object use it have it enabled by default. - * Scenes, Layers and other "whole screen" object don't use it, have it disabled by default. +/** If YES, the Anchor Point will be (0,0) when you position the CCNode. + Used by CCLayer and CCScene. */ -@property(nonatomic,readwrite,assign) BOOL isRelativeAnchorPoint; +@property(nonatomic,readwrite,assign) BOOL ignoreAnchorPointForPosition; /** A tag used to identify the node easily */ @property(nonatomic,readwrite,assign) NSInteger tag; /** A custom user data pointer */ -@property(nonatomic,readwrite,assign) void *userData; +@property(nonatomic,readwrite,assign) void* userData; +/** Similar to userData, but instead of holding a void* it holds an id */ +@property(nonatomic,readwrite,retain) id userObject; + +/** Shader Program + @since v2.0 + */ +@property(nonatomic,readwrite,retain) CCGLProgram *shaderProgram; + +/** used internally for zOrder sorting, don't change this manually */ +@property(nonatomic,readwrite) NSUInteger orderOfArrival; + +/** GL server side state + @since v2.0 +*/ +@property (nonatomic, readwrite) ccGLServerState glServerState; + +/** CCActionManager used by all the actions. + IMPORTANT: If you set a new CCActionManager, then previously created actions are going to be removed. + @since v2.0 + */ +@property (nonatomic, readwrite, retain) CCActionManager *actionManager; + +/** CCScheduler used to schedule all "updates" and timers. + IMPORTANT: If you set a new CCScheduler, then previously created timers/update are going to be removed. + @since v2.0 + */ +@property (nonatomic, readwrite, retain) CCScheduler *scheduler; // initializators /** allocates and initializes a node. @@ -266,22 +294,31 @@ enum { // scene managment -/** callback that is called every time the CCNode enters the 'stage'. - If the CCNode enters the 'stage' with a transition, this callback is called when the transition starts. - During onEnter you can't a "sister/brother" node. +/** Event that is called every time the CCNode enters the 'stage'. + If the CCNode enters the 'stage' with a transition, this event is called when the transition starts. + During onEnter you can't access a sibling node. + If you override onEnter, you shall call [super onEnter]. */ -(void) onEnter; -/** callback that is called when the CCNode enters in the 'stage'. - If the CCNode enters the 'stage' with a transition, this callback is called when the transition finishes. + +/** Event that is called when the CCNode enters in the 'stage'. + If the CCNode enters the 'stage' with a transition, this event is called when the transition finishes. + If you override onEnterTransitionDidFinish, you shall call [super onEnterTransitionDidFinish]. @since v0.8 */ -(void) onEnterTransitionDidFinish; -/** callback that is called every time the CCNode leaves the 'stage'. - If the CCNode leaves the 'stage' with a transition, this callback is called when the transition finishes. + +/** Event that is called every time the CCNode leaves the 'stage'. + If the CCNode leaves the 'stage' with a transition, this event is called when the transition finishes. During onExit you can't access a sibling node. + If you override onExit, you shall call [super onExit]. */ -(void) onExit; +/** callback that is called every time the CCNode leaves the 'stage'. + If the CCNode leaves the 'stage' with a transition, this callback is called when the transition starts. + */ +-(void) onExitTransitionDidStart; // composition: ADD @@ -338,7 +375,14 @@ enum { */ -(void) reorderChild:(CCNode*)child z:(NSInteger)zOrder; -/** Stops all running actions and schedulers +/** performance improvement, Sort the children array once before drawing, instead of every time when a child is added or reordered + don't call this manually unless a child added needs to be removed in the same frame */ +- (void) sortAllChildren; + +/** Event that is called when the running node is no longer running (eg: its CCScene is being removed from the "stage" ). + On cleanup you should break any possible circular references. + CCNode's cleanup removes any possible scheduled timer and/or any possible action. + If you override cleanup, you shall call [super cleanup] @since v0.8 */ -(void) cleanup; @@ -346,17 +390,12 @@ enum { // draw /** Override this method to draw your own node. - The following GL states will be enabled by default: - - glEnableClientState(GL_VERTEX_ARRAY); - - glEnableClientState(GL_COLOR_ARRAY); - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - glEnable(GL_TEXTURE_2D); - - AND YOU SHOULD NOT DISABLE THEM AFTER DRAWING YOUR NODE - - But if you enable any other GL state, you should disable it after drawing your node. + You should use cocos2d's GL API to enable/disable the GL state / shaders. + For further info, please see ccGLstate.h. + You shall NOT call [super draw]; */ -(void) draw; + /** recursive method that visit its children and draw them */ -(void) visit; @@ -365,9 +404,8 @@ enum { /** performs OpenGL view-matrix transformation based on position, scale, rotation and other attributes. */ -(void) transform; -/** performs OpenGL view-matrix transformation of it's ancestors. - Generally the ancestors are already transformed, but in certain cases (eg: attaching a FBO) - it's necessary to transform the ancestors again. +/** performs OpenGL view-matrix transformation of its ancestors. + Generally the ancestors are already transformed, but in certain cases (eg: attaching a FBO) it is necessary to transform the ancestors again. @since v0.7.2 */ -(void) transformAncestors; @@ -375,20 +413,11 @@ enum { /** returns a "local" axis aligned bounding box of the node in points. The returned box is relative only to its parent. The returned box is in Points. - + @since v0.8.2 */ - (CGRect) boundingBox; -/** returns a "local" axis aligned bounding box of the node in pixels. - The returned box is relative only to its parent. - The returned box is in Points. - - @since v0.99.5 - */ -- (CGRect) boundingBoxInPixels; - - // actions /** Executes an action, and returns the action that is executed. @@ -411,7 +440,7 @@ enum { @return the Action the with the given tag */ -(CCAction*) getActionByTag:(NSInteger) tag; -/** Returns the numbers of actions that are running plus the ones that are schedule to run (actions in actionsToAdd and actions arrays). +/** Returns the numbers of actions that are running plus the ones that are schedule to run (actions in actionsToAdd and actions arrays). * Composable actions are counted as 1 action. Example: * If you are running 1 Sequence of 7 actions, it will return 1. * If you are running 7 Sequences of 2 actions, it will return 7. @@ -426,7 +455,7 @@ enum { /** schedules the "update" method. It will use the order number 0. This method will be called every frame. Scheduled methods with a lower order value will be called before the ones that have a higher order value. Only one "udpate" method could be scheduled per node. - + @since v0.99.3 */ -(void) scheduleUpdate; @@ -440,7 +469,7 @@ enum { -(void) scheduleUpdateWithPriority:(NSInteger)priority; /* unschedules the "update" method. - + @since v0.99.3 */ -(void) unscheduleUpdate; @@ -453,10 +482,21 @@ enum { /** schedules a custom selector with an interval time in seconds. If time is 0 it will be ticked every frame. If time is 0, it is recommended to use 'scheduleUpdate' instead. - + If the selector is already scheduled, then the interval parameter will be updated without scheduling it again. */ -(void) schedule: (SEL) s interval:(ccTime)seconds; +/** + repeat will execute the action repeat + 1 times, for a continues action use kCCRepeatForever + delay is the amount of time the action will wait before execution + */ +-(void) schedule:(SEL)selector interval:(ccTime)interval repeat: (uint) repeat delay:(ccTime) delay; + +/** + Schedules a selector that runs only once, with a delay of 0 or larger +*/ +- (void) scheduleOnce:(SEL) selector delay:(ccTime) delay; + /** unschedules a custom selector.*/ -(void) unschedule: (SEL) s; @@ -515,7 +555,7 @@ enum { */ - (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint; -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS /** Converts a UITouch to node (local) space coordinates. The result is in Points. @since v0.7.1 */ @@ -525,5 +565,5 @@ enum { @since v0.7.1 */ - (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch; -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS @end diff --git a/cocos2d/cocos2d/CCNode.m b/cocos2d/cocos2d/CCNode.m old mode 100644 new mode 100755 index 495ad22..a1da57f --- a/cocos2d/cocos2d/CCNode.m +++ b/cocos2d/cocos2d/CCNode.m @@ -5,17 +5,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -37,19 +37,23 @@ #import "Support/ccCArray.h" #import "Support/TransformUtils.h" #import "ccMacros.h" +#import "CCGLProgram.h" -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +// externals +#import "kazmath/GL/matrix.h" + +#ifdef __CC_PLATFORM_IOS #import "Platforms/iOS/CCDirectorIOS.h" #endif -#if CC_COCOSNODE_RENDER_SUBPIXEL +#if CC_NODE_RENDER_SUBPIXEL #define RENDER_IN_SUBPIXEL #else #define RENDER_IN_SUBPIXEL (NSInteger) #endif + @interface CCNode () // lazy allocs -(void) childrenAlloc; @@ -62,6 +66,9 @@ -(void) detachChild:(CCNode *)child cleanup:(BOOL)doCleanup; @implementation CCNode +// XXX: Yes, nodes might have a sort problem once every 15 days if the game runs at 60 FPS and each frame sprites are reordered. +static NSUInteger globalOrderOfArrival = 1; + @synthesize children = children_; @synthesize visible = visible_; @synthesize parent = parent_; @@ -70,111 +77,173 @@ @implementation CCNode @synthesize tag = tag_; @synthesize vertexZ = vertexZ_; @synthesize isRunning = isRunning_; -@synthesize userData = userData_; +@synthesize userData = userData_, userObject = userObject_; +@synthesize shaderProgram = shaderProgram_; +@synthesize orderOfArrival = orderOfArrival_; +@synthesize glServerState = glServerState_; #pragma mark CCNode - Transform related properties @synthesize rotation = rotation_, scaleX = scaleX_, scaleY = scaleY_; +@synthesize position = position_; +@synthesize anchorPoint = anchorPoint_, anchorPointInPoints = anchorPointInPoints_; +@synthesize contentSize = contentSize_; +@synthesize ignoreAnchorPointForPosition = ignoreAnchorPointForPosition_; @synthesize skewX = skewX_, skewY = skewY_; -@synthesize position = position_, positionInPixels = positionInPixels_; -@synthesize anchorPoint = anchorPoint_, anchorPointInPixels = anchorPointInPixels_; -@synthesize contentSize = contentSize_, contentSizeInPixels = contentSizeInPixels_; -@synthesize isRelativeAnchorPoint = isRelativeAnchorPoint_; -// getters synthesized, setters explicit +#pragma mark CCNode - Init & cleanup --(void) setSkewX:(float)newSkewX ++(id) node { - skewX_ = newSkewX; - isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif + return [[[self alloc] init] autorelease]; } --(void) setSkewY:(float)newSkewY +-(id) init { - skewY_ = newSkewY; - isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif + if ((self=[super init]) ) { + + isRunning_ = NO; + + skewX_ = skewY_ = 0.0f; + rotation_ = 0.0f; + scaleX_ = scaleY_ = 1.0f; + position_ = CGPointZero; + contentSize_ = CGSizeZero; + anchorPointInPoints_ = anchorPoint_ = CGPointZero; + + + // "whole screen" objects. like Scenes and Layers, should set ignoreAnchorPointForPosition to YES + ignoreAnchorPointForPosition_ = NO; + + isTransformDirty_ = isInverseDirty_ = YES; + + vertexZ_ = 0; + + grid_ = nil; + + visible_ = YES; + + tag_ = kCCNodeTagInvalid; + + zOrder_ = 0; + + // lazy alloc + camera_ = nil; + + // children (lazy allocs) + children_ = nil; + + // userData is always inited as nil + userData_ = NULL; + userObject_ = nil; + + //initialize parent to nil + parent_ = nil; + + shaderProgram_ = nil; + + orderOfArrival_ = 0; + + glServerState_ = CC_GL_BLEND; + + // set default scheduler and actionManager + CCDirector *director = [CCDirector sharedDirector]; + self.actionManager = [director actionManager]; + self.scheduler = [director scheduler]; + } + + return self; } +- (void)cleanup +{ + // actions + [self stopAllActions]; + [self unscheduleAllSelectors]; + + // timers + [children_ makeObjectsPerformSelector:@selector(cleanup)]; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %p | Tag = %ld>", [self class], self, (long)tag_]; +} + +- (void) dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@", self); + + [actionManager_ release]; + [scheduler_ release]; + [camera_ release]; + [grid_ release]; + [shaderProgram_ release]; + [userObject_ release]; + + // children + CCNode *child; + CCARRAY_FOREACH(children_, child) + child.parent = nil; + + [children_ release]; + + [super dealloc]; +} + +#pragma mark Setters + +// getters synthesized, setters explicit -(void) setRotation: (float)newRotation { rotation_ = newRotation; isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif } -(void) setScaleX: (float)newScaleX { scaleX_ = newScaleX; isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif } -(void) setScaleY: (float)newScaleY { scaleY_ = newScaleY; isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif } --(void) setPosition: (CGPoint)newPosition +-(void) setSkewX:(float)newSkewX { - position_ = newPosition; - if( CC_CONTENT_SCALE_FACTOR() == 1 ) - positionInPixels_ = position_; - else - positionInPixels_ = ccpMult( newPosition, CC_CONTENT_SCALE_FACTOR() ); - + skewX_ = newSkewX; isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif } --(void) setPositionInPixels:(CGPoint)newPosition +-(void) setSkewY:(float)newSkewY { - positionInPixels_ = newPosition; - - if( CC_CONTENT_SCALE_FACTOR() == 1 ) - position_ = positionInPixels_; - else - position_ = ccpMult( newPosition, 1/CC_CONTENT_SCALE_FACTOR() ); - + skewY_ = newSkewY; isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif } --(void) setIsRelativeAnchorPoint: (BOOL)newValue +-(void) setPosition: (CGPoint)newPosition { - isRelativeAnchorPoint_ = newValue; + position_ = newPosition; isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif +} + +-(void) setIgnoreAnchorPointForPosition: (BOOL)newValue +{ + if( newValue != ignoreAnchorPointForPosition_ ) { + ignoreAnchorPointForPosition_ = newValue; + isTransformDirty_ = isInverseDirty_ = YES; + } } -(void) setAnchorPoint:(CGPoint)point { if( ! CGPointEqualToPoint(point, anchorPoint_) ) { anchorPoint_ = point; - anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y ); + anchorPointInPoints_ = ccp( contentSize_.width * anchorPoint_.x, contentSize_.height * anchorPoint_.y ); isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif } } @@ -182,58 +251,21 @@ -(void) setContentSize:(CGSize)size { if( ! CGSizeEqualToSize(size, contentSize_) ) { contentSize_ = size; - - if( CC_CONTENT_SCALE_FACTOR() == 1 ) - contentSizeInPixels_ = contentSize_; - else - contentSizeInPixels_ = CGSizeMake( size.width * CC_CONTENT_SCALE_FACTOR(), size.height * CC_CONTENT_SCALE_FACTOR() ); - - anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y ); - isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif - } -} - --(void) setContentSizeInPixels:(CGSize)size -{ - if( ! CGSizeEqualToSize(size, contentSizeInPixels_) ) { - contentSizeInPixels_ = size; - if( CC_CONTENT_SCALE_FACTOR() == 1 ) - contentSize_ = contentSizeInPixels_; - else - contentSize_ = CGSizeMake( size.width / CC_CONTENT_SCALE_FACTOR(), size.height / CC_CONTENT_SCALE_FACTOR() ); - - anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y ); + anchorPointInPoints_ = ccp( contentSize_.width * anchorPoint_.x, contentSize_.height * anchorPoint_.y ); isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif } } - (CGRect) boundingBox { - CGRect ret = [self boundingBoxInPixels]; - return CC_RECT_PIXELS_TO_POINTS( ret ); -} - -- (CGRect) boundingBoxInPixels -{ - CGRect rect = CGRectMake(0, 0, contentSizeInPixels_.width, contentSizeInPixels_.height); + CGRect rect = CGRectMake(0, 0, contentSize_.width, contentSize_.height); return CGRectApplyAffineTransform(rect, [self nodeToParentTransform]); } -(void) setVertexZ:(float)vertexZ { - vertexZ_ = vertexZ * CC_CONTENT_SCALE_FACTOR(); -} - --(float) vertexZ -{ - return vertexZ_ / CC_CONTENT_SCALE_FACTOR(); + vertexZ_ = vertexZ; } -(float) scale @@ -246,98 +278,14 @@ -(void) setScale:(float) s { scaleX_ = scaleY_ = s; isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif -} - -#pragma mark CCNode - Init & cleanup - -+(id) node -{ - return [[[self alloc] init] autorelease]; -} - --(id) init -{ - if ((self=[super init]) ) { - - isRunning_ = NO; - - skewX_ = skewY_ = 0.0f; - rotation_ = 0.0f; - scaleX_ = scaleY_ = 1.0f; - positionInPixels_ = position_ = CGPointZero; - anchorPointInPixels_ = anchorPoint_ = CGPointZero; - contentSizeInPixels_ = contentSize_ = CGSizeZero; - - - // "whole screen" objects. like Scenes and Layers, should set isRelativeAnchorPoint to NO - isRelativeAnchorPoint_ = YES; - - isTransformDirty_ = isInverseDirty_ = YES; -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - isTransformGLDirty_ = YES; -#endif - - vertexZ_ = 0; - - grid_ = nil; - - visible_ = YES; - - tag_ = kCCNodeTagInvalid; - - zOrder_ = 0; - - // lazy alloc - camera_ = nil; - - // children (lazy allocs) - children_ = nil; - - // userData is always inited as nil - userData_ = nil; - - //initialize parent to nil - parent_ = nil; - } - - return self; } -- (void)cleanup +- (void) setZOrder:(NSInteger)zOrder { - // actions - [self stopAllActions]; - [self unscheduleAllSelectors]; - - // timers - [children_ makeObjectsPerformSelector:@selector(cleanup)]; -} + [self _setZOrder:zOrder]; -- (NSString*) description -{ - return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_]; -} - -- (void) dealloc -{ - CCLOGINFO( @"cocos2d: deallocing %@", self); - - // attributes - [camera_ release]; - - [grid_ release]; - - // children - CCNode *child; - CCARRAY_FOREACH(children_, child) - child.parent = nil; - - [children_ release]; - - [super dealloc]; + if (parent_) + [parent_ reorderChild:self z:zOrder]; } #pragma mark CCNode Composition @@ -352,23 +300,22 @@ -(CCCamera*) camera { if( ! camera_ ) { camera_ = [[CCCamera alloc] init]; - + // by default, center camera at the Sprite's anchor point - // [camera_ setCenterX:anchorPointInPixels_.x centerY:anchorPointInPixels_.y centerZ:0]; - // [camera_ setEyeX:anchorPointInPixels_.x eyeY:anchorPointInPixels_.y eyeZ:1]; - - // [camera_ setCenterX:0 centerY:0 centerZ:0]; - // [camera_ setEyeX:0 eyeY:0 eyeZ:1]; - +// [camera_ setCenterX:anchorPointInPoints_.x centerY:anchorPointInPoints_.y centerZ:0]; +// [camera_ setEyeX:anchorPointInPoints_.x eyeY:anchorPointInPoints_.y eyeZ:1]; + +// [camera_ setCenterX:0 centerY:0 centerZ:0]; +// [camera_ setEyeX:0 eyeY:0 eyeZ:1]; } - + return camera_; } -(CCNode*) getChildByTag:(NSInteger) aTag { NSAssert( aTag != kCCNodeTagInvalid, @"Invalid tag"); - + CCNode *node; CCARRAY_FOREACH(children_, node){ if( node.tag == aTag ) @@ -383,19 +330,21 @@ -(CCNode*) getChildByTag:(NSInteger) aTag * to override this method */ -(void) addChild: (CCNode*) child z:(NSInteger)z tag:(NSInteger) aTag -{ +{ NSAssert( child != nil, @"Argument must be non-nil"); NSAssert( child.parent == nil, @"child already added. It can't be added again"); - + if( ! children_ ) [self childrenAlloc]; - + [self insertChild:child z:z]; - + child.tag = aTag; - + [child setParent: self]; - + + [child setOrderOfArrival: globalOrderOfArrival++]; + if( isRunning_ ) { [child onEnter]; [child onEnterTransitionDidFinish]; @@ -428,7 +377,7 @@ -(void) removeChild: (CCNode*)child cleanup:(BOOL)cleanup // explicit nil handling if (child == nil) return; - + if ( [children_ containsObject:child] ) [self detachChild:child cleanup:cleanup]; } @@ -436,9 +385,9 @@ -(void) removeChild: (CCNode*)child cleanup:(BOOL)cleanup -(void) removeChildByTag:(NSInteger)aTag cleanup:(BOOL)cleanup { NSAssert( aTag != kCCNodeTagInvalid, @"Invalid tag"); - + CCNode *child = [self getChildByTag:aTag]; - + if (child == nil) CCLOG(@"cocos2d: removeChildByTag: child not found!"); else @@ -455,15 +404,18 @@ -(void) removeAllChildrenWithCleanup:(BOOL)cleanup // -1st do onExit // -2nd cleanup if (isRunning_) + { + [c onExitTransitionDidStart]; [c onExit]; - + } + if (cleanup) [c cleanup]; - + // set parent nil at the end (issue #476) [c setParent:nil]; } - + [children_ removeAllObjects]; } @@ -473,16 +425,19 @@ -(void) detachChild:(CCNode *)child cleanup:(BOOL)doCleanup // -1st do onExit // -2nd cleanup if (isRunning_) + { + [child onExitTransitionDidStart]; [child onExit]; - + } + // If you don't do cleanup, the child's actions will not get removed and the // its scheduledSelectors_ dict will not get released! if (doCleanup) [child cleanup]; - + // set parent nil at the end (issue #476) [child setParent:nil]; - + [children_ removeObject:child]; } @@ -495,67 +450,77 @@ -(void) _setZOrder:(NSInteger) z // helper used by reorderChild & add -(void) insertChild:(CCNode*)child z:(NSInteger)z { - NSUInteger index=0; - CCNode *a = [children_ lastObject]; - - // quick comparison to improve performance - if (!a || a.zOrder <= z) - [children_ addObject:child]; - - else - { - CCARRAY_FOREACH(children_, a) { - if ( a.zOrder > z ) { - [children_ insertObject:child atIndex:index]; - break; - } - index++; - } - } - + isReorderChildDirty_=YES; + + ccArrayAppendObjectWithResize(children_->data, child); [child _setZOrder:z]; } -(void) reorderChild:(CCNode*) child z:(NSInteger)z { NSAssert( child != nil, @"Child must be non-nil"); - - [child retain]; - [children_ removeObject:child]; - - [self insertChild:child z:z]; - - [child release]; + + isReorderChildDirty_ = YES; + + [child setOrderOfArrival: globalOrderOfArrival++]; + [child _setZOrder:z]; +} + +- (void) sortAllChildren +{ + if (isReorderChildDirty_) + { + NSInteger i,j,length = children_->data->num; + CCNode ** x = children_->data->arr; + CCNode *tempItem; + + // insertion sort + for(i=1; i=0 && ( tempItem.zOrder < x[j].zOrder || ( tempItem.zOrder== x[j].zOrder && tempItem.orderOfArrival < x[j].orderOfArrival ) ) ) + { + x[j+1] = x[j]; + j = j-1; + } + x[j+1] = tempItem; + } + + //don't need to check children recursively, that's done in visit of each child + + isReorderChildDirty_ = NO; + } } #pragma mark CCNode Draw -(void) draw { - // override me - // Only use this function to draw your staff. - // DON'T draw your stuff outside this method } -(void) visit { - // quick return if not visible + // quick return if not visible. children won't be drawn. if (!visible_) return; - - glPushMatrix(); - - if ( grid_ && grid_.active) { + + kmGLPushMatrix(); + + if ( grid_ && grid_.active) [grid_ beforeDraw]; - [self transformAncestors]; - } [self transform]; - + if(children_) { + + [self sortAllChildren]; + ccArray *arrayData = children_->data; NSUInteger i = 0; - + // draw children zOrder < 0 for( ; i < arrayData->num; i++ ) { CCNode *child = arrayData->arr[i]; @@ -564,10 +529,10 @@ -(void) visit else break; } - + // self draw [self draw]; - + // draw children zOrder >= 0 for( ; i < arrayData->num; i++ ) { CCNode *child = arrayData->arr[i]; @@ -576,11 +541,14 @@ -(void) visit } else [self draw]; - + + // reset for next frame + orderOfArrival_ = 0; + if ( grid_ && grid_.active) [grid_ afterDraw:self]; - - glPopMatrix(); + + kmGLPopMatrix(); } #pragma mark CCNode - Transformations @@ -594,87 +562,41 @@ -(void) transformAncestors } -(void) transform -{ - // transformations - -#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - // BEGIN alternative -- using cached transform - // - if( isTransformGLDirty_ ) { - CGAffineTransform t = [self nodeToParentTransform]; - CGAffineToGL(&t, transformGL_); - isTransformGLDirty_ = NO; - } - - glMultMatrixf(transformGL_); - if( vertexZ_ ) - glTranslatef(0, 0, vertexZ_); - +{ + kmMat4 transfrom4x4; + + // Convert 3x3 into 4x4 matrix + CGAffineTransform tmpAffine = [self nodeToParentTransform]; + CGAffineToGL(&tmpAffine, transfrom4x4.mat); + + // Update Z vertex manually + transfrom4x4.mat[14] = vertexZ_; + + kmGLMultMatrix( &transfrom4x4 ); + + // XXX: Expensive calls. Camera should be integrated into the cached affine matrix if ( camera_ && !(grid_ && grid_.active) ) { - BOOL translate = (anchorPointInPixels_.x != 0.0f || anchorPointInPixels_.y != 0.0f); - + BOOL translate = (anchorPointInPoints_.x != 0.0f || anchorPointInPoints_.y != 0.0f); + if( translate ) - ccglTranslate(RENDER_IN_SUBPIXEL(anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(anchorPointInPixels_.y), 0); - + kmGLTranslatef(RENDER_IN_SUBPIXEL(anchorPointInPoints_.x), RENDER_IN_SUBPIXEL(anchorPointInPoints_.y), 0 ); + [camera_ locate]; - + if( translate ) - ccglTranslate(RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0); + kmGLTranslatef(RENDER_IN_SUBPIXEL(-anchorPointInPoints_.x), RENDER_IN_SUBPIXEL(-anchorPointInPoints_.y), 0 ); } - - - // END alternative - -#else - // BEGIN original implementation - // - // translate - if ( isRelativeAnchorPoint_ && (anchorPointInPixels_.x != 0 || anchorPointInPixels_.y != 0 ) ) - glTranslatef( RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0); - - if (anchorPointInPixels_.x != 0 || anchorPointInPixels_.y != 0) - glTranslatef( RENDER_IN_SUBPIXEL(positionInPixels_.x + anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(positionInPixels_.y + anchorPointInPixels_.y), vertexZ_); - else if ( positionInPixels_.x !=0 || positionInPixels_.y !=0 || vertexZ_ != 0) - glTranslatef( RENDER_IN_SUBPIXEL(positionInPixels_.x), RENDER_IN_SUBPIXEL(positionInPixels_.y), vertexZ_ ); - - // rotate - if (rotation_ != 0.0f ) - glRotatef( -rotation_, 0.0f, 0.0f, 1.0f ); - - // skew - if ( (skewX_ != 0.0f) || (skewY_ != 0.0f) ) { - CGAffineTransform skewMatrix = CGAffineTransformMake( 1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, 0.0f, 0.0f ); - GLfloat glMatrix[16]; - CGAffineToGL(&skewMatrix, glMatrix); - glMultMatrixf(glMatrix); - } - - // scale - if (scaleX_ != 1.0f || scaleY_ != 1.0f) - glScalef( scaleX_, scaleY_, 1.0f ); - - if ( camera_ && !(grid_ && grid_.active) ) - [camera_ locate]; - - // restore and re-position point - if (anchorPointInPixels_.x != 0.0f || anchorPointInPixels_.y != 0.0f) - glTranslatef(RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0); - - // - // END original implementation -#endif - } #pragma mark CCNode SceneManagement -(void) onEnter { - [children_ makeObjectsPerformSelector:@selector(onEnter)]; + [children_ makeObjectsPerformSelector:@selector(onEnter)]; [self resumeSchedulerAndActions]; - + isRunning_ = YES; } @@ -683,53 +605,88 @@ -(void) onEnterTransitionDidFinish [children_ makeObjectsPerformSelector:@selector(onEnterTransitionDidFinish)]; } +-(void) onExitTransitionDidStart +{ + [children_ makeObjectsPerformSelector:@selector(onExitTransitionDidStart)]; +} + -(void) onExit { [self pauseSchedulerAndActions]; - isRunning_ = NO; - + isRunning_ = NO; + [children_ makeObjectsPerformSelector:@selector(onExit)]; } #pragma mark CCNode Actions +-(void) setActionManager:(CCActionManager *)actionManager +{ + if( actionManager != actionManager_ ) { + [self stopAllActions]; + [actionManager_ release]; + + actionManager_ = [actionManager retain]; + } +} + +-(CCActionManager*) actionManager +{ + return actionManager_; +} + -(CCAction*) runAction:(CCAction*) action { NSAssert( action != nil, @"Argument must be non-nil"); - - [[CCActionManager sharedManager] addAction:action target:self paused:!isRunning_]; + + [actionManager_ addAction:action target:self paused:!isRunning_]; return action; } -(void) stopAllActions { - [[CCActionManager sharedManager] removeAllActionsFromTarget:self]; + [actionManager_ removeAllActionsFromTarget:self]; } -(void) stopAction: (CCAction*) action { - [[CCActionManager sharedManager] removeAction:action]; + [actionManager_ removeAction:action]; } -(void) stopActionByTag:(NSInteger)aTag { NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag"); - [[CCActionManager sharedManager] removeActionByTag:aTag target:self]; + [actionManager_ removeActionByTag:aTag target:self]; } -(CCAction*) getActionByTag:(NSInteger) aTag { NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag"); - return [[CCActionManager sharedManager] getActionByTag:aTag target:self]; + return [actionManager_ getActionByTag:aTag target:self]; } -(NSUInteger) numberOfRunningActions { - return [[CCActionManager sharedManager] numberOfRunningActionsInTarget:self]; + return [actionManager_ numberOfRunningActionsInTarget:self]; } #pragma mark CCNode - Scheduler +-(void) setScheduler:(CCScheduler *)scheduler +{ + if( scheduler != scheduler_ ) { + [self unscheduleAllSelectors]; + [scheduler_ release]; + + scheduler_ = [scheduler retain]; + } +} + +-(CCScheduler*) scheduler +{ + return scheduler_; +} + -(void) scheduleUpdate { [self scheduleUpdateWithPriority:0]; @@ -737,25 +694,35 @@ -(void) scheduleUpdate -(void) scheduleUpdateWithPriority:(NSInteger)priority { - [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:priority paused:!isRunning_]; + [scheduler_ scheduleUpdateForTarget:self priority:priority paused:!isRunning_]; } -(void) unscheduleUpdate { - [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self]; + [scheduler_ unscheduleUpdateForTarget:self]; } -(void) schedule:(SEL)selector { - [self schedule:selector interval:0]; + [self schedule:selector interval:0 repeat:kCCRepeatForever delay:0]; } -(void) schedule:(SEL)selector interval:(ccTime)interval +{ + [self schedule:selector interval:interval repeat:kCCRepeatForever delay:0]; +} + +-(void) schedule:(SEL)selector interval:(ccTime)interval repeat: (uint) repeat delay:(ccTime) delay { NSAssert( selector != nil, @"Argument must be non-nil"); NSAssert( interval >=0, @"Arguemnt must be positive"); - - [[CCScheduler sharedScheduler] scheduleSelector:selector forTarget:self interval:interval paused:!isRunning_]; + + [scheduler_ scheduleSelector:selector forTarget:self interval:interval paused:!isRunning_ repeat:repeat delay:delay]; +} + +- (void) scheduleOnce:(SEL) selector delay:(ccTime) delay +{ + [self schedule:selector interval:0.f repeat:0 delay:delay]; } -(void) unschedule:(SEL)selector @@ -763,24 +730,24 @@ -(void) unschedule:(SEL)selector // explicit nil handling if (selector == nil) return; - - [[CCScheduler sharedScheduler] unscheduleSelector:selector forTarget:self]; + + [scheduler_ unscheduleSelector:selector forTarget:self]; } -(void) unscheduleAllSelectors { - [[CCScheduler sharedScheduler] unscheduleAllSelectorsForTarget:self]; + [scheduler_ unscheduleAllSelectorsForTarget:self]; } - (void) resumeSchedulerAndActions { - [[CCScheduler sharedScheduler] resumeTarget:self]; - [[CCActionManager sharedManager] resumeTarget:self]; + [scheduler_ resumeTarget:self]; + [actionManager_ resumeTarget:self]; } - (void) pauseSchedulerAndActions { - [[CCScheduler sharedScheduler] pauseTarget:self]; - [[CCActionManager sharedManager] pauseTarget:self]; + [scheduler_ pauseTarget:self]; + [actionManager_ pauseTarget:self]; } #pragma mark CCNode Transform @@ -788,34 +755,56 @@ - (void) pauseSchedulerAndActions - (CGAffineTransform)nodeToParentTransform { if ( isTransformDirty_ ) { - - transform_ = CGAffineTransformIdentity; - - if ( !isRelativeAnchorPoint_ && !CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) ) - transform_ = CGAffineTransformTranslate(transform_, anchorPointInPixels_.x, anchorPointInPixels_.y); - if( ! CGPointEqualToPoint(positionInPixels_, CGPointZero) ) - transform_ = CGAffineTransformTranslate(transform_, positionInPixels_.x, positionInPixels_.y); - - if( rotation_ != 0 ) - transform_ = CGAffineTransformRotate(transform_, -CC_DEGREES_TO_RADIANS(rotation_)); - - if( skewX_ != 0 || skewY_ != 0 ) { - // create a skewed coordinate system - CGAffineTransform skew = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, 0.0f, 0.0f); - // apply the skew to the transform - transform_ = CGAffineTransformConcat(skew, transform_); + // Translate values + float x = position_.x; + float y = position_.y; + + if ( ignoreAnchorPointForPosition_ ) { + x += anchorPointInPoints_.x; + y += anchorPointInPoints_.y; } - - if( ! (scaleX_ == 1 && scaleY_ == 1) ) - transform_ = CGAffineTransformScale(transform_, scaleX_, scaleY_); - - if( ! CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) ) - transform_ = CGAffineTransformTranslate(transform_, -anchorPointInPixels_.x, -anchorPointInPixels_.y); - + + // Rotation values + float c = 1, s = 0; + if( rotation_ ) { + float radians = -CC_DEGREES_TO_RADIANS(rotation_); + c = cosf(radians); + s = sinf(radians); + } + + BOOL needsSkewMatrix = ( skewX_ || skewY_ ); + + + // optimization: + // inline anchor point calculation if skew is not needed + if( !needsSkewMatrix && !CGPointEqualToPoint(anchorPointInPoints_, CGPointZero) ) { + x += c * -anchorPointInPoints_.x * scaleX_ + -s * -anchorPointInPoints_.y * scaleY_; + y += s * -anchorPointInPoints_.x * scaleX_ + c * -anchorPointInPoints_.y * scaleY_; + } + + + // Build Transform Matrix + transform_ = CGAffineTransformMake( c * scaleX_, s * scaleX_, + -s * scaleY_, c * scaleY_, + x, y ); + + // XXX: Try to inline skew + // If skew is needed, apply skew and then anchor point + if( needsSkewMatrix ) { + CGAffineTransform skewMatrix = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), + tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, + 0.0f, 0.0f ); + transform_ = CGAffineTransformConcat(skewMatrix, transform_); + + // adjust anchor point + if( ! CGPointEqualToPoint(anchorPointInPoints_, CGPointZero) ) + transform_ = CGAffineTransformTranslate(transform_, -anchorPointInPoints_.x, -anchorPointInPoints_.y); + } + isTransformDirty_ = NO; } - + return transform_; } @@ -825,17 +814,17 @@ - (CGAffineTransform)parentToNodeTransform inverse_ = CGAffineTransformInvert([self nodeToParentTransform]); isInverseDirty_ = NO; } - + return inverse_; } - (CGAffineTransform)nodeToWorldTransform { CGAffineTransform t = [self nodeToParentTransform]; - + for (CCNode *p = parent_; p != nil; p = p.parent) t = CGAffineTransformConcat(t, [p nodeToParentTransform]); - + return t; } @@ -846,53 +835,25 @@ - (CGAffineTransform)worldToNodeTransform - (CGPoint)convertToNodeSpace:(CGPoint)worldPoint { - CGPoint ret; - if( CC_CONTENT_SCALE_FACTOR() == 1 ) - ret = CGPointApplyAffineTransform(worldPoint, [self worldToNodeTransform]); - else { - ret = ccpMult( worldPoint, CC_CONTENT_SCALE_FACTOR() ); - ret = CGPointApplyAffineTransform(ret, [self worldToNodeTransform]); - ret = ccpMult( ret, 1/CC_CONTENT_SCALE_FACTOR() ); - } - + CGPoint ret = CGPointApplyAffineTransform(worldPoint, [self worldToNodeTransform]); return ret; } - (CGPoint)convertToWorldSpace:(CGPoint)nodePoint { - CGPoint ret; - if( CC_CONTENT_SCALE_FACTOR() == 1 ) - ret = CGPointApplyAffineTransform(nodePoint, [self nodeToWorldTransform]); - else { - ret = ccpMult( nodePoint, CC_CONTENT_SCALE_FACTOR() ); - ret = CGPointApplyAffineTransform(ret, [self nodeToWorldTransform]); - ret = ccpMult( ret, 1/CC_CONTENT_SCALE_FACTOR() ); - } - + CGPoint ret = CGPointApplyAffineTransform(nodePoint, [self nodeToWorldTransform]); return ret; } - (CGPoint)convertToNodeSpaceAR:(CGPoint)worldPoint { CGPoint nodePoint = [self convertToNodeSpace:worldPoint]; - CGPoint anchorInPoints; - if( CC_CONTENT_SCALE_FACTOR() == 1 ) - anchorInPoints = anchorPointInPixels_; - else - anchorInPoints = ccpMult( anchorPointInPixels_, 1/CC_CONTENT_SCALE_FACTOR() ); - - return ccpSub(nodePoint, anchorInPoints); + return ccpSub(nodePoint, anchorPointInPoints_); } - (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint { - CGPoint anchorInPoints; - if( CC_CONTENT_SCALE_FACTOR() == 1 ) - anchorInPoints = anchorPointInPixels_; - else - anchorInPoints = ccpMult( anchorPointInPixels_, 1/CC_CONTENT_SCALE_FACTOR() ); - - nodePoint = ccpAdd(nodePoint, anchorInPoints); + nodePoint = ccpAdd(nodePoint, anchorPointInPoints_); return [self convertToWorldSpace:nodePoint]; } @@ -904,7 +865,7 @@ - (CGPoint)convertToWindowSpace:(CGPoint)nodePoint // convenience methods which take a UITouch instead of CGPoint -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS - (CGPoint)convertTouchToNodeSpace:(UITouch *)touch { @@ -920,7 +881,7 @@ - (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch return [self convertToNodeSpaceAR:point]; } -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS @end diff --git a/cocos2d/cocos2d/CCParallaxNode.h b/cocos2d/cocos2d/CCParallaxNode.h old mode 100644 new mode 100755 index 5728eb1..9f3eac3 --- a/cocos2d/cocos2d/CCParallaxNode.h +++ b/cocos2d/cocos2d/CCParallaxNode.h @@ -3,17 +3,17 @@ * * Copyright (c) 2009-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,9 +28,9 @@ #import "Support/ccCArray.h" /** CCParallaxNode: A node that simulates a parallax scroller - + The children will be moved faster / slower than the parent according the the parallax ratio. - + */ @interface CCParallaxNode : CCNode { diff --git a/cocos2d/cocos2d/CCParallaxNode.m b/cocos2d/cocos2d/CCParallaxNode.m old mode 100644 new mode 100755 index d4cda39..d30486c --- a/cocos2d/cocos2d/CCParallaxNode.m +++ b/cocos2d/cocos2d/CCParallaxNode.m @@ -3,17 +3,17 @@ * * Copyright (c) 2009-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -66,7 +66,7 @@ @implementation CCParallaxNode -(id) init { if( (self=[super init]) ) { - parallaxArray_ = ccArrayNew(5); + parallaxArray_ = ccArrayNew(5); lastPosition = CGPointMake(-100,-100); } return self; @@ -92,12 +92,12 @@ -(void) addChild: (CCNode*) child z:(NSInteger)z parallaxRatio:(CGPoint)ratio po CGPointObject *obj = [CGPointObject pointWithCGPoint:ratio offset:offset]; obj.child = child; ccArrayAppendObjectWithResize(parallaxArray_, obj); - + CGPoint pos = self.position; pos.x = pos.x * ratio.x + offset.x; pos.y = pos.y * ratio.y + offset.y; child.position = pos; - + [super addChild: child z:z tag:child.tag]; } @@ -122,21 +122,21 @@ -(void) removeAllChildrenWithCleanup:(BOOL)cleanup -(CGPoint) absolutePosition_ { CGPoint ret = position_; - + CCNode *cn = self; - + while (cn.parent != nil) { cn = cn.parent; ret = ccpAdd( ret, cn.position ); } - + return ret; } /* The positions are updated at visit because: - using a timer is not guaranteed that it will called after all the positions were updated - - overriding "draw" will only precise if the children have a z > 0 + - overriding "draw" will only be precise if the children have a z > 0 */ -(void) visit { @@ -144,18 +144,18 @@ -(void) visit // CGPoint pos = [self convertToWorldSpace:CGPointZero]; CGPoint pos = [self absolutePosition_]; if( ! CGPointEqualToPoint(pos, lastPosition) ) { - + for(unsigned int i=0; i < parallaxArray_->num; i++ ) { CGPointObject *point = parallaxArray_->arr[i]; float x = -pos.x + pos.x * point.ratio.x + point.offset.x; - float y = -pos.y + pos.y * point.ratio.y + point.offset.y; + float y = -pos.y + pos.y * point.ratio.y + point.offset.y; point.child.position = ccp(x,y); } - + lastPosition = pos; } - + [super visit]; } @end diff --git a/cocos2d/cocos2d/CCParticleBatchNode.h b/cocos2d/cocos2d/CCParticleBatchNode.h new file mode 100755 index 0000000..ec5e9bf --- /dev/null +++ b/cocos2d/cocos2d/CCParticleBatchNode.h @@ -0,0 +1,99 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Matt Oswald + * + * Copyright (c) 2009-2010 Ricardo Quesada + * Copyright (c) 2011 Zynga Inc. + * + * Copyright (c) 2011 Marco Tillemans + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#import "CCNode.h" +@class CCTextureAtlas; +@class CCParticleSystem; + +//don't use lazy sorting for particle systems +@interface CCNode (extension) +-(void) setZOrder:(NSUInteger) z; +@end + +/** CCParticleBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call + * (often known as "batch draw"). + * + * A CCParticleBatchNode can reference one and only one texture (one image file, one texture atlas). + * Only the CCParticleSystems that are contained in that texture can be added to the CCSpriteBatchNode. + * All CCParticleSystems added to a CCSpriteBatchNode are drawn in one OpenGL ES draw call. + * If the CCParticleSystems are not added to a CCParticleBatchNode then an OpenGL ES draw call will be needed for each one, which is less efficient. + * + * + * Limitations: + * - At the moment only CCParticleSystemQuad is supported + * - All systems need to be drawn with the same parameters, blend function, aliasing, texture + * + * Most efficient usage + * - Initialize the ParticleBatchNode with the texture and enough capacity for all the particle systems + * - Initialize all particle systems and add them as child to the batch node + * @since v1.1 + */ + +@interface CCParticleBatchNode : CCNode { + + CCTextureAtlas *textureAtlas_; + ccBlendFunc blendFunc_; +} + +/** the texture atlas used for drawing the quads */ +@property (nonatomic, retain) CCTextureAtlas* textureAtlas; +/** the blend function used for drawing the quads */ +@property (nonatomic, readwrite) ccBlendFunc blendFunc; + +/** initializes the particle system with CCTexture2D, a default capacity of 500 */ ++(id)batchNodeWithTexture:(CCTexture2D *)tex; + +/** initializes the particle system with the name of a file on disk (for a list of supported formats look at the CCTexture2D class), a default capacity of 500 particles */ ++(id)batchNodeWithFile:(NSString*) imageFile; + +/** initializes the particle system with CCTexture2D, a capacity of particles, which particle system to use */ ++(id)batchNodeWithTexture:(CCTexture2D *)tex capacity:(NSUInteger) capacity; + +/** initializes the particle system with the name of a file on disk (for a list of supported formats look at the CCTexture2D class), a capacity of particles */ ++(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity; + +/** initializes the particle system with CCTexture2D, a capacity of particles */ +-(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity; + +/** initializes the particle system with the name of a file on disk (for a list of supported formats look at the CCTexture2D class), a capacity of particles */ +-(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity; + +/** Add a child into the CCParticleBatchNode */ +-(void) addChild:(CCParticleSystem*)child z:(NSInteger)z tag:(NSInteger) aTag; + +/** Inserts a child into the CCParticleBatchNode */ +-(void) insertChild:(CCParticleSystem*) pSystem inAtlasAtIndex:(NSUInteger)index; + +/** remove child from the CCParticleBatchNode */ +-(void) removeChild:(CCParticleSystem*) pSystem cleanup:(BOOL)doCleanUp; + +/** disables a particle by inserting a 0'd quad into the texture atlas */ +-(void) disableParticle:(NSUInteger) particleIndex; +@end diff --git a/cocos2d/cocos2d/CCParticleBatchNode.m b/cocos2d/cocos2d/CCParticleBatchNode.m new file mode 100755 index 0000000..dcdad1a --- /dev/null +++ b/cocos2d/cocos2d/CCParticleBatchNode.m @@ -0,0 +1,473 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (C) 2009 Matt Oswald + * + * Copyright (c) 2009-2010 Ricardo Quesada + * Copyright (c) 2011 Zynga Inc. + * + * Copyright (c) 2011 Marco Tillemans + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#import "CCParticleBatchNode.h" +#import "CCTextureCache.h" +#import "CCTextureAtlas.h" +#import "ccConfig.h" +#import "ccMacros.h" +#import "CCGrid.h" +#import "Support/CGPointExtension.h" +#import "CCParticleSystem.h" +#import "CCParticleSystem.h" +#import "CCShaderCache.h" +#import "CCGLProgram.h" +#import "ccGLStateCache.h" + +#import "Support/base64.h" +#import "Support/ZipUtils.h" +#import "Support/CCFileUtils.h" + +#import "kazmath/GL/matrix.h" + +#define kCCParticleDefaultCapacity 500 + +@interface CCNode() +-(void) _setZOrder:(NSInteger)z; +@end + +@interface CCParticleBatchNode (private) +-(void) updateAllAtlasIndexes; +-(void) increaseAtlasCapacityTo:(NSUInteger) quantity; +-(NSUInteger) searchNewPositionInChildrenForZ:(NSInteger)z; +-(void) getCurrentIndex:(NSUInteger*)oldIndex newIndex:(NSUInteger*)newIndex forChild:(CCNode*)child z:(NSInteger)z; +-(NSUInteger) addChildHelper: (CCNode*) child z:(NSInteger)z tag:(NSInteger) aTag; +@end + +@implementation CCParticleBatchNode + +@synthesize textureAtlas = textureAtlas_; +@synthesize blendFunc = blendFunc_; + +/* + * creation with CCTexture2D + */ ++(id)batchNodeWithTexture:(CCTexture2D *)tex +{ + return [[[self alloc] initWithTexture:tex capacity:kCCParticleDefaultCapacity] autorelease]; +} + ++(id)batchNodeWithTexture:(CCTexture2D *)tex capacity:(NSUInteger) capacity +{ + return [[[self alloc] initWithTexture:tex capacity:capacity] autorelease]; +} + +/* + * creation with File Image + */ ++(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity +{ + return [[[self alloc] initWithFile:fileImage capacity:capacity] autorelease]; +} + ++(id)batchNodeWithFile:(NSString*) imageFile +{ + return [[[self alloc] initWithFile:imageFile capacity:kCCParticleDefaultCapacity] autorelease]; +} + +/* + * init with CCTexture2D + */ +-(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity +{ + if (self = [super init]) + { + textureAtlas_ = [[CCTextureAtlas alloc] initWithTexture:tex capacity:capacity]; + + // no lazy alloc in this node + children_ = [[CCArray alloc] initWithCapacity:capacity]; + + blendFunc_.src = CC_BLEND_SRC; + blendFunc_.dst = CC_BLEND_DST; + + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTextureColor]; + } + + return self; +} + +/* + * init with FileImage + */ +-(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity +{ + CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:fileImage]; + return [self initWithTexture:tex capacity:capacity]; +} + +-(NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %p | Tag = %ld>", [self class], self, (long)tag_ ]; +} + +-(void)dealloc +{ + [textureAtlas_ release]; + [super dealloc]; +} + +#pragma mark CCParticleBatchNode - composition + +// override visit. +// Don't call visit on it's children +-(void) visit +{ + // CAREFUL: + // This visit is almost identical to CCNode#visit + // with the exception that it doesn't call visit on it's children + // + // The alternative is to have a void CCSprite#visit, but + // although this is less mantainable, is faster + // + if (!visible_) + return; + + kmGLPushMatrix(); + + if ( grid_ && grid_.active) { + [grid_ beforeDraw]; + [self transformAncestors]; + } + + [self transform]; + + [self draw]; + + if ( grid_ && grid_.active) + [grid_ afterDraw:self]; + + kmGLPopMatrix(); +} + +// override addChild: +-(void) addChild:(CCParticleSystem*)child z:(NSInteger)z tag:(NSInteger) aTag +{ + NSAssert( child != nil, @"Argument must be non-nil"); + NSAssert( [child isKindOfClass:[CCParticleSystem class]], @"CCParticleBatchNode only supports CCQuadParticleSystems as children"); + NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCParticleSystem is not using the same texture id"); + + // If this is the 1st children, then copy blending function + if( [children_ count] == 0 ) + blendFunc_ = [child blendFunc]; + + NSAssert( blendFunc_.src == child.blendFunc.src && blendFunc_.dst == child.blendFunc.dst, @"Can't add a PaticleSystem that uses a differnt blending function"); + + //no lazy sorting, so don't call super addChild, call helper instead + NSUInteger pos = [self addChildHelper:child z:z tag:aTag]; + + //get new atlasIndex + NSUInteger atlasIndex; + + if (pos != 0) + atlasIndex = [[children_ objectAtIndex:pos-1] atlasIndex] + [[children_ objectAtIndex:pos-1] totalParticles]; + else + atlasIndex = 0; + + [self insertChild:child inAtlasAtIndex:atlasIndex]; + + // update quad info + [child setBatchNode:self]; +} + +// don't use lazy sorting, reordering the particle systems quads afterwards would be too complex +// XXX research whether lazy sorting + freeing current quads and calloc a new block with size of capacity would be faster +// XXX or possibly using vertexZ for reordering, that would be fastest +// this helper is almost equivalent to CCNode's addChild, but doesn't make use of the lazy sorting +-(NSUInteger) addChildHelper: (CCNode*) child z:(NSInteger)z tag:(NSInteger) aTag +{ + NSAssert( child != nil, @"Argument must be non-nil"); + NSAssert( child.parent == nil, @"child already added. It can't be added again"); + + if( ! children_ ) + children_ = [[CCArray alloc] initWithCapacity:4]; + + //don't use a lazy insert + NSUInteger pos = [self searchNewPositionInChildrenForZ:z]; + + [children_ insertObject:child atIndex:pos]; + + child.tag = aTag; + [child _setZOrder:z]; + + [child setParent: self]; + + if( isRunning_ ) { + [child onEnter]; + [child onEnterTransitionDidFinish]; + } + return pos; +} + +// Reorder will be done in this function, no "lazy" reorder to particles +-(void) reorderChild:(CCParticleSystem*)child z:(NSInteger)z +{ + NSAssert( child != nil, @"Child must be non-nil"); + NSAssert( [children_ containsObject:child], @"Child doesn't belong to batch" ); + + if( z == child.zOrder ) + return; + + // no reordering if only 1 child + if( [children_ count] > 1) + { + NSUInteger newIndex, oldIndex; + + [self getCurrentIndex:&oldIndex newIndex:&newIndex forChild:child z:z]; + + if( oldIndex != newIndex ) { + + // reorder children_ array + [child retain]; + [children_ removeObjectAtIndex:oldIndex]; + [children_ insertObject:child atIndex:newIndex]; + [child release]; + + // save old altasIndex + NSUInteger oldAtlasIndex = child.atlasIndex; + + // update atlas index + [self updateAllAtlasIndexes]; + + // Find new AtlasIndex + NSUInteger newAtlasIndex = 0; + for( NSUInteger i=0;i < [children_ count];i++) { + CCParticleSystem *node = [children_ objectAtIndex:i]; + if( node == child ) { + newAtlasIndex = [child atlasIndex]; + break; + } + } + + // reorder textureAtlas quads + [textureAtlas_ moveQuadsFromIndex:oldAtlasIndex amount:child.totalParticles atIndex:newAtlasIndex]; + + [child updateWithNoTime]; + } + } + + [child _setZOrder:z]; +} + +-(void) getCurrentIndex:(NSUInteger*)oldIndex newIndex:(NSUInteger*)newIndex forChild:(CCNode*)child z:(NSInteger)z +{ + BOOL foundCurrentIdx = NO; + BOOL foundNewIdx = NO; + + NSInteger minusOne = 0; + NSUInteger count = [children_ count]; + + for( NSUInteger i=0; i < count; i++ ) { + + CCNode *node = [children_ objectAtIndex:i]; + + // new index + if( node.zOrder > z && ! foundNewIdx ) { + *newIndex = i; + foundNewIdx = YES; + + if( foundCurrentIdx && foundNewIdx ) + break; + } + + // current index + if( child == node ) { + *oldIndex = i; + foundCurrentIdx = YES; + + if( ! foundNewIdx ) + minusOne = -1; + + if( foundCurrentIdx && foundNewIdx ) + break; + + } + + } + + if( ! foundNewIdx ) + *newIndex = count; + + *newIndex += minusOne; +} + +-(NSUInteger) searchNewPositionInChildrenForZ: (NSInteger) z +{ + NSUInteger count = [children_ count]; + + for( NSUInteger i=0; i < count; i++ ) { + CCNode *child = [children_ objectAtIndex:i]; + if (child.zOrder > z) + return i; + } + return count; +} + +// override removeChild: +-(void)removeChild: (CCParticleSystem*) child cleanup:(BOOL)doCleanup +{ + // explicit nil handling + if (child == nil) + return; + + NSAssert([children_ containsObject:child], @"CCParticleBatchNode doesn't contain the sprite. Can't remove it"); + + [super removeChild:child cleanup:doCleanup]; + + // remove child helper + [textureAtlas_ removeQuadsAtIndex:child.atlasIndex amount:child.totalParticles]; + + // after memmove of data, empty the quads at the end of array + [textureAtlas_ fillWithEmptyQuadsFromIndex:textureAtlas_.totalQuads amount:child.totalParticles]; + + // paticle could be reused for self rendering + [child setBatchNode:nil]; + + [self updateAllAtlasIndexes]; +} + +-(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL) doCleanup +{ + [self removeChild:(CCParticleSystem *)[children_ objectAtIndex:index] cleanup:doCleanup]; +} + +-(void)removeAllChildrenWithCleanup:(BOOL)doCleanup +{ + [children_ makeObjectsPerformSelector:@selector(useSelfRender)]; + + [super removeAllChildrenWithCleanup:doCleanup]; + + [textureAtlas_ removeAllQuads]; +} + +#pragma mark CCParticleBatchNode - Node overrides +-(void) draw +{ + CC_PROFILER_STOP(@"CCParticleBatchNode - draw"); + + if( textureAtlas_.totalQuads == 0 ) + return; + + CC_NODE_DRAW_SETUP(); + + ccGLBlendFunc( blendFunc_.src, blendFunc_.dst ); + + [textureAtlas_ drawQuads]; + + CC_PROFILER_STOP(@"CCParticleBatchNode - draw"); +} + +#pragma mark CCParticleBatchNode - private + +-(void) increaseAtlasCapacityTo:(NSUInteger) quantity +{ + CCLOG(@"cocos2d: CCParticleBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu].", + (long)textureAtlas_.capacity, + (long)quantity); + + if( ! [textureAtlas_ resizeCapacity:quantity] ) { + // serious problems + CCLOGWARN(@"cocos2d: WARNING: Not enough memory to resize the atlas"); + NSAssert(NO,@"XXX: CCParticleBatchNode #increaseAtlasCapacity SHALL handle this assert"); + } +} + +//sets a 0'd quad into the quads array +-(void) disableParticle:(NSUInteger)particleIndex +{ + ccV3F_C4B_T2F_Quad* quad = &((textureAtlas_.quads)[particleIndex]); + quad->br.vertices.x = quad->br.vertices.y = quad->tr.vertices.x = quad->tr.vertices.y = quad->tl.vertices.x = quad->tl.vertices.y = quad->bl.vertices.x = quad->bl.vertices.y = 0.0f; +} + +#pragma mark CCParticleBatchNode - add / remove / reorder helper methods + +// add child helper +-(void) insertChild:(CCParticleSystem*) pSystem inAtlasAtIndex:(NSUInteger)index +{ + pSystem.atlasIndex = index; + + if(textureAtlas_.totalQuads + pSystem.totalParticles > textureAtlas_.capacity) + { + [self increaseAtlasCapacityTo:textureAtlas_.totalQuads + pSystem.totalParticles]; + + // after a realloc empty quads of textureAtlas can be filled with gibberish (realloc doesn't perform calloc), insert empty quads to prevent it + [textureAtlas_ fillWithEmptyQuadsFromIndex:textureAtlas_.capacity - pSystem.totalParticles amount:pSystem.totalParticles]; + } + + // make room for quads, not necessary for last child + if (pSystem.atlasIndex + pSystem.totalParticles != textureAtlas_.totalQuads) + [textureAtlas_ moveQuadsFromIndex:index to:index+pSystem.totalParticles]; + + // increase totalParticles here for new particles, update method of particlesystem will fill the quads + [textureAtlas_ increaseTotalQuadsWith:pSystem.totalParticles]; + + [self updateAllAtlasIndexes]; +} + +//rebuild atlas indexes +-(void) updateAllAtlasIndexes +{ + CCParticleSystem *child; + NSUInteger index = 0; + + CCARRAY_FOREACH(children_,child) + { + child.atlasIndex = index; + index += child.totalParticles; + } +} + +#pragma mark CCParticleBatchNode - CocosNodeTexture protocol + +-(void) updateBlendFunc +{ + if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } +} + +-(void) setTexture:(CCTexture2D*)texture +{ + textureAtlas_.texture = texture; + + // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it + if( texture && ! [texture hasPremultipliedAlpha] && ( blendFunc_.src == CC_BLEND_SRC && blendFunc_.dst == CC_BLEND_DST ) ) + { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } +} + +-(CCTexture2D*) texture +{ + return textureAtlas_.texture; +} + +@end diff --git a/cocos2d/cocos2d/CCParticleExamples.h b/cocos2d/cocos2d/CCParticleExamples.h old mode 100644 new mode 100755 index cd382c4..313f86d --- a/cocos2d/cocos2d/CCParticleExamples.h +++ b/cocos2d/cocos2d/CCParticleExamples.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,87 +25,71 @@ */ -#import - -#import "CCParticleSystemPoint.h" +#import "ccMacros.h" #import "CCParticleSystemQuad.h" -// build each architecture with the optimal particle system - -// ARMv7, Mac or Simulator use "Quad" particle -#if defined(__ARM_NEON__) || defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || TARGET_IPHONE_SIMULATOR - #define ARCH_OPTIMAL_PARTICLE_SYSTEM CCParticleSystemQuad - -// ARMv6 use "Point" particle -#elif __arm__ - #define ARCH_OPTIMAL_PARTICLE_SYSTEM CCParticleSystemPoint -#else - #error(unknown architecture) -#endif - - //! A fire particle system -@interface CCParticleFire: ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleFire: CCParticleSystemQuad { } @end //! A fireworks particle system -@interface CCParticleFireworks : ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleFireworks : CCParticleSystemQuad { } @end //! A sun particle system -@interface CCParticleSun : ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleSun : CCParticleSystemQuad { } @end //! A galaxy particle system -@interface CCParticleGalaxy : ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleGalaxy : CCParticleSystemQuad { } @end //! A flower particle system -@interface CCParticleFlower : ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleFlower : CCParticleSystemQuad { } @end //! A meteor particle system -@interface CCParticleMeteor : ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleMeteor : CCParticleSystemQuad { } @end //! An spiral particle system -@interface CCParticleSpiral : ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleSpiral : CCParticleSystemQuad { } @end //! An explosion particle system -@interface CCParticleExplosion : ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleExplosion : CCParticleSystemQuad { } @end //! An smoke particle system -@interface CCParticleSmoke : ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleSmoke : CCParticleSystemQuad { } @end //! An snow particle system -@interface CCParticleSnow : ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleSnow : CCParticleSystemQuad { } @end //! A rain particle system -@interface CCParticleRain : ARCH_OPTIMAL_PARTICLE_SYSTEM +@interface CCParticleRain : CCParticleSystemQuad { } @end diff --git a/cocos2d/cocos2d/CCParticleExamples.m b/cocos2d/cocos2d/CCParticleExamples.m old mode 100644 new mode 100755 index 38c8b46..c21cf83 --- a/cocos2d/cocos2d/CCParticleExamples.m +++ b/cocos2d/cocos2d/CCParticleExamples.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -51,7 +51,7 @@ -(id) initWithTotalParticles:(NSUInteger)p // Gravity Mode: gravity self.gravity = ccp(0,-90); - + // Gravity Mode: radial self.radialAccel = 0; self.radialAccelVar = 0; @@ -59,22 +59,22 @@ -(id) initWithTotalParticles:(NSUInteger)p // Gravity Mode: speed of particles self.speed = 180; self.speedVar = 50; - + // emitter position CGSize winSize = [[CCDirector sharedDirector] winSize]; self.position = ccp(winSize.width/2, winSize.height/2); - + // angle angle = 90; angleVar = 20; - + // life of particles life = 3.5f; lifeVar = 1; - + // emits per frame emissionRate = totalParticles/life; - + // color of particles startColor.r = 0.5f; startColor.g = 0.5f; @@ -92,7 +92,7 @@ -(id) initWithTotalParticles:(NSUInteger)p endColorVar.g = 0.1f; endColorVar.b = 0.1f; endColorVar.a = 0.2f; - + // size, in pixels startSize = 8.0f; startSizeVar = 2.0f; @@ -103,7 +103,7 @@ -(id) initWithTotalParticles:(NSUInteger)p // additive self.blendAdditive = NO; } - + return self; } @end @@ -129,29 +129,29 @@ -(id) initWithTotalParticles:(NSUInteger) p // Gravity Mode: gravity self.gravity = ccp(0,0); - + // Gravity Mode: radial acceleration self.radialAccel = 0; self.radialAccelVar = 0; - + // Gravity Mode: speed of particles self.speed = 60; - self.speedVar = 20; - + self.speedVar = 20; + // starting angle angle = 90; angleVar = 10; - + // emitter position CGSize winSize = [[CCDirector sharedDirector] winSize]; self.position = ccp(winSize.width/2, 60); posVar = ccp(40, 20); - + // life of particles life = 3; lifeVar = 0.25f; - - + + // size, in pixels startSize = 54.0f; startSizeVar = 10.0f; @@ -159,7 +159,7 @@ -(id) initWithTotalParticles:(NSUInteger) p // emits per frame emissionRate = totalParticles/life; - + // color of particles startColor.r = 0.76f; startColor.g = 0.25f; @@ -177,13 +177,13 @@ -(id) initWithTotalParticles:(NSUInteger) p endColorVar.g = 0.0f; endColorVar.b = 0.0f; endColorVar.a = 0.0f; - + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; - + // additive self.blendAdditive = YES; } - + return self; } @end @@ -203,38 +203,38 @@ -(id) initWithTotalParticles:(NSUInteger) p // additive self.blendAdditive = YES; - + // duration duration = kCCParticleDurationInfinity; - + // Gravity Mode self.emitterMode = kCCParticleModeGravity; - + // Gravity Mode: gravity self.gravity = ccp(0,0); - + // Gravity mode: radial acceleration self.radialAccel = 0; self.radialAccelVar = 0; - + // Gravity mode: speed of particles self.speed = 20; self.speedVar = 5; - - + + // angle angle = 90; angleVar = 360; - + // emitter position CGSize winSize = [[CCDirector sharedDirector] winSize]; self.position = ccp(winSize.width/2, winSize.height/2); posVar = CGPointZero; - + // life of particles life = 1; lifeVar = 0.5f; - + // size, in pixels startSize = 30.0f; startSizeVar = 10.0f; @@ -242,7 +242,7 @@ -(id) initWithTotalParticles:(NSUInteger) p // emits per seconds emissionRate = totalParticles/life; - + // color of particles startColor.r = 0.76f; startColor.g = 0.25f; @@ -260,10 +260,10 @@ -(id) initWithTotalParticles:(NSUInteger) p endColorVar.g = 0.0f; endColorVar.b = 0.0f; endColorVar.a = 0.0f; - + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; } - + return self; } @end @@ -289,40 +289,40 @@ -(id) initWithTotalParticles:(NSUInteger)p // Gravity Mode: gravity self.gravity = ccp(0,0); - + // Gravity Mode: speed of particles self.speed = 60; self.speedVar = 10; - + // Gravity Mode: radial self.radialAccel = -80; self.radialAccelVar = 0; - + // Gravity Mode: tagential self.tangentialAccel = 80; self.tangentialAccelVar = 0; - + // angle angle = 90; angleVar = 360; - + // emitter position CGSize winSize = [[CCDirector sharedDirector] winSize]; self.position = ccp(winSize.width/2, winSize.height/2); posVar = CGPointZero; - + // life of particles life = 4; lifeVar = 1; - + // size, in pixels startSize = 37.0f; startSizeVar = 10.0f; endSize = kCCParticleStartSizeEqualToEndSize; - + // emits per second emissionRate = totalParticles/life; - + // color of particles startColor.r = 0.12f; startColor.g = 0.25f; @@ -340,13 +340,13 @@ -(id) initWithTotalParticles:(NSUInteger)p endColorVar.g = 0.0f; endColorVar.b = 0.0f; endColorVar.a = 0.0f; - + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; // additive self.blendAdditive = YES; } - + return self; } @end @@ -363,7 +363,7 @@ -(id) init -(id) initWithTotalParticles:(NSUInteger) p { if( (self=[super initWithTotalParticles:p]) ) { - + // duration duration = kCCParticleDurationInfinity; @@ -372,15 +372,15 @@ -(id) initWithTotalParticles:(NSUInteger) p // Gravity Mode: gravity self.gravity = ccp(0,0); - + // Gravity Mode: speed of particles self.speed = 80; self.speedVar = 10; - + // Gravity Mode: radial self.radialAccel = -60; self.radialAccelVar = 0; - + // Gravity Mode: tagential self.tangentialAccel = 15; self.tangentialAccelVar = 0; @@ -388,16 +388,16 @@ -(id) initWithTotalParticles:(NSUInteger) p // angle angle = 90; angleVar = 360; - + // emitter position CGSize winSize = [[CCDirector sharedDirector] winSize]; self.position = ccp(winSize.width/2, winSize.height/2); posVar = CGPointZero; - + // life of particles life = 4; lifeVar = 1; - + // size, in pixels startSize = 30.0f; startSizeVar = 10.0f; @@ -405,7 +405,7 @@ -(id) initWithTotalParticles:(NSUInteger) p // emits per second emissionRate = totalParticles/life; - + // color of particles startColor.r = 0.50f; startColor.g = 0.50f; @@ -423,13 +423,13 @@ -(id) initWithTotalParticles:(NSUInteger) p endColorVar.g = 0.0f; endColorVar.b = 0.0f; endColorVar.a = 0.0f; - + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; // additive self.blendAdditive = YES; } - + return self; } @end @@ -449,7 +449,7 @@ -(id) initWithTotalParticles:(NSUInteger) p // duration duration = kCCParticleDurationInfinity; - + // Gravity Mode self.emitterMode = kCCParticleModeGravity; @@ -459,28 +459,28 @@ -(id) initWithTotalParticles:(NSUInteger) p // Gravity Mode: speed of particles self.speed = 15; self.speedVar = 5; - + // Gravity Mode: radial self.radialAccel = 0; self.radialAccelVar = 0; - + // Gravity Mode: tagential self.tangentialAccel = 0; self.tangentialAccelVar = 0; - + // angle angle = 90; angleVar = 360; - + // emitter position CGSize winSize = [[CCDirector sharedDirector] winSize]; self.position = ccp(winSize.width/2, winSize.height/2); posVar = CGPointZero; - + // life of particles life = 2; lifeVar = 1; - + // size, in pixels startSize = 60.0f; startSizeVar = 10.0f; @@ -488,7 +488,7 @@ -(id) initWithTotalParticles:(NSUInteger) p // emits per second emissionRate = totalParticles/life; - + // color of particles startColor.r = 0.2f; startColor.g = 0.4f; @@ -506,13 +506,13 @@ -(id) initWithTotalParticles:(NSUInteger) p endColorVar.g = 0.0f; endColorVar.b = 0.0f; endColorVar.a = 0.0f; - + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; - + // additive self.blendAdditive = YES; } - + return self; } @end @@ -529,41 +529,41 @@ -(id) init -(id) initWithTotalParticles:(NSUInteger) p { if( (self=[super initWithTotalParticles:p]) ) { - + // duration duration = kCCParticleDurationInfinity; // Gravity Mode self.emitterMode = kCCParticleModeGravity; - + // Gravity Mode: gravity self.gravity = ccp(0,0); - + // Gravity Mode: speed of particles self.speed = 150; self.speedVar = 0; - + // Gravity Mode: radial self.radialAccel = -380; self.radialAccelVar = 0; - + // Gravity Mode: tagential self.tangentialAccel = 45; self.tangentialAccelVar = 0; - + // angle angle = 90; angleVar = 0; - + // emitter position CGSize winSize = [[CCDirector sharedDirector] winSize]; self.position = ccp(winSize.width/2, winSize.height/2); posVar = CGPointZero; - + // life of particles life = 12; lifeVar = 0; - + // size, in pixels startSize = 20.0f; startSizeVar = 0.0f; @@ -571,7 +571,7 @@ -(id) initWithTotalParticles:(NSUInteger) p // emits per second emissionRate = totalParticles/life; - + // color of particles startColor.r = 0.5f; startColor.g = 0.5f; @@ -589,13 +589,13 @@ -(id) initWithTotalParticles:(NSUInteger) p endColorVar.g = 0.5f; endColorVar.b = 0.5f; endColorVar.a = 0.0f; - + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; // additive self.blendAdditive = NO; } - + return self; } @end @@ -612,40 +612,40 @@ -(id) init -(id) initWithTotalParticles:(NSUInteger)p { if( (self=[super initWithTotalParticles:p]) ) { - + // duration duration = 0.1f; - + self.emitterMode = kCCParticleModeGravity; // Gravity Mode: gravity self.gravity = ccp(0,0); - + // Gravity Mode: speed of particles self.speed = 70; self.speedVar = 40; - + // Gravity Mode: radial self.radialAccel = 0; self.radialAccelVar = 0; - + // Gravity Mode: tagential self.tangentialAccel = 0; self.tangentialAccelVar = 0; - + // angle angle = 90; angleVar = 360; - + // emitter position CGSize winSize = [[CCDirector sharedDirector] winSize]; self.position = ccp(winSize.width/2, winSize.height/2); posVar = CGPointZero; - + // life of particles life = 5.0f; lifeVar = 2; - + // size, in pixels startSize = 15.0f; startSizeVar = 10.0f; @@ -653,7 +653,7 @@ -(id) initWithTotalParticles:(NSUInteger)p // emits per second emissionRate = totalParticles/duration; - + // color of particles startColor.r = 0.7f; startColor.g = 0.1f; @@ -671,13 +671,13 @@ -(id) initWithTotalParticles:(NSUInteger)p endColorVar.g = 0.5f; endColorVar.b = 0.5f; endColorVar.a = 0.0f; - + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; // additive self.blendAdditive = NO; } - + return self; } @end @@ -694,37 +694,37 @@ -(id) init -(id) initWithTotalParticles:(NSUInteger) p { if( (self=[super initWithTotalParticles:p]) ) { - + // duration duration = kCCParticleDurationInfinity; - + // Emitter mode: Gravity Mode self.emitterMode = kCCParticleModeGravity; - + // Gravity Mode: gravity self.gravity = ccp(0,0); // Gravity Mode: radial acceleration self.radialAccel = 0; self.radialAccelVar = 0; - + // Gravity Mode: speed of particles self.speed = 25; self.speedVar = 10; - + // angle angle = 90; angleVar = 5; - + // emitter position CGSize winSize = [[CCDirector sharedDirector] winSize]; self.position = ccp(winSize.width/2, 0); posVar = ccp(20, 0); - + // life of particles life = 4; lifeVar = 1; - + // size, in pixels startSize = 60.0f; startSizeVar = 10.0f; @@ -732,7 +732,7 @@ -(id) initWithTotalParticles:(NSUInteger) p // emits per frame emissionRate = totalParticles/life; - + // color of particles startColor.r = 0.8f; startColor.g = 0.8f; @@ -750,13 +750,13 @@ -(id) initWithTotalParticles:(NSUInteger) p endColorVar.g = 0.0f; endColorVar.b = 0.0f; endColorVar.a = 0.0f; - + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; - + // additive self.blendAdditive = NO; } - + return self; } @end @@ -770,35 +770,35 @@ -(id) init -(id) initWithTotalParticles:(NSUInteger)p { if( (self=[super initWithTotalParticles:p]) ) { - + // duration duration = kCCParticleDurationInfinity; - + // set gravity mode. self.emitterMode = kCCParticleModeGravity; // Gravity Mode: gravity self.gravity = ccp(0,-1); - + // Gravity Mode: speed of particles self.speed = 5; self.speedVar = 1; - + // Gravity Mode: radial self.radialAccel = 0; self.radialAccelVar = 1; - + // Gravity mode: tagential self.tangentialAccel = 0; self.tangentialAccelVar = 1; - + // emitter position self.position = (CGPoint) { [[CCDirector sharedDirector] winSize].width / 2, [[CCDirector sharedDirector] winSize].height + 10 }; posVar = ccp( [[CCDirector sharedDirector] winSize].width / 2, 0 ); - + // angle angle = -90; angleVar = 5; @@ -806,7 +806,7 @@ -(id) initWithTotalParticles:(NSUInteger)p // life of particles life = 45; lifeVar = 15; - + // size, in pixels startSize = 10.0f; startSizeVar = 5.0f; @@ -814,7 +814,7 @@ -(id) initWithTotalParticles:(NSUInteger)p // emits per second emissionRate = 10; - + // color of particles startColor.r = 1.0f; startColor.g = 1.0f; @@ -832,13 +832,13 @@ -(id) initWithTotalParticles:(NSUInteger)p endColorVar.g = 0.0f; endColorVar.b = 0.0f; endColorVar.a = 0.0f; - + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; - + // additive self.blendAdditive = NO; } - + return self; } @end @@ -852,19 +852,19 @@ -(id) init -(id) initWithTotalParticles:(NSUInteger)p { if( (self=[super initWithTotalParticles:p]) ) { - + // duration duration = kCCParticleDurationInfinity; - + self.emitterMode = kCCParticleModeGravity; // Gravity Mode: gravity self.gravity = ccp(10,-10); - + // Gravity Mode: radial self.radialAccel = 0; self.radialAccelVar = 1; - + // Gravity Mode: tagential self.tangentialAccel = 0; self.tangentialAccelVar = 1; @@ -872,23 +872,23 @@ -(id) initWithTotalParticles:(NSUInteger)p // Gravity Mode: speed of particles self.speed = 130; self.speedVar = 30; - + // angle angle = -90; angleVar = 5; - - + + // emitter position self.position = (CGPoint) { [[CCDirector sharedDirector] winSize].width / 2, [[CCDirector sharedDirector] winSize].height }; posVar = ccp( [[CCDirector sharedDirector] winSize].width / 2, 0 ); - + // life of particles life = 4.5f; lifeVar = 0; - + // size, in pixels startSize = 4.0f; startSizeVar = 2.0f; @@ -896,7 +896,7 @@ -(id) initWithTotalParticles:(NSUInteger)p // emits per second emissionRate = 20; - + // color of particles startColor.r = 0.7f; startColor.g = 0.8f; @@ -914,13 +914,13 @@ -(id) initWithTotalParticles:(NSUInteger)p endColorVar.g = 0.0f; endColorVar.b = 0.0f; endColorVar.a = 0.0f; - + self.texture = [[CCTextureCache sharedTextureCache] addImage: @"fire.png"]; - + // additive self.blendAdditive = NO; } - + return self; } @end diff --git a/cocos2d/cocos2d/CCParticleSystem.h b/cocos2d/cocos2d/CCParticleSystem.h old mode 100644 new mode 100755 index 429e814..10050ff --- a/cocos2d/cocos2d/CCParticleSystem.h +++ b/cocos2d/cocos2d/CCParticleSystem.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,9 +30,7 @@ #import "ccTypes.h" #import "ccConfig.h" -#if CC_ENABLE_PROFILERS -@class CCProfilingTimer; -#endif +@class CCParticleBatchNode; //* @enum enum { @@ -41,7 +39,7 @@ enum { /** The starting size of the particle is equal to the ending size */ kCCParticleStartSizeEqualToEndSize = -1, - + /** The starting radius of the particle is equal to the ending radius */ kCCParticleStartRadiusEqualToEndRadius = -1, @@ -54,9 +52,9 @@ enum { enum { /** Gravity mode (A mode) */ kCCParticleModeGravity, - + /** Radius mode (B mode) */ - kCCParticleModeRadius, + kCCParticleModeRadius, }; @@ -71,7 +69,7 @@ typedef enum { Use case: Attach an emitter to an sprite, and you want that the emitter follows the sprite. */ kCCPositionTypeRelative, - + /** Living particles are attached to the emitter and are translated along with it. */ kCCPositionTypeGrouped, }tCCPositionType; @@ -80,7 +78,7 @@ typedef enum { enum { kPositionTypeFree = kCCPositionTypeFree, kPositionTypeGrouped = kCCPositionTypeGrouped, -}; +}; /** @struct tCCParticle Structure that contains the values of each particle @@ -100,6 +98,8 @@ typedef struct sCCParticle { ccTime timeToLive; + NSUInteger atlasIndex; + union { // Mode A: gravity, direction, radial accel, tangential accel struct { @@ -107,7 +107,7 @@ typedef struct sCCParticle { float radialAccel; float tangentialAccel; } A; - + // Mode B: radius mode struct { float angle; @@ -150,43 +150,43 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); cocos2d also supports particles generated by Particle Designer (http://particledesigner.71squared.com/). 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d, - cocos2d uses a another approach, but the results are almost identical. - + cocos2d uses a another approach, but the results are almost identical. + cocos2d supports all the variables used by Particle Designer plus a bit more: - spinning particles (supported when using CCParticleSystemQuad) - tangential acceleration (Gravity mode) - radial acceleration (Gravity mode) - radius direction (Radius mode) (Particle Designer supports outwards to inwards direction only) - + It is possible to customize any of the above mentioned properties in runtime. Example: - + @code emitter.radialAccel = 15; emitter.startSpin = 0; @endcode - + */ @interface CCParticleSystem : CCNode -{ +{ // is the particle system active ? BOOL active; // duration in seconds of the system. -1 is infinity float duration; // time elapsed since the start of the system (in seconds) float elapsed; - + // position is from "superclass" CocosNode CGPoint sourcePosition; // Position variance CGPoint posVar; - + // The angle (direction) of the particles measured in degrees float angle; // Angle variance measured in degrees; float angleVar; - + // Different modes - + NSInteger emitterMode_; union { // Mode A:Gravity + Tangential Accel + Radial Accel @@ -212,7 +212,7 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); // Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode) struct { - + // The starting radius of the particles float startRadius; // The starting radius variance of the particles @@ -220,14 +220,14 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); // The ending radius of the particles float endRadius; // The ending radius variance of the particles - float endRadiusVar; + float endRadiusVar; // Number of degress to rotate a particle around the source pos per second float rotatePerSecond; // Variance in degrees for rotatePerSecond float rotatePerSecondVar; } B; } mode; - + // start ize of the particles float startSize; // start Size variance @@ -236,12 +236,12 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); float endSize; // end size of variance float endSizeVar; - + // How many seconds will the particle live float life; // Life variance float lifeVar; - + // Start color of the particles ccColor4F startColor; // Start color variance @@ -250,7 +250,7 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); ccColor4F endColor; // End color variance ccColor4F endColorVar; - + // start angle of the particles float startSpin; // start angle variance @@ -259,26 +259,26 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); float endSpin; // end angle ariance float endSpinVar; - - + // Array of particles tCCParticle *particles; // Maximum particles NSUInteger totalParticles; // Count of active particles NSUInteger particleCount; - - // color modulate -// BOOL colorModulate; - + // Number of allocated particles + NSUInteger allocatedParticles; + // How many particles can be emitted per second float emissionRate; float emitCounter; - + // Texture of the particles CCTexture2D *texture_; // blend function ccBlendFunc blendFunc_; + // Texture alpha behavior + BOOL opacityModifyRGB_; // movment type: free or grouped tCCPositionType positionType_; @@ -288,15 +288,19 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); // particle idx NSUInteger particleIdx; - + // Optimization CC_UPDATE_PARTICLE_IMP updateParticleImp; SEL updateParticleSel; - -// profiling -#if CC_ENABLE_PROFILERS - CCProfilingTimer* _profilingTimer; -#endif + + // for batching. If nil, then it won't be batched + CCParticleBatchNode *batchNode_; + + // index of system in batch node array + NSUInteger atlasIndex_; + + //YES if scaled or rotated + BOOL transformSystemDirty_; } /** Is the emitter active */ @@ -340,7 +344,7 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); /** The ending radius of the particles. Only available in 'Radius' mode. */ @property (nonatomic,readwrite,assign) float endRadius; /** The ending radius variance of the particles. Only available in 'Radius' mode. */ -@property (nonatomic,readwrite,assign) float endRadiusVar; +@property (nonatomic,readwrite,assign) float endRadiusVar; /** Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. */ @property (nonatomic,readwrite,assign) float rotatePerSecond; /** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */ @@ -378,6 +382,8 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); @property (nonatomic,readwrite, retain) CCTexture2D * texture; /** conforms to CocosNodeTexture protocol */ @property (nonatomic,readwrite) ccBlendFunc blendFunc; +/** does the alpha value modify color */ +@property (nonatomic, readwrite, getter=doesOpacityModifyRGB, assign) BOOL opacityModifyRGB; /** whether or not the particles are using blend additive. If enabled, the following blending function will be used. @code @@ -401,6 +407,11 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); */ @property (nonatomic,readwrite) NSInteger emitterMode; +/** weak reference to the CCSpriteBatchNode that renders the CCSprite */ +@property (nonatomic,readwrite,assign) CCParticleBatchNode *batchNode; + +@property (nonatomic,readwrite) NSUInteger atlasIndex; + /** creates an initializes a CCParticleSystem from a plist file. This plist files can be creted manually or with Particle Designer: http://particledesigner.71squared.com/ @@ -415,17 +426,13 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); */ -(id) initWithFile:(NSString*) plistFile; -/** initializes a CCQuadParticleSystem from a NSDictionary. +/** initializes a particle system from a NSDictionary. @since v0.99.3 */ -(id) initWithDictionary:(NSDictionary*)dictionary; //! Initializes a system with a fixed number of particles -(id) initWithTotalParticles:(NSUInteger) numberOfParticles; -//! Add a particle to the emitter --(BOOL) addParticle; -//! Initializes a particle --(void) initParticle: (tCCParticle*) particle; //! stop emitting particles. Running particles will continue to run until they die -(void) stopSystem; //! Kill all living particles. @@ -441,5 +448,6 @@ typedef void (*CC_UPDATE_PARTICLE_IMP)(id, SEL, tCCParticle*, CGPoint); //! called in every loop. -(void) update: (ccTime) dt; -@end +-(void) updateWithNoTime; +@end diff --git a/cocos2d/cocos2d/CCParticleSystem.m b/cocos2d/cocos2d/CCParticleSystem.m old mode 100644 new mode 100755 index 742676e..3d5c38c --- a/cocos2d/cocos2d/CCParticleSystem.m +++ b/cocos2d/cocos2d/CCParticleSystem.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -39,7 +39,7 @@ // // IMPORTANT: Particle Designer is supported by cocos2d, but // 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d, -// cocos2d uses a another approach, but the results are almost identical. +// cocos2d uses a another approach, but the results are almost identical. // // opengl @@ -47,12 +47,12 @@ // cocos2d #import "ccConfig.h" -#if CC_ENABLE_PROFILERS -#import "Support/CCProfiling.h" -#endif #import "CCParticleSystem.h" +#import "CCParticleBatchNode.h" #import "CCTextureCache.h" +#import "CCTextureAtlas.h" #import "ccMacros.h" +#import "Support/CCProfiling.h" // support #import "Support/OpenGL_Internal.h" @@ -61,6 +61,10 @@ #import "Support/ZipUtils.h" #import "Support/CCFileUtils.h" +@interface CCParticleSystem () +-(void) updateBlendFunc; +@end + @implementation CCParticleSystem @synthesize active, duration; @synthesize sourcePosition, posVar; @@ -70,14 +74,14 @@ @implementation CCParticleSystem @synthesize startColor, startColorVar, endColor, endColorVar; @synthesize startSpin, startSpinVar, endSpin, endSpinVar; @synthesize emissionRate; -@synthesize totalParticles; @synthesize startSize, startSizeVar; @synthesize endSize, endSizeVar; +@synthesize opacityModifyRGB = opacityModifyRGB_; @synthesize blendFunc = blendFunc_; @synthesize positionType = positionType_; @synthesize autoRemoveOnFinish = autoRemoveOnFinish_; @synthesize emitterMode = emitterMode_; - +@synthesize atlasIndex = atlasIndex_; +(id) particleWithFile:(NSString*) plistFile { @@ -85,85 +89,82 @@ +(id) particleWithFile:(NSString*) plistFile } -(id) init { - NSAssert(NO, @"CCParticleSystem: Init not supported."); - [self release]; - return nil; + return [self initWithTotalParticles:150]; } -(id) initWithFile:(NSString *)plistFile { - NSString *path = [CCFileUtils fullPathFromRelativePath:plistFile]; + NSString *path = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:plistFile]; NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; - + NSAssert( dict != nil, @"Particles: file not found"); return [self initWithDictionary:dict]; } -(id) initWithDictionary:(NSDictionary *)dictionary { - NSUInteger maxParticles = [[dictionary valueForKey:@"maxParticles"] intValue]; + NSUInteger maxParticles = [[dictionary valueForKey:@"maxParticles"] integerValue]; // self, not super - if ((self=[self initWithTotalParticles:maxParticles] ) ) { - + + if ((self=[self initWithTotalParticles:maxParticles] ) ) + { // angle angle = [[dictionary valueForKey:@"angle"] floatValue]; angleVar = [[dictionary valueForKey:@"angleVariance"] floatValue]; - + // duration duration = [[dictionary valueForKey:@"duration"] floatValue]; - - // blend function + + // blend function blendFunc_.src = [[dictionary valueForKey:@"blendFuncSource"] intValue]; blendFunc_.dst = [[dictionary valueForKey:@"blendFuncDestination"] intValue]; - + // color float r,g,b,a; - + r = [[dictionary valueForKey:@"startColorRed"] floatValue]; g = [[dictionary valueForKey:@"startColorGreen"] floatValue]; b = [[dictionary valueForKey:@"startColorBlue"] floatValue]; a = [[dictionary valueForKey:@"startColorAlpha"] floatValue]; startColor = (ccColor4F) {r,g,b,a}; - + r = [[dictionary valueForKey:@"startColorVarianceRed"] floatValue]; g = [[dictionary valueForKey:@"startColorVarianceGreen"] floatValue]; b = [[dictionary valueForKey:@"startColorVarianceBlue"] floatValue]; a = [[dictionary valueForKey:@"startColorVarianceAlpha"] floatValue]; startColorVar = (ccColor4F) {r,g,b,a}; - + r = [[dictionary valueForKey:@"finishColorRed"] floatValue]; g = [[dictionary valueForKey:@"finishColorGreen"] floatValue]; b = [[dictionary valueForKey:@"finishColorBlue"] floatValue]; a = [[dictionary valueForKey:@"finishColorAlpha"] floatValue]; endColor = (ccColor4F) {r,g,b,a}; - + r = [[dictionary valueForKey:@"finishColorVarianceRed"] floatValue]; g = [[dictionary valueForKey:@"finishColorVarianceGreen"] floatValue]; b = [[dictionary valueForKey:@"finishColorVarianceBlue"] floatValue]; a = [[dictionary valueForKey:@"finishColorVarianceAlpha"] floatValue]; endColorVar = (ccColor4F) {r,g,b,a}; - + // particle size startSize = [[dictionary valueForKey:@"startParticleSize"] floatValue]; startSizeVar = [[dictionary valueForKey:@"startParticleSizeVariance"] floatValue]; endSize = [[dictionary valueForKey:@"finishParticleSize"] floatValue]; endSizeVar = [[dictionary valueForKey:@"finishParticleSizeVariance"] floatValue]; - - + // position float x = [[dictionary valueForKey:@"sourcePositionx"] floatValue]; float y = [[dictionary valueForKey:@"sourcePositiony"] floatValue]; self.position = ccp(x,y); posVar.x = [[dictionary valueForKey:@"sourcePositionVariancex"] floatValue]; posVar.y = [[dictionary valueForKey:@"sourcePositionVariancey"] floatValue]; - - + // Spinning startSpin = [[dictionary valueForKey:@"rotationStart"] floatValue]; startSpinVar = [[dictionary valueForKey:@"rotationStartVariance"] floatValue]; endSpin = [[dictionary valueForKey:@"rotationEnd"] floatValue]; endSpinVar = [[dictionary valueForKey:@"rotationEndVariance"] floatValue]; - + emitterMode_ = [[dictionary valueForKey:@"emitterType"] intValue]; // Mode A: Gravity + tangential accel + radial accel @@ -171,34 +172,33 @@ -(id) initWithDictionary:(NSDictionary *)dictionary // gravity mode.A.gravity.x = [[dictionary valueForKey:@"gravityx"] floatValue]; mode.A.gravity.y = [[dictionary valueForKey:@"gravityy"] floatValue]; - + // // speed mode.A.speed = [[dictionary valueForKey:@"speed"] floatValue]; mode.A.speedVar = [[dictionary valueForKey:@"speedVariance"] floatValue]; - - // radial acceleration + + // radial acceleration NSString *tmp = [dictionary valueForKey:@"radialAcceleration"]; mode.A.radialAccel = tmp ? [tmp floatValue] : 0; - + tmp = [dictionary valueForKey:@"radialAccelVariance"]; mode.A.radialAccelVar = tmp ? [tmp floatValue] : 0; - + // tangential acceleration tmp = [dictionary valueForKey:@"tangentialAcceleration"]; mode.A.tangentialAccel = tmp ? [tmp floatValue] : 0; - + tmp = [dictionary valueForKey:@"tangentialAccelVariance"]; mode.A.tangentialAccelVar = tmp ? [tmp floatValue] : 0; } - - + // or Mode B: radius movement else if( emitterMode_ == kCCParticleModeRadius ) { float maxRadius = [[dictionary valueForKey:@"maxRadius"] floatValue]; float maxRadiusVar = [[dictionary valueForKey:@"maxRadiusVariance"] floatValue]; float minRadius = [[dictionary valueForKey:@"minRadius"] floatValue]; - + mode.B.startRadius = maxRadius; mode.B.startRadiusVar = maxRadiusVar; mode.B.endRadius = minRadius; @@ -209,57 +209,63 @@ -(id) initWithDictionary:(NSDictionary *)dictionary } else { NSAssert( NO, @"Invalid emitterType in config file"); } - + // life span life = [[dictionary valueForKey:@"particleLifespan"] floatValue]; - lifeVar = [[dictionary valueForKey:@"particleLifespanVariance"] floatValue]; - + lifeVar = [[dictionary valueForKey:@"particleLifespanVariance"] floatValue]; + // emission Rate emissionRate = totalParticles/life; - // texture - // Try to get the texture from the cache - NSString *textureName = [dictionary valueForKey:@"textureFileName"]; + //don't get the internal texture if a batchNode is used + if (!batchNode_) + { + // Set a compatible default for the alpha transfer + opacityModifyRGB_ = NO; - CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:textureName]; + // texture + // Try to get the texture from the cache - if( tex ) - self.texture = tex; + NSString *textureName = [dictionary valueForKey:@"textureFileName"]; - else { + CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:textureName]; + + if( tex ) + [self setTexture:tex]; + else { + + NSString *textureData = [dictionary valueForKey:@"textureImageData"]; + NSAssert( textureData, @"CCParticleSystem: Couldn't load texture"); + + // if it fails, try to get it from the base64-gzipped data + unsigned char *buffer = NULL; + int len = base64Decode((unsigned char*)[textureData UTF8String], (unsigned int)[textureData length], &buffer); + NSAssert( buffer != NULL, @"CCParticleSystem: error decoding textureImageData"); - NSString *textureData = [dictionary valueForKey:@"textureImageData"]; - NSAssert( textureData, @"CCParticleSystem: Couldn't load texture"); - - // if it fails, try to get it from the base64-gzipped data - unsigned char *buffer = NULL; - int len = base64Decode((unsigned char*)[textureData UTF8String], (unsigned int)[textureData length], &buffer); - NSAssert( buffer != NULL, @"CCParticleSystem: error decoding textureImageData"); - - unsigned char *deflated = NULL; - NSUInteger deflatedLen = ccInflateMemory(buffer, len, &deflated); - free( buffer ); - - NSAssert( deflated != NULL, @"CCParticleSystem: error ungzipping textureImageData"); - NSData *data = [[NSData alloc] initWithBytes:deflated length:deflatedLen]; - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - UIImage *image = [[UIImage alloc] initWithData:data]; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) - NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data]; + unsigned char *deflated = NULL; + NSUInteger deflatedLen = ccInflateMemory(buffer, len, &deflated); + free( buffer ); + + NSAssert( deflated != NULL, @"CCParticleSystem: error ungzipping textureImageData"); + NSData *data = [[NSData alloc] initWithBytes:deflated length:deflatedLen]; + +#ifdef __CC_PLATFORM_IOS + UIImage *image = [[UIImage alloc] initWithData:data]; +#elif defined(__CC_PLATFORM_MAC) + NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data]; #endif - - free(deflated); deflated = NULL; - self.texture = [[CCTextureCache sharedTextureCache] addCGImage:[image CGImage] forKey:textureName]; - [data release]; - [image release]; + free(deflated); deflated = NULL; + + [self setTexture: [ [CCTextureCache sharedTextureCache] addCGImage:[image CGImage] forKey:textureName]]; + [data release]; + [image release]; + } + + NSAssert( [self texture] != NULL, @"CCParticleSystem: error loading the texture"); } - - NSAssert( [self texture] != NULL, @"CCParticleSystem: error loading the texture"); - } - + return self; } @@ -268,79 +274,65 @@ -(id) initWithTotalParticles:(NSUInteger) numberOfParticles if( (self=[super init]) ) { totalParticles = numberOfParticles; - + particles = calloc( totalParticles, sizeof(tCCParticle) ); if( ! particles ) { - NSLog(@"Particle system: not enough memory"); + CCLOG(@"Particle system: not enough memory"); [self release]; return nil; } - + allocatedParticles = numberOfParticles; + + if (batchNode_) + { + for (int i = 0; i < totalParticles; i++) + { + particles[i].atlasIndex=i; + } + } + // default, active active = YES; - + // default blend function blendFunc_ = (ccBlendFunc) { CC_BLEND_SRC, CC_BLEND_DST }; - + // default movement type; positionType_ = kCCPositionTypeFree; - + // by default be in mode A: emitterMode_ = kCCParticleModeGravity; - - // default: modulate - // XXX: not used - // colorModulate = YES; - + autoRemoveOnFinish_ = NO; - // profiling -#if CC_ENABLE_PROFILERS - _profilingTimer = [[CCProfiler timerWithName:@"particle system" andInstance:self] retain]; -#endif - // Optimization: compile udpateParticle method updateParticleSel = @selector(updateQuadWithParticle:newPosition:); updateParticleImp = (CC_UPDATE_PARTICLE_IMP) [self methodForSelector:updateParticleSel]; + //for batchNode + transformSystemDirty_ = NO; + // udpate after action in run! [self scheduleUpdateWithPriority:1]; - } - return self; } -(void) dealloc { + [self unscheduleUpdate]; + free( particles ); [texture_ release]; - // profiling -#if CC_ENABLE_PROFILERS - [CCProfiler releaseTimer:_profilingTimer]; -#endif - - [super dealloc]; -} --(BOOL) addParticle -{ - if( [self isFull] ) - return NO; - - tCCParticle * particle = &particles[ particleCount ]; - - [self initParticle: particle]; - particleCount++; - - return YES; + [super dealloc]; } -(void) initParticle: (tCCParticle*) particle { - + //CGPoint currentPosition = position_; // timeToLive // no negative life. prevent division by 0 particle->timeToLive = life + lifeVar * CCRANDOM_MINUS1_1(); @@ -348,101 +340,102 @@ -(void) initParticle: (tCCParticle*) particle // position particle->pos.x = sourcePosition.x + posVar.x * CCRANDOM_MINUS1_1(); - particle->pos.x *= CC_CONTENT_SCALE_FACTOR(); particle->pos.y = sourcePosition.y + posVar.y * CCRANDOM_MINUS1_1(); - particle->pos.y *= CC_CONTENT_SCALE_FACTOR(); - + // Color ccColor4F start; start.r = clampf( startColor.r + startColorVar.r * CCRANDOM_MINUS1_1(), 0, 1); start.g = clampf( startColor.g + startColorVar.g * CCRANDOM_MINUS1_1(), 0, 1); start.b = clampf( startColor.b + startColorVar.b * CCRANDOM_MINUS1_1(), 0, 1); start.a = clampf( startColor.a + startColorVar.a * CCRANDOM_MINUS1_1(), 0, 1); - + ccColor4F end; end.r = clampf( endColor.r + endColorVar.r * CCRANDOM_MINUS1_1(), 0, 1); end.g = clampf( endColor.g + endColorVar.g * CCRANDOM_MINUS1_1(), 0, 1); end.b = clampf( endColor.b + endColorVar.b * CCRANDOM_MINUS1_1(), 0, 1); end.a = clampf( endColor.a + endColorVar.a * CCRANDOM_MINUS1_1(), 0, 1); - + particle->color = start; particle->deltaColor.r = (end.r - start.r) / particle->timeToLive; particle->deltaColor.g = (end.g - start.g) / particle->timeToLive; particle->deltaColor.b = (end.b - start.b) / particle->timeToLive; particle->deltaColor.a = (end.a - start.a) / particle->timeToLive; - + // size float startS = startSize + startSizeVar * CCRANDOM_MINUS1_1(); startS = MAX(0, startS); // No negative value - startS *= CC_CONTENT_SCALE_FACTOR(); - + particle->size = startS; if( endSize == kCCParticleStartSizeEqualToEndSize ) particle->deltaSize = 0; else { float endS = endSize + endSizeVar * CCRANDOM_MINUS1_1(); endS = MAX(0, endS); // No negative values - endS *= CC_CONTENT_SCALE_FACTOR(); particle->deltaSize = (endS - startS) / particle->timeToLive; } - + // rotation float startA = startSpin + startSpinVar * CCRANDOM_MINUS1_1(); float endA = endSpin + endSpinVar * CCRANDOM_MINUS1_1(); particle->rotation = startA; particle->deltaRotation = (endA - startA) / particle->timeToLive; - + // position - if( positionType_ == kCCPositionTypeFree ) { - CGPoint p = [self convertToWorldSpace:CGPointZero]; - particle->startPos = ccpMult( p, CC_CONTENT_SCALE_FACTOR() ); - } - else if( positionType_ == kCCPositionTypeRelative ) { - particle->startPos = ccpMult( position_, CC_CONTENT_SCALE_FACTOR() ); - } - + if( positionType_ == kCCPositionTypeFree ) + particle->startPos = [self convertToWorldSpace:CGPointZero]; + + else if( positionType_ == kCCPositionTypeRelative ) + particle->startPos = position_; + + // direction - float a = CC_DEGREES_TO_RADIANS( angle + angleVar * CCRANDOM_MINUS1_1() ); - + float a = CC_DEGREES_TO_RADIANS( angle + angleVar * CCRANDOM_MINUS1_1() ); + // Mode Gravity: A if( emitterMode_ == kCCParticleModeGravity ) { CGPoint v = {cosf( a ), sinf( a )}; float s = mode.A.speed + mode.A.speedVar * CCRANDOM_MINUS1_1(); - s *= CC_CONTENT_SCALE_FACTOR(); - + // direction particle->mode.A.dir = ccpMult( v, s ); - + // radial accel particle->mode.A.radialAccel = mode.A.radialAccel + mode.A.radialAccelVar * CCRANDOM_MINUS1_1(); - particle->mode.A.radialAccel *= CC_CONTENT_SCALE_FACTOR(); - + // tangential accel particle->mode.A.tangentialAccel = mode.A.tangentialAccel + mode.A.tangentialAccelVar * CCRANDOM_MINUS1_1(); - particle->mode.A.tangentialAccel *= CC_CONTENT_SCALE_FACTOR(); - } - + // Mode Radius: B else { // Set the default diameter of the particle from the source position float startRadius = mode.B.startRadius + mode.B.startRadiusVar * CCRANDOM_MINUS1_1(); float endRadius = mode.B.endRadius + mode.B.endRadiusVar * CCRANDOM_MINUS1_1(); - startRadius *= CC_CONTENT_SCALE_FACTOR(); - endRadius *= CC_CONTENT_SCALE_FACTOR(); - particle->mode.B.radius = startRadius; if( mode.B.endRadius == kCCParticleStartRadiusEqualToEndRadius ) particle->mode.B.deltaRadius = 0; else particle->mode.B.deltaRadius = (endRadius - startRadius) / particle->timeToLive; - + particle->mode.B.angle = a; particle->mode.B.degreesPerSecond = CC_DEGREES_TO_RADIANS(mode.B.rotatePerSecond + mode.B.rotatePerSecondVar * CCRANDOM_MINUS1_1()); - } + } +} + +-(BOOL) addParticle +{ + if( [self isFull] ) + return NO; + + tCCParticle * particle = &particles[ particleCount ]; + + [self initParticle: particle]; + particleCount++; + + return YES; } -(void) stopSystem @@ -460,6 +453,7 @@ -(void) resetSystem tCCParticle *p = &particles[particleIdx]; p->timeToLive = 0; } + } -(BOOL) isFull @@ -470,137 +464,158 @@ -(BOOL) isFull #pragma mark ParticleSystem - MainLoop -(void) update: (ccTime) dt { + CC_PROFILER_START_CATEGORY(kCCProfilerCategoryParticles , @"CCParticleSystem - update"); + if( active && emissionRate ) { float rate = 1.0f / emissionRate; - emitCounter += dt; + + //issue #1201, prevent bursts of particles, due to too high emitCounter + if (particleCount < totalParticles) + emitCounter += dt; + while( particleCount < totalParticles && emitCounter > rate ) { [self addParticle]; emitCounter -= rate; } - + elapsed += dt; + if(duration != -1 && duration < elapsed) [self stopSystem]; } - + particleIdx = 0; - - -#if CC_ENABLE_PROFILERS - CCProfilingBeginTimingBlock(_profilingTimer); -#endif - - + CGPoint currentPosition = CGPointZero; - if( positionType_ == kCCPositionTypeFree ) { + if( positionType_ == kCCPositionTypeFree ) currentPosition = [self convertToWorldSpace:CGPointZero]; - currentPosition.x *= CC_CONTENT_SCALE_FACTOR(); - currentPosition.y *= CC_CONTENT_SCALE_FACTOR(); - } - else if( positionType_ == kCCPositionTypeRelative ) { + + else if( positionType_ == kCCPositionTypeRelative ) currentPosition = position_; - currentPosition.x *= CC_CONTENT_SCALE_FACTOR(); - currentPosition.y *= CC_CONTENT_SCALE_FACTOR(); - } - - while( particleIdx < particleCount ) + + if (visible_) { - tCCParticle *p = &particles[particleIdx]; - - // life - p->timeToLive -= dt; - - if( p->timeToLive > 0 ) { - - // Mode A: gravity, direction, tangential accel & radial accel - if( emitterMode_ == kCCParticleModeGravity ) { - CGPoint tmp, radial, tangential; - - radial = CGPointZero; - // radial acceleration - if(p->pos.x || p->pos.y) - radial = ccpNormalize(p->pos); - - tangential = radial; - radial = ccpMult(radial, p->mode.A.radialAccel); - - // tangential acceleration - float newy = tangential.x; - tangential.x = -tangential.y; - tangential.y = newy; - tangential = ccpMult(tangential, p->mode.A.tangentialAccel); - - // (gravity + radial + tangential) * dt - tmp = ccpAdd( ccpAdd( radial, tangential), mode.A.gravity); - tmp = ccpMult( tmp, dt); - p->mode.A.dir = ccpAdd( p->mode.A.dir, tmp); - tmp = ccpMult(p->mode.A.dir, dt); - p->pos = ccpAdd( p->pos, tmp ); - } - - // Mode B: radius movement - else { - // Update the angle and radius of the particle. - p->mode.B.angle += p->mode.B.degreesPerSecond * dt; - p->mode.B.radius += p->mode.B.deltaRadius * dt; - - p->pos.x = - cosf(p->mode.B.angle) * p->mode.B.radius; - p->pos.y = - sinf(p->mode.B.angle) * p->mode.B.radius; + while( particleIdx < particleCount ) + { + tCCParticle *p = &particles[particleIdx]; + + // life + p->timeToLive -= dt; + + if( p->timeToLive > 0 ) { + + // Mode A: gravity, direction, tangential accel & radial accel + if( emitterMode_ == kCCParticleModeGravity ) { + CGPoint tmp, radial, tangential; + + radial = CGPointZero; + // radial acceleration + if(p->pos.x || p->pos.y) + radial = ccpNormalize(p->pos); + + tangential = radial; + radial = ccpMult(radial, p->mode.A.radialAccel); + + // tangential acceleration + float newy = tangential.x; + tangential.x = -tangential.y; + tangential.y = newy; + tangential = ccpMult(tangential, p->mode.A.tangentialAccel); + + // (gravity + radial + tangential) * dt + tmp = ccpAdd( ccpAdd( radial, tangential), mode.A.gravity); + tmp = ccpMult( tmp, dt); + p->mode.A.dir = ccpAdd( p->mode.A.dir, tmp); + tmp = ccpMult(p->mode.A.dir, dt); + p->pos = ccpAdd( p->pos, tmp ); + } + + // Mode B: radius movement + else { + // Update the angle and radius of the particle. + p->mode.B.angle += p->mode.B.degreesPerSecond * dt; + p->mode.B.radius += p->mode.B.deltaRadius * dt; + + p->pos.x = - cosf(p->mode.B.angle) * p->mode.B.radius; + p->pos.y = - sinf(p->mode.B.angle) * p->mode.B.radius; + } + + // color + p->color.r += (p->deltaColor.r * dt); + p->color.g += (p->deltaColor.g * dt); + p->color.b += (p->deltaColor.b * dt); + p->color.a += (p->deltaColor.a * dt); + + // size + p->size += (p->deltaSize * dt); + p->size = MAX( 0, p->size ); + + // angle + p->rotation += (p->deltaRotation * dt); + + // + // update values in quad + // + + CGPoint newPos; + + if( positionType_ == kCCPositionTypeFree || positionType_ == kCCPositionTypeRelative ) + { + CGPoint diff = ccpSub( currentPosition, p->startPos ); + newPos = ccpSub(p->pos, diff); + } else + newPos = p->pos; + + // translate newPos to correct position, since matrix transform isn't performed in batchnode + // don't update the particle with the new position information, it will interfere with the radius and tangential calculations + if (batchNode_) + { + newPos.x+=position_.x; + newPos.y+=position_.y; + } + + updateParticleImp(self, updateParticleSel, p, newPos); + + // update particle counter + particleIdx++; + + } else { + // life < 0 + NSInteger currentIndex = p->atlasIndex; + + if( particleIdx != particleCount-1 ) + particles[particleIdx] = particles[particleCount-1]; + + if (batchNode_) + { + //disable the switched particle + [batchNode_ disableParticle:(atlasIndex_+currentIndex)]; + + //switch indexes + particles[particleCount-1].atlasIndex = currentIndex; + } + + particleCount--; + + if( particleCount == 0 && autoRemoveOnFinish_ ) { + [self unscheduleUpdate]; + [parent_ removeChild:self cleanup:YES]; + return; + } } - - // color - p->color.r += (p->deltaColor.r * dt); - p->color.g += (p->deltaColor.g * dt); - p->color.b += (p->deltaColor.b * dt); - p->color.a += (p->deltaColor.a * dt); - - // size - p->size += (p->deltaSize * dt); - p->size = MAX( 0, p->size ); - - // angle - p->rotation += (p->deltaRotation * dt); - - // - // update values in quad - // - - CGPoint newPos; - - if( positionType_ == kCCPositionTypeFree || positionType_ == kCCPositionTypeRelative ) { - CGPoint diff = ccpSub( currentPosition, p->startPos ); - newPos = ccpSub(p->pos, diff); - - } else - newPos = p->pos; - - - updateParticleImp(self, updateParticleSel, p, newPos); - - // update particle counter - particleIdx++; - - } else { - // life < 0 - if( particleIdx != particleCount-1 ) - particles[particleIdx] = particles[particleCount-1]; - particleCount--; - - if( particleCount == 0 && autoRemoveOnFinish_ ) { - [self unscheduleUpdate]; - [parent_ removeChild:self cleanup:YES]; - return; - } - } + }//while + transformSystemDirty_ = NO; } - -#if CC_ENABLE_PROFILERS - CCProfilingEndTimingBlock(_profilingTimer); -#endif - -#ifdef CC_USES_VBO - [self postStep]; -#endif + + if (!batchNode_) + [self postStep]; + + CC_PROFILER_STOP_CATEGORY(kCCProfilerCategoryParticles , @"CCParticleSystem - update"); +} + +-(void) updateWithNoTime +{ + [self update:0.0f]; } -(void) updateQuadWithParticle:(tCCParticle*)particle newPosition:(CGPoint)pos; @@ -617,15 +632,11 @@ -(void) postStep -(void) setTexture:(CCTexture2D*) texture { - [texture_ release]; - texture_ = [texture retain]; + if( texture_ != texture ) { + [texture_ release]; + texture_ = [texture retain]; - // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it - if( texture_ && ! [texture hasPremultipliedAlpha] && - ( blendFunc_.src == CC_BLEND_SRC && blendFunc_.dst == CC_BLEND_DST ) ) { - - blendFunc_.src = GL_SRC_ALPHA; - blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + [self updateBlendFunc]; } } @@ -642,7 +653,7 @@ -(void) setBlendAdditive:(BOOL)additive blendFunc_.dst = GL_ONE; } else { - + if( texture_ && ! [texture_ hasPremultipliedAlpha] ) { blendFunc_.src = GL_SRC_ALPHA; blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; @@ -658,7 +669,27 @@ -(BOOL) blendAdditive return( blendFunc_.src == GL_SRC_ALPHA && blendFunc_.dst == GL_ONE); } -#pragma mark ParticleSystem - Properties of Gravity Mode +-(void) setBlendFunc:(ccBlendFunc)blendFunc +{ + if( blendFunc_.src != blendFunc.src || blendFunc_.dst != blendFunc.dst ) { + blendFunc_ = blendFunc; + [self updateBlendFunc]; + } +} +#pragma mark ParticleSystem - Total Particles Property + +- (void) setTotalParticles:(NSUInteger)tp +{ + NSAssert( tp <= allocatedParticles, @"Particle: resizing particle array only supported for quads"); + totalParticles = tp; +} + +- (NSUInteger) totalParticles +{ + return totalParticles; +} + +#pragma mark ParticleSystem - Properties of Gravity Mode -(void) setTangentialAccel:(float)t { NSAssert( emitterMode_ == kCCParticleModeGravity, @"Particle Mode should be Gravity"); @@ -803,6 +834,74 @@ -(float) rotatePerSecondVar NSAssert( emitterMode_ == kCCParticleModeRadius, @"Particle Mode should be Radius"); return mode.B.rotatePerSecondVar; } -@end +#pragma mark ParticleSystem - methods for batchNode rendering + +-(CCParticleBatchNode*) batchNode +{ + return batchNode_; +} + +-(void) setBatchNode:(CCParticleBatchNode*) batchNode +{ + if( batchNode_ != batchNode ) { + + batchNode_ = batchNode; // weak reference + + if( batchNode ) { + //each particle needs a unique index + for (int i = 0; i < totalParticles; i++) + { + particles[i].atlasIndex=i; + } + } + } +} +//don't use a transform matrix, this is faster +-(void) setScale:(float) s +{ + transformSystemDirty_ = YES; + [super setScale:s]; +} + +-(void) setRotation: (float)newRotation +{ + transformSystemDirty_ = YES; + [super setRotation:newRotation]; +} + +-(void) setScaleX: (float)newScaleX +{ + transformSystemDirty_ = YES; + [super setScaleX:newScaleX]; +} + +-(void) setScaleY: (float)newScaleY +{ + transformSystemDirty_ = YES; + [super setScaleY:newScaleY]; +} + +#pragma mark Particle - Helpers + +-(void) updateBlendFunc +{ + NSAssert(! batchNode_, @"Can't change blending functions when the particle is being batched"); + + BOOL premultiplied = [texture_ hasPremultipliedAlpha]; + + opacityModifyRGB_ = NO; + + if( texture_ && ( blendFunc_.src == CC_BLEND_SRC && blendFunc_.dst == CC_BLEND_DST ) ) { + if( premultiplied ) + opacityModifyRGB_ = YES; + else { + blendFunc_.src = GL_SRC_ALPHA; + blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; + } + } +} + + +@end diff --git a/cocos2d/cocos2d/CCParticleSystemPoint.h b/cocos2d/cocos2d/CCParticleSystemPoint.h deleted file mode 100644 index f0918fe..0000000 --- a/cocos2d/cocos2d/CCParticleSystemPoint.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * cocos2d for iPhone: http://www.cocos2d-iphone.org - * - * Copyright (c) 2008-2010 Ricardo Quesada - * Copyright (c) 2011 Zynga Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - - -#import -#import "CCParticleSystem.h" - -#define CC_MAX_PARTICLE_SIZE 64 - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - -/** CCParticleSystemPoint is a subclass of CCParticleSystem - Attributes of a Particle System: - * All the attributes of Particle System - - Features: - * consumes small memory: uses 1 vertex (x,y) per particle, no need to assign tex coordinates - * size can't be bigger than 64 - * the system can't be scaled since the particles are rendered using GL_POINT_SPRITE - - Limitations: - * On 3rd gen iPhone devices and iPads, this node performs MUCH slower than CCParticleSystemQuad. - */ -@interface CCParticleSystemPoint : CCParticleSystem -{ - // Array of (x,y,size) - ccPointSprite *vertices; - // vertices buffer id -#if CC_USES_VBO - GLuint verticesID; -#endif -} -@end - -#elif __MAC_OS_X_VERSION_MAX_ALLOWED - -#import "CCParticleSystemQuad.h" - -@interface CCParticleSystemPoint : CCParticleSystemQuad -@end - -#endif diff --git a/cocos2d/cocos2d/CCParticleSystemPoint.m b/cocos2d/cocos2d/CCParticleSystemPoint.m deleted file mode 100644 index 0894d2b..0000000 --- a/cocos2d/cocos2d/CCParticleSystemPoint.m +++ /dev/null @@ -1,211 +0,0 @@ -/* - * cocos2d for iPhone: http://www.cocos2d-iphone.org - * - * Copyright (c) 2008-2010 Ricardo Quesada - * Copyright (c) 2011 Zynga Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -#import -#import "CCParticleSystemPoint.h" - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - -// opengl -#import "Platforms/CCGL.h" - -// cocos2d -#import "CCTextureCache.h" -#import "ccMacros.h" - -// support -#import "Support/OpenGL_Internal.h" -#import "Support/CGPointExtension.h" - -@implementation CCParticleSystemPoint - --(id) initWithTotalParticles:(NSUInteger) numberOfParticles -{ - if( (self=[super initWithTotalParticles:numberOfParticles]) ) { - - vertices = malloc( sizeof(ccPointSprite) * totalParticles ); - - if( ! vertices ) { - NSLog(@"cocos2d: Particle system: not enough memory"); - [self release]; - return nil; - } - -#if CC_USES_VBO - glGenBuffers(1, &verticesID); - - // initial binding - glBindBuffer(GL_ARRAY_BUFFER, verticesID); - glBufferData(GL_ARRAY_BUFFER, sizeof(ccPointSprite)*totalParticles, vertices, GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); -#endif - } - - return self; -} - --(void) dealloc -{ - free(vertices); -#if CC_USES_VBO - glDeleteBuffers(1, &verticesID); -#endif - - [super dealloc]; -} - --(void) updateQuadWithParticle:(tCCParticle*)p newPosition:(CGPoint)newPos -{ - // place vertices and colos in array - vertices[particleIdx].pos = (ccVertex2F) {newPos.x, newPos.y}; - vertices[particleIdx].size = p->size; - ccColor4B color = { p->color.r*255, p->color.g*255, p->color.b*255, p->color.a*255 }; - vertices[particleIdx].color = color; -} - --(void) postStep -{ -#if CC_USES_VBO - glBindBuffer(GL_ARRAY_BUFFER, verticesID); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(ccPointSprite)*particleCount, vertices); - glBindBuffer(GL_ARRAY_BUFFER, 0); -#endif -} - --(void) draw -{ - [super draw]; - - if (particleIdx==0) - return; - - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY - // Unneeded states: GL_TEXTURE_COORD_ARRAY - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glBindTexture(GL_TEXTURE_2D, texture_.name); - - glEnable(GL_POINT_SPRITE_OES); - glTexEnvi( GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE ); - -#define kPointSize sizeof(vertices[0]) - -#if CC_USES_VBO - glBindBuffer(GL_ARRAY_BUFFER, verticesID); - - glVertexPointer(2,GL_FLOAT, kPointSize, 0); - - glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (GLvoid*) offsetof(ccPointSprite, color) ); - - glEnableClientState(GL_POINT_SIZE_ARRAY_OES); - glPointSizePointerOES(GL_FLOAT, kPointSize, (GLvoid*) offsetof(ccPointSprite, size) ); -#else // Uses Vertex Array List - int offset = (int)vertices; - glVertexPointer(2,GL_FLOAT, kPointSize, (GLvoid*) offset); - - int diff = offsetof(ccPointSprite, color); - glColorPointer(4, GL_UNSIGNED_BYTE, kPointSize, (GLvoid*) (offset+diff)); - - glEnableClientState(GL_POINT_SIZE_ARRAY_OES); - diff = offsetof(ccPointSprite, size); - glPointSizePointerOES(GL_FLOAT, kPointSize, (GLvoid*) (offset+diff)); -#endif - - BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; - if( newBlend ) - glBlendFunc( blendFunc_.src, blendFunc_.dst ); - - - glDrawArrays(GL_POINTS, 0, particleIdx); - - // restore blend state - if( newBlend ) - glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST); - - -#if CC_USES_VBO - // unbind VBO buffer - glBindBuffer(GL_ARRAY_BUFFER, 0); -#endif - - glDisableClientState(GL_POINT_SIZE_ARRAY_OES); - glDisable(GL_POINT_SPRITE_OES); - - // restore GL default state - glEnableClientState(GL_TEXTURE_COORD_ARRAY); -} - -#pragma mark Non supported properties - -// -// SPIN IS NOT SUPPORTED -// --(void) setStartSpin:(float)a -{ - NSAssert(a == 0, @"PointParticleSystem doesn't support spinning"); - [super setStartSpin:a]; -} --(void) setStartSpinVar:(float)a -{ - NSAssert(a == 0, @"PointParticleSystem doesn't support spinning"); - [super setStartSpin:a]; -} --(void) setEndSpin:(float)a -{ - NSAssert(a == 0, @"PointParticleSystem doesn't support spinning"); - [super setStartSpin:a]; -} --(void) setEndSpinVar:(float)a -{ - NSAssert(a == 0, @"PointParticleSystem doesn't support spinning"); - [super setStartSpin:a]; -} - -// -// SIZE > 64 IS NOT SUPPORTED -// --(void) setStartSize:(float)size -{ - NSAssert(size >= 0 && size <= CC_MAX_PARTICLE_SIZE, @"PointParticleSystem only supports 0 <= size <= 64"); - [super setStartSize:size]; -} - --(void) setEndSize:(float)size -{ - NSAssert( (size == kCCParticleStartSizeEqualToEndSize) || - ( size >= 0 && size <= CC_MAX_PARTICLE_SIZE), @"PointParticleSystem only supports 0 <= size <= 64"); - [super setEndSize:size]; -} -@end - -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) -@implementation CCParticleSystemPoint -@end - -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED - - diff --git a/cocos2d/cocos2d/CCParticleSystemQuad.h b/cocos2d/cocos2d/CCParticleSystemQuad.h old mode 100644 new mode 100755 index 74a9d93..613fee3 --- a/cocos2d/cocos2d/CCParticleSystemQuad.h +++ b/cocos2d/cocos2d/CCParticleSystemQuad.h @@ -5,17 +5,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -33,26 +33,23 @@ @class CCSpriteFrame; /** CCParticleSystemQuad is a subclass of CCParticleSystem - - It includes all the features of ParticleSystem. - Special features and Limitations: + It includes all the features of ParticleSystem. + + Special features and Limitations: - Particle size can be any float number. - The system can be scaled - The particles can be rotated - - On 1st and 2nd gen iPhones: It is only a bit slower that CCParticleSystemPoint - - On 3rd gen iPhone and iPads: It is MUCH faster than CCParticleSystemPoint - - It consumes more RAM and more GPU memory than CCParticleSystemPoint - It supports subrects + - It supports batched rendering since 1.1 @since v0.8 */ @interface CCParticleSystemQuad : CCParticleSystem { - ccV2F_C4B_T2F_Quad *quads_; // quads to be rendered + ccV3F_C4B_T2F_Quad *quads_; // quads to be rendered GLushort *indices_; // indices -#if CC_USES_VBO - GLuint quadsID_; // VBO id -#endif + GLuint VAOname_; + GLuint buffersVBO_[2]; //0: vertex 1: indices } /** initialices the indices for the vertices */ diff --git a/cocos2d/cocos2d/CCParticleSystemQuad.m b/cocos2d/cocos2d/CCParticleSystemQuad.m old mode 100644 new mode 100755 index 4916964..b87504a --- a/cocos2d/cocos2d/CCParticleSystemQuad.m +++ b/cocos2d/cocos2d/CCParticleSystemQuad.m @@ -5,17 +5,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -33,71 +33,201 @@ // cocos2d #import "ccConfig.h" #import "CCParticleSystemQuad.h" +#import "CCParticleBatchNode.h" +#import "CCTextureAtlas.h" #import "CCTextureCache.h" #import "ccMacros.h" #import "CCSpriteFrame.h" +#import "CCDirector.h" +#import "CCShaderCache.h" +#import "ccGLStateCache.h" +#import "CCGLProgram.h" +#import "CCConfiguration.h" // support #import "Support/OpenGL_Internal.h" #import "Support/CGPointExtension.h" +#import "Support/TransformUtils.h" +#import "Support/NSThread+performBlock.h" -@implementation CCParticleSystemQuad +// extern +#import "kazmath/GL/matrix.h" +@interface CCParticleSystemQuad () +-(void) initVAO; +-(BOOL) allocMemory; +@end + +@implementation CCParticleSystemQuad // overriding the init method -(id) initWithTotalParticles:(NSUInteger) numberOfParticles { // base initialization if( (self=[super initWithTotalParticles:numberOfParticles]) ) { - + // allocating data space - quads_ = calloc( sizeof(quads_[0]) * totalParticles, 1 ); - indices_ = calloc( sizeof(indices_[0]) * totalParticles * 6, 1 ); - - if( !quads_ || !indices_) { - NSLog(@"cocos2d: Particle system: not enough memory"); - if( quads_ ) - free( quads_ ); - if(indices_) - free(indices_); - + if( ! [self allocMemory] ) { [self release]; return nil; } - - // initialize only once the texCoords and the indices - [self initTexCoordsWithRect:CGRectMake(0, 0, [texture_ pixelsWide], [texture_ pixelsHigh])]; + + // Don't initialize the texCoords yet since there are not textures +// [self initTexCoordsWithRect:CGRectMake(0, 0, [texture_ pixelsWide], [texture_ pixelsHigh])]; + [self initIndices]; + [self initVAO]; -#if CC_USES_VBO - // create the VBO buffer - glGenBuffers(1, &quadsID_); - - // initial binding - glBindBuffer(GL_ARRAY_BUFFER, quadsID_); - glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0])*totalParticles, quads_,GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); -#endif + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTextureColor]; } - + return self; } --(void) dealloc +-(BOOL) allocMemory +{ + NSAssert( ( !quads_ && !indices_), @"Memory already alloced"); + NSAssert( !batchNode_, @"Memory should not be alloced when not using batchNode"); + + quads_ = calloc( sizeof(quads_[0]) * totalParticles, 1 ); + indices_ = calloc( sizeof(indices_[0]) * totalParticles * 6, 1 ); + + if( !quads_ || !indices_) { + CCLOG(@"cocos2d: Particle system: not enough memory"); + if( quads_ ) + free( quads_ ); + if(indices_) + free(indices_); + + return NO; + } + + return YES; +} + +- (void) setTotalParticles:(NSUInteger)tp { - free(quads_); - free(indices_); -#if CC_USES_VBO - glDeleteBuffers(1, &quadsID_); -#endif + // If we are setting the total numer of particles to a number higher + // than what is allocated, we need to allocate new arrays + if( tp > allocatedParticles ) + { + // Allocate new memory + size_t particlesSize = tp * sizeof(tCCParticle); + size_t quadsSize = sizeof(quads_[0]) * tp * 1; + size_t indicesSize = sizeof(indices_[0]) * tp * 6 * 1; + + tCCParticle* particlesNew = realloc(particles, particlesSize); + ccV3F_C4B_T2F_Quad *quadsNew = realloc(quads_, quadsSize); + GLushort* indicesNew = realloc(indices_, indicesSize); + + if (particlesNew && quadsNew && indicesNew) + { + // Assign pointers + particles = particlesNew; + quads_ = quadsNew; + indices_ = indicesNew; + + // Clear the memory + memset(particles, 0, particlesSize); + memset(quads_, 0, quadsSize); + memset(indices_, 0, indicesSize); + + allocatedParticles = tp; + } + else + { + // Out of memory, failed to resize some array + if (particlesNew) particles = particlesNew; + if (quadsNew) quads_ = quadsNew; + if (indicesNew) indices_ = indicesNew; + + CCLOG(@"Particle system: out of memory"); + return; + } + + totalParticles = tp; + + // Init particles + if (batchNode_) + { + for (int i = 0; i < totalParticles; i++) + { + particles[i].atlasIndex=i; + } + } + + [self initIndices]; + [self initVAO]; + } + else + { + totalParticles = tp; + } +} + +-(void) initVAO +{ + // VAO requires GL_APPLE_vertex_array_object in order to be created on a different thread + // https://devforums.apple.com/thread/145566?tstart=0 + + void (^createVAO)(void) = ^ { + glGenVertexArrays(1, &VAOname_); + glBindVertexArray(VAOname_); + + #define kQuadSize sizeof(quads_[0].bl) + + glGenBuffers(2, &buffersVBO_[0]); + + glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * totalParticles, quads_, GL_DYNAMIC_DRAW); + + // vertices + glEnableVertexAttribArray(kCCVertexAttrib_Position); + glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices)); + + // colors + glEnableVertexAttribArray(kCCVertexAttrib_Color); + glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors)); + + // tex coords + glEnableVertexAttribArray(kCCVertexAttrib_TexCoords); + glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords)); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices_[0]) * totalParticles * 6, indices_, GL_STATIC_DRAW); + + glBindVertexArray(0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + CHECK_GL_ERROR_DEBUG(); + }; + NSThread *cocos2dThread = [[CCDirector sharedDirector] runningThread]; + if( cocos2dThread == [NSThread currentThread] || [[CCConfiguration sharedConfiguration] supportsShareableVAO] ) + createVAO(); + else + [cocos2dThread performBlock:createVAO waitUntilDone:YES]; +} + +-(void) dealloc +{ + if( ! batchNode_ ) { + free(quads_); + free(indices_); + + glDeleteBuffers(2, &buffersVBO_[0]); + glDeleteVertexArrays(1, &VAOname_); + } + [super dealloc]; } // pointRect is in Points coordinates. -(void) initTexCoordsWithRect:(CGRect)pointRect { - // convert to pixels coords + // convert to Tex coords + CGRect rect = CGRectMake( pointRect.origin.x * CC_CONTENT_SCALE_FACTOR(), pointRect.origin.y * CC_CONTENT_SCALE_FACTOR(), @@ -118,23 +248,39 @@ -(void) initTexCoordsWithRect:(CGRect)pointRect GLfloat right = left + rect.size.width / wide; GLfloat top = bottom + rect.size.height / high; #endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL - + // Important. Texture in cocos2d are inverted, so the Y component should be inverted CC_SWAP( top, bottom); - - for(NSUInteger i=0; icolor.r*255, p->color.g*255, p->color.b*255, p->color.a*255}; + ccV3F_C4B_T2F_Quad *quad; + + if (batchNode_) + { + ccV3F_C4B_T2F_Quad *batchQuads = [[batchNode_ textureAtlas] quads]; + quad = &(batchQuads[atlasIndex_+p->atlasIndex]); + } + else + quad = &(quads_[particleIdx]); + + ccColor4B color = (opacityModifyRGB_) + ? (ccColor4B){ p->color.r*p->color.a*255, p->color.g*p->color.a*255, p->color.b*p->color.a*255, p->color.a*255} + : (ccColor4B){ p->color.r*255, p->color.g*255, p->color.b*255, p->color.a*255}; + quad->bl.colors = color; quad->br.colors = color; quad->tl.colors = color; quad->tr.colors = color; - + // vertices GLfloat size_2 = p->size/2; if( p->rotation ) { GLfloat x1 = -size_2; GLfloat y1 = -size_2; - + GLfloat x2 = size_2; GLfloat y2 = size_2; GLfloat x = newPos.x; GLfloat y = newPos.y; - + GLfloat r = (GLfloat)-CC_DEGREES_TO_RADIANS(p->rotation); GLfloat cr = cosf(r); GLfloat sr = sinf(r); @@ -211,19 +367,19 @@ -(void) updateQuadWithParticle:(tCCParticle*)p newPosition:(CGPoint)newPos GLfloat cy = x2 * sr + y2 * cr + y; GLfloat dx = x1 * cr - y2 * sr + x; GLfloat dy = x1 * sr + y2 * cr + y; - + // bottom-left quad->bl.vertices.x = ax; quad->bl.vertices.y = ay; - + // bottom-right vertex: quad->br.vertices.x = bx; quad->br.vertices.y = by; - + // top-left vertex: quad->tl.vertices.x = dx; quad->tl.vertices.y = dy; - + // top-right vertex: quad->tr.vertices.x = cx; quad->tr.vertices.y = cy; @@ -231,88 +387,89 @@ -(void) updateQuadWithParticle:(tCCParticle*)p newPosition:(CGPoint)newPos // bottom-left vertex: quad->bl.vertices.x = newPos.x - size_2; quad->bl.vertices.y = newPos.y - size_2; - + // bottom-right vertex: quad->br.vertices.x = newPos.x + size_2; quad->br.vertices.y = newPos.y - size_2; - + // top-left vertex: quad->tl.vertices.x = newPos.x - size_2; quad->tl.vertices.y = newPos.y + size_2; - + // top-right vertex: quad->tr.vertices.x = newPos.x + size_2; - quad->tr.vertices.y = newPos.y + size_2; + quad->tr.vertices.y = newPos.y + size_2; } } -(void) postStep { -#if CC_USES_VBO - glBindBuffer(GL_ARRAY_BUFFER, quadsID_); + glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0] ); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(quads_[0])*particleCount, quads_); glBindBuffer(GL_ARRAY_BUFFER, 0); -#endif + + CHECK_GL_ERROR_DEBUG(); } // overriding draw method -(void) draw -{ - [super draw]; +{ + NSAssert(!batchNode_,@"draw should not be called when added to a particleBatchNode"); - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Unneeded states: - + CC_NODE_DRAW_SETUP(); - glBindTexture(GL_TEXTURE_2D, [texture_ name]); + ccGLBindTexture2D( [texture_ name] ); + ccGLBlendFunc( blendFunc_.src, blendFunc_.dst ); -#define kQuadSize sizeof(quads_[0].bl) + NSAssert( particleIdx == particleCount, @"Abnormal error in particle quad"); -#if CC_USES_VBO - glBindBuffer(GL_ARRAY_BUFFER, quadsID_); + glBindVertexArray( VAOname_ ); - glVertexPointer(2,GL_FLOAT, kQuadSize, 0); + glDrawElements(GL_TRIANGLES, (GLsizei) particleIdx*6, GL_UNSIGNED_SHORT, 0); - glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*) offsetof(ccV2F_C4B_T2F,colors) ); - - glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*) offsetof(ccV2F_C4B_T2F,texCoords) ); -#else // vertex array list + glBindVertexArray( 0 ); - NSUInteger offset = (NSUInteger) quads_; + CC_INCREMENT_GL_DRAWS(1); - // vertex - NSUInteger diff = offsetof( ccV2F_C4B_T2F, vertices); - glVertexPointer(2,GL_FLOAT, kQuadSize, (GLvoid*) (offset+diff) ); - - // color - diff = offsetof( ccV2F_C4B_T2F, colors); - glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*)(offset + diff)); - - // tex coords - diff = offsetof( ccV2F_C4B_T2F, texCoords); - glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*)(offset + diff)); + CHECK_GL_ERROR_DEBUG(); +} -#endif // ! CC_USES_VBO - - BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; - if( newBlend ) - glBlendFunc( blendFunc_.src, blendFunc_.dst ); - - NSAssert( particleIdx == particleCount, @"Abnormal error in particle quad"); - glDrawElements(GL_TRIANGLES, (GLsizei) particleIdx*6, GL_UNSIGNED_SHORT, indices_); - - // restore blend state - if( newBlend ) - glBlendFunc( CC_BLEND_SRC, CC_BLEND_DST ); +-(void) setBatchNode:(CCParticleBatchNode *)batchNode +{ + if( batchNode_ != batchNode ) { -#if CC_USES_VBO - glBindBuffer(GL_ARRAY_BUFFER, 0); -#endif + CCParticleBatchNode *oldBatch = batchNode_; - // restore GL default state - // - -} + [super setBatchNode:batchNode]; -@end + // NEW: is self render ? + if( ! batchNode ) { + [self allocMemory]; + [self initIndices]; + [self setTexture:[oldBatch texture]]; + [self initVAO]; + } + + // OLD: was it self render ? cleanup + else if( ! oldBatch ) + { + // copy current state to batch + ccV3F_C4B_T2F_Quad *batchQuads = [[batchNode_ textureAtlas] quads]; + ccV3F_C4B_T2F_Quad *quad = &(batchQuads[atlasIndex_] ); + memcpy( quad, quads_, totalParticles * sizeof(quads_[0]) ); + + if (quads_) + free(quads_); + quads_ = NULL; + if (indices_) + free(indices_); + indices_ = NULL; + + glDeleteBuffers(2, &buffersVBO_[0]); + glDeleteVertexArrays(1, &VAOname_); + } + } +} +@end diff --git a/cocos2d/cocos2d/CCProgressTimer.h b/cocos2d/cocos2d/CCProgressTimer.h old mode 100644 new mode 100755 index 9a07f2f..ce058d9 --- a/cocos2d/cocos2d/CCProgressTimer.h +++ b/cocos2d/cocos2d/CCProgressTimer.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,18 +30,10 @@ @since v0.99.1 */ typedef enum { - /// Radial Counter-Clockwise - kCCProgressTimerTypeRadialCCW, - /// Radial ClockWise - kCCProgressTimerTypeRadialCW, - /// Horizontal Left-Right - kCCProgressTimerTypeHorizontalBarLR, - /// Horizontal Right-Left - kCCProgressTimerTypeHorizontalBarRL, - /// Vertical Bottom-top - kCCProgressTimerTypeVerticalBarBT, - /// Vertical Top-Bottom - kCCProgressTimerTypeVerticalBarTB, + /// Radial Counter-Clockwise + kCCProgressTimerTypeRadial, + /// Bar + kCCProgressTimerTypeBar, } CCProgressTimerType; /** @@ -50,18 +42,44 @@ typedef enum { The progress can be Radial, Horizontal or vertical. @since v0.99.1 */ -@interface CCProgressTimer : CCNode -{ +@interface CCProgressTimer : CCNode { CCProgressTimerType type_; float percentage_; CCSprite *sprite_; - + int vertexDataCount_; ccV2F_C4B_T2F *vertexData_; + CGPoint midpoint_; + CGPoint barChangeRate_; + BOOL reverseDirection_; } - +@property (nonatomic) ccColor3B color; +@property (nonatomic) GLubyte opacity; /** Change the percentage to change progress. */ @property (nonatomic, readwrite) CCProgressTimerType type; +@property (nonatomic, readwrite) BOOL reverseDirection; +@property (nonatomic, readonly) ccV2F_C4B_T2F *vertexData; +@property (nonatomic, readonly) int vertexDataCount; + +/** + * Midpoint is used to modify the progress start position. + * If you're using radials type then the midpoint changes the center point + * If you're using bar type the the midpoint changes the bar growth + * it expands from the center but clamps to the sprites edge so: + * you want a left to right then set the midpoint all the way to ccp(0,y) + * you want a right to left then set the midpoint all the way to ccp(1,y) + * you want a bottom to top then set the midpoint all the way to ccp(x,0) + * you want a top to bottom then set the midpoint all the way to ccp(x,1) + */ +@property (nonatomic, readwrite) CGPoint midpoint; + +/** + * This allows the bar type to move the component at a specific rate + * Set the component to 0 to make sure it stays at 100%. + * For example you want a left to right bar but not have the height stay 100% + * Set the rate to be ccp(0,1); and set the midpoint to = ccp(0,.5f); + */ +@property (nonatomic, readwrite) CGPoint barChangeRate; /** Percentages are from 0 to 100 */ @property (nonatomic, readwrite) float percentage; @@ -69,15 +87,8 @@ typedef enum { /** The image to show the progress percentage */ @property (nonatomic, readwrite, retain) CCSprite *sprite; - -/** Creates a progress timer with an image filename as the shape the timer goes through */ -+ (id) progressWithFile:(NSString*) filename; -/** Initializes a progress timer with an image filename as the shape the timer goes through */ -- (id) initWithFile:(NSString*) filename; - -/** Creates a progress timer with the texture as the shape the timer goes through */ -+ (id) progressWithTexture:(CCTexture2D*) texture; -/** Creates a progress timer with the texture as the shape the timer goes through */ -- (id) initWithTexture:(CCTexture2D*) texture; - +/** Creates a progress timer with the sprite as the shape the timer goes through */ ++ (id) progressWithSprite:(CCSprite*) sprite; +/** Initializes a progress timer with the sprite as the shape the timer goes through */ +- (id) initWithSprite:(CCSprite*) sprite; @end diff --git a/cocos2d/cocos2d/CCProgressTimer.m b/cocos2d/cocos2d/CCProgressTimer.m old mode 100644 new mode 100755 index 4e697b2..084b0c6 --- a/cocos2d/cocos2d/CCProgressTimer.m +++ b/cocos2d/cocos2d/CCProgressTimer.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,15 +27,22 @@ #import "ccMacros.h" #import "CCTextureCache.h" +#import "CCGLProgram.h" +#import "CCShaderCache.h" +#import "ccGLStateCache.h" +#import "CCDirector.h" #import "Support/CGPointExtension.h" +#import "Support/TransformUtils.h" +#import "CCDrawingPrimitives.h" - +// extern +#import "kazmath/GL/matrix.h" #define kProgressTextureCoordsCount 4 -// kProgressTextureCoords holds points {0,0} {0,1} {1,1} {1,0} we can represent it as bits -const char kProgressTextureCoords = 0x1e; +// kProgressTextureCoords holds points {0,1} {0,0} {1,0} {1,1} we can represent it as bits +const char kCCProgressTextureCoords = 0x4b; -@interface CCProgressTimer (Internal) +@interface CCProgressTimer () -(void)updateProgress; -(void)updateBar; @@ -49,38 +56,47 @@ @implementation CCProgressTimer @synthesize percentage = percentage_; @synthesize sprite = sprite_; @synthesize type = type_; +@synthesize reverseDirection = reverseDirection_; +@synthesize midpoint = midpoint_; +@synthesize barChangeRate = barChangeRate_; +@synthesize vertexData = vertexData_; +@synthesize vertexDataCount = vertexDataCount_; -+(id)progressWithFile:(NSString*) filename -{ - return [[[self alloc]initWithFile:filename] autorelease]; -} --(id)initWithFile:(NSString*) filename ++(id)progressWithSprite:(CCSprite*) sprite { - return [self initWithTexture:[[CCTextureCache sharedTextureCache] addImage: filename]]; + return [[[self alloc]initWithSprite:sprite] autorelease]; } -+(id)progressWithTexture:(CCTexture2D*) texture +-(id) init { - return [[[self alloc]initWithTexture:texture] autorelease]; + return [self initWithSprite:nil]; } --(id)initWithTexture:(CCTexture2D*) texture + +// designated initializer +-(id)initWithSprite:(CCSprite*) sprite { if(( self = [super init] )){ - self.sprite = [CCSprite spriteWithTexture:texture]; percentage_ = 0.f; vertexData_ = NULL; vertexDataCount_ = 0; - self.anchorPoint = ccp(.5f,.5f); - self.contentSize = sprite_.contentSize; - self.type = kCCProgressTimerTypeRadialCCW; + self.anchorPoint = ccp(0.5f,0.5f); + self.type = kCCProgressTimerTypeRadial; + self.reverseDirection = NO; + self.midpoint = ccp(.5f, .5f); + self.barChangeRate = ccp(1,1); + self.sprite = sprite; + + // shader program + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTextureColor]; } return self; } + -(void)dealloc { - if(vertexData_) + if(vertexData_){ free(vertexData_); - + } [sprite_ release]; [super dealloc]; } @@ -88,16 +104,18 @@ -(void)dealloc -(void)setPercentage:(float) percentage { if(percentage_ != percentage) { - percentage_ = clampf( percentage, 0, 100); + percentage_ = clampf( percentage, 0, 100); [self updateProgress]; } } + -(void)setSprite:(CCSprite *)newSprite { if(sprite_ != newSprite){ - [sprite_ release]; + [sprite_ release]; sprite_ = [newSprite retain]; - + self.contentSize = sprite_.contentSize; + // Everytime we set a new sprite, we free the current vertex data if(vertexData_){ free(vertexData_); @@ -106,10 +124,11 @@ -(void)setSprite:(CCSprite *)newSprite } } } + -(void)setType:(CCProgressTimerType)newType { if (newType != type_) { - + // release all previous information if(vertexData_){ free(vertexData_); @@ -119,45 +138,83 @@ -(void)setType:(CCProgressTimerType)newType type_ = newType; } } -@end -@implementation CCProgressTimer(Internal) +-(void)setReverseProgress:(BOOL)reverse +{ + if( reverseDirection_ != reverse ) { + reverseDirection_ = reverse; + + // release all previous information + if(vertexData_){ + free(vertexData_); + vertexData_ = NULL; + vertexDataCount_ = 0; + } + } +} + +-(void)setColor:(ccColor3B)c +{ + sprite_.color = c; + [self updateColor]; +} + +-(ccColor3B)color +{ + return sprite_.color; +} + +-(void)setOpacity:(GLubyte)o +{ + sprite_.opacity = o; + [self updateColor]; +} + +-(GLubyte)opacity +{ + return sprite_.opacity; +} + +#pragma mark ProgressTimer Internal /// // @returns the vertex position from the texture coordinate /// --(ccVertex2F)vertexFromTexCoord:(CGPoint) texCoord +-(ccTex2F)textureCoordFromAlphaPoint:(CGPoint) alpha { - CGPoint tmp; - ccVertex2F ret; - if (sprite_.texture) { - CCTexture2D *texture = [sprite_ texture]; - CGSize texSize = [texture contentSizeInPixels]; - tmp = ccp(texSize.width * texCoord.x/texture.maxS, - texSize.height * (1 - (texCoord.y/texture.maxT))); - } else - tmp = CGPointZero; - - ret.x = tmp.x; - ret.y = tmp.y; - return ret; + if (!sprite_) { + return (ccTex2F){0,0}; + } + ccV3F_C4B_T2F_Quad quad = sprite_.quad; + CGPoint min = (CGPoint){quad.bl.texCoords.u,quad.bl.texCoords.v}; + CGPoint max = (CGPoint){quad.tr.texCoords.u,quad.tr.texCoords.v}; + // Fix bug #1303 so that progress timer handles sprite frame texture rotation + if (sprite_.textureRectRotated) { + CC_SWAP(alpha.x, alpha.y); + } + return (ccTex2F){min.x * (1.f - alpha.x) + max.x * alpha.x, min.y * (1.f - alpha.y) + max.y * alpha.y}; +} + +-(ccVertex2F)vertexFromAlphaPoint:(CGPoint) alpha +{ + if (!sprite_) { + return (ccVertex2F){0.f, 0.f}; + } + ccV3F_C4B_T2F_Quad quad = sprite_.quad; + CGPoint min = (CGPoint){quad.bl.vertices.x,quad.bl.vertices.y}; + CGPoint max = (CGPoint){quad.tr.vertices.x,quad.tr.vertices.y}; + return (ccVertex2F){min.x * (1.f - alpha.x) + max.x * alpha.x, min.y * (1.f - alpha.y) + max.y * alpha.y}; } -(void)updateColor { - GLubyte op = sprite_.opacity; - ccColor3B c3b = sprite_.color; - - ccColor4B color = { c3b.r, c3b.g, c3b.b, op }; - if([sprite_.texture hasPremultipliedAlpha]){ - color.r *= op/255; - color.g *= op/255; - color.b *= op/255; + if (!sprite_) { + return; } - if(vertexData_){ + ccColor4B sc = sprite_.quad.tl.colors; for (int i=0; i < vertexDataCount_; ++i) { - vertexData_[i].colors = color; + vertexData_[i].colors = sc; } } } @@ -165,14 +222,10 @@ -(void)updateColor -(void)updateProgress { switch (type_) { - case kCCProgressTimerTypeRadialCW: - case kCCProgressTimerTypeRadialCCW: + case kCCProgressTimerTypeRadial: [self updateRadial]; break; - case kCCProgressTimerTypeHorizontalBarLR: - case kCCProgressTimerTypeHorizontalBarRL: - case kCCProgressTimerTypeVerticalBarBT: - case kCCProgressTimerTypeVerticalBarTB: + case kCCProgressTimerTypeBar: [self updateBar]; break; default: @@ -180,38 +233,50 @@ -(void)updateProgress } } +-(void)setAnchorPoint:(CGPoint)anchorPoint +{ + [super setAnchorPoint:anchorPoint]; +} + +-(CGPoint) midpoint +{ + return midpoint_; +} + +-(void)setMidpoint:(CGPoint)midPoint +{ + midpoint_ = ccpClamp(midPoint, CGPointZero, ccp(1,1)); +} + /// // Update does the work of mapping the texture onto the triangles // It now doesn't occur the cost of free/alloc data every update cycle. // It also only changes the percentage point but no other points if they have not // been modified. -// +// // It now deals with flipped texture. If you run into this problem, just use the // sprite property and enable the methods flipX, flipY. /// -(void)updateRadial -{ - // Texture Max is the actual max coordinates to deal with non-power of 2 textures - CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT); - - // Grab the midpoint - CGPoint midpoint = ccpCompMult(self.anchorPoint, tMax); - +{ + if (!sprite_) { + return; + } + float alpha = percentage_ / 100.f; - - // Otherwise we can get the angle from the alpha - float angle = 2.f*((float)M_PI) * ( type_ == kCCProgressTimerTypeRadialCW? alpha : 1.f - alpha); - + + float angle = 2.f*((float)M_PI) * ( reverseDirection_ == YES ? alpha : 1.f - alpha); + // We find the vector to do a hit detection based on the percentage - // We know the first vector is the one @ 12 o'clock (top,mid) so we rotate - // from that by the progress angle around the midpoint pivot - CGPoint topMid = ccp(midpoint.x, 0.f); - CGPoint percentagePt = ccpRotateByAngle(topMid, midpoint, angle); - - + // We know the first vector is the one @ 12 o'clock (top,mid) so we rotate + // from that by the progress angle around the midpoint_ pivot + CGPoint topMid = ccp(midpoint_.x, 1.f); + CGPoint percentagePt = ccpRotateByAngle(topMid, midpoint_, angle); + + int index = 0; CGPoint hit = CGPointZero; - + if (alpha == 0.f) { // More efficient since we don't always need to check intersection // If the alpha is zero then the hit point is top mid and the index is 0. @@ -226,28 +291,28 @@ -(void)updateRadial // We run a for loop checking the edges of the texture to find the // intersection point // We loop through five points since the top is split in half - + float min_t = FLT_MAX; - + for (int i = 0; i <= kProgressTextureCoordsCount; ++i) { int pIndex = (i + (kProgressTextureCoordsCount - 1))%kProgressTextureCoordsCount; - - CGPoint edgePtA = ccpCompMult([self boundaryTexCoord:i % kProgressTextureCoordsCount],tMax); - CGPoint edgePtB = ccpCompMult([self boundaryTexCoord:pIndex],tMax); - + + CGPoint edgePtA = [self boundaryTexCoord:i % kProgressTextureCoordsCount]; + CGPoint edgePtB = [self boundaryTexCoord:pIndex]; + // Remember that the top edge is split in half for the 12 o'clock position // Let's deal with that here by finding the correct endpoints if(i == 0){ - edgePtB = ccpLerp(edgePtA,edgePtB,.5f); + edgePtB = ccpLerp(edgePtA, edgePtB, 1 - midpoint_.x); } else if(i == 4){ - edgePtA = ccpLerp(edgePtA,edgePtB,.5f); + edgePtA = ccpLerp(edgePtA, edgePtB, 1 - midpoint_.x); } - + // s and t are returned by ccpLineIntersect float s = 0, t = 0; - if(ccpLineIntersect(edgePtA, edgePtB, midpoint, percentagePt, &s, &t)) + if(ccpLineIntersect(edgePtA, edgePtB, midpoint_, percentagePt, &s, &t)) { - + // Since our hit test is on rays we have to deal with the top edge // being in split in half so we have to test as a segment if ((i == 0 || i == 4)) { @@ -256,8 +321,8 @@ -(void)updateRadial continue; } } - // As long as our t isn't negative we are at least finding a - // correct hitpoint from midpoint to percentagePt. + // As long as our t isn't negative we are at least finding a + // correct hitpoint from midpoint_ to percentagePt. if (t >= 0.f) { // Because the percentage line and all the texture edges are // rays we should only account for the shortest intersection @@ -268,16 +333,16 @@ -(void)updateRadial } } } - + // Now that we have the minimum magnitude we can use that to find our intersection - hit = ccpAdd(midpoint, ccpMult(ccpSub(percentagePt, midpoint),min_t)); - + hit = ccpAdd(midpoint_, ccpMult(ccpSub(percentagePt, midpoint_),min_t)); + } - - + + // The size of the vertex data is the index from the hitpoint - // the 3 is for the midpoint, 12 o'clock point and hitpoint position. - + // the 3 is for the midpoint_, 12 o'clock point and hitpoint position. + BOOL sameIndexCount = YES; if(vertexDataCount_ != index + 3){ sameIndexCount = NO; @@ -287,58 +352,35 @@ -(void)updateRadial vertexDataCount_ = 0; } } - - + + if(!vertexData_) { vertexDataCount_ = index + 3; vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F)); NSAssert( vertexData_, @"CCProgressTimer. Not enough memory"); - - [self updateColor]; } - + [self updateColor]; + if (!sameIndexCount) { - - // First we populate the array with the midpoint, then all + + // First we populate the array with the midpoint_, then all // vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint - vertexData_[0].texCoords = (ccTex2F){midpoint.x, midpoint.y}; - vertexData_[0].vertices = [self vertexFromTexCoord:midpoint]; - - vertexData_[1].texCoords = (ccTex2F){midpoint.x, 0.f}; - vertexData_[1].vertices = [self vertexFromTexCoord:ccp(midpoint.x, 0.f)]; - + vertexData_[0].texCoords = [self textureCoordFromAlphaPoint:midpoint_]; + vertexData_[0].vertices = [self vertexFromAlphaPoint:midpoint_]; + + vertexData_[1].texCoords = [self textureCoordFromAlphaPoint:topMid]; + vertexData_[1].vertices = [self vertexFromAlphaPoint:topMid]; + for(int i = 0; i < index; ++i){ - CGPoint texCoords = ccpCompMult([self boundaryTexCoord:i], tMax); - - vertexData_[i+2].texCoords = (ccTex2F){texCoords.x, texCoords.y}; - vertexData_[i+2].vertices = [self vertexFromTexCoord:texCoords]; - } - - // Flip the texture coordinates if set - if (sprite_.flipY || sprite_.flipX) { - for(int i = 0; i < vertexDataCount_ - 1; ++i){ - if (sprite_.flipX) { - vertexData_[i].texCoords.u = tMax.x - vertexData_[i].texCoords.u; - } - if(sprite_.flipY){ - vertexData_[i].texCoords.v = tMax.y - vertexData_[i].texCoords.v; - } - } + CGPoint alphaPoint = [self boundaryTexCoord:i]; + vertexData_[i+2].texCoords = [self textureCoordFromAlphaPoint:alphaPoint]; + vertexData_[i+2].vertices = [self vertexFromAlphaPoint:alphaPoint]; } } - + // hitpoint will go last - vertexData_[vertexDataCount_ - 1].texCoords = (ccTex2F){hit.x, hit.y}; - vertexData_[vertexDataCount_ - 1].vertices = [self vertexFromTexCoord:hit]; - - if (sprite_.flipY || sprite_.flipX) { - if (sprite_.flipX) { - vertexData_[vertexDataCount_ - 1].texCoords.u = tMax.x - vertexData_[vertexDataCount_ - 1].texCoords.u; - } - if(sprite_.flipY){ - vertexData_[vertexDataCount_ - 1].texCoords.v = tMax.y - vertexData_[vertexDataCount_ - 1].texCoords.v; - } - } + vertexData_[vertexDataCount_ - 1].texCoords = [self textureCoordFromAlphaPoint:hit]; + vertexData_[vertexDataCount_ - 1].vertices = [self vertexFromAlphaPoint:hit]; } /// @@ -346,148 +388,153 @@ -(void)updateRadial // It now doesn't occur the cost of free/alloc data every update cycle. // It also only changes the percentage point but no other points if they have not // been modified. -// +// // It now deals with flipped texture. If you run into this problem, just use the // sprite property and enable the methods flipX, flipY. /// -(void)updateBar -{ - +{ + if (!sprite_) { + return; + } float alpha = percentage_ / 100.f; - - CGPoint tMax = ccp(sprite_.texture.maxS,sprite_.texture.maxT); - - unsigned char vIndexes[2] = {0,0}; - unsigned char index = 0; - - // We know vertex data is always equal to the 4 corners - // If we don't have vertex data then we create it here and populate - // the side of the bar vertices that won't ever change. - if (!vertexData_) { - vertexDataCount_ = kProgressTextureCoordsCount; - vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F)); - NSAssert( vertexData_, @"CCProgressTimer. Not enough memory"); - - if(type_ == kCCProgressTimerTypeHorizontalBarLR){ - vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0,0}; - vertexData_[vIndexes[1] = 1].texCoords = (ccTex2F){0, tMax.y}; - }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) { - vertexData_[vIndexes[0] = 2].texCoords = (ccTex2F){tMax.x, tMax.y}; - vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, 0.f}; - }else if (type_ == kCCProgressTimerTypeVerticalBarBT) { - vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y}; - vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y}; - }else if (type_ == kCCProgressTimerTypeVerticalBarTB) { - vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, 0}; - vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, 0}; - } - - index = vIndexes[0]; - vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; - - index = vIndexes[1]; - vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; - - if (sprite_.flipY || sprite_.flipX) { - if (sprite_.flipX) { - index = vIndexes[0]; - vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; - index = vIndexes[1]; - vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; - } - if(sprite_.flipY){ - index = vIndexes[0]; - vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; - index = vIndexes[1]; - vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; - } - } - - [self updateColor]; + CGPoint alphaOffset = ccpMult(ccp(1.f * (1.f - barChangeRate_.x) + alpha * barChangeRate_.x, 1.f * (1.f - barChangeRate_.y) + alpha * barChangeRate_.y), .5f); + CGPoint min = ccpSub(midpoint_, alphaOffset); + CGPoint max = ccpAdd(midpoint_, alphaOffset); + + if (min.x < 0.f) { + max.x += -min.x; + min.x = 0.f; + } + + if (max.x > 1.f) { + min.x -= max.x - 1.f; + max.x = 1.f; + } + + if (min.y < 0.f) { + max.y += -min.y; + min.y = 0.f; } - - if(type_ == kCCProgressTimerTypeHorizontalBarLR){ - vertexData_[vIndexes[0] = 3].texCoords = (ccTex2F){tMax.x*alpha, tMax.y}; - vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x*alpha, 0}; - }else if (type_ == kCCProgressTimerTypeHorizontalBarRL) { - vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){tMax.x*(1.f - alpha), 0}; - vertexData_[vIndexes[1] = 0].texCoords = (ccTex2F){tMax.x*(1.f - alpha), tMax.y}; - }else if (type_ == kCCProgressTimerTypeVerticalBarBT) { - vertexData_[vIndexes[0] = 0].texCoords = (ccTex2F){0, tMax.y*(1.f - alpha)}; - vertexData_[vIndexes[1] = 2].texCoords = (ccTex2F){tMax.x, tMax.y*(1.f - alpha)}; - }else if (type_ == kCCProgressTimerTypeVerticalBarTB) { - vertexData_[vIndexes[0] = 1].texCoords = (ccTex2F){0, tMax.y*alpha}; - vertexData_[vIndexes[1] = 3].texCoords = (ccTex2F){tMax.x, tMax.y*alpha}; + + if (max.y > 1.f) { + min.y -= max.y - 1.f; + max.y = 1.f; } - - index = vIndexes[0]; - vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; - index = vIndexes[1]; - vertexData_[index].vertices = [self vertexFromTexCoord:ccp(vertexData_[index].texCoords.u, vertexData_[index].texCoords.v)]; - - if (sprite_.flipY || sprite_.flipX) { - if (sprite_.flipX) { - index = vIndexes[0]; - vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; - index = vIndexes[1]; - vertexData_[index].texCoords.u = tMax.x - vertexData_[index].texCoords.u; + + + if (!reverseDirection_) { + if(!vertexData_) { + vertexDataCount_ = 4; + vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F)); + NSAssert( vertexData_, @"CCProgressTimer. Not enough memory"); } - if(sprite_.flipY){ - index = vIndexes[0]; - vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; - index = vIndexes[1]; - vertexData_[index].texCoords.v = tMax.y - vertexData_[index].texCoords.v; + // TOPLEFT + vertexData_[0].texCoords = [self textureCoordFromAlphaPoint:ccp(min.x,max.y)]; + vertexData_[0].vertices = [self vertexFromAlphaPoint:ccp(min.x,max.y)]; + + // BOTLEFT + vertexData_[1].texCoords = [self textureCoordFromAlphaPoint:ccp(min.x,min.y)]; + vertexData_[1].vertices = [self vertexFromAlphaPoint:ccp(min.x,min.y)]; + + // TOPRIGHT + vertexData_[2].texCoords = [self textureCoordFromAlphaPoint:ccp(max.x,max.y)]; + vertexData_[2].vertices = [self vertexFromAlphaPoint:ccp(max.x,max.y)]; + + // BOTRIGHT + vertexData_[3].texCoords = [self textureCoordFromAlphaPoint:ccp(max.x,min.y)]; + vertexData_[3].vertices = [self vertexFromAlphaPoint:ccp(max.x,min.y)]; + } else { + if(!vertexData_) { + vertexDataCount_ = 8; + vertexData_ = malloc(vertexDataCount_ * sizeof(ccV2F_C4B_T2F)); + NSAssert( vertexData_, @"CCProgressTimer. Not enough memory"); + // TOPLEFT 1 + vertexData_[0].texCoords = [self textureCoordFromAlphaPoint:ccp(0,1)]; + vertexData_[0].vertices = [self vertexFromAlphaPoint:ccp(0,1)]; + + // BOTLEFT 1 + vertexData_[1].texCoords = [self textureCoordFromAlphaPoint:ccp(0,0)]; + vertexData_[1].vertices = [self vertexFromAlphaPoint:ccp(0,0)]; + + // TOPRIGHT 2 + vertexData_[6].texCoords = [self textureCoordFromAlphaPoint:ccp(1,1)]; + vertexData_[6].vertices = [self vertexFromAlphaPoint:ccp(1,1)]; + + // BOTRIGHT 2 + vertexData_[7].texCoords = [self textureCoordFromAlphaPoint:ccp(1,0)]; + vertexData_[7].vertices = [self vertexFromAlphaPoint:ccp(1,0)]; } + + // TOPRIGHT 1 + vertexData_[2].texCoords = [self textureCoordFromAlphaPoint:ccp(min.x,max.y)]; + vertexData_[2].vertices = [self vertexFromAlphaPoint:ccp(min.x,max.y)]; + + // BOTRIGHT 1 + vertexData_[3].texCoords = [self textureCoordFromAlphaPoint:ccp(min.x,min.y)]; + vertexData_[3].vertices = [self vertexFromAlphaPoint:ccp(min.x,min.y)]; + + // TOPLEFT 2 + vertexData_[4].texCoords = [self textureCoordFromAlphaPoint:ccp(max.x,max.y)]; + vertexData_[4].vertices = [self vertexFromAlphaPoint:ccp(max.x,max.y)]; + + // BOTLEFT 2 + vertexData_[5].texCoords = [self textureCoordFromAlphaPoint:ccp(max.x,min.y)]; + vertexData_[5].vertices = [self vertexFromAlphaPoint:ccp(max.x,min.y)]; } - + [self updateColor]; } -(CGPoint)boundaryTexCoord:(char)index { if (index < kProgressTextureCoordsCount) { - switch (type_) { - case kCCProgressTimerTypeRadialCW: - return ccp((kProgressTextureCoords>>((index<<1)+1))&1,(kProgressTextureCoords>>(index<<1))&1); - case kCCProgressTimerTypeRadialCCW: - return ccp((kProgressTextureCoords>>(7-(index<<1)))&1,(kProgressTextureCoords>>(7-((index<<1)+1)))&1); - default: - break; + if (reverseDirection_) { + return ccp((kCCProgressTextureCoords>>(7-(index<<1)))&1,(kCCProgressTextureCoords>>(7-((index<<1)+1)))&1); + } else { + return ccp((kCCProgressTextureCoords>>((index<<1)+1))&1,(kCCProgressTextureCoords>>(index<<1))&1); } } return CGPointZero; } --(void)draw +-(void) draw { - [super draw]; - - if(!vertexData_)return; - if(!sprite_)return; - ccBlendFunc blendFunc = sprite_.blendFunc; - BOOL newBlend = blendFunc.src != CC_BLEND_SRC || blendFunc.dst != CC_BLEND_DST; - if( newBlend ) - glBlendFunc( blendFunc.src, blendFunc.dst ); - - /// ======================================================================== - // Replaced [texture_ drawAtPoint:CGPointZero] with my own vertexData - // Everything above me and below me is copied from CCTextureNode's draw - glBindTexture(GL_TEXTURE_2D, sprite_.texture.name); - glVertexPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].vertices); - glTexCoordPointer(2, GL_FLOAT, sizeof(ccV2F_C4B_T2F), &vertexData_[0].texCoords); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ccV2F_C4B_T2F), &vertexData_[0].colors); - if(type_ == kCCProgressTimerTypeRadialCCW || type_ == kCCProgressTimerTypeRadialCW){ + if( ! vertexData_ || ! sprite_) + return; + + CC_NODE_DRAW_SETUP(); + + ccGLBlendFunc( sprite_.blendFunc.src, sprite_.blendFunc.dst ); + + ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex ); + + ccGLBindTexture2D( sprite_.texture.name ); + + glVertexAttribPointer( kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, sizeof(vertexData_[0]) , &vertexData_[0].vertices); + glVertexAttribPointer( kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, sizeof(vertexData_[0]), &vertexData_[0].texCoords); + glVertexAttribPointer( kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertexData_[0]), &vertexData_[0].colors); + + if(type_ == kCCProgressTimerTypeRadial) + { glDrawArrays(GL_TRIANGLE_FAN, 0, vertexDataCount_); - } else if (type_ == kCCProgressTimerTypeHorizontalBarLR || - type_ == kCCProgressTimerTypeHorizontalBarRL || - type_ == kCCProgressTimerTypeVerticalBarBT || - type_ == kCCProgressTimerTypeVerticalBarTB) { - glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexDataCount_); + } + else if (type_ == kCCProgressTimerTypeBar) + { + if (!reverseDirection_) + { + glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexDataCount_); + } + else + { + glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexDataCount_/2); + glDrawArrays(GL_TRIANGLE_STRIP, 4, vertexDataCount_/2); + + // 2 draw calls + CC_INCREMENT_GL_DRAWS(1); + } } - //glDrawElements(GL_TRIANGLES, indicesCount_, GL_UNSIGNED_BYTE, indices_); - /// ======================================================================== - - if( newBlend ) - glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + CC_INCREMENT_GL_DRAWS(1); + } @end diff --git a/cocos2d/cocos2d/CCProtocols.h b/cocos2d/cocos2d/CCProtocols.h old mode 100644 new mode 100755 index f7043fc..e55ec92 --- a/cocos2d/cocos2d/CCProtocols.h +++ b/cocos2d/cocos2d/CCProtocols.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,7 +23,7 @@ * THE SOFTWARE. */ - +#import "ccMacros.h" #import "ccTypes.h" #import "CCTexture2D.h" @@ -115,11 +115,18 @@ #pragma mark - -#pragma mark CCProjectionProtocol -/** OpenGL projection protocol */ -@protocol CCProjectionProtocol -/** Called by CCDirector when the porjection is updated, and "custom" projection is used - @since v0.99.5 - */ +#pragma mark CCDirectorDelegate +/** CCDirector delegate */ +@protocol CCDirectorDelegate + +@optional +/** Called by CCDirector when the porjection is updated, and "custom" projection is used */ -(void) updateProjection; + +#ifdef __CC_PLATFORM_IOS +/** Returns a Boolean value indicating whether the CCDirector supports the specified orientation. Default value is YES (supports all possible orientations) */ +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; + +#endif // __CC_PLATFORM_IOS + @end diff --git a/cocos2d/cocos2d/CCRenderTexture.h b/cocos2d/cocos2d/CCRenderTexture.h old mode 100644 new mode 100755 index d5e39cc..5d46875 --- a/cocos2d/cocos2d/CCRenderTexture.h +++ b/cocos2d/cocos2d/CCRenderTexture.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,40 +24,42 @@ */ #import + +#import "ccMacros.h" #import "CCNode.h" #import "CCSprite.h" #import "Support/OpenGL_Internal.h" +#import "kazmath/mat4.h" -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import #endif // iPHone -enum +typedef enum { - kCCImageFormatJPG = 0, + kCCImageFormatJPEG = 0, kCCImageFormatPNG = 1, - kCCImageFormatRawData =2 -}; +} tCCImageFormat; /** CCRenderTexture is a generic rendering target. To render things into it, - simply construct a render target, call begin on it, call visit on any cocos + simply construct a render target, call begin on it, call visit on any cocos2d scenes or objects to render them, and call end. For convienience, render texture - adds a sprite as it's display child with the results, so you can simply add - the render texture to your scene and treat it like any other CocosNode. + adds a sprite as its display child with the results, so you can simply add + the render texture to your scene and treat it like any other CCNode. There are also functions for saving the render texture to disk in PNG or JPG format. - + @since v0.8.1 */ -@interface CCRenderTexture : CCNode +@interface CCRenderTexture : CCNode { GLuint fbo_; - GLint oldFBO_; + GLuint depthRenderBufffer_; + GLint oldFBO_; CCTexture2D* texture_; CCSprite* sprite_; - + GLenum pixelFormat_; } @@ -68,6 +70,9 @@ enum */ @property (nonatomic,readwrite, assign) CCSprite* sprite; +/** initializes a RenderTexture object with width and height in Points and a pixel format( only RGB and RGBA formats are valid ) and depthStencil format*/ ++(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format depthStencilFormat:(GLuint)depthStencilFormat; + /** creates a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are valid */ +(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format; @@ -77,6 +82,9 @@ enum /** initializes a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are valid */ -(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format; +/** initializes a RenderTexture object with width and height in Points and a pixel format( only RGB and RGBA formats are valid ) and depthStencil format*/ +- (id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat)format depthStencilFormat:(GLuint)depthStencilFormat; + /** starts grabbing */ -(void)begin; @@ -84,25 +92,48 @@ enum This is more efficient then calling -clear first and then -begin */ -(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a; +/** starts rendering to the texture while clearing the texture first. + This is more efficient then calling -clear first and then -begin */ +- (void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue; + +/** starts rendering to the texture while clearing the texture first. + This is more efficient then calling -clear first and then -begin */ +- (void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue stencil:(int)stencilValue; + + /** ends grabbing */ -(void)end; /** clears the texture with a color */ -(void)clear:(float)r g:(float)g b:(float)b a:(float)a; -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +/** clears the texture with a specified depth value */ +- (void)clearDepth:(float)depthValue; -/** saves the texture into a file */ --(BOOL)saveBuffer:(NSString*)name; -/** saves the texture into a file. The format can be JPG or PNG */ --(BOOL)saveBuffer:(NSString*)name format:(int)format; -/* get buffer as UIImage, can only save a render buffer which has a RGBA8888 pixel format */ --(NSData*)getUIImageAsDataFromBuffer:(int) format; -/* get buffer as UIImage */ --(UIImage *)getUIImageFromBuffer; +/** clears the texture with a specified stencil value */ +- (void)clearStencil:(int)stencilValue; -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +/* creates a new CGImage from with the texture's data. + Caller is responsible for releasing it by calling CGImageRelease(). + */ +-(CGImageRef) newCGImage; -@end +/** saves the texture into a file using JPEG format. The file will be saved in the Documents folder. + Returns YES if the operation is successful. + */ +-(BOOL)saveToFile:(NSString*)name; +/** saves the texture into a file. The format could be JPG or PNG. The file will be saved in the Documents folder. + Returns YES if the operation is successful. + */ +-(BOOL)saveToFile:(NSString*)name format:(tCCImageFormat)format; + +#ifdef __CC_PLATFORM_IOS + +/* returns an autoreleased UIImage from the texture */ +-(UIImage *) getUIImage; + +#endif // __CC_PLATFORM_IOS + +@end diff --git a/cocos2d/cocos2d/CCRenderTexture.m b/cocos2d/cocos2d/CCRenderTexture.m old mode 100644 new mode 100755 index 4a4768e..905d97d --- a/cocos2d/cocos2d/CCRenderTexture.m +++ b/cocos2d/cocos2d/CCRenderTexture.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,18 +23,32 @@ * */ -#import #import "CCRenderTexture.h" #import "CCDirector.h" #import "ccMacros.h" +#import "CCGLProgram.h" +#import "ccGLStateCache.h" +#import "CCConfiguration.h" #import "Support/ccUtils.h" #import "Support/CCFileUtils.h" +#if __CC_PLATFORM_MAC +#import +#endif + +// extern +#import "kazmath/GL/matrix.h" + @implementation CCRenderTexture @synthesize sprite=sprite_; -// issue #994 ++(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format depthStencilFormat:(GLuint)depthStencilFormat +{ + return [[[self alloc] initWithWidth:w height:h pixelFormat:format depthStencilFormat:depthStencilFormat] autorelease]; +} + +// issue #994 +(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format { return [[[self alloc] initWithWidth:w height:h pixelFormat:format] autorelease]; @@ -42,53 +56,85 @@ +(id)renderTextureWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFo +(id)renderTextureWithWidth:(int)w height:(int)h { - return [[[self alloc] initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888] autorelease]; + return [[[self alloc] initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888 depthStencilFormat:0] autorelease]; } --(id)initWithWidth:(int)w height:(int)h +-(id)initWithWidth:(int)w height:(int)h { return [self initWithWidth:w height:h pixelFormat:kCCTexture2DPixelFormat_RGBA8888]; } --(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format +- (id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat)format +{ + return [self initWithWidth:w height:h pixelFormat:format depthStencilFormat:0]; +} + +-(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) format depthStencilFormat:(GLuint)depthStencilFormat { if ((self = [super init])) { NSAssert(format != kCCTexture2DPixelFormat_A8,@"only RGB and RGBA formats are valid for a render texture"); + + CCDirector *director = [CCDirector sharedDirector]; + + // XXX multithread + if( [director runningThread] != [NSThread currentThread] ) + CCLOGWARN(@"cocos2d: WARNING. CCRenderTexture is running on its own thread. Make sure that an OpenGL context is being used on this thread!"); + w *= CC_CONTENT_SCALE_FACTOR(); h *= CC_CONTENT_SCALE_FACTOR(); - glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO_); - + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO_); + // textures must be power of two - NSUInteger powW = ccNextPOT(w); - NSUInteger powH = ccNextPOT(h); - + NSUInteger powW; + NSUInteger powH; + + if( [[CCConfiguration sharedConfiguration] supportsNPOT] ) { + powW = w; + powH = h; + } else { + powW = ccNextPOT(w); + powH = ccNextPOT(h); + } + void *data = malloc((int)(powW * powH * 4)); memset(data, 0, (int)(powW * powH * 4)); - pixelFormat_=format; - + pixelFormat_=format; + texture_ = [[CCTexture2D alloc] initWithData:data pixelFormat:pixelFormat_ pixelsWide:powW pixelsHigh:powH contentSize:CGSizeMake(w, h)]; free( data ); - + + GLint oldRBO; + glGetIntegerv(GL_RENDERBUFFER_BINDING, &oldRBO); + // generate FBO - ccglGenFramebuffers(1, &fbo_); - ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo_); - + glGenFramebuffers(1, &fbo_); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); + // associate texture with FBO - ccglFramebufferTexture2D(CC_GL_FRAMEBUFFER, CC_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_.name, 0); - - // check if it worked (probably worth doing :) ) - GLuint status = ccglCheckFramebufferStatus(CC_GL_FRAMEBUFFER); - if (status != CC_GL_FRAMEBUFFER_COMPLETE) - { - [NSException raise:@"Render Texture" format:@"Could not attach texture to framebuffer"]; + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_.name, 0); + + if (depthStencilFormat != 0) { + //create and attach depth buffer + glGenRenderbuffers(1, &depthRenderBufffer_); + glBindRenderbuffer(GL_RENDERBUFFER, depthRenderBufffer_); + glRenderbufferStorage(GL_RENDERBUFFER, depthStencilFormat, (GLsizei)powW, (GLsizei)powH); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderBufffer_); + + // if depth format is the one with stencil part, bind same render buffer as stencil attachment + if (depthStencilFormat == CC_GL_DEPTH24_STENCIL8) + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderBufffer_); } + + // check if it worked (probably worth doing :) ) + NSAssert( glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, @"Could not attach texture to framebuffer"); + [texture_ setAliasTexParameters]; - + sprite_ = [CCSprite spriteWithTexture:texture_]; - + [texture_ release]; [sprite_ setScaleY:-1]; [self addChild:sprite_]; @@ -96,50 +142,47 @@ -(id)initWithWidth:(int)w height:(int)h pixelFormat:(CCTexture2DPixelFormat) for // issue #937 [sprite_ setBlendFunc:(ccBlendFunc){GL_ONE, GL_ONE_MINUS_SRC_ALPHA}]; - ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO_); + glBindRenderbuffer(GL_RENDERBUFFER, oldRBO); + glBindFramebuffer(GL_FRAMEBUFFER, oldFBO_); } return self; } -(void)dealloc { -// [self removeAllChildrenWithCleanup:YES]; - ccglDeleteFramebuffers(1, &fbo_); + glDeleteFramebuffers(1, &fbo_); + if (depthRenderBufffer_) + glDeleteRenderbuffers(1, &depthRenderBufffer_); + [super dealloc]; } -(void)begin { - // Save the current matrix - glPushMatrix(); + CCDirector *director = [CCDirector sharedDirector]; + // Save the current matrix + kmGLPushMatrix(); + CGSize texSize = [texture_ contentSizeInPixels]; - - + + // Calculate the adjustment ratios based on the old and new projections - CGSize size = [[CCDirector sharedDirector] displaySizeInPixels]; + CGSize size = [director winSizeInPixels]; float widthRatio = size.width / texSize.width; float heightRatio = size.height / texSize.height; - - - // Adjust the orthographic propjection and viewport - ccglOrtho((float)-1.0 / widthRatio, (float)1.0 / widthRatio, (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1,1); - glViewport(0, 0, texSize.width, texSize.height); - - - glGetIntegerv(CC_GL_FRAMEBUFFER_BINDING, &oldFBO_); - ccglBindFramebuffer(CC_GL_FRAMEBUFFER, fbo_);//Will direct drawing to the frame buffer created above - - // Issue #1145 - // There is no need to enable the default GL states here - // but since CCRenderTexture is mostly used outside the "render" loop - // these states needs to be enabled. - // Since this bug was discovered in API-freeze (very close of 1.0 release) - // This bug won't be fixed to prevent incompatibilities with code. - // - // If you understand the above mentioned message, then you can comment the following line - // and enable the gl states manually, in case you need them. - CC_ENABLE_DEFAULT_GL_STATES(); + + + // Adjust the orthographic projection and viewport + glViewport(0, 0, texSize.width, texSize.height ); + + kmMat4 orthoMatrix; + kmMat4OrthographicProjection(&orthoMatrix, (float)-1.0 / widthRatio, (float)1.0 / widthRatio, + (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1,1 ); + kmGLMultMatrix(&orthoMatrix); + + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO_); + glBindFramebuffer(GL_FRAMEBUFFER, fbo_); } -(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a @@ -148,7 +191,7 @@ -(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a // save clear color GLfloat clearColor[4]; - glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor); + glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor); glClearColor(r, g, b, a); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -157,13 +200,66 @@ -(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); } +-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue +{ + [self begin]; + + // save clear color + GLfloat clearColor[4]; + GLfloat depthClearValue; + glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor); + glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depthClearValue); + + glClearColor(r, g, b, a); + glClearDepth(depthValue); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // restore clear color + glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + glClearDepth(depthClearValue); +} + +-(void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue stencil:(int)stencilValue +{ + [self begin]; + + // save clear color + GLfloat clearColor[4]; + GLfloat depthClearValue; + int stencilClearValue; + glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor); + glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depthClearValue); + glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilClearValue); + + glClearColor(r, g, b, a); + glClearDepth(depthValue); + glClearStencil(stencilValue); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + // restore clear color + glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + glClearDepth(depthClearValue); + glClearStencil(stencilClearValue); +} + -(void)end { - ccglBindFramebuffer(CC_GL_FRAMEBUFFER, oldFBO_); - // Restore the original matrix and viewport - glPopMatrix(); - CGSize size = [[CCDirector sharedDirector] displaySizeInPixels]; - glViewport(0, 0, size.width, size.height); + CCDirector *director = [CCDirector sharedDirector]; + + glBindFramebuffer(GL_FRAMEBUFFER, oldFBO_); + + kmGLPopMatrix(); + + CGSize size = [director winSizeInPixels]; + + // restore viewport + glViewport(0, 0, size.width * CC_CONTENT_SCALE_FACTOR(), size.height * CC_CONTENT_SCALE_FACTOR() ); + + // special viewport for 3d projection + retina display + if ( director.projection == kCCDirectorProjection3D && CC_CONTENT_SCALE_FACTOR() != 1 ) + glViewport(-size.width/2, -size.height/2, size.width * CC_CONTENT_SCALE_FACTOR(), size.height * CC_CONTENT_SCALE_FACTOR() ); + + [director setProjection:director.projection]; } -(void)clear:(float)r g:(float)g b:(float)b a:(float)a @@ -172,56 +268,73 @@ -(void)clear:(float)r g:(float)g b:(float)b a:(float)a [self end]; } -#pragma mark RenderTexture - Save Image - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED --(BOOL)saveBuffer:(NSString*)name +- (void)clearDepth:(float)depthValue { - return [self saveBuffer:name format:kCCImageFormatJPG]; + [self begin]; + //! save old depth value + GLfloat depthClearValue; + glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depthClearValue); + + glClearDepth(depthValue); + glClear(GL_DEPTH_BUFFER_BIT); + + // restore clear color + glClearDepth(depthClearValue); + [self end]; } --(BOOL)saveBuffer:(NSString*)fileName format:(int)format +- (void)clearStencil:(int)stencilValue { - NSString *fullPath = [CCFileUtils fullPathFromRelativePath:fileName]; - - NSData *data = [self getUIImageAsDataFromBuffer:format]; - - return [data writeToFile:fullPath atomically:YES]; + // save old stencil value + int stencilClearValue; + glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilClearValue); + + glClearStencil(stencilValue); + glClear(GL_STENCIL_BUFFER_BIT); + + // restore clear color + glClearStencil(stencilClearValue); } -/* get buffer as UIImage */ --(UIImage *)getUIImageFromBuffer +#pragma mark RenderTexture - Save Image + +-(CGImageRef) newCGImage { NSAssert(pixelFormat_ == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image"); + CGSize s = [texture_ contentSizeInPixels]; int tx = s.width; int ty = s.height; int bitsPerComponent = 8; - int bitsPerPixel = 32; - int bytesPerPixel = (bitsPerComponent * 4)/8; + int bitsPerPixel = 4 * 8; + int bytesPerPixel = bitsPerPixel / 8; int bytesPerRow = bytesPerPixel * tx; NSInteger myDataLength = bytesPerRow * ty; - NSMutableData *buffer = [[NSMutableData alloc] initWithCapacity:myDataLength]; - NSMutableData *pixels = [[NSMutableData alloc] initWithCapacity:myDataLength]; + GLubyte *buffer = calloc(myDataLength,1); + GLubyte *pixels = calloc(myDataLength,1); + if( ! (buffer && pixels) ) { - CCLOG(@"cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory"); - [buffer release]; - [pixels release]; + CCLOG(@"cocos2d: CCRenderTexture#getCGImageFromBuffer: not enough memory"); + free(buffer); + free(pixels); return nil; } [self begin]; - glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, [buffer mutableBytes]); + + + glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, buffer); + [self end]; // make data provider with data. CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault; - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, [buffer mutableBytes], myDataLength, NULL); + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, myDataLength, NULL); CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); CGImageRef iref = CGImageCreate(tx, ty, bitsPerComponent, bitsPerPixel, bytesPerRow, @@ -229,112 +342,109 @@ -(UIImage *)getUIImageFromBuffer NULL, false, kCGRenderingIntentDefault); - CGContextRef context = CGBitmapContextCreate([pixels mutableBytes], tx, + CGContextRef context = CGBitmapContextCreate(pixels, tx, ty, CGImageGetBitsPerComponent(iref), CGImageGetBytesPerRow(iref), CGImageGetColorSpace(iref), bitmapInfo); - CGContextTranslateCTM(context, 0.0f, ty); - CGContextScaleCTM(context, 1.0f, -1.0f); + + // vertically flipped + if( YES ) { + CGContextTranslateCTM(context, 0.0f, ty); + CGContextScaleCTM(context, 1.0f, -1.0f); + } CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, tx, ty), iref); - CGImageRef outputRef = CGBitmapContextCreateImage(context); - UIImage* image = [[UIImage alloc] initWithCGImage:outputRef]; + CGImageRef image = CGBitmapContextCreateImage(context); CGImageRelease(iref); CGContextRelease(context); CGColorSpaceRelease(colorSpaceRef); CGDataProviderRelease(provider); - CGImageRelease(outputRef); - [pixels release]; - [buffer release]; + free(pixels); + free(buffer); - return [image autorelease]; + return image; } --(NSData*)getUIImageAsDataFromBuffer:(int) format +-(BOOL) saveToFile:(NSString*)name { - NSAssert(pixelFormat_ == kCCTexture2DPixelFormat_RGBA8888,@"only RGBA8888 can be saved as image"); - - CGSize s = [texture_ contentSizeInPixels]; - int tx = s.width; - int ty = s.height; - - int bitsPerComponent=8; - int bitsPerPixel=32; - - int bytesPerRow = (bitsPerPixel/8) * tx; - NSInteger myDataLength = bytesPerRow * ty; + return [self saveToFile:name format:kCCImageFormatJPEG]; +} + +-(BOOL)saveToFile:(NSString*)fileName format:(tCCImageFormat)format +{ + BOOL success; - GLubyte *buffer = malloc(sizeof(GLubyte)*myDataLength); - GLubyte *pixels = malloc(sizeof(GLubyte)*myDataLength); + NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:fileName]; - if( ! (buffer && pixels) ) { - CCLOG(@"cocos2d: CCRenderTexture#getUIImageFromBuffer: not enough memory"); - free(buffer); - free(pixels); - return nil; + CGImageRef imageRef = [self newCGImage]; + + if( ! imageRef ) { + CCLOG(@"cocos2d: Error: Cannot create CGImage ref from texture"); + return NO; } - [self begin]; - glReadPixels(0,0,tx,ty,GL_RGBA,GL_UNSIGNED_BYTE, buffer); - [self end]; +#if __CC_PLATFORM_IOS - int x,y; + UIImage* image = [[UIImage alloc] initWithCGImage:imageRef scale:CC_CONTENT_SCALE_FACTOR() orientation:UIImageOrientationUp]; + NSData *imageData; + + if( format == kCCImageFormatPNG ) + imageData = UIImagePNGRepresentation( image ); + + else if( format == kCCImageFormatJPEG ) + imageData = UIImageJPEGRepresentation(image, 0.9f); + + else + NSAssert(NO, @"Unsupported format"); - for(y = 0; y -{ - NSMutableArray* segments_; - NSMutableArray* deletedSegments_; - - CGPoint lastPoint1_; - CGPoint lastPoint2_; - CGPoint lastLocation_; - int vertCount_; - float texVPos_; - float curTime_; - float fadeTime_; - float delta_; - float lastWidth_; - float lastSign_; - BOOL pastFirstPoint_; - - // Texture used - CCTexture2D* texture_; - - // texture length - float textureLength_; - - // RGBA protocol - ccColor4B color_; - - // blend func - ccBlendFunc blendFunc_; -} - -/** Texture used by the ribbon. Conforms to CCTextureProtocol protocol */ -@property (nonatomic,readwrite,retain) CCTexture2D* texture; - -/** Texture lengths in pixels */ -@property (nonatomic,readwrite) float textureLength; - -/** GL blendind function */ -@property (nonatomic,readwrite,assign) ccBlendFunc blendFunc; - -/** color used by the Ribbon (RGBA) */ -@property (nonatomic,readwrite) ccColor4B color; - -/** creates the ribbon */ -+(id)ribbonWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade; -/** init the ribbon */ --(id)initWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade; -/** add a point to the ribbon */ --(void)addPointAt:(CGPoint)location width:(float)w; -/** polling function */ --(void)update:(ccTime)delta; -/** determine side of line */ --(float)sideOfLine:(CGPoint)p l1:(CGPoint)l1 l2:(CGPoint)l2; - -@end - -/** object to hold ribbon segment data */ -@interface CCRibbonSegment : NSObject -{ -@public - GLfloat verts[50*6]; - GLfloat coords[50*4]; - GLubyte colors[50*8]; - float creationTime[50]; - BOOL finished; - NSUInteger end; - NSUInteger begin; -} --(id)init; --(void)reset; --(void)draw:(float)curTime fadeTime:(float)fadeTime color:(ccColor4B)color; -@end diff --git a/cocos2d/cocos2d/CCRibbon.m b/cocos2d/cocos2d/CCRibbon.m deleted file mode 100644 index 1e0d10a..0000000 --- a/cocos2d/cocos2d/CCRibbon.m +++ /dev/null @@ -1,383 +0,0 @@ -/* - * cocos2d for iPhone: http://www.cocos2d-iphone.org - * - * Copyright (c) 2008, 2009 Jason Booth - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* - * A ribbon is a dynamically generated list of polygons drawn as a single or series - * of triangle strips. The primary use of Ribbon is as the drawing class of Motion Streak, - * but it is quite useful on it's own. When manually drawing a ribbon, you can call addPointAt - * and pass in the parameters for the next location in the ribbon. The system will automatically - * generate new polygons, texture them accourding to your texture width, etc, etc. - * - * Ribbon data is stored in a RibbonSegment class. This class statically allocates enough verticies and - * texture coordinates for 50 locations (100 verts or 48 triangles). The ribbon class will allocate - * new segments when they are needed, and reuse old ones if available. The idea is to avoid constantly - * allocating new memory and prefer a more static method. However, since there is no way to determine - * the maximum size of some ribbons (motion streaks), a truely static allocation is not possible. - * - */ - - -#import "CCRibbon.h" -#import "CCTextureCache.h" -#import "Support/CGPointExtension.h" -#import "ccMacros.h" - -// -// Ribbon -// -@implementation CCRibbon -@synthesize blendFunc=blendFunc_; -@synthesize color=color_; -@synthesize textureLength = textureLength_; - -+(id)ribbonWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade -{ - self = [[[self alloc] initWithWidth:w image:path length:l color:color fade:fade] autorelease]; - return self; -} - --(id)initWithWidth:(float)w image:(NSString*)path length:(float)l color:(ccColor4B)color fade:(float)fade -{ - self = [super init]; - if (self) - { - - segments_ = [[NSMutableArray alloc] init]; - deletedSegments_ = [[NSMutableArray alloc] init]; - - /* 1 initial segment */ - CCRibbonSegment* seg = [[CCRibbonSegment alloc] init]; - [segments_ addObject:seg]; - [seg release]; - - textureLength_ = l; - - color_ = color; - fadeTime_ = fade; - lastLocation_ = CGPointZero; - lastWidth_ = w/2; - texVPos_ = 0.0f; - - curTime_ = 0; - pastFirstPoint_ = NO; - - /* XXX: - Ribbon, by default uses this blend function, which might not be correct - if you are using premultiplied alpha images, - but 99% you might want to use this blending function regarding of the texture - */ - blendFunc_.src = GL_SRC_ALPHA; - blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; - - self.texture = [[CCTextureCache sharedTextureCache] addImage:path]; - - /* default texture parameter */ - ccTexParams params = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT }; - [texture_ setTexParameters:¶ms]; - } - return self; -} - --(void)dealloc -{ - [segments_ release]; - [deletedSegments_ release]; - [texture_ release]; - [super dealloc]; -} - -// rotates a point around 0, 0 --(CGPoint)rotatePoint:(CGPoint)vec rotation:(float)a -{ - float xtemp = (vec.x * cosf(a)) - (vec.y * sinf(a)); - vec.y = (vec.x * sinf(a)) + (vec.y * cosf(a)); - vec.x = xtemp; - return vec; -} - --(void)update:(ccTime)delta -{ - curTime_+= delta; - delta_ = delta; -} - --(float)sideOfLine:(CGPoint)p l1:(CGPoint)l1 l2:(CGPoint)l2 -{ - CGPoint vp = ccpPerp(ccpSub(l1, l2)); - CGPoint vx = ccpSub(p, l1); - return ccpDot(vx, vp); -} - -// adds a new segment to the ribbon --(void)addPointAt:(CGPoint)location width:(float)w -{ - location.x *= CC_CONTENT_SCALE_FACTOR(); - location.y *= CC_CONTENT_SCALE_FACTOR(); - - w = w*0.5f; - // if this is the first point added, cache it and return - if (!pastFirstPoint_) - { - lastWidth_ = w; - lastLocation_ = location; - pastFirstPoint_ = YES; - return; - } - - CGPoint sub = ccpSub(lastLocation_, location); - float r = ccpToAngle(sub) + (float)M_PI_2; - CGPoint p1 = ccpAdd([self rotatePoint:ccp(-w, 0) rotation:r], location); - CGPoint p2 = ccpAdd([self rotatePoint:ccp(w, 0) rotation:r], location); - float len = sqrtf(powf(lastLocation_.x - location.x, 2) + powf(lastLocation_.y - location.y, 2)); - float tend = texVPos_ + len/textureLength_; - CCRibbonSegment* seg; - // grab last segment - seg = [segments_ lastObject]; - // lets kill old segments - for (CCRibbonSegment* seg2 in segments_) - { - if (seg2 != seg && seg2->finished) - { - [deletedSegments_ addObject:seg2]; - } - } - [segments_ removeObjectsInArray:deletedSegments_]; - // is the segment full? - if (seg->end >= 50) - [segments_ removeObjectsInArray:deletedSegments_]; - // grab last segment and append to it if it's not full - seg = [segments_ lastObject]; - // is the segment full? - if (seg->end >= 50) - { - CCRibbonSegment* newSeg; - // grab it from the cache if we can - if ([deletedSegments_ count] > 0) - { - newSeg = [deletedSegments_ objectAtIndex:0]; - [newSeg retain]; // will be released later - [deletedSegments_ removeObject:newSeg]; - [newSeg reset]; - } - else - { - newSeg = [[CCRibbonSegment alloc] init]; // will be released later - } - - newSeg->creationTime[0] = seg->creationTime[seg->end - 1]; - int v = (seg->end-1)*6; - int c = (seg->end-1)*4; - newSeg->verts[0] = seg->verts[v]; - newSeg->verts[1] = seg->verts[v+1]; - newSeg->verts[2] = seg->verts[v+2]; - newSeg->verts[3] = seg->verts[v+3]; - newSeg->verts[4] = seg->verts[v+4]; - newSeg->verts[5] = seg->verts[v+5]; - - newSeg->coords[0] = seg->coords[c]; - newSeg->coords[1] = seg->coords[c+1]; - newSeg->coords[2] = seg->coords[c+2]; - newSeg->coords[3] = seg->coords[c+3]; - newSeg->end++; - seg = newSeg; - [segments_ addObject:seg]; - [newSeg release]; // it was retained before - - } - if (seg->end == 0) - { - // first edge has to get rotation from the first real polygon - CGPoint lp1 = ccpAdd([self rotatePoint:ccp(-lastWidth_, 0) rotation:r], lastLocation_); - CGPoint lp2 = ccpAdd([self rotatePoint:ccp(+lastWidth_, 0) rotation:r], lastLocation_); - seg->creationTime[0] = curTime_ - delta_; - seg->verts[0] = lp1.x; - seg->verts[1] = lp1.y; - seg->verts[2] = 0.0f; - seg->verts[3] = lp2.x; - seg->verts[4] = lp2.y; - seg->verts[5] = 0.0f; - seg->coords[0] = 0.0f; - seg->coords[1] = texVPos_; - seg->coords[2] = 1.0f; - seg->coords[3] = texVPos_; - seg->end++; - } - - int v = seg->end*6; - int c = seg->end*4; - // add new vertex - seg->creationTime[seg->end] = curTime_; - seg->verts[v] = p1.x; - seg->verts[v+1] = p1.y; - seg->verts[v+2] = 0.0f; - seg->verts[v+3] = p2.x; - seg->verts[v+4] = p2.y; - seg->verts[v+5] = 0.0f; - - - seg->coords[c] = 0.0f; - seg->coords[c+1] = tend; - seg->coords[c+2] = 1.0f; - seg->coords[c+3] = tend; - - texVPos_ = tend; - lastLocation_ = location; - lastPoint1_ = p1; - lastPoint2_ = p2; - lastWidth_ = w; - seg->end++; -} - --(void) draw -{ - [super draw]; - - if ([segments_ count] > 0) - { - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY - // Unneeded states: GL_COLOR_ARRAY - glDisableClientState(GL_COLOR_ARRAY); - - glBindTexture(GL_TEXTURE_2D, [texture_ name]); - - BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; - if( newBlend ) - glBlendFunc( blendFunc_.src, blendFunc_.dst ); - - for (CCRibbonSegment* seg in segments_) - [seg draw:curTime_ fadeTime:fadeTime_ color:color_]; - - if( newBlend ) - glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); - - // restore default GL state - glEnableClientState( GL_COLOR_ARRAY ); - } -} - -#pragma mark Ribbon - CocosNodeTexture protocol --(void) setTexture:(CCTexture2D*) texture -{ - [texture_ release]; - texture_ = [texture retain]; - [self setContentSizeInPixels: texture.contentSizeInPixels]; - /* XXX Don't update blending function in Ribbons */ -} - --(CCTexture2D*) texture -{ - return texture_; -} - -@end - - -#pragma mark - -#pragma mark RibbonSegment - -@implementation CCRibbonSegment - --(id)init -{ - self = [super init]; - if (self) - { - [self reset]; - } - return self; -} - -- (NSString*) description -{ - return [NSString stringWithFormat:@"<%@ = %08X | end = %i, begin = %i>", [self class], self, end, begin]; -} - -- (void) dealloc -{ - CCLOGINFO(@"cocos2d: deallocing %@", self); - [super dealloc]; -} - --(void)reset -{ - end = 0; - begin = 0; - finished = NO; -} - --(void)draw:(float)curTime fadeTime:(float)fadeTime color:(ccColor4B)color -{ - GLubyte r = color.r; - GLubyte g = color.g; - GLubyte b = color.b; - GLubyte a = color.a; - - if (begin < 50) - { - // the motion streak class will call update and cause time to change, thus, if curTime_ != 0 - // we have to generate alpha for the ribbon each frame. - if (curTime == 0) - { - // no alpha over time, so just set the color - glColor4ub(r,g,b,a); - } - else - { - // generate alpha/color for each point - glEnableClientState(GL_COLOR_ARRAY); - NSUInteger i = begin; - for (; i < end; ++i) - { - int idx = i*8; - colors[idx] = r; - colors[idx+1] = g; - colors[idx+2] = b; - colors[idx+4] = r; - colors[idx+5] = g; - colors[idx+6] = b; - float alive = ((curTime - creationTime[i]) / fadeTime); - if (alive > 1) - { - begin++; - colors[idx+3] = 0; - colors[idx+7] = 0; - } - else - { - colors[idx+3] = (GLubyte)(255.f - (alive * 255.f)); - colors[idx+7] = colors[idx+3]; - } - } - glColorPointer(4, GL_UNSIGNED_BYTE, 0, &colors[begin*8]); - } - glVertexPointer(3, GL_FLOAT, 0, &verts[begin*6]); - glTexCoordPointer(2, GL_FLOAT, 0, &coords[begin*4]); - glDrawArrays(GL_TRIANGLE_STRIP, 0, (end - begin) * 2); - } - else - finished = YES; -} -@end - diff --git a/cocos2d/cocos2d/CCScene.h b/cocos2d/cocos2d/CCScene.h old mode 100644 new mode 100755 index 1d104bc..36b9085 --- a/cocos2d/cocos2d/CCScene.h +++ b/cocos2d/cocos2d/CCScene.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,8 +28,8 @@ #import "CCNode.h" /** CCScene is a subclass of CCNode that is used only as an abstract concept. - - CCScene an CCNode are almost identical with the difference that CCScene has it's + + CCScene an CCNode are almost identical with the difference that CCScene has its anchor point (by default) at the center of the screen. For the moment CCScene has no other logic than that, but in future releases it might have diff --git a/cocos2d/cocos2d/CCScene.m b/cocos2d/cocos2d/CCScene.m old mode 100644 new mode 100755 index e991d6e..3016e19 --- a/cocos2d/cocos2d/CCScene.m +++ b/cocos2d/cocos2d/CCScene.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -35,11 +35,11 @@ -(id) init { if( (self=[super init]) ) { CGSize s = [[CCDirector sharedDirector] winSize]; - self.isRelativeAnchorPoint = NO; + self.ignoreAnchorPointForPosition = YES; anchorPoint_ = ccp(0.5f, 0.5f); - [self setContentSize:s]; + [self setContentSize:s]; } - + return self; } @end diff --git a/cocos2d/cocos2d/CCScheduler.h b/cocos2d/cocos2d/CCScheduler.h old mode 100644 new mode 100755 index 122b8fe..9ae05db --- a/cocos2d/cocos2d/CCScheduler.h +++ b/cocos2d/cocos2d/CCScheduler.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,6 +28,12 @@ #import "Support/uthash.h" #import "ccTypes.h" +// Priority level reserved for system services. +#define kCCPrioritySystem INT_MIN + +// Minimum priority level for user scheduling. +#define kCCPriorityNonSystemMin (kCCPrioritySystem+1) + typedef void (*TICK_IMP)(id, SEL, ccTime); // @@ -38,14 +44,18 @@ typedef void (*TICK_IMP)(id, SEL, ccTime); { id target; TICK_IMP impMethod; - + ccTime elapsed; + BOOL runForever; + BOOL useDelay; + uint nTimesExecuted; + uint repeat; //0 = once, 1 is 2 x executed + ccTime delay; @public // optimization ccTime interval; SEL selector; } - /** interval in seconds */ @property (nonatomic,readwrite,assign) ccTime interval; @@ -61,9 +71,9 @@ typedef void (*TICK_IMP)(id, SEL, ccTime); */ -(id) initWithTarget:(id) t selector:(SEL)s; -/** Initializes a timer with a target, a selector and an interval in seconds. +/** Initializes a timer with a target, a selector, an interval in seconds, repeat in number of times to repeat, delay in seconds */ --(id) initWithTarget:(id) t selector:(SEL)s interval:(ccTime)seconds; +-(id) initWithTarget:(id)t selector:(SEL)s interval:(ccTime) seconds repeat:(uint) r delay:(ccTime) d; /** triggers the timer */ @@ -75,14 +85,14 @@ typedef void (*TICK_IMP)(id, SEL, ccTime); // // CCScheduler // -/** Scheduler is responsible of triggering the scheduled callbacks. +/** CCScheduler is responsible of triggering the scheduled callbacks. You should not use NSTimer. Instead use this class. - + There are 2 different types of callbacks (selectors): - update selector: the 'update' selector will be called every frame. You can customize the priority. - custom selector: A custom selector will be called every frame, or with a custom interval of time - + The 'custom selectors' should be avoided when possible. It is faster, and consumes less memory to use the 'update selector'. */ @@ -92,9 +102,9 @@ struct _hashSelectorEntry; struct _hashUpdateEntry; @interface CCScheduler : NSObject -{ +{ ccTime timeScale_; - + // // "updates with priority" stuff // @@ -102,16 +112,16 @@ struct _hashUpdateEntry; struct _listEntry *updates0; // list priority == 0 struct _listEntry *updatesPos; // list priority > 0 struct _hashUpdateEntry *hashForUpdates; // hash used to fetch quickly the list entries for pause,delete,etc. - + // Used for "selectors with interval" struct _hashSelectorEntry *hashForSelectors; struct _hashSelectorEntry *currentTarget; BOOL currentTargetSalvaged; - + // Optimization TICK_IMP impMethod; SEL updateSelector; - + BOOL updateHashLocked; // If true unschedule will not remove anything from a hash. Elements will only be marked for deletion. } @@ -124,26 +134,23 @@ struct _hashUpdateEntry; */ @property (nonatomic,readwrite) ccTime timeScale; -/** returns a shared instance of the Scheduler */ -+(CCScheduler *)sharedScheduler; - -/** purges the shared scheduler. It releases the retained instance. - @since v0.99.0 - */ -+(void)purgeSharedScheduler; - -/** 'tick' the scheduler. +/** 'update' the scheduler. You should NEVER call this method, unless you know what you are doing. */ --(void) tick:(ccTime)dt; +-(void) update:(ccTime)dt; /** The scheduled method will be called every 'interval' seconds. If paused is YES, then it won't be called until it is resumed. If 'interval' is 0, it will be called every frame, but if so, it recommened to use 'scheduleUpdateForTarget:' instead. If the selector is already scheduled, then only the interval parameter will be updated without re-scheduling it again. + repeat let the action be repeated repeat + 1 times, use kCCRepeatForever to let the action run continiously + delay is the amount of time the action will wait before it'll start - @since v0.99.3 + @since v0.99.3, repeat and delay added in v1.1 */ +-(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)interval paused:(BOOL)paused repeat: (uint) repeat delay: (ccTime) delay; + +/** calls scheduleSelector with kCCRepeatForever and a 0 delay */ -(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)interval paused:(BOOL)paused; /** Schedules the 'update' selector for a given target with a given priority. @@ -177,6 +184,12 @@ struct _hashUpdateEntry; */ -(void) unscheduleAllSelectors; +/** Unschedules all selectors from all targets with a minimum priority. + You should only call this with kCCPriorityNonSystemMin or higher. + @since v2.0.0 + */ +-(void) unscheduleAllSelectorsWithMinPriority:(NSInteger)minPriority; + /** Pauses the target. All scheduled selectors/update for a given target won't be 'ticked' until the target is resumed. If the target is not present, nothing happens. @@ -196,4 +209,22 @@ struct _hashUpdateEntry; */ -(BOOL) isTargetPaused:(id)target; +/** Pause all selectors from all targets. + You should NEVER call this method, unless you know what you are doing. + @since v2.0.0 + */ +-(NSSet*) pauseAllTargets; + +/** Pause all selectors from all targets with a minimum priority. + You should only call this with kCCPriorityNonSystemMin or higher. + @since v2.0.0 + */ +-(NSSet*) pauseAllTargetsWithMinPriority:(NSInteger)minPriority; + +/** Resume selectors on a set of targets. + This can be useful for undoing a call to pauseAllSelectors. + @since v2.0.0 + */ +-(void) resumeTargets:(NSSet *)targetsToResume; + @end diff --git a/cocos2d/cocos2d/CCScheduler.m b/cocos2d/cocos2d/CCScheduler.m old mode 100644 new mode 100755 index a14ca10..f99e18a --- a/cocos2d/cocos2d/CCScheduler.m +++ b/cocos2d/cocos2d/CCScheduler.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,6 +27,7 @@ // cocos2d imports #import "CCScheduler.h" #import "ccMacros.h" +#import "CCDirector.h" #import "Support/uthash.h" #import "Support/utlist.h" #import "Support/ccCArray.h" @@ -44,8 +45,8 @@ TICK_IMP impMethod; id target; // not retained (retained by hashUpdateEntry) NSInteger priority; - BOOL paused; - BOOL markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick + BOOL paused; + BOOL markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick } tListEntry; typedef struct _hashUpdateEntry @@ -89,40 +90,46 @@ -(id) init +(id) timerWithTarget:(id)t selector:(SEL)s { - return [[[self alloc] initWithTarget:t selector:s] autorelease]; + return [[[self alloc] initWithTarget:t selector:s interval:0 repeat:kCCRepeatForever delay:0] autorelease]; } +(id) timerWithTarget:(id)t selector:(SEL)s interval:(ccTime) i { - return [[[self alloc] initWithTarget:t selector:s interval:i] autorelease]; + return [[[self alloc] initWithTarget:t selector:s interval:i repeat:kCCRepeatForever delay:0] autorelease]; } -(id) initWithTarget:(id)t selector:(SEL)s { - return [self initWithTarget:t selector:s interval:0]; + return [self initWithTarget:t selector:s interval:0 repeat:kCCRepeatForever delay: 0]; } --(id) initWithTarget:(id)t selector:(SEL)s interval:(ccTime) seconds +-(id) initWithTarget:(id)t selector:(SEL)s interval:(ccTime) seconds repeat:(uint) r delay:(ccTime) d { if( (self=[super init]) ) { #if COCOS2D_DEBUG NSMethodSignature *sig = [t methodSignatureForSelector:s]; NSAssert(sig !=0 , @"Signature not found for selector - does it have the following form? -(void) name: (ccTime) dt"); #endif - + // target is not retained. It is retained in the hash structure target = t; selector = s; impMethod = (TICK_IMP) [t methodForSelector:s]; elapsed = -1; interval = seconds; + repeat = r; + delay = d; + useDelay = (delay > 0) ? YES : NO; + repeat = r; + runForever = (repeat == kCCRepeatForever) ? YES : NO; } return self; } + - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | target:%@ selector:(%@)>", [self class], self, [target class], NSStringFromSelector(selector)]; + return [NSString stringWithFormat:@"<%@ = %p | target:%@ selector:(%@)>", [self class], self, [target class], NSStringFromSelector(selector)]; } -(void) dealloc @@ -134,12 +141,50 @@ -(void) dealloc -(void) update: (ccTime) dt { if( elapsed == - 1) + { elapsed = 0; + nTimesExecuted = 0; + } else - elapsed += dt; - if( elapsed >= interval ) { - impMethod(target, selector, elapsed); - elapsed = 0; + { + if (runForever && !useDelay) + {//standard timer usage + elapsed += dt; + if( elapsed >= interval ) { + impMethod(target, selector, elapsed); + elapsed = 0; + + } + } + else + {//advanced usage + elapsed += dt; + if (useDelay) + { + if( elapsed >= delay ) + { + impMethod(target, selector, elapsed); + elapsed = elapsed - delay; + nTimesExecuted+=1; + useDelay = NO; + } + } + else + { + if (elapsed >= interval) + { + impMethod(target, selector, elapsed); + elapsed = 0; + nTimesExecuted += 1; + + } + } + + if (nTimesExecuted > repeat) + { //unschedule timer + [[[CCDirector sharedDirector] scheduler] unscheduleSelector:selector forTarget:target]; + } + } } } @end @@ -156,33 +201,11 @@ -(void) removeHashElement:(tHashSelectorEntry*)element; @implementation CCScheduler -static CCScheduler *sharedScheduler; - @synthesize timeScale = timeScale_; -+ (CCScheduler *)sharedScheduler -{ - if (!sharedScheduler) - sharedScheduler = [[CCScheduler alloc] init]; - - return sharedScheduler; -} - -+(id)alloc -{ - NSAssert(sharedScheduler == nil, @"Attempted to allocate a second instance of a singleton."); - return [super alloc]; -} - -+(void)purgeSharedScheduler -{ - [sharedScheduler release]; - sharedScheduler = nil; -} - - (id) init { - if( (self=[super init]) ) { + if( (self=[super init]) ) { timeScale_ = 1.0f; // used to trigger CCTimer#update @@ -194,7 +217,7 @@ - (id) init updatesNeg = NULL; updatesPos = NULL; hashForUpdates = NULL; - + // selectors with interval currentTarget = nil; currentTargetSalvaged = NO; @@ -205,14 +228,17 @@ - (id) init return self; } +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %p | timeScale = %0.2f >", [self class], self, timeScale_]; +} + - (void) dealloc { CCLOG(@"cocos2d: deallocing %@", self); [self unscheduleAllSelectors]; - sharedScheduler = nil; - [super dealloc]; } @@ -228,25 +254,30 @@ -(void) removeHashElement:(tHashSelectorEntry*)element } -(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)interval paused:(BOOL)paused +{ + [self scheduleSelector:selector forTarget:target interval:interval paused:paused repeat:kCCRepeatForever delay:0.0f]; +} + +-(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)interval paused:(BOOL)paused repeat:(uint) repeat delay:(ccTime) delay { NSAssert( selector != nil, @"Argument selector must be non-nil"); - NSAssert( target != nil, @"Argument target must be non-nil"); - + NSAssert( target != nil, @"Argument target must be non-nil"); + tHashSelectorEntry *element = NULL; HASH_FIND_INT(hashForSelectors, &target, element); - + if( ! element ) { element = calloc( sizeof( *element ), 1 ); element->target = [target retain]; HASH_ADD_INT( hashForSelectors, target, element ); - + // Is this the 1st element ? Then set the pause level to all the selectors of this target element->paused = paused; - + } else NSAssert( element->paused == paused, @"CCScheduler. Trying to schedule a selector with a pause value different than the target"); - - + + if( element->timers == nil ) element->timers = ccArrayNew(10); else @@ -254,15 +285,15 @@ -(void) scheduleSelector:(SEL)selector forTarget:(id)target interval:(ccTime)int for( unsigned int i=0; i< element->timers->num; i++ ) { CCTimer *timer = element->timers->arr[i]; if( selector == timer->selector ) { - CCLOG(@"CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.2f to %.2f", timer->interval, interval); + CCLOG(@"CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->interval, interval); timer->interval = interval; return; } } ccArrayEnsureExtraCapacity(element->timers, 1); } - - CCTimer *timer = [[CCTimer alloc] initWithTarget:target selector:selector interval:interval]; + + CCTimer *timer = [[CCTimer alloc] initWithTarget:target selector:selector interval:interval repeat:repeat delay:delay]; ccArrayAppendObject(element->timers, timer); [timer release]; } @@ -272,28 +303,28 @@ -(void) unscheduleSelector:(SEL)selector forTarget:(id)target // explicity handle nil arguments when removing an object if( target==nil && selector==NULL) return; - + NSAssert( target != nil, @"Target MUST not be nil"); NSAssert( selector != NULL, @"Selector MUST not be NULL"); - + tHashSelectorEntry *element = NULL; HASH_FIND_INT(hashForSelectors, &target, element); - + if( element ) { - + for( unsigned int i=0; i< element->timers->num; i++ ) { CCTimer *timer = element->timers->arr[i]; - - + + if( selector == timer->selector ) { - + if( timer == element->currentTimer && !element->currentTimerSalvaged ) { [element->currentTimer retain]; element->currentTimerSalvaged = YES; } ccArrayRemoveObjectAtIndex(element->timers, i ); - + // update timerIndex in case we are in tick:, looping over the actions if( element->timerIndex >= i ) element->timerIndex--; @@ -308,7 +339,7 @@ -(void) unscheduleSelector:(SEL)selector forTarget:(id)target } } } - + // Not Found // NSLog(@"CCScheduler#unscheduleSelector:forTarget: selector not found: %@", selString); @@ -326,17 +357,17 @@ -(void) priorityIn:(tListEntry**)list target:(id)target priority:(NSInteger)prio listElement->impMethod = (TICK_IMP) [target methodForSelector:updateSelector]; listElement->next = listElement->prev = NULL; listElement->markedForDeletion = NO; - + // empty list ? if( ! *list ) { DL_APPEND( *list, listElement ); - + } else { - BOOL added = NO; - + BOOL added = NO; + for( tListEntry *elem = *list; elem ; elem = elem->next ) { if( priority < elem->priority ) { - + if( elem == *list ) DL_PREPEND(*list, listElement); else { @@ -346,17 +377,17 @@ -(void) priorityIn:(tListEntry**)list target:(id)target priority:(NSInteger)prio elem->prev->next = listElement; elem->prev = listElement; } - + added = YES; break; } } - + // Not added? priority has the higher value. Append it. if( !added ) DL_APPEND(*list, listElement); } - + // update hash entry for quicker access tHashUpdateEntry *hashElement = calloc( sizeof(*hashElement), 1 ); hashElement->target = [target retain]; @@ -368,21 +399,21 @@ -(void) priorityIn:(tListEntry**)list target:(id)target priority:(NSInteger)prio -(void) appendIn:(tListEntry**)list target:(id)target paused:(BOOL)paused { tListEntry *listElement = malloc( sizeof( * listElement ) ); - + listElement->target = target; listElement->paused = paused; listElement->markedForDeletion = NO; listElement->impMethod = (TICK_IMP) [target methodForSelector:updateSelector]; - + DL_APPEND(*list, listElement); - + // update hash entry for quicker access tHashUpdateEntry *hashElement = calloc( sizeof(*hashElement), 1 ); hashElement->target = [target retain]; hashElement->list = list; hashElement->entry = listElement; - HASH_ADD_INT(hashForUpdates, target, hashElement ); + HASH_ADD_INT(hashForUpdates, target, hashElement ); } -(void) scheduleUpdateForTarget:(id)target priority:(NSInteger)priority paused:(BOOL)paused @@ -391,15 +422,15 @@ -(void) scheduleUpdateForTarget:(id)target priority:(NSInteger)priority paused:( HASH_FIND_INT(hashForUpdates, &target, hashElement); if(hashElement) { -#if COCOS2D_DEBUG >= 1 +#if COCOS2D_DEBUG >= 1 NSAssert( hashElement->entry->markedForDeletion, @"CCScheduler: You can't re-schedule an 'update' selector'. Unschedule it first"); -#endif +#endif // TODO : check if priority has changed! - + hashElement->entry->markedForDeletion = NO; return; } - + // most of the updates are going to be 0, that's way there // is an special list for updates with priority 0 if( priority == 0 ) @@ -414,38 +445,42 @@ -(void) scheduleUpdateForTarget:(id)target priority:(NSInteger)priority paused:( - (void) removeUpdateFromHash:(tListEntry*)entry { - tHashUpdateEntry * element = NULL; - - HASH_FIND_INT(hashForUpdates, &entry->target, element); - if( element ) { - // list entry - DL_DELETE( *element->list, element->entry ); - free( element->entry ); - - // hash entry - [element->target release]; - HASH_DEL( hashForUpdates, element); - free(element); - } + tHashUpdateEntry * element = NULL; + + HASH_FIND_INT(hashForUpdates, &entry->target, element); + if( element ) { + // list entry + DL_DELETE( *element->list, element->entry ); + free( element->entry ); + + // hash entry + id target = element->target; + HASH_DEL( hashForUpdates, element); + free(element); + + // target#release should be the last one to prevent + // a possible double-free. eg: If the [target dealloc] might want to remove it itself from there + [target release]; + } } -(void) unscheduleUpdateForTarget:(id)target { if( target == nil ) return; - + tHashUpdateEntry * element = NULL; HASH_FIND_INT(hashForUpdates, &target, element); - if( element ) { + if( element ) { if(updateHashLocked) element->entry->markedForDeletion = YES; else [self removeUpdateFromHash:element->entry]; - + // // list entry // DL_DELETE( *element->list, element->entry ); // free( element->entry ); -// +// // // hash entry // [element->target release]; // HASH_DEL( hashForUpdates, element); @@ -456,9 +491,14 @@ -(void) unscheduleUpdateForTarget:(id)target #pragma mark CCScheduler - Common for Update selector & Custom Selectors -(void) unscheduleAllSelectors +{ + [self unscheduleAllSelectorsWithMinPriority:kCCPrioritySystem]; +} + +-(void) unscheduleAllSelectorsWithMinPriority:(NSInteger)minPriority { // Custom Selectors - for(tHashSelectorEntry *element=hashForSelectors; element != NULL; ) { + for(tHashSelectorEntry *element=hashForSelectors; element != NULL; ) { id target = element->target; element=element->hh.next; [self unscheduleAllSelectorsForTarget:target]; @@ -466,16 +506,24 @@ -(void) unscheduleAllSelectors // Updates selectors tListEntry *entry, *tmp; - DL_FOREACH_SAFE( updates0, entry, tmp ) { - [self unscheduleUpdateForTarget:entry->target]; - } - DL_FOREACH_SAFE( updatesNeg, entry, tmp ) { - [self unscheduleUpdateForTarget:entry->target]; - } + if(minPriority < 0) { + DL_FOREACH_SAFE( updatesNeg, entry, tmp ) { + if(entry->priority >= minPriority) { + [self unscheduleUpdateForTarget:entry->target]; + } + } + } + if(minPriority <= 0) { + DL_FOREACH_SAFE( updates0, entry, tmp ) { + [self unscheduleUpdateForTarget:entry->target]; + } + } DL_FOREACH_SAFE( updatesPos, entry, tmp ) { - [self unscheduleUpdateForTarget:entry->target]; + if(entry->priority >= minPriority) { + [self unscheduleUpdateForTarget:entry->target]; + } } - + } -(void) unscheduleAllSelectorsForTarget:(id)target @@ -483,11 +531,11 @@ -(void) unscheduleAllSelectorsForTarget:(id)target // explicit nil handling if( target == nil ) return; - + // Custom Selectors tHashSelectorEntry *element = NULL; HASH_FIND_INT(hashForSelectors, &target, element); - + if( element ) { if( ccArrayContainsObject(element->timers, element->currentTimer) && !element->currentTimerSalvaged ) { [element->currentTimer retain]; @@ -499,7 +547,7 @@ -(void) unscheduleAllSelectorsForTarget:(id)target else [self removeHashElement:element]; } - + // Update Selector [self unscheduleUpdateForTarget:target]; } @@ -507,32 +555,32 @@ -(void) unscheduleAllSelectorsForTarget:(id)target -(void) resumeTarget:(id)target { NSAssert( target != nil, @"target must be non nil" ); - + // Custom Selectors tHashSelectorEntry *element = NULL; HASH_FIND_INT(hashForSelectors, &target, element); if( element ) element->paused = NO; - + // Update selector tHashUpdateEntry * elementUpdate = NULL; HASH_FIND_INT(hashForUpdates, &target, elementUpdate); if( elementUpdate ) { NSAssert( elementUpdate->entry != NULL, @"resumeTarget: unknown error"); elementUpdate->entry->paused = NO; - } + } } -(void) pauseTarget:(id)target { NSAssert( target != nil, @"target must be non nil" ); - + // Custom selectors tHashSelectorEntry *element = NULL; HASH_FIND_INT(hashForSelectors, &target, element); if( element ) element->paused = YES; - + // Update selector tHashUpdateEntry * elementUpdate = NULL; HASH_FIND_INT(hashForUpdates, &target, elementUpdate); @@ -540,13 +588,13 @@ -(void) pauseTarget:(id)target NSAssert( elementUpdate->entry != NULL, @"pauseTarget: unknown error"); elementUpdate->entry->paused = YES; } - + } -(BOOL) isTargetPaused:(id)target { NSAssert( target != nil, @"target must be non nil" ); - + // Custom selectors tHashSelectorEntry *element = NULL; HASH_FIND_INT(hashForSelectors, &target, element); @@ -555,18 +603,66 @@ -(BOOL) isTargetPaused:(id)target return element->paused; } return NO; // should never get here - + +} + +-(NSSet*) pauseAllTargets +{ + return [self pauseAllTargetsWithMinPriority:kCCPrioritySystem]; +} + +-(NSSet*) pauseAllTargetsWithMinPriority:(NSInteger)minPriority +{ + NSMutableSet* idsWithSelectors = [NSMutableSet setWithCapacity:50]; + + // Custom Selectors + for(tHashSelectorEntry *element=hashForSelectors; element != NULL; element=element->hh.next) { + element->paused = YES; + [idsWithSelectors addObject:element->target]; + } + + // Updates selectors + tListEntry *entry, *tmp; + if(minPriority < 0) { + DL_FOREACH_SAFE( updatesNeg, entry, tmp ) { + if(entry->priority >= minPriority) { + entry->paused = YES; + [idsWithSelectors addObject:entry->target]; + } + } + } + if(minPriority <= 0) { + DL_FOREACH_SAFE( updates0, entry, tmp ) { + entry->paused = YES; + [idsWithSelectors addObject:entry->target]; + } + } + DL_FOREACH_SAFE( updatesPos, entry, tmp ) { + if(entry->priority >= minPriority) { + entry->paused = YES; + [idsWithSelectors addObject:entry->target]; + } + } + + return idsWithSelectors; +} + +-(void) resumeTargets:(NSSet *)targetsToResume +{ + for(id target in targetsToResume) { + [self resumeTarget:target]; + } } #pragma mark CCScheduler - Main Loop --(void) tick: (ccTime) dt +-(void) update: (ccTime) dt { updateHashLocked = YES; - + if( timeScale_ != 1.0f ) dt *= timeScale_; - + // Iterate all over the Updates selectors tListEntry *entry, *tmp; @@ -583,48 +679,48 @@ -(void) tick: (ccTime) dt entry->impMethod( entry->target, updateSelector, dt ); } } - + // updates with priority > 0 DL_FOREACH_SAFE( updatesPos, entry, tmp ) { if( ! entry->paused && !entry->markedForDeletion ) entry->impMethod( entry->target, updateSelector, dt ); } - + // Iterate all over the custome selectors - for(tHashSelectorEntry *elt=hashForSelectors; elt != NULL; ) { - + for(tHashSelectorEntry *elt=hashForSelectors; elt != NULL; ) { + currentTarget = elt; currentTargetSalvaged = NO; if( ! currentTarget->paused ) { - + // The 'timers' ccArray may change while inside this loop. for( elt->timerIndex = 0; elt->timerIndex < elt->timers->num; elt->timerIndex++) { elt->currentTimer = elt->timers->arr[elt->timerIndex]; elt->currentTimerSalvaged = NO; impMethod( elt->currentTimer, updateSelector, dt); - + if( elt->currentTimerSalvaged ) { // The currentTimer told the remove itself. To prevent the timer from // accidentally deallocating itself before finishing its step, we retained - // it. Now that step is done, it's safe to release it. + // it. Now that step is done, it is safe to release it. [elt->currentTimer release]; } - + elt->currentTimer = nil; - } + } } - + // elt, at this moment, is still valid // so it is safe to ask this here (issue #490) elt = elt->hh.next; - + // only delete currentTarget if no actions were scheduled during the cycle (issue #481) if( currentTargetSalvaged && currentTarget->timers->num == 0 ) - [self removeHashElement:currentTarget]; + [self removeHashElement:currentTarget]; } - + // delete all updates that are morked for deletion // updates with priority < 0 DL_FOREACH_SAFE( updatesNeg, entry, tmp ) { @@ -633,7 +729,7 @@ -(void) tick: (ccTime) dt [self removeUpdateFromHash:entry]; } } - + // updates with priority == 0 DL_FOREACH_SAFE( updates0, entry, tmp ) { if(entry->markedForDeletion ) @@ -641,7 +737,7 @@ -(void) tick: (ccTime) dt [self removeUpdateFromHash:entry]; } } - + // updates with priority > 0 DL_FOREACH_SAFE( updatesPos, entry, tmp ) { if(entry->markedForDeletion ) diff --git a/cocos2d/cocos2d/CCShaderCache.h b/cocos2d/cocos2d/CCShaderCache.h new file mode 100755 index 0000000..5c93a99 --- /dev/null +++ b/cocos2d/cocos2d/CCShaderCache.h @@ -0,0 +1,60 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011 Zynga Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import +#import "ccMacros.h" +#ifdef __CC_PLATFORM_IOS +#import +#endif // __CC_PLATFORM_IOS + +@class CCGLProgram; + +/** CCShaderCache + Singleton that stores manages GL shaders + @since v2.0 + */ +@interface CCShaderCache : NSObject { + + NSMutableDictionary *programs_; + +} + +/** returns the shared instance */ ++ (CCShaderCache *)sharedShaderCache; + +/** purges the cache. It releases the retained instance. */ ++(void)purgeSharedShaderCache; + +/** loads the default shaders */ +-(void) loadDefaultShaders; + +/** returns a GL program for a given key */ +-(CCGLProgram *) programForKey:(NSString*)key; + +/** adds a CCGLProgram to the cache for a given name */ +- (void) addProgram:(CCGLProgram*)program forKey:(NSString*)key; + +@end + diff --git a/cocos2d/cocos2d/CCShaderCache.m b/cocos2d/cocos2d/CCShaderCache.m new file mode 100755 index 0000000..db701cf --- /dev/null +++ b/cocos2d/cocos2d/CCShaderCache.m @@ -0,0 +1,214 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2011 Ricardo Quesada + * Copyright (c) 2011 Zynga Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import "CCShaderCache.h" +#import "ccShaders.h" +#import "CCGLProgram.h" +#import "ccMacros.h" +#import "Support/OpenGL_Internal.h" + +static CCShaderCache *_sharedShaderCache; + +@implementation CCShaderCache + +#pragma mark CCShaderCache - Alloc, Init & Dealloc + ++ (CCShaderCache *)sharedShaderCache +{ + if (!_sharedShaderCache) + _sharedShaderCache = [[CCShaderCache alloc] init]; + + return _sharedShaderCache; +} + ++(void)purgeSharedShaderCache +{ + [_sharedShaderCache release]; + _sharedShaderCache = nil; +} + + ++(id)alloc +{ + NSAssert(_sharedShaderCache == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; +} + +- (void)dealloc +{ + CCLOGINFO(@"cocos2d deallocing %@", self); + + [programs_ release]; + [super dealloc]; +} + ++(void)purgeSharedTextureCache +{ + [_sharedShaderCache release]; + _sharedShaderCache = nil; +} + +-(id) init +{ + if( (self=[super init]) ) { + programs_ = [[NSMutableDictionary alloc ] initWithCapacity: 10]; + + [self loadDefaultShaders]; + } + + return self; +} + +-(void) loadDefaultShaders +{ + // Position Texture Color shader + CCGLProgram *p = [[CCGLProgram alloc] initWithVertexShaderByteArray:ccPositionTextureColor_vert + fragmentShaderByteArray:ccPositionTextureColor_frag]; + + [p addAttribute:kCCAttributeNamePosition index:kCCVertexAttrib_Position]; + [p addAttribute:kCCAttributeNameColor index:kCCVertexAttrib_Color]; + [p addAttribute:kCCAttributeNameTexCoord index:kCCVertexAttrib_TexCoords]; + + [p link]; + [p updateUniforms]; + + [programs_ setObject:p forKey:kCCShader_PositionTextureColor]; + [p release]; + + CHECK_GL_ERROR_DEBUG(); + + // Position Texture Color alpha test + p = [[CCGLProgram alloc] initWithVertexShaderByteArray:ccPositionTextureColor_vert + fragmentShaderByteArray:ccPositionTextureColorAlphaTest_frag]; + + [p addAttribute:kCCAttributeNamePosition index:kCCVertexAttrib_Position]; + [p addAttribute:kCCAttributeNameColor index:kCCVertexAttrib_Color]; + [p addAttribute:kCCAttributeNameTexCoord index:kCCVertexAttrib_TexCoords]; + + [p link]; + [p updateUniforms]; + + [programs_ setObject:p forKey:kCCShader_PositionTextureColorAlphaTest]; + [p release]; + + CHECK_GL_ERROR_DEBUG(); + + // + // Position, Color shader + // + p = [[CCGLProgram alloc] initWithVertexShaderByteArray:ccPositionColor_vert + fragmentShaderByteArray:ccPositionColor_frag]; + + [p addAttribute:kCCAttributeNamePosition index:kCCVertexAttrib_Position]; + [p addAttribute:kCCAttributeNameColor index:kCCVertexAttrib_Color]; + + [p link]; + [p updateUniforms]; + + [programs_ setObject:p forKey:kCCShader_PositionColor]; + [p release]; + + CHECK_GL_ERROR_DEBUG(); + + // + // Position Texture shader + // + p = [[CCGLProgram alloc] initWithVertexShaderByteArray:ccPositionTexture_vert + fragmentShaderByteArray:ccPositionTexture_frag]; + + [p addAttribute:kCCAttributeNamePosition index:kCCVertexAttrib_Position]; + [p addAttribute:kCCAttributeNameTexCoord index:kCCVertexAttrib_TexCoords]; + + [p link]; + [p updateUniforms]; + + [programs_ setObject:p forKey:kCCShader_PositionTexture]; + [p release]; + + CHECK_GL_ERROR_DEBUG(); + + // + // Position, Texture attribs, 1 Color as uniform shader + // + p = [[CCGLProgram alloc] initWithVertexShaderByteArray:ccPositionTexture_uColor_vert + fragmentShaderByteArray:ccPositionTexture_uColor_frag]; + + [p addAttribute:kCCAttributeNamePosition index:kCCVertexAttrib_Position]; + [p addAttribute:kCCAttributeNameTexCoord index:kCCVertexAttrib_TexCoords]; + + [p link]; + [p updateUniforms]; + + [programs_ setObject:p forKey:kCCShader_PositionTexture_uColor]; + [p release]; + + CHECK_GL_ERROR_DEBUG(); + + // + // Position Texture A8 Color shader + // + p = [[CCGLProgram alloc] initWithVertexShaderByteArray:ccPositionTextureA8Color_vert + fragmentShaderByteArray:ccPositionTextureA8Color_frag]; + + [p addAttribute:kCCAttributeNamePosition index:kCCVertexAttrib_Position]; + [p addAttribute:kCCAttributeNameColor index:kCCVertexAttrib_Color]; + [p addAttribute:kCCAttributeNameTexCoord index:kCCVertexAttrib_TexCoords]; + + [p link]; + [p updateUniforms]; + + [programs_ setObject:p forKey:kCCShader_PositionTextureA8Color]; + [p release]; + + CHECK_GL_ERROR_DEBUG(); + + // + // Position and 1 color passed as a uniform (to similate glColor4ub ) + // + p = [[CCGLProgram alloc] initWithVertexShaderByteArray:ccPosition_uColor_vert + fragmentShaderByteArray:ccPosition_uColor_frag]; + + [p addAttribute:@"aVertex" index:kCCVertexAttrib_Position]; + + [p link]; + [p updateUniforms]; + + [programs_ setObject:p forKey:kCCShader_Position_uColor]; + [p release]; + + CHECK_GL_ERROR_DEBUG(); +} + +-(CCGLProgram *) programForKey:(NSString*)key +{ + return [programs_ objectForKey:key]; +} + +- (void) addProgram:(CCGLProgram*)program forKey:(NSString*)key +{ + [programs_ setObject:program forKey:key]; +} + +@end diff --git a/cocos2d/cocos2d/CCSprite.h b/cocos2d/cocos2d/CCSprite.h old mode 100644 new mode 100755 index d48bdfe..497ce22 --- a/cocos2d/cocos2d/CCSprite.h +++ b/cocos2d/cocos2d/CCSprite.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -37,26 +37,6 @@ #define CCSpriteIndexNotInitialized 0xffffffff /// CCSprite invalid index on the CCSpriteBatchode -/** - Whether or not an CCSprite will rotate, scale or translate with it's parent. - Useful in health bars, when you want that the health bar translates with it's parent but you don't - want it to rotate with its parent. - @since v0.99.0 - */ -typedef enum { - //! Translate with it's parent - CC_HONOR_PARENT_TRANSFORM_TRANSLATE = 1 << 0, - //! Rotate with it's parent - CC_HONOR_PARENT_TRANSFORM_ROTATE = 1 << 1, - //! Scale with it's parent - CC_HONOR_PARENT_TRANSFORM_SCALE = 1 << 2, - //! Skew with it's parent - CC_HONOR_PARENT_TRANSFORM_SKEW = 1 << 3, - - //! All possible transformation enabled. Default value. - CC_HONOR_PARENT_TRANSFORM_ALL = CC_HONOR_PARENT_TRANSFORM_TRANSLATE | CC_HONOR_PARENT_TRANSFORM_ROTATE | CC_HONOR_PARENT_TRANSFORM_SCALE | CC_HONOR_PARENT_TRANSFORM_SKEW, - -} ccHonorParentTransform; /** CCSprite is a 2d image ( http://en.wikipedia.org/wiki/Sprite_(computer_graphics) ) * @@ -82,18 +62,19 @@ typedef enum { */ @interface CCSprite : CCNode { - + // // Data used when the sprite is rendered using a CCSpriteBatchNode // CCTextureAtlas *textureAtlas_; // Sprite Sheet texture atlas (weak reference) NSUInteger atlasIndex_; // Absolute (real) Index on the batch node CCSpriteBatchNode *batchNode_; // Used batch node (weak reference) - ccHonorParentTransform honorParentTransform_; // whether or not to transform according to its parent transformations + CGAffineTransform transformToBatch_; // BOOL dirty_; // Sprite needs to be updated BOOL recursiveDirty_; // Subchildren needs to be updated BOOL hasChildren_; // optimization to check if it contain children - + BOOL shouldBeHidden_; // should not be drawn because one of the ancestors is not visible + // // Data used when the sprite is self-rendered // @@ -104,34 +85,28 @@ typedef enum { // Shared data // - // whether or not it's parent is a CCSpriteBatchNode - BOOL usesBatchNode_; + // sprite rectangle + CGRect rect_; // texture - CGRect rect_; - CGRect rectInPixels_; BOOL rectRotated_; - + // Offset Position (used by Zwoptex) - CGPoint offsetPositionInPixels_; + CGPoint offsetPosition_; CGPoint unflippedOffsetPositionFromCenter_; // vertex coords, texture coords and color info ccV3F_C4B_T2F_Quad quad_; - + // opacity and RGB protocol GLubyte opacity_; ccColor3B color_; ccColor3B colorUnmodified_; BOOL opacityModifyRGB_; - + // image is flipped BOOL flipX_; BOOL flipY_; - -@public - // used internally. - void (*updateMethod)(id, SEL); } /** whether or not the Sprite needs to be updated in the Atlas */ @@ -140,15 +115,15 @@ typedef enum { @property (nonatomic,readonly) ccV3F_C4B_T2F_Quad quad; /** The index used on the TextureAtlas. Don't modify this value unless you know what you are doing */ @property (nonatomic,readwrite) NSUInteger atlasIndex; -/** returns the rect of the CCSprite in points */ +/** returns the texture rect of the CCSprite in points */ @property (nonatomic,readonly) CGRect textureRect; /** returns whether or not the texture rectangle is rotated */ @property (nonatomic,readonly) BOOL textureRectRotated; -/** whether or not the sprite is flipped horizontally. +/** whether or not the sprite is flipped horizontally. It only flips the texture of the sprite, and not the texture of the sprite's children. Also, flipping the texture doesn't alter the anchorPoint. If you want to flip the anchorPoint too, and/or to flip the children too use: - + sprite.scaleX *= -1; */ @property (nonatomic,readwrite) BOOL flipX; @@ -156,7 +131,7 @@ typedef enum { It only flips the texture of the sprite, and not the texture of the sprite's children. Also, flipping the texture doesn't alter the anchorPoint. If you want to flip the anchorPoint too, and/or to flip the children too use: - + sprite.scaleY *= -1; */ @property (nonatomic,readwrite) BOOL flipY; @@ -164,22 +139,14 @@ typedef enum { @property (nonatomic,readwrite) GLubyte opacity; /** RGB colors: conforms to CCRGBAProtocol protocol */ @property (nonatomic,readwrite) ccColor3B color; -/** whether or not the Sprite is rendered using a CCSpriteBatchNode */ -@property (nonatomic,readwrite) BOOL usesBatchNode; /** weak reference of the CCTextureAtlas used when the sprite is rendered using a CCSpriteBatchNode */ @property (nonatomic,readwrite,assign) CCTextureAtlas *textureAtlas; /** weak reference to the CCSpriteBatchNode that renders the CCSprite */ @property (nonatomic,readwrite,assign) CCSpriteBatchNode *batchNode; -/** whether or not to transform according to its parent transfomrations. - Useful for health bars. eg: Don't rotate the health bar, even if the parent rotates. - IMPORTANT: Only valid if it is rendered using an CCSpriteBatchNode. - @since v0.99.0 - */ -@property (nonatomic,readwrite) ccHonorParentTransform honorParentTransform; -/** offset position in pixels of the sprite in points. Calculated automatically by editors like Zwoptex. +/** offset position in points of the sprite in points. Calculated automatically by editors like Zwoptex. @since v0.99.0 */ -@property (nonatomic,readonly) CGPoint offsetPositionInPixels; +@property (nonatomic,readonly) CGPoint offsetPosition; /** conforms to CCTextureProtocol protocol */ @property (nonatomic,readwrite) ccBlendFunc blendFunc; @@ -221,28 +188,29 @@ typedef enum { /** Creates an sprite with a CGImageRef and a key. The key is used by the CCTextureCache to know if a texture was already created with this CGImage. For example, a valid key is: @"sprite_frame_01". - If key is nil, then a new texture will be created each time by the CCTextureCache. + If key is nil, then a new texture will be created each time by the CCTextureCache. @since v0.99.0 */ +(id) spriteWithCGImage: (CGImageRef)image key:(NSString*)key; - -/** Creates an sprite with an CCBatchNode and a rect - */ -+(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect; - - /** Initializes an sprite with a texture. The rect used will be the size of the texture. The offset will be (0,0). */ -(id) initWithTexture:(CCTexture2D*)texture; -/** Initializes an sprite with a texture and a rect in points. +/** Initializes an sprite with a texture and a rect in points (unrotated) The offset will be (0,0). */ -(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect; +/** Initializes an sprite with a texture and a rect in points, optionally rotated. + The offset will be (0,0). + IMPORTANT: This is the designated initializer. + */ +- (id)initWithTexture:(CCTexture2D *)texture rect:(CGRect)rect rotated:(BOOL)rotated; + + /** Initializes an sprite with an sprite frame. */ -(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame; @@ -268,44 +236,34 @@ typedef enum { /** Initializes an sprite with a CGImageRef and a key The key is used by the CCTextureCache to know if a texture was already created with this CGImage. For example, a valid key is: @"sprite_frame_01". - If key is nil, then a new texture will be created each time by the CCTextureCache. + If key is nil, then a new texture will be created each time by the CCTextureCache. @since v0.99.0 */ -(id) initWithCGImage:(CGImageRef)image key:(NSString*)key; -/** Initializes an sprite with an CCSpriteBatchNode and a rect in points - */ --(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect; - -/** Initializes an sprite with an CCSpriteBatchNode and a rect in pixels - @since v0.99.5 - */ --(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rectInPixels:(CGRect)rect; - - - #pragma mark CCSprite - BatchNode methods /** updates the quad according the the rotation, position, scale values. */ -(void)updateTransform; -/** updates the texture rect of the CCSprite in points. +#pragma mark CCSprite - Texture methods + +/** set the texture rect of the CCSprite in points. + It will call setTextureRect:rotated:untrimmedSize with rotated = NO, and utrimmedSize = rect.size. */ -(void) setTextureRect:(CGRect) rect; -/** updates the texture rect, rectRotated and untrimmed size of the CCSprite in pixels - */ --(void) setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)size; -/** tell the sprite to use self-render. - @since v0.99.0 +/** set the texture rect, rectRotated and untrimmed size of the CCSprite in points. + It will update the texture coordinates and the vertex rectangle. */ --(void) useSelfRender; +-(void) setTextureRect:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)size; -/** tell the sprite to use sprite batch node - @since v0.99.0 +/** set the vertex rect. + It will be called internally by setTextureRect. Useful if you want to create 2x images from SD images in Retina Display. + Do not call it manually. Use setTextureRect instead. */ --(void) useBatchNode:(CCSpriteBatchNode*)batchNode; +-(void)setVertexRect:(CGRect)rect; #pragma mark CCSprite - Frames @@ -317,7 +275,7 @@ typedef enum { -(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame; /** returns the current displayed frame. */ --(CCSpriteFrame*) displayedFrame; +-(CCSpriteFrame*) displayFrame; #pragma mark CCSprite - Animation diff --git a/cocos2d/cocos2d/CCSprite.m b/cocos2d/cocos2d/CCSprite.m old mode 100644 new mode 100755 index 413e708..67478d8 --- a/cocos2d/cocos2d/CCSprite.m +++ b/cocos2d/cocos2d/CCSprite.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,8 +24,6 @@ * */ -#import - #import "ccConfig.h" #import "CCSpriteBatchNode.h" #import "CCSprite.h" @@ -34,8 +32,18 @@ #import "CCAnimation.h" #import "CCAnimationCache.h" #import "CCTextureCache.h" -#import "Support/CGPointExtension.h" #import "CCDrawingPrimitives.h" +#import "CCShaderCache.h" +#import "ccGLStateCache.h" +#import "CCGLProgram.h" +#import "CCDirector.h" +#import "Support/CGPointExtension.h" +#import "Support/TransformUtils.h" +#import "Support/CCProfiling.h" +#import "Support/OpenGL_Internal.h" + +// external +#import "kazmath/GL/matrix.h" #pragma mark - #pragma mark CCSprite @@ -46,21 +54,11 @@ #define RENDER_IN_SUBPIXEL(__A__) ( (int)(__A__)) #endif -// XXX: Optmization -struct transformValues_ { - CGPoint pos; // position x and y - CGPoint scale; // scale x and y - float rotation; - CGPoint skew; // skew x and y - CGPoint ap; // anchor point in pixels - BOOL visible; -}; - -@interface CCSprite (Private) --(void)updateTextureCoords:(CGRect)rect; --(void)updateBlendFunc; --(void) initAnimationDictionary; --(void) getTransformValues:(struct transformValues_*)tv; // optimization + +@interface CCSprite () +-(void) setTextureCoords:(CGRect)rect; +-(void) updateBlendFunc; +-(void) setReorderChildDirtyRecursively; @end @implementation CCSprite @@ -71,11 +69,8 @@ @implementation CCSprite @synthesize textureRect = rect_; @synthesize textureRectRotated = rectRotated_; @synthesize blendFunc = blendFunc_; -@synthesize usesBatchNode = usesBatchNode_; @synthesize textureAtlas = textureAtlas_; -@synthesize batchNode = batchNode_; -@synthesize honorParentTransform = honorParentTransform_; -@synthesize offsetPositionInPixels = offsetPositionInPixels_; +@synthesize offsetPosition = offsetPosition_; +(id)spriteWithTexture:(CCTexture2D*)texture @@ -106,7 +101,7 @@ +(id)spriteWithSpriteFrame:(CCSpriteFrame*)spriteFrame +(id)spriteWithSpriteFrameName:(NSString*)spriteFrameName { CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName]; - + NSAssert1(frame!=nil, @"Invalid spriteFrameName: %@", spriteFrameName); return [self spriteWithSpriteFrame:frame]; } @@ -116,75 +111,64 @@ +(id)spriteWithCGImage:(CGImageRef)image key:(NSString*)key return [[[self alloc] initWithCGImage:image key:key] autorelease]; } -+(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect +-(id) init { - return [[[self alloc] initWithBatchNode:batchNode rect:rect] autorelease]; + return [self initWithTexture:nil rect:CGRectZero]; } --(id) init +// designated initializer +-(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect rotated:(BOOL)rotated { - if( (self=[super init]) ) { + if( (self = [super init]) ) + { + // shader program + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTextureColor]; + dirty_ = recursiveDirty_ = NO; - - // by default use "Self Render". - // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" - [self useSelfRender]; - + opacityModifyRGB_ = YES; opacity_ = 255; color_ = colorUnmodified_ = ccWHITE; - + blendFunc_.src = CC_BLEND_SRC; blendFunc_.dst = CC_BLEND_DST; - - // update texture (calls updateBlendFunc) - [self setTexture:nil]; - - // clean the Quad - bzero(&quad_, sizeof(quad_)); - + flipY_ = flipX_ = NO; - + // default transform anchor: center anchorPoint_ = ccp(0.5f, 0.5f); - + // zwoptex default values - offsetPositionInPixels_ = CGPointZero; - - honorParentTransform_ = CC_HONOR_PARENT_TRANSFORM_ALL; + offsetPosition_ = CGPointZero; + hasChildren_ = NO; - + batchNode_ = nil; + + // clean the Quad + bzero(&quad_, sizeof(quad_)); + // Atlas: Color ccColor4B tmpColor = {255,255,255,255}; quad_.bl.colors = tmpColor; quad_.br.colors = tmpColor; quad_.tl.colors = tmpColor; - quad_.tr.colors = tmpColor; - - // Atlas: Vertex - - // updated in "useSelfRender" - - // Atlas: TexCoords - [self setTextureRectInPixels:CGRectZero rotated:NO untrimmedSize:CGSizeZero]; - - // updateMethod selector - updateMethod = (__typeof__(updateMethod))[self methodForSelector:@selector(updateTransform)]; + quad_.tr.colors = tmpColor; + + [self setTexture:texture]; + [self setTextureRect:rect rotated:rotated untrimmedSize:rect.size]; + + + // by default use "Self Render". + // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" + [self setBatchNode:nil]; + } - return self; } -(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect { - NSAssert(texture!=nil, @"Invalid texture for sprite"); - // IMPORTANT: [self init] and not [super init]; - if( (self = [self init]) ) - { - [self setTexture:texture]; - [self setTextureRect:rect]; - } - return self; + return [self initWithTexture:texture rect:rect rotated:NO]; } -(id) initWithTexture:(CCTexture2D*)texture @@ -198,7 +182,7 @@ -(id) initWithTexture:(CCTexture2D*)texture -(id) initWithFile:(NSString*)filename { - NSAssert(filename!=nil, @"Invalid filename for sprite"); + NSAssert(filename != nil, @"Invalid filename for sprite"); CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename]; if( texture ) { @@ -243,39 +227,22 @@ -(id)initWithSpriteFrameName:(NSString*)spriteFrameName - (id) initWithCGImage:(CGImageRef)image key:(NSString*)key { NSAssert(image!=nil, @"Invalid CGImageRef for sprite"); - + // XXX: possible bug. See issue #349. New API should be added CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key]; - + CGRect rect = CGRectZero; rect.size = texture.contentSize; - - return [self initWithTexture:texture rect:rect]; -} - --(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect -{ - id ret = [self initWithTexture:batchNode.texture rect:rect]; - [self useBatchNode:batchNode]; - - return ret; -} --(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rectInPixels:(CGRect)rect -{ - id ret = [self initWithTexture:batchNode.texture]; - [self setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size]; - [self useBatchNode:batchNode]; - - return ret; + return [self initWithTexture:texture rect:rect]; } - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | Rect = (%.2f,%.2f,%.2f,%.2f) | tag = %i | atlasIndex = %i>", [self class], self, + return [NSString stringWithFormat:@"<%@ = %p | Rect = (%.2f,%.2f,%.2f,%.2f) | tag = %ld | atlasIndex = %ld>", [self class], self, rect_.origin.x, rect_.origin.y, rect_.size.width, rect_.size.height, - tag_, - atlasIndex_ + (long)tag_, + (unsigned long)atlasIndex_ ]; } @@ -285,60 +252,66 @@ - (void) dealloc [super dealloc]; } --(void) useSelfRender +-(CCSpriteBatchNode*) batchNode { - atlasIndex_ = CCSpriteIndexNotInitialized; - usesBatchNode_ = NO; - textureAtlas_ = nil; - batchNode_ = nil; - dirty_ = recursiveDirty_ = NO; - - float x1 = 0 + offsetPositionInPixels_.x; - float y1 = 0 + offsetPositionInPixels_.y; - float x2 = x1 + rectInPixels_.size.width; - float y2 = y1 + rectInPixels_.size.height; - quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 }; - quad_.br.vertices = (ccVertex3F) { x2, y1, 0 }; - quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 }; - quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 }; + return batchNode_; } --(void) useBatchNode:(CCSpriteBatchNode*)batchNode +-(void) setBatchNode:(CCSpriteBatchNode *)batchNode { - usesBatchNode_ = YES; - textureAtlas_ = [batchNode textureAtlas]; // weak ref - batchNode_ = batchNode; // weak ref + batchNode_ = batchNode; // weak reference + + // self render + if( ! batchNode ) { + atlasIndex_ = CCSpriteIndexNotInitialized; + textureAtlas_ = nil; + dirty_ = recursiveDirty_ = NO; + + float x1 = offsetPosition_.x; + float y1 = offsetPosition_.y; + float x2 = x1 + rect_.size.width; + float y2 = y1 + rect_.size.height; + quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 }; + quad_.br.vertices = (ccVertex3F) { x2, y1, 0 }; + quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 }; + quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 }; + + } else { + + // using batch + transformToBatch_ = CGAffineTransformIdentity; + textureAtlas_ = [batchNode textureAtlas]; // weak ref + } } --(void)setTextureRect:(CGRect)rect +-(void) setTextureRect:(CGRect)rect { - CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect ); - [self setTextureRectInPixels:rectInPixels rotated:NO untrimmedSize:rectInPixels.size]; + [self setTextureRect:rect rotated:NO untrimmedSize:rect.size]; } --(void)setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)untrimmedSize +-(void) setTextureRect:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)untrimmedSize { - rectInPixels_ = rect; - rect_ = CC_RECT_PIXELS_TO_POINTS( rect ); rectRotated_ = rotated; - [self setContentSizeInPixels:untrimmedSize]; - [self updateTextureCoords:rectInPixels_]; + [self setContentSize:untrimmedSize]; + [self setVertexRect:rect]; + [self setTextureCoords:rect]; + + CGPoint relativeOffset = unflippedOffsetPositionFromCenter_; - CGPoint relativeOffsetInPixels = unflippedOffsetPositionFromCenter_; - // issue #732 if( flipX_ ) - relativeOffsetInPixels.x = -relativeOffsetInPixels.x; + relativeOffset.x = -relativeOffset.x; if( flipY_ ) - relativeOffsetInPixels.y = -relativeOffsetInPixels.y; - - offsetPositionInPixels_.x = relativeOffsetInPixels.x + (contentSizeInPixels_.width - rectInPixels_.size.width) / 2; - offsetPositionInPixels_.y = relativeOffsetInPixels.y + (contentSizeInPixels_.height - rectInPixels_.size.height) / 2; - - + relativeOffset.y = -relativeOffset.y; + + + offsetPosition_.x = relativeOffset.x + (contentSize_.width - rect_.size.width) / 2; + offsetPosition_.y = relativeOffset.y + (contentSize_.height - rect_.size.height) / 2; + + // rendering using batch node - if( usesBatchNode_ ) { + if( batchNode_ ) { // update dirty_, don't update recursiveDirty_ dirty_ = YES; } @@ -347,31 +320,40 @@ -(void)setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:( else { // Atlas: Vertex - float x1 = 0 + offsetPositionInPixels_.x; - float y1 = 0 + offsetPositionInPixels_.y; - float x2 = x1 + rectInPixels_.size.width; - float y2 = y1 + rectInPixels_.size.height; - + float x1 = offsetPosition_.x; + float y1 = offsetPosition_.y; + float x2 = x1 + rect_.size.width; + float y2 = y1 + rect_.size.height; + // Don't update Z. quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 }; quad_.br.vertices = (ccVertex3F) { x2, y1, 0 }; quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 }; - quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 }; - } + quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 }; + } +} + +// override this method to generate "double scale" sprites +-(void) setVertexRect:(CGRect)rect +{ + rect_ = rect; } --(void)updateTextureCoords:(CGRect)rect +-(void) setTextureCoords:(CGRect)rect { - CCTexture2D *tex = (usesBatchNode_)?[textureAtlas_ texture]:texture_; + rect = CC_RECT_POINTS_TO_PIXELS(rect); + + CCTexture2D *tex = (batchNode_) ? [textureAtlas_ texture] : texture_; if(!tex) return; - + float atlasWidth = (float)tex.pixelsWide; float atlasHeight = (float)tex.pixelsHigh; - - float left,right,top,bottom; - - if(rectRotated_){ + + float left, right ,top , bottom; + + if(rectRotated_) + { #if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL left = (2*rect.origin.x+1)/(2*atlasWidth); right = left+(rect.size.height*2-2)/(2*atlasWidth); @@ -379,16 +361,16 @@ -(void)updateTextureCoords:(CGRect)rect bottom = top+(rect.size.width*2-2)/(2*atlasHeight); #else left = rect.origin.x/atlasWidth; - right = left+(rect.size.height/atlasWidth); + right = (rect.origin.x+rect.size.height) / atlasWidth; top = rect.origin.y/atlasHeight; - bottom = top+(rect.size.width/atlasHeight); + bottom = (rect.origin.y+rect.size.width) / atlasHeight; #endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL - + if( flipX_) CC_SWAP(top,bottom); if( flipY_) CC_SWAP(left,right); - + quad_.bl.texCoords.u = left; quad_.bl.texCoords.v = top; quad_.br.texCoords.u = left; @@ -405,16 +387,16 @@ -(void)updateTextureCoords:(CGRect)rect bottom = top + (rect.size.height*2-2)/(2*atlasHeight); #else left = rect.origin.x/atlasWidth; - right = left + rect.size.width/atlasWidth; + right = (rect.origin.x + rect.size.width) / atlasWidth; top = rect.origin.y/atlasHeight; - bottom = top + rect.size.height/atlasHeight; + bottom = (rect.origin.y + rect.size.height) / atlasHeight; #endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL - + if( flipX_) CC_SWAP(left,right); if( flipY_) CC_SWAP(top,bottom); - + quad_.bl.texCoords.u = left; quad_.bl.texCoords.v = bottom; quad_.br.texCoords.u = right; @@ -428,202 +410,151 @@ -(void)updateTextureCoords:(CGRect)rect -(void)updateTransform { - NSAssert( usesBatchNode_, @"updateTransform is only valid when CCSprite is being renderd using an CCSpriteBatchNode"); + NSAssert( batchNode_, @"updateTransform is only valid when CCSprite is being rendered using an CCSpriteBatchNode"); - // optimization. Quick return if not dirty - if( ! dirty_ ) - return; - - CGAffineTransform matrix; - - // Optimization: if it is not visible, then do nothing - if( ! visible_ ) { - quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0}; - [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; - dirty_ = recursiveDirty_ = NO; - return ; - } - - - // Optimization: If parent is batchnode, or parent is nil - // build Affine transform manually - if( ! parent_ || parent_ == batchNode_ ) { - - float radians = -CC_DEGREES_TO_RADIANS(rotation_); - float c = cosf(radians); - float s = sinf(radians); - - matrix = CGAffineTransformMake( c * scaleX_, s * scaleX_, - -s * scaleY_, c * scaleY_, - positionInPixels_.x, positionInPixels_.y); - if( skewX_ || skewY_ ) { - CGAffineTransform skewMatrix = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), - tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, - 0.0f, 0.0f); - matrix = CGAffineTransformConcat(skewMatrix, matrix); + // recaculate matrix only if it is dirty + if( self.dirty ) { + + // If it is not visible, or one of its ancestors is not visible, then do nothing: + if( !visible_ || ( parent_ && parent_ != batchNode_ && ((CCSprite*)parent_)->shouldBeHidden_) ) { + quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0}; + shouldBeHidden_ = YES; } - matrix = CGAffineTransformTranslate(matrix, -anchorPointInPixels_.x, -anchorPointInPixels_.y); - - - } else { // parent_ != batchNode_ - - // else do affine transformation according to the HonorParentTransform - - matrix = CGAffineTransformIdentity; - ccHonorParentTransform prevHonor = CC_HONOR_PARENT_TRANSFORM_ALL; - - for (CCNode *p = self ; p && p != batchNode_ ; p = p.parent) { - - // Might happen. Issue #1053 - NSAssert( [p isKindOfClass:[CCSprite class]], @"CCSprite should be a CCSprite subclass. Probably you initialized an sprite with a batchnode, but you didn't add it to the batch node." ); - - struct transformValues_ tv; - [(CCSprite*)p getTransformValues: &tv]; - - // If any of the parents are not visible, then don't draw this node - if( ! tv.visible ) { - quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0}; - [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; - dirty_ = recursiveDirty_ = NO; - return; - } - CGAffineTransform newMatrix = CGAffineTransformIdentity; - - // 2nd: Translate, Skew, Rotate, Scale - if( prevHonor & CC_HONOR_PARENT_TRANSFORM_TRANSLATE ) - newMatrix = CGAffineTransformTranslate(newMatrix, tv.pos.x, tv.pos.y); - if( prevHonor & CC_HONOR_PARENT_TRANSFORM_ROTATE ) - newMatrix = CGAffineTransformRotate(newMatrix, -CC_DEGREES_TO_RADIANS(tv.rotation)); - if ( prevHonor & CC_HONOR_PARENT_TRANSFORM_SKEW ) { - CGAffineTransform skew = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(tv.skew.y)), tanf(CC_DEGREES_TO_RADIANS(tv.skew.x)), 1.0f, 0.0f, 0.0f); - // apply the skew to the transform - newMatrix = CGAffineTransformConcat(skew, newMatrix); - } - if( prevHonor & CC_HONOR_PARENT_TRANSFORM_SCALE ) { - newMatrix = CGAffineTransformScale(newMatrix, tv.scale.x, tv.scale.y); + + else { + + shouldBeHidden_ = NO; + + if( ! parent_ || parent_ == batchNode_ ) + transformToBatch_ = [self nodeToParentTransform]; + + else { + NSAssert( [parent_ isKindOfClass:[CCSprite class]], @"Logic error in CCSprite. Parent must be a CCSprite"); + + transformToBatch_ = CGAffineTransformConcat( [self nodeToParentTransform] , ((CCSprite*)parent_)->transformToBatch_ ); } - - // 3rd: Translate anchor point - newMatrix = CGAffineTransformTranslate(newMatrix, -tv.ap.x, -tv.ap.y); - - // 4th: Matrix multiplication - matrix = CGAffineTransformConcat( matrix, newMatrix); - - prevHonor = [(CCSprite*)p honorParentTransform]; - } + + // + // calculate the Quad based on the Affine Matrix + // + + CGSize size = rect_.size; + + float x1 = offsetPosition_.x; + float y1 = offsetPosition_.y; + + float x2 = x1 + size.width; + float y2 = y1 + size.height; + float x = transformToBatch_.tx; + float y = transformToBatch_.ty; + + float cr = transformToBatch_.a; + float sr = transformToBatch_.b; + float cr2 = transformToBatch_.d; + float sr2 = -transformToBatch_.c; + float ax = x1 * cr - y1 * sr2 + x; + float ay = x1 * sr + y1 * cr2 + y; + + float bx = x2 * cr - y1 * sr2 + x; + float by = x2 * sr + y1 * cr2 + y; + + float cx = x2 * cr - y2 * sr2 + x; + float cy = x2 * sr + y2 * cr2 + y; + + float dx = x1 * cr - y2 * sr2 + x; + float dy = x1 * sr + y2 * cr2 + y; + + quad_.bl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(ax), RENDER_IN_SUBPIXEL(ay), vertexZ_ }; + quad_.br.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(bx), RENDER_IN_SUBPIXEL(by), vertexZ_ }; + quad_.tl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(dx), RENDER_IN_SUBPIXEL(dy), vertexZ_ }; + quad_.tr.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(cx), RENDER_IN_SUBPIXEL(cy), vertexZ_ }; + } + + [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; + dirty_ = recursiveDirty_ = NO; } - - - // - // calculate the Quad based on the Affine Matrix - // - - CGSize size = rectInPixels_.size; - - float x1 = offsetPositionInPixels_.x; - float y1 = offsetPositionInPixels_.y; - - float x2 = x1 + size.width; - float y2 = y1 + size.height; - float x = matrix.tx; - float y = matrix.ty; - - float cr = matrix.a; - float sr = matrix.b; - float cr2 = matrix.d; - float sr2 = -matrix.c; - float ax = x1 * cr - y1 * sr2 + x; - float ay = x1 * sr + y1 * cr2 + y; - - float bx = x2 * cr - y1 * sr2 + x; - float by = x2 * sr + y1 * cr2 + y; - - float cx = x2 * cr - y2 * sr2 + x; - float cy = x2 * sr + y2 * cr2 + y; - - float dx = x1 * cr - y2 * sr2 + x; - float dy = x1 * sr + y2 * cr2 + y; - - quad_.bl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(ax), RENDER_IN_SUBPIXEL(ay), vertexZ_ }; - quad_.br.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(bx), RENDER_IN_SUBPIXEL(by), vertexZ_ }; - quad_.tl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(dx), RENDER_IN_SUBPIXEL(dy), vertexZ_ }; - quad_.tr.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(cx), RENDER_IN_SUBPIXEL(cy), vertexZ_ }; - - [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; - dirty_ = recursiveDirty_ = NO; -} - -// XXX: Optimization: instead of calling 5 times the parent sprite to obtain: position, scale.x, scale.y, anchorpoint and rotation, -// this fuction return the 5 values in 1 single call --(void) getTransformValues:(struct transformValues_*) tv -{ - tv->pos = positionInPixels_; - tv->scale.x = scaleX_; - tv->scale.y = scaleY_; - tv->rotation = rotation_; - tv->skew.x = skewX_; - tv->skew.y = skewY_; - tv->ap = anchorPointInPixels_; - tv->visible = visible_; + + // recursively iterate over children + if( hasChildren_ ) + [children_ makeObjectsPerformSelector:@selector(updateTransform)]; + +#if CC_SPRITE_DEBUG_DRAW + // draw bounding box + CGPoint vertices[4] = { + ccp( quad_.bl.vertices.x, quad_.bl.vertices.y ), + ccp( quad_.br.vertices.x, quad_.br.vertices.y ), + ccp( quad_.tr.vertices.x, quad_.tr.vertices.y ), + ccp( quad_.tl.vertices.x, quad_.tl.vertices.y ), + }; + ccDrawPoly(vertices, 4, YES); +#endif // CC_SPRITE_DEBUG_DRAW + } #pragma mark CCSprite - draw -(void) draw { - [super draw]; + CC_PROFILER_START_CATEGORY(kCCProfilerCategorySprite, @"CCSprite - draw"); - NSAssert(!usesBatchNode_, @"If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called"); + NSAssert(!batchNode_, @"If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called"); - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Unneeded states: - + CC_NODE_DRAW_SETUP(); - BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; - if( newBlend ) - glBlendFunc( blendFunc_.src, blendFunc_.dst ); + ccGLBlendFunc( blendFunc_.src, blendFunc_.dst ); + + ccGLBindTexture2D( [texture_ name] ); + + // + // Attributes + // + + ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex ); #define kQuadSize sizeof(quad_.bl) - glBindTexture(GL_TEXTURE_2D, [texture_ name]); - long offset = (long)&quad_; - + // vertex NSInteger diff = offsetof( ccV3F_C4B_T2F, vertices); - glVertexPointer(3, GL_FLOAT, kQuadSize, (void*) (offset + diff) ); - + glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff)); + + // texCoods + diff = offsetof( ccV3F_C4B_T2F, texCoords); + glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff)); + // color diff = offsetof( ccV3F_C4B_T2F, colors); - glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff)); - - // tex coords - diff = offsetof( ccV3F_C4B_T2F, texCoords); - glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff)); - + glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff)); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - if( newBlend ) - glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); - + + CHECK_GL_ERROR_DEBUG(); + + #if CC_SPRITE_DEBUG_DRAW == 1 // draw bounding box - CGSize s = self.contentSize; - CGPoint vertices[4] = { - ccp(0,0), ccp(s.width,0), - ccp(s.width,s.height), ccp(0,s.height) + CGPoint vertices[4]={ + ccp(quad_.tl.vertices.x,quad_.tl.vertices.y), + ccp(quad_.bl.vertices.x,quad_.bl.vertices.y), + ccp(quad_.br.vertices.x,quad_.br.vertices.y), + ccp(quad_.tr.vertices.x,quad_.tr.vertices.y), }; ccDrawPoly(vertices, 4, YES); #elif CC_SPRITE_DEBUG_DRAW == 2 // draw texture box CGSize s = self.textureRect.size; - CGPoint offsetPix = self.offsetPositionInPixels; + CGPoint offsetPix = self.offsetPosition; CGPoint vertices[4] = { ccp(offsetPix.x,offsetPix.y), ccp(offsetPix.x+s.width,offsetPix.y), ccp(offsetPix.x+s.width,offsetPix.y+s.height), ccp(offsetPix.x,offsetPix.y+s.height) }; ccDrawPoly(vertices, 4, YES); #endif // CC_SPRITE_DEBUG_DRAW - + + CC_INCREMENT_GL_DRAWS(1); + + CC_PROFILER_STOP_CATEGORY(kCCProfilerCategorySprite, @"CCSprite - draw"); } #pragma mark CCSprite - CCNode overrides @@ -631,17 +562,21 @@ -(void) draw -(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag { NSAssert( child != nil, @"Argument must be non-nil"); - - [super addChild:child z:z tag:aTag]; - - if( usesBatchNode_ ) { + + if( batchNode_ ) { NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSprite only supports CCSprites as children when using CCSpriteBatchNode"); NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id"); - - NSUInteger index = [batchNode_ atlasIndexForChild:child atZ:z]; - [batchNode_ insertChild:child inAtlasAtIndex:index]; + + //put it in descendants array of batch node + [batchNode_ appendChild:child]; + + if (!isReorderChildDirty_) + [self setReorderChildDirtyRecursively]; } - + + //CCNode already sets isReorderChildDirty_ so this needs to be after batchNode check + [super addChild:child z:z tag:aTag]; + hasChildren_ = YES; } @@ -653,47 +588,89 @@ -(void) reorderChild:(CCSprite*)child z:(NSInteger)z if( z == child.zOrder ) return; - if( usesBatchNode_ ) { - // XXX: Instead of removing/adding, it is more efficient to reorder manually - [child retain]; - [self removeChild:child cleanup:NO]; - [self addChild:child z:z]; - [child release]; + if( batchNode_ && ! isReorderChildDirty_) + { + [self setReorderChildDirtyRecursively]; + [batchNode_ reorderBatch:YES]; } - else - [super reorderChild:child z:z]; + [super reorderChild:child z:z]; } -(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup { - if( usesBatchNode_ ) + if( batchNode_ ) [batchNode_ removeSpriteFromAtlas:sprite]; [super removeChild:sprite cleanup:doCleanup]; - + hasChildren_ = ( [children_ count] > 0 ); } -(void)removeAllChildrenWithCleanup:(BOOL)doCleanup { - if( usesBatchNode_ ) { + if( batchNode_ ) { CCSprite *child; CCARRAY_FOREACH(children_, child) [batchNode_ removeSpriteFromAtlas:child]; } - + [super removeAllChildrenWithCleanup:doCleanup]; - + hasChildren_ = NO; } +- (void) sortAllChildren +{ + if (isReorderChildDirty_) + { + NSInteger i,j,length = children_->data->num; + CCNode** x = children_->data->arr; + CCNode *tempItem; + + // insertion sort + for(i=1; i=0 && ( tempItem.zOrder < x[j].zOrder || ( tempItem.zOrder == x[j].zOrder && tempItem.orderOfArrival < x[j].orderOfArrival ) ) ) + { + x[j+1] = x[j]; + j = j-1; + } + x[j+1] = tempItem; + } + + if ( batchNode_) + [children_ makeObjectsPerformSelector:@selector(sortAllChildren)]; + + isReorderChildDirty_=NO; + } +} + // // CCNode property overloads // used only when parent is CCSpriteBatchNode // #pragma mark CCSprite - property overloads +-(void) setReorderChildDirtyRecursively +{ + //only set parents flag the first time + + if ( ! isReorderChildDirty_ ) + { + isReorderChildDirty_ = YES; + CCNode* node = (CCNode*) parent_; + while (node && node != batchNode_) + { + [(CCSprite*)node setReorderChildDirtyRecursively]; + node=node.parent; + } + } +} -(void) setDirtyRecursively:(BOOL)b { @@ -708,7 +685,7 @@ -(void) setDirtyRecursively:(BOOL)b // XXX HACK: optimization #define SET_DIRTY_RECURSIVELY() { \ - if( usesBatchNode_ && ! recursiveDirty_ ) { \ + if( batchNode_ && ! recursiveDirty_ ) { \ dirty_ = recursiveDirty_ = YES; \ if( hasChildren_) \ [self setDirtyRecursively:YES]; \ @@ -721,12 +698,6 @@ -(void)setPosition:(CGPoint)pos SET_DIRTY_RECURSIVELY(); } --(void)setPositionInPixels:(CGPoint)pos -{ - [super setPositionInPixels:pos]; - SET_DIRTY_RECURSIVELY(); -} - -(void)setRotation:(float)rot { [super setRotation:rot]; @@ -775,10 +746,10 @@ -(void)setAnchorPoint:(CGPoint)anchor SET_DIRTY_RECURSIVELY(); } --(void)setIsRelativeAnchorPoint:(BOOL)relative +-(void) setIgnoreAnchorPointForPosition:(BOOL)value { - NSAssert( ! usesBatchNode_, @"relativeTransformAnchor is invalid in CCSprite"); - [super setIsRelativeAnchorPoint:relative]; + NSAssert( ! batchNode_, @"ignoreAnchorPointForPosition is invalid in CCSprite"); + [super setIgnoreAnchorPointForPosition:value]; } -(void)setVisible:(BOOL)v @@ -791,7 +762,7 @@ -(void)setFlipX:(BOOL)b { if( flipX_ != b ) { flipX_ = b; - [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_]; + [self setTextureRect:rect_ rotated:rectRotated_ untrimmedSize:contentSize_]; } } -(BOOL) flipX @@ -802,9 +773,9 @@ -(BOOL) flipX -(void) setFlipY:(BOOL)b { if( flipY_ != b ) { - flipY_ = b; - [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_]; - } + flipY_ = b; + [self setTextureRect:rect_ rotated:rectRotated_ untrimmedSize:contentSize_]; + } } -(BOOL) flipY { @@ -817,15 +788,15 @@ -(BOOL) flipY #pragma mark CCSprite - RGBA protocol -(void) updateColor { - ccColor4B color4 = {color_.r, color_.g, color_.b, opacity_ }; - + ccColor4B color4 = {color_.r, color_.g, color_.b, opacity_}; + quad_.bl.colors = color4; quad_.br.colors = color4; quad_.tl.colors = color4; quad_.tr.colors = color4; - - // renders using Sprite Manager - if( usesBatchNode_ ) { + + // renders using batch node + if( batchNode_ ) { if( atlasIndex_ != CCSpriteIndexNotInitialized) [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; else @@ -849,7 +820,7 @@ -(void) setOpacity:(GLubyte) anOpacity // special opacity for premultiplied textures if( opacityModifyRGB_ ) [self setColor: colorUnmodified_]; - + [self updateColor]; } @@ -857,20 +828,20 @@ - (ccColor3B) color { if(opacityModifyRGB_) return colorUnmodified_; - + return color_; } -(void) setColor:(ccColor3B)color3 { color_ = colorUnmodified_ = color3; - + if( opacityModifyRGB_ ){ - color_.r = color3.r * opacity_/255; - color_.g = color3.g * opacity_/255; - color_.b = color3.b * opacity_/255; + color_.r = color3.r * opacity_/255.0f; + color_.g = color3.g * opacity_/255.0f; + color_.b = color3.b * opacity_/255.0f; } - + [self updateColor]; } @@ -893,57 +864,59 @@ -(BOOL) doesOpacityModifyRGB -(void) setDisplayFrame:(CCSpriteFrame*)frame { - unflippedOffsetPositionFromCenter_ = frame.offsetInPixels; + unflippedOffsetPositionFromCenter_ = frame.offset; CCTexture2D *newTexture = [frame texture]; // update texture before updating texture rect if ( newTexture.name != texture_.name ) [self setTexture: newTexture]; - + // update rect rectRotated_ = frame.rotated; - [self setTextureRectInPixels:frame.rectInPixels rotated:frame.rotated untrimmedSize:frame.originalSizeInPixels]; + + [self setTextureRect:frame.rect rotated:rectRotated_ untrimmedSize:frame.originalSize]; } -(void) setDisplayFrameWithAnimationName: (NSString*) animationName index:(int) frameIndex { NSAssert( animationName, @"CCSprite#setDisplayFrameWithAnimationName. animationName must not be nil"); - + CCAnimation *a = [[CCAnimationCache sharedAnimationCache] animationByName:animationName]; - + NSAssert( a, @"CCSprite#setDisplayFrameWithAnimationName: Frame not found"); - - CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex]; - + + CCAnimationFrame *frame = [[a frames] objectAtIndex:frameIndex]; + NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame"); - - [self setDisplayFrame:frame]; + + [self setDisplayFrame:frame.spriteFrame]; } --(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame +-(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame { CGRect r = [frame rect]; return ( CGRectEqualToRect(r, rect_) && - frame.texture.name == self.texture.name ); + frame.texture.name == self.texture.name && + CGPointEqualToPoint( frame.offset, unflippedOffsetPositionFromCenter_ ) ); } --(CCSpriteFrame*) displayedFrame -{ +-(CCSpriteFrame*) displayFrame +{ return [CCSpriteFrame frameWithTexture:texture_ - rectInPixels:rectInPixels_ + rectInPixels:CC_RECT_POINTS_TO_PIXELS(rect_) rotated:rectRotated_ offset:unflippedOffsetPositionFromCenter_ - originalSize:contentSizeInPixels_]; + originalSize:CC_SIZE_POINTS_TO_PIXELS(contentSize_)]; } #pragma mark CCSprite - CocosNodeTexture protocol -(void) updateBlendFunc { - NSAssert( ! usesBatchNode_, @"CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a CCSpriteBatchNode"); + NSAssert( ! batchNode_, @"CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a CCSpriteBatchNode"); - // it's possible to have an untextured sprite + // it is possible to have an untextured sprite if( !texture_ || ! [texture_ hasPremultipliedAlpha] ) { blendFunc_.src = GL_SRC_ALPHA; blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; @@ -957,15 +930,18 @@ -(void) updateBlendFunc -(void) setTexture:(CCTexture2D*)texture { - NSAssert( ! usesBatchNode_, @"CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode"); - + // If batchnode, then texture id should be the same + NSAssert( !batchNode_ || texture.name == batchNode_.texture.name , @"CCSprite: Batched sprites should use the same texture as the batchnode"); + // accept texture==nil as argument NSAssert( !texture || [texture isKindOfClass:[CCTexture2D class]], @"setTexture expects a CCTexture2D. Invalid argument"); - [texture_ release]; - texture_ = [texture retain]; - - [self updateBlendFunc]; + if( ! batchNode_ && texture_ != texture ) { + [texture_ release]; + texture_ = [texture retain]; + + [self updateBlendFunc]; + } } -(CCTexture2D*) texture diff --git a/cocos2d/cocos2d/CCSpriteBatchNode.h b/cocos2d/cocos2d/CCSpriteBatchNode.h old mode 100644 new mode 100755 index 2e6ae97..ca13ef0 --- a/cocos2d/cocos2d/CCSpriteBatchNode.h +++ b/cocos2d/cocos2d/CCSpriteBatchNode.h @@ -5,17 +5,17 @@ * * Copyright (c) 2009-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -48,7 +48,7 @@ * Limitations: * - The only object that is accepted as child (or grandchild, grand-grandchild, etc...) is CCSprite or any subclass of CCSprite. eg: particles, labels and layer can't be added to a CCSpriteBatchNode. * - Either all its children are Aliased or Antialiased. It can't be a mix. This is because "alias" is a property of the texture, and all the sprites share the same texture. - * + * * @since v0.7.1 */ @interface CCSpriteBatchNode : CCNode @@ -114,9 +114,25 @@ -(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup; -(void) insertChild:(CCSprite*)child inAtlasAtIndex:(NSUInteger)index; +-(void) appendChild:(CCSprite*)sprite; -(void) removeSpriteFromAtlas:(CCSprite*)sprite; -(NSUInteger) rebuildIndexInOrder:(CCSprite*)parent atlasIndex:(NSUInteger)index; -(NSUInteger) atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z; +/* Sprites use this to start sortChildren, don't call this manually */ +- (void) reorderBatch:(BOOL) reorder; @end + +@interface CCSpriteBatchNode (QuadExtensions) +/** Adds a quad into the texture atlas but it won't be added into the children array. + This method should be called only when you are dealing with very big AtlasSrite and when most of the CCSprite won't be updated. + For example: a tile map (CCTMXMap) or a label with lots of characgers (CCLabelBMFont) + */ +-(id) addSpriteWithoutQuad:(CCSprite*)child z:(NSUInteger)z tag:(NSInteger)aTag; + +/* This is the opposite of "addQuadFromSprite". + It adds the sprite to the children and descendants array, but it doesn't add it to the texture atlas. + */ +-(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index; +@end diff --git a/cocos2d/cocos2d/CCSpriteBatchNode.m b/cocos2d/cocos2d/CCSpriteBatchNode.m old mode 100644 new mode 100755 index 921d892..9928e50 --- a/cocos2d/cocos2d/CCSpriteBatchNode.m +++ b/cocos2d/cocos2d/CCSpriteBatchNode.m @@ -5,17 +5,17 @@ * * Copyright (c) 2009-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -33,16 +33,25 @@ #import "CCGrid.h" #import "CCDrawingPrimitives.h" #import "CCTextureCache.h" +#import "CCShaderCache.h" +#import "CCGLProgram.h" +#import "ccGLStateCache.h" +#import "CCDirector.h" #import "Support/CGPointExtension.h" +#import "Support/TransformUtils.h" +#import "Support/CCProfiling.h" + +// external +#import "kazmath/GL/matrix.h" const NSUInteger defaultCapacity = 29; #pragma mark - #pragma mark CCSpriteBatchNode -static SEL selUpdate = NULL; - @interface CCSpriteBatchNode (private) +-(void) updateAtlasIndex:(CCSprite*) sprite currentIndex:(NSInteger*) curIndex; +-(void) swap:(NSInteger) oldIndex withNewIndex:(NSInteger) newIndex; -(void) updateBlendFunc; @end @@ -53,12 +62,6 @@ @implementation CCSpriteBatchNode @synthesize descendants = descendants_; -+(void) initialize -{ - if ( self == [CCSpriteBatchNode class] ) { - selUpdate = @selector(updateTransform); - } -} /* * creation with CCTexture2D */ @@ -85,83 +88,92 @@ +(id)batchNodeWithFile:(NSString*) imageFile return [[[self alloc] initWithFile:imageFile capacity:defaultCapacity] autorelease]; } -/* - * init with CCTexture2D - */ +-(id)init +{ + return [self initWithTexture:[[[CCTexture2D alloc] init] autorelease] capacity:0]; +} + +-(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity +{ + CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:fileImage]; + return [self initWithTexture:tex capacity:capacity]; +} + +// Designated initializer -(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity { if( (self=[super init])) { - + blendFunc_.src = CC_BLEND_SRC; blendFunc_.dst = CC_BLEND_DST; textureAtlas_ = [[CCTextureAtlas alloc] initWithTexture:tex capacity:capacity]; - + [self updateBlendFunc]; - + // no lazy alloc in this node children_ = [[CCArray alloc] initWithCapacity:capacity]; descendants_ = [[CCArray alloc] initWithCapacity:capacity]; + + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTextureColor]; } - + return self; } -/* - * init with FileImage - */ --(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity -{ - CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:fileImage]; - return [self initWithTexture:tex capacity:capacity]; -} - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_ ]; + return [NSString stringWithFormat:@"<%@ = %p | Tag = %ld>", [self class], self, (long)tag_ ]; } -(void)dealloc -{ +{ [textureAtlas_ release]; [descendants_ release]; - + [super dealloc]; } #pragma mark CCSpriteBatchNode - composition // override visit. -// Don't call visit on it's children +// Don't call visit on its children -(void) visit { - + CC_PROFILER_START_CATEGORY(kCCProfilerCategoryBatchSprite, @"CCSpriteBatchNode - visit"); + + NSAssert(parent_ != nil, @"CCSpriteBatchNode should NOT be root node"); + // CAREFUL: - // This visit is almost identical to CocosNode#visit - // with the exception that it doesn't call visit on it's children + // This visit is almost identical to CCNode#visit + // with the exception that it doesn't call visit on its children // // The alternative is to have a void CCSprite#visit, but // although this is less mantainable, is faster // if (!visible_) return; - - glPushMatrix(); - + + kmGLPushMatrix(); + if ( grid_ && grid_.active) { [grid_ beforeDraw]; [self transformAncestors]; } - + + [self sortAllChildren]; [self transform]; - [self draw]; - + if ( grid_ && grid_.active) [grid_ afterDraw:self]; - - glPopMatrix(); -} + kmGLPopMatrix(); + + orderOfArrival_ = 0; + + CC_PROFILER_STOP_CATEGORY(kCCProfilerCategoryBatchSprite, @"CCSpriteBatchNode - visit"); +} // override addChild: -(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag @@ -169,11 +181,10 @@ -(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag NSAssert( child != nil, @"Argument must be non-nil"); NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children"); NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id"); - + [super addChild:child z:z tag:aTag]; - - NSUInteger index = [self atlasIndexForChild:child atZ:z]; - [self insertChild:child inAtlasAtIndex:index]; + + [self appendChild:child]; } // override reorderChild @@ -181,15 +192,12 @@ -(void) reorderChild:(CCSprite*)child z:(NSInteger)z { NSAssert( child != nil, @"Child must be non-nil"); NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" ); - + if( z == child.zOrder ) return; - - // XXX: Instead of removing/adding, it is more efficient to reorder manually - [child retain]; - [self removeChild:child cleanup:NO]; - [self addChild:child z:z]; - [child release]; + + //set the z-order and sort later + [super reorderChild:child z:z]; } // override removeChild: @@ -198,12 +206,12 @@ -(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup // explicit nil handling if (sprite == nil) return; - + NSAssert([children_ containsObject:sprite], @"CCSpriteBatchNode doesn't contain the sprite. Can't remove it"); - + // cleanup before removing [self removeSpriteFromAtlas:sprite]; - + [super removeChild:sprite cleanup:doCleanup]; } @@ -215,82 +223,181 @@ -(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL)doCleanup -(void)removeAllChildrenWithCleanup:(BOOL)doCleanup { // Invalidate atlas index. issue #569 - [children_ makeObjectsPerformSelector:@selector(useSelfRender)]; - + // useSelfRender should be performed on all descendants. issue #1216 + [descendants_ makeObjectsPerformSelector:@selector(setBatchNode:) withObject:nil]; + [super removeAllChildrenWithCleanup:doCleanup]; - + [descendants_ removeAllObjects]; [textureAtlas_ removeAllQuads]; } +//override sortAllChildren +- (void) sortAllChildren +{ + if (isReorderChildDirty_) + { + NSInteger i,j,length = children_->data->num; + CCNode ** x = children_->data->arr; + CCNode *tempItem; + CCSprite *child; + + //insertion sort + for(i=1; i=0 && ( tempItem.zOrder < x[j].zOrder || ( tempItem.zOrder == x[j].zOrder && tempItem.orderOfArrival < x[j].orderOfArrival ) ) ) + { + x[j+1] = x[j]; + j--; + } + + x[j+1] = tempItem; + } + + //sorted now check all children + if ([children_ count] > 0) + { + //first sort all children recursively based on zOrder + [children_ makeObjectsPerformSelector:@selector(sortAllChildren)]; + + NSInteger index=0; + + //fast dispatch, give every child a new atlasIndex based on their relative zOrder (keep parent -> child relations intact) + // and at the same time reorder descedants and the quads to the right index + CCARRAY_FOREACH(children_, child) + [self updateAtlasIndex:child currentIndex:&index]; + } + + isReorderChildDirty_=NO; + } +} + +-(void) updateAtlasIndex:(CCSprite*) sprite currentIndex:(NSInteger*) curIndex +{ + CCArray *array = [sprite children]; + NSUInteger count = [array count]; + NSInteger oldIndex; + + if( count == 0 ) + { + oldIndex = sprite.atlasIndex; + sprite.atlasIndex = *curIndex; + sprite.orderOfArrival = 0; + if (oldIndex != *curIndex) + [self swap:oldIndex withNewIndex:*curIndex]; + (*curIndex)++; + } + else + { + BOOL needNewIndex=YES; + + if (((CCSprite*) (array->data->arr[0])).zOrder >= 0) + { + //all children are in front of the parent + oldIndex = sprite.atlasIndex; + sprite.atlasIndex = *curIndex; + sprite.orderOfArrival = 0; + if (oldIndex != *curIndex) + [self swap:oldIndex withNewIndex:*curIndex]; + (*curIndex)++; + + needNewIndex = NO; + } + + CCSprite* child; + CCARRAY_FOREACH(array,child) + { + if (needNewIndex && child.zOrder >= 0) + { + oldIndex = sprite.atlasIndex; + sprite.atlasIndex = *curIndex; + sprite.orderOfArrival = 0; + if (oldIndex != *curIndex) + [self swap:oldIndex withNewIndex:*curIndex]; + (*curIndex)++; + needNewIndex = NO; + + } + + [self updateAtlasIndex:child currentIndex:curIndex]; + } + + if (needNewIndex) + {//all children have a zOrder < 0) + oldIndex=sprite.atlasIndex; + sprite.atlasIndex=*curIndex; + sprite.orderOfArrival=0; + if (oldIndex!=*curIndex) + [self swap:oldIndex withNewIndex:*curIndex]; + (*curIndex)++; + } + } +} + +- (void) swap:(NSInteger) oldIndex withNewIndex:(NSInteger) newIndex +{ + id* x = descendants_->data->arr; + ccV3F_C4B_T2F_Quad* quads = textureAtlas_.quads; + + id tempItem = x[oldIndex]; + ccV3F_C4B_T2F_Quad tempItemQuad=quads[oldIndex]; + + //update the index of other swapped item + ((CCSprite*) x[newIndex]).atlasIndex=oldIndex; + + x[oldIndex]=x[newIndex]; + quads[oldIndex]=quads[newIndex]; + x[newIndex]=tempItem; + quads[newIndex]=tempItemQuad; +} + +- (void) reorderBatch:(BOOL) reorder +{ + isReorderChildDirty_=reorder; +} + #pragma mark CCSpriteBatchNode - draw -(void) draw { - [super draw]; + CC_PROFILER_START(@"CCSpriteBatchNode - draw"); - // Optimization: Fast Dispatch + // Optimization: Fast Dispatch if( textureAtlas_.totalQuads == 0 ) - return; - - CCSprite *child; - ccArray *array = descendants_->data; - - NSUInteger i = array->num; - id *arr = array->arr; - - if( i > 0 ) { - - while (i-- > 0) { - child = *arr++; - - // fast dispatch - child->updateMethod(child, selUpdate); - -#if CC_SPRITEBATCHNODE_DEBUG_DRAW - //Issue #528 - CGRect rect = [child boundingBox]; - CGPoint vertices[4]={ - ccp(rect.origin.x,rect.origin.y), - ccp(rect.origin.x+rect.size.width,rect.origin.y), - ccp(rect.origin.x+rect.size.width,rect.origin.y+rect.size.height), - ccp(rect.origin.x,rect.origin.y+rect.size.height), - }; - ccDrawPoly(vertices, 4, YES); -#endif // CC_SPRITEBATCHNODE_DEBUG_DRAW - } - } - - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Unneeded states: - - - BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; - if( newBlend ) - glBlendFunc( blendFunc_.src, blendFunc_.dst ); - + return; + + CC_NODE_DRAW_SETUP(); + + [children_ makeObjectsPerformSelector:@selector(updateTransform)]; + + ccGLBlendFunc( blendFunc_.src, blendFunc_.dst ); + [textureAtlas_ drawQuads]; - if( newBlend ) - glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); + + CC_PROFILER_STOP(@"CCSpriteBatchNode - draw"); } #pragma mark CCSpriteBatchNode - private -(void) increaseAtlasCapacity { - // if we're going beyond the current TextureAtlas's capacity, + // if we're going beyond the current CCTextureAtlas's capacity, // all the previously initialized sprites will need to redo their texture coords // this is likely computationally expensive NSUInteger quantity = (textureAtlas_.capacity + 1) * 4 / 3; - + CCLOG(@"cocos2d: CCSpriteBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu].", (long)textureAtlas_.capacity, (long)quantity); - - + + if( ! [textureAtlas_ resizeCapacity:quantity] ) { // serious problems - CCLOG(@"cocos2d: WARNING: Not enough memory to resize the atlas"); + CCLOGWARN(@"cocos2d: WARNING: Not enough memory to resize the atlas"); NSAssert(NO,@"XXX: CCSpriteBatchNode#increaseAtlasCapacity SHALL handle this assert"); - } + } } @@ -303,18 +410,18 @@ -(NSUInteger) rebuildIndexInOrder:(CCSprite*)node atlasIndex:(NSUInteger)index if( sprite.zOrder < 0 ) index = [self rebuildIndexInOrder:sprite atlasIndex:index]; } - + // ignore self (batch node) if( ! [node isEqual:self]) { node.atlasIndex = index; index++; } - + CCARRAY_FOREACH(node.children, sprite){ if( sprite.zOrder >= 0 ) index = [self rebuildIndexInOrder:sprite atlasIndex:index]; } - + return index; } @@ -343,13 +450,13 @@ -(NSUInteger)atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z { CCArray *brothers = [[sprite parent] children]; NSUInteger childIndex = [brothers indexOfObject:sprite]; - + // ignore parent Z if parent is batchnode BOOL ignoreParent = ( sprite.parent == self ); CCSprite *previous = nil; if( childIndex > 0 ) previous = [brothers objectAtIndex:childIndex-1]; - + // first child of the sprite sheet if( ignoreParent ) { if( childIndex == 0 ) @@ -357,30 +464,30 @@ -(NSUInteger)atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z // else return [self highestAtlasIndexInChild: previous] + 1; } - + // parent is a CCSprite, so, it must be taken into account - + // first child of an CCSprite ? if( childIndex == 0 ) { CCSprite *p = (CCSprite*) sprite.parent; - + // less than parent and brothers if( z < 0 ) return p.atlasIndex; else return p.atlasIndex+1; - + } else { // previous & sprite belong to the same branch if( ( previous.zOrder < 0 && z < 0 )|| (previous.zOrder >= 0 && z >= 0) ) return [self highestAtlasIndexInChild:previous] + 1; - + // else (previous < 0 and sprite >= 0 ) CCSprite *p = (CCSprite*) sprite.parent; return p.atlasIndex + 1; } - + NSAssert( NO, @"Should not happen. Error calculating Z on Batch Node"); return 0; } @@ -389,20 +496,20 @@ -(NSUInteger)atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z // add child helper -(void) insertChild:(CCSprite*)sprite inAtlasAtIndex:(NSUInteger)index { - [sprite useBatchNode:self]; + [sprite setBatchNode:self]; [sprite setAtlasIndex:index]; [sprite setDirty: YES]; - + if(textureAtlas_.totalQuads == textureAtlas_.capacity) [self increaseAtlasCapacity]; - + ccV3F_C4B_T2F_Quad quad = [sprite quad]; [textureAtlas_ insertQuad:&quad atIndex:index]; - + ccArray *descendantsData = descendants_->data; - + ccArrayInsertObjectAtIndex(descendantsData, sprite, index); - + // update indices NSUInteger i = index+1; CCSprite *child; @@ -410,7 +517,7 @@ -(void) insertChild:(CCSprite*)sprite inAtlasAtIndex:(NSUInteger)index child = descendantsData->arr[i]; child.atlasIndex = child.atlasIndex + 1; } - + // add children recursively CCARRAY_FOREACH(sprite.children, child){ NSUInteger idx = [self atlasIndexForChild:child atZ: child.zOrder]; @@ -418,30 +525,58 @@ -(void) insertChild:(CCSprite*)sprite inAtlasAtIndex:(NSUInteger)index } } +// addChild helper, faster than insertChild +-(void) appendChild:(CCSprite*)sprite +{ + isReorderChildDirty_=YES; + [sprite setBatchNode:self]; + [sprite setDirty: YES]; + + if(textureAtlas_.totalQuads == textureAtlas_.capacity) + [self increaseAtlasCapacity]; + + ccArray *descendantsData = descendants_->data; + + ccArrayAppendObjectWithResize(descendantsData, sprite); + + NSUInteger index=descendantsData->num-1; + + sprite.atlasIndex=index; + + ccV3F_C4B_T2F_Quad quad = [sprite quad]; + [textureAtlas_ insertQuad:&quad atIndex:index]; + + // add children recursively + CCSprite* child; + CCARRAY_FOREACH(sprite.children, child) + [self appendChild:child]; +} + + // remove child helper -(void) removeSpriteFromAtlas:(CCSprite*)sprite { // remove from TextureAtlas [textureAtlas_ removeQuadAtIndex:sprite.atlasIndex]; - + // Cleanup sprite. It might be reused (issue #569) - [sprite useSelfRender]; - + [sprite setBatchNode:nil]; + ccArray *descendantsData = descendants_->data; NSUInteger index = ccArrayGetIndexOfObject(descendantsData, sprite); if( index != NSNotFound ) { ccArrayRemoveObjectAtIndex(descendantsData, index); - + // update all sprites beyond this one NSUInteger count = descendantsData->num; - + for(; index < count; index++) { CCSprite *s = descendantsData->arr[index]; s.atlasIndex = s.atlasIndex - 1; } } - + // remove children recursively CCSprite *child; CCARRAY_FOREACH(sprite.children, child) @@ -469,3 +604,61 @@ -(CCTexture2D*) texture return textureAtlas_.texture; } @end + +#pragma mark - CCSpriteBatchNode Extension + + +@implementation CCSpriteBatchNode (QuadExtension) + +-(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index +{ + NSAssert( sprite != nil, @"Argument must be non-nil"); + NSAssert( [sprite isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children"); + + + while(index >= textureAtlas_.capacity || textureAtlas_.capacity == textureAtlas_.totalQuads ) + [self increaseAtlasCapacity]; + + // + // update the quad directly. Don't add the sprite to the scene graph + // + + [sprite setBatchNode:self]; + [sprite setAtlasIndex:index]; + + ccV3F_C4B_T2F_Quad quad = [sprite quad]; + [textureAtlas_ insertQuad:&quad atIndex:index]; + + // XXX: updateTransform will update the textureAtlas too using updateQuad. + // XXX: so, it should be AFTER the insertQuad + [sprite setDirty:YES]; + [sprite updateTransform]; +} + +-(id) addSpriteWithoutQuad:(CCSprite*)child z:(NSUInteger)z tag:(NSInteger)aTag +{ + NSAssert( child != nil, @"Argument must be non-nil"); + NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children"); + + // quad index is Z + [child setAtlasIndex:z]; + + // XXX: optimize with a binary search + int i=0; + for( CCSprite *c in descendants_ ) { + if( c.atlasIndex >= z ) + break; + i++; + } + [descendants_ insertObject:child atIndex:i]; + + + // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array + [super addChild:child z:z tag:aTag]; + + //#issue 1262 don't use lazy sorting, tiles are added as quads not as sprites, so sprites need to be added in order + [self reorderBatch:NO]; + return self; +} +@end + diff --git a/cocos2d/cocos2d/CCSpriteFrame.h b/cocos2d/cocos2d/CCSpriteFrame.h old mode 100644 new mode 100755 index 983aeed..fcc8084 --- a/cocos2d/cocos2d/CCSpriteFrame.h +++ b/cocos2d/cocos2d/CCSpriteFrame.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2011 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -34,7 +34,7 @@ You can modify the frame of a CCSprite by doing: - + CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:texture rect:rect offset:offset]; [sprite setDisplayFrame:frame]; */ @@ -43,9 +43,12 @@ CGRect rect_; CGRect rectInPixels_; BOOL rotated_; + CGPoint offset_; CGPoint offsetInPixels_; + CGSize originalSize_; CGSize originalSizeInPixels_; CCTexture2D *texture_; + NSString *textureFilename_; } /** rect of the frame in points. If it is updated, then rectInPixels will be updated too. */ @property (nonatomic,readwrite) CGRect rect; @@ -56,35 +59,68 @@ /** whether or not the rect of the frame is rotated ( x = x+width, y = y+height, width = height, height = width ) */ @property (nonatomic,readwrite) BOOL rotated; +/** offset of the frame in points */ +@property (nonatomic,readwrite) CGPoint offset; + /** offset of the frame in pixels */ @property (nonatomic,readwrite) CGPoint offsetInPixels; +/** original size of the trimmed image in points */ +@property (nonatomic,readwrite) CGSize originalSize; + /** original size of the trimmed image in pixels */ @property (nonatomic,readwrite) CGSize originalSizeInPixels; /** texture of the frame */ @property (nonatomic, retain, readwrite) CCTexture2D *texture; +/** texture file name of the frame */ +@property (nonatomic, retain, readonly) NSString *textureFilename; + /** Create a CCSpriteFrame with a texture, rect in points. It is assumed that the frame was not trimmed. */ +(id) frameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect; +/** Create a CCSpriteFrame with a texture filename, rect in points. + It is assumed that the frame was not trimmed. + */ ++(id) frameWithTextureFilename:(NSString*)filename rect:(CGRect)rect; + /** Create a CCSpriteFrame with a texture, rect, rotated, offset and originalSize in pixels. - The originalSize is the size in points of the frame before being trimmed. + The originalSize is the size in pixels of the frame before being trimmed. */ +(id) frameWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize; +/** Create a CCSpriteFrame with a texture filename, rect, rotated, offset and originalSize in pixels. + The originalSize is the size in pixels of the frame before being trimmed. + */ ++(id) frameWithTextureFilename:(NSString*)filename rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize; + + /** Initializes a CCSpriteFrame with a texture, rect in points; It is assumed that the frame was not trimmed. */ -(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect; +/** Initializes a CCSpriteFrame with a texture filename, rect in points; + It is assumed that the frame was not trimmed. + */ +-(id) initWithTextureFilename:(NSString*)filename rect:(CGRect)rect; + + /** Initializes a CCSpriteFrame with a texture, rect, rotated, offset and originalSize in pixels. - The originalSize is the size in points of the frame before being trimmed. + The originalSize is the size in pixels of the frame before being trimmed. */ -(id) initWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize; +/** Initializes a CCSpriteFrame with a texture, rect, rotated, offset and originalSize in pixels. + The originalSize is the size in pixels of the frame before being trimmed. + + @since v1.1 + */ +-(id) initWithTextureFilename:(NSString*)filename rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize; + @end diff --git a/cocos2d/cocos2d/CCSpriteFrame.m b/cocos2d/cocos2d/CCSpriteFrame.m old mode 100644 new mode 100755 index e9ebd04..92b4baa --- a/cocos2d/cocos2d/CCSpriteFrame.m +++ b/cocos2d/cocos2d/CCSpriteFrame.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2011 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,47 +30,87 @@ #import "ccMacros.h" @implementation CCSpriteFrame -@synthesize rotated = rotated_, offsetInPixels = offsetInPixels_, texture = texture_; -@synthesize originalSizeInPixels=originalSizeInPixels_; +@synthesize offsetInPixels = offsetInPixels_, offset = offset_; +@synthesize originalSize = originalSize_, originalSizeInPixels = originalSizeInPixels_; +@synthesize textureFilename = textureFilename_; +@synthesize rotated = rotated_; +(id) frameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect { return [[[self alloc] initWithTexture:texture rect:rect] autorelease]; } ++(id) frameWithTextureFilename:(NSString*)filename rect:(CGRect)rect +{ + return [[[self alloc] initWithTextureFilename:filename rect:rect] autorelease]; +} + +(id) frameWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize { return [[[self alloc] initWithTexture:texture rectInPixels:rect rotated:rotated offset:offset originalSize:originalSize] autorelease]; } ++(id) frameWithTextureFilename:(NSString*)filename rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize +{ + return [[[self alloc] initWithTextureFilename:filename rectInPixels:rect rotated:rotated offset:offset originalSize:originalSize] autorelease]; +} + -(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect { CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect ); return [self initWithTexture:texture rectInPixels:rectInPixels rotated:NO offset:CGPointZero originalSize:rectInPixels.size]; } +-(id) initWithTextureFilename:(NSString*)filename rect:(CGRect)rect +{ + CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect ); + return [self initWithTextureFilename:filename rectInPixels:rectInPixels rotated:NO offset:CGPointZero originalSize:rectInPixels.size]; +} + -(id) initWithTexture:(CCTexture2D*)texture rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize { - if( (self=[super init]) ) { + if( (self=[super init]) ) + { self.texture = texture; rectInPixels_ = rect; rect_ = CC_RECT_PIXELS_TO_POINTS( rect ); - rotated_ = rotated; offsetInPixels_ = offset; + offset_ = CC_POINT_PIXELS_TO_POINTS( offsetInPixels_ ); + originalSizeInPixels_ = originalSize; + originalSize_ = CC_SIZE_PIXELS_TO_POINTS( originalSizeInPixels_ ); + rotated_ = rotated; + } + return self; +} + +-(id) initWithTextureFilename:(NSString *)filename rectInPixels:(CGRect)rect rotated:(BOOL)rotated offset:(CGPoint)offset originalSize:(CGSize)originalSize +{ + if( (self=[super init]) ) + { + texture_ = nil; + textureFilename_ = [filename copy]; + rectInPixels_ = rect; + rect_ = CC_RECT_PIXELS_TO_POINTS( rect ); + offsetInPixels_ = offset; + offset_ = CC_POINT_PIXELS_TO_POINTS( offsetInPixels_ ); originalSizeInPixels_ = originalSize; + originalSize_ = CC_SIZE_PIXELS_TO_POINTS( originalSizeInPixels_ ); + rotated_ = rotated; } - return self; + return self; } - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | TextureName=%d, Rect = (%.2f,%.2f,%.2f,%.2f)> rotated:%d", [self class], self, - texture_.name, + return [NSString stringWithFormat:@"<%@ = %p | Texture=%@, Rect = (%.2f,%.2f,%.2f,%.2f)> rotated:%d offset=(%.2f,%.2f)", [self class], self, + textureFilename_, rect_.origin.x, rect_.origin.y, rect_.size.width, rect_.size.height, - rotated_ + rotated_, + offsetInPixels_.x, + offsetInPixels_.y ]; } @@ -78,12 +118,14 @@ - (void) dealloc { CCLOGINFO( @"cocos2d: deallocing %@",self); [texture_ release]; + [textureFilename_ release]; [super dealloc]; } -(id) copyWithZone: (NSZone*) zone { - CCSpriteFrame *copy = [[[self class] allocWithZone: zone] initWithTexture:texture_ rectInPixels:rectInPixels_ rotated:rotated_ offset:offsetInPixels_ originalSize:originalSizeInPixels_]; + CCSpriteFrame *copy = [[[self class] allocWithZone: zone] initWithTextureFilename:textureFilename_ rectInPixels:rectInPixels_ rotated:rotated_ offset:offsetInPixels_ originalSize:originalSizeInPixels_]; + copy.texture = texture_; return copy; } @@ -106,6 +148,38 @@ -(void) setRect:(CGRect)rect -(void) setRectInPixels:(CGRect)rectInPixels { rectInPixels_ = rectInPixels; - rect_ = CC_RECT_PIXELS_TO_POINTS(rectInPixels); + rect_ = CC_RECT_PIXELS_TO_POINTS( rectInPixels_ ); +} + +-(void) setOffset:(CGPoint)offsets +{ + offset_ = offsets; + offsetInPixels_ = CC_POINT_POINTS_TO_PIXELS( offset_ ); +} + +-(void) setOffsetInPixels:(CGPoint)offsetInPixels +{ + offsetInPixels_ = offsetInPixels; + offset_ = CC_POINT_PIXELS_TO_POINTS( offsetInPixels_ ); +} + +-(void) setTexture:(CCTexture2D *)texture +{ + if( texture_ != texture ) { + [texture_ release]; + texture_ = [texture retain]; + } +} + +-(CCTexture2D*) texture +{ + if( texture_ ) + return texture_; + + if( textureFilename_ ) + return [[CCTextureCache sharedTextureCache] addImage:textureFilename_]; + + // no texture or texture filename + return nil; } @end diff --git a/cocos2d/cocos2d/CCSpriteFrameCache.h b/cocos2d/cocos2d/CCSpriteFrameCache.h old mode 100644 new mode 100755 index 67504d8..a6a2f09 --- a/cocos2d/cocos2d/CCSpriteFrameCache.h +++ b/cocos2d/cocos2d/CCSpriteFrameCache.h @@ -8,17 +8,17 @@ * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. * - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -38,9 +38,9 @@ #import #import "CCSpriteFrame.h" -#import "CCTexture2D.h" @class CCSprite; +@class CCTexture2D; /** Singleton that handles the loading of the sprite frames. It saves in a cache the sprite frames. @@ -50,6 +50,7 @@ { NSMutableDictionary *spriteFrames_; NSMutableDictionary *spriteFramesAliases_; + NSMutableSet *loadedFilenames_; } /** Retruns ths shared instance of the Sprite Frame cache */ @@ -60,24 +61,19 @@ +(void)purgeSharedSpriteFrameCache; -/** Adds multiple Sprite Frames with a dictionary. The texture will be associated with the created sprite frames. - */ --(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTexture2D*)texture; - /** Adds multiple Sprite Frames from a plist file. - * A texture will be loaded automatically. The texture name will composed by replacing the .plist suffix with .png + * A texture will be loaded automatically. The texture name will composed by replacing the .plist suffix with .png . * If you want to use another texture, you should use the addSpriteFramesWithFile:texture method. */ -(void) addSpriteFramesWithFile:(NSString*)plist; -/** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames. +/** Adds multiple Sprite Frames from a plist file. The texture filename will be associated with the created sprite frames. */ --(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture; +-(void) addSpriteFramesWithFile:(NSString*)plist textureFilename:(NSString*)filename; /** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames. - @since v0.99.5 */ --(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName; +-(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture; /** Adds an sprite frame with a given name. If the name already exists, then the contents of the old name will be replaced with the new one. @@ -110,11 +106,6 @@ */ - (void) removeSpriteFramesFromFile:(NSString*) plist; -/** Removes multiple Sprite Frames from NSDictionary. - * @since v0.99.5 - */ -- (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary; - /** Removes all Sprite Frames associated with the specified textures. * It is convinient to call this method when a specific texture needs to be removed. * @since v0.995. diff --git a/cocos2d/cocos2d/CCSpriteFrameCache.m b/cocos2d/cocos2d/CCSpriteFrameCache.m old mode 100644 new mode 100755 index ebed2cf..eec6ee0 --- a/cocos2d/cocos2d/CCSpriteFrameCache.m +++ b/cocos2d/cocos2d/CCSpriteFrameCache.m @@ -7,17 +7,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,8 +29,10 @@ */ /* - * To create sprite frames and texture atlas, use this tool: - * http://zwoptex.zwopple.com/ + * To create sprite frames and texture atlas, use any of these tools: + * http://zwoptexapp.com/ + * http://www.texturepacker.com/ + * */ #import "Platforms/CCNS.h" @@ -41,6 +43,12 @@ #import "CCSprite.h" #import "Support/CCFileUtils.h" +@interface CCSpriteFrameCache () +- (void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary textureFilename:(NSString*)filename; +- (void) addSpriteFramesWithDictionary:(NSDictionary *)dictionary texture:(CCTexture2D *)texture; +- (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary; +@end + @implementation CCSpriteFrameCache @@ -52,7 +60,7 @@ + (CCSpriteFrameCache *)sharedSpriteFrameCache { if (!sharedSpriteFrameCache_) sharedSpriteFrameCache_ = [[CCSpriteFrameCache alloc] init]; - + return sharedSpriteFrameCache_; } @@ -73,28 +81,31 @@ -(id) init if( (self=[super init]) ) { spriteFrames_ = [[NSMutableDictionary alloc] initWithCapacity: 100]; spriteFramesAliases_ = [[NSMutableDictionary alloc] initWithCapacity:10]; + loadedFilenames_ = [[NSMutableSet alloc] initWithCapacity:30]; } - + return self; } - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | num of sprite frames = %i>", [self class], self, [spriteFrames_ count]]; + return [NSString stringWithFormat:@"<%@ = %p | num of sprite frames = %lu>", [self class], self, (unsigned long)[spriteFrames_ count]]; } -(void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); - + [spriteFrames_ release]; [spriteFramesAliases_ release]; + [loadedFilenames_ release]; + [super dealloc]; } #pragma mark CCSpriteFrameCache - loading sprite frames --(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTexture2D*)texture +-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary textureReference:(id)textureReference { /* Supported Zwoptex Formats: @@ -107,19 +118,24 @@ -(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTextu NSDictionary *framesDict = [dictionary objectForKey:@"frames"]; int format = 0; - + // get the format if(metadataDict != nil) format = [[metadataDict objectForKey:@"format"] intValue]; - + // check the format - NSAssert( format >= 0 && format <= 3, @"cocos2d: WARNING: format is not supported for CCSpriteFrameCache addSpriteFramesWithDictionary:texture:"); - - + NSAssert( format >= 0 && format <= 3, @"format is not supported for CCSpriteFrameCache addSpriteFramesWithDictionary:textureFilename:"); + + // SpriteFrame info + CGRect rectInPixels; + BOOL isRotated; + CGPoint frameOffset; + CGSize originalSize; + // add real frames for(NSString *frameDictKey in framesDict) { NSDictionary *frameDict = [framesDict objectForKey:frameDictKey]; - CCSpriteFrame *spriteFrame; + CCSpriteFrame *spriteFrame=nil; if(format == 0) { float x = [[frameDict objectForKey:@"x"] floatValue]; float y = [[frameDict objectForKey:@"y"] floatValue]; @@ -131,35 +147,33 @@ -(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTextu int oh = [[frameDict objectForKey:@"originalHeight"] intValue]; // check ow/oh if(!ow || !oh) - CCLOG(@"cocos2d: WARNING: originalWidth/Height not found on the CCSpriteFrame. AnchorPoint won't work as expected. Regenerate the .plist"); - + CCLOGWARN(@"cocos2d: WARNING: originalWidth/Height not found on the CCSpriteFrame. AnchorPoint won't work as expected. Regenerate the .plist"); + // abs ow/oh ow = abs(ow); oh = abs(oh); - // create frame - - spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture - rectInPixels:CGRectMake(x, y, w, h) - rotated:NO - offset:CGPointMake(ox, oy) - originalSize:CGSizeMake(ow, oh)]; + + // set frame info + rectInPixels = CGRectMake(x, y, w, h); + isRotated = NO; + frameOffset = CGPointMake(ox, oy); + originalSize = CGSizeMake(ow, oh); } else if(format == 1 || format == 2) { CGRect frame = CCRectFromString([frameDict objectForKey:@"frame"]); BOOL rotated = NO; - + // rotation if(format == 2) rotated = [[frameDict objectForKey:@"rotated"] boolValue]; - + CGPoint offset = CCPointFromString([frameDict objectForKey:@"offset"]); CGSize sourceSize = CCSizeFromString([frameDict objectForKey:@"sourceSize"]); - - // create frame - spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture - rectInPixels:frame - rotated:rotated - offset:offset - originalSize:sourceSize]; + + // set frame info + rectInPixels = frame; + isRotated = rotated; + frameOffset = offset; + originalSize = sourceSize; } else if(format == 3) { // get values CGSize spriteSize = CCSizeFromString([frameDict objectForKey:@"spriteSize"]); @@ -167,22 +181,42 @@ -(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTextu CGSize spriteSourceSize = CCSizeFromString([frameDict objectForKey:@"spriteSourceSize"]); CGRect textureRect = CCRectFromString([frameDict objectForKey:@"textureRect"]); BOOL textureRotated = [[frameDict objectForKey:@"textureRotated"] boolValue]; - + // get aliases NSArray *aliases = [frameDict objectForKey:@"aliases"]; for(NSString *alias in aliases) { if( [spriteFramesAliases_ objectForKey:alias] ) - CCLOG(@"cocos2d: WARNING: an alias with name %@ already exists",alias); - + CCLOGWARN(@"cocos2d: WARNING: an alias with name %@ already exists",alias); + [spriteFramesAliases_ setObject:frameDictKey forKey:alias]; } - - // create frame - spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture - rectInPixels:CGRectMake(textureRect.origin.x, textureRect.origin.y, spriteSize.width, spriteSize.height) - rotated:textureRotated - offset:spriteOffset - originalSize:spriteSourceSize]; + + // set frame info + rectInPixels = CGRectMake(textureRect.origin.x, textureRect.origin.y, spriteSize.width, spriteSize.height); + isRotated = textureRotated; + frameOffset = spriteOffset; + originalSize = spriteSourceSize; + } + + NSString *textureFileName = nil; + CCTexture2D * texture = nil; + + if ( [textureReference isKindOfClass:[NSString class]] ) + { + textureFileName = textureReference; + } + else if ( [textureReference isKindOfClass:[CCTexture2D class]] ) + { + texture = textureReference; + } + + if ( textureFileName ) + { + spriteFrame = [[CCSpriteFrame alloc] initWithTextureFilename:textureFileName rectInPixels:rectInPixels rotated:isRotated offset:frameOffset originalSize:originalSize]; + } + else + { + spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture rectInPixels:rectInPixels rotated:isRotated offset:frameOffset originalSize:originalSize]; } // add sprite frame @@ -191,57 +225,81 @@ -(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTextu } } --(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture +-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary textureFilename:(NSString*)textureFilename { - NSString *path = [CCFileUtils fullPathFromRelativePath:plist]; - NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; + return [self addSpriteFramesWithDictionary:dictionary textureReference:textureFilename]; +} - [self addSpriteFramesWithDictionary:dict texture:texture]; +-(void) addSpriteFramesWithDictionary:(NSDictionary *)dictionary texture:(CCTexture2D *)texture +{ + return [self addSpriteFramesWithDictionary:dictionary textureReference:texture]; } --(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName +-(void) addSpriteFramesWithFile:(NSString*)plist textureReference:(id)textureReference { - NSAssert( textureFileName, @"Invalid texture file name"); - CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:textureFileName]; + NSAssert(textureReference, @"textureReference should not be nil"); + NSAssert(plist, @"plist filename should not be nil"); - if( texture ) - [self addSpriteFramesWithFile:plist texture:texture]; + if( ! [loadedFilenames_ member:plist] ) { + + NSString *path = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:plist]; + NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; + + [self addSpriteFramesWithDictionary:dict textureReference:textureReference]; + + [loadedFilenames_ addObject:plist]; + } else - CCLOG(@"cocos2d: CCSpriteFrameCache: couldn't load texture file. File not found: %@", textureFileName); + CCLOGINFO(@"cocos2d: CCSpriteFrameCache: file already loaded: %@", plist); } +-(void) addSpriteFramesWithFile:(NSString*)plist textureFilename:(NSString*)textureFilename +{ + return [self addSpriteFramesWithFile:plist textureReference:textureFilename]; +} + +-(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture +{ + return [self addSpriteFramesWithFile:plist textureReference:texture]; +} + + -(void) addSpriteFramesWithFile:(NSString*)plist { - NSString *path = [CCFileUtils fullPathFromRelativePath:plist]; - NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; - - NSString *texturePath = nil; - NSDictionary *metadataDict = [dict objectForKey:@"metadata"]; - if( metadataDict ) - // try to read texture file name from meta data - texturePath = [metadataDict objectForKey:@"textureFileName"]; + NSAssert(plist, @"plist filename should not be nil"); - - if( texturePath ) - { - // build texture path relative to plist file - NSString *textureBase = [plist stringByDeletingLastPathComponent]; - texturePath = [textureBase stringByAppendingPathComponent:texturePath]; - } else { - // build texture path by replacing file extension - texturePath = [plist stringByDeletingPathExtension]; - texturePath = [texturePath stringByAppendingPathExtension:@"png"]; + if( ! [loadedFilenames_ member:plist] ) { + + NSString *path = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:plist]; + NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; + + NSString *texturePath = nil; + NSDictionary *metadataDict = [dict objectForKey:@"metadata"]; + if( metadataDict ) + // try to read texture file name from meta data + texturePath = [metadataDict objectForKey:@"textureFileName"]; + + + if( texturePath ) + { + // build texture path relative to plist file + NSString *textureBase = [plist stringByDeletingLastPathComponent]; + texturePath = [textureBase stringByAppendingPathComponent:texturePath]; + } else { + // build texture path by replacing file extension + texturePath = [plist stringByDeletingPathExtension]; + texturePath = [texturePath stringByAppendingPathExtension:@"png"]; + + CCLOG(@"cocos2d: CCSpriteFrameCache: Trying to use file '%@' as texture", texturePath); + } + + [self addSpriteFramesWithDictionary:dict textureFilename:texturePath]; - CCLOG(@"cocos2d: CCSpriteFrameCache: Trying to use file '%@' as texture", texturePath); - } - - CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:texturePath]; - - if( texture ) - [self addSpriteFramesWithDictionary:dict texture:texture]; - - else - CCLOG(@"cocos2d: CCSpriteFrameCache: Couldn't load texture"); + [loadedFilenames_ addObject:plist]; + } + else + CCLOGINFO(@"cocos2d: CCSpriteFrameCache: file already loaded: %@", plist); + } -(void) addSpriteFrame:(CCSpriteFrame*)frame name:(NSString*)frameName @@ -255,18 +313,25 @@ -(void) removeSpriteFrames { [spriteFrames_ removeAllObjects]; [spriteFramesAliases_ removeAllObjects]; + [loadedFilenames_ removeAllObjects]; } -(void) removeUnusedSpriteFrames { + BOOL removed_ = NO; NSArray *keys = [spriteFrames_ allKeys]; for( id key in keys ) { - id value = [spriteFrames_ objectForKey:key]; + id value = [spriteFrames_ objectForKey:key]; if( [value retainCount] == 1 ) { CCLOG(@"cocos2d: CCSpriteFrameCache: removing unused frame: %@", key); [spriteFrames_ removeObjectForKey:key]; + removed_ = YES; } - } + } + + // XXX. Since we don't know the .plist file that originated the frame, we must remove all .plist from the cache + if( removed_ ) + [loadedFilenames_ removeAllObjects]; } -(void) removeSpriteFrameByName:(NSString*)name @@ -274,31 +339,39 @@ -(void) removeSpriteFrameByName:(NSString*)name // explicit nil handling if( ! name ) return; - + // Is this an alias ? NSString *key = [spriteFramesAliases_ objectForKey:name]; - + if( key ) { [spriteFrames_ removeObjectForKey:key]; [spriteFramesAliases_ removeObjectForKey:name]; } else [spriteFrames_ removeObjectForKey:name]; + + // XXX. Since we don't know the .plist file that originated the frame, we must remove all .plist from the cache + [loadedFilenames_ removeAllObjects]; } - (void) removeSpriteFramesFromFile:(NSString*) plist { - NSString *path = [CCFileUtils fullPathFromRelativePath:plist]; + NSString *path = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:plist]; NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; - + [self removeSpriteFramesFromDictionary:dict]; + + // remove it from the cache + id ret = [loadedFilenames_ member:plist]; + if( ret ) + [loadedFilenames_ removeObject:ret]; } - (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary { NSDictionary *framesDict = [dictionary objectForKey:@"frames"]; NSMutableArray *keysToRemove=[NSMutableArray array]; - + for(NSString *frameDictKey in framesDict) { if ([spriteFrames_ objectForKey:frameDictKey]!=nil) @@ -310,12 +383,12 @@ - (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary - (void) removeSpriteFramesFromTexture:(CCTexture2D*) texture { NSMutableArray *keysToRemove=[NSMutableArray array]; - + for (NSString *spriteFrameKey in spriteFrames_) { - if ([[spriteFrames_ valueForKey:spriteFrameKey] texture] == texture) + if ([[spriteFrames_ valueForKey:spriteFrameKey] texture] == texture) [keysToRemove addObject:spriteFrameKey]; - + } [spriteFrames_ removeObjectsForKeys:keysToRemove]; } @@ -329,11 +402,11 @@ -(CCSpriteFrame*) spriteFrameByName:(NSString*)name // try alias dictionary NSString *key = [spriteFramesAliases_ objectForKey:name]; frame = [spriteFrames_ objectForKey:key]; - + if( ! frame ) CCLOG(@"cocos2d: CCSpriteFrameCache: Frame '%@' not found", name); } - + return frame; } diff --git a/cocos2d/cocos2d/CCTMXLayer.h b/cocos2d/cocos2d/CCTMXLayer.h old mode 100644 new mode 100755 index 477a380..ed1f4f1 --- a/cocos2d/cocos2d/CCTMXLayer.h +++ b/cocos2d/cocos2d/CCTMXLayer.h @@ -3,17 +3,17 @@ * * Copyright (c) 2009-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -31,36 +31,35 @@ #import "CCAtlasNode.h" #import "CCSpriteBatchNode.h" - +#import "CCTMXXMLParser.h" @class CCTMXMapInfo; @class CCTMXLayerInfo; @class CCTMXTilesetInfo; + /** CCTMXLayer represents the TMX layer. - + It is a subclass of CCSpriteBatchNode. By default the tiles are rendered using a CCTextureAtlas. If you mofify a tile on runtime, then, that tile will become a CCSprite, otherwise no CCSprite objects are created. The benefits of using CCSprite objects as tiles are: - tiles (CCSprite) can be rotated/scaled/moved with a nice API - - If the layer contains a property named "cc_vertexz" with an integer (in can be positive or negative), - then all the tiles belonging to the layer will use that value as their OpenGL vertex Z for depth. - On the other hand, if the "cc_vertexz" property has the "automatic" value, then the tiles will use an automatic vertex Z value. - Also before drawing the tiles, GL_ALPHA_TEST will be enabled, and disabled after drawing them. The used alpha func will be: + cocos2d v2.0 doesn't support the cc_vertexz value. Whenever a the cc_vertexz property is found, it will raise an exception. - glAlphaFunc( GL_GREATER, value ) - "value" by default is 0, but you can change it from Tiled by adding the "cc_alpha_func" property to the layer. The value 0 should work for most cases, but if you have tiles that are semi-transparent, then you might want to use a differnt value, like 0.5. - + For further information, please see the programming guide: - + http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:tiled_maps - + @since v0.8.1 + + Tiles can have tile flags for additional properties. At the moment only flip horizontal and flip vertical are used. These bit flags are defined in CCTMXXMLParser.h. + + @since 1.1 */ @interface CCTMXLayer : CCSpriteBatchNode { @@ -71,17 +70,16 @@ uint32_t *tiles_; // GID are 32 bit NSUInteger layerOrientation_; NSMutableArray *properties_; - + unsigned char opacity_; // TMX Layer supports opacity - + NSUInteger minGID_; NSUInteger maxGID_; - + // Only used when vertexZ is used NSInteger vertexZvalue_; BOOL useAutomaticVertexZ_; - float alphaFuncValue_; - + // used for optimization CCSprite *reusedTile_; ccCArray *atlasIndexArray_; @@ -127,16 +125,30 @@ */ -(uint32_t) tileGIDAt:(CGPoint)tileCoordinate; +/** returns the tile gid at a given tile coordinate. It also returns the tile flags. + This method requires the the tile map has not been previously released (eg. don't call [layer releaseMap]) + */ +-(uint32_t) tileGIDAt:(CGPoint)pos withFlags:(ccTMXTileFlags*)flags; + /** sets the tile gid (gid = tile global id) at a given tile coordinate. The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor -> Tileset Mgr +1. If a tile is already placed at that position, then it will be removed. */ -(void) setTileGID:(uint32_t)gid at:(CGPoint)tileCoordinate; +/** sets the tile gid (gid = tile global id) at a given tile coordinate. + The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor -> Tileset Mgr +1. + If a tile is already placed at that position, then it will be removed. + + Use withFlags if the tile flags need to be changed as well + */ + +-(void) setTileGID:(uint32_t)gid at:(CGPoint)pos withFlags:(ccTMXTileFlags)flags; + /** removes a tile at given tile coordinate */ -(void) removeTileAt:(CGPoint)tileCoordinate; -/** returns the position in pixels of a given tile coordinate */ +/** returns the position in points of a given tile coordinate */ -(CGPoint) positionAt:(CGPoint)tileCoordinate; /** return the value for the specific property name */ diff --git a/cocos2d/cocos2d/CCTMXLayer.m b/cocos2d/cocos2d/CCTMXLayer.m old mode 100644 new mode 100755 index bb2ba60..15c57fa --- a/cocos2d/cocos2d/CCTMXLayer.m +++ b/cocos2d/cocos2d/CCTMXLayer.m @@ -3,17 +3,17 @@ * * Copyright (c) 2009-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -34,77 +34,10 @@ #import "CCSprite.h" #import "CCSpriteBatchNode.h" #import "CCTextureCache.h" +#import "CCShaderCache.h" +#import "CCGLProgram.h" #import "Support/CGPointExtension.h" -#pragma mark - -#pragma mark CCSpriteBatchNode Extension - -@interface CCSpriteBatchNode (TMXTiledMapExtensions) --(id) addSpriteWithoutQuad:(CCSprite*)child z:(NSUInteger)z tag:(NSInteger)aTag; --(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index; -@end - -/* IMPORTANT XXX IMPORTNAT: - * These 2 methods can't be part of CCTMXLayer since they call [super add...], and CCSpriteBatchNode#add SHALL not be called - */ -@implementation CCSpriteBatchNode (TMXTiledMapExtension) - -/* Adds a quad into the texture atlas but it won't be added into the children array. - This method should be called only when you are dealing with very big AtlasSrite and when most of the CCSprite won't be updated. - For example: a tile map (CCTMXMap) or a label with lots of characgers (CCLabelBMFont) - */ --(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index -{ - NSAssert( sprite != nil, @"Argument must be non-nil"); - NSAssert( [sprite isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children"); - - - while(index >= textureAtlas_.capacity || textureAtlas_.capacity == textureAtlas_.totalQuads ) - [self increaseAtlasCapacity]; - - // - // update the quad directly. Don't add the sprite to the scene graph - // - - [sprite useBatchNode:self]; - [sprite setAtlasIndex:index]; - - ccV3F_C4B_T2F_Quad quad = [sprite quad]; - [textureAtlas_ insertQuad:&quad atIndex:index]; - - // XXX: updateTransform will update the textureAtlas too using updateQuad. - // XXX: so, it should be AFTER the insertQuad - [sprite setDirty:YES]; - [sprite updateTransform]; -} - -/* This is the opposite of "addQuadFromSprite. - It add the sprite to the children and descendants array, but it doesn't update add it to the texture atlas - */ --(id) addSpriteWithoutQuad:(CCSprite*)child z:(NSUInteger)z tag:(NSInteger)aTag -{ - NSAssert( child != nil, @"Argument must be non-nil"); - NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children"); - - // quad index is Z - [child setAtlasIndex:z]; - - // XXX: optimize with a binary search - int i=0; - for( CCSprite *c in descendants_ ) { - if( c.atlasIndex >= z ) - break; - i++; - } - [descendants_ insertObject:child atIndex:i]; - - - // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array - [super addChild:child z:z tag:aTag]; - return self; -} -@end - #pragma mark - #pragma mark CCTMXLayer @@ -126,6 +59,7 @@ -(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos; /* The layer recognizes some special properties, like cc_vertez */ -(void) parseInternalProperties; +- (void) setupTileSprite:(CCSprite*) sprite position:(CGPoint)pos withGID:(uint32_t)gid; -(NSInteger) vertexZForPos:(CGPoint)pos; @@ -149,21 +83,21 @@ +(id) layerWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerI } -(id) initWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerInfo*)layerInfo mapInfo:(CCTMXMapInfo*)mapInfo -{ +{ // XXX: is 35% a good estimate ? CGSize size = layerInfo.layerSize; float totalNumberOfTiles = size.width * size.height; float capacity = totalNumberOfTiles * 0.35f + 1; // 35 percent is occupied ? - + CCTexture2D *tex = nil; if( tilesetInfo ) tex = [[CCTextureCache sharedTextureCache] addImage:tilesetInfo.sourceImage]; - - if((self = [super initWithTexture:tex capacity:capacity])) { + + if((self = [super initWithTexture:tex capacity:capacity])) { // layerInfo self.layerName = layerInfo.name; - layerSize_ = layerInfo.layerSize; + layerSize_ = size; tiles_ = layerInfo.tiles; minGID_ = layerInfo.minGID; maxGID_ = layerInfo.maxGID; @@ -172,24 +106,23 @@ -(id) initWithTilesetInfo:(CCTMXTilesetInfo*)tilesetInfo layerInfo:(CCTMXLayerIn // tilesetInfo self.tileset = tilesetInfo; - + // mapInfo mapTileSize_ = mapInfo.tileSize; layerOrientation_ = mapInfo.orientation; - + // offset (after layer orientation is set); CGPoint offset = [self calculateLayerOffset:layerInfo.offset]; - [self setPositionInPixels:offset]; - + [self setPosition:CC_POINT_PIXELS_TO_POINTS(offset)]; + atlasIndexArray_ = ccCArrayNew(totalNumberOfTiles); - - [self setContentSizeInPixels: CGSizeMake( layerSize_.width * mapTileSize_.width, layerSize_.height * mapTileSize_.height )]; - + + [self setContentSize:CC_SIZE_PIXELS_TO_POINTS(CGSizeMake( layerSize_.width * mapTileSize_.width, layerSize_.height * mapTileSize_.height ))]; + useAutomaticVertexZ_= NO; vertexZvalue_ = 0; - alphaFuncValue_ = 0; - } + return self; } @@ -199,17 +132,17 @@ - (void) dealloc [tileset_ release]; [reusedTile_ release]; [properties_ release]; - + if( atlasIndexArray_ ) { ccCArrayFree(atlasIndexArray_); atlasIndexArray_ = NULL; } - + if( tiles_ ) { free(tiles_); tiles_ = NULL; } - + [super dealloc]; } @@ -219,7 +152,7 @@ -(void) releaseMap free( tiles_); tiles_ = NULL; } - + if( atlasIndexArray_ ) { ccCArrayFree(atlasIndexArray_); atlasIndexArray_ = NULL; @@ -228,52 +161,71 @@ -(void) releaseMap #pragma mark CCTMXLayer - setup Tiles --(void) setupTiles +-(CCSprite*) reusedTileWithRect:(CGRect)rect { + if( ! reusedTile_ ) { + reusedTile_ = [[CCSprite alloc] initWithTexture:textureAtlas_.texture rect:rect rotated:NO]; + [reusedTile_ setBatchNode:self]; + } + else + { + // XXX: should not be re-init. Potential memeory leak. Not following best practices + // XXX: it shall call directory [setRect:rect] + [reusedTile_ initWithTexture:textureAtlas_.texture rect:rect rotated:NO]; + + // Since initWithTexture resets the batchNode, we need to re add it. + // but should be removed once initWithTexture is not called again + [reusedTile_ setBatchNode:self]; + } + + return reusedTile_; +} + +-(void) setupTiles +{ // Optimization: quick hack that sets the image size on the tileset tileset_.imageSize = [textureAtlas_.texture contentSizeInPixels]; - + // By default all the tiles are aliased // pros: // - easier to render // cons: // - difficult to scale / rotate / etc. [textureAtlas_.texture setAliasTexParameters]; - - CFByteOrder o = CFByteOrderGetCurrent(); - + // Parse cocos2d properties [self parseInternalProperties]; - - for( NSUInteger y=0; y < layerSize_.height; y++ ) { - for( NSUInteger x=0; x < layerSize_.width; x++ ) { - + + for( NSUInteger y = 0; y < layerSize_.height; y++ ) { + for( NSUInteger x = 0; x < layerSize_.width; x++ ) { + NSUInteger pos = x + layerSize_.width * y; uint32_t gid = tiles_[ pos ]; - + // gid are stored in little endian. // if host is big endian, then swap - if( o == CFByteOrderBigEndian ) - gid = CFSwapInt32( gid ); - + gid = CFSwapInt32LittleToHost( gid ); + // XXX: gid == 0 --> empty tile if( gid != 0 ) { [self appendTileForGID:gid at:ccp(x,y)]; - + // Optimization: update min and max GID rendered by the layer minGID_ = MIN(gid, minGID_); maxGID_ = MAX(gid, maxGID_); +// minGID_ = MIN((gid & kFlippedMask), minGID_); +// maxGID_ = MAX((gid & kFlippedMask), maxGID_); } } } - + NSAssert( maxGID_ >= tileset_.firstGid && - minGID_ >= tileset_.firstGid, @"TMX: Only 1 tilset per layer is supported"); + minGID_ >= tileset_.firstGid, @"TMX: Only 1 tilset per layer is supported"); } #pragma mark CCTMXLayer - Properties --(id) propertyNamed:(NSString *)propertyName +-(id) propertyNamed:(NSString *)propertyName { return [properties_ valueForKey:propertyName]; } @@ -284,14 +236,24 @@ -(void) parseInternalProperties NSString *vertexz = [self propertyNamed:@"cc_vertexz"]; if( vertexz ) { - if( [vertexz isEqualToString:@"automatic"] ) + + // If "automatic" is on, then parse the "cc_alpha_func" too + if( [vertexz isEqualToString:@"automatic"] ) { useAutomaticVertexZ_ = YES; + + NSString *alphaFuncVal = [self propertyNamed:@"cc_alpha_func"]; + float alphaFuncValue = [alphaFuncVal floatValue]; + + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTextureColorAlphaTest]; + + GLint alphaValueLocation = glGetUniformLocation(self.shaderProgram->program_, kCCUniformAlphaTestValue); + + // NOTE: alpha test shader is hard-coded to use the equivalent of a glAlphaFunc(GL_GREATER) comparison + [self.shaderProgram setUniformLocation:alphaValueLocation withF1:alphaFuncValue]; + } else vertexZvalue_ = [vertexz intValue]; } - - NSString *alphaFuncVal = [self propertyNamed:@"cc_alpha_func"]; - alphaFuncValue_ = [alphaFuncVal floatValue]; } #pragma mark CCTMXLayer - obtaining tiles/gids @@ -300,24 +262,28 @@ -(CCSprite*) tileAt:(CGPoint)pos { NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); - + CCSprite *tile = nil; uint32_t gid = [self tileGIDAt:pos]; - + // if GID == 0, then no tile is present if( gid ) { int z = pos.x + pos.y * layerSize_.width; tile = (CCSprite*) [self getChildByTag:z]; - + // tile not created yet. create it if( ! tile ) { - CGRect rect = [tileset_ rectForGID:gid]; - tile = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; - [tile setPositionInPixels: [self positionAt:pos]]; + CGRect rect = [tileset_ rectForGID:gid]; + rect = CC_RECT_PIXELS_TO_POINTS(rect); + tile = [[CCSprite alloc] initWithTexture:self.texture rect:rect]; + [tile setBatchNode:self]; + + CGPoint p = [self positionAt:pos]; + [tile setPosition:p]; [tile setVertexZ: [self vertexZForPos:pos]]; tile.anchorPoint = CGPointZero; [tile setOpacity:opacity_]; - + NSUInteger indexForZ = [self atlasIndexForExistantZ:z]; [self addSpriteWithoutQuad:tile z:indexForZ tag:z]; [tile release]; @@ -327,41 +293,104 @@ -(CCSprite*) tileAt:(CGPoint)pos } -(uint32_t) tileGIDAt:(CGPoint)pos +{ + return [self tileGIDAt:pos withFlags:NULL]; +} + +-(uint32_t) tileGIDAt:(CGPoint)pos withFlags:(ccTMXTileFlags*)flags { NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); - + NSInteger idx = pos.x + pos.y * layerSize_.width; - return tiles_[ idx ]; + + // Bits on the far end of the 32-bit global tile ID are used for tile flags + + uint32_t tile = tiles_[idx]; + + // issue1264, flipped tiles can be changed dynamically + if (flags) + *flags = tile & kCCFlipedAll; + + return ( tile & kCCFlippedMask); } #pragma mark CCTMXLayer - adding helper methods +- (void) setupTileSprite:(CCSprite*) sprite position:(CGPoint)pos withGID:(uint32_t)gid +{ + [sprite setPosition: [self positionAt:pos]]; + [sprite setVertexZ: [self vertexZForPos:pos]]; + sprite.anchorPoint = CGPointZero; + [sprite setOpacity:opacity_]; + + //issue 1264, flip can be undone as well + sprite.flipX = NO; + sprite.flipY = NO; + sprite.rotation = 0; + sprite.anchorPoint = ccp(0,0); + + // Rotation in tiled is achieved using 3 flipped states, flipping across the horizontal, vertical, and diagonal axes of the tiles. + if (gid & kCCTMXTileDiagonalFlag) + { + // put the anchor in the middle for ease of rotation. + sprite.anchorPoint = ccp(0.5f,0.5f); + [sprite setPosition: ccp([self positionAt:pos].x + sprite.contentSize.height/2, + [self positionAt:pos].y + sprite.contentSize.width/2 ) + ]; + + uint32_t flag = gid & (kCCTMXTileHorizontalFlag | kCCTMXTileVerticalFlag ); + + // handle the 4 diagonally flipped states. + if (flag == kCCTMXTileHorizontalFlag) + { + sprite.rotation = 90; + } + else if (flag == kCCTMXTileVerticalFlag) + { + sprite.rotation = 270; + } + else if (flag == (kCCTMXTileVerticalFlag | kCCTMXTileHorizontalFlag) ) + { + sprite.rotation = 90; + sprite.flipX = YES; + } + else + { + sprite.rotation = 270; + sprite.flipX = YES; + } + } + else + { + if (gid & kCCTMXTileHorizontalFlag) + sprite.flipX = YES; + + if (gid & kCCTMXTileVerticalFlag) + sprite.flipY = YES; + } +} + -(CCSprite*) insertTileForGID:(uint32_t)gid at:(CGPoint)pos { CGRect rect = [tileset_ rectForGID:gid]; - + rect = CC_RECT_PIXELS_TO_POINTS(rect); + NSInteger z = pos.x + pos.y * layerSize_.width; - - if( ! reusedTile_ ) - reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; - else - [reusedTile_ initWithBatchNode:self rectInPixels:rect]; - - [reusedTile_ setPositionInPixels: [self positionAt:pos]]; - [reusedTile_ setVertexZ: [self vertexZForPos:pos]]; - reusedTile_.anchorPoint = CGPointZero; - [reusedTile_ setOpacity:opacity_]; - + + CCSprite *tile = [self reusedTileWithRect:rect]; + + [self setupTileSprite:tile position:pos withGID:gid]; + // get atlas index NSUInteger indexForZ = [self atlasIndexForNewZ:z]; - + // Optimization: add the quad without adding a child - [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ]; - + [self addQuadFromSprite:tile quadIndex:indexForZ]; + // insert it into the local atlasindex array ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ); - + // update possible children CCSprite *sprite; CCARRAY_FOREACH(children_, sprite) { @@ -369,37 +398,32 @@ -(CCSprite*) insertTileForGID:(uint32_t)gid at:(CGPoint)pos if( ai >= indexForZ) [sprite setAtlasIndex: ai+1]; } - + tiles_[z] = gid; - - return reusedTile_; + + return tile; } -(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos { CGRect rect = [tileset_ rectForGID:gid]; - + rect = CC_RECT_PIXELS_TO_POINTS(rect); + int z = pos.x + pos.y * layerSize_.width; - - if( ! reusedTile_ ) - reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; - else - [reusedTile_ initWithBatchNode:self rectInPixels:rect]; - - [reusedTile_ setPositionInPixels: [self positionAt:pos]]; - [reusedTile_ setVertexZ: [self vertexZForPos:pos]]; - reusedTile_.anchorPoint = CGPointZero; - [reusedTile_ setOpacity:opacity_]; + + CCSprite *tile = [self reusedTileWithRect:rect]; + + [self setupTileSprite:tile position:pos withGID:gid]; // get atlas index NSUInteger indexForZ = [self atlasIndexForExistantZ:z]; - [reusedTile_ setAtlasIndex:indexForZ]; - [reusedTile_ setDirty:YES]; - [reusedTile_ updateTransform]; + [tile setAtlasIndex:indexForZ]; + [tile setDirty:YES]; + [tile updateTransform]; tiles_[z] = gid; - - return reusedTile_; + + return tile; } @@ -408,19 +432,14 @@ -(CCSprite*) updateTileForGID:(uint32_t)gid at:(CGPoint)pos -(CCSprite*) appendTileForGID:(uint32_t)gid at:(CGPoint)pos { CGRect rect = [tileset_ rectForGID:gid]; - + rect = CC_RECT_PIXELS_TO_POINTS(rect); + NSInteger z = pos.x + pos.y * layerSize_.width; - - if( ! reusedTile_ ) - reusedTile_ = [[CCSprite alloc] initWithBatchNode:self rectInPixels:rect]; - else - [reusedTile_ initWithBatchNode:self rectInPixels:rect]; - - [reusedTile_ setPositionInPixels: [self positionAt:pos]]; - [reusedTile_ setVertexZ: [self vertexZForPos:pos]]; - reusedTile_.anchorPoint = CGPointZero; - [reusedTile_ setOpacity:opacity_]; - + + CCSprite *tile = [self reusedTileWithRect:rect]; + + [self setupTileSprite:tile position:pos withGID:gid]; + // optimization: // The difference between appendTileForGID and insertTileforGID is that append is faster, since // it appends the tile at the end of the texture atlas @@ -428,13 +447,13 @@ -(CCSprite*) appendTileForGID:(uint32_t)gid at:(CGPoint)pos // don't add it using the "standard" way. - [self addQuadFromSprite:reusedTile_ quadIndex:indexForZ]; - - + [self addQuadFromSprite:tile quadIndex:indexForZ]; + + // append should be after addQuadFromSprite since it modifies the quantity values ccCArrayInsertValueAtIndex(atlasIndexArray_, (void*)z, indexForZ); - - return reusedTile_; + + return tile; } #pragma mark CCTMXLayer - atlasIndex and Z @@ -448,7 +467,7 @@ -(NSUInteger) atlasIndexForExistantZ:(NSUInteger)z { NSInteger key = z; NSInteger *item = bsearch((void*)&key, (void*)&atlasIndexArray_->arr[0], atlasIndexArray_->num, sizeof(void*), compareInts); - + NSAssert( item, @"TMX atlas index not found. Shall not happen"); NSUInteger index = ((NSInteger)item - (NSInteger)atlasIndexArray_->arr) / sizeof(void*); @@ -463,41 +482,54 @@ -(NSUInteger)atlasIndexForNewZ:(NSUInteger)z NSUInteger val = (NSUInteger) atlasIndexArray_->arr[i]; if( z < val ) break; - } + } return i; } #pragma mark CCTMXLayer - adding / remove tiles - -(void) setTileGID:(uint32_t)gid at:(CGPoint)pos +{ + [self setTileGID:gid at:pos withFlags:NO]; +} + +-(void) setTileGID:(uint32_t)gid at:(CGPoint)pos withFlags:(ccTMXTileFlags)flags { NSAssert( pos.x < layerSize_.width && pos.y < layerSize_.height && pos.x >=0 && pos.y >=0, @"TMXLayer: invalid position"); NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); NSAssert( gid == 0 || gid >= tileset_.firstGid, @"TMXLayer: invalid gid" ); - uint32_t currentGID = [self tileGIDAt:pos]; + ccTMXTileFlags currentFlags; + uint32_t currentGID = [self tileGIDAt:pos withFlags:¤tFlags]; - if( currentGID != gid ) { - + if (currentGID != gid || currentFlags != flags ) + { + uint32_t gidAndFlags = gid | flags; + // setting gid=0 is equal to remove the tile if( gid == 0 ) [self removeTileAt:pos]; // empty tile. create a new one else if( currentGID == 0 ) - [self insertTileForGID:gid at:pos]; + [self insertTileForGID:gidAndFlags at:pos]; // modifying an existing tile with a non-empty tile else { NSUInteger z = pos.x + pos.y * layerSize_.width; - id sprite = [self getChildByTag:z]; + CCSprite *sprite = (CCSprite*)[self getChildByTag:z]; if( sprite ) { CGRect rect = [tileset_ rectForGID:gid]; - [sprite setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size]; - tiles_[z] = gid; + rect = CC_RECT_PIXELS_TO_POINTS(rect); + + [sprite setTextureRect:rect rotated:NO untrimmedSize:rect.size]; + + if (flags) + [self setupTileSprite:sprite position:[sprite position] withGID:gidAndFlags]; + + tiles_[z] = gidAndFlags; } else - [self updateTileForGID:gid at:pos]; + [self updateTileForGID:gidAndFlags at:pos]; } } } @@ -514,7 +546,7 @@ -(void) removeChild:(CCSprite*)sprite cleanup:(BOOL)cleanup return; NSAssert( [children_ containsObject:sprite], @"Tile does not belong to TMXLayer"); - + NSUInteger atlasIndex = [sprite atlasIndex]; NSUInteger zz = (NSUInteger) atlasIndexArray_->arr[atlasIndex]; tiles_[zz] = 0; @@ -528,18 +560,18 @@ -(void) removeTileAt:(CGPoint)pos NSAssert( tiles_ && atlasIndexArray_, @"TMXLayer: the tiles map has been released"); uint32_t gid = [self tileGIDAt:pos]; - + if( gid ) { - + NSUInteger z = pos.x + pos.y * layerSize_.width; NSUInteger atlasIndex = [self atlasIndexForExistantZ:z]; - + // remove tile from GID map tiles_[z] = 0; // remove tile from atlas position array ccCArrayRemoveValueAtIndex(atlasIndexArray_, atlasIndex); - + // remove it from sprites and/or texture atlas id sprite = [self getChildByTag:z]; if( sprite ) @@ -575,7 +607,7 @@ -(CGPoint) calculateLayerOffset:(CGPoint)pos NSAssert(CGPointEqualToPoint(pos, CGPointZero), @"offset for hexagonal map not implemented yet"); break; } - return ret; + return ret; } -(CGPoint) positionAt:(CGPoint)pos @@ -592,6 +624,8 @@ -(CGPoint) positionAt:(CGPoint)pos ret = [self positionForHexAt:pos]; break; } + + ret = CC_POINT_PIXELS_TO_POINTS( ret ); return ret; } @@ -618,7 +652,7 @@ -(CGPoint) positionForHexAt:(CGPoint)pos float diffY = 0; if( (int)pos.x % 2 == 1 ) diffY = -mapTileSize_.height/2 ; - + CGPoint xy = { pos.x * mapTileSize_.width*3/4, (layerSize_.height - pos.y - 1) * mapTileSize_.height + diffY @@ -648,23 +682,9 @@ -(NSInteger) vertexZForPos:(CGPoint)pos } } else ret = vertexZvalue_; - + return ret; } -#pragma mark CCTMXLayer - draw - --(void) draw -{ - if( useAutomaticVertexZ_ ) { - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, alphaFuncValue_); - } - - [super draw]; - - if( useAutomaticVertexZ_ ) - glDisable(GL_ALPHA_TEST); -} @end diff --git a/cocos2d/cocos2d/CCTMXObjectGroup.h b/cocos2d/cocos2d/CCTMXObjectGroup.h old mode 100644 new mode 100755 index 02feadf..17fa09c --- a/cocos2d/cocos2d/CCTMXObjectGroup.h +++ b/cocos2d/cocos2d/CCTMXObjectGroup.h @@ -2,7 +2,7 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2010 Neophit - * + * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. * @@ -12,10 +12,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/cocos2d/cocos2d/CCTMXObjectGroup.m b/cocos2d/cocos2d/CCTMXObjectGroup.m old mode 100644 new mode 100755 index 648cda4..970effa --- a/cocos2d/cocos2d/CCTMXObjectGroup.m +++ b/cocos2d/cocos2d/CCTMXObjectGroup.m @@ -2,20 +2,20 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2010 Neophit - * + * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -60,7 +60,7 @@ -(id) init -(void) dealloc { CCLOGINFO( @"cocos2d: deallocing %@", self ); - + [groupName_ release]; [objects_ release]; [properties_ release]; @@ -78,7 +78,7 @@ -(NSMutableDictionary*) objectNamed:(NSString *)objectName return nil; } --(id) propertyNamed:(NSString *)propertyName +-(id) propertyNamed:(NSString *)propertyName { return [properties_ valueForKey:propertyName]; } diff --git a/cocos2d/cocos2d/CCTMXTiledMap.h b/cocos2d/cocos2d/CCTMXTiledMap.h old mode 100644 new mode 100755 index 4f4641a..084187c --- a/cocos2d/cocos2d/CCTMXTiledMap.h +++ b/cocos2d/cocos2d/CCTMXTiledMap.h @@ -3,17 +3,17 @@ * * Copyright (c) 2009-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,7 +30,6 @@ #import "CCNode.h" - @class CCTMXLayer; @class CCTMXObjectGroup; @@ -39,16 +38,16 @@ enum { /** Orthogonal orientation */ CCTMXOrientationOrtho, - + /** Hexagonal orientation */ CCTMXOrientationHex, - + /** Isometric orientation */ CCTMXOrientationIso, }; /** CCTMXTiledMap knows how to parse and render a TMX map. - + It adds support for the TMX tiled map format used by http://www.mapeditor.org It supports isometric, hexagonal and orthogonal tiles. It also supports object groups, objects, and properties. @@ -69,12 +68,12 @@ enum - Each object group will be treated as an NSMutableArray - Object class which will contain all the properties in a dictionary - Properties can be assigned to the Map, Layer, Object Group, and Object - + Limitations: - It only supports one tileset per layer. - Embeded images are not supported - It only supports the XML format (the JSON format is not supported) - + Technical description: Each layer is created using an CCTMXLayer (subclass of CCSpriteBatchNode). If you have 5 layers, then 5 CCTMXLayer will be created, unless the layer visibility is off. In that case, the layer won't be created at all. @@ -85,12 +84,12 @@ enum Each object group is created using a CCTMXObjectGroup which is a subclass of NSMutableArray. You can obtain the object groups at runtime by: - [map objectGroupNamed: name_of_the_object_group]; - + Each object is a CCTMXObject. Each property is stored as a key-value pair in an NSMutableDictionary. You can obtain the properties at runtime by: - + [map propertyNamed: name_of_the_property]; [layer propertyNamed: name_of_the_property]; [objectGroup propertyNamed: name_of_the_property]; @@ -122,9 +121,15 @@ enum /** creates a TMX Tiled Map with a TMX file.*/ +(id) tiledMapWithTMXFile:(NSString*)tmxFile; +/** initializes a TMX Tiled Map with a TMX formatted XML string and a path to TMX resources */ ++(id) tiledMapWithXML:(NSString*)tmxString resourcePath:(NSString*)resourcePath; + /** initializes a TMX Tiled Map with a TMX file */ -(id) initWithTMXFile:(NSString*)tmxFile; +/** initializes a TMX Tiled Map with a TMX formatted XML string and a path to TMX resources */ +-(id) initWithXML:(NSString*)tmxString resourcePath:(NSString*)resourcePath; + /** return the TMXLayer for the specific layer */ -(CCTMXLayer*) layerNamed:(NSString *)layerName; diff --git a/cocos2d/cocos2d/CCTMXTiledMap.m b/cocos2d/cocos2d/CCTMXTiledMap.m old mode 100644 new mode 100755 index 3afba49..0117b2e --- a/cocos2d/cocos2d/CCTMXTiledMap.m +++ b/cocos2d/cocos2d/CCTMXTiledMap.m @@ -3,17 +3,17 @@ * * Copyright (c) 2009-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -43,6 +43,7 @@ @interface CCTMXTiledMap (Private) -(id) parseLayer:(CCTMXLayerInfo*)layer map:(CCTMXMapInfo*)mapInfo; -(CCTMXTilesetInfo*) tilesetForLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo; +-(void) buildWithMapInfo:(CCTMXMapInfo*)mapInfo; @end @implementation CCTMXTiledMap @@ -57,43 +58,67 @@ +(id) tiledMapWithTMXFile:(NSString*)tmxFile return [[[self alloc] initWithTMXFile:tmxFile] autorelease]; } ++(id) tiledMapWithXML:(NSString*)tmxString resourcePath:(NSString*)resourcePath +{ + return [[[self alloc] initWithXML:tmxString resourcePath:resourcePath] autorelease]; +} + +-(void) buildWithMapInfo:(CCTMXMapInfo*)mapInfo +{ + mapSize_ = mapInfo.mapSize; + tileSize_ = mapInfo.tileSize; + mapOrientation_ = mapInfo.orientation; + objectGroups_ = [mapInfo.objectGroups retain]; + properties_ = [mapInfo.properties retain]; + tileProperties_ = [mapInfo.tileProperties retain]; + + int idx=0; + + for( CCTMXLayerInfo *layerInfo in mapInfo.layers ) { + + if( layerInfo.visible ) { + CCNode *child = [self parseLayer:layerInfo map:mapInfo]; + [self addChild:child z:idx tag:idx]; + + // update content size with the max size + CGSize childSize = [child contentSize]; + CGSize currentSize = [self contentSize]; + currentSize.width = MAX( currentSize.width, childSize.width ); + currentSize.height = MAX( currentSize.height, childSize.height ); + [self setContentSize:currentSize]; + + idx++; + } + } +} + +-(id) initWithXML:(NSString*)tmxString resourcePath:(NSString*)resourcePath +{ + if ((self=[super init])) { + + [self setContentSize:CGSizeZero]; + + CCTMXMapInfo *mapInfo = [CCTMXMapInfo formatWithXML:tmxString resourcePath:resourcePath]; + + NSAssert( [mapInfo.tilesets count] != 0, @"TMXTiledMap: Map not found. Please check the filename."); + [self buildWithMapInfo:mapInfo]; + } + + return self; +} + -(id) initWithTMXFile:(NSString*)tmxFile { NSAssert(tmxFile != nil, @"TMXTiledMap: tmx file should not bi nil"); if ((self=[super init])) { - + [self setContentSize:CGSizeZero]; CCTMXMapInfo *mapInfo = [CCTMXMapInfo formatWithTMXFile:tmxFile]; - + NSAssert( [mapInfo.tilesets count] != 0, @"TMXTiledMap: Map not found. Please check the filename."); - - mapSize_ = mapInfo.mapSize; - tileSize_ = mapInfo.tileSize; - mapOrientation_ = mapInfo.orientation; - objectGroups_ = [mapInfo.objectGroups retain]; - properties_ = [mapInfo.properties retain]; - tileProperties_ = [mapInfo.tileProperties retain]; - - int idx=0; - - for( CCTMXLayerInfo *layerInfo in mapInfo.layers ) { - - if( layerInfo.visible ) { - CCNode *child = [self parseLayer:layerInfo map:mapInfo]; - [self addChild:child z:idx tag:idx]; - - // update content size with the max size - CGSize childSize = [child contentSize]; - CGSize currentSize = [self contentSize]; - currentSize.width = MAX( currentSize.width, childSize.width ); - currentSize.height = MAX( currentSize.height, childSize.height ); - [self setContentSize:currentSize]; - - idx++; - } - } + [self buildWithMapInfo:mapInfo]; } return self; @@ -117,41 +142,38 @@ -(id) parseLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo layerInfo.ownTiles = NO; [layer setupTiles]; - + return layer; } -(CCTMXTilesetInfo*) tilesetForLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInfo*)mapInfo { - CFByteOrder o = CFByteOrderGetCurrent(); - CGSize size = layerInfo.layerSize; id iter = [mapInfo.tilesets reverseObjectEnumerator]; for( CCTMXTilesetInfo* tileset in iter) { for( unsigned int y = 0; y < size.height; y++ ) { for( unsigned int x = 0; x < size.width; x++ ) { - + unsigned int pos = x + size.width * y; unsigned int gid = layerInfo.tiles[ pos ]; - + // gid are stored in little endian. // if host is big endian, then swap - if( o == CFByteOrderBigEndian ) - gid = CFSwapInt32( gid ); - + gid = CFSwapInt32LittleToHost( gid ); + // XXX: gid == 0 --> empty tile if( gid != 0 ) { - + // Optimization: quick return // if the layer is invalid (more than 1 tileset per layer) an assert will be thrown later - if( gid >= tileset.firstGid ) + if( (gid & kCCFlippedMask) >= tileset.firstGid ) return tileset; } } - } + } } - + // If all the tiles are 0, return empty tileset CCLOG(@"cocos2d: Warning: TMX Layer '%@' has no tiles", layerInfo.name); return nil; @@ -160,7 +182,7 @@ -(CCTMXTilesetInfo*) tilesetForLayer:(CCTMXLayerInfo*)layerInfo map:(CCTMXMapInf // public --(CCTMXLayer*) layerNamed:(NSString *)layerName +-(CCTMXLayer*) layerNamed:(NSString *)layerName { CCTMXLayer *layer; CCARRAY_FOREACH(children_, layer) { @@ -168,23 +190,23 @@ -(CCTMXLayer*) layerNamed:(NSString *)layerName if([layer.layerName isEqual:layerName]) return layer; } - + // layer not found return nil; } --(CCTMXObjectGroup*) objectGroupNamed:(NSString *)groupName +-(CCTMXObjectGroup*) objectGroupNamed:(NSString *)groupName { for( CCTMXObjectGroup *objectGroup in objectGroups_ ) { if( [objectGroup.groupName isEqual:groupName] ) return objectGroup; } - + // objectGroup not found return nil; } --(id) propertyNamed:(NSString *)propertyName +-(id) propertyNamed:(NSString *)propertyName { return [properties_ valueForKey:propertyName]; } diff --git a/cocos2d/cocos2d/CCTMXXMLParser.h b/cocos2d/cocos2d/CCTMXXMLParser.h old mode 100644 new mode 100755 index 18d7a8a..91b89f4 --- a/cocos2d/cocos2d/CCTMXXMLParser.h +++ b/cocos2d/cocos2d/CCTMXXMLParser.h @@ -3,17 +3,17 @@ * * Copyright (c) 2009-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -35,11 +35,12 @@ * since the user should not use them. * */ - -#import + #import +#import "ccMacros.h" + enum { TMXLayerAttribNone = 1 << 0, TMXLayerAttribBase64 = 1 << 1, @@ -56,12 +57,23 @@ enum { TMXPropertyTile }; +typedef enum ccTMXTileFlags_ { + kCCTMXTileHorizontalFlag = 0x80000000, + kCCTMXTileVerticalFlag = 0x40000000, + kCCTMXTileDiagonalFlag = 0x20000000, + + kCCFlipedAll = (kCCTMXTileHorizontalFlag|kCCTMXTileVerticalFlag|kCCTMXTileDiagonalFlag), + kCCFlippedMask = ~(kCCFlipedAll), +} ccTMXTileFlags; + +// Bits on the far end of the 32-bit global tile ID (GID's) are used for tile flags + /* CCTMXLayerInfo contains the information about the layers like: - Layer name - Layer size - Layer opacity at creation time (it can be modified at runtime) - - Whether the layer is visible (if it's not visible, then the CocosNode won't be created) - + - Whether the layer is visible (if it is not visible, then the CCNode won't be created) + This information is obtained from the TMX file. */ @interface CCTMXLayerInfo : NSObject @@ -97,8 +109,8 @@ enum { - size of the tiles - Image used for the tiles - Image size - - This information is obtained from the TMX file. + + This information is obtained from the TMX file. */ @interface CCTMXTilesetInfo : NSObject { @@ -107,10 +119,10 @@ enum { CGSize tileSize_; unsigned int spacing_; unsigned int margin_; - + // filename containing the tiles (should be spritesheet / texture atlas) NSString *sourceImage_; - + // size in pixels of the image CGSize imageSize_; } @@ -129,57 +141,51 @@ enum { - Map orientation (hexagonal, isometric or orthogonal) - Tile size - Map size - + And it also contains: - Layers (an array of TMXLayerInfo objects) - Tilesets (an array of TMXTilesetInfo objects) - ObjectGroups (an array of TMXObjectGroupInfo objects) - + This information is obtained from the TMX file. - - */ -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#if defined(__IPHONE_4_0) -@interface CCTMXMapInfo : NSObject -#else -@interface CCTMXMapInfo : NSObject -#endif -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + */ @interface CCTMXMapInfo : NSObject -#endif -{ +{ NSMutableString *currentString; - BOOL storingCharacters; + BOOL storingCharacters; int layerAttribs; int parentElement; unsigned int parentGID_; - + // tmx filename NSString *filename_; + // tmx resource path + NSString *resources_; + // map orientation - int orientation_; - + int orientation_; + // map width & height CGSize mapSize_; - + // tiles width & height CGSize tileSize_; - + // Layers NSMutableArray *layers_; - + // tilesets NSMutableArray *tilesets_; - + // ObjectGroups NSMutableArray *objectGroups_; - + // properties NSMutableDictionary *properties_; - + // tile properties NSMutableDictionary *tileProperties_; } @@ -190,13 +196,22 @@ enum { @property (nonatomic,readwrite,retain) NSMutableArray *layers; @property (nonatomic,readwrite,retain) NSMutableArray *tilesets; @property (nonatomic,readwrite,retain) NSString *filename; +@property (nonatomic,readwrite,retain) NSString *resources; @property (nonatomic,readwrite,retain) NSMutableArray *objectGroups; @property (nonatomic,readwrite,retain) NSMutableDictionary *properties; @property (nonatomic,readwrite,retain) NSMutableDictionary *tileProperties; /** creates a TMX Format with a tmx file */ +(id) formatWithTMXFile:(NSString*)tmxFile; -/** initializes a TMX format witha tmx file */ + +/** creates a TMX Format with an XML string and a TMX resource path */ ++(id) formatWithXML:(NSString*)tmxString resourcePath:(NSString*)resourcePath; + +/** initializes a TMX format with a tmx file */ -(id) initWithTMXFile:(NSString*)tmxFile; + +/** initializes a TMX format with an XML string and a TMX resource path */ +-(id) initWithXML:(NSString*)tmxString resourcePath:(NSString*)resourcePath; + @end diff --git a/cocos2d/cocos2d/CCTMXXMLParser.m b/cocos2d/cocos2d/CCTMXXMLParser.m old mode 100644 new mode 100755 index 77cea0e..9eb0231 --- a/cocos2d/cocos2d/CCTMXXMLParser.m +++ b/cocos2d/cocos2d/CCTMXXMLParser.m @@ -3,17 +3,17 @@ * * Copyright (c) 2009-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -65,7 +65,7 @@ -(id) init - (void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@",self); - + [name_ release]; [properties_ release]; @@ -96,15 +96,16 @@ -(CGRect) rectForGID:(unsigned int)gid { CGRect rect; rect.size = tileSize_; - + + gid &= kCCFlippedMask; gid = gid - firstGid_; - + int max_x = (imageSize_.width - margin_*2 + spacing_) / (tileSize_.width + spacing_); // int max_y = (imageSize.height - margin*2 + spacing) / (tileSize.height + spacing); - + rect.origin.x = (gid % max_x) * (tileSize_.width + spacing_) + margin_; rect.origin.y = (gid / max_x) * (tileSize_.height + spacing_) + margin_; - + return rect; } @end @@ -115,12 +116,15 @@ -(CGRect) rectForGID:(unsigned int)gid @interface CCTMXMapInfo (Private) /* initalises parsing of an XML file, either a tmx (Map) file or tsx (Tileset) file */ -(void) parseXMLFile:(NSString *)xmlFilename; +/* initalises parsing of an XML string, either a tmx (Map) string or tsx (Tileset) string */ +- (void) parseXMLString:(NSString *)xmlString; +/* handles the work of parsing for parseXMLFile: and parseXMLString: */ +- (void) parseXMLData:(NSData*)data; @end - @implementation CCTMXMapInfo -@synthesize orientation = orientation_, mapSize = mapSize_, layers = layers_, tilesets = tilesets_, tileSize = tileSize_, filename = filename_, objectGroups = objectGroups_, properties = properties_; +@synthesize orientation = orientation_, mapSize = mapSize_, layers = layers_, tilesets = tilesets_, tileSize = tileSize_, filename = filename_, resources = resources_, objectGroups = objectGroups_, properties = properties_; @synthesize tileProperties = tileProperties_; +(id) formatWithTMXFile:(NSString*)tmxFile @@ -128,33 +132,53 @@ +(id) formatWithTMXFile:(NSString*)tmxFile return [[[self alloc] initWithTMXFile:tmxFile] autorelease]; } ++(id) formatWithXML:(NSString*)tmxString resourcePath:(NSString*)resourcePath +{ + return [[[self alloc] initWithXML:tmxString resourcePath:resourcePath] autorelease]; +} + +- (void) internalInit:(NSString*)tmxFileName resourcePath:(NSString*)resourcePath +{ + self.tilesets = [NSMutableArray arrayWithCapacity:4]; + self.layers = [NSMutableArray arrayWithCapacity:4]; + self.filename = tmxFileName; + self.resources = resourcePath; + self.objectGroups = [NSMutableArray arrayWithCapacity:4]; + self.properties = [NSMutableDictionary dictionaryWithCapacity:5]; + self.tileProperties = [NSMutableDictionary dictionaryWithCapacity:5]; + + // tmp vars + currentString = [[NSMutableString alloc] initWithCapacity:1024]; + storingCharacters = NO; + layerAttribs = TMXLayerAttribNone; + parentElement = TMXPropertyNone; +} + +-(id) initWithXML:(NSString *)tmxString resourcePath:(NSString*)resourcePath +{ + if( (self=[super init])) { + [self internalInit:nil resourcePath:resourcePath]; + [self parseXMLString:tmxString]; + } + return self; +} + -(id) initWithTMXFile:(NSString*)tmxFile { if( (self=[super init])) { - - self.tilesets = [NSMutableArray arrayWithCapacity:4]; - self.layers = [NSMutableArray arrayWithCapacity:4]; - self.filename = tmxFile; - self.objectGroups = [NSMutableArray arrayWithCapacity:4]; - self.properties = [NSMutableDictionary dictionaryWithCapacity:5]; - self.tileProperties = [NSMutableDictionary dictionaryWithCapacity:5]; - - // tmp vars - currentString = [[NSMutableString alloc] initWithCapacity:1024]; - storingCharacters = NO; - layerAttribs = TMXLayerAttribNone; - parentElement = TMXPropertyNone; - - [self parseXMLFile:filename_]; + [self internalInit:tmxFile resourcePath:nil]; + [self parseXMLFile:filename_]; } return self; } + - (void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); [tilesets_ release]; [layers_ release]; [filename_ release]; + [resources_ release]; [currentString release]; [objectGroups_ release]; [properties_ release]; @@ -162,11 +186,9 @@ - (void) dealloc [super dealloc]; } -- (void) parseXMLFile:(NSString *)xmlFilename +- (void) parseXMLData:(NSData*)data { - NSURL *url = [NSURL fileURLWithPath:[CCFileUtils fullPathFromRelativePath:xmlFilename] ]; - NSData *data = [NSData dataWithContentsOfURL:url]; - NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; + NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:data] autorelease]; // we'll do the parsing [parser setDelegate:self]; @@ -174,20 +196,31 @@ - (void) parseXMLFile:(NSString *)xmlFilename [parser setShouldReportNamespacePrefixes:NO]; [parser setShouldResolveExternalEntities:NO]; [parser parse]; + + NSAssert1( ![parser parserError], @"Error parsing TMX data: %@.", [NSString stringWithCharacters:[data bytes] length:[data length]] ); +} - NSAssert1( ! [parser parserError], @"Error parsing file: %@.", xmlFilename ); +- (void) parseXMLString:(NSString *)xmlString +{ + NSData* data = [xmlString dataUsingEncoding:NSUTF8StringEncoding]; + [self parseXMLData:data]; +} - [parser release]; +- (void) parseXMLFile:(NSString *)xmlFilename +{ + NSURL *url = [NSURL fileURLWithPath:[[CCFileUtils sharedFileUtils] fullPathFromRelativePath:xmlFilename] ]; + NSData *data = [NSData dataWithContentsOfURL:url]; + [self parseXMLData:data]; } // the XML parser calls here with all the elements -(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict -{ +{ if([elementName isEqualToString:@"map"]) { - NSString *version = [attributeDict valueForKey:@"version"]; + NSString *version = [attributeDict objectForKey:@"version"]; if( ! [version isEqualToString:@"1.0"] ) CCLOG(@"cocos2d: TMXFormat: Unsupported TMX version: %@", version); - NSString *orientationStr = [attributeDict valueForKey:@"orientation"]; + NSString *orientationStr = [attributeDict objectForKey:@"orientation"]; if( [orientationStr isEqualToString:@"orthogonal"]) orientation_ = CCTMXOrientationOrtho; else if ( [orientationStr isEqualToString:@"isometric"]) @@ -195,37 +228,39 @@ -(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName name else if( [orientationStr isEqualToString:@"hexagonal"]) orientation_ = CCTMXOrientationHex; else - CCLOG(@"cocos2d: TMXFomat: Unsupported orientation: %@", orientation_); + CCLOG(@"cocos2d: TMXFomat: Unsupported orientation: %d", orientation_); - mapSize_.width = [[attributeDict valueForKey:@"width"] intValue]; - mapSize_.height = [[attributeDict valueForKey:@"height"] intValue]; - tileSize_.width = [[attributeDict valueForKey:@"tilewidth"] intValue]; - tileSize_.height = [[attributeDict valueForKey:@"tileheight"] intValue]; + mapSize_.width = [[attributeDict objectForKey:@"width"] intValue]; + mapSize_.height = [[attributeDict objectForKey:@"height"] intValue]; + tileSize_.width = [[attributeDict objectForKey:@"tilewidth"] intValue]; + tileSize_.height = [[attributeDict objectForKey:@"tileheight"] intValue]; // The parent element is now "map" parentElement = TMXPropertyMap; } else if([elementName isEqualToString:@"tileset"]) { - + // If this is an external tileset then start parsing that - NSString *externalTilesetFilename = [attributeDict valueForKey:@"source"]; + NSString *externalTilesetFilename = [attributeDict objectForKey:@"source"]; if (externalTilesetFilename) { // Tileset file will be relative to the map file. So we need to convert it to an absolute path NSString *dir = [filename_ stringByDeletingLastPathComponent]; // Directory of map file + if (!dir) + dir = resources_; externalTilesetFilename = [dir stringByAppendingPathComponent:externalTilesetFilename]; // Append path to tileset file - + [self parseXMLFile:externalTilesetFilename]; } else { - + CCTMXTilesetInfo *tileset = [CCTMXTilesetInfo new]; - tileset.name = [attributeDict valueForKey:@"name"]; - tileset.firstGid = [[attributeDict valueForKey:@"firstgid"] intValue]; - tileset.spacing = [[attributeDict valueForKey:@"spacing"] intValue]; - tileset.margin = [[attributeDict valueForKey:@"margin"] intValue]; + tileset.name = [attributeDict objectForKey:@"name"]; + tileset.firstGid = [[attributeDict objectForKey:@"firstgid"] intValue]; + tileset.spacing = [[attributeDict objectForKey:@"spacing"] intValue]; + tileset.margin = [[attributeDict objectForKey:@"margin"] intValue]; CGSize s; - s.width = [[attributeDict valueForKey:@"tilewidth"] intValue]; - s.height = [[attributeDict valueForKey:@"tileheight"] intValue]; + s.width = [[attributeDict objectForKey:@"tilewidth"] intValue]; + s.height = [[attributeDict objectForKey:@"tileheight"] intValue]; tileset.tileSize = s; - + [tilesets_ addObject:tileset]; [tileset release]; } @@ -233,69 +268,71 @@ -(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName name }else if([elementName isEqualToString:@"tile"]){ CCTMXTilesetInfo* info = [tilesets_ lastObject]; NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:3]; - parentGID_ = [info firstGid] + [[attributeDict valueForKey:@"id"] intValue]; + parentGID_ = [info firstGid] + [[attributeDict objectForKey:@"id"] intValue]; [tileProperties_ setObject:dict forKey:[NSNumber numberWithInt:parentGID_]]; - + parentElement = TMXPropertyTile; - + }else if([elementName isEqualToString:@"layer"]) { CCTMXLayerInfo *layer = [CCTMXLayerInfo new]; - layer.name = [attributeDict valueForKey:@"name"]; - + layer.name = [attributeDict objectForKey:@"name"]; + CGSize s; - s.width = [[attributeDict valueForKey:@"width"] intValue]; - s.height = [[attributeDict valueForKey:@"height"] intValue]; + s.width = [[attributeDict objectForKey:@"width"] intValue]; + s.height = [[attributeDict objectForKey:@"height"] intValue]; layer.layerSize = s; - - layer.visible = ![[attributeDict valueForKey:@"visible"] isEqualToString:@"0"]; - - if( [attributeDict valueForKey:@"opacity"] ) - layer.opacity = 255 * [[attributeDict valueForKey:@"opacity"] floatValue]; + + layer.visible = ![[attributeDict objectForKey:@"visible"] isEqualToString:@"0"]; + + if( [attributeDict objectForKey:@"opacity"] ) + layer.opacity = 255 * [[attributeDict objectForKey:@"opacity"] floatValue]; else layer.opacity = 255; - - int x = [[attributeDict valueForKey:@"x"] intValue]; - int y = [[attributeDict valueForKey:@"y"] intValue]; + + int x = [[attributeDict objectForKey:@"x"] intValue]; + int y = [[attributeDict objectForKey:@"y"] intValue]; layer.offset = ccp(x,y); - + [layers_ addObject:layer]; [layer release]; - + // The parent element is now "layer" parentElement = TMXPropertyLayer; - + } else if([elementName isEqualToString:@"objectgroup"]) { - + CCTMXObjectGroup *objectGroup = [[CCTMXObjectGroup alloc] init]; - objectGroup.groupName = [attributeDict valueForKey:@"name"]; + objectGroup.groupName = [attributeDict objectForKey:@"name"]; CGPoint positionOffset; - positionOffset.x = [[attributeDict valueForKey:@"x"] intValue] * tileSize_.width; - positionOffset.y = [[attributeDict valueForKey:@"y"] intValue] * tileSize_.height; + positionOffset.x = [[attributeDict objectForKey:@"x"] intValue] * tileSize_.width; + positionOffset.y = [[attributeDict objectForKey:@"y"] intValue] * tileSize_.height; objectGroup.positionOffset = positionOffset; - + [objectGroups_ addObject:objectGroup]; [objectGroup release]; - + // The parent element is now "objectgroup" parentElement = TMXPropertyObjectGroup; - + } else if([elementName isEqualToString:@"image"]) { CCTMXTilesetInfo *tileset = [tilesets_ lastObject]; - + // build full path - NSString *imagename = [attributeDict valueForKey:@"source"]; - NSString *path = [filename_ stringByDeletingLastPathComponent]; + NSString *imagename = [attributeDict objectForKey:@"source"]; + NSString *path = [filename_ stringByDeletingLastPathComponent]; + if (!path) + path = resources_; tileset.sourceImage = [path stringByAppendingPathComponent:imagename]; } else if([elementName isEqualToString:@"data"]) { - NSString *encoding = [attributeDict valueForKey:@"encoding"]; - NSString *compression = [attributeDict valueForKey:@"compression"]; - + NSString *encoding = [attributeDict objectForKey:@"encoding"]; + NSString *compression = [attributeDict objectForKey:@"compression"]; + if( [encoding isEqualToString:@"base64"] ) { layerAttribs |= TMXLayerAttribBase64; storingCharacters = YES; - + if( [compression isEqualToString:@"gzip"] ) layerAttribs |= TMXLayerAttribGzip; @@ -304,81 +341,107 @@ -(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName name NSAssert( !compression || [compression isEqualToString:@"gzip"] || [compression isEqualToString:@"zlib"], @"TMX: unsupported compression method" ); } - + NSAssert( layerAttribs != TMXLayerAttribNone, @"TMX tile map: Only base64 and/or gzip/zlib maps are supported" ); - + } else if([elementName isEqualToString:@"object"]) { - + CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject]; - + // The value for "type" was blank or not a valid class name // Create an instance of TMXObjectInfo to store the object and its properties - NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:5]; + NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:10]; + + // Parse everything automatically + NSArray *array = [NSArray arrayWithObjects:@"name", @"type", @"width", @"height", @"gid", nil]; + for( id key in array ) { + NSObject *obj = [attributeDict objectForKey:key]; + if( obj ) + [dict setObject:obj forKey:key]; + } - // Set the name of the object to the value for "name" - [dict setValue:[attributeDict valueForKey:@"name"] forKey:@"name"]; + // But X and Y since they need special treatment + // X + NSString *value = [attributeDict objectForKey:@"x"]; + if( value ) { + int x = [value intValue] + objectGroup.positionOffset.x; + [dict setObject:[NSNumber numberWithInt:x] forKey:@"x"]; + } - // Assign all the attributes as key/name pairs in the properties dictionary - [dict setValue:[attributeDict valueForKey:@"type"] forKey:@"type"]; - int x = [[attributeDict valueForKey:@"x"] intValue] + objectGroup.positionOffset.x; - [dict setValue:[NSNumber numberWithInt:x] forKey:@"x"]; - int y = [[attributeDict valueForKey:@"y"] intValue] + objectGroup.positionOffset.y; - // Correct y position. (Tiled uses Flipped, cocos2d uses Standard) - y = (mapSize_.height * tileSize_.height) - y - [[attributeDict valueForKey:@"height"] intValue]; - [dict setValue:[NSNumber numberWithInt:y] forKey:@"y"]; - [dict setValue:[attributeDict valueForKey:@"width"] forKey:@"width"]; - [dict setValue:[attributeDict valueForKey:@"height"] forKey:@"height"]; + // Y + value = [attributeDict objectForKey:@"y"]; + if( value ) { + int y = [value intValue] + objectGroup.positionOffset.y; + + // Correct y position. (Tiled uses Flipped, cocos2d uses Standard) + y = (mapSize_.height * tileSize_.height) - y - [[attributeDict objectForKey:@"height"] intValue]; + [dict setObject:[NSNumber numberWithInt:y] forKey:@"y"]; + } // Add the object to the objectGroup [[objectGroup objects] addObject:dict]; [dict release]; - + // The parent element is now "object" parentElement = TMXPropertyObject; - + } else if([elementName isEqualToString:@"property"]) { - + if ( parentElement == TMXPropertyNone ) { - + CCLOG( @"TMX tile map: Parent element is unsupported. Cannot add property named '%@' with value '%@'", - [attributeDict valueForKey:@"name"], [attributeDict valueForKey:@"value"] ); - + [attributeDict objectForKey:@"name"], [attributeDict objectForKey:@"value"] ); + } else if ( parentElement == TMXPropertyMap ) { - + // The parent element is the map - [properties_ setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]]; - + [properties_ setObject:[attributeDict objectForKey:@"value"] forKey:[attributeDict objectForKey:@"name"]]; + } else if ( parentElement == TMXPropertyLayer ) { - + // The parent element is the last layer CCTMXLayerInfo *layer = [layers_ lastObject]; // Add the property to the layer - [[layer properties] setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]]; - + [[layer properties] setObject:[attributeDict objectForKey:@"value"] forKey:[attributeDict objectForKey:@"name"]]; + } else if ( parentElement == TMXPropertyObjectGroup ) { - + // The parent element is the last object group CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject]; - [[objectGroup properties] setValue:[attributeDict valueForKey:@"value"] forKey:[attributeDict valueForKey:@"name"]]; - + [[objectGroup properties] setObject:[attributeDict objectForKey:@"value"] forKey:[attributeDict objectForKey:@"name"]]; + } else if ( parentElement == TMXPropertyObject ) { - + // The parent element is the last object CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject]; NSMutableDictionary *dict = [[objectGroup objects] lastObject]; - - NSString *propertyName = [attributeDict valueForKey:@"name"]; - NSString *propertyValue = [attributeDict valueForKey:@"value"]; - [dict setValue:propertyValue forKey:propertyName]; + NSString *propertyName = [attributeDict objectForKey:@"name"]; + NSString *propertyValue = [attributeDict objectForKey:@"value"]; + + [dict setObject:propertyValue forKey:propertyName]; + } else if ( parentElement == TMXPropertyTile ) { - + NSMutableDictionary* dict = [tileProperties_ objectForKey:[NSNumber numberWithInt:parentGID_]]; - NSString *propertyName = [attributeDict valueForKey:@"name"]; - NSString *propertyValue = [attributeDict valueForKey:@"value"]; + NSString *propertyName = [attributeDict objectForKey:@"name"]; + NSString *propertyValue = [attributeDict objectForKey:@"value"]; [dict setObject:propertyValue forKey:propertyName]; - } + + } else if ([elementName isEqualToString:@"polygon"]) { + + // find parent object's dict and add polygon-points to it + CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject]; + NSMutableDictionary *dict = [[objectGroup objects] lastObject]; + [dict setObject:[attributeDict objectForKey:@"points"] forKey:@"polygonPoints"]; + + } else if ([elementName isEqualToString:@"polyline"]) { + + // find parent object's dict and add polyline-points to it + CCTMXObjectGroup *objectGroup = [objectGroups_ lastObject]; + NSMutableDictionary *dict = [[objectGroup objects] lastObject]; + [dict setObject:[attributeDict objectForKey:@"points"] forKey:@"polylinePoints"]; } } @@ -388,16 +451,16 @@ - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName names if([elementName isEqualToString:@"data"] && layerAttribs&TMXLayerAttribBase64) { storingCharacters = NO; - + CCTMXLayerInfo *layer = [layers_ lastObject]; - + unsigned char *buffer; len = base64Decode((unsigned char*)[currentString UTF8String], (unsigned int) [currentString length], &buffer); if( ! buffer ) { CCLOG(@"cocos2d: TiledMap: decode data error"); return; } - + if( layerAttribs & (TMXLayerAttribGzip | TMXLayerAttribZlib) ) { unsigned char *deflated; CGSize s = [layer layerSize]; @@ -405,34 +468,34 @@ - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName names int inflatedLen = ccInflateMemoryWithHint(buffer, len, &deflated, sizeHint); NSAssert( inflatedLen == sizeHint, @"CCTMXXMLParser: Hint failed!"); - + inflatedLen = (int)&inflatedLen; // XXX: to avoid warings in compiler free( buffer ); - + if( ! deflated ) { CCLOG(@"cocos2d: TiledMap: inflate data error"); return; } - + layer.tiles = (unsigned int*) deflated; } else layer.tiles = (unsigned int*) buffer; - + [currentString setString:@""]; - + } else if ([elementName isEqualToString:@"map"]) { // The map element has ended parentElement = TMXPropertyNone; - + } else if ([elementName isEqualToString:@"layer"]) { // The layer element has ended parentElement = TMXPropertyNone; - + } else if ([elementName isEqualToString:@"objectgroup"]) { // The objectgroup element has ended parentElement = TMXPropertyNone; - + } else if ([elementName isEqualToString:@"object"]) { // The object element has ended parentElement = TMXPropertyNone; diff --git a/cocos2d/cocos2d/CCTexture2D.h b/cocos2d/cocos2d/CCTexture2D.h old mode 100644 new mode 100755 index 45eea9c..415a3b8 --- a/cocos2d/cocos2d/CCTexture2D.h +++ b/cocos2d/cocos2d/CCTexture2D.h @@ -60,14 +60,11 @@ Copyright (C) 2008 Apple Inc. All Rights Reserved. */ -#import - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#import // for UIImage -#endif - #import // for NSObject +#import "ccTypes.h" +#import "ccMacros.h" + #import "Platforms/CCGL.h" // OpenGL stuff #import "Platforms/CCNS.h" // Next-Step stuff @@ -77,9 +74,10 @@ Copyright (C) 2008 Apple Inc. All Rights Reserved. Possible texture pixel formats */ typedef enum { - kCCTexture2DPixelFormat_Automatic = 0, //! 32-bit texture: RGBA8888 kCCTexture2DPixelFormat_RGBA8888, + //! 32-bit texture without Alpha channel. Don't use it. + kCCTexture2DPixelFormat_RGB888, //! 16-bit texture without Alpha channel kCCTexture2DPixelFormat_RGB565, //! 8-bit textures used as masks @@ -91,7 +89,7 @@ typedef enum { //! 16-bit textures: RGBA4444 kCCTexture2DPixelFormat_RGBA4444, //! 16-bit textures: RGB5A1 - kCCTexture2DPixelFormat_RGB5A1, + kCCTexture2DPixelFormat_RGB5A1, //! 4-bit PVRTC-compressed texture: PVRTC4 kCCTexture2DPixelFormat_PVRTC4, //! 2-bit PVRTC-compressed texture: PVRTC2 @@ -100,22 +98,14 @@ typedef enum { //! Default texture format: RGBA8888 kCCTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_RGBA8888, - // backward compatibility stuff - kTexture2DPixelFormat_Automatic = kCCTexture2DPixelFormat_Automatic, - kTexture2DPixelFormat_RGBA8888 = kCCTexture2DPixelFormat_RGBA8888, - kTexture2DPixelFormat_RGB565 = kCCTexture2DPixelFormat_RGB565, - kTexture2DPixelFormat_A8 = kCCTexture2DPixelFormat_A8, - kTexture2DPixelFormat_RGBA4444 = kCCTexture2DPixelFormat_RGBA4444, - kTexture2DPixelFormat_RGB5A1 = kCCTexture2DPixelFormat_RGB5A1, - kTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_Default - } CCTexture2DPixelFormat; -//CLASS INTERFACES: + +@class CCGLProgram; /** CCTexture2D class. * This class allows to easily create OpenGL 2D textures from images, text or raw data. - * The created CCTexture2D object will always have power-of-two dimensions. + * The created CCTexture2D object will always have power-of-two dimensions. * Depending on how you create the CCTexture2D object, the actual image area of the texture might be smaller than the texture dimensions i.e. "contentSize" != (pixelsWide, pixelsHigh) and (maxS, maxT) != (1.0, 1.0). * Be aware that the content of the generated textures will be upside-down! */ @@ -129,6 +119,15 @@ typedef enum { GLfloat maxS_, maxT_; BOOL hasPremultipliedAlpha_; + BOOL hasMipmaps_; + +#ifdef __CC_PLATFORM_IOS + ccResolutionType resolutionType_; +#endif + + // needed for drawAtRect, drawInPoint + CCGLProgram *shaderProgram_; + } /** Intializes with a texture2d with data */ - (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size; @@ -157,8 +156,25 @@ typedef enum { /** whether or not the texture has their Alpha premultiplied */ @property(nonatomic,readonly) BOOL hasPremultipliedAlpha; +/** shader program used by drawAtPoint and drawInRect */ +@property(nonatomic,readwrite,retain) CCGLProgram *shaderProgram; + +#ifdef __CC_PLATFORM_IOS +/** Returns the resolution type of the texture. + Is it a RetinaDisplay texture, an iPad texture or an standard texture ? + Only valid on iOS. Not valid on OS X. + + Should be a readonly property. It is readwrite as a hack. + + @since v1.1 + */ +@property (nonatomic, readwrite) ccResolutionType resolutionType; +#endif + /** returns the content size of the texture in points */ -(CGSize) contentSize; + + @end /** @@ -177,11 +193,11 @@ Extensions to make it easy to create a CCTexture2D object from an image file. Note that RGBA type textures will have their alpha premultiplied - use the blending mode (GL_ONE, GL_ONE_MINUS_SRC_ALPHA). */ @interface CCTexture2D (Image) -/** Initializes a texture from a UIImage object */ -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -- (id) initWithImage:(UIImage *)uiImage; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) -- (id) initWithImage:(CGImageRef)cgImage; +/** Initializes a texture from a CGImage object */ +#ifdef __CC_PLATFORM_IOS +- (id) initWithCGImage:(CGImageRef)cgImage resolutionType:(ccResolutionType)resolution; +#elif defined(__CC_PLATFORM_MAC) +- (id) initWithCGImage:(CGImageRef)cgImage; #endif @end @@ -196,9 +212,9 @@ Note that the generated textures are of type A8 - use the blending mode (GL_SRC_ - Mac: Only NSLineBreakByWordWrapping is supported. @since v1.0 */ -- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment) vertAlignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size; /** Initializes a texture from a string with dimensions, alignment, font name and font size */ -- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size; +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)alignment vAlignment:(CCVerticalTextAlignment) vertAlignment fontName:(NSString*)name fontSize:(CGFloat)size; /** Initializes a texture from a string with font name and font size */ - (id) initWithString:(NSString*)string fontName:(NSString*)name fontSize:(CGFloat)size; @end @@ -209,15 +225,8 @@ Note that the generated textures are of type A8 - use the blending mode (GL_SRC_ Note that the generated textures don't have their alpha premultiplied - use the blending mode (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). */ @interface CCTexture2D (PVRSupport) -/** Initializes a texture from a PVR Texture Compressed (PVRTC) buffer - * - * IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version. - */ -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED --(id) initWithPVRTCData: (const void*)data level:(int)level bpp:(int)bpp hasAlpha:(BOOL)hasAlpha length:(int)length pixelFormat:(CCTexture2DPixelFormat)pixelFormat; -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED /** Initializes a texture from a PVR file. - + Supported PVR formats: - BGRA 8888 - RGBA 8888 @@ -229,21 +238,21 @@ Note that the generated textures are of type A8 - use the blending mode (GL_SRC_ - AI 8 - PVRTC 2BPP - PVRTC 4BPP - + By default PVR images are treated as if they alpha channel is NOT premultiplied. You can override this behavior with this class method: - PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied; - + IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version. - + */ -(id) initWithPVRFile: (NSString*) file; /** treats (or not) PVR files as if they have alpha premultiplied. Since it is impossible to know at runtime if the PVR images have the alpha channel premultiplied, it is possible load them as if they have (or not) the alpha channel premultiplied. - + By default it is disabled. - + @since v0.99.5 */ +(void) PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied; @@ -262,6 +271,9 @@ typedef struct _ccTexParams { @interface CCTexture2D (GLFilter) /** sets the min filter, mag filter, wrap s and wrap t texture parameters. If the texture size is NPOT (non power of 2), then in can only use GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T}. + + @warning Calling this method could allocate additional texture memory. + @since v0.8 */ -(void) setTexParameters: (ccTexParams*) texParams; @@ -269,6 +281,8 @@ typedef struct _ccTexParams { /** sets antialias texture parameters: - GL_TEXTURE_MIN_FILTER = GL_LINEAR - GL_TEXTURE_MAG_FILTER = GL_LINEAR + + @warning Calling this method could allocate additional texture memory. @since v0.8 */ @@ -277,7 +291,9 @@ typedef struct _ccTexParams { /** sets alias texture parameters: - GL_TEXTURE_MIN_FILTER = GL_NEAREST - GL_TEXTURE_MAG_FILTER = GL_NEAREST - + + @warning Calling this method could allocate additional texture memory. + @since v0.8 */ - (void) setAliasTexParameters; @@ -293,20 +309,21 @@ typedef struct _ccTexParams { @end @interface CCTexture2D (PixelFormat) -/** sets the default pixel format for UIImages that contains alpha channel. - If the UIImage contains alpha channel, then the options are: +/** sets the default pixel format for CGImages that contains alpha channel. + If the CGImage contains alpha channel, then the options are: - generate 32-bit textures: kCCTexture2DPixelFormat_RGBA8888 (default one) - generate 16-bit textures: kCCTexture2DPixelFormat_RGBA4444 - generate 16-bit textures: kCCTexture2DPixelFormat_RGB5A1 - - generate 16-bit textures: kCCTexture2DPixelFormat_RGB565 + - generate 24-bit textures: kCCTexture2DPixelFormat_RGB888 (no alpha) + - generate 16-bit textures: kCCTexture2DPixelFormat_RGB565 (no alpha) - generate 8-bit textures: kCCTexture2DPixelFormat_A8 (only use it if you use just 1 color) How does it work ? - If the image is an RGBA (with Alpha) then the default pixel format will be used (it can be a 8-bit, 16-bit or 32-bit texture) - - If the image is an RGB (without Alpha) then an RGB565 texture will be used (16-bit texture) - - This parameter is not valid for PVR images. - + - If the image is an RGB (without Alpha) then: If the default pixel format is RGBA8888 then a RGBA8888 (32-bit) will be used. Otherwise a RGB565 (16-bit texture) will be used. + + This parameter is not valid for PVR / PVR.CCZ images. + @since v0.8 */ +(void) setDefaultAlphaPixelFormat:(CCTexture2DPixelFormat)format; @@ -320,6 +337,18 @@ typedef struct _ccTexParams { @since v1.0 */ -(NSUInteger) bitsPerPixelForFormat; + +/** returns the pixel format in a NSString. + @since v2.0 + */ +-(NSString*) stringForFormat; + + +/** Helper functions that returns bits per pixels for a given format. + @since v2.0 + */ ++(NSUInteger) bitsPerPixelForFormat:(CCTexture2DPixelFormat)format; + @end diff --git a/cocos2d/cocos2d/CCTexture2D.m b/cocos2d/cocos2d/CCTexture2D.m old mode 100644 new mode 100755 index afa64e2..ad9c4fd --- a/cocos2d/cocos2d/CCTexture2D.m +++ b/cocos2d/cocos2d/CCTexture2D.m @@ -1,97 +1,98 @@ /* -===== IMPORTANT ===== - -This is sample code demonstrating API, technology or techniques in development. -Although this sample code has been reviewed for technical accuracy, it is not -final. Apple is supplying this information to help you plan for the adoption of -the technologies and programming interfaces described herein. This information -is subject to change, and software implemented based on this sample code should -be tested with final operating system software and final documentation. Newer -versions of this sample code may be provided with future seeds of the API or -technology. For information about updates to this and other developer -documentation, view the New & Updated sidebars in subsequent documentationd -seeds. - -===================== - -File: Texture2D.m -Abstract: Creates OpenGL 2D textures from images or text. - -Version: 1.6 - -Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. -("Apple") in consideration of your agreement to the following terms, and your -use, installation, modification or redistribution of this Apple software -constitutes acceptance of these terms. If you do not agree with these terms, -please do not use, install, modify or redistribute this Apple software. - -In consideration of your agreement to abide by the following terms, and subject -to these terms, Apple grants you a personal, non-exclusive license, under -Apple's copyrights in this original Apple software (the "Apple Software"), to -use, reproduce, modify and redistribute the Apple Software, with or without -modifications, in source and/or binary forms; provided that if you redistribute -the Apple Software in its entirety and without modifications, you must retain -this notice and the following text and disclaimers in all such redistributions -of the Apple Software. -Neither the name, trademarks, service marks or logos of Apple Inc. may be used -to endorse or promote products derived from the Apple Software without specific -prior written permission from Apple. Except as expressly stated in this notice, -no other rights or licenses, express or implied, are granted by Apple herein, -including but not limited to any patent rights that may be infringed by your -derivative works or by other works in which the Apple Software may be -incorporated. - -The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO -WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED -WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN -COMBINATION WITH YOUR PRODUCTS. - -IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR -DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF -CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF -APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Copyright (C) 2008 Apple Inc. All Rights Reserved. - -*/ + ===== IMPORTANT ===== + + This is sample code demonstrating API, technology or techniques in development. + Although this sample code has been reviewed for technical accuracy, it is not + final. Apple is supplying this information to help you plan for the adoption of + the technologies and programming interfaces described herein. This information + is subject to change, and software implemented based on this sample code should + be tested with final operating system software and final documentation. Newer + versions of this sample code may be provided with future seeds of the API or + technology. For information about updates to this and other developer + documentation, view the New & Updated sidebars in subsequent documentationd + seeds. + + ===================== + + File: Texture2D.m + Abstract: Creates OpenGL 2D textures from images or text. + + Version: 1.6 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under + Apple's copyrights in this original Apple software (the "Apple Software"), to + use, reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions + of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may be used + to endorse or promote products derived from the Apple Software without specific + prior written permission from Apple. Except as expressly stated in this notice, + no other rights or licenses, express or implied, are granted by Apple herein, + including but not limited to any patent rights that may be infringed by your + derivative works or by other works in which the Apple Software may be + incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2008 Apple Inc. All Rights Reserved. + + */ /* * Support for RGBA_4_4_4_4 and RGBA_5_5_5_1 was copied from: * https://devforums.apple.com/message/37855#37855 by a1studmuffin */ - -#import +/* + * Added many additions for cocos2d + */ #import "Platforms/CCGL.h" #import "Platforms/CCNS.h" - #import "CCTexture2D.h" #import "ccConfig.h" #import "ccMacros.h" #import "CCConfiguration.h" -#import "Support/ccUtils.h" #import "CCTexturePVR.h" +#import "CCGLProgram.h" +#import "ccGLStateCache.h" +#import "CCShaderCache.h" +#import "CCDirector.h" -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && CC_FONT_LABEL_SUPPORT -// FontLabel support -#import "FontManager.h" -#import "FontLabelStringDrawing.h" -#endif// CC_FONT_LABEL_SUPPORT +#import "Support/ccUtils.h" +#import "Support/CCFileUtils.h" +#import "ccDeprecated.h" -// For Labels use 16-bit textures on iPhone 3GS / iPads since A8 textures are very slow -#if (defined(__ARM_NEON__) || TARGET_IPHONE_SIMULATOR) && CC_USE_LA88_LABELS_ON_NEON_ARCH -#define USE_TEXT_WITH_A8_TEXTURES 0 + +#if CC_USE_LA88_LABELS +#define LABEL_PIXEL_FORMAT kCCTexture2DPixelFormat_AI88 #else -#define USE_TEXT_WITH_A8_TEXTURES 1 +#define LABEL_PIXEL_FORMAT kCCTexture2DPixelFormat_A8 #endif //CLASS IMPLEMENTATIONS: @@ -108,18 +109,34 @@ @implementation CCTexture2D @synthesize contentSizeInPixels = size_, pixelFormat = format_, pixelsWide = width_, pixelsHigh = height_, name = name_, maxS = maxS_, maxT = maxT_; @synthesize hasPremultipliedAlpha = hasPremultipliedAlpha_; +@synthesize shaderProgram = shaderProgram_; + +#ifdef __CC_PLATFORM_IOS +@synthesize resolutionType = resolutionType_; +#endif + - (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size { if((self = [super init])) { - glPixelStorei(GL_UNPACK_ALIGNMENT,1); - glGenTextures(1, &name_); - glBindTexture(GL_TEXTURE_2D, name_); + + + // XXX: 32 bits or POT textures uses UNPACK of 4 (is this correct ??? ) + if( pixelFormat == kCCTexture2DPixelFormat_RGBA8888 || ( ccNextPOT(width)==width && ccNextPOT(height)==height) ) + glPixelStorei(GL_UNPACK_ALIGNMENT,4); + else + glPixelStorei(GL_UNPACK_ALIGNMENT,1); - [self setAntiAliasTexParameters]; + glGenTextures(1, &name_); + ccGLBindTexture2D( name_ ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + // Specify OpenGL texture image - + switch(pixelFormat) { case kCCTexture2DPixelFormat_RGBA8888: @@ -134,6 +151,9 @@ - (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelF case kCCTexture2DPixelFormat_RGB565: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei) width, (GLsizei) height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); break; + case kCCTexture2DPixelFormat_RGB888: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei) width, (GLsizei) height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + break; case kCCTexture2DPixelFormat_AI88: glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, (GLsizei) width, (GLsizei) height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data); break; @@ -142,7 +162,7 @@ - (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelF break; default: [NSException raise:NSInternalInconsistencyException format:@""]; - + } size_ = size; @@ -153,7 +173,14 @@ - (id) initWithData:(const void*)data pixelFormat:(CCTexture2DPixelFormat)pixelF maxT_ = size.height / (float)height; hasPremultipliedAlpha_ = NO; - } + + hasMipmaps_ = NO; + +#ifdef __CC_PLATFORM_IOS + resolutionType_ = kCCResolutionUnknown; +#endif + self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTexture]; + } return self; } @@ -172,15 +199,18 @@ - (void*) keepData:(void*)data length:(NSUInteger)length - (void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); - if(name_) - glDeleteTextures(1, &name_); - + + [shaderProgram_ release]; + + if( name_ ) + ccGLDeleteTexture( name_ ); + [super dealloc]; } - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | Name = %i | Dimensions = %ix%i | Coordinates = (%.2f, %.2f)>", [self class], self, name_, width_, height_, maxS_, maxT_]; + return [NSString stringWithFormat:@"<%@ = %p | Name = %i | Dimensions = %lux%lu | Pixel format = %@ | Coordinates = (%.2f, %.2f)>", [self class], self, name_, (unsigned long)width_, (unsigned long)height_, [self stringForFormat], maxS_, maxT_]; } -(CGSize) contentSize @@ -188,24 +218,26 @@ -(CGSize) contentSize CGSize ret; ret.width = size_.width / CC_CONTENT_SCALE_FACTOR(); ret.height = size_.height / CC_CONTENT_SCALE_FACTOR(); - + return ret; } + @end #pragma mark - #pragma mark CCTexture2D - Image @implementation CCTexture2D (Image) -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -- (id) initWithImage:(UIImage *)uiImage -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) -- (id) initWithImage:(CGImageRef)CGImage + +#ifdef __CC_PLATFORM_IOS +- (id) initWithCGImage:(CGImageRef)cgImage resolutionType:(ccResolutionType)resolution +#elif defined(__CC_PLATFORM_MAC) +- (id) initWithCGImage:(CGImageRef)cgImage #endif { - NSUInteger POTWide, POTHigh; + NSUInteger textureWidth, textureHeight; CGContextRef context = nil; - void* data = nil;; + void* data = nil; CGColorSpaceRef colorSpace; void* tempData; unsigned int* inPixel32; @@ -214,150 +246,218 @@ - (id) initWithImage:(CGImageRef)CGImage CGImageAlphaInfo info; CGSize imageSize; CCTexture2DPixelFormat pixelFormat; - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - CGImageRef CGImage = uiImage.CGImage; -#endif - - if(CGImage == NULL) { - CCLOG(@"cocos2d: CCTexture2D. Can't create Texture. UIImage is nil"); + + if(cgImage == NULL) { + CCLOG(@"cocos2d: CCTexture2D. Can't create Texture. cgImage is nil"); [self release]; return nil; } - + CCConfiguration *conf = [CCConfiguration sharedConfiguration]; -#if CC_TEXTURE_NPOT_SUPPORT - if( [conf supportsNPOT] ) { - POTWide = CGImageGetWidth(CGImage); - POTHigh = CGImageGetHeight(CGImage); + info = CGImageGetAlphaInfo(cgImage); - } else -#endif - { - POTWide = ccNextPOT(CGImageGetWidth(CGImage)); - POTHigh = ccNextPOT(CGImageGetHeight(CGImage)); - } - - NSUInteger maxTextureSize = [conf maxTextureSize]; - if( POTHigh > maxTextureSize || POTWide > maxTextureSize ) { - CCLOG(@"cocos2d: WARNING: Image (%lu x %lu) is bigger than the supported %ld x %ld", - (long)POTWide, (long)POTHigh, - (long)maxTextureSize, (long)maxTextureSize); - [self release]; - return nil; - } +#ifdef __CC_PLATFORM_IOS + + // Bug #886. It is present on iOS 4 only + unsigned int version = [conf OSVersion]; + if( version >= kCCiOSVersion_4_0 && version < kCCiOSVersion_5_0 ) + hasAlpha = ((info == kCGImageAlphaNoneSkipLast) || (info == kCGImageAlphaPremultipliedLast) || (info == kCGImageAlphaPremultipliedFirst) || (info == kCGImageAlphaLast) || (info == kCGImageAlphaFirst) ? YES : NO); + else +#endif // __CC_PLATFORM_IOS - info = CGImageGetAlphaInfo(CGImage); hasAlpha = ((info == kCGImageAlphaPremultipliedLast) || (info == kCGImageAlphaPremultipliedFirst) || (info == kCGImageAlphaLast) || (info == kCGImageAlphaFirst) ? YES : NO); - - size_t bpp = CGImageGetBitsPerComponent(CGImage); - colorSpace = CGImageGetColorSpace(CGImage); + + colorSpace = CGImageGetColorSpace(cgImage); if(colorSpace) { - if(hasAlpha || bpp >= 8) + if( hasAlpha ) { pixelFormat = defaultAlphaPixelFormat_; - else { + info = kCGImageAlphaPremultipliedLast; + } + else + { + info = kCGImageAlphaNoneSkipLast; + + // Use RGBA8888 if default is RGBA8888, otherwise use RGB565. + // DO NOT USE RGB888 since it is the same as RGBA8888, but it is more expensive to create it + if( defaultAlphaPixelFormat_ == kCCTexture2DPixelFormat_RGBA8888 ) + pixelFormat = kCCTexture2DPixelFormat_RGBA8888; + else + pixelFormat = kCCTexture2DPixelFormat_RGB565; + CCLOG(@"cocos2d: CCTexture2D: Using RGB565 texture since image has no alpha"); - pixelFormat = kCCTexture2DPixelFormat_RGB565; + } } else { // NOTE: No colorspace means a mask image CCLOG(@"cocos2d: CCTexture2D: Using A8 texture since image is a mask"); pixelFormat = kCCTexture2DPixelFormat_A8; } - - imageSize = CGSizeMake(CGImageGetWidth(CGImage), CGImageGetHeight(CGImage)); + + if( ! [conf supportsNPOT] ) + { + textureWidth = ccNextPOT(CGImageGetWidth(cgImage)); + textureHeight = ccNextPOT(CGImageGetHeight(cgImage)); + } + else + { + textureWidth = CGImageGetWidth(cgImage); + textureHeight = CGImageGetHeight(cgImage); + } + +#ifdef __CC_PLATFORM_IOS + + // iOS 5 BUG: + // If width is not word aligned, convert it to word aligned. + // http://www.cocos2d-iphone.org/forum/topic/31092 + if( [conf OSVersion] >= kCCiOSVersion_5_0 ) + { + + NSUInteger bpp = [[self class] bitsPerPixelForFormat:pixelFormat]; + NSUInteger bytes = textureWidth * bpp / 8; + + // XXX: Should it be 4 or sizeof(int) ?? + NSUInteger mod = bytes % 4; + + // Not word aligned ? + if( mod != 0 ) { + + NSUInteger neededBytes = (4 - mod ) / (bpp/8); + + CCLOGWARN(@"cocos2d: WARNING converting size=(%d,%d) to size=(%d,%d) due to iOS 5.x memory BUG. See: http://www.cocos2d-iphone.org/forum/topic/31092", textureWidth, textureHeight, textureWidth + neededBytes, textureHeight ); + textureWidth = textureWidth + neededBytes; + } + } +#endif // IOS + + NSUInteger maxTextureSize = [conf maxTextureSize]; + if( textureHeight > maxTextureSize || textureWidth > maxTextureSize ) { + CCLOGWARN(@"cocos2d: WARNING: Image (%lu x %lu) is bigger than the supported %ld x %ld", + (long)textureWidth, (long)textureHeight, + (long)maxTextureSize, (long)maxTextureSize); + [self release]; + return nil; + } + + imageSize = CGSizeMake(CGImageGetWidth(cgImage), CGImageGetHeight(cgImage)); // Create the bitmap graphics context - - switch(pixelFormat) { + + switch(pixelFormat) { case kCCTexture2DPixelFormat_RGBA8888: case kCCTexture2DPixelFormat_RGBA4444: case kCCTexture2DPixelFormat_RGB5A1: - colorSpace = CGColorSpaceCreateDeviceRGB(); - data = malloc(POTHigh * POTWide * 4); - info = hasAlpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNoneSkipLast; -// info = kCGImageAlphaPremultipliedLast; // issue #886. This patch breaks BMP images. - context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big); - CGColorSpaceRelease(colorSpace); - break; - case kCCTexture2DPixelFormat_RGB565: + case kCCTexture2DPixelFormat_RGB888: colorSpace = CGColorSpaceCreateDeviceRGB(); - data = malloc(POTHigh * POTWide * 4); - info = kCGImageAlphaNoneSkipLast; - context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, 4 * POTWide, colorSpace, info | kCGBitmapByteOrder32Big); + data = malloc(textureHeight * textureWidth * 4); +// info = hasAlpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNoneSkipLast; +// info = kCGImageAlphaPremultipliedLast; // issue #886. This patch breaks BMP images. + context = CGBitmapContextCreate(data, textureWidth, textureHeight, 8, 4 * textureWidth, colorSpace, info | kCGBitmapByteOrder32Big); CGColorSpaceRelease(colorSpace); break; case kCCTexture2DPixelFormat_A8: - data = malloc(POTHigh * POTWide); - info = kCGImageAlphaOnly; - context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, POTWide, NULL, info); - break; + data = malloc(textureHeight * textureWidth); + info = kCGImageAlphaOnly; + context = CGBitmapContextCreate(data, textureWidth, textureHeight, 8, textureWidth, NULL, info); + break; default: [NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"]; } - - - CGContextClearRect(context, CGRectMake(0, 0, POTWide, POTHigh)); - CGContextTranslateCTM(context, 0, POTHigh - imageSize.height); - CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(CGImage), CGImageGetHeight(CGImage)), CGImage); - + + + CGContextClearRect(context, CGRectMake(0, 0, textureWidth, textureHeight)); + CGContextTranslateCTM(context, 0, textureHeight - imageSize.height); + CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(cgImage), CGImageGetHeight(cgImage)), cgImage); + // Repack the pixel data into the right format - + if(pixelFormat == kCCTexture2DPixelFormat_RGB565) { //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB" - tempData = malloc(POTHigh * POTWide * 2); + tempData = malloc(textureHeight * textureWidth * 2); inPixel32 = (unsigned int*)data; outPixel16 = (unsigned short*)tempData; - for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32) + for(unsigned int i = 0; i < textureWidth * textureHeight; ++i, ++inPixel32) *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0); free(data); data = tempData; + + } + + else if(pixelFormat == kCCTexture2DPixelFormat_RGB888) { + //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRRRRGGGGGGGGBBBBBBB" + tempData = malloc(textureHeight * textureWidth * 3); + char *inData = (char*)data; + char *outData = (char*)tempData; + int j=0; + for(unsigned int i = 0; i < textureWidth * textureHeight *4; i++) { + outData[j++] = inData[i++]; + outData[j++] = inData[i++]; + outData[j++] = inData[i++]; + } + free(data); + data = tempData; } + else if (pixelFormat == kCCTexture2DPixelFormat_RGBA4444) { //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA" - tempData = malloc(POTHigh * POTWide * 2); + tempData = malloc(textureHeight * textureWidth * 2); inPixel32 = (unsigned int*)data; outPixel16 = (unsigned short*)tempData; - for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32) - *outPixel16++ = + for(unsigned int i = 0; i < textureWidth * textureHeight; ++i, ++inPixel32) + *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | // R ((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | // G ((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | // B ((((*inPixel32 >> 24) & 0xFF) >> 4) << 0); // A - - + + free(data); data = tempData; - + } else if (pixelFormat == kCCTexture2DPixelFormat_RGB5A1) { //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA" - tempData = malloc(POTHigh * POTWide * 2); + /* + Here was a bug. + When you convert RGBA8888 texture to RGB5A1 texture and then render it on black background, you'll see a "ghost" image as if the texture is still RGBA8888. + On background lighter than the pixel color this effect disappers. + This happens because the old convertion function doesn't premultiply old RGB with new A. + As Result = sourceRGB + destination*(1-source A), then + if Destination = 0000, then Result = source. Here comes the ghost! + We need to check new alpha value first (it may be 1 or 0) and depending on it whether convert RGB values or just set pixel to 0 + */ + tempData = malloc(textureHeight * textureWidth * 2); inPixel32 = (unsigned int*)data; outPixel16 = (unsigned short*)tempData; - for(unsigned int i = 0; i < POTWide * POTHigh; ++i, ++inPixel32) - *outPixel16++ = - ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R - ((((*inPixel32 >> 8) & 0xFF) >> 3) << 6) | // G - ((((*inPixel32 >> 16) & 0xFF) >> 3) << 1) | // B - ((((*inPixel32 >> 24) & 0xFF) >> 7) << 0); // A - + for(unsigned int i = 0; i < textureWidth * textureHeight; ++i, ++inPixel32) { + if ((*inPixel32 >> 31))// A can be 1 or 0 + *outPixel16++ = + ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R + ((((*inPixel32 >> 8) & 0xFF) >> 3) << 6) | // G + ((((*inPixel32 >> 16) & 0xFF) >> 3) << 1) | // B + 1; // A + else + *outPixel16++ = 0; + } free(data); data = tempData; } - self = [self initWithData:data pixelFormat:pixelFormat pixelsWide:POTWide pixelsHigh:POTHigh contentSize:imageSize]; - + self = [self initWithData:data pixelFormat:pixelFormat pixelsWide:textureWidth pixelsHigh:textureHeight contentSize:imageSize]; + // should be after calling super init hasPremultipliedAlpha_ = (info == kCGImageAlphaPremultipliedLast || info == kCGImageAlphaPremultipliedFirst); - + CGContextRelease(context); [self releaseData:data]; - + +#ifdef __CC_PLATFORM_IOS + resolutionType_ = resolution; +#endif + return self; } @end @@ -367,217 +467,247 @@ - (id) initWithImage:(CGImageRef)CGImage @implementation CCTexture2D (Text) -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS -- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode font:(id)uifont +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)hAlignment vAlignment:(CCVerticalTextAlignment) vAlignment lineBreakMode:(CCLineBreakMode)lineBreakMode font:(UIFont*)uifont { NSAssert( uifont, @"Invalid font"); - - NSUInteger POTWide = ccNextPOT(dimensions.width); - NSUInteger POTHigh = ccNextPOT(dimensions.height); + + // MUST have the same order declared on ccTypes + NSInteger linebreaks[] = {UILineBreakModeWordWrap, UILineBreakModeCharacterWrap, UILineBreakModeClip, UILineBreakModeHeadTruncation, UILineBreakModeTailTruncation, UILineBreakModeMiddleTruncation}; + + NSUInteger textureWidth = ccNextPOT(dimensions.width); + NSUInteger textureHeight = ccNextPOT(dimensions.height); unsigned char* data; - + CGContextRef context; CGColorSpaceRef colorSpace; -#if USE_TEXT_WITH_A8_TEXTURES - data = calloc(POTHigh, POTWide); +#if CC_USE_LA88_LABELS + data = calloc(textureHeight, textureWidth * 2); #else - data = calloc(POTHigh, POTWide * 2); + data = calloc(textureHeight, textureWidth); #endif colorSpace = CGColorSpaceCreateDeviceGray(); - context = CGBitmapContextCreate(data, POTWide, POTHigh, 8, POTWide, colorSpace, kCGImageAlphaNone); + context = CGBitmapContextCreate(data, textureWidth, textureHeight, 8, textureWidth, colorSpace, kCGImageAlphaNone); CGColorSpaceRelease(colorSpace); - + if( ! context ) { free(data); [self release]; return nil; } - + CGContextSetGrayFillColor(context, 1.0f, 1.0f); - CGContextTranslateCTM(context, 0.0f, POTHigh); + CGContextTranslateCTM(context, 0.0f, textureHeight); CGContextScaleCTM(context, 1.0f, -1.0f); //NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential - + UIGraphicsPushContext(context); - // normal fonts - if( [uifont isKindOfClass:[UIFont class] ] ) - [string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withFont:uifont lineBreakMode:lineBreakMode alignment:alignment]; - -#if CC_FONT_LABEL_SUPPORT - else // ZFont class - [string drawInRect:CGRectMake(0, 0, dimensions.width, dimensions.height) withZFont:uifont lineBreakMode:lineBreakMode alignment:alignment]; -#endif - + CGRect drawArea; + if(vAlignment == kCCVerticalTextAlignmentTop) + { + drawArea = CGRectMake(0, 0, dimensions.width, dimensions.height); + } + else + { + CGSize drawSize = [string sizeWithFont:uifont constrainedToSize:dimensions lineBreakMode:linebreaks[lineBreakMode] ]; + + if(vAlignment == kCCVerticalTextAlignmentBottom) + { + drawArea = CGRectMake(0, dimensions.height - drawSize.height, dimensions.width, drawSize.height); + } + else // kCCVerticalTextAlignmentCenter + { + drawArea = CGRectMake(0, (dimensions.height - drawSize.height) / 2, dimensions.width, drawSize.height); + } + } + + // must follow the same order of CCTextureAligment + NSUInteger alignments[] = { UITextAlignmentLeft, UITextAlignmentCenter, UITextAlignmentRight }; + + [string drawInRect:drawArea withFont:uifont lineBreakMode:linebreaks[lineBreakMode] alignment:alignments[hAlignment]]; + UIGraphicsPopContext(); - -#if USE_TEXT_WITH_A8_TEXTURES - self = [self initWithData:data pixelFormat:kCCTexture2DPixelFormat_A8 pixelsWide:POTWide pixelsHigh:POTHigh contentSize:dimensions]; -#else // ! USE_TEXT_WITH_A8_TEXTURES - NSUInteger textureSize = POTWide*POTHigh; +#if CC_USE_LA88_LABELS + NSUInteger textureSize = textureWidth*textureHeight; unsigned short *la88_data = (unsigned short*)data; for(int i = textureSize-1; i>=0; i--) //Convert A8 to AI88 la88_data[i] = (data[i] << 8) | 0xff; - - self = [self initWithData:data pixelFormat:kCCTexture2DPixelFormat_AI88 pixelsWide:POTWide pixelsHigh:POTHigh contentSize:dimensions]; -#endif // ! USE_TEXT_WITH_A8_TEXTURES + +#endif + + self = [self initWithData:data pixelFormat:LABEL_PIXEL_FORMAT pixelsWide:textureWidth pixelsHigh:textureHeight contentSize:dimensions]; CGContextRelease(context); [self releaseData:data]; - + return self; } - -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) - -- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment attributedString:(NSAttributedString*)stringWithAttributes -{ - NSAssert( stringWithAttributes, @"Invalid stringWithAttributes"); +#elif defined(__CC_PLATFORM_MAC) - NSUInteger POTWide = ccNextPOT(dimensions.width); - NSUInteger POTHigh = ccNextPOT(dimensions.height); - unsigned char* data; - - NSSize realDimensions = [stringWithAttributes size]; +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions hAlignment:(CCTextAlignment)hAlignment vAlignment:(CCVerticalTextAlignment)vAlignment attributedString:(NSAttributedString*)stringWithAttributes +{ + NSAssert(stringWithAttributes, @"Invalid stringWithAttributes"); - //Alignment - float xPadding = 0; - + // get nearest power of two + NSSize POTSize = NSMakeSize(ccNextPOT(dimensions.width), ccNextPOT(dimensions.height)); + + // Get actual rendered dimensions + NSRect boundingRect = [stringWithAttributes boundingRectWithSize:NSSizeFromCGSize(dimensions) options:NSStringDrawingUsesLineFragmentOrigin]; + // Mac crashes if the width or height is 0 - if( realDimensions.width > 0 && realDimensions.height > 0 ) { - switch (alignment) { - case CCTextAlignmentLeft: xPadding = 0; break; - case CCTextAlignmentCenter: xPadding = (dimensions.width-realDimensions.width)/2.0f; break; - case CCTextAlignmentRight: xPadding = dimensions.width-realDimensions.width; break; + if( boundingRect.size.width > 0 && boundingRect.size.height > 0 ) { + + CGSize offset = CGSizeMake(0, POTSize.height - dimensions.height); + + //Alignment + switch (hAlignment) { + case kCCTextAlignmentLeft: break; + case kCCTextAlignmentCenter: offset.width = (dimensions.width-boundingRect.size.width)/2.0f; break; + case kCCTextAlignmentRight: offset.width = dimensions.width-boundingRect.size.width; break; default: break; } + switch (vAlignment) { + case kCCVerticalTextAlignmentTop: offset.height += dimensions.height - boundingRect.size.height; break; + case kCCVerticalTextAlignmentCenter: offset.height += (dimensions.height - boundingRect.size.height) / 2; break; + case kCCVerticalTextAlignmentBottom: break; + default: break; + } + + CGRect drawArea = CGRectMake(offset.width, offset.height, boundingRect.size.width, boundingRect.size.height); //Disable antialias [[NSGraphicsContext currentContext] setShouldAntialias:NO]; - NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(POTWide, POTHigh)]; + NSImage *image = [[NSImage alloc] initWithSize:POTSize]; [image lockFocus]; - [stringWithAttributes drawAtPoint:NSMakePoint(xPadding, POTHigh-dimensions.height)]; // draw at offset position + [stringWithAttributes drawWithRect:NSRectFromCGRect(drawArea) options:NSStringDrawingUsesLineFragmentOrigin]; - NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, POTWide, POTHigh)]; + NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect (0.0f, 0.0f, POTSize.width, POTSize.height)]; [image unlockFocus]; - - data = (unsigned char*) [bitmap bitmapData]; //Use the same buffer to improve the performance. - - NSUInteger textureSize = POTWide*POTHigh; - for(int i = 0; i 1 ); [pvr release]; - - [self setAntiAliasTexParameters]; + } else { - - CCLOG(@"cocos2d: Couldn't load PVR image: %@", file); + + CCLOG(@"cocos2d: Couldn't load PVR image: %@", relPath); [self release]; return nil; } +#ifdef __CC_PLATFORM_IOS + resolutionType_ = resolution; +#endif } return self; } @@ -671,42 +771,63 @@ +(void) PVRImagesHavePremultipliedAlpha:(BOOL)haveAlphaPremultiplied @implementation CCTexture2D (Drawing) -- (void) drawAtPoint:(CGPoint)point +- (void) drawAtPoint:(CGPoint)point { GLfloat coordinates[] = { 0.0f, maxT_, - maxS_, maxT_, - 0.0f, 0.0f, - maxS_, 0.0f }; + maxS_, maxT_, + 0.0f, 0.0f, + maxS_, 0.0f }; GLfloat width = (GLfloat)width_ * maxS_, - height = (GLfloat)height_ * maxT_; + height = (GLfloat)height_ * maxT_; + + GLfloat vertices[] = { point.x, point.y, + width + point.x, point.y, + point.x, height + point.y, + width + point.x, height + point.y }; + + ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); + [shaderProgram_ use]; + [shaderProgram_ setUniformForModelViewProjectionMatrix]; + + ccGLBindTexture2D( name_ ); + + + glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices); + glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, coordinates); + - GLfloat vertices[] = { point.x, point.y, 0.0f, - width + point.x, point.y, 0.0f, - point.x, height + point.y, 0.0f, - width + point.x, height + point.y, 0.0f }; - - glBindTexture(GL_TEXTURE_2D, name_); - glVertexPointer(3, GL_FLOAT, 0, vertices); - glTexCoordPointer(2, GL_FLOAT, 0, coordinates); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + CC_INCREMENT_GL_DRAWS(1); } - (void) drawInRect:(CGRect)rect { GLfloat coordinates[] = { 0.0f, maxT_, - maxS_, maxT_, - 0.0f, 0.0f, - maxS_, 0.0f }; - GLfloat vertices[] = { rect.origin.x, rect.origin.y, /*0.0f,*/ - rect.origin.x + rect.size.width, rect.origin.y, /*0.0f,*/ - rect.origin.x, rect.origin.y + rect.size.height, /*0.0f,*/ - rect.origin.x + rect.size.width, rect.origin.y + rect.size.height, /*0.0f*/ }; - - glBindTexture(GL_TEXTURE_2D, name_); - glVertexPointer(2, GL_FLOAT, 0, vertices); - glTexCoordPointer(2, GL_FLOAT, 0, coordinates); + maxS_, maxT_, + 0.0f, 0.0f, + maxS_, 0.0f }; + GLfloat vertices[] = { rect.origin.x, rect.origin.y, + rect.origin.x + rect.size.width, rect.origin.y, + rect.origin.x, rect.origin.y + rect.size.height, + rect.origin.x + rect.size.width, rect.origin.y + rect.size.height }; + + + [shaderProgram_ use]; + [shaderProgram_ setUniformForModelViewProjectionMatrix]; + + ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); + + ccGLBindTexture2D( name_ ); + + glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices); + glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, coordinates); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + CC_INCREMENT_GL_DRAWS(1); } @end @@ -723,32 +844,46 @@ @implementation CCTexture2D (GLFilter) -(void) generateMipmap { NSAssert( width_ == ccNextPOT(width_) && height_ == ccNextPOT(height_), @"Mimpap texture only works in POT textures"); - glBindTexture( GL_TEXTURE_2D, name_ ); - ccglGenerateMipmap(GL_TEXTURE_2D); + ccGLBindTexture2D( name_ ); + glGenerateMipmap(GL_TEXTURE_2D); + hasMipmaps_ = YES; } -(void) setTexParameters: (ccTexParams*) texParams { - NSAssert( (width_ == ccNextPOT(width_) && height_ == ccNextPOT(height_)) || - (texParams->wrapS == GL_CLAMP_TO_EDGE && texParams->wrapT == GL_CLAMP_TO_EDGE), - @"GL_CLAMP_TO_EDGE should be used in NPOT textures"); - glBindTexture( GL_TEXTURE_2D, name_ ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texParams->minFilter ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texParams->magFilter ); + NSAssert( (width_ == ccNextPOT(width_) || texParams->wrapS == GL_CLAMP_TO_EDGE) && + (height_ == ccNextPOT(height_) || texParams->wrapT == GL_CLAMP_TO_EDGE), + @"GL_CLAMP_TO_EDGE should be used in NPOT dimensions"); + + ccGLBindTexture2D( name_ ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texParams->minFilter ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texParams->magFilter ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParams->wrapS ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParams->wrapT ); } -(void) setAliasTexParameters { - ccTexParams texParams = { GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE }; - [self setTexParameters: &texParams]; + ccGLBindTexture2D( name_ ); + + if( ! hasMipmaps_ ) + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + else + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } -(void) setAntiAliasTexParameters { - ccTexParams texParams = { GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE }; - [self setTexParameters: &texParams]; + ccGLBindTexture2D( name_ ); + + if( ! hasMipmaps_ ) + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + else + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); } @end @@ -770,45 +905,97 @@ +(CCTexture2DPixelFormat) defaultAlphaPixelFormat return defaultAlphaPixelFormat_; } --(NSUInteger) bitsPerPixelForFormat ++(NSUInteger) bitsPerPixelForFormat:(CCTexture2DPixelFormat)format { NSUInteger ret=0; - - switch (format_) { + + switch (format) { case kCCTexture2DPixelFormat_RGBA8888: ret = 32; break; + case kCCTexture2DPixelFormat_RGB888: + // It is 32 and not 24, since its internal representation uses 32 bits. + ret = 32; + break; case kCCTexture2DPixelFormat_RGB565: ret = 16; break; - case kCCTexture2DPixelFormat_A8: - ret = 8; - break; case kCCTexture2DPixelFormat_RGBA4444: ret = 16; break; case kCCTexture2DPixelFormat_RGB5A1: ret = 16; break; - case kCCTexture2DPixelFormat_PVRTC4: - ret = 4; + case kCCTexture2DPixelFormat_AI88: + ret = 16; break; - case kCCTexture2DPixelFormat_PVRTC2: - ret = 2; + case kCCTexture2DPixelFormat_A8: + ret = 8; break; case kCCTexture2DPixelFormat_I8: ret = 8; break; - case kCCTexture2DPixelFormat_AI88: - ret = 16; + case kCCTexture2DPixelFormat_PVRTC4: + ret = 4; + break; + case kCCTexture2DPixelFormat_PVRTC2: + ret = 2; break; default: ret = -1; - NSAssert1(NO , @"bitsPerPixelForFormat: %ld, unrecognised pixel format", (long)format_); - CCLOG(@"bitsPerPixelForFormat: %ld, cannot give useful result", (long)format_); + NSAssert1(NO , @"bitsPerPixelForFormat: %ld, unrecognised pixel format", (long)format); + CCLOG(@"bitsPerPixelForFormat: %ld, cannot give useful result", (long)format); break; } return ret; } + +-(NSUInteger) bitsPerPixelForFormat +{ + return [[self class] bitsPerPixelForFormat:format_]; +} + +-(NSString*) stringForFormat +{ + + switch (format_) { + case kCCTexture2DPixelFormat_RGBA8888: + return @"RGBA8888"; + + case kCCTexture2DPixelFormat_RGB888: + return @"RGB888"; + + case kCCTexture2DPixelFormat_RGB565: + return @"RGB565"; + + case kCCTexture2DPixelFormat_RGBA4444: + return @"RGBA4444"; + + case kCCTexture2DPixelFormat_RGB5A1: + return @"RGB5A1"; + + case kCCTexture2DPixelFormat_AI88: + return @"AI88"; + + case kCCTexture2DPixelFormat_A8: + return @"A8"; + + case kCCTexture2DPixelFormat_I8: + return @"I8"; + + case kCCTexture2DPixelFormat_PVRTC4: + return @"PVRTC4"; + + case kCCTexture2DPixelFormat_PVRTC2: + return @"PVRTC2"; + + default: + NSAssert1(NO , @"stringForFormat: %ld, unrecognised pixel format", (long)format_); + CCLOG(@"stringForFormat: %ld, cannot give useful result", (long)format_); + break; + } + + return nil; +} @end diff --git a/cocos2d/cocos2d/CCTextureAtlas.h b/cocos2d/cocos2d/CCTextureAtlas.h old mode 100644 new mode 100755 index f70bb54..4a61485 --- a/cocos2d/cocos2d/CCTextureAtlas.h +++ b/cocos2d/cocos2d/CCTextureAtlas.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -38,7 +38,7 @@ * The TextureAtlas capacity can be increased or decreased in runtime * OpenGL component: V3F, C4B, T2F. The quads are rendered using an OpenGL ES VBO. - To render the quads using an interleaved vertex array list, you should modify the ccConfig.h file + To render the quads using an interleaved vertex array list, you should modify the ccConfig.h file */ @interface CCTextureAtlas : NSObject { @@ -47,10 +47,13 @@ ccV3F_C4B_T2F_Quad *quads_; // quads to be rendered GLushort *indices_; CCTexture2D *texture_; -#if CC_USES_VBO + GLuint buffersVBO_[2]; //0: vertex 1: indices - BOOL dirty_; //indicates whether or not the array buffer of the VBO needs to be updated -#endif // CC_USES_VBO + BOOL dirty_; //indicates whether or not the array buffer of the VBO needs to be updated + +#if CC_TEXTURE_ATLAS_USE_VAO + GLuint VAOname_; +#endif } /** quantity of quads that are going to be drawn */ @@ -75,13 +78,13 @@ -(id) initWithFile: (NSString*) file capacity:(NSUInteger)capacity; /** creates a TextureAtlas with a previously initialized Texture2D object, and - * with an initial capacity for n Quads. + * with an initial capacity for n Quads. * The TextureAtlas capacity can be increased in runtime. */ +(id) textureAtlasWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity; /** initializes a TextureAtlas with a previously initialized Texture2D object, and - * with an initial capacity for Quads. + * with an initial capacity for Quads. * The TextureAtlas capacity can be increased in runtime. * * WARNING: Do not reinitialize the TextureAtlas because it will leak memory (issue #706) @@ -100,6 +103,13 @@ */ -(void) insertQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index; +/** Inserts a c array of quads at a given index + index must be between 0 and the atlas capacity - 1 + this method doesn't enlarge the array when amount + index > totalQuads + @since v1.1 +*/ +-(void) insertQuads:(ccV3F_C4B_T2F_Quad*)quads atIndex:(NSUInteger)index amount:(NSUInteger)amount; + /** Removes the quad that is located at a certain index and inserts it at a new index This operation is faster than removing and inserting in a quad in 2 different steps @since v0.7.2 @@ -112,6 +122,11 @@ */ -(void) removeQuadAtIndex:(NSUInteger) index; +/** removes a amount of quads starting from index + @since 1.1 + */ +- (void) removeQuadsAtIndex:(NSUInteger) index amount:(NSUInteger) amount; + /** removes all Quads. The TextureAtlas capacity remains untouched. No memory is freed. The total number of quads to be drawn will be 0 @@ -126,16 +141,42 @@ */ -(BOOL) resizeCapacity: (NSUInteger) n; +/** + Used internally by CCParticleBatchNode + don't use this unless you know what you're doing + @since 1.1 +*/ +- (void) increaseTotalQuadsWith:(NSUInteger) amount; + +/** Moves an amount of quads from oldIndex at newIndex + @since v1.1 + */ +-(void) moveQuadsFromIndex:(NSUInteger)oldIndex amount:(NSUInteger) amount atIndex:(NSUInteger)newIndex; + +/** + Moves quads from index till totalQuads to the newIndex + Used internally by CCParticleBatchNode + This method doesn't enlarge the array if newIndex + quads to be moved > capacity + @since 1.1 +*/ +- (void) moveQuadsFromIndex:(NSUInteger) index to:(NSUInteger) newIndex; + +/** + Ensures that after a realloc quads are still empty + Used internally by CCParticleBatchNode + @since 1.1 +*/ +- (void) fillWithEmptyQuadsFromIndex:(NSUInteger) index amount:(NSUInteger) amount; /** draws n quads * n can't be greater than the capacity of the Atlas */ --(void) drawNumberOfQuads: (NSUInteger) n; +-(void) drawNumberOfQuads: (NSUInteger) n; /** draws n quads from an index (offset). n + start can't be greater than the capacity of the atlas - + @since v1.0 */ -(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) start; diff --git a/cocos2d/cocos2d/CCTextureAtlas.m b/cocos2d/cocos2d/CCTextureAtlas.m old mode 100644 new mode 100755 index 7c7df75..5e703bf --- a/cocos2d/cocos2d/CCTextureAtlas.m +++ b/cocos2d/cocos2d/CCTextureAtlas.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,16 +24,28 @@ * */ - // cocos2d #import "CCTextureAtlas.h" #import "ccMacros.h" #import "CCTexture2D.h" #import "CCTextureCache.h" +#import "CCGLProgram.h" +#import "ccGLStateCache.h" +#import "CCDirector.h" +#import "CCConfiguration.h" + +#import "Support/NSThread+performBlock.h" +#import "Support/OpenGL_Internal.h" +@interface CCTextureAtlas () +-(void) setupIndices; +-(void) mapBuffers; -@interface CCTextureAtlas (Private) --(void) initIndices; +#if CC_TEXTURE_ATLAS_USE_VAO +-(void) setupVBOandVAO; +#else +-(void) setupVBO; +#endif @end //According to some tests GL_TRIANGLE_STRIP is slower, MUCH slower. Probably I'm doing something very wrong @@ -77,7 +89,7 @@ -(id) initWithTexture:(CCTexture2D*)tex capacity:(NSUInteger)n capacity_ = n; totalQuads_ = 0; - + // retained in property self.texture = tex; @@ -93,24 +105,28 @@ -(id) initWithTexture:(CCTexture2D*)tex capacity:(NSUInteger)n free(quads_); if( indices_ ) free(indices_); + + [self release]; return nil; } -#if CC_USES_VBO - // initial binding - glGenBuffers(2, &buffersVBO_[0]); - dirty_ = YES; -#endif // CC_USES_VBO + [self setupIndices]; - [self initIndices]; +#if CC_TEXTURE_ATLAS_USE_VAO + [self setupVBOandVAO]; +#else + [self setupVBO]; +#endif + + dirty_ = YES; } return self; } -- (NSString*) description +-(NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | totalQuads = %i>", [self class], self, totalQuads_]; + return [NSString stringWithFormat:@"<%@ = %p | totalQuads = %lu>", [self class], self, (unsigned long)totalQuads_]; } -(void) dealloc @@ -120,23 +136,25 @@ -(void) dealloc free(quads_); free(indices_); -#if CC_USES_VBO glDeleteBuffers(2, buffersVBO_); -#endif // CC_USES_VBO +#if CC_TEXTURE_ATLAS_USE_VAO + glDeleteVertexArrays(1, &VAOname_); +#endif [texture_ release]; [super dealloc]; } --(void) initIndices +-(void) setupIndices { - for( NSUInteger i=0;i< capacity_;i++) { + for( NSUInteger i = 0; i < capacity_;i++) + { #if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP indices_[i*6+0] = i*4+0; indices_[i*6+1] = i*4+0; - indices_[i*6+2] = i*4+2; + indices_[i*6+2] = i*4+2; indices_[i*6+3] = i*4+1; indices_[i*6+4] = i*4+3; indices_[i*6+5] = i*4+3; @@ -144,43 +162,105 @@ -(void) initIndices indices_[i*6+0] = i*4+0; indices_[i*6+1] = i*4+1; indices_[i*6+2] = i*4+2; - + // inverted index. issue #179 indices_[i*6+3] = i*4+3; indices_[i*6+4] = i*4+2; - indices_[i*6+5] = i*4+1; -// indices_[i*6+3] = i*4+2; -// indices_[i*6+4] = i*4+3; -// indices_[i*6+5] = i*4+1; -#endif + indices_[i*6+5] = i*4+1; +#endif } +} + +#pragma mark TextureAtlas - VAO / VBO specific + +#if CC_TEXTURE_ATLAS_USE_VAO +-(void) setupVBOandVAO +{ + // VAO requires GL_APPLE_vertex_array_object in order to be created on a different thread + // https://devforums.apple.com/thread/145566?tstart=0 + + void (^createVAO)(void) = ^{ + glGenVertexArrays(1, &VAOname_); + glBindVertexArray(VAOname_); + + #define kQuadSize sizeof(quads_[0].bl) + + glGenBuffers(2, &buffersVBO_[0]); + + glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * capacity_, quads_, GL_DYNAMIC_DRAW); + + // vertices + glEnableVertexAttribArray(kCCVertexAttrib_Position); + glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices)); + + // colors + glEnableVertexAttribArray(kCCVertexAttrib_Color); + glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors)); + + // tex coords + glEnableVertexAttribArray(kCCVertexAttrib_TexCoords); + glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords)); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices_[0]) * capacity_ * 6, indices_, GL_STATIC_DRAW); + + glBindVertexArray(0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + CHECK_GL_ERROR_DEBUG(); + }; + + NSThread *cocos2dThread = [[CCDirector sharedDirector] runningThread]; + if( cocos2dThread == [NSThread currentThread] || [[CCConfiguration sharedConfiguration] supportsShareableVAO] ) + createVAO(); + else + [cocos2dThread performBlock:createVAO waitUntilDone:YES]; +} +#else // CC_TEXTURE_ATLAS_USE_VAO +-(void) setupVBO +{ + glGenBuffers(2, &buffersVBO_[0]); + + [self mapBuffers]; +} +#endif // ! // CC_TEXTURE_ATLAS_USE_VAO -#if CC_USES_VBO + +-(void) mapBuffers +{ glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * capacity_, quads_, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices_[0]) * capacity_ * 6, indices_, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -#endif // CC_USES_VBO + + CHECK_GL_ERROR_DEBUG(); } #pragma mark TextureAtlas - Update, Insert, Move & Remove +-(ccV3F_C4B_T2F_Quad *) quads +{ + //if someone accesses the quads directly, presume that changes will be made + dirty_ = YES; + return quads_; +} + -(void) updateQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger) n { NSAssert(n < capacity_, @"updateQuadWithTexture: Invalid index"); totalQuads_ = MAX( n+1, totalQuads_); - quads_[n] = *quad; + quads_[n] = *quad; -#if CC_USES_VBO dirty_ = YES; -#endif } - -(void) insertQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index { NSAssert(index < capacity_, @"insertQuadWithTexture: Invalid index"); @@ -198,11 +278,38 @@ -(void) insertQuad:(ccV3F_C4B_T2F_Quad*)quad atIndex:(NSUInteger)index quads_[index] = *quad; -#if CC_USES_VBO dirty_ = YES; -#endif } +-(void) insertQuads:(ccV3F_C4B_T2F_Quad*)quads atIndex:(NSUInteger)index amount:(NSUInteger) amount +{ + NSAssert(index + amount <= capacity_, @"insertQuadWithTexture: Invalid index + amount"); + + totalQuads_+= amount; + + NSAssert( totalQuads_ <= capacity_, @"invalid totalQuads"); + + // issue #575. index can be > totalQuads + NSInteger remaining = (totalQuads_-1) - index - amount; + + // last object doesn't need to be moved + if( remaining > 0) + // tex coordinates + memmove( &quads_[index+amount],&quads_[index], sizeof(quads_[0]) * remaining ); + + + + NSUInteger max = index + amount; + NSUInteger j = 0; + for (NSUInteger i = index; i < max ; i++) + { + quads_[index] = quads[j]; + index++; + j++; + } + + dirty_ = YES; +} -(void) insertQuadFromIndex:(NSUInteger)oldIndex atIndex:(NSUInteger)newIndex { @@ -225,9 +332,37 @@ -(void) insertQuadFromIndex:(NSUInteger)oldIndex atIndex:(NSUInteger)newIndex memmove( &quads_[dst],&quads_[src], sizeof(quads_[0]) * howMany ); quads_[newIndex] = quadsBackup; -#if CC_USES_VBO dirty_ = YES; -#endif +} + +-(void) moveQuadsFromIndex:(NSUInteger)oldIndex amount:(NSUInteger) amount atIndex:(NSUInteger)newIndex +{ + NSAssert(newIndex + amount <= totalQuads_, @"insertQuadFromIndex:atIndex: Invalid index"); + NSAssert(oldIndex < totalQuads_, @"insertQuadFromIndex:atIndex: Invalid index"); + + if( oldIndex == newIndex ) + return; + + //create buffer + size_t quadSize = sizeof(ccV3F_C4B_T2F_Quad); + ccV3F_C4B_T2F_Quad *tempQuads = malloc( quadSize * amount); + memcpy( tempQuads, &quads_[oldIndex], quadSize * amount ); + + if (newIndex < oldIndex) + { + // move quads from newIndex to newIndex + amount to make room for buffer + memmove( &quads_[newIndex], &quads_[newIndex+amount], (oldIndex-newIndex)*quadSize); + } + else + { + // move quads above back + memmove( &quads_[oldIndex], &quads_[oldIndex+amount], (newIndex-oldIndex)*quadSize); + } + memcpy( &quads_[newIndex], tempQuads, amount*quadSize); + + free(tempQuads); + + dirty_ = YES; } -(void) removeQuadAtIndex:(NSUInteger) index @@ -236,17 +371,27 @@ -(void) removeQuadAtIndex:(NSUInteger) index NSUInteger remaining = (totalQuads_-1) - index; - // last object doesn't need to be moved if( remaining ) - // tex coordinates memmove( &quads_[index],&quads_[index+1], sizeof(quads_[0]) * remaining ); totalQuads_--; -#if CC_USES_VBO dirty_ = YES; -#endif +} + +-(void) removeQuadsAtIndex:(NSUInteger) index amount:(NSUInteger) amount +{ + NSAssert(index + amount <= totalQuads_, @"removeQuadAtIndex: index + amount out of bounds"); + + NSUInteger remaining = (totalQuads_) - (index + amount); + + totalQuads_ -= amount; + + if ( remaining ) + memmove( &quads_[index], &quads_[index+amount], sizeof(quads_[0]) * remaining ); + + dirty_ = YES; } -(void) removeAllQuads @@ -289,14 +434,41 @@ -(BOOL) resizeCapacity: (NSUInteger) newCapacity quads_ = tmpQuads; indices_ = tmpIndices; - [self initIndices]; + // Update Indices + [self setupIndices]; + [self mapBuffers]; -#if CC_USES_VBO dirty_ = YES; -#endif + return YES; } +#pragma mark TextureAtlas - CCParticleBatchNode Specific + +-(void) fillWithEmptyQuadsFromIndex:(NSUInteger) index amount:(NSUInteger) amount +{ + ccV3F_C4B_T2F_Quad quad; + bzero( &quad, sizeof(quad) ); + + NSUInteger to = index + amount; + for (NSInteger i = index ; i < to ; i++) + { + quads_[i] = quad; + } + +} +-(void) increaseTotalQuadsWith:(NSUInteger) amount +{ + totalQuads_ += amount; +} + +-(void) moveQuadsFromIndex:(NSUInteger) index to:(NSUInteger) newIndex +{ + NSAssert(newIndex + (totalQuads_ - index) <= capacity_, @"moveQuadsFromIndex move is out of bounds"); + + memmove(quads_ + newIndex,quads_ + index, (totalQuads_ - index) * sizeof(quads_[0])); +} + #pragma mark TextureAtlas - Drawing -(void) drawQuads @@ -305,65 +477,82 @@ -(void) drawQuads } -(void) drawNumberOfQuads: (NSUInteger) n -{ +{ [self drawNumberOfQuads:n fromIndex:0]; } -(void) drawNumberOfQuads: (NSUInteger) n fromIndex: (NSUInteger) start { - // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY - // Unneeded states: - + ccGLBindTexture2D( [texture_ name] ); + +#if CC_TEXTURE_ATLAS_USE_VAO + + // + // Using VBO and VAO + // + + // XXX: update is done in draw... perhaps it should be done in a timer + if (dirty_) { + glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]); + glBufferSubData(GL_ARRAY_BUFFER, sizeof(quads_[0])*start, sizeof(quads_[0]) * n , &quads_[start] ); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + dirty_ = NO; + } + + glBindVertexArray( VAOname_ ); + +#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP + glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) ); +#else + glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) ); +#endif // CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP + + glBindVertexArray(0); + + +#else // ! CC_TEXTURE_ATLAS_USE_VAO + + // + // Using VBO without VAO + // - glBindTexture(GL_TEXTURE_2D, [texture_ name]); #define kQuadSize sizeof(quads_[0].bl) -#if CC_USES_VBO glBindBuffer(GL_ARRAY_BUFFER, buffersVBO_[0]); - + // XXX: update is done in draw... perhaps it should be done in a timer if (dirty_) { glBufferSubData(GL_ARRAY_BUFFER, sizeof(quads_[0])*start, sizeof(quads_[0]) * n , &quads_[start] ); dirty_ = NO; } - // vertices - glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices)); + ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex ); + // vertices + glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices)); + // colors - glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors)); - + glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors)); + // tex coords - glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords)); + glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords)); + + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffersVBO_[1]); + #if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) ); #else glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(indices_[0])) ); #endif // CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP - glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -#else // ! CC_USES_VBO - - NSUInteger offset = (NSUInteger)quads_; - // vertex - NSUInteger diff = offsetof( ccV3F_C4B_T2F, vertices); - glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) (offset + diff) ); - // color - diff = offsetof( ccV3F_C4B_T2F, colors); - glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*)(offset + diff)); - // tex coords - diff = offsetof( ccV3F_C4B_T2F, texCoords); - glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*)(offset + diff)); +#endif // CC_TEXTURE_ATLAS_USE_VAO -#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP - glDrawElements(GL_TRIANGLE_STRIP, n*6, GL_UNSIGNED_SHORT, indices_ + start * 6 ); -#else - glDrawElements(GL_TRIANGLES, n*6, GL_UNSIGNED_SHORT, indices_ + start * 6 ); -#endif + CC_INCREMENT_GL_DRAWS(1); -#endif // CC_USES_VBO + CHECK_GL_ERROR_DEBUG(); } @end diff --git a/cocos2d/cocos2d/CCTextureCache.h b/cocos2d/cocos2d/CCTextureCache.h old mode 100644 new mode 100755 index 7084793..18d24f0 --- a/cocos2d/cocos2d/CCTextureCache.h +++ b/cocos2d/cocos2d/CCTextureCache.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,9 +24,9 @@ * */ -#import +#import "ccMacros.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import #endif @@ -41,8 +41,9 @@ @interface CCTextureCache : NSObject { NSMutableDictionary *textures_; - NSLock *dictLock_; - NSLock *contextLock_; + + dispatch_queue_t _loadingQueue; + dispatch_queue_t _dictQueue; } /** Retruns ths shared instance of the cache */ @@ -62,15 +63,25 @@ */ -(CCTexture2D*) addImage: (NSString*) fileimage; -/** Returns a Texture2D object given a file image - * If the file image was not previously loaded, it will create a new CCTexture2D object and it will return it. +/** Asynchronously, load a texture2d from a file. + * If the file image was previously loaded, it will use it. * Otherwise it will load a texture in a new thread, and when the image is loaded, the callback will be called with the Texture2D as a parameter. - * The callback will be called from the main thread, so it is safe to create any cocos2d object from the callback. + * The callback will be called in the cocos2d thread, so it is safe to create any cocos2d object from the callback. * Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif * @since v0.8 */ -(void) addImageAsync:(NSString*) filename target:(id)target selector:(SEL)selector; +/** Asynchronously, load a texture2d from a file. + * If the file image was previously loaded, it will use it. + * Otherwise it will load a texture in a new thread, and when the image is loaded, the block will be called. + * The callback will be called in the cocos2d thread, so it is safe to create any cocos2d object from the callback. + * Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif + * @since v2.0 + */ +-(void) addImageAsync:(NSString*) filename withBlock:(void(^)(CCTexture2D *tex))block; + + /** Returns a Texture2D object given an CGImageRef image * If the image was not previously loaded, it will create a new CCTexture2D object and it will return it. * Otherwise it will return a reference of a previously loaded image @@ -114,20 +125,6 @@ @interface CCTextureCache (PVRSupport) -/** Returns a Texture2D object given an PVRTC RAW filename - * If the file image was not previously loaded, it will create a new CCTexture2D - * object and it will return it. Otherwise it will return a reference of a previosly loaded image - * - * It can only load square images: width == height, and it must be a power of 2 (128,256,512...) - * bpp can only be 2 or 4. 2 means more compression but lower quality. - * hasAlpha: whether or not the image contains alpha channel - * - * IMPORTANT: This method is only defined on iOS. It is not supported on the Mac version. - */ -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED --(CCTexture2D*) addPVRTCImage:(NSString*)fileimage bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w; -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED - /** Returns a Texture2D object given an PVR filename. * If the file image was not previously loaded, it will create a new CCTexture2D * object and it will return it. Otherwise it will return a reference of a previosly loaded image diff --git a/cocos2d/cocos2d/CCTextureCache.m b/cocos2d/cocos2d/CCTextureCache.m old mode 100644 new mode 100755 index 2f59514..e151e39 --- a/cocos2d/cocos2d/CCTextureCache.m +++ b/cocos2d/cocos2d/CCTextureCache.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,53 +24,33 @@ * */ -#import - +#import "ccMacros.h" #import "Platforms/CCGL.h" #import "CCTextureCache.h" #import "CCTexture2D.h" #import "CCTexturePVR.h" -#import "ccMacros.h" #import "CCConfiguration.h" -#import "Support/CCFileUtils.h" #import "CCDirector.h" #import "ccConfig.h" +#import "ccTypes.h" -// needed for CCCallFuncO in Mac-display_link version -#import "CCActionManager.h" -#import "CCActionInstant.h" - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -static EAGLContext *auxGLcontext = nil; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) -static NSOpenGLContext *auxGLcontext = nil; -#endif +#import "Support/CCFileUtils.h" +#import "Support/NSThread+performBlock.h" -@interface CCAsyncObject : NSObject -{ - SEL selector_; - id target_; - id data_; -} -@property (nonatomic,readwrite,assign) SEL selector; -@property (nonatomic,readwrite,retain) id target; -@property (nonatomic,readwrite,retain) id data; -@end +#ifdef __CC_PLATFORM_MAC +#import "Platforms/Mac/CCDirectorMac.h" +#endif -@implementation CCAsyncObject -@synthesize selector = selector_; -@synthesize target = target_; -@synthesize data = data_; -- (void) dealloc -{ - CCLOGINFO(@"cocos2d: deallocing %@", self); - [target_ release]; - [data_ release]; - [super dealloc]; -} -@end +// needed for CCCallFuncO in Mac-display_link version +//#import "CCActionManager.h" +//#import "CCActionInstant.h" +#ifdef __CC_PLATFORM_IOS +static EAGLContext *_auxGLcontext = nil; +#elif defined(__CC_PLATFORM_MAC) +static NSOpenGLContext *_auxGLcontext = nil; +#endif @implementation CCTextureCache @@ -80,8 +60,8 @@ @implementation CCTextureCache + (CCTextureCache *)sharedTextureCache { if (!sharedTextureCache) - sharedTextureCache = [[CCTextureCache alloc] init]; - + sharedTextureCache = [[self alloc] init]; + return sharedTextureCache; } @@ -101,8 +81,29 @@ -(id) init { if( (self=[super init]) ) { textures_ = [[NSMutableDictionary dictionaryWithCapacity: 10] retain]; - dictLock_ = [[NSLock alloc] init]; - contextLock_ = [[NSLock alloc] init]; + + // init "global" stuff + _loadingQueue = dispatch_queue_create("org.cocos2d.texturecacheloading", NULL); + _dictQueue = dispatch_queue_create("org.cocos2d.texturecachedict", NULL); + + CCGLView *view = (CCGLView*)[[CCDirector sharedDirector] view]; + NSAssert(view, @"Do not initialize the TextureCache before the Director"); + +#ifdef __CC_PLATFORM_IOS + _auxGLcontext = [[EAGLContext alloc] + initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:[[view context] sharegroup]]; + +#elif defined(__CC_PLATFORM_MAC) + NSOpenGLPixelFormat *pf = [view pixelFormat]; + NSOpenGLContext *share = [view openGLContext]; + + _auxGLcontext = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:share]; + +#endif // __CC_PLATFORM_MAC + + NSAssert( _auxGLcontext, @"TextureCache: Could not create EAGL context"); + } return self; @@ -110,225 +111,236 @@ -(id) init - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | num of textures = %i | keys: %@>", + __block NSString *desc = nil; + dispatch_sync(_dictQueue, ^{ + desc = [NSString stringWithFormat:@"<%@ = %p | num of textures = %lu | keys: %@>", [self class], self, - [textures_ count], + (unsigned long)[textures_ count], [textures_ allKeys] ]; - + }); + return desc; } -(void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); - [textures_ release]; - [dictLock_ release]; - [contextLock_ release]; - [auxGLcontext release]; - auxGLcontext = nil; + dispatch_sync(_dictQueue, ^{ + [textures_ release]; + }); + [_auxGLcontext release]; + _auxGLcontext = nil; sharedTextureCache = nil; + dispatch_release(_loadingQueue); + dispatch_release(_dictQueue); + [super dealloc]; } #pragma mark TextureCache - Add Images --(void) addImageWithAsyncObject:(CCAsyncObject*)async +-(void) addImageAsync: (NSString*)path target:(id)target selector:(SEL)selector { - NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - // textures will be created on the main OpenGL context - // it seems that in SDK 2.2.x there can't be 2 threads creating textures at the same time - // the lock is used for this purpose: issue #472 - [contextLock_ lock]; - if( auxGLcontext == nil ) { - auxGLcontext = [[EAGLContext alloc] - initWithAPI:kEAGLRenderingAPIOpenGLES1 - sharegroup:[[[[CCDirector sharedDirector] openGLView] context] sharegroup]]; - - if( ! auxGLcontext ) - CCLOG(@"cocos2d: TextureCache: Could not create EAGL context"); + NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); + NSAssert(target != nil, @"TextureCache: target can't be nil"); + NSAssert(selector != NULL, @"TextureCache: selector can't be NULL"); + + // optimization + + __block CCTexture2D * tex; + +#ifdef __CC_PLATFORM_IOS + path = [[CCFileUtils sharedFileUtils] removeSuffixFromFile:path]; +#endif + + dispatch_sync(_dictQueue, ^{ + tex = [textures_ objectForKey:path]; + }); + + if(tex) { + [target performSelector:selector withObject:tex]; + return; } - - if( [EAGLContext setCurrentContext:auxGLcontext] ) { + + // dispatch it serially + dispatch_async(_loadingQueue, ^{ + + CCTexture2D *texture; + +#ifdef __CC_PLATFORM_IOS + if( [EAGLContext setCurrentContext:_auxGLcontext] ) { + + // load / create the texture + texture = [self addImage:path]; + + glFlush(); + + // callback should be executed in cocos2d thread + [target performSelector:selector onThread:[[CCDirector sharedDirector] runningThread] withObject:texture waitUntilDone:NO]; + + [EAGLContext setCurrentContext:nil]; + } else { + CCLOG(@"cocos2d: ERROR: TetureCache: Could not set EAGLContext"); + } + +#elif defined(__CC_PLATFORM_MAC) + + [_auxGLcontext makeCurrentContext]; // load / create the texture - CCTexture2D *tex = [self addImage:async.data]; - - // The callback will be executed on the main thread - [async.target performSelectorOnMainThread:async.selector withObject:tex waitUntilDone:NO]; - - [EAGLContext setCurrentContext:nil]; - } else { - CCLOG(@"cocos2d: TetureCache: EAGLContext error"); - } - [contextLock_ unlock]; - - [autoreleasepool release]; + texture = [self addImage:path]; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + glFlush(); - [contextLock_ lock]; - if( auxGLcontext == nil ) { + // callback should be executed in cocos2d thread + [target performSelector:selector onThread:[[CCDirector sharedDirector] runningThread] withObject:texture waitUntilDone:NO]; - MacGLView *view = [[CCDirector sharedDirector] openGLView]; - - NSOpenGLPixelFormat *pf = [view pixelFormat]; - NSOpenGLContext *share = [view openGLContext]; + [NSOpenGLContext clearCurrentContext]; - auxGLcontext = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:share]; +#endif // __CC_PLATFORM_MAC - if( ! auxGLcontext ) - CCLOG(@"cocos2d: TextureCache: Could not create NSOpenGLContext"); - } - - [auxGLcontext makeCurrentContext]; - - // load / create the texture - CCTexture2D *tex = [self addImage:async.data]; - -#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD - id action = [CCCallFuncO actionWithTarget:async.target selector:async.selector object:tex]; - [[CCActionManager sharedManager] addAction:action target:async.target paused:NO]; -#else - // The callback will be executed on the main thread - [async.target performSelector:async.selector - onThread:[[CCDirector sharedDirector] runningThread] - withObject:tex - waitUntilDone:NO]; -#endif - - - [NSOpenGLContext clearCurrentContext]; - - [contextLock_ unlock]; - - [autoreleasepool release]; - -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED + }); } --(void) addImageAsync: (NSString*)path target:(id)target selector:(SEL)selector +-(void) addImageAsync:(NSString*)path withBlock:(void(^)(CCTexture2D *tex))block { - NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); + NSAssert(path != nil, @"TextureCache: fileimage MUST not be nil"); // optimization - - CCTexture2D * tex; - - path = ccRemoveHDSuffixFromFile(path); - - if( (tex=[textures_ objectForKey: path] ) ) { - [target performSelector:selector withObject:tex]; + + __block CCTexture2D * tex; + +#ifdef __CC_PLATFORM_IOS + path = [[CCFileUtils sharedFileUtils] removeSuffixFromFile:path]; +#endif + + dispatch_sync(_dictQueue, ^{ + tex = [textures_ objectForKey:path]; + }); + + if(tex) { + block(tex); return; } - // schedule the load - - CCAsyncObject *asyncObject = [[CCAsyncObject alloc] init]; - asyncObject.selector = selector; - asyncObject.target = target; - asyncObject.data = path; - - [NSThread detachNewThreadSelector:@selector(addImageWithAsyncObject:) toTarget:self withObject:asyncObject]; - [asyncObject release]; + // dispatch it serially + dispatch_async( _loadingQueue, ^{ + + CCTexture2D *texture; + +#ifdef __CC_PLATFORM_IOS + if( [EAGLContext setCurrentContext:_auxGLcontext] ) { + + // load / create the texture + texture = [self addImage:path]; + + glFlush(); + + // callback should be executed in cocos2d thread + NSThread *thread = [[CCDirector sharedDirector] runningThread]; + [thread performBlock:block withObject:texture waitUntilDone:NO]; + + [EAGLContext setCurrentContext:nil]; + } else { + CCLOG(@"cocos2d: ERROR: TetureCache: Could not set EAGLContext"); + } + +#elif defined(__CC_PLATFORM_MAC) + + [_auxGLcontext makeCurrentContext]; + + // load / create the texture + texture = [self addImage:path]; + + glFlush(); + + // callback should be executed in cocos2d thread + NSThread *thread = [[CCDirector sharedDirector] runningThread]; + [thread performBlock:block withObject:texture waitUntilDone:NO]; + + [NSOpenGLContext clearCurrentContext]; + +#endif // __CC_PLATFORM_MAC + + }); } -(CCTexture2D*) addImage: (NSString*) path { NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); - CCTexture2D * tex = nil; + __block CCTexture2D * tex = nil; - // MUTEX: - // Needed since addImageAsync calls this method from a different thread - [dictLock_ lock]; - // remove possible -HD suffix to prevent caching the same image twice (issue #1040) - path = ccRemoveHDSuffixFromFile( path ); +#ifdef __CC_PLATFORM_IOS + path = [[CCFileUtils sharedFileUtils] removeSuffixFromFile: path]; +#endif + + dispatch_sync(_dictQueue, ^{ + tex = [textures_ objectForKey: path]; + }); - tex=[textures_ objectForKey: path]; - if( ! tex ) { - + NSString *lowerCase = [path lowercaseString]; - // all images are handled by UIImage except PVR extension that is handled by our own handler - + + // all images are handled by UIKit/AppKit except PVR extension that is handled by cocos2d's handler + if ( [lowerCase hasSuffix:@".pvr"] || [lowerCase hasSuffix:@".pvr.gz"] || [lowerCase hasSuffix:@".pvr.ccz"] ) tex = [self addPVRImage:path]; - // Only iPhone -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - - // Issue #886: TEMPORARY FIX FOR TRANSPARENT JPEGS IN IOS4 - else if ( ( [[CCConfiguration sharedConfiguration] OSVersion] >= kCCiOSVersion_4_0) && - ( [lowerCase hasSuffix:@".jpg"] || [lowerCase hasSuffix:@".jpeg"] ) - ) { - // convert jpg to png before loading the texture - - NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ]; - - UIImage *jpg = [[UIImage alloc] initWithContentsOfFile:fullpath]; - UIImage *png = [[UIImage alloc] initWithData:UIImagePNGRepresentation(jpg)]; - tex = [ [CCTexture2D alloc] initWithImage: png ]; - [png release]; - [jpg release]; - - if( tex ) - [textures_ setObject: tex forKey:path]; - else - CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path); - - // autorelease prevents possible crash in multithreaded environments - [tex autorelease]; - } - +#ifdef __CC_PLATFORM_IOS + else { - - // prevents overloading the autorelease pool - NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ]; - UIImage *image = [ [UIImage alloc] initWithContentsOfFile: fullpath ]; - tex = [ [CCTexture2D alloc] initWithImage: image ]; + ccResolutionType resolution; + NSString *fullpath = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:path resolutionType:&resolution]; + + UIImage *image = [[UIImage alloc] initWithContentsOfFile:fullpath]; + tex = [[CCTexture2D alloc] initWithCGImage:image.CGImage resolutionType:resolution]; [image release]; - - if( tex ) - [textures_ setObject: tex forKey:path]; - else + + if( tex ){ + dispatch_sync(_dictQueue, ^{ + [textures_ setObject: tex forKey:path]; + }); + }else{ CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path); - + } + // autorelease prevents possible crash in multithreaded environments - [tex autorelease]; + [tex autorelease]; } - // Only in Mac -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +#elif defined(__CC_PLATFORM_MAC) else { - NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ]; + NSString *fullpath = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath: path ]; NSData *data = [[NSData alloc] initWithContentsOfFile:fullpath]; NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data]; - tex = [ [CCTexture2D alloc] initWithImage:[image CGImage]]; - + tex = [ [CCTexture2D alloc] initWithCGImage:[image CGImage]]; + [data release]; [image release]; - if( tex ) - [textures_ setObject: tex forKey:path]; - else + if( tex ){ + dispatch_sync(_dictQueue, ^{ + [textures_ setObject: tex forKey:path]; + }); + }else{ CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path); - + } + // autorelease prevents possible crash in multithreaded environments - [tex autorelease]; + [tex autorelease]; } -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_MAC } - - [dictLock_ unlock]; - + return tex; } @@ -336,29 +348,32 @@ -(CCTexture2D*) addImage: (NSString*) path -(CCTexture2D*) addCGImage: (CGImageRef) imageref forKey: (NSString *)key { NSAssert(imageref != nil, @"TextureCache: image MUST not be nill"); - - CCTexture2D * tex = nil; - + + __block CCTexture2D * tex = nil; + // If key is nil, then create a new texture each time - if( key && (tex=[textures_ objectForKey: key] ) ) { - return tex; + if( key ) { + dispatch_sync(_dictQueue, ^{ + tex = [textures_ objectForKey:key]; + }); + if(tex) + return tex; } - -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - // prevents overloading the autorelease pool - UIImage *image = [[UIImage alloc] initWithCGImage:imageref]; - tex = [[CCTexture2D alloc] initWithImage: image]; - [image release]; - -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) - tex = [[CCTexture2D alloc] initWithImage: imageref]; + +#ifdef __CC_PLATFORM_IOS + tex = [[CCTexture2D alloc] initWithCGImage:imageref resolutionType:kCCResolutionUnknown]; +#elif __CC_PLATFORM_MAC + tex = [[CCTexture2D alloc] initWithCGImage:imageref]; #endif - - if(tex && key) - [textures_ setObject: tex forKey:key]; - else + + if(tex && key){ + dispatch_sync(_dictQueue, ^{ + [textures_ setObject: tex forKey:key]; + }); + }else{ CCLOG(@"cocos2d: Couldn't add CGImage in CCTextureCache"); - + } + return [tex autorelease]; } @@ -366,44 +381,58 @@ -(CCTexture2D*) addCGImage: (CGImageRef) imageref forKey: (NSString *)key -(void) removeAllTextures { - [textures_ removeAllObjects]; + dispatch_sync(_dictQueue, ^{ + [textures_ removeAllObjects]; + }); } -(void) removeUnusedTextures { - NSArray *keys = [textures_ allKeys]; - for( id key in keys ) { - id value = [textures_ objectForKey:key]; - if( [value retainCount] == 1 ) { - CCLOG(@"cocos2d: CCTextureCache: removing unused texture: %@", key); - [textures_ removeObjectForKey:key]; + dispatch_sync(_dictQueue, ^{ + NSArray *keys = [textures_ allKeys]; + for( id key in keys ) { + id value = [textures_ objectForKey:key]; + if( [value retainCount] == 1 ) { + CCLOG(@"cocos2d: CCTextureCache: removing unused texture: %@", key); + [textures_ removeObjectForKey:key]; + } } - } + }); } -(void) removeTexture: (CCTexture2D*) tex { if( ! tex ) return; - - NSArray *keys = [textures_ allKeysForObject:tex]; - - for( NSUInteger i = 0; i < [keys count]; i++ ) - [textures_ removeObjectForKey:[keys objectAtIndex:i]]; + + dispatch_sync(_dictQueue, ^{ + NSArray *keys = [textures_ allKeysForObject:tex]; + + for( NSUInteger i = 0; i < [keys count]; i++ ) + [textures_ removeObjectForKey:[keys objectAtIndex:i]]; + }); } -(void) removeTextureForKey:(NSString*)name { if( ! name ) return; - - [textures_ removeObjectForKey:name]; + + dispatch_sync(_dictQueue, ^{ + [textures_ removeObjectForKey:name]; + }); } #pragma mark TextureCache - Get - (CCTexture2D *)textureForKey:(NSString *)key { - return [textures_ objectForKey:key]; + __block CCTexture2D *tex = nil; + + dispatch_sync(_dictQueue, ^{ + tex = [textures_ objectForKey:key]; + }); + + return tex; } @end @@ -411,59 +440,34 @@ - (CCTexture2D *)textureForKey:(NSString *)key @implementation CCTextureCache (PVRSupport) -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED --(CCTexture2D*) addPVRTCImage:(NSString*)path bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w +-(CCTexture2D*) addPVRImage:(NSString*)path { NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); - NSAssert( bpp==2 || bpp==4, @"TextureCache: bpp must be either 2 or 4"); - - CCTexture2D * tex; - - // remove possible -HD suffix to prevent caching the same image twice (issue #1040) - path = ccRemoveHDSuffixFromFile( path ); - if( (tex=[textures_ objectForKey: path] ) ) { - return tex; - } - - // Split up directory and filename - NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path]; - - NSData *nsdata = [[NSData alloc] initWithContentsOfFile:fullpath]; - tex = [[CCTexture2D alloc] initWithPVRTCData:[nsdata bytes] level:0 bpp:bpp hasAlpha:alpha length:w pixelFormat:bpp==2?kCCTexture2DPixelFormat_PVRTC2:kCCTexture2DPixelFormat_PVRTC4]; - if( tex ) - [textures_ setObject: tex forKey:path]; - else - CCLOG(@"cocos2d: Couldn't add PVRTCImage:%@ in CCTextureCache",path); - - [nsdata release]; - - return [tex autorelease]; -} -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED + __block CCTexture2D * tex; --(CCTexture2D*) addPVRImage:(NSString*)path -{ - NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); - - CCTexture2D * tex; - // remove possible -HD suffix to prevent caching the same image twice (issue #1040) - path = ccRemoveHDSuffixFromFile( path ); +#ifdef __CC_PLATFORM_IOS + path = [[CCFileUtils sharedFileUtils] removeSuffixFromFile: path]; +#endif - if( (tex=[textures_ objectForKey: path] ) ) { + dispatch_sync(_dictQueue, ^{ + tex = [textures_ objectForKey:path]; + }); + + if(tex) { return tex; } - - // Split up directory and filename - NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path]; - - tex = [[CCTexture2D alloc] initWithPVRFile: fullpath]; - if( tex ) - [textures_ setObject: tex forKey:path]; - else - CCLOG(@"cocos2d: Couldn't add PVRImage:%@ in CCTextureCache",path); - + + tex = [[CCTexture2D alloc] initWithPVRFile: path]; + if( tex ){ + dispatch_sync(_dictQueue, ^{ + [textures_ setObject: tex forKey:path]; + }); + }else{ + CCLOG(@"cocos2d: Couldn't add PVRImage:%@ in CCTextureCache",path); + } + return [tex autorelease]; } @@ -474,25 +478,28 @@ @implementation CCTextureCache (Debug) -(void) dumpCachedTextureInfo { - NSUInteger count = 0; - NSUInteger totalBytes = 0; - for (NSString* texKey in textures_) { - CCTexture2D* tex = [textures_ objectForKey:texKey]; - NSUInteger bpp = [tex bitsPerPixelForFormat]; - // Each texture takes up width * height * bytesPerPixel bytes. - NSUInteger bytes = tex.pixelsWide * tex.pixelsHigh * bpp / 8; - totalBytes += bytes; - count++; - CCLOG( @"cocos2d: \"%@\" rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB", - texKey, - (long)[tex retainCount], - (long)tex.name, - (long)tex.pixelsWide, - (long)tex.pixelsHigh, - (long)bpp, - (long)bytes / 1024 ); - } - CCLOG( @"cocos2d: CCTextureCache dumpDebugInfo: %ld textures, for %lu KB (%.2f MB)", (long)count, (long)totalBytes / 1024, totalBytes / (1024.0f*1024.0f)); + __block NSUInteger count = 0; + __block NSUInteger totalBytes = 0; + + dispatch_sync(_dictQueue, ^{ + for (NSString* texKey in textures_) { + CCTexture2D* tex = [textures_ objectForKey:texKey]; + NSUInteger bpp = [tex bitsPerPixelForFormat]; + // Each texture takes up width * height * bytesPerPixel bytes. + NSUInteger bytes = tex.pixelsWide * tex.pixelsHigh * bpp / 8; + totalBytes += bytes; + count++; + NSLog( @"cocos2d: \"%@\"\trc=%lu\tid=%lu\t%lu x %lu\t@ %ld bpp =>\t%lu KB", + texKey, + (long)[tex retainCount], + (long)tex.name, + (long)tex.pixelsWide, + (long)tex.pixelsHigh, + (long)bpp, + (long)bytes / 1024 ); + } + }); + NSLog( @"cocos2d: CCTextureCache dumpDebugInfo:\t%ld textures,\tfor %lu KB (%.2f MB)", (long)count, (long)totalBytes / 1024, totalBytes / (1024.0f*1024.0f)); } @end diff --git a/cocos2d/cocos2d/CCTexturePVR.h b/cocos2d/cocos2d/CCTexturePVR.h old mode 100644 new mode 100755 index 66f8286..f490818 --- a/cocos2d/cocos2d/CCTexturePVR.h +++ b/cocos2d/cocos2d/CCTexturePVR.h @@ -64,9 +64,9 @@ enum { }; /** CCTexturePVR - + Object that loads PVR images. - + Supported PVR formats: - RGBA8888 - BGRA8888 @@ -81,20 +81,20 @@ enum { Limitations: Pre-generated mipmaps, such as PVR textures with mipmap levels embedded in file, - are only supported if all individual sprites are of _square_ size. + are only supported if all individual sprites are of _square_ size. To use mipmaps with non-square textures, instead call CCTexture2D#generateMipmap on the sheet texture itself (and to save space, save the PVR sprite sheet without mip maps included). */ @interface CCTexturePVR : NSObject { struct CCPVRMipmap mipmaps_[CC_PVRMIPMAP_MAX]; // pointer to mipmap images - int numberOfMipmaps_; // number of mipmap used - + NSUInteger numberOfMipmaps_; // number of mipmap used + unsigned int tableFormatIndex_; uint32_t width_, height_; GLuint name_; BOOL hasAlpha_; - + // cocos2d integration BOOL retainName_; CCTexture2DPixelFormat format_; @@ -117,6 +117,8 @@ enum { @property (nonatomic,readonly) uint32_t height; /** whether or not the texture has alpha */ @property (nonatomic,readonly) BOOL hasAlpha; +/** how many mipmaps the texture has. 1 means one level (level 0 */ +@property (nonatomic, readonly) NSUInteger numberOfMipmaps; // cocos2d integration @property (nonatomic,readwrite) BOOL retainName; diff --git a/cocos2d/cocos2d/CCTexturePVR.m b/cocos2d/cocos2d/CCTexturePVR.m old mode 100644 new mode 100755 index 692d5f9..39d2829 --- a/cocos2d/cocos2d/CCTexturePVR.m +++ b/cocos2d/cocos2d/CCTexturePVR.m @@ -49,6 +49,7 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * Extended PVR formats for cocos2d project ( http://www.cocos2d-iphone.org ) * - RGBA8888 * - BGRA8888 + * - RGB888 * - RGBA4444 * - RGBA5551 * - RGB565 @@ -57,13 +58,12 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * - AI88 */ -#import - #import #import "CCTexturePVR.h" #import "ccMacros.h" #import "CCConfiguration.h" +#import "ccGLStateCache.h" #import "Support/ccUtils.h" #import "Support/CCFileUtils.h" #import "Support/ZipUtils.h" @@ -97,17 +97,17 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE kPVRTexturePixelTypeRGBA_8888, kPVRTexturePixelTypeRGB_565, kPVRTexturePixelTypeRGB_555, // unsupported - kPVRTexturePixelTypeRGB_888, // unsupported + kPVRTexturePixelTypeRGB_888, kPVRTexturePixelTypeI_8, kPVRTexturePixelTypeAI_88, kPVRTexturePixelTypePVRTC_2, - kPVRTexturePixelTypePVRTC_4, + kPVRTexturePixelTypePVRTC_4, kPVRTexturePixelTypeBGRA_8888, kPVRTexturePixelTypeA_8, }; static const uint32_t tableFormats[][7] = { - + // - PVR texture format // - OpenGL internal format // - OpenGL format @@ -115,18 +115,19 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE // - bpp // - compressed // - Cocos2d texture format constant - { kPVRTexturePixelTypeRGBA_4444, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 16, NO, kCCTexture2DPixelFormat_RGBA4444 }, - { kPVRTexturePixelTypeRGBA_5551, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 16, NO, kCCTexture2DPixelFormat_RGB5A1 }, - { kPVRTexturePixelTypeRGBA_8888, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 32, NO, kCCTexture2DPixelFormat_RGBA8888 }, - { kPVRTexturePixelTypeRGB_565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 16, NO, kCCTexture2DPixelFormat_RGB565 }, - { kPVRTexturePixelTypeA_8, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, 8, NO, kCCTexture2DPixelFormat_A8 }, - { kPVRTexturePixelTypeI_8, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 8, NO, kCCTexture2DPixelFormat_I8 }, + { kPVRTexturePixelTypeRGBA_4444, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 16, NO, kCCTexture2DPixelFormat_RGBA4444 }, + { kPVRTexturePixelTypeRGBA_5551, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 16, NO, kCCTexture2DPixelFormat_RGB5A1 }, + { kPVRTexturePixelTypeRGBA_8888, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 32, NO, kCCTexture2DPixelFormat_RGBA8888 }, + { kPVRTexturePixelTypeRGB_565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 16, NO, kCCTexture2DPixelFormat_RGB565 }, + { kPVRTexturePixelTypeRGB_888, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, 24, NO, kCCTexture2DPixelFormat_RGB888 }, + { kPVRTexturePixelTypeA_8, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, 8, NO, kCCTexture2DPixelFormat_A8 }, + { kPVRTexturePixelTypeI_8, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 8, NO, kCCTexture2DPixelFormat_I8 }, { kPVRTexturePixelTypeAI_88, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 16, NO, kCCTexture2DPixelFormat_AI88 }, -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - { kPVRTexturePixelTypePVRTC_2, GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, -1, -1, 2, YES, kCCTexture2DPixelFormat_PVRTC2 }, - { kPVRTexturePixelTypePVRTC_4, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, -1, -1, 4, YES, kCCTexture2DPixelFormat_PVRTC4 }, +#ifdef __CC_PLATFORM_IOS + { kPVRTexturePixelTypePVRTC_2, GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, -1, -1, 2, YES, kCCTexture2DPixelFormat_PVRTC2 }, + { kPVRTexturePixelTypePVRTC_4, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, -1, -1, 4, YES, kCCTexture2DPixelFormat_PVRTC4 }, #endif // iphone only - { kPVRTexturePixelTypeBGRA_8888, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, 32, NO, kCCTexture2DPixelFormat_RGBA8888 }, + { kPVRTexturePixelTypeBGRA_8888, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, 32, NO, kCCTexture2DPixelFormat_RGBA8888 }, }; #define MAX_TABLE_ELEMENTS (sizeof(tableFormats) / sizeof(tableFormats[0])) @@ -164,6 +165,7 @@ @implementation CCTexturePVR @synthesize width = width_; @synthesize height = height_; @synthesize hasAlpha = hasAlpha_; +@synthesize numberOfMipmaps = numberOfMipmaps_; // cocos2d integration @synthesize retainName = retainName_; @@ -180,9 +182,9 @@ - (BOOL)unpackPVRData:(unsigned char*)data PVRLen:(NSUInteger)len uint32_t width = 0, height = 0, bpp = 4; uint8_t *bytes = NULL; uint32_t formatFlags; - + header = (PVRTexHeader *)data; - + pvrTag = CFSwapInt32LittleToHost(header->pvrTag); if ((uint32_t)gPVRTexIdentifier[0] != ((pvrTag >> 0) & 0xff) || @@ -190,41 +192,42 @@ - (BOOL)unpackPVRData:(unsigned char*)data PVRLen:(NSUInteger)len (uint32_t)gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) || (uint32_t)gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff)) { + CCLOG(@"Unsupported PVR format. Use the Legacy format until the new format is supported"); return FALSE; } - + CCConfiguration *configuration = [CCConfiguration sharedConfiguration]; flags = CFSwapInt32LittleToHost(header->flags); formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK; BOOL flipped = flags & kPVRTextureFlagVerticalFlip; if( flipped ) - CCLOG(@"cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool"); - + CCLOGWARN(@"cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool"); + if( ! [configuration supportsNPOT] && ( header->width != ccNextPOT(header->width) || header->height != ccNextPOT(header->height ) ) ) { - CCLOG(@"cocos2d: ERROR: Loding an NPOT texture (%dx%d) but is not supported on this device", header->width, header->height); + CCLOGWARN(@"cocos2d: ERROR: Loding an NPOT texture (%dx%d) but is not supported on this device", header->width, header->height); return FALSE; } - + for( tableFormatIndex_=0; tableFormatIndex_ < (unsigned int)MAX_TABLE_ELEMENTS ; tableFormatIndex_++) { if( tableFormats[tableFormatIndex_][kCCInternalPVRTextureFormat] == formatFlags ) { - + numberOfMipmaps_ = 0; - + width_ = width = CFSwapInt32LittleToHost(header->width); height_ = height = CFSwapInt32LittleToHost(header->height); - + if (CFSwapInt32LittleToHost(header->bitmaskAlpha)) hasAlpha_ = TRUE; else hasAlpha_ = FALSE; - + dataLength = CFSwapInt32LittleToHost(header->dataLength); bytes = ((uint8_t *)data) + sizeof(PVRTexHeader); format_ = tableFormats[tableFormatIndex_][kCCInternalCCTexture2DPixelFormat]; bpp = tableFormats[tableFormatIndex_][kCCInternalBPP]; - + // Calculate the data size for each texture level and respect the minimum number of blocks while (dataOffset < dataLength) { @@ -250,7 +253,7 @@ - (BOOL)unpackPVRData:(unsigned char*)data PVRLen:(NSUInteger)len heightBlocks = height; break; } - + // Clamp to minimum number of blocks if (widthBlocks < 2) widthBlocks = 2; @@ -258,29 +261,29 @@ - (BOOL)unpackPVRData:(unsigned char*)data PVRLen:(NSUInteger)len heightBlocks = 2; dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8); - float packetLength = (dataLength-dataOffset); + unsigned int packetLength = (dataLength-dataOffset); packetLength = packetLength > dataSize ? dataSize : packetLength; - + mipmaps_[numberOfMipmaps_].address = bytes+dataOffset; mipmaps_[numberOfMipmaps_].len = packetLength; numberOfMipmaps_++; - + NSAssert( numberOfMipmaps_ < CC_PVRMIPMAP_MAX, @"TexturePVR: Maximum number of mimpaps reached. Increate the CC_PVRMIPMAP_MAX value"); - + dataOffset += packetLength; - + width = MAX(width >> 1, 1); height = MAX(height >> 1, 1); } - + success = TRUE; break; } } - + if( ! success ) - CCLOG(@"cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%2x. Re-encode it with a OpenGL pixel format variant", formatFlags); - + CCLOGWARN(@"cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%2x. Re-encode it with a OpenGL pixel format variant", formatFlags); + return success; } @@ -290,96 +293,161 @@ - (BOOL)createGLTexture GLsizei width = width_; GLsizei height = height_; GLenum err; - + if (numberOfMipmaps_ > 0) { if (name_ != 0) - glDeleteTextures(1, &name_); - + ccGLDeleteTexture( name_ ); + + // From PVR sources: "PVR files are never row aligned." glPixelStorei(GL_UNPACK_ALIGNMENT,1); + glGenTextures(1, &name_); - glBindTexture(GL_TEXTURE_2D, name_); - } + ccGLBindTexture2D( name_ ); + // Default: Anti alias. + if( numberOfMipmaps_ == 1 ) + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + else + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + } + CHECK_GL_ERROR(); // clean possible GL error + GLenum internalFormat = tableFormats[tableFormatIndex_][kCCInternalOpenGLInternalFormat]; + GLenum format = tableFormats[tableFormatIndex_][kCCInternalOpenGLFormat]; + GLenum type = tableFormats[tableFormatIndex_][kCCInternalOpenGLType]; + BOOL compressed = tableFormats[tableFormatIndex_][kCCInternalCompressedImage]; + // Generate textures with mipmaps for (GLint i=0; i < numberOfMipmaps_; i++) { - GLenum internalFormat = tableFormats[tableFormatIndex_][kCCInternalOpenGLInternalFormat]; - GLenum format = tableFormats[tableFormatIndex_][kCCInternalOpenGLFormat]; - GLenum type = tableFormats[tableFormatIndex_][kCCInternalOpenGLType]; - BOOL compressed = tableFormats[tableFormatIndex_][kCCInternalCompressedImage]; - if( compressed && ! [[CCConfiguration sharedConfiguration] supportsPVRTC] ) { - CCLOG(@"cocos2d: WARNING: PVRTC images are not supported"); + CCLOGWARN(@"cocos2d: WARNING: PVRTC images are not supported"); return FALSE; - } - + } + unsigned char *data = mipmaps_[i].address; unsigned int datalen = mipmaps_[i].len; - + if( compressed) glCompressedTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, datalen, data); - else + else glTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, format, type, data); if( i > 0 && (width != height || ccNextPOT(width) != width ) ) - CCLOG(@"cocos2d: TexturePVR. WARNING. Mipmap level %u is not squared. Texture won't render correctly. width=%u != height=%u", i, width, height); - + CCLOGWARN(@"cocos2d: TexturePVR. WARNING. Mipmap level %u is not squared. Texture won't render correctly. width=%u != height=%u", i, width, height); + err = glGetError(); if (err != GL_NO_ERROR) { - CCLOG(@"cocos2d: TexturePVR: Error uploading compressed texture level: %u . glError: 0x%04X", i, err); + CCLOGWARN(@"cocos2d: TexturePVR: Error uploading compressed texture level: %u . glError: 0x%04X", i, err); return FALSE; } - + width = MAX(width >> 1, 1); height = MAX(height >> 1, 1); } - + return TRUE; } - (id)initWithContentsOfFile:(NSString *)path { - if((self = [super init])) - { + if((self = [super init])) + { unsigned char *pvrdata = NULL; NSInteger pvrlen = 0; - NSString *lowerCase = [path lowercaseString]; - - if ( [lowerCase hasSuffix:@".ccz"]) + NSString *lowerCase = [path lowercaseString]; + + if ( [lowerCase hasSuffix:@".ccz"]) pvrlen = ccInflateCCZFile( [path UTF8String], &pvrdata ); - + else if( [lowerCase hasSuffix:@".gz"] ) pvrlen = ccInflateGZipFile( [path UTF8String], &pvrdata ); - + else pvrlen = ccLoadFileIntoMemory( [path UTF8String], &pvrdata ); - + if( pvrlen < 0 ) { [self release]; return nil; - } - + } + numberOfMipmaps_ = 0; - + name_ = 0; width_ = height_ = 0; tableFormatIndex_ = -1; hasAlpha_ = FALSE; retainName_ = NO; // cocos2d integration - + if( ! [self unpackPVRData:pvrdata PVRLen:pvrlen] || ![self createGLTexture] ) { free(pvrdata); [self release]; return nil; } +#if defined(__CC_PLATFORM_IOS) && defined(DEBUG) + + GLenum pixelFormat = tableFormats[tableFormatIndex_][kCCInternalCCTexture2DPixelFormat]; + CCConfiguration *conf = [CCConfiguration sharedConfiguration]; + + if( [conf OSVersion] >= kCCiOSVersion_5_0 ) + { + + // iOS 5 BUG: + // RGB888 textures allocate much more memory than needed on iOS 5 + // http://www.cocos2d-iphone.org/forum/topic/31092 + + if( pixelFormat == kCCTexture2DPixelFormat_RGB888 ) { + printf("\n"); + NSLog(@"cocos2d: WARNING. Using RGB888 texture. Convert it to RGB565 or RGBA8888 in order to reduce memory"); + NSLog(@"cocos2d: WARNING: File: %@", [path lastPathComponent] ); + NSLog(@"cocos2d: WARNING: For furhter info visit: http://www.cocos2d-iphone.org/forum/topic/31092"); + printf("\n"); + } + + + else if( width_ != ccNextPOT(width_) ) { + + // XXX: Is this applicable for compressed textures ? + // Since they are squared and POT (PVRv2) it is not an issue now. Not sure in the future. + + // iOS 5 BUG: + // If width is not word aligned, then log warning. + // http://www.cocos2d-iphone.org/forum/topic/31092 + + + NSUInteger bpp = [CCTexture2D bitsPerPixelForFormat:pixelFormat]; + NSUInteger bytes = width_ * bpp / 8; + + // XXX: Should it be 4 or sizeof(int) ?? + NSUInteger mod = bytes % 4; + + // Not word aligned ? + if( mod != 0 ) { + + NSUInteger neededBytes = (4 - mod ) / (bpp/8); + printf("\n"); + NSLog(@"cocos2d: WARNING. Current texture size=(%d,%d). Convert it to size=(%d,%d) in order to save memory", width_, height_, width_ + neededBytes, height_ ); + NSLog(@"cocos2d: WARNING: File: %@", [path lastPathComponent] ); + NSLog(@"cocos2d: WARNING: For furhter info visit: http://www.cocos2d-iphone.org/forum/topic/31092"); + printf("\n"); + } + } + } +#endif // iOS + + + free(pvrdata); } @@ -394,7 +462,7 @@ - (id)initWithContentsOfURL:(NSURL *)url [self release]; return nil; } - + return [self initWithContentsOfFile:[url path]]; } @@ -409,7 +477,7 @@ + (id)pvrTextureWithContentsOfURL:(NSURL *)url { if (![url isFileURL]) return nil; - + return [CCTexturePVR pvrTextureWithContentsOfFile:[url path]]; } @@ -417,10 +485,10 @@ + (id)pvrTextureWithContentsOfURL:(NSURL *)url - (void)dealloc { CCLOGINFO( @"cocos2d: deallocing %@", self); - + if (name_ != 0 && ! retainName_ ) - glDeleteTextures(1, &name_); - + ccGLDeleteTexture( name_ ); + [super dealloc]; } diff --git a/cocos2d/cocos2d/CCTileMapAtlas.h b/cocos2d/cocos2d/CCTileMapAtlas.h old mode 100644 new mode 100755 index 102ae46..e8365e4 --- a/cocos2d/cocos2d/CCTileMapAtlas.h +++ b/cocos2d/cocos2d/CCTileMapAtlas.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,15 +29,15 @@ #import "Support/TGAlib.h" /** CCTileMapAtlas is a subclass of CCAtlasNode. - + It knows how to render a map based of tiles. The tiles must be in a .PNG format while the map must be a .TGA file. - + For more information regarding the format, please see this post: http://www.cocos2d-iphone.org/archives/27 - + All features from CCAtlasNode are valid in CCTileMapAtlas - + IMPORTANT: This class is deprecated. It is maintained for compatibility reasons only. You SHOULD not use this class. @@ -45,13 +45,13 @@ */ @interface CCTileMapAtlas : CCAtlasNode { - + /// info about the map file tImageTGA *tgaInfo; - + /// x,y to altas dicctionary NSMutableDictionary *posToAtlasIndex; - + /// numbers of tiles to render int itemsToRender; } diff --git a/cocos2d/cocos2d/CCTileMapAtlas.m b/cocos2d/cocos2d/CCTileMapAtlas.m old mode 100644 new mode 100755 index aef6fe0..30168a8 --- a/cocos2d/cocos2d/CCTileMapAtlas.m +++ b/cocos2d/cocos2d/CCTileMapAtlas.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -54,10 +54,12 @@ -(id) initWithTileFile:(NSString*)tile mapFile:(NSString*)map tileWidth:(int)w t if( (self=[super initWithTileFile:tile tileWidth:w tileHeight:h itemsToRender: itemsToRender]) ) { + color_ = ccWHITE; + posToAtlasIndex = [[NSMutableDictionary dictionaryWithCapacity:itemsToRender] retain]; [self updateAtlasValues]; - + [self setContentSize: CGSizeMake(tgaInfo->width*itemWidth_, tgaInfo->height*itemHeight_)]; } @@ -78,7 +80,7 @@ -(void) releaseMap { if( tgaInfo ) tgaDestroy(tgaInfo); - + tgaInfo = nil; [posToAtlasIndex release]; @@ -104,18 +106,18 @@ -(void) loadTGAfile:(NSString*)file { NSAssert( file != nil, @"file must be non-nil"); - NSString *path = [CCFileUtils fullPathFromRelativePath:file ]; + NSString *path = [[CCFileUtils sharedFileUtils] fullPathFromRelativePath:file ]; // //Find the path of the file // NSBundle *mainBndl = [CCDirector sharedDirector].loadingBundle; // NSString *resourcePath = [mainBndl resourcePath]; // NSString * path = [resourcePath stringByAppendingPathComponent:file]; - + tgaInfo = tgaLoad( [path UTF8String] ); #if 1 if( tgaInfo->status != TGA_OK ) [NSException raise:@"TileMapAtlasLoadTGA" format:@"TileMapAtas cannot load TGA file"]; - + #endif } @@ -128,19 +130,19 @@ -(void) setTile:(ccColor3B) tile at:(ccGridSize) pos NSAssert( pos.x < tgaInfo->width, @"Invalid position.x"); NSAssert( pos.y < tgaInfo->height, @"Invalid position.x"); NSAssert( tile.r != 0, @"R component must be non 0"); - + ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData; ccColor3B value = ptr[pos.x + pos.y * tgaInfo->width]; if( value.r == 0 ) CCLOG(@"cocos2d: Value.r must be non 0."); else { ptr[pos.x + pos.y * tgaInfo->width] = tile; - + // XXX: this method consumes a lot of memory // XXX: a tree of something like that shall be impolemented - NSNumber *num = [posToAtlasIndex objectForKey: [NSString stringWithFormat:@"%d,%d", pos.x, pos.y]]; + NSNumber *num = [posToAtlasIndex objectForKey: [NSString stringWithFormat:@"%ld,%ld", (long)pos.x, (long)pos.y]]; [self updateAtlasValueAt:pos withValue:tile withIndex: [num integerValue]]; - } + } } -(ccColor3B) tileAt:(ccGridSize) pos @@ -148,11 +150,11 @@ -(ccColor3B) tileAt:(ccGridSize) pos NSAssert( tgaInfo != nil, @"tgaInfo must not be nil"); NSAssert( pos.x < tgaInfo->width, @"Invalid position.x"); NSAssert( pos.y < tgaInfo->height, @"Invalid position.y"); - + ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData; ccColor3B value = ptr[pos.x + pos.y * tgaInfo->width]; - - return value; + + return value; } -(void) updateAtlasValueAt:(ccGridSize)pos withValue:(ccColor3B)value withIndex:(NSUInteger)idx @@ -163,22 +165,26 @@ -(void) updateAtlasValueAt:(ccGridSize)pos withValue:(ccColor3B)value withIndex: NSInteger y = pos.y; float row = (value.r % itemsPerRow_); float col = (value.r / itemsPerRow_); - + float textureWide = [[textureAtlas_ texture] pixelsWide]; float textureHigh = [[textureAtlas_ texture] pixelsHigh]; + float itemWidthInPixels = itemWidth_ * CC_CONTENT_SCALE_FACTOR(); + float itemHeightInPixels = itemHeight_ * CC_CONTENT_SCALE_FACTOR(); + + #if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL - float left = (2*row*itemWidth_+1)/(2*textureWide); - float right = left+(itemWidth_*2-2)/(2*textureWide); - float top = (2*col*itemHeight_+1)/(2*textureHigh); - float bottom = top+(itemHeight_*2-2)/(2*textureHigh); + float left = (2*row*itemWidthInPixels+1)/(2*textureWide); + float right = left+(itemWidthInPixels*2-2)/(2*textureWide); + float top = (2*col*itemHeightInPixels+1)/(2*textureHigh); + float bottom = top+(itemHeightInPixels*2-2)/(2*textureHigh); #else - float left = (row*itemWidth_)/textureWide; - float right = left+itemWidth_/textureWide; - float top = (col*itemHeight_)/textureHigh; - float bottom = top+itemHeight_/textureHigh; + float left = (row*itemWidthInPixels)/textureWide; + float right = left+itemWidthInPixels/textureWide; + float top = (col*itemHeightInPixels)/textureHigh; + float bottom = top+itemHeightInPixels/textureHigh; #endif - + quad.tl.texCoords.u = left; quad.tl.texCoords.v = top; @@ -201,7 +207,12 @@ -(void) updateAtlasValueAt:(ccGridSize)pos withValue:(ccColor3B)value withIndex: quad.tr.vertices.x = (int)(x * itemWidth_ + itemWidth_); quad.tr.vertices.y = (int)(y * itemHeight_ + itemHeight_); quad.tr.vertices.z = 0.0f; - + + ccColor4B color = { color_.r, color_.g, color_.b, opacity_ }; + quad.tr.colors = color; + quad.tl.colors = color; + quad.br.colors = color; + quad.bl.colors = color; [textureAtlas_ updateQuad:&quad atIndex:idx]; } @@ -209,7 +220,7 @@ -(void) updateAtlasValues { NSAssert( tgaInfo != nil, @"tgaInfo must be non-nil"); - + int total = 0; for(int x = 0;x < tgaInfo->width; x++ ) { @@ -217,10 +228,10 @@ -(void) updateAtlasValues if( total < itemsToRender ) { ccColor3B *ptr = (ccColor3B*) tgaInfo->imageData; ccColor3B value = ptr[x + y * tgaInfo->width]; - + if( value.r != 0 ) { [self updateAtlasValueAt:ccg(x,y) withValue:value withIndex:total]; - + NSString *key = [NSString stringWithFormat:@"%d,%d", x,y]; NSNumber *num = [NSNumber numberWithInt:total]; [posToAtlasIndex setObject:num forKey:key]; diff --git a/cocos2d/cocos2d/CCTransition.h b/cocos2d/cocos2d/CCTransition.h old mode 100644 new mode 100755 index e37d3e8..f57a949 --- a/cocos2d/cocos2d/CCTransition.h +++ b/cocos2d/cocos2d/CCTransition.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -87,14 +87,14 @@ typedef enum { /** CCTransitionRotoZoom: - Rotate and zoom out the outgoing scene, and then rotate and zoom in the incoming + Rotate and zoom out the outgoing scene, and then rotate and zoom in the incoming */ @interface CCTransitionRotoZoom : CCTransitionScene {} @end /** CCTransitionJumpZoom: - Zoom out and jump the outgoing scene, and then jump and zoom in the incoming + Zoom out and jump the outgoing scene, and then jump and zoom in the incoming */ @interface CCTransitionJumpZoom : CCTransitionScene {} @@ -121,7 +121,7 @@ typedef enum { /** CCTransitionMoveInT: Move in from to the top the incoming scene. */ -@interface CCTransitionMoveInT : CCTransitionMoveInL +@interface CCTransitionMoveInT : CCTransitionMoveInL {} @end @@ -146,7 +146,7 @@ typedef enum { /** CCTransitionSlideInR: Slide in the incoming scene from the right border. */ -@interface CCTransitionSlideInR : CCTransitionSlideInL +@interface CCTransitionSlideInR : CCTransitionSlideInL {} @end diff --git a/cocos2d/cocos2d/CCTransition.m b/cocos2d/cocos2d/CCTransition.m old mode 100644 new mode 100755 index 22eed50..f360a29 --- a/cocos2d/cocos2d/CCTransition.m +++ b/cocos2d/cocos2d/CCTransition.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,6 +28,7 @@ #import "CCTransition.h" #import "CCNode.h" +#import "CCSprite.h" #import "CCDirector.h" #import "CCActionInterval.h" #import "CCActionInstant.h" @@ -37,16 +38,18 @@ #import "CCActionTiledGrid.h" #import "CCActionEase.h" #import "CCRenderTexture.h" +#import "ccMacros.h" #import "Support/CGPointExtension.h" -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import "Platforms/iOS/CCTouchDispatcher.h" -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "Platforms/iOS/CCDirectorIOS.h" +#elif defined(__CC_PLATFORM_MAC) +#import "Platforms/Mac/CCDirectorMac.h" #import "Platforms/Mac/CCEventDispatcher.h" #endif -const uint32_t kSceneFade = 0xFADEFADE; +const NSInteger kSceneFade = 0xFADEFADE; @interface CCTransitionScene (Private) @@ -63,23 +66,24 @@ +(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s -(id) initWithDuration:(ccTime) t scene:(CCScene*)s { NSAssert( s != nil, @"Argument scene must be non-nil"); - + if( (self=[super init]) ) { - + duration_ = t; - + // retain inScene_ = [s retain]; outScene_ = [[CCDirector sharedDirector] runningScene]; [outScene_ retain]; - + NSAssert( inScene_ != outScene_, @"Incoming scene must be different from the outgoing scene" ); // disable events while transitions -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - [[CCTouchDispatcher sharedDispatcher] setDispatchEvents: NO]; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) - [[CCEventDispatcher sharedDispatcher] setDispatchEvents: NO]; + CCDirector *director = [CCDirector sharedDirector]; +#ifdef __CC_PLATFORM_IOS + [[director touchDispatcher] setDispatchEvents: NO]; +#elif defined(__CC_PLATFORM_MAC) + [[director eventDispatcher] setDispatchEvents: NO]; #endif [self sceneOrder]; @@ -106,42 +110,42 @@ -(void) draw -(void) finish { - /* clean up */ + /* clean up */ [inScene_ setVisible:YES]; [inScene_ setPosition:ccp(0,0)]; [inScene_ setScale:1.0f]; [inScene_ setRotation:0.0f]; [inScene_.camera restore]; - + [outScene_ setVisible:NO]; [outScene_ setPosition:ccp(0,0)]; [outScene_ setScale:1.0f]; [outScene_ setRotation:0.0f]; [outScene_.camera restore]; - + [self schedule:@selector(setNewScene:) interval:0]; } -(void) setNewScene: (ccTime) dt -{ +{ [self unschedule:_cmd]; - + CCDirector *director = [CCDirector sharedDirector]; - + // Before replacing, save the "send cleanup to scene" sendCleanupToScene_ = [director sendCleanupToScene]; - + [director replaceScene: inScene_]; // enable events while transitions -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - [[CCTouchDispatcher sharedDispatcher] setDispatchEvents: YES]; -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) - [[CCEventDispatcher sharedDispatcher] setDispatchEvents: YES]; +#ifdef __CC_PLATFORM_IOS + [[director touchDispatcher] setDispatchEvents: YES]; +#elif defined(__CC_PLATFORM_MAC) + [[director eventDispatcher] setDispatchEvents: YES]; #endif - + // issue #267 - [outScene_ setVisible:YES]; + [outScene_ setVisible:YES]; } -(void) hideOutShowIn @@ -154,8 +158,12 @@ -(void) hideOutShowIn -(void) onEnter { [super onEnter]; + + // outScene_ should not receive the onExit callback + // only the onExitTransitionDidStart + [outScene_ onExitTransitionDidStart]; + [inScene_ onEnter]; - // outScene_ should not receive the onEnter callback } // custom onExit @@ -164,7 +172,7 @@ -(void) onExit [super onExit]; [outScene_ onExit]; - // inScene_ should not receive the onExit callback + // inScene_ should not receive the onEnter callback // only the onEnterTransitionDidFinish [inScene_ onEnterTransitionDidFinish]; } @@ -173,7 +181,7 @@ -(void) onExit -(void) cleanup { [super cleanup]; - + if( sendCleanupToScene_ ) [outScene_ cleanup]; } @@ -211,21 +219,21 @@ @implementation CCTransitionRotoZoom -(void) onEnter { [super onEnter]; - + [inScene_ setScale:0.001f]; [outScene_ setScale:1.0f]; - + [inScene_ setAnchorPoint:ccp(0.5f, 0.5f)]; [outScene_ setAnchorPoint:ccp(0.5f, 0.5f)]; - + CCActionInterval *rotozoom = [CCSequence actions: [CCSpawn actions: [CCScaleBy actionWithDuration:duration_/2 scale:0.001f], [CCRotateBy actionWithDuration:duration_/2 angle:360 *2], nil], [CCDelayTime actionWithDuration:duration_/2], nil]; - - + + [outScene_ runAction: rotozoom]; [inScene_ runAction: [CCSequence actions: [rotozoom reverse], @@ -242,7 +250,7 @@ -(void) onEnter { [super onEnter]; CGSize s = [[CCDirector sharedDirector] winSize]; - + [inScene_ setScale:0.5f]; [inScene_ setPosition:ccp( s.width,0 )]; @@ -252,12 +260,12 @@ -(void) onEnter CCActionInterval *jump = [CCJumpBy actionWithDuration:duration_/4 position:ccp(-s.width,0) height:s.width/4 jumps:2]; CCActionInterval *scaleIn = [CCScaleTo actionWithDuration:duration_/4 scale:1.0f]; CCActionInterval *scaleOut = [CCScaleTo actionWithDuration:duration_/4 scale:0.5f]; - + CCActionInterval *jumpZoomOut = [CCSequence actions: scaleOut, jump, nil]; CCActionInterval *jumpZoomIn = [CCSequence actions: jump, scaleIn, nil]; - + CCActionInterval *delay = [CCDelayTime actionWithDuration:duration_/2]; - + [outScene_ runAction: jumpZoomOut]; [inScene_ runAction: [CCSequence actions: delay, jumpZoomIn, @@ -273,9 +281,9 @@ @implementation CCTransitionMoveInL -(void) onEnter { [super onEnter]; - + [self initScenes]; - + CCActionInterval *a = [self action]; [inScene_ runAction: [CCSequence actions: @@ -283,7 +291,7 @@ -(void) onEnter [CCCallFunc actionWithTarget:self selector:@selector(finish)], nil] ]; - + } -(CCActionInterval*) action { @@ -351,7 +359,7 @@ -(void) onEnter [super onEnter]; [self initScenes]; - + CCActionInterval *in = [self action]; CCActionInterval *out = [self action]; @@ -360,7 +368,7 @@ -(void) onEnter [self easeActionWithAction:out], [CCCallFunc actionWithTarget:self selector:@selector(finish)], nil]; - + [inScene_ runAction: inAction]; [outScene_ runAction: outAction]; } @@ -460,13 +468,13 @@ @implementation CCTransitionShrinkGrow -(void) onEnter { [super onEnter]; - + [inScene_ setScale:0.001f]; [outScene_ setScale:1.0f]; [inScene_ setAnchorPoint:ccp(2/3.0f,0.5f)]; - [outScene_ setAnchorPoint:ccp(1/3.0f,0.5f)]; - + [outScene_ setAnchorPoint:ccp(1/3.0f,0.5f)]; + CCActionInterval *scaleOut = [CCScaleTo actionWithDuration:duration_ scale:0.01f]; CCActionInterval *scaleIn = [CCScaleTo actionWithDuration:duration_ scale:1.0f]; @@ -490,7 +498,7 @@ @implementation CCTransitionFlipX -(void) onEnter { [super onEnter]; - + CCActionInterval *inA, *outA; [inScene_ setVisible: NO]; @@ -508,7 +516,7 @@ -(void) onEnter outDeltaZ = -90; outAngleZ = 0; } - + inA = [CCSequence actions: [CCDelayTime actionWithDuration:duration_/2], [CCShow action], @@ -518,12 +526,12 @@ -(void) onEnter outA = [CCSequence actions: [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:0 deltaAngleX:0], [CCHide action], - [CCDelayTime actionWithDuration:duration_/2], + [CCDelayTime actionWithDuration:duration_/2], nil ]; - + [inScene_ runAction: inA]; [outScene_ runAction: outA]; - + } @end @@ -534,7 +542,7 @@ @implementation CCTransitionFlipY -(void) onEnter { [super onEnter]; - + CCActionInterval *inA, *outA; [inScene_ setVisible: NO]; @@ -561,12 +569,12 @@ -(void) onEnter outA = [CCSequence actions: [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:90 deltaAngleX:0], [CCHide action], - [CCDelayTime actionWithDuration:duration_/2], + [CCDelayTime actionWithDuration:duration_/2], nil ]; - + [inScene_ runAction: inA]; [outScene_ runAction: outA]; - + } @end @@ -577,7 +585,7 @@ @implementation CCTransitionFlipAngular -(void) onEnter { [super onEnter]; - + CCActionInterval *inA, *outA; [inScene_ setVisible: NO]; @@ -604,7 +612,7 @@ -(void) onEnter outA = [CCSequence actions: [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:45 deltaAngleX:0], [CCHide action], - [CCDelayTime actionWithDuration:duration_/2], + [CCDelayTime actionWithDuration:duration_/2], nil ]; [inScene_ runAction: inA]; @@ -619,13 +627,13 @@ @implementation CCTransitionZoomFlipX -(void) onEnter { [super onEnter]; - + CCActionInterval *inA, *outA; [inScene_ setVisible: NO]; - + float inDeltaZ, inAngleZ; float outDeltaZ, outAngleZ; - + if( orientation == kOrientationRightOver ) { inDeltaZ = 90; inAngleZ = 270; @@ -652,9 +660,9 @@ -(void) onEnter [CCScaleTo actionWithDuration:duration_/2 scale:0.5f], nil], [CCHide action], - [CCDelayTime actionWithDuration:duration_/2], + [CCDelayTime actionWithDuration:duration_/2], nil ]; - + inScene_.scale = 0.5f; [inScene_ runAction: inA]; [outScene_ runAction: outA]; @@ -668,10 +676,10 @@ @implementation CCTransitionZoomFlipY -(void) onEnter { [super onEnter]; - + CCActionInterval *inA, *outA; [inScene_ setVisible: NO]; - + float inDeltaZ, inAngleZ; float outDeltaZ, outAngleZ; @@ -686,7 +694,7 @@ -(void) onEnter outDeltaZ = -90; outAngleZ = 0; } - + inA = [CCSequence actions: [CCDelayTime actionWithDuration:duration_/2], [CCSpawn actions: @@ -700,9 +708,9 @@ -(void) onEnter [CCSpawn actions: [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:90 deltaAngleX:0], [CCScaleTo actionWithDuration:duration_/2 scale:0.5f], - nil], + nil], [CCHide action], - [CCDelayTime actionWithDuration:duration_/2], + [CCDelayTime actionWithDuration:duration_/2], nil ]; inScene_.scale = 0.5f; @@ -718,13 +726,13 @@ @implementation CCTransitionZoomFlipAngular -(void) onEnter { [super onEnter]; - + CCActionInterval *inA, *outA; [inScene_ setVisible: NO]; - + float inDeltaZ, inAngleZ; float outDeltaZ, outAngleZ; - + if( orientation == kOrientationRightOver ) { inDeltaZ = 90; inAngleZ = 270; @@ -736,14 +744,14 @@ -(void) onEnter outDeltaZ = -90; outAngleZ = 0; } - + inA = [CCSequence actions: [CCDelayTime actionWithDuration:duration_/2], [CCSpawn actions: [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:inAngleZ deltaAngleZ:inDeltaZ angleX:-45 deltaAngleX:0], [CCScaleTo actionWithDuration:duration_/2 scale:1], [CCShow action], - nil], + nil], [CCShow action], [CCCallFunc actionWithTarget:self selector:@selector(finish)], nil ]; @@ -751,11 +759,11 @@ -(void) onEnter [CCSpawn actions: [CCOrbitCamera actionWithDuration: duration_/2 radius: 1 deltaRadius:0 angleZ:outAngleZ deltaAngleZ:outDeltaZ angleX:45 deltaAngleX:0], [CCScaleTo actionWithDuration:duration_/2 scale:0.5f], - nil], + nil], [CCHide action], - [CCDelayTime actionWithDuration:duration_/2], + [CCDelayTime actionWithDuration:duration_/2], nil ]; - + inScene_.scale = 0.5f; [inScene_ runAction: inA]; [outScene_ runAction: outA]; @@ -779,7 +787,7 @@ -(id) initWithDuration:(ccTime)d scene:(CCScene*)s withColor:(ccColor3B)aColor color.g = aColor.g; color.b = aColor.b; } - + return self; } @@ -791,15 +799,15 @@ -(id) initWithDuration:(ccTime)d scene:(CCScene*)s -(void) onEnter { [super onEnter]; - + CCLayerColor *l = [CCLayerColor layerWithColor:color]; [inScene_ setVisible: NO]; - + [self addChild: l z:2 tag:kSceneFade]; - - + + CCNode *f = [self getChildByTag:kSceneFade]; - + CCActionInterval *a = [CCSequence actions: [CCFadeIn actionWithDuration:duration_/2], [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)], @@ -830,63 +838,63 @@ -(void) draw -(void) onEnter { [super onEnter]; - + // create a transparent color layer // in which we are going to add our rendertextures ccColor4B color = {0,0,0,0}; CGSize size = [[CCDirector sharedDirector] winSize]; CCLayerColor * layer = [CCLayerColor layerWithColor:color]; - + // create the first render texture for inScene_ CCRenderTexture *inTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height]; inTexture.sprite.anchorPoint= ccp(0.5f,0.5f); inTexture.position = ccp(size.width/2, size.height/2); inTexture.anchorPoint = ccp(0.5f,0.5f); - + // render inScene_ to its texturebuffer [inTexture begin]; [inScene_ visit]; [inTexture end]; - + // create the second render texture for outScene_ CCRenderTexture *outTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height]; outTexture.sprite.anchorPoint= ccp(0.5f,0.5f); outTexture.position = ccp(size.width/2, size.height/2); outTexture.anchorPoint = ccp(0.5f,0.5f); - + // render outScene_ to its texturebuffer [outTexture begin]; [outScene_ visit]; [outTexture end]; - + // create blend functions - + ccBlendFunc blend1 = {GL_ONE, GL_ONE}; // inScene_ will lay on background and will not be used with alpha - ccBlendFunc blend2 = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}; // we are going to blend outScene_ via alpha - + ccBlendFunc blend2 = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}; // we are going to blend outScene_ via alpha + // set blendfunctions [inTexture.sprite setBlendFunc:blend1]; - [outTexture.sprite setBlendFunc:blend2]; - + [outTexture.sprite setBlendFunc:blend2]; + // add render textures to the layer [layer addChild:inTexture]; [layer addChild:outTexture]; - + // initial opacity: [inTexture.sprite setOpacity:255]; [outTexture.sprite setOpacity:255]; - + // create the blend action CCActionInterval * layerAction = [CCSequence actions: [CCFadeTo actionWithDuration:duration_ opacity:0], [CCCallFunc actionWithTarget:self selector:@selector(hideOutShowIn)], [CCCallFunc actionWithTarget:self selector:@selector(finish)], nil ]; - - + + // run the blend action [outTexture.sprite runAction: layerAction]; - + // add the layer (which contains our two rendertextures) to the scene [self addChild: layer z:2 tag:kSceneFade]; } @@ -894,10 +902,10 @@ -(void) onEnter // clean up on exit -(void) onExit { - // remove our layer and release all containing objects + // remove our layer and release all containing objects [self removeChildByTag:kSceneFade cleanup:NO]; - - [super onExit]; + + [super onExit]; } @end @@ -919,7 +927,7 @@ -(void) onEnter float aspect = s.width / s.height; int x = 12 * aspect; int y = 12; - + id toff = [CCTurnOffTiles actionWithSize: ccg(x,y) duration:duration_]; id action = [self easeActionWithAction:toff]; [outScene_ runAction: [CCSequence actions: action, @@ -948,7 +956,7 @@ -(void) onEnter [super onEnter]; inScene_.visible = NO; - + id split = [self action]; id seq = [CCSequence actions: split, @@ -1000,12 +1008,12 @@ -(void) sceneOrder -(void) onEnter { [super onEnter]; - + CGSize s = [[CCDirector sharedDirector] winSize]; float aspect = s.width / s.height; int x = 12 * aspect; int y = 12; - + id action = [self actionWithSize:ccg(x,y)]; [outScene_ runAction: [CCSequence actions: @@ -1024,7 +1032,7 @@ -(CCActionInterval*) actionWithSize: (ccGridSize) v -(CCActionInterval*) easeActionWithAction:(CCActionInterval*)action { return action; -// return [EaseIn actionWithAction:action rate:2.0f]; +// return [CCEaseOut actionWithAction:action rate:3.0f]; } @end diff --git a/cocos2d/cocos2d/CCTransitionPageTurn.h b/cocos2d/cocos2d/CCTransitionPageTurn.h old mode 100644 new mode 100755 index aacb7fc..1e83c08 --- a/cocos2d/cocos2d/CCTransitionPageTurn.h +++ b/cocos2d/cocos2d/CCTransitionPageTurn.h @@ -2,17 +2,17 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/ - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -30,10 +30,10 @@ * A transition which peels back the bottom right hand corner of a scene * to transition to the scene beneath it simulating a page turn * - * This uses a 3DAction so it's strongly recommended that depth buffering + * This uses a 3DAction so it is strongly recommended that depth buffering * is turned on in CCDirector using: * - * [[CCDirector sharedDirector] setDepthBufferFormat:kCCDepthBuffer16]; + * [[CCDirector sharedDirector] setDepthBufferFormat:kCCDepthBuffer16]; * * @since v0.8.2 */ @@ -43,14 +43,14 @@ } /** * creates a base transition with duration and incoming scene - * if back is TRUE then the effect is reversed to appear as if the incoming + * if back is TRUE then the effect is reversed to appear as if the incoming * scene is being turned from left over the outgoing scene */ +(id) transitionWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back; /** * creates a base transition with duration and incoming scene - * if back is TRUE then the effect is reversed to appear as if the incoming + * if back is TRUE then the effect is reversed to appear as if the incoming * scene is being turned from left over the outgoing scene */ -(id) initWithDuration:(ccTime) t scene:(CCScene*)s backwards:(BOOL) back; diff --git a/cocos2d/cocos2d/CCTransitionPageTurn.m b/cocos2d/cocos2d/CCTransitionPageTurn.m old mode 100644 new mode 100755 index bff43a7..810284f --- a/cocos2d/cocos2d/CCTransitionPageTurn.m +++ b/cocos2d/cocos2d/CCTransitionPageTurn.m @@ -2,17 +2,17 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2009 Sindesso Pty Ltd http://www.sindesso.com/ - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -58,7 +58,7 @@ -(void) sceneOrder -(void) onEnter { [super onEnter]; - + CGSize s = [[CCDirector sharedDirector] winSize]; int x, y; if( s.width > s.height) @@ -71,9 +71,9 @@ -(void) onEnter x = 12; y = 16; } - + id action = [self actionWithSize:ccg(x,y)]; - + if(! back_ ) { [outScene_ runAction: [CCSequence actions: @@ -95,7 +95,7 @@ -(void) onEnter nil] ]; } - + } -(CCActionInterval*) actionWithSize: (ccGridSize) v diff --git a/cocos2d/cocos2d/CCTransitionRadial.h b/cocos2d/cocos2d/CCTransitionProgress.h old mode 100644 new mode 100755 similarity index 67% rename from cocos2d/cocos2d/CCTransitionRadial.h rename to cocos2d/cocos2d/CCTransitionProgress.h index 6d4a5e0..241ff63 --- a/cocos2d/cocos2d/CCTransitionRadial.h +++ b/cocos2d/cocos2d/CCTransitionProgress.h @@ -2,17 +2,19 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2009 Lam Pham - * + * + * Copyright (c) 2012 Ricardo Quesada + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,17 +26,37 @@ */ #import "CCTransition.h" -#import "CCProgressTimer.h" -#import "CCActionProgressTimer.h" + +@interface CCTransitionProgress : CCTransitionScene +{ + float to_, from_; + CCScene *sceneToBeModified_; +} +@end /** CCTransitionRadialCCW transition. A counter colock-wise radial transition to the next scene */ -@interface CCTransitionRadialCCW : CCTransitionScene +@interface CCTransitionProgressRadialCCW : CCTransitionProgress @end /** CCTransitionRadialCW transition. A counter colock-wise radial transition to the next scene */ -@interface CCTransitionRadialCW : CCTransitionRadialCCW +@interface CCTransitionProgressRadialCW : CCTransitionProgress +@end + +/** CCTransitionProgressHorizontal transition. + A colock-wise radial transition to the next scene + */ +@interface CCTransitionProgressHorizontal : CCTransitionProgress +@end + +@interface CCTransitionProgressVertical : CCTransitionProgress +@end + +@interface CCTransitionProgressInOut : CCTransitionProgress +@end + +@interface CCTransitionProgressOutIn : CCTransitionProgress @end diff --git a/cocos2d/cocos2d/CCTransitionProgress.m b/cocos2d/cocos2d/CCTransitionProgress.m new file mode 100755 index 0000000..afe4de6 --- /dev/null +++ b/cocos2d/cocos2d/CCTransitionProgress.m @@ -0,0 +1,276 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Lam Pham + * + * Copyright (c) 2012 Ricardo Quesada + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + +#import "CCTransitionProgress.h" +#import "CCDirector.h" +#import "CCRenderTexture.h" +#import "CCLayer.h" +#import "CCActionInstant.h" +#import "CCProgressTimer.h" +#import "CCActionProgressTimer.h" +#import "Support/CGPointExtension.h" + +enum { + kCCSceneRadial = 0xc001, +}; + +#pragma mark - CCTransitionProgress + +@interface CCTransitionProgress() +-(CCProgressTimer*) progressTimerNodeWithRenderTexture:(CCRenderTexture*)texture; +-(void) setupTransition; +@end + +@implementation CCTransitionProgress +-(void) onEnter +{ + [super onEnter]; + + [self setupTransition]; + + // create a transparent color layer + // in which we are going to add our rendertextures + CGSize size = [[CCDirector sharedDirector] winSize]; + + // create the second render texture for outScene + CCRenderTexture *texture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height]; + texture.sprite.anchorPoint= ccp(0.5f,0.5f); + texture.position = ccp(size.width/2, size.height/2); + texture.anchorPoint = ccp(0.5f,0.5f); + + // render outScene to its texturebuffer + [texture clear:0 g:0 b:0 a:1]; + [texture begin]; + [sceneToBeModified_ visit]; + [texture end]; + + + // Since we've passed the outScene to the texture we don't need it. + if( sceneToBeModified_ == outScene_ ) + [self hideOutShowIn]; + + // We need the texture in RenderTexture. + CCProgressTimer *node = [self progressTimerNodeWithRenderTexture:texture]; + + // create the blend action + CCActionInterval * layerAction = [CCSequence actions: + [CCProgressFromTo actionWithDuration:duration_ from:from_ to:to_], + [CCCallFunc actionWithTarget:self selector:@selector(finish)], + nil ]; + // run the blend action + [node runAction: layerAction]; + + // add the layer (which contains our two rendertextures) to the scene + [self addChild: node z:2 tag:kCCSceneRadial]; +} + +// clean up on exit +-(void) onExit +{ + // remove our layer and release all containing objects + [self removeChildByTag:kCCSceneRadial cleanup:NO]; + [super onExit]; +} + +-(void) sceneOrder +{ + inSceneOnTop_ = NO; +} + +-(void) setupTransition +{ + sceneToBeModified_ = outScene_; + from_ = 100; + to_ = 0; +} + +-(CCProgressTimer*) progressTimerNodeWithRenderTexture:(CCRenderTexture*)texture +{ + NSAssert(NO, @"override me - abstract class"); + + return nil; +} +@end + +#pragma mark - CCTransitionProgressRadialCCW + +@implementation CCTransitionProgressRadialCCW +-(CCProgressTimer*) progressTimerNodeWithRenderTexture:(CCRenderTexture*)texture +{ + CGSize size = [[CCDirector sharedDirector] winSize]; + + CCProgressTimer *node = [CCProgressTimer progressWithSprite:texture.sprite]; + + // but it is flipped upside down so we flip the sprite + node.sprite.flipY = YES; + node.type = kCCProgressTimerTypeRadial; + + // Return the radial type that we want to use + node.reverseDirection = NO; + node.percentage = 100; + node.position = ccp(size.width/2, size.height/2); + node.anchorPoint = ccp(0.5f,0.5f); + + return node; +} +@end + +#pragma mark - CCTransitionProgressRadialCW + +@implementation CCTransitionProgressRadialCW +-(CCProgressTimer*) progressTimerNodeWithRenderTexture:(CCRenderTexture*)texture +{ + CGSize size = [[CCDirector sharedDirector] winSize]; + + CCProgressTimer *node = [CCProgressTimer progressWithSprite:texture.sprite]; + + // but it is flipped upside down so we flip the sprite + node.sprite.flipY = YES; + node.type = kCCProgressTimerTypeRadial; + + // Return the radial type that we want to use + node.reverseDirection = YES; + node.percentage = 100; + node.position = ccp(size.width/2, size.height/2); + node.anchorPoint = ccp(0.5f,0.5f); + + return node; +} +@end + +#pragma mark - CCTransitionProgressHorizontal + +@implementation CCTransitionProgressHorizontal +-(CCProgressTimer*) progressTimerNodeWithRenderTexture:(CCRenderTexture*)texture +{ + CGSize size = [[CCDirector sharedDirector] winSize]; + + CCProgressTimer *node = [CCProgressTimer progressWithSprite:texture.sprite]; + + // but it is flipped upside down so we flip the sprite + node.sprite.flipY = YES; + node.type = kCCProgressTimerTypeBar; + + node.midpoint = ccp(1, 0); + node.barChangeRate = ccp(1,0); + + node.percentage = 100; + node.position = ccp(size.width/2, size.height/2); + node.anchorPoint = ccp(0.5f,0.5f); + + return node; +} +@end + +#pragma mark - CCTransitionProgressVertical + +@implementation CCTransitionProgressVertical +-(CCProgressTimer*) progressTimerNodeWithRenderTexture:(CCRenderTexture*)texture +{ + CGSize size = [[CCDirector sharedDirector] winSize]; + + CCProgressTimer *node = [CCProgressTimer progressWithSprite:texture.sprite]; + + // but it is flipped upside down so we flip the sprite + node.sprite.flipY = YES; + node.type = kCCProgressTimerTypeBar; + + node.midpoint = ccp(0, 0); + node.barChangeRate = ccp(0,1); + + node.percentage = 100; + node.position = ccp(size.width/2, size.height/2); + node.anchorPoint = ccp(0.5f,0.5f); + + return node; +} +@end + +#pragma mark - CCTransitionProgressInOut + +@implementation CCTransitionProgressInOut + +-(void) sceneOrder +{ + inSceneOnTop_ = NO; +} + +-(void) setupTransition +{ + sceneToBeModified_ = inScene_; + from_ = 0; + to_ = 100; +} + +-(CCProgressTimer*) progressTimerNodeWithRenderTexture:(CCRenderTexture*)texture +{ + CGSize size = [[CCDirector sharedDirector] winSize]; + + CCProgressTimer *node = [CCProgressTimer progressWithSprite:texture.sprite]; + + // but it is flipped upside down so we flip the sprite + node.sprite.flipY = YES; + node.type = kCCProgressTimerTypeBar; + + node.midpoint = ccp(.5f, .5f); + node.barChangeRate = ccp(1, 1); + + node.percentage = 0; + node.position = ccp(size.width/2, size.height/2); + node.anchorPoint = ccp(0.5f,0.5f); + + return node; +} +@end + +#pragma mark - CCTransitionProgressOutIn + +@implementation CCTransitionProgressOutIn +-(CCProgressTimer*) progressTimerNodeWithRenderTexture:(CCRenderTexture*)texture +{ + CGSize size = [[CCDirector sharedDirector] winSize]; + + CCProgressTimer *node = [CCProgressTimer progressWithSprite:texture.sprite]; + + // but it is flipped upside down so we flip the sprite + node.sprite.flipY = YES; + node.type = kCCProgressTimerTypeBar; + + node.midpoint = ccp(.5f, .5f); + node.barChangeRate = ccp(1, 1); + + node.percentage = 100; + node.position = ccp(size.width/2, size.height/2); + node.anchorPoint = ccp(0.5f,0.5f); + + return node; +} +@end + + + diff --git a/cocos2d/cocos2d/CCTransitionRadial.m b/cocos2d/cocos2d/CCTransitionRadial.m deleted file mode 100644 index a892f35..0000000 --- a/cocos2d/cocos2d/CCTransitionRadial.m +++ /dev/null @@ -1,115 +0,0 @@ -/* - * cocos2d for iPhone: http://www.cocos2d-iphone.org - * - * Copyright (c) 2009 Lam Pham - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - - - -#import "CCDirector.h" -#import "CCTransitionRadial.h" -#import "CCRenderTexture.h" -#import "CCLayer.h" -#import "CCActionInstant.h" -#import "Support/CGPointExtension.h" - -enum { - kSceneRadial = 0xc001, -}; - -#pragma mark - -#pragma mark Transition Radial CCW - -@implementation CCTransitionRadialCCW --(void) sceneOrder -{ - inSceneOnTop_ = NO; -} - --(CCProgressTimerType) radialType -{ - return kCCProgressTimerTypeRadialCCW; -} - --(void) onEnter -{ - [super onEnter]; - // create a transparent color layer - // in which we are going to add our rendertextures - CGSize size = [[CCDirector sharedDirector] winSize]; - - // create the second render texture for outScene - CCRenderTexture *outTexture = [CCRenderTexture renderTextureWithWidth:size.width height:size.height]; - outTexture.sprite.anchorPoint= ccp(0.5f,0.5f); - outTexture.position = ccp(size.width/2, size.height/2); - outTexture.anchorPoint = ccp(0.5f,0.5f); - - // render outScene to its texturebuffer - [outTexture clear:0 g:0 b:0 a:1]; - [outTexture begin]; - [outScene_ visit]; - [outTexture end]; - - // Since we've passed the outScene to the texture we don't need it. - [self hideOutShowIn]; - - // We need the texture in RenderTexture. - CCProgressTimer *outNode = [CCProgressTimer progressWithTexture:outTexture.sprite.texture]; - // but it's flipped upside down so we flip the sprite - outNode.sprite.flipY = YES; - // Return the radial type that we want to use - outNode.type = [self radialType]; - outNode.percentage = 100.f; - outNode.position = ccp(size.width/2, size.height/2); - outNode.anchorPoint = ccp(0.5f,0.5f); - - // create the blend action - CCActionInterval * layerAction = [CCSequence actions: - [CCProgressFromTo actionWithDuration:duration_ from:100.f to:0.f], - [CCCallFunc actionWithTarget:self selector:@selector(finish)], - nil ]; - // run the blend action - [outNode runAction: layerAction]; - - // add the layer (which contains our two rendertextures) to the scene - [self addChild: outNode z:2 tag:kSceneRadial]; -} - -// clean up on exit --(void) onExit -{ - // remove our layer and release all containing objects - [self removeChildByTag:kSceneRadial cleanup:NO]; - [super onExit]; -} -@end - -#pragma mark - -#pragma mark Transition Radial CW - -@implementation CCTransitionRadialCW --(CCProgressTimerType) radialType -{ - return kCCProgressTimerTypeRadialCW; -} -@end - diff --git a/cocos2d/cocos2d/Platforms/CCGL.h b/cocos2d/cocos2d/Platforms/CCGL.h old mode 100644 new mode 100755 index 0725f89..fd08b43 --- a/cocos2d/cocos2d/Platforms/CCGL.h +++ b/cocos2d/cocos2d/Platforms/CCGL.h @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,57 +27,46 @@ // Common layer for OpenGL stuff // -#import +#import "../ccMacros.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#import -#import +#if __CC_PLATFORM_IOS +#import +#import #import -#import "iOS/glu.h" -#import "iOS/EAGLView.h" +#import "iOS/CCGLView.h" -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif __CC_PLATFORM_MAC #import #import #import // needed for NSOpenGLView -#import "Mac/MacGLView.h" +#import "Mac/CCGLView.h" #endif // iOS -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#define CC_GLVIEW EAGLView -#define ccglOrtho glOrthof -#define ccglClearDepth glClearDepthf -#define ccglGenerateMipmap glGenerateMipmapOES -#define ccglGenFramebuffers glGenFramebuffersOES -#define ccglBindFramebuffer glBindFramebufferOES -#define ccglFramebufferTexture2D glFramebufferTexture2DOES -#define ccglDeleteFramebuffers glDeleteFramebuffersOES -#define ccglCheckFramebufferStatus glCheckFramebufferStatusOES -#define ccglTranslate glTranslatef +#if __CC_PLATFORM_IOS +#define glClearDepth glClearDepthf +#define glDeleteVertexArrays glDeleteVertexArraysOES +#define glGenVertexArrays glGenVertexArraysOES +#define glBindVertexArray glBindVertexArrayOES -#define CC_GL_FRAMEBUFFER GL_FRAMEBUFFER_OES -#define CC_GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING_OES -#define CC_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_OES -#define CC_GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_OES +#define CC_GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES // Mac -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) -#define CC_GLVIEW MacGLView -#define ccglOrtho glOrtho -#define ccglClearDepth glClearDepth -#define ccglGenerateMipmap glGenerateMipmap -#define ccglGenFramebuffers glGenFramebuffers -#define ccglBindFramebuffer glBindFramebuffer -#define ccglFramebufferTexture2D glFramebufferTexture2D -#define ccglDeleteFramebuffers glDeleteFramebuffers -#define ccglCheckFramebufferStatus glCheckFramebufferStatus -#define ccglTranslate glTranslated +#elif __CC_PLATFORM_MAC + +#define CC_GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8 -#define CC_GL_FRAMEBUFFER GL_FRAMEBUFFER -#define CC_GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING -#define CC_GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0 -#define CC_GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE +#if 1 +#define glDeleteVertexArrays glDeleteVertexArraysAPPLE +#define glGenVertexArrays glGenVertexArraysAPPLE +#define glBindVertexArray glBindVertexArrayAPPLE + +#else // OpenGL 3.2 Core Profile + +#define glDeleteVertexArrays glDeleteVertexArrays +#define glGenVertexArrays glGenVertexArrays +#define glBindVertexArray glBindVertexArray +#endif #endif diff --git a/cocos2d/cocos2d/Platforms/CCNS.h b/cocos2d/cocos2d/Platforms/CCNS.h old mode 100644 new mode 100755 index c595a18..5a396be --- a/cocos2d/cocos2d/Platforms/CCNS.h +++ b/cocos2d/cocos2d/Platforms/CCNS.h @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,11 +27,11 @@ // Common layer for NS (Next-Step) stuff // -#import +#import "../ccMacros.h" #import // for NSObject -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #define CCRectFromString(__r__) CGRectFromString(__r__) #define CCPointFromString(__p__) CGPointFromString(__p__) @@ -39,21 +39,9 @@ #define CCNSSizeToCGSize #define CCNSRectToCGRect #define CCNSPointToCGPoint -#define CCTextAlignment UITextAlignment -#define CCTextAlignmentCenter UITextAlignmentCenter -#define CCTextAlignmentLeft UITextAlignmentLeft -#define CCTextAlignmentRight UITextAlignmentRight -#define CCLineBreakMode UILineBreakMode -#define CCLineBreakModeWordWrap UILineBreakModeWordWrap -#define CCLineBreakModeCharacterWrap UILineBreakModeCharacterWrap -#define CCLineBreakModeClip UILineBreakModeClip -#define CCLineBreakModeHeadTruncation UILineBreakModeHeadTruncation -#define CCLineBreakModeTailTruncation UILineBreakModeTailTruncation -#define CCLineBreakModeMiddleTruncation UILineBreakModeMiddleTruncation - -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) #define CCRectFromString(__r__) NSRectToCGRect( NSRectFromString(__r__) ) #define CCPointFromString(__p__) NSPointToCGPoint( NSPointFromString(__p__) ) @@ -61,18 +49,6 @@ #define CCNSSizeToCGSize NSSizeToCGSize #define CCNSRectToCGRect NSRectToCGRect #define CCNSPointToCGPoint NSPointToCGPoint -#define CCTextAlignment NSTextAlignment -#define CCTextAlignmentCenter NSCenterTextAlignment -#define CCTextAlignmentLeft NSLeftTextAlignment -#define CCTextAlignmentRight NSRightTextAlignment -#define CCLineBreakMode NSLineBreakMode -#define CCLineBreakModeWordWrap NSLineBreakByWordWrapping -#define CCLineBreakModeClip -1 -#define CCLineBreakModeHeadTruncation -1 -#define CCLineBreakModeTailTruncation -1 -#define CCLineBreakModeMiddleTruncation -1 - - #endif diff --git a/cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.h b/cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.h old mode 100644 new mode 100755 index 0d623b4..63ff82f --- a/cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.h +++ b/cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.h @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,13 +26,14 @@ // Only compile this code on Mac. These files should not be included on your iOS project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_MAC #import #import "../../CCDirector.h" +@class CCEventDispatcher; + enum { /// If the window is resized, it won't be autoscaled kCCDirectorResize_NoScale, @@ -41,7 +42,10 @@ enum { }; @interface CCDirector (MacExtension) -/** converts an NSEvent to GL coordinates */ +/** sets the CCEventDispatcher (Mac only) */ +@property (nonatomic, readwrite, retain) CCEventDispatcher* eventDispatcher; + +/** converts an NSEvent to GL coordinates (Mac only) */ -(CGPoint) convertEventToGL:(NSEvent*)event; @end @@ -54,9 +58,12 @@ enum { int resizeMode_; CGPoint winOffset_; CGSize originalWinSize_; - + NSWindow *fullScreenWindow_; - + + // Event Dispatcher + CCEventDispatcher *eventDispatcher_; + // cache NSWindow *windowGLView_; NSView *superViewGLView_; @@ -99,5 +106,5 @@ enum { } @end -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_MAC diff --git a/cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.m b/cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.m old mode 100644 new mode 100755 index 270b026..baa8e27 --- a/cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.m +++ b/cocos2d/cocos2d/Platforms/Mac/CCDirectorMac.m @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,19 +25,25 @@ // Only compile this code on Mac. These files should not be included on your iOS project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_MAC #import - + #import "CCDirectorMac.h" #import "CCEventDispatcher.h" -#import "MacGLView.h" +#import "CCGLView.h" +#import "CCWindow.h" #import "../../CCNode.h" #import "../../CCScheduler.h" #import "../../ccMacros.h" +#import "../../CCGLProgram.h" +#import "../../ccGLStateCache.h" + +// external +#import "kazmath/kazmath.h" +#import "kazmath/GL/matrix.h" #pragma mark - #pragma mark Director Mac extensions @@ -45,19 +51,30 @@ @interface CCDirector () -(void) setNextScene; --(void) showFPS; +-(void) showStats; -(void) calculateDeltaTime; +-(void) calculateMPF; @end @implementation CCDirector (MacExtension) -(CGPoint) convertEventToGL:(NSEvent*)event { - NSPoint point = [openGLView_ convertPoint:[event locationInWindow] fromView:nil]; + NSPoint point = [[self view] convertPoint:[event locationInWindow] fromView:nil]; CGPoint p = NSPointToCGPoint(point); - + return [(CCDirectorMac*)self convertToLogicalCoordinates:p]; } +-(void) setEventDispatcher:(CCEventDispatcher *)dispatcher +{ + NSAssert(NO, @"override me"); +} + +-(CCEventDispatcher *) eventDispatcher +{ + NSAssert(NO, @"override me"); + return nil; +} @end #pragma mark - @@ -73,21 +90,26 @@ -(id) init if( (self = [super init]) ) { isFullScreen_ = NO; resizeMode_ = kCCDirectorResize_AutoScale; - + originalWinSize_ = CGSizeZero; fullScreenWindow_ = nil; windowGLView_ = nil; winOffset_ = CGPointZero; + + eventDispatcher_ = [[CCEventDispatcher alloc] init]; } - + return self; } - (void) dealloc { + [eventDispatcher_ release]; + [view_ release]; [superViewGLView_ release]; [fullScreenWindow_ release]; [windowGLView_ release]; + [super dealloc]; } @@ -96,84 +118,108 @@ - (void) dealloc // - (void) setFullScreen:(BOOL)fullscreen { +// isFullScreen_ = !isFullScreen_; +// +// if (isFullScreen_) +// { +// [self.view enterFullScreenMode:[[self.view window] screen] withOptions:nil]; +// } +// else +// { +// [self.view exitFullScreenModeWithOptions:nil]; +// [[self.view window] makeFirstResponder: self.view]; +// } +// +// return; + // Mac OS X 10.6 and later offer a simplified mechanism to create full-screen contexts #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 - - if (isFullScreen_ == fullscreen) return; - + + if (isFullScreen_ == fullscreen) + return; + + CCGLView *openGLview = (CCGLView*) self.view; + if( fullscreen ) { - originalWinRect_ = [openGLView_ frame]; + originalWinRect_ = [openGLview frame]; // Cache normal window and superview of openGLView if(!windowGLView_) - windowGLView_ = [[openGLView_ window] retain]; - + windowGLView_ = [[openGLview window] retain]; + [superViewGLView_ release]; - superViewGLView_ = [[openGLView_ superview] retain]; - - + superViewGLView_ = [[openGLview superview] retain]; + + // Get screen size NSRect displayRect = [[NSScreen mainScreen] frame]; - + // Create a screen-sized window on the display you want to take over - fullScreenWindow_ = [[MacWindow alloc] initWithFrame:displayRect fullscreen:YES]; - + fullScreenWindow_ = [[CCWindow alloc] initWithFrame:displayRect fullscreen:YES]; + // Remove glView from window - [openGLView_ removeFromSuperview]; - + [openGLview removeFromSuperview]; + // Set new frame - [openGLView_ setFrame:displayRect]; - + [openGLview setFrame:displayRect]; + // Attach glView to fullscreen window - [fullScreenWindow_ setContentView:openGLView_]; - + [fullScreenWindow_ setContentView:openGLview]; + // Show the fullscreen window [fullScreenWindow_ makeKeyAndOrderFront:self]; [fullScreenWindow_ makeMainWindow]; - + } else { - + // Remove glView from fullscreen window - [openGLView_ removeFromSuperview]; - + [openGLview removeFromSuperview]; + // Release fullscreen window [fullScreenWindow_ release]; fullScreenWindow_ = nil; - + // Attach glView to superview - [superViewGLView_ addSubview:openGLView_]; - + [superViewGLView_ addSubview:openGLview]; + // Set new frame - [openGLView_ setFrame:originalWinRect_]; - + [openGLview setFrame:originalWinRect_]; + // Show the window [windowGLView_ makeKeyAndOrderFront:self]; [windowGLView_ makeMainWindow]; } + + // issue #1189 + [windowGLView_ makeFirstResponder:openGLview]; + isFullScreen_ = fullscreen; - - [openGLView_ retain]; // Retain +1 - + + [openGLview retain]; // Retain +1 + // re-configure glView - [self setOpenGLView:openGLView_]; - - [openGLView_ release]; // Retain -1 - - [openGLView_ setNeedsDisplay:YES]; + [self setView:openGLview]; + + [openGLview release]; // Retain -1 + + [openGLview setNeedsDisplay:YES]; #else #error Full screen is not supported for Mac OS 10.5 or older yet #error If you don't want FullScreen support, you can safely remove these 2 lines #endif } --(void) setOpenGLView:(MacGLView *)view +-(void) setView:(CCGLView *)view { - [super setOpenGLView:view]; - - // cache the NSWindow and NSOpenGLView created from the NIB - if( !isFullScreen_ && CGSizeEqualToSize(originalWinSize_, CGSizeZero)) - { - originalWinSize_ = winSizeInPixels_; + if( view != view_) { + + [super setView:view]; + + // cache the NSWindow and NSOpenGLView created from the NIB + if( !isFullScreen_ && CGSizeEqualToSize(originalWinSize_, CGSizeZero)) + { + originalWinSize_ = winSizeInPixels_; + } } } @@ -189,85 +235,111 @@ -(void) setResizeMode:(int)mode resizeMode_ = mode; [self setProjection:projection_]; - [openGLView_ setNeedsDisplay: YES]; + [self.view setNeedsDisplay: YES]; } } -(void) setProjection:(ccDirectorProjection)projection { CGSize size = winSizeInPixels_; - + CGPoint offset = CGPointZero; float widthAspect = size.width; float heightAspect = size.height; - - + + if( resizeMode_ == kCCDirectorResize_AutoScale && ! CGSizeEqualToSize(originalWinSize_, CGSizeZero ) ) { - + size = originalWinSize_; float aspect = originalWinSize_.width / originalWinSize_.height; widthAspect = winSizeInPixels_.width; heightAspect = winSizeInPixels_.width / aspect; - + if( heightAspect > winSizeInPixels_.height ) { widthAspect = winSizeInPixels_.height * aspect; - heightAspect = winSizeInPixels_.height; + heightAspect = winSizeInPixels_.height; } - + winOffset_.x = (winSizeInPixels_.width - widthAspect) / 2; winOffset_.y = (winSizeInPixels_.height - heightAspect) / 2; - + offset = winOffset_; } switch (projection) { case kCCDirectorProjection2D: + glViewport(offset.x, offset.y, widthAspect, heightAspect); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - ccglOrtho(0, size.width, 0, size.height, -1024, 1024); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + kmGLMatrixMode(KM_GL_PROJECTION); + kmGLLoadIdentity(); + + kmMat4 orthoMatrix; + kmMat4OrthographicProjection(&orthoMatrix, 0, size.width, 0, size.height, -1024, 1024); + kmGLMultMatrix( &orthoMatrix ); + + kmGLMatrixMode(KM_GL_MODELVIEW); + kmGLLoadIdentity(); break; - + + case kCCDirectorProjection3D: + { + + float zeye = [self getZEye]; + glViewport(offset.x, offset.y, widthAspect, heightAspect); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(60, (GLfloat)widthAspect/heightAspect, 0.1f, 1500.0f); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - float eyeZ = size.height * [self getZEye] / winSizeInPixels_.height; - - gluLookAt( size.width/2, size.height/2, eyeZ, - size.width/2, size.height/2, 0, - 0.0f, 1.0f, 0.0f); + kmGLMatrixMode(KM_GL_PROJECTION); + kmGLLoadIdentity(); + + kmMat4 matrixPerspective, matrixLookup; + + // issue #1334 + kmMat4PerspectiveProjection( &matrixPerspective, 60, (GLfloat)size.width/size.height, 0.1f, MAX(zeye*2,1500) ); +// kmMat4PerspectiveProjection( &matrixPerspective, 60, (GLfloat)size.width/size.height, 0.1f, 1500); + + + kmGLMultMatrix(&matrixPerspective); + + + kmGLMatrixMode(KM_GL_MODELVIEW); + kmGLLoadIdentity(); + kmVec3 eye, center, up; + + float eyeZ = size.height * zeye / winSizeInPixels_.height; + + kmVec3Fill( &eye, size.width/2, size.height/2, eyeZ ); + kmVec3Fill( ¢er, size.width/2, size.height/2, 0 ); + kmVec3Fill( &up, 0, 1, 0); + kmMat4LookAt(&matrixLookup, &eye, ¢er, &up); + kmGLMultMatrix(&matrixLookup); break; - + } + case kCCDirectorProjectionCustom: - if( projectionDelegate_ ) - [projectionDelegate_ updateProjection]; + if( [delegate_ respondsToSelector:@selector(updateProjection)] ) + [delegate_ updateProjection]; break; - + default: - CCLOG(@"cocos2d: Director: unrecognized projecgtion"); + CCLOG(@"cocos2d: Director: unrecognized projection"); break; } - + projection_ = projection; + + ccSetProjectionMatrixDirty(); } + // If scaling is supported, then it should always return the original size // otherwise it should return the "real" size. -(CGSize) winSize { if( resizeMode_ == kCCDirectorResize_AutoScale ) return originalWinSize_; - + return winSizeInPixels_; } @@ -279,23 +351,36 @@ -(CGSize) winSizeInPixels - (CGPoint) convertToLogicalCoordinates:(CGPoint)coords { CGPoint ret; - + if( resizeMode_ == kCCDirectorResize_NoScale ) ret = coords; - + else { - + float x_diff = originalWinSize_.width / (winSizeInPixels_.width - winOffset_.x * 2); float y_diff = originalWinSize_.height / (winSizeInPixels_.height - winOffset_.y * 2); - + float adjust_x = (winSizeInPixels_.width * x_diff - originalWinSize_.width ) / 2; float adjust_y = (winSizeInPixels_.height * y_diff - originalWinSize_.height ) / 2; - - ret = CGPointMake( (x_diff * coords.x) - adjust_x, ( y_diff * coords.y ) - adjust_y ); + + ret = CGPointMake( (x_diff * coords.x) - adjust_x, ( y_diff * coords.y ) - adjust_y ); } - + return ret; } + +-(void) setEventDispatcher:(CCEventDispatcher *)dispatcher +{ + if( dispatcher != eventDispatcher_ ) { + [eventDispatcher_ release]; + eventDispatcher_ = [dispatcher retain]; + } +} + +-(CCEventDispatcher *) eventDispatcher +{ + return eventDispatcher_; +} @end @@ -307,23 +392,23 @@ @implementation CCDirectorDisplayLink - (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime { -#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD +#if (CC_DIRECTOR_MAC_THREAD == CC_MAC_USE_DISPLAY_LINK_THREAD) if( ! runningThread_ ) runningThread_ = [NSThread currentThread]; - + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [self drawScene]; - [[CCEventDispatcher sharedDispatcher] dispatchQueuedEvents]; - + + // Process timers and other events [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:nil]; - - [pool release]; + [pool release]; + #else [self performSelector:@selector(drawScene) onThread:runningThread_ withObject:nil waitUntilDone:YES]; #endif - + return kCVReturnSuccess; } @@ -336,41 +421,59 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime - (void) startAnimation { -#if ! CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + if(isAnimating_) + return; + + CCLOG(@"cocos2d: startAnimation"); +#if (CC_DIRECTOR_MAC_THREAD == CC_MAC_USE_OWN_THREAD) runningThread_ = [[NSThread alloc] initWithTarget:self selector:@selector(mainLoop) object:nil]; - [runningThread_ start]; + [runningThread_ start]; +#elif (CC_DIRECTOR_MAC_THREAD == CC_MAC_USE_MAIN_THREAD) + runningThread_ = [NSThread mainThread]; #endif - + gettimeofday( &lastUpdate_, NULL); - + // Create a display link capable of being used with all active displays CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); - + // Set the renderer output callback function CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self); - + // Set the display link for the current renderer - CGLContextObj cglContext = [[openGLView_ openGLContext] CGLContextObj]; - CGLPixelFormatObj cglPixelFormat = [[openGLView_ pixelFormat] CGLPixelFormatObj]; + CCGLView *openGLview = (CCGLView*) self.view; + CGLContextObj cglContext = [[openGLview openGLContext] CGLContextObj]; + CGLPixelFormatObj cglPixelFormat = [[openGLview pixelFormat] CGLPixelFormatObj]; CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat); - + // Activate the display link CVDisplayLinkStart(displayLink); + + isAnimating_ = YES; } - (void) stopAnimation { + if(!isAnimating_) + return; + + CCLOG(@"cocos2d: stopAnimation"); + if( displayLink ) { CVDisplayLinkStop(displayLink); CVDisplayLinkRelease(displayLink); displayLink = NULL; - -#if ! CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + +#if CC_DIRECTOR_MAC_THREAD == CC_MAC_USE_OWN_THREAD [runningThread_ cancel]; [runningThread_ release]; runningThread_ = nil; +#elif (CC_DIRECTOR_MAC_THREAD == CC_MAC_USE_MAIN_THREAD) + runningThread_ = nil; #endif } + + isAnimating_ = NO; } -(void) dealloc @@ -391,91 +494,86 @@ -(void) mainLoop // There is no autorelease pool when this method is called because it will be called from a background thread // It's important to create one or you will leak objects NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - + [[NSRunLoop currentRunLoop] run]; [pool release]; } } - + // // Draw the Scene // - (void) drawScene -{ +{ + /* calculate "global" dt */ + [self calculateDeltaTime]; + // We draw on a secondary thread through the display link // When resizing the view, -reshape is called automatically on the main thread // Add a mutex around to avoid the threads accessing the context simultaneously when resizing - CGLLockContext([[openGLView_ openGLContext] CGLContextObj]); - [[openGLView_ openGLContext] makeCurrentContext]; - - /* calculate "global" dt */ - [self calculateDeltaTime]; - + + [self.view lockOpenGLContext]; + /* tick before glClear: issue #533 */ - if( ! isPaused_ ) { - [[CCScheduler sharedScheduler] tick: dt]; - } - + if( ! isPaused_ ) + [scheduler_ update: dt]; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + /* to avoid flickr, nextScene MUST be here: after tick and before draw. XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */ if( nextScene_ ) [self setNextScene]; - - glPushMatrix(); - - - // By default enable VertexArray, ColorArray, TextureCoordArray and Texture2D - CC_ENABLE_DEFAULT_GL_STATES(); - + + kmGLPushMatrix(); + + /* draw the scene */ [runningScene_ visit]; - + /* draw the notification node */ [notificationNode_ visit]; - if( displayFPS_ ) - [self showFPS]; - -#if CC_ENABLE_PROFILERS - [self showProfilers]; -#endif - - CC_DISABLE_DEFAULT_GL_STATES(); - - glPopMatrix(); - + if( displayStats_ ) + [self showStats]; + + kmGLPopMatrix(); + totalFrames_++; + - [[openGLView_ openGLContext] flushBuffer]; - CGLUnlockContext([[openGLView_ openGLContext] CGLContextObj]); + // flush buffer + [self.view.openGLContext flushBuffer]; + + [self.view unlockOpenGLContext]; + + if( displayStats_ ) + [self calculateMPF]; } // set the event dispatcher --(void) setOpenGLView:(MacGLView *)view +-(void) setView:(CCGLView *)view { - if( view != openGLView_ ) { - - [super setOpenGLView:view]; - - CCEventDispatcher *eventDispatcher = [CCEventDispatcher sharedDispatcher]; - [openGLView_ setEventDelegate: eventDispatcher]; - [eventDispatcher setDispatchEvents: YES]; - - // Enable Touches. Default no. - [view setAcceptsTouchEvents:NO]; + [super setView:view]; + + [view setEventDelegate:eventDispatcher_]; + [eventDispatcher_ setDispatchEvents: YES]; + + // Enable Touches. Default no. + // Only available on OS X 10.6+ +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 + [view setAcceptsTouchEvents:NO]; // [view setAcceptsTouchEvents:YES]; - +#endif - // Synchronize buffer swaps with vertical refresh rate - [[view openGLContext] makeCurrentContext]; - GLint swapInt = 1; - [[view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; - } + + // Synchronize buffer swaps with vertical refresh rate + [[view openGLContext] makeCurrentContext]; + GLint swapInt = 1; + [[view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; } @end -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_MAC diff --git a/cocos2d/cocos2d/Platforms/Mac/CCEventDispatcher.h b/cocos2d/cocos2d/Platforms/Mac/CCEventDispatcher.h old mode 100644 new mode 100755 index 06889e8..fb114bc --- a/cocos2d/cocos2d/Platforms/Mac/CCEventDispatcher.h +++ b/cocos2d/cocos2d/Platforms/Mac/CCEventDispatcher.h @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,13 +25,12 @@ // Only compile this code on Mac. These files should not be included on your iOS project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_MAC #import -#import "MacGLView.h" +#import "CCGLView.h" #import "../../Support/uthash.h" // hack: uthash needs to be imported before utlist to prevent warning #import "../../Support/utlist.h" #import "../../ccConfig.h" @@ -187,36 +186,47 @@ @end +#pragma mark - CCEventObject -#pragma mark - -#pragma mark CCEventDispatcher +@interface CCEventObject : NSObject +{ +@public + NSEvent *event; + SEL selector; +} +@end + +#pragma mark - CCEventDispatcher struct _listEntry; +struct _listDeletedEntry; +struct _listAddedEntry; /** CCEventDispatcher - + This is object is responsible for dispatching the events: - Mouse events - Keyboard events - Touch events - + Only available on Mac */ -@interface CCEventDispatcher : NSObject { +@interface CCEventDispatcher : NSObject { BOOL dispatchEvents_; - + BOOL dispatchingInProgress_; + struct _listEntry *keyboardDelegates_; struct _listEntry *mouseDelegates_; struct _listEntry *touchDelegates_; + + struct _listDeletedEntry *delegatesToBeRemoved_; + struct _listAddedEntry *delegatesToBeAdded_; + } @property (nonatomic, readwrite) BOOL dispatchEvents; - -/** CCEventDispatcher singleton */ -+(CCEventDispatcher*) sharedDispatcher; - #pragma mark CCEventDispatcher - Mouse /** Adds a mouse delegate to the dispatcher's list. @@ -238,7 +248,7 @@ struct _listEntry; /** Adds a Keyboard delegate to the dispatcher's list. Delegates with a lower priority value will be called before higher priority values. All the events will be propgated to all the delegates, unless the one delegate returns YES. - + IMPORTANT: The delegate will be retained. */ -(void) addKeyboardDelegate:(id) delegate priority:(NSInteger)priority; @@ -254,7 +264,7 @@ struct _listEntry; /** Adds a Touch delegate to the dispatcher's list. Delegates with a lower priority value will be called before higher priority values. All the events will be propgated to all the delegates, unless the one delegate returns YES. - + IMPORTANT: The delegate will be retained. */ - (void)addTouchDelegate:(id)delegate priority:(NSInteger)priority; @@ -265,13 +275,9 @@ struct _listEntry; /** Removes all touch delegates, releasing all the delegates */ - (void)removeAllTouchDelegates; -#pragma mark CCEventDispatcher - Dispatch Events - -#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD --(void) dispatchQueuedEvents; -#endif +-(void) dispatchEvent:(CCEventObject*)event; @end -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_MAC diff --git a/cocos2d/cocos2d/Platforms/Mac/CCEventDispatcher.m b/cocos2d/cocos2d/Platforms/Mac/CCEventDispatcher.m old mode 100644 new mode 100755 index 1d1740e..78c8c99 --- a/cocos2d/cocos2d/Platforms/Mac/CCEventDispatcher.m +++ b/cocos2d/cocos2d/Platforms/Mac/CCEventDispatcher.m @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,20 +25,18 @@ // Only compile this code on Mac. These files should not be included on your iOS project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_MAC #import "CCEventDispatcher.h" +#import "../../CCDirector.h" #import "../../ccConfig.h" -static CCEventDispatcher *sharedDispatcher = nil; - enum { // mouse kCCImplementsMouseDown = 1 << 0, kCCImplementsMouseMoved = 1 << 1, - kCCImplementsMouseDragged = 1 << 2, + kCCImplementsMouseDragged = 1 << 2, kCCImplementsMouseUp = 1 << 3, kCCImplementsRightMouseDown = 1 << 4, kCCImplementsRightMouseDragged = 1 << 5, @@ -53,7 +51,7 @@ kCCImplementsTouchesBegan = 1 << 13, kCCImplementsTouchesMoved = 1 << 14, kCCImplementsTouchesEnded = 1 << 15, - kCCImplementsTouchesCancelled = 1 << 16, + kCCImplementsTouchesCancelled = 1 << 16, // keyboard kCCImplementsKeyUp = 1 << 0, @@ -70,43 +68,35 @@ NSUInteger flags; } tListEntry; +typedef struct _listDeletedEntry +{ + struct _listDeletedEntry *prev, *next; + id delegate; + struct _listEntry **listToBeDeleted; -#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD - -#define QUEUE_EVENT_MAX 128 -struct _eventQueue { - SEL selector; - NSEvent *event; -}; +} tListDeletedEntry; -static struct _eventQueue eventQueue[QUEUE_EVENT_MAX]; -static int eventQueueCount; +typedef struct _listAddedEntry +{ + struct _listAddedEntry *prev, *next; + id delegate; + NSInteger priority; + NSUInteger flags; + struct _listEntry **listToBeAdded; +} tListAddedEntry; -#endif // CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD -@implementation CCEventDispatcher +#pragma mark - CCEventObject -@synthesize dispatchEvents=dispatchEvents_; +@implementation CCEventObject +@end +#pragma mark - CCEventDispatcher -+(CCEventDispatcher*) sharedDispatcher -{ - @synchronized(self) { - if (sharedDispatcher == nil) - sharedDispatcher = [[self alloc] init]; // assignment not done here - } - return sharedDispatcher; -} +@implementation CCEventDispatcher -+(id) allocWithZone:(NSZone *)zone -{ - @synchronized(self) { - NSAssert(sharedDispatcher == nil, @"Attempted to allocate a second instance of a singleton."); - return [super allocWithZone:zone]; - } - return nil; // on subsequent allocation attempts return nil -} +@synthesize dispatchEvents=dispatchEvents_; -(id) init { @@ -118,13 +108,14 @@ -(id) init // delegates keyboardDelegates_ = NULL; mouseDelegates_ = NULL; - touchDelegates_ = NULL; + touchDelegates_ = NULL; -#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD - eventQueueCount = 0; -#endif + delegatesToBeAdded_ = NULL; + delegatesToBeRemoved_ = NULL; + + dispatchingInProgress_ = NO; } - + return self; } @@ -134,47 +125,70 @@ - (void) dealloc } #pragma mark CCEventDispatcher - add / remove delegates +-(void) addLaterDelegate:(id)delegate priority:(NSInteger)priority flags:(NSUInteger)flags list:(tListEntry**)list +{ + tListAddedEntry *listElement = malloc( sizeof(*listElement) ); + + listElement->delegate = [delegate retain]; + listElement->priority = priority; + listElement->flags = flags; + listElement->listToBeAdded = list; + listElement->next = listElement->prev = NULL; + + DL_APPEND( delegatesToBeAdded_, listElement ); +} -(void) addDelegate:(id)delegate priority:(NSInteger)priority flags:(NSUInteger)flags list:(tListEntry**)list { tListEntry *listElement = malloc( sizeof(*listElement) ); - + listElement->delegate = [delegate retain]; listElement->priority = priority; listElement->flags = flags; listElement->next = listElement->prev = NULL; - + // empty list ? if( ! *list ) { DL_APPEND( *list, listElement ); - + } else { - BOOL added = NO; - + BOOL added = NO; + for( tListEntry *elem = *list; elem ; elem = elem->next ) { if( priority < elem->priority ) { - + if( elem == *list ) DL_PREPEND(*list, listElement); else { listElement->next = elem; listElement->prev = elem->prev; - + elem->prev->next = listElement; elem->prev = listElement; } - + added = YES; break; } } - + // Not added? priority has the higher value. Append it. if( !added ) DL_APPEND(*list, listElement); } } +-(void) removeLaterDelegate:(id)delegate fromList:(tListEntry**)list +{ + tListDeletedEntry *listElement = malloc( sizeof(*listElement) ); + + listElement->delegate = [delegate retain]; + listElement->listToBeDeleted = list; + listElement->next = listElement->prev = NULL; + + DL_APPEND( delegatesToBeRemoved_, listElement ); +} + -(void) removeDelegate:(id)delegate fromList:(tListEntry**)list { tListEntry *entry, *tmp; @@ -192,11 +206,16 @@ -(void) removeDelegate:(id)delegate fromList:(tListEntry**)list -(void) removeAllDelegatesFromList:(tListEntry**)list { - tListEntry *entry, *tmp; + NSAssert( ! dispatchingInProgress_, @"BUG. Open a ticket. Can't call this function when processing events."); - DL_FOREACH_SAFE( *list, entry, tmp ) { - DL_DELETE( *list, entry ); - free(entry); + @synchronized(self) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( *list, entry, tmp ) { + DL_DELETE( *list, entry ); + [entry->delegate release]; + free(entry); + } } } @@ -204,7 +223,7 @@ -(void) removeAllDelegatesFromList:(tListEntry**)list -(void) addMouseDelegate:(id) delegate priority:(NSInteger)priority { NSUInteger flags = 0; - + flags |= ( [delegate respondsToSelector:@selector(ccMouseDown:)] ? kCCImplementsMouseDown : 0 ); flags |= ( [delegate respondsToSelector:@selector(ccMouseDragged:)] ? kCCImplementsMouseDragged : 0 ); flags |= ( [delegate respondsToSelector:@selector(ccMouseMoved:)] ? kCCImplementsMouseMoved : 0 ); @@ -223,12 +242,19 @@ -(void) addMouseDelegate:(id) delegate priority:(NSInteger flags |= ( [delegate respondsToSelector:@selector(ccScrollWheel:)] ? kCCImplementsScrollWheel : 0 ); - [self addDelegate:delegate priority:priority flags:flags list:&mouseDelegates_]; + if( dispatchingInProgress_ ) + [self addLaterDelegate:delegate priority:priority flags:flags list:&mouseDelegates_]; + else + [self addDelegate:delegate priority:priority flags:flags list:&mouseDelegates_]; + } -(void) removeMouseDelegate:(id) delegate { - [self removeDelegate:delegate fromList:&mouseDelegates_]; + if( dispatchingInProgress_ ) + [self removeLaterDelegate:delegate fromList:&mouseDelegates_]; + else + [self removeDelegate:delegate fromList:&mouseDelegates_]; } -(void) removeAllMouseDelegates @@ -239,17 +265,23 @@ -(void) removeAllMouseDelegates -(void) addKeyboardDelegate:(id) delegate priority:(NSInteger)priority { NSUInteger flags = 0; - + flags |= ( [delegate respondsToSelector:@selector(ccKeyUp:)] ? kCCImplementsKeyUp : 0 ); flags |= ( [delegate respondsToSelector:@selector(ccKeyDown:)] ? kCCImplementsKeyDown : 0 ); flags |= ( [delegate respondsToSelector:@selector(ccFlagsChanged:)] ? kCCImplementsFlagsChanged : 0 ); - - [self addDelegate:delegate priority:priority flags:flags list:&keyboardDelegates_]; + + if( dispatchingInProgress_ ) + [self addLaterDelegate:delegate priority:priority flags:flags list:&keyboardDelegates_]; + else + [self addDelegate:delegate priority:priority flags:flags list:&keyboardDelegates_]; } -(void) removeKeyboardDelegate:(id) delegate { - [self removeDelegate:delegate fromList:&keyboardDelegates_]; + if( dispatchingInProgress_ ) + [self removeLaterDelegate:delegate fromList:&keyboardDelegates_]; + else + [self removeDelegate:delegate fromList:&keyboardDelegates_]; } -(void) removeAllKeyboardDelegates @@ -260,18 +292,24 @@ -(void) removeAllKeyboardDelegates -(void) addTouchDelegate:(id) delegate priority:(NSInteger)priority { NSUInteger flags = 0; - + flags |= ( [delegate respondsToSelector:@selector(ccTouchesBeganWithEvent:)] ? kCCImplementsTouchesBegan : 0 ); flags |= ( [delegate respondsToSelector:@selector(ccTouchesMovedWithEvent:)] ? kCCImplementsTouchesMoved : 0 ); flags |= ( [delegate respondsToSelector:@selector(ccTouchesEndedWithEvent:)] ? kCCImplementsTouchesEnded : 0 ); flags |= ( [delegate respondsToSelector:@selector(ccTouchesCancelledWithEvent:)] ? kCCImplementsTouchesCancelled : 0 ); - - [self addDelegate:delegate priority:priority flags:flags list:&touchDelegates_]; + + if( dispatchingInProgress_ ) + [self addLaterDelegate:delegate priority:priority flags:flags list:&touchDelegates_]; + else + [self addDelegate:delegate priority:priority flags:flags list:&touchDelegates_]; } -(void) removeTouchDelegate:(id) delegate { - [self removeDelegate:delegate fromList:&touchDelegates_]; + if( dispatchingInProgress_ ) + [self removeLaterDelegate:delegate fromList:&touchDelegates_]; + else + [self removeDelegate:delegate fromList:&touchDelegates_]; } -(void) removeAllTouchDelegates @@ -307,7 +345,7 @@ - (void)mouseMoved:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsMouseMoved ) { void *swallows = [entry->delegate performSelector:@selector(ccMouseMoved:) withObject:event]; @@ -322,7 +360,7 @@ - (void)mouseDragged:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsMouseDragged ) { void *swallows = [entry->delegate performSelector:@selector(ccMouseDragged:) withObject:event]; @@ -335,14 +373,16 @@ - (void)mouseDragged:(NSEvent *)event - (void)mouseUp:(NSEvent *)event { - if( dispatchEvents_ ) { - tListEntry *entry, *tmp; - - DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { - if ( entry->flags & kCCImplementsMouseUp ) { - void *swallows = [entry->delegate performSelector:@selector(ccMouseUp:) withObject:event]; - if( swallows ) - break; + @synchronized(self) { + if( dispatchEvents_ ) { + tListEntry *entry, *tmp; + + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { + if ( entry->flags & kCCImplementsMouseUp ) { + void *swallows = [entry->delegate performSelector:@selector(ccMouseUp:) withObject:event]; + if( swallows ) + break; + } } } } @@ -355,7 +395,7 @@ - (void)rightMouseDown:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsRightMouseDown ) { void *swallows = [entry->delegate performSelector:@selector(ccRightMouseDown:) withObject:event]; @@ -370,7 +410,7 @@ - (void)rightMouseDragged:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsRightMouseDragged ) { void *swallows = [entry->delegate performSelector:@selector(ccRightMouseDragged:) withObject:event]; @@ -385,7 +425,7 @@ - (void)rightMouseUp:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsRightMouseUp ) { void *swallows = [entry->delegate performSelector:@selector(ccRightMouseUp:) withObject:event]; @@ -403,7 +443,7 @@ - (void)otherMouseDown:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsOtherMouseDown ) { void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseDown:) withObject:event]; @@ -418,7 +458,7 @@ - (void)otherMouseDragged:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsOtherMouseDragged ) { void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseDragged:) withObject:event]; @@ -433,7 +473,7 @@ - (void)otherMouseUp:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsOtherMouseUp ) { void *swallows = [entry->delegate performSelector:@selector(ccOtherMouseUp:) withObject:event]; @@ -451,7 +491,7 @@ - (void)scrollWheel:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsScrollWheel ) { void *swallows = [entry->delegate performSelector:@selector(ccScrollWheel:) withObject:event]; @@ -468,30 +508,30 @@ - (void)mouseExited:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { - if ( entry->flags & kCCImplementsMouseEntered ) { - void *swallows = [entry->delegate performSelector:@selector(ccMouseEntered:) withObject:event]; + if ( entry->flags & kCCImplementsMouseExited) { + void *swallows = [entry->delegate performSelector:@selector(ccMouseExited:) withObject:event]; if( swallows ) break; } } - } + } } - (void)mouseEntered:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( mouseDelegates_, entry, tmp ) { - if ( entry->flags & kCCImplementsMouseExited) { - void *swallows = [entry->delegate performSelector:@selector(ccMouseExited:) withObject:event]; + if ( entry->flags & kCCImplementsMouseEntered) { + void *swallows = [entry->delegate performSelector:@selector(ccMouseEntered:) withObject:event]; if( swallows ) break; } } - } + } } @@ -502,7 +542,7 @@ - (void)keyDown:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsKeyDown ) { void *swallows = [entry->delegate performSelector:@selector(ccKeyDown:) withObject:event]; @@ -517,7 +557,7 @@ - (void)keyUp:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsKeyUp ) { void *swallows = [entry->delegate performSelector:@selector(ccKeyUp:) withObject:event]; @@ -532,7 +572,7 @@ - (void)flagsChanged:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( keyboardDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsFlagsChanged ) { void *swallows = [entry->delegate performSelector:@selector(ccFlagsChanged:) withObject:event]; @@ -550,7 +590,7 @@ - (void)touchesBeganWithEvent:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsTouchesBegan) { void *swallows = [entry->delegate performSelector:@selector(ccTouchesBeganWithEvent:) withObject:event]; @@ -558,14 +598,14 @@ - (void)touchesBeganWithEvent:(NSEvent *)event break; } } - } + } } - (void)touchesMovedWithEvent:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsTouchesMoved) { void *swallows = [entry->delegate performSelector:@selector(ccTouchesMovedWithEvent:) withObject:event]; @@ -573,14 +613,14 @@ - (void)touchesMovedWithEvent:(NSEvent *)event break; } } - } + } } - (void)touchesEndedWithEvent:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsTouchesEnded) { void *swallows = [entry->delegate performSelector:@selector(ccTouchesEndedWithEvent:) withObject:event]; @@ -588,14 +628,14 @@ - (void)touchesEndedWithEvent:(NSEvent *)event break; } } - } + } } - (void)touchesCancelledWithEvent:(NSEvent *)event { if( dispatchEvents_ ) { tListEntry *entry, *tmp; - + DL_FOREACH_SAFE( touchDelegates_, entry, tmp ) { if ( entry->flags & kCCImplementsTouchesCancelled) { void *swallows = [entry->delegate performSelector:@selector(ccTouchesCancelledWithEvent:) withObject:event]; @@ -603,43 +643,52 @@ - (void)touchesCancelledWithEvent:(NSEvent *)event break; } } - } -} - - -#pragma mark CCEventDispatcher - queue events - -#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD --(void) queueEvent:(NSEvent*)event selector:(SEL)selector -{ - NSAssert( eventQueueCount < QUEUE_EVENT_MAX, @"CCEventDispatcher: recompile. Increment QUEUE_EVENT_MAX value"); - - @synchronized (self) { - eventQueue[eventQueueCount].selector = selector; - eventQueue[eventQueueCount].event = [event copy]; - - eventQueueCount++; } } --(void) dispatchQueuedEvents +- (void)dispatchEvent:(CCEventObject*)e { - @synchronized (self) { - for( int i=0; i < eventQueueCount; i++ ) { - SEL sel = eventQueue[i].selector; - NSEvent *event = eventQueue[i].event; + @synchronized(self) + { + NSEvent *event = e->event; + SEL selector = e->selector; + + // Dispatch events + if( dispatchEvents_ ) { + dispatchingInProgress_ = YES; + [self performSelector:selector onThread:[[CCDirector sharedDirector] runningThread] withObject:event waitUntilDone:YES]; + dispatchingInProgress_ = NO; + } + + + [event release]; + + // Remove possible delegates + tListDeletedEntry *dEntry, *tTmp; + DL_FOREACH_SAFE( delegatesToBeRemoved_ , dEntry, tTmp ) { + + [self removeDelegate:dEntry->delegate fromList:dEntry->listToBeDeleted]; + + DL_DELETE( delegatesToBeRemoved_, dEntry ); + [dEntry->delegate release]; + free(dEntry); + } + + // Add possible delegates + tListAddedEntry *entry, *tmp; + + DL_FOREACH_SAFE( delegatesToBeAdded_, entry, tmp ) { - [self performSelector:sel withObject:event]; + [self addDelegate:entry->delegate priority:entry->priority flags:entry->flags list:entry->listToBeAdded]; - [event release]; + DL_DELETE( delegatesToBeAdded_, entry ); + [entry->delegate release]; + free(entry); } - eventQueueCount = 0; } } -#endif // CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD - @end -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_MAC diff --git a/cocos2d/cocos2d/Platforms/Mac/MacGLView.h b/cocos2d/cocos2d/Platforms/Mac/CCGLView.h old mode 100644 new mode 100755 similarity index 80% rename from cocos2d/cocos2d/Platforms/Mac/MacGLView.h rename to cocos2d/cocos2d/Platforms/Mac/CCGLView.h index 8099273..a2b8224 --- a/cocos2d/cocos2d/Platforms/Mac/MacGLView.h +++ b/cocos2d/cocos2d/Platforms/Mac/CCGLView.h @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,9 +25,8 @@ // Only compile this code on Mac. These files should not be included on your iOS project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_MAC #import @@ -35,7 +34,7 @@ //PROTOCOLS: -@protocol MacEventDelegate +@protocol CCEventDelegate // Mouse - (void)mouseDown:(NSEvent *)theEvent; - (void)mouseUp:(NSEvent *)theEvent; @@ -63,27 +62,34 @@ - (void)touchesEndedWithEvent:(NSEvent *)event; - (void)touchesCancelledWithEvent:(NSEvent *)event; -#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD -- (void)queueEvent:(NSEvent*)event selector:(SEL)selector; -#endif - @end -/** MacGLView - +/** CCGLView + Only available for Mac OS X */ -@interface MacGLView : NSOpenGLView { - id eventDelegate_; +@interface CCGLView : NSOpenGLView { + id eventDelegate_; } -@property (nonatomic, readwrite, assign) id eventDelegate; +/** Event delegate */ +@property (nonatomic, readwrite, assign) id eventDelegate; -// initializes the MacGLView with a frame rect and an OpenGL context +/** initializes the CCGLView with a frame rect and an OpenGL context */ - (id) initWithFrame:(NSRect)frameRect shareContext:(NSOpenGLContext*)context; +/** uses and locks the OpenGL context */ +-(void) lockOpenGLContext; + +/** unlocks the openGL context */ +-(void) unlockOpenGLContext; + +/** returns the depth format of the view in BPP */ +- (NSUInteger) depthFormat; + // private +(void) load_; @end -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED \ No newline at end of file +#endif // __CC_PLATFORM_MAC + diff --git a/cocos2d/cocos2d/Platforms/Mac/MacGLView.m b/cocos2d/cocos2d/Platforms/Mac/CCGLView.m old mode 100644 new mode 100755 similarity index 67% rename from cocos2d/cocos2d/Platforms/Mac/MacGLView.m rename to cocos2d/cocos2d/Platforms/Mac/CCGLView.m index a041dc8..62c2237 --- a/cocos2d/cocos2d/Platforms/Mac/MacGLView.m +++ b/cocos2d/cocos2d/Platforms/Mac/CCGLView.m @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,24 +29,24 @@ // Only compile this code on Mac. These files should not be included on your iOS project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) - -#import "MacGLView.h" -#import +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_MAC +#import "../../Platforms/CCGL.h" +#import "CCGLView.h" #import "CCDirectorMac.h" +#import "CCEventDispatcher.h" #import "../../ccConfig.h" +#import "../../ccMacros.h" -@implementation MacGLView +@implementation CCGLView @synthesize eventDelegate = eventDelegate_; +(void) load_ { - NSLog(@"%@ loaded", self); + CCLOG(@"%@ loaded", self); } - (id) initWithFrame:(NSRect)frameRect @@ -59,75 +59,124 @@ - (id) initWithFrame:(NSRect)frameRect shareContext:(NSOpenGLContext*)context { NSOpenGLPixelFormatAttribute attribs[] = { - NSOpenGLPFAAccelerated, - NSOpenGLPFANoRecovery, +// NSOpenGLPFAAccelerated, +// NSOpenGLPFANoRecovery, NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 24, - + +#if 0 + // Must specify the 3.2 Core Profile to use OpenGL 3.2 + NSOpenGLPFAOpenGLProfile, + NSOpenGLProfileVersion3_2Core, +#endif + 0 }; - + NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; - + if (!pixelFormat) - NSLog(@"No OpenGL pixel format"); - + CCLOG(@"No OpenGL pixel format"); + if( (self = [super initWithFrame:frameRect pixelFormat:[pixelFormat autorelease]]) ) { - + if( context ) [self setOpenGLContext:context]; - // Synchronize buffer swaps with vertical refresh rate - GLint swapInt = 1; - [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; - -// GLint order = -1; -// [[self openGLContext] setValues:&order forParameter:NSOpenGLCPSurfaceOrder]; - // event delegate - eventDelegate_ = nil; + eventDelegate_ = nil; } - + return self; } + +- (void) update +{ + // XXX: Should I do something here ? + [super update]; +} + +- (void) prepareOpenGL +{ + // XXX: Initialize OpenGL context + + [super prepareOpenGL]; + + // Make this openGL context current to the thread + // (i.e. all openGL on this thread calls will go to this context) + [[self openGLContext] makeCurrentContext]; + // Synchronize buffer swaps with vertical refresh rate + GLint swapInt = 1; + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + +// GLint order = -1; +// [[self openGLContext] setValues:&order forParameter:NSOpenGLCPSurfaceOrder]; +} + +- (NSUInteger) depthFormat +{ + return 24; +} + - (void) reshape { // We draw on a secondary thread through the display link // When resizing the view, -reshape is called automatically on the main thread // Add a mutex around to avoid the threads accessing the context simultaneously when resizing - CGLLockContext([[self openGLContext] CGLContextObj]); - + + [self lockOpenGLContext]; + NSRect rect = [self bounds]; - + CCDirector *director = [CCDirector sharedDirector]; [director reshapeProjection: NSSizeToCGSize(rect.size) ]; - + // avoid flicker [director drawScene]; // [self setNeedsDisplay:YES]; - CGLUnlockContext([[self openGLContext] CGLContextObj]); + [self unlockOpenGLContext]; +} + + +-(void) lockOpenGLContext +{ + NSOpenGLContext *glContext = [self openGLContext]; + NSAssert( glContext, @"FATAL: could not get openGL context"); + + [glContext makeCurrentContext]; + CGLLockContext([glContext CGLContextObj]); +} + +-(void) unlockOpenGLContext +{ + NSOpenGLContext *glContext = [self openGLContext]; + NSAssert( glContext, @"FATAL: could not get openGL context"); + + CGLUnlockContext([glContext CGLContextObj]); } - (void) dealloc -{ +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); [super dealloc]; } -#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD -#define DISPATCH_EVENT(__event__, __selector__) [eventDelegate_ queueEvent:__event__ selector:__selector__]; -#else #define DISPATCH_EVENT(__event__, __selector__) \ id obj = eventDelegate_; \ - [obj performSelector:__selector__ \ - onThread:[(CCDirectorMac*)[CCDirector sharedDirector] runningThread] \ - withObject:__event__ \ - waitUntilDone:NO]; -#endif + CCEventObject *event = [[CCEventObject alloc] init]; \ + event->event = [__event__ retain]; \ + event->selector = __selector__; \ + [obj performSelector:@selector(dispatchEvent:) \ + onThread:[[CCDirector sharedDirector] runningThread] \ + withObject:event \ + waitUntilDone:NO]; \ + [event release]; + +#pragma mark CCGLView - Mouse events -#pragma mark MacGLView - Mouse events - (void)mouseDown:(NSEvent *)theEvent { DISPATCH_EVENT(theEvent, _cmd); @@ -184,7 +233,7 @@ -(void) scrollWheel:(NSEvent *)theEvent { DISPATCH_EVENT(theEvent, _cmd); } -#pragma mark MacGLView - Key events +#pragma mark CCGLView - Key events -(BOOL) becomeFirstResponder { @@ -216,7 +265,7 @@ - (void)flagsChanged:(NSEvent *)theEvent DISPATCH_EVENT(theEvent, _cmd); } -#pragma mark MacGLView - Touch events +#pragma mark CCGLView - Touch events - (void)touchesBeganWithEvent:(NSEvent *)theEvent { DISPATCH_EVENT(theEvent, _cmd); @@ -239,4 +288,4 @@ - (void)touchesCancelledWithEvent:(NSEvent *)theEvent @end -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_MAC diff --git a/cocos2d/cocos2d/Platforms/Mac/MacWindow.h b/cocos2d/cocos2d/Platforms/Mac/CCWindow.h old mode 100644 new mode 100755 similarity index 87% rename from cocos2d/cocos2d/Platforms/Mac/MacWindow.h rename to cocos2d/cocos2d/Platforms/Mac/CCWindow.h index 716fe9b..7f37d4d --- a/cocos2d/cocos2d/Platforms/Mac/MacWindow.h +++ b/cocos2d/cocos2d/Platforms/Mac/CCWindow.h @@ -2,17 +2,17 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2010 Ricardo Quesada - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,14 +24,13 @@ // Only compile this code on Mac. These files should not be included on your iOS project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_MAC #import -@interface MacWindow : NSWindow +@interface CCWindow : NSWindow { } - (id) initWithFrame:(NSRect)frame fullscreen:(BOOL)fullscreen; @@ -39,4 +38,4 @@ @end -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED \ No newline at end of file +#endif // __CC_PLATFORM_MAC diff --git a/cocos2d/cocos2d/Platforms/Mac/MacWindow.m b/cocos2d/cocos2d/Platforms/Mac/CCWindow.m old mode 100644 new mode 100755 similarity index 90% rename from cocos2d/cocos2d/Platforms/Mac/MacWindow.m rename to cocos2d/cocos2d/Platforms/Mac/CCWindow.m index 28736a3..8852a20 --- a/cocos2d/cocos2d/Platforms/Mac/MacWindow.m +++ b/cocos2d/cocos2d/Platforms/Mac/CCWindow.m @@ -2,17 +2,17 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2010 Ricardo Quesada - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,14 +24,13 @@ // Only compile this code on Mac. These files should not be included on your iOS project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_MAC -#import "MacWindow.h" +#import "CCWindow.h" -@implementation MacWindow +@implementation CCWindow - (id) initWithFrame:(NSRect)frame fullscreen:(BOOL)fullscreen { @@ -40,7 +39,7 @@ - (id) initWithFrame:(NSRect)frame fullscreen:(BOOL)fullscreen styleMask:styleMask backing:NSBackingStoreBuffered defer:YES]; - + if (self != nil) { if(fullscreen) @@ -49,7 +48,7 @@ - (id) initWithFrame:(NSRect)frame fullscreen:(BOOL)fullscreen [self setHidesOnDeactivate:YES]; [self setHasShadow:NO]; } - + [self setAcceptsMouseMovedEvents:NO]; [self setOpaque:YES]; } @@ -67,4 +66,4 @@ - (BOOL) canBecomeMainWindow } @end -#endif // __MAC_OS_X_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_MAC diff --git a/cocos2d/cocos2d/Platforms/iOS/CCDirectorIOS.h b/cocos2d/cocos2d/Platforms/iOS/CCDirectorIOS.h index 1c264e4..871b251 100755 --- a/cocos2d/cocos2d/Platforms/iOS/CCDirectorIOS.h +++ b/cocos2d/cocos2d/Platforms/iOS/CCDirectorIOS.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,101 +26,20 @@ // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS #import "../../CCDirector.h" +#import "kazmath/mat4.h" -/** @typedef ccDeviceOrientation - Possible device orientations - */ -typedef enum { - /// Device oriented vertically, home button on the bottom - kCCDeviceOrientationPortrait = UIDeviceOrientationPortrait, - /// Device oriented vertically, home button on the top - kCCDeviceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, - /// Device oriented horizontally, home button on the right - kCCDeviceOrientationLandscapeLeft = UIDeviceOrientationLandscapeLeft, - /// Device oriented horizontally, home button on the left - kCCDeviceOrientationLandscapeRight = UIDeviceOrientationLandscapeRight, - - // Backward compatibility stuff - CCDeviceOrientationPortrait = kCCDeviceOrientationPortrait, - CCDeviceOrientationPortraitUpsideDown = kCCDeviceOrientationPortraitUpsideDown, - CCDeviceOrientationLandscapeLeft = kCCDeviceOrientationLandscapeLeft, - CCDeviceOrientationLandscapeRight = kCCDeviceOrientationLandscapeRight, -} ccDeviceOrientation; - -/** @typedef ccDirectorType - Possible Director Types. - @since v0.8.2 - */ -typedef enum { - /** Will use a Director that triggers the main loop from an NSTimer object - * - * Features and Limitations: - * - Integrates OK with UIKit objects - * - It the slowest director - * - The invertal update is customizable from 1 to 60 - */ - kCCDirectorTypeNSTimer, - - /** will use a Director that triggers the main loop from a custom main loop. - * - * Features and Limitations: - * - Faster than NSTimer Director - * - It doesn't integrate well with UIKit objecgts - * - The interval update can't be customizable - */ - kCCDirectorTypeMainLoop, - - /** Will use a Director that triggers the main loop from a thread, but the main loop will be executed on the main thread. - * - * Features and Limitations: - * - Faster than NSTimer Director - * - It doesn't integrate well with UIKit objecgts - * - The interval update can't be customizable - */ - kCCDirectorTypeThreadMainLoop, - - /** Will use a Director that synchronizes timers with the refresh rate of the display. - * - * Features and Limitations: - * - Faster than NSTimer Director - * - Only available on 3.1+ - * - Scheduled timers & drawing are synchronizes with the refresh rate of the display - * - Integrates OK with UIKit objects - * - The interval update can be 1/60, 1/30, 1/15 - */ - kCCDirectorTypeDisplayLink, - - /** Default director is the NSTimer directory */ - kCCDirectorTypeDefault = kCCDirectorTypeNSTimer, - - // backward compatibility stuff - CCDirectorTypeNSTimer = kCCDirectorTypeNSTimer, - CCDirectorTypeMainLoop = kCCDirectorTypeMainLoop, - CCDirectorTypeThreadMainLoop = kCCDirectorTypeThreadMainLoop, - CCDirectorTypeDisplayLink = kCCDirectorTypeDisplayLink, - CCDirectorTypeDefault = kCCDirectorTypeDefault, - - -} ccDirectorType; +@class CCTouchDispatcher; /** CCDirector extensions for iPhone */ @interface CCDirector (iOSExtension) -// rotates the screen if an orientation differnent than Portrait is used --(void) applyOrientation; - -/** Sets the device orientation. - If the orientation is going to be controlled by an UIViewController, then the orientation should be Portrait - */ --(void) setDeviceOrientation:(ccDeviceOrientation)orientation; - -/** returns the device orientation */ --(ccDeviceOrientation) deviceOrientation; +/** sets the CCTouchDispatcher (iOS only) */ +@property (nonatomic,readwrite,retain) CCTouchDispatcher * touchDispatcher; /** The size in pixels of the surface. It could be different than the screen size. High-res devices might have a higher surface size than the screen size. @@ -135,37 +54,16 @@ typedef enum { /** Will enable Retina Display on devices that supports it. It will enable Retina Display on iPhone4 and iPod Touch 4. It will return YES, if it could enabled it, otherwise it will return NO. - + This is the recommened way to enable Retina Display. @since v0.99.5 */ --(BOOL) enableRetinaDisplay:(BOOL)yes; - +-(BOOL) enableRetinaDisplay:(BOOL)enableRetina; /** returns the content scale factor */ -(CGFloat) contentScaleFactor; @end -@interface CCDirector (iOSExtensionClassMethods) - -/** There are 4 types of Director. - - kCCDirectorTypeNSTimer (default) - - kCCDirectorTypeMainLoop - - kCCDirectorTypeThreadMainLoop - - kCCDirectorTypeDisplayLink - - Each Director has it's own benefits, limitations. - If you are using SDK 3.1 or newer it is recommed to use the DisplayLink director - - This method should be called before any other call to the director. - - It will return NO if the director type is kCCDirectorTypeDisplayLink and the running SDK is < 3.1. Otherwise it will return YES. - - @since v0.8.2 - */ -+(BOOL) setDirectorType:(ccDirectorType) directorType; -@end - #pragma mark - #pragma mark CCDirectorIOS @@ -174,47 +72,13 @@ typedef enum { */ @interface CCDirectorIOS : CCDirector { - /* orientation */ - ccDeviceOrientation deviceOrientation_; - /* contentScaleFactor could be simulated */ BOOL isContentScaleSupported_; + CCTouchDispatcher *touchDispatcher_; } @end -/** FastDirector is a Director that triggers the main loop as fast as possible. - * - * Features and Limitations: - * - Faster than "normal" director - * - Consumes more battery than the "normal" director - * - It has some issues while using UIKit objects - */ -@interface CCDirectorFast : CCDirectorIOS -{ - BOOL isRunning; - - NSAutoreleasePool *autoreleasePool; -} --(void) mainLoop; -@end - -/** ThreadedFastDirector is a Director that triggers the main loop from a thread. - * - * Features and Limitations: - * - Faster than "normal" director - * - Consumes more battery than the "normal" director - * - It can be used with UIKit objects - * - * @since v0.8.2 - */ -@interface CCDirectorFastThreaded : CCDirectorIOS -{ - BOOL isRunning; -} --(void) mainLoop; -@end - /** DisplayLinkDirector is a Director that synchronizes timers with the refresh rate of the display. * * Features and Limitations: @@ -228,28 +92,13 @@ typedef enum { */ @interface CCDirectorDisplayLink : CCDirectorIOS { - id displayLink; + CADisplayLink *displayLink_; + CFTimeInterval lastDisplayTime_; } -(void) mainLoop:(id)sender; @end -/** TimerDirector is a Director that calls the main loop from an NSTimer object - * - * Features and Limitations: - * - Integrates OK with UIKit objects - * - It the slowest director - * - The invertal update is customizable from 1 to 60 - * - * It is the default Director. - */ -@interface CCDirectorTimer : CCDirectorIOS -{ - NSTimer *animationTimer; -} --(void) mainLoop; -@end - // optimization. Should only be used to read it. Never to write it. extern CGFloat __ccContentScaleFactor; -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/CCDirectorIOS.m b/cocos2d/cocos2d/Platforms/iOS/CCDirectorIOS.m index 15ad5cb..22854f6 100755 --- a/cocos2d/cocos2d/Platforms/iOS/CCDirectorIOS.m +++ b/cocos2d/cocos2d/Platforms/iOS/CCDirectorIOS.m @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,8 +26,8 @@ // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS #import @@ -40,13 +40,17 @@ #import "../../CCTextureCache.h" #import "../../ccMacros.h" #import "../../CCScene.h" +#import "../../CCGLProgram.h" +#import "../../ccGLStateCache.h" +#import "../../CCLayer.h" // support imports -#import "glu.h" #import "../../Support/OpenGL_Internal.h" #import "../../Support/CGPointExtension.h" +#import "../../Support/TransformUtils.h" -#import "CCLayer.h" +#import "kazmath/kazmath.h" +#import "kazmath/GL/matrix.h" #if CC_ENABLE_PROFILERS #import "../../Support/CCProfiling.h" @@ -59,50 +63,36 @@ CGFloat __ccContentScaleFactor = 1; #pragma mark - -#pragma mark Director iOS +#pragma mark Director @interface CCDirector () -(void) setNextScene; --(void) showFPS; +-(void) showStats; -(void) calculateDeltaTime; +-(void) calculateMPF; @end @implementation CCDirector (iOSExtensionClassMethods) +(Class) defaultDirector { - return [CCDirectorTimer class]; + return [CCDirectorDisplayLink class]; } -+ (BOOL) setDirectorType:(ccDirectorType)type +-(void) setInterfaceOrientationDelegate:(id)delegate { - if( type == CCDirectorTypeDisplayLink ) { - NSString *reqSysVer = @"3.1"; - NSString *currSysVer = [[UIDevice currentDevice] systemVersion]; - - if([currSysVer compare:reqSysVer options:NSNumericSearch] == NSOrderedAscending) - return NO; - } - switch (type) { - case CCDirectorTypeNSTimer: - [CCDirectorTimer sharedDirector]; - break; - case CCDirectorTypeDisplayLink: - [CCDirectorDisplayLink sharedDirector]; - break; - case CCDirectorTypeMainLoop: - [CCDirectorFast sharedDirector]; - break; - case CCDirectorTypeThreadMainLoop: - [CCDirectorFastThreaded sharedDirector]; - break; - default: - NSAssert(NO,@"Unknown director type"); - } - - return YES; + // override me } +-(CCTouchDispatcher*) touchDispatcher +{ + return nil; +} + +-(void) setTouchDispatcher:(CCTouchDispatcher*)touchDispatcher +{ + // +} @end @@ -112,30 +102,33 @@ + (BOOL) setDirectorType:(ccDirectorType)type @interface CCDirectorIOS () -(void) updateContentScaleFactor; - @end @implementation CCDirectorIOS - (id) init -{ +{ if( (self=[super init]) ) { - - // portrait mode default - deviceOrientation_ = CCDeviceOrientationPortrait; - + __ccContentScaleFactor = 1; isContentScaleSupported_ = NO; - + + touchDispatcher_ = [[CCTouchDispatcher alloc] init]; + // running thread is main thread on iOS runningThread_ = [NSThread currentThread]; + + // Apparently it comes with a default view, and we don't want it +// [self setView:nil]; } - + return self; } - (void) dealloc -{ +{ + [touchDispatcher_ release]; + [super dealloc]; } @@ -143,113 +136,118 @@ - (void) dealloc // Draw the Scene // - (void) drawScene -{ +{ /* calculate "global" dt */ [self calculateDeltaTime]; - + + CCGLView *openGLview = (CCGLView*)[self view]; + + [EAGLContext setCurrentContext: [openGLview context]]; + /* tick before glClear: issue #533 */ - if( ! isPaused_ ) { - [[CCScheduler sharedScheduler] tick: dt]; - } - + if( ! isPaused_ ) + [scheduler_ update: dt]; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - + /* to avoid flickr, nextScene MUST be here: after tick and before draw. XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */ if( nextScene_ ) [self setNextScene]; - - glPushMatrix(); - - [self applyOrientation]; - - // By default enable VertexArray, ColorArray, TextureCoordArray and Texture2D - CC_ENABLE_DEFAULT_GL_STATES(); - - /* draw the scene */ + + kmGLPushMatrix(); + [runningScene_ visit]; - - /* draw the notification node */ + [notificationNode_ visit]; - if( displayFPS_ ) - [self showFPS]; - -#if CC_ENABLE_PROFILERS - [self showProfilers]; -#endif - - CC_DISABLE_DEFAULT_GL_STATES(); - - glPopMatrix(); - + if( displayStats_ ) + [self showStats]; + + kmGLPopMatrix(); + totalFrames_++; - [openGLView_ swapBuffers]; + [openGLview swapBuffers]; + + if( displayStats_ ) + [self calculateMPF]; } -(void) setProjection:(ccDirectorProjection)projection { CGSize size = winSizeInPixels_; - + CGSize sizePoint = winSizeInPoints_; + + glViewport(0, 0, size.width, size.height ); + switch (projection) { case kCCDirectorProjection2D: - glViewport(0, 0, size.width, size.height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - ccglOrtho(0, size.width, 0, size.height, -1024 * CC_CONTENT_SCALE_FACTOR(), 1024 * CC_CONTENT_SCALE_FACTOR()); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + + kmGLMatrixMode(KM_GL_PROJECTION); + kmGLLoadIdentity(); + + kmMat4 orthoMatrix; + kmMat4OrthographicProjection(&orthoMatrix, 0, size.width / CC_CONTENT_SCALE_FACTOR(), 0, size.height / CC_CONTENT_SCALE_FACTOR(), -1024, 1024 ); + kmGLMultMatrix( &orthoMatrix ); + + kmGLMatrixMode(KM_GL_MODELVIEW); + kmGLLoadIdentity(); break; - + case kCCDirectorProjection3D: { float zeye = [self getZEye]; - glViewport(0, 0, size.width, size.height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); -// gluPerspective(60, (GLfloat)size.width/size.height, zeye-size.height/2, zeye+size.height/2 ); - gluPerspective(60, (GLfloat)size.width/size.height, 0.5f, 1500); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - gluLookAt( size.width/2, size.height/2, zeye, - size.width/2, size.height/2, 0, - 0.0f, 1.0f, 0.0f); + kmMat4 matrixPerspective, matrixLookup; + + kmGLMatrixMode(KM_GL_PROJECTION); + kmGLLoadIdentity(); + + // issue #1334 + kmMat4PerspectiveProjection( &matrixPerspective, 60, (GLfloat)size.width/size.height, 0.1f, zeye*2); +// kmMat4PerspectiveProjection( &matrixPerspective, 60, (GLfloat)size.width/size.height, 0.1f, 1500); + + kmGLMultMatrix(&matrixPerspective); + + kmGLMatrixMode(KM_GL_MODELVIEW); + kmGLLoadIdentity(); + kmVec3 eye, center, up; + kmVec3Fill( &eye, sizePoint.width/2, sizePoint.height/2, zeye ); + kmVec3Fill( ¢er, sizePoint.width/2, sizePoint.height/2, 0 ); + kmVec3Fill( &up, 0, 1, 0); + kmMat4LookAt(&matrixLookup, &eye, ¢er, &up); + kmGLMultMatrix(&matrixLookup); break; } - + case kCCDirectorProjectionCustom: - if( projectionDelegate_ ) - [projectionDelegate_ updateProjection]; + if( [delegate_ respondsToSelector:@selector(updateProjection)] ) + [delegate_ updateProjection]; break; - + default: - CCLOG(@"cocos2d: Director: unrecognized projecgtion"); + CCLOG(@"cocos2d: Director: unrecognized projection"); break; } - + projection_ = projection; + + ccSetProjectionMatrixDirty(); } -#pragma mark Director Integration with a UIKit view +#pragma mark Director - TouchDispatcher --(void) setOpenGLView:(EAGLView *)view +-(CCTouchDispatcher*) touchDispatcher { - if( view != openGLView_ ) { - - [super setOpenGLView:view]; + return touchDispatcher_; +} - // set size - winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor); - - if( __ccContentScaleFactor != 1 ) - [self updateContentScaleFactor]; - - CCTouchDispatcher *touchDispatcher = [CCTouchDispatcher sharedDispatcher]; - [openGLView_ setTouchDelegate: touchDispatcher]; - [touchDispatcher setDispatchEvents: YES]; +-(void) setTouchDispatcher:(CCTouchDispatcher*)touchDispatcher +{ + if( touchDispatcher != touchDispatcher_ ) { + [touchDispatcher_ release]; + touchDispatcher_ = [touchDispatcher retain]; } } @@ -263,13 +261,13 @@ -(CGFloat) contentScaleFactor -(void) setContentScaleFactor:(CGFloat)scaleFactor { if( scaleFactor != __ccContentScaleFactor ) { - + __ccContentScaleFactor = scaleFactor; winSizeInPixels_ = CGSizeMake( winSizeInPoints_.width * scaleFactor, winSizeInPoints_.height * scaleFactor ); - - if( openGLView_ ) + + if( view_ ) [self updateContentScaleFactor]; - + // update projection [self setProjection:projection_]; } @@ -277,15 +275,10 @@ -(void) setContentScaleFactor:(CGFloat)scaleFactor -(void) updateContentScaleFactor { - // Based on code snippet from: http://developer.apple.com/iphone/prerelease/library/snippets/sp2010/sp28.html - if ([openGLView_ respondsToSelector:@selector(setContentScaleFactor:)]) - { - [openGLView_ setContentScaleFactor: __ccContentScaleFactor]; - - isContentScaleSupported_ = YES; - } - else - CCLOG(@"cocos2d: 'setContentScaleFactor:' is not supported on this device"); + NSAssert( [view_ respondsToSelector:@selector(setContentScaleFactor:)], @"cocos2d v2.0+ runs on iOS 4 or later"); + + [view_ setContentScaleFactor: __ccContentScaleFactor]; + isContentScaleSupported_ = YES; } -(BOOL) enableRetinaDisplay:(BOOL)enabled @@ -293,13 +286,13 @@ -(BOOL) enableRetinaDisplay:(BOOL)enabled // Already enabled ? if( enabled && __ccContentScaleFactor == 2 ) return YES; - + // Already disabled if( ! enabled && __ccContentScaleFactor == 1 ) return YES; // setContentScaleFactor is not supported - if (! [openGLView_ respondsToSelector:@selector(setContentScaleFactor:)]) + if (! [view_ respondsToSelector:@selector(setContentScaleFactor:)]) return NO; // SD device @@ -308,390 +301,155 @@ -(BOOL) enableRetinaDisplay:(BOOL)enabled float newScale = enabled ? 2 : 1; [self setContentScaleFactor:newScale]; - + + // Load Hi-Res FPS label + [self createStatsLabel]; + return YES; } // overriden, don't call super -(void) reshapeProjection:(CGSize)size { - winSizeInPoints_ = [openGLView_ bounds].size; + winSizeInPoints_ = [view_ bounds].size; winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor); - + [self setProjection:projection_]; } -#pragma mark Director Scene Landscape +#pragma mark Director Point Convertion -(CGPoint)convertToGL:(CGPoint)uiPoint { CGSize s = winSizeInPoints_; float newY = s.height - uiPoint.y; - float newX = s.width - uiPoint.x; - - CGPoint ret = CGPointZero; - switch ( deviceOrientation_) { - case CCDeviceOrientationPortrait: - ret = ccp( uiPoint.x, newY ); - break; - case CCDeviceOrientationPortraitUpsideDown: - ret = ccp(newX, uiPoint.y); - break; - case CCDeviceOrientationLandscapeLeft: - ret.x = uiPoint.y; - ret.y = uiPoint.x; - break; - case CCDeviceOrientationLandscapeRight: - ret.x = newY; - ret.y = newX; - break; - } - return ret; + + return ccp( uiPoint.x, newY ); } -(CGPoint)convertToUI:(CGPoint)glPoint { CGSize winSize = winSizeInPoints_; - int oppositeX = winSize.width - glPoint.x; int oppositeY = winSize.height - glPoint.y; - CGPoint uiPoint = CGPointZero; - switch ( deviceOrientation_) { - case CCDeviceOrientationPortrait: - uiPoint = ccp(glPoint.x, oppositeY); - break; - case CCDeviceOrientationPortraitUpsideDown: - uiPoint = ccp(oppositeX, glPoint.y); - break; - case CCDeviceOrientationLandscapeLeft: - uiPoint = ccp(glPoint.y, glPoint.x); - break; - case CCDeviceOrientationLandscapeRight: - // Can't use oppositeX/Y because x/y are flipped - uiPoint = ccp(winSize.width-glPoint.y, winSize.height-glPoint.x); - break; - } - return uiPoint; -} - -// get the current size of the glview --(CGSize) winSize -{ - CGSize s = winSizeInPoints_; - - if( deviceOrientation_ == CCDeviceOrientationLandscapeLeft || deviceOrientation_ == CCDeviceOrientationLandscapeRight ) { - // swap x,y in landscape mode - CGSize tmp = s; - s.width = tmp.height; - s.height = tmp.width; - } - return s; -} --(CGSize) winSizeInPixels -{ - CGSize s = [self winSize]; - - s.width *= CC_CONTENT_SCALE_FACTOR(); - s.height *= CC_CONTENT_SCALE_FACTOR(); - - return s; -} - --(ccDeviceOrientation) deviceOrientation -{ - return deviceOrientation_; -} - -- (void) setDeviceOrientation:(ccDeviceOrientation) orientation -{ - if( deviceOrientation_ != orientation ) { - deviceOrientation_ = orientation; - switch( deviceOrientation_) { - case CCDeviceOrientationPortrait: - [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:NO]; - break; - case CCDeviceOrientationPortraitUpsideDown: - [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortraitUpsideDown animated:NO]; - break; - case CCDeviceOrientationLandscapeLeft: - [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO]; - break; - case CCDeviceOrientationLandscapeRight: - [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft animated:NO]; - break; - default: - NSLog(@"Director: Unknown device orientation"); - break; - } - } -} - --(void) applyOrientation -{ - CGSize s = winSizeInPixels_; - float w = s.width / 2; - float h = s.height / 2; - - // XXX it's using hardcoded values. - // What if the the screen size changes in the future? - switch ( deviceOrientation_ ) { - case CCDeviceOrientationPortrait: - // nothing - break; - case CCDeviceOrientationPortraitUpsideDown: - // upside down - glTranslatef(w,h,0); - glRotatef(180,0,0,1); - glTranslatef(-w,-h,0); - break; - case CCDeviceOrientationLandscapeRight: - glTranslatef(w,h,0); - glRotatef(90,0,0,1); - glTranslatef(-h,-w,0); - break; - case CCDeviceOrientationLandscapeLeft: - glTranslatef(w,h,0); - glRotatef(-90,0,0,1); - glTranslatef(-h,-w,0); - break; - } + return ccp(glPoint.x, oppositeY); } -(void) end { // don't release the event handlers // They are needed in case the director is run again - [[CCTouchDispatcher sharedDispatcher] removeAllDelegates]; - + [touchDispatcher_ removeAllDelegates]; + [super end]; } -@end - +#pragma mark Director - UIViewController delegate -#pragma mark - -#pragma mark Director TimerDirector -@implementation CCDirectorTimer -- (void)startAnimation +-(void) setView:(CCGLView *)view { - NSAssert( animationTimer == nil, @"animationTimer must be nil. Calling startAnimation twice?"); - - if( gettimeofday( &lastUpdate_, NULL) != 0 ) { - CCLOG(@"cocos2d: Director: Error in gettimeofday"); + if( view != view_) { + [super setView:view]; + + if( view ) { + // set size + winSizeInPixels_ = CGSizeMake(winSizeInPoints_.width * __ccContentScaleFactor, winSizeInPoints_.height *__ccContentScaleFactor); + + if( __ccContentScaleFactor != 1 ) + [self updateContentScaleFactor]; + + [view setTouchDelegate: touchDispatcher_]; + [touchDispatcher_ setDispatchEvents: YES]; + } } - - animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval_ target:self selector:@selector(mainLoop) userInfo:nil repeats:YES]; - - // - // If you want to attach the opengl view into UIScrollView - // uncomment this line to prevent 'freezing'. - // It doesn't work on with the Fast Director - // - // [[NSRunLoop currentRunLoop] addTimer:animationTimer - // forMode:NSRunLoopCommonModes]; } --(void) mainLoop +// Override to allow orientations other than the default portrait orientation. +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { - [self drawScene]; + BOOL ret =YES; + if( [delegate_ respondsToSelector:_cmd] ) + ret = (BOOL) [delegate_ shouldAutorotateToInterfaceOrientation:interfaceOrientation]; + + return ret; } -- (void)stopAnimation +-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - [animationTimer invalidate]; - animationTimer = nil; + // do something ? } -- (void)setAnimationInterval:(NSTimeInterval)interval + +-(void) viewWillAppear:(BOOL)animated { - animationInterval_ = interval; - - if(animationTimer) { - [self stopAnimation]; - [self startAnimation]; - } + [super viewWillAppear:animated]; + [self startAnimation]; } --(void) dealloc +-(void) viewDidAppear:(BOOL)animated { - [animationTimer release]; - [super dealloc]; + [super viewDidAppear:animated]; +// [self startAnimation]; } -@end - -#pragma mark - -#pragma mark Director DirectorFast - -@implementation CCDirectorFast - -- (id) init +-(void) viewWillDisappear:(BOOL)animated { - if(( self = [super init] )) { - -#if CC_DIRECTOR_DISPATCH_FAST_EVENTS - CCLOG(@"cocos2d: Fast Events enabled"); -#else - CCLOG(@"cocos2d: Fast Events disabled"); -#endif - isRunning = NO; - - // XXX: - // XXX: Don't create any autorelease object before calling "fast director" - // XXX: else it will be leaked - // XXX: - autoreleasePool = [NSAutoreleasePool new]; - } +// [self stopAnimation]; - return self; + [super viewWillDisappear:animated]; } -- (void) startAnimation +-(void) viewDidDisappear:(BOOL)animated { - NSAssert( isRunning == NO, @"isRunning must be NO. Calling startAnimation twice?"); - - // XXX: - // XXX: release autorelease objects created - // XXX: between "use fast director" and "runWithScene" - // XXX: - [autoreleasePool release]; - autoreleasePool = nil; - - if ( gettimeofday( &lastUpdate_, NULL) != 0 ) { - CCLOG(@"cocos2d: Director: Error in gettimeofday"); - } - - - isRunning = YES; - - SEL selector = @selector(mainLoop); - NSMethodSignature* sig = [[[CCDirector sharedDirector] class] - instanceMethodSignatureForSelector:selector]; - NSInvocation* invocation = [NSInvocation - invocationWithMethodSignature:sig]; - [invocation setTarget:[CCDirector sharedDirector]]; - [invocation setSelector:selector]; - [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) - withObject:[CCDirector sharedDirector] waitUntilDone:NO]; - -// NSInvocationOperation *loopOperation = [[[NSInvocationOperation alloc] -// initWithTarget:self selector:@selector(mainLoop) object:nil] -// autorelease]; -// -// [loopOperation performSelectorOnMainThread:@selector(start) withObject:nil -// waitUntilDone:NO]; -} - --(void) mainLoop -{ - while (isRunning) { - - NSAutoreleasePool *loopPool = [NSAutoreleasePool new]; - -#if CC_DIRECTOR_DISPATCH_FAST_EVENTS - while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004f, FALSE) == kCFRunLoopRunHandledSource); -#else - while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource); -#endif + [self stopAnimation]; - if (isPaused_) { - usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps. - } - - [self drawScene]; - -#if CC_DIRECTOR_DISPATCH_FAST_EVENTS - while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.004f, FALSE) == kCFRunLoopRunHandledSource); -#else - while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource); -#endif - - [loopPool release]; - } -} -- (void) stopAnimation -{ - isRunning = NO; + [super viewDidDisappear:animated]; } -- (void)setAnimationInterval:(NSTimeInterval)interval +- (void)didReceiveMemoryWarning { - NSLog(@"FastDirectory doesn't support setAnimationInterval, yet"); -} -@end - -#pragma mark - -#pragma mark Director DirectorThreadedFast - -@implementation CCDirectorFastThreaded + // Release any cached data, images, etc that aren't in use. + [super purgeCachedData]; -- (id) init -{ - if(( self = [super init] )) { - isRunning = NO; - } - - return self; + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; } -- (void) startAnimation +-(void) viewDidLoad { - NSAssert( isRunning == NO, @"isRunning must be NO. Calling startAnimation twice?"); - - if ( gettimeofday( &lastUpdate_, NULL) != 0 ) { - CCLOG(@"cocos2d: ThreadedFastDirector: Error on gettimeofday"); - } + CCLOG(@"cocos2d: viewDidLoad"); - isRunning = YES; - - NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(mainLoop) object:nil]; - [thread start]; - [thread release]; + [super viewDidLoad]; } --(void) mainLoop -{ - while( ![[NSThread currentThread] isCancelled] ) { - if( isRunning ) - [self performSelectorOnMainThread:@selector(drawScene) withObject:nil waitUntilDone:YES]; - - if (isPaused_) { - usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps. - } else { -// usleep(2000); - } - } -} -- (void) stopAnimation -{ - isRunning = NO; -} -- (void)setAnimationInterval:(NSTimeInterval)interval +- (void)viewDidUnload { - NSLog(@"FastDirector doesn't support setAnimationInterval, yet"); + CCLOG(@"cocos2d: viewDidUnload"); + + [super viewDidUnload]; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; } @end + #pragma mark - #pragma mark DirectorDisplayLink -// Allows building DisplayLinkDirector for pre-3.1 SDKS -// without getting compiler warnings. -@interface NSObject(CADisplayLink) -+ (id) displayLinkWithTarget:(id)arg1 selector:(SEL)arg2; -- (void) addToRunLoop:(id)arg1 forMode:(id)arg2; -- (void) setFrameInterval:(int)interval; -- (void) invalidate; -@end - @implementation CCDirectorDisplayLink + +-(void) mainLoop:(id)sender +{ + [self drawScene]; +} + - (void)setAnimationInterval:(NSTimeInterval)interval { animationInterval_ = interval; - if(displayLink){ + if(displayLink_){ [self stopAnimation]; [self startAnimation]; } @@ -699,39 +457,99 @@ - (void)setAnimationInterval:(NSTimeInterval)interval - (void) startAnimation { - NSAssert( displayLink == nil, @"displayLink must be nil. Calling startAnimation twice?"); + if(isAnimating_) + return; + + gettimeofday( &lastUpdate_, NULL); - if ( gettimeofday( &lastUpdate_, NULL) != 0 ) { - CCLOG(@"cocos2d: DisplayLinkDirector: Error on gettimeofday"); - } - // approximate frame rate // assumes device refreshes at 60 fps int frameInterval = (int) floor(animationInterval_ * 60.0f); - - CCLOG(@"cocos2d: Frame interval: %d", frameInterval); - displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(mainLoop:)]; - [displayLink setFrameInterval:frameInterval]; - [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + CCLOG(@"cocos2d: animation started with frame interval: %.2f", 60.0f/frameInterval); + + displayLink_ = [CADisplayLink displayLinkWithTarget:self selector:@selector(mainLoop:)]; + [displayLink_ setFrameInterval:frameInterval]; + +#if CC_DIRECTOR_IOS_USE_BACKGROUND_THREAD + // + runningThread_ = [[NSThread alloc] initWithTarget:self selector:@selector(threadMainLoop) object:nil]; + [runningThread_ start]; + +#else + // setup DisplayLink in main thread + [displayLink_ addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; +#endif + + isAnimating_ = YES; } --(void) mainLoop:(id)sender +- (void) stopAnimation { - [self drawScene]; + if(!isAnimating_) + return; + + CCLOG(@"cocos2d: animation stopped"); + +#if CC_DIRECTOR_IOS_USE_BACKGROUND_THREAD + [runningThread_ cancel]; + [runningThread_ release]; + runningThread_ = nil; +#endif + + [displayLink_ invalidate]; + displayLink_ = nil; + isAnimating_ = NO; +} + +// Overriden in order to use a more stable delta time +-(void) calculateDeltaTime +{ + // New delta time. Re-fixed issue #1277 + if( nextDeltaTimeZero_ || lastDisplayTime_==0 ) { + dt = 0; + nextDeltaTimeZero_ = NO; + } else { + dt = displayLink_.timestamp - lastDisplayTime_; + dt = MAX(0,dt); + } + // Store this timestamp for next time + lastDisplayTime_ = displayLink_.timestamp; + + // needed for SPF + if( displayStats_ ) + gettimeofday( &lastUpdate_, NULL); + +#ifdef DEBUG + // If we are debugging our code, prevent big delta time + if( dt > 0.2f ) + dt = 1/60.0f; +#endif } -- (void) stopAnimation + +#pragma mark Director Thread + +// +// Director has its own thread +// +-(void) threadMainLoop { - [displayLink invalidate]; - displayLink = nil; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [displayLink_ addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + + // start the run loop + [[NSRunLoop currentRunLoop] run]; + + [pool release]; } -(void) dealloc { - [displayLink release]; + [displayLink_ release]; [super dealloc]; } @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/ES1Renderer.h b/cocos2d/cocos2d/Platforms/iOS/CCES2Renderer.h similarity index 78% rename from cocos2d/cocos2d/Platforms/iOS/ES1Renderer.h rename to cocos2d/cocos2d/Platforms/iOS/CCES2Renderer.h index fd946a7..9f6163b 100755 --- a/cocos2d/cocos2d/Platforms/iOS/ES1Renderer.h +++ b/cocos2d/cocos2d/Platforms/iOS/CCES2Renderer.h @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,16 +28,15 @@ // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS -#import "ESRenderer.h" +#import "CCESRenderer.h" -#import -#import +#import +#import -@interface ES1Renderer : NSObject +@interface CCES2Renderer : NSObject { // The pixel dimensions of the CAEAGLLayer GLint backingWidth_; @@ -45,7 +44,7 @@ unsigned int samplesToUse_; BOOL multiSampling_; - + unsigned int depthFormat_; unsigned int pixelFormat_; @@ -53,20 +52,32 @@ GLuint defaultFramebuffer_; GLuint colorRenderbuffer_; GLuint depthBuffer_; - - + + //buffers for MSAA GLuint msaaFramebuffer_; GLuint msaaColorbuffer_; - + EAGLContext *context_; } +/** Color Renderbuffer */ +@property (nonatomic,readonly) GLuint colorRenderbuffer; + +/** Default Renderbuffer */ +@property (nonatomic,readonly) GLuint defaultFramebuffer; + +/** MSAA Framebuffer */ +@property (nonatomic,readonly) GLuint msaaFramebuffer; + +/** MSAA Color Buffer */ +@property (nonatomic,readonly) GLuint msaaColorbuffer; + /** EAGLContext */ @property (nonatomic,readonly) EAGLContext* context; - (BOOL)resizeFromLayer:(CAEAGLLayer *)layer; - @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS + diff --git a/cocos2d/cocos2d/Platforms/iOS/ES1Renderer.m b/cocos2d/cocos2d/Platforms/iOS/CCES2Renderer.m similarity index 50% rename from cocos2d/cocos2d/Platforms/iOS/ES1Renderer.m rename to cocos2d/cocos2d/Platforms/iOS/CCES2Renderer.m index 398d946..ad7f72e 100755 --- a/cocos2d/cocos2d/Platforms/iOS/ES1Renderer.m +++ b/cocos2d/cocos2d/Platforms/iOS/CCES2Renderer.m @@ -1,19 +1,19 @@ /* * cocos2d for iPhone: http://www.cocos2d-iphone.org * - * Copyright (c) 2010 Ricardo Quesada + * Copyright (c) 2011 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,68 +28,54 @@ // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - -#import "ES1Renderer.h" -#import "../../Support/OpenGL_Internal.h" #import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS +#import "CCES2Renderer.h" -@interface ES1Renderer (private) - -- (GLenum) convertPixelFormat:(int) pixelFormat; - -@end - +#import "../../Support/OpenGL_Internal.h" +#import "../../ccMacros.h" -@implementation ES1Renderer +@implementation CCES2Renderer @synthesize context=context_; +@synthesize defaultFramebuffer=defaultFramebuffer_; +@synthesize colorRenderbuffer=colorRenderbuffer_; +@synthesize msaaColorbuffer=msaaColorbuffer_; +@synthesize msaaFramebuffer=msaaFramebuffer_; +// Create an OpenGL ES 2.0 context - (id) initWithDepthFormat:(unsigned int)depthFormat withPixelFormat:(unsigned int)pixelFormat withSharegroup:(EAGLSharegroup*)sharegroup withMultiSampling:(BOOL) multiSampling withNumberOfSamples:(unsigned int) requestedSamples { - if ((self = [super init])) + self = [super init]; + if (self) { - if ( sharegroup == nil ) - { - context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; - } + if( ! sharegroup ) + context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; else - { - context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:sharegroup]; - } + context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:sharegroup]; - if (!context_ || ![EAGLContext setCurrentContext:context_]) + if (!context_ || ![EAGLContext setCurrentContext:context_] ) { [self release]; return nil; } + + depthFormat_ = depthFormat; + pixelFormat_ = pixelFormat; + multiSampling_ = multiSampling; // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer - glGenFramebuffersOES(1, &defaultFramebuffer_); + glGenFramebuffers(1, &defaultFramebuffer_); NSAssert( defaultFramebuffer_, @"Can't create default frame buffer"); - glGenRenderbuffersOES(1, &colorRenderbuffer_); + + glGenRenderbuffers(1, &colorRenderbuffer_); NSAssert( colorRenderbuffer_, @"Can't create default render buffer"); - glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer_); - glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_); - glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer_); + glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer_); + glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer_); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer_); - depthFormat_ = depthFormat; - - if( depthFormat_ ) { -// glGenRenderbuffersOES(1, &depthBuffer_); -// glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthBuffer_); -// glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthFormat_, 100, 100); -// glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthBuffer_); - - // default buffer -// glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_); - } - - pixelFormat_ = pixelFormat; - multiSampling_ = multiSampling; if (multiSampling_) { GLint maxSamplesAllowed; @@ -97,85 +83,92 @@ - (id) initWithDepthFormat:(unsigned int)depthFormat withPixelFormat:(unsigned i samplesToUse_ = MIN(maxSamplesAllowed,requestedSamples); /* Create the MSAA framebuffer (offscreen) */ - glGenFramebuffersOES(1, &msaaFramebuffer_); - glBindFramebufferOES(GL_FRAMEBUFFER_OES, msaaFramebuffer_); + glGenFramebuffers(1, &msaaFramebuffer_); + NSAssert( msaaFramebuffer_, @"Can't create default MSAA frame buffer"); + glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebuffer_); } - CHECK_GL_ERROR(); + CHECK_GL_ERROR_DEBUG(); } return self; } - (BOOL)resizeFromLayer:(CAEAGLLayer *)layer -{ - // Allocate color buffer backing based on the current layer size - - glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_); +{ + // Allocate color buffer backing based on the current layer size + glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer_); - if (![context_ renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer]) - { - CCLOG(@"failed to call context"); - } + if( ! [context_ renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer] ) + CCLOG(@"failed to call context"); - glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth_); - glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight_); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth_); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight_); CCLOG(@"cocos2d: surface size: %dx%d", (int)backingWidth_, (int)backingHeight_); if (multiSampling_) { - if ( msaaColorbuffer_) { - glDeleteRenderbuffersOES(1, &msaaColorbuffer_); + glDeleteRenderbuffers(1, &msaaColorbuffer_); msaaColorbuffer_ = 0; } - + /* Create the offscreen MSAA color buffer. After rendering, the contents of this will be blitted into ColorRenderbuffer */ //msaaFrameBuffer needs to be binded - glBindFramebufferOES(GL_FRAMEBUFFER_OES, msaaFramebuffer_); - glGenRenderbuffersOES(1, &msaaColorbuffer_); - glBindRenderbufferOES(GL_RENDERBUFFER_OES, msaaColorbuffer_); - glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, samplesToUse_,pixelFormat_ , backingWidth_, backingHeight_); - glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, msaaColorbuffer_); - - if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) + glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebuffer_); + glGenRenderbuffers(1, &msaaColorbuffer_); + NSAssert(msaaFramebuffer_, @"Can't create MSAA color buffer"); + + glBindRenderbuffer(GL_RENDERBUFFER, msaaColorbuffer_); + + glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, samplesToUse_, pixelFormat_ , backingWidth_, backingHeight_); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorbuffer_); + + GLenum error; + if ( (error=glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) { - CCLOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); + CCLOG(@"Failed to make complete framebuffer object 0x%X", error); return NO; } } - - if (depthFormat_) + + CHECK_GL_ERROR(); + + if (depthFormat_) { - if( ! depthBuffer_ ) - glGenRenderbuffersOES(1, &depthBuffer_); + if( ! depthBuffer_ ) { + glGenRenderbuffers(1, &depthBuffer_); + NSAssert(depthBuffer_, @"Can't create depth buffer"); + } + + glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer_); - glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthBuffer_); if( multiSampling_ ) - glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER_OES, samplesToUse_, depthFormat_,backingWidth_, backingHeight_); + glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, samplesToUse_, depthFormat_,backingWidth_, backingHeight_); else - glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthFormat_, backingWidth_, backingHeight_); - glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthBuffer_); - + glRenderbufferStorage(GL_RENDERBUFFER, depthFormat_, backingWidth_, backingHeight_); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer_); + // bind color buffer - glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer_); + glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer_); } - - glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer_); - - if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) + + CHECK_GL_ERROR(); + + GLenum error; + if( (error=glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) { - CCLOG(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); + CCLOG(@"Failed to make complete framebuffer object 0x%X", error); return NO; } - CHECK_GL_ERROR(); - - return YES; + return YES; } -(CGSize) backingSize @@ -185,45 +178,61 @@ -(CGSize) backingSize - (NSString*) description { - return [NSString stringWithFormat:@"<%@ = %08X | size = %ix%i>", [self class], self, backingWidth_, backingHeight_]; + return [NSString stringWithFormat:@"<%@ = %p | size = %ix%i>", [self class], self, backingWidth_, backingHeight_]; +} + +- (unsigned int) colorRenderBuffer +{ + return colorRenderbuffer_; +} + +- (unsigned int) defaultFrameBuffer +{ + return defaultFramebuffer_; +} + +- (unsigned int) msaaFrameBuffer +{ + return msaaFramebuffer_; } +- (unsigned int) msaaColorBuffer +{ + return msaaColorbuffer_; +} - (void)dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); // Tear down GL - if(defaultFramebuffer_) - { - glDeleteFramebuffersOES(1, &defaultFramebuffer_); + if (defaultFramebuffer_) { + glDeleteFramebuffers(1, &defaultFramebuffer_); defaultFramebuffer_ = 0; } - if(colorRenderbuffer_) - { - glDeleteRenderbuffersOES(1, &colorRenderbuffer_); + if (colorRenderbuffer_) { + glDeleteRenderbuffers(1, &colorRenderbuffer_); colorRenderbuffer_ = 0; } - if( depthBuffer_ ) - { - glDeleteRenderbuffersOES(1, &depthBuffer_); + if( depthBuffer_ ) { + glDeleteRenderbuffers(1, &depthBuffer_ ); depthBuffer_ = 0; } - + if ( msaaColorbuffer_) { - glDeleteRenderbuffersOES(1, &msaaColorbuffer_); + glDeleteRenderbuffers(1, &msaaColorbuffer_); msaaColorbuffer_ = 0; } if ( msaaFramebuffer_) { - glDeleteRenderbuffersOES(1, &msaaFramebuffer_); + glDeleteRenderbuffers(1, &msaaFramebuffer_); msaaFramebuffer_ = 0; } - + // Tear down context if ([EAGLContext currentContext] == context_) [EAGLContext setCurrentContext:nil]; @@ -234,26 +243,6 @@ - (void)dealloc [super dealloc]; } -- (unsigned int) colorRenderBuffer -{ - return colorRenderbuffer_; -} - -- (unsigned int) defaultFrameBuffer -{ - return defaultFramebuffer_; -} - -- (unsigned int) msaaFrameBuffer -{ - return msaaFramebuffer_; -} - -- (unsigned int) msaaColorBuffer -{ - return msaaColorbuffer_; -} - @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/ESRenderer.h b/cocos2d/cocos2d/Platforms/iOS/CCESRenderer.h similarity index 92% rename from cocos2d/cocos2d/Platforms/iOS/ESRenderer.h rename to cocos2d/cocos2d/Platforms/iOS/CCESRenderer.h index e612eee..59768f5 100755 --- a/cocos2d/cocos2d/Platforms/iOS/ESRenderer.h +++ b/cocos2d/cocos2d/Platforms/iOS/CCESRenderer.h @@ -3,17 +3,17 @@ * * Copyright (c) 2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,15 +28,15 @@ // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS #import #import #import -@protocol ESRenderer +@protocol CCESRenderer - (id) initWithDepthFormat:(unsigned int)depthFormat withPixelFormat:(unsigned int)pixelFormat withSharegroup:(EAGLSharegroup*)sharegroup withMultiSampling:(BOOL) multiSampling withNumberOfSamples:(unsigned int) requestedSamples; @@ -51,4 +51,4 @@ - (unsigned int) msaaColorBuffer; @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/EAGLView.h b/cocos2d/cocos2d/Platforms/iOS/CCGLView.h similarity index 84% rename from cocos2d/cocos2d/Platforms/iOS/EAGLView.h rename to cocos2d/cocos2d/Platforms/iOS/CCGLView.h index 3b6c2f3..ecac2e0 100755 --- a/cocos2d/cocos2d/Platforms/iOS/EAGLView.h +++ b/cocos2d/cocos2d/Platforms/iOS/CCGLView.h @@ -15,7 +15,7 @@ seeds. ===================== -File: EAGLView.h +File: CCGLView.h Abstract: Convenience class that wraps the CAEAGLLayer from CoreAnimation into a UIView subclass. @@ -63,25 +63,24 @@ Copyright (C) 2008 Apple Inc. All Rights Reserved. // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS #import #import #import -#import -#import +#import +#import -#import "ESRenderer.h" +#import "CCESRenderer.h" //CLASSES: -@class EAGLView; -@class EAGLSharegroup; +@class CCGLView; //PROTOCOLS: -@protocol EAGLTouchDelegate +@protocol CCTouchDelegate - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; @@ -90,14 +89,14 @@ Copyright (C) 2008 Apple Inc. All Rights Reserved. //CLASS INTERFACE: -/** EAGLView Class. +/** CCGLView Class. * This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass. * The view content is basically an EAGL surface you render your OpenGL scene into. * Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel. */ -@interface EAGLView : UIView +@interface CCGLView : UIView { - id renderer_; + id renderer_; EAGLContext *context_; // weak ref NSString *pixelformat_; @@ -106,27 +105,27 @@ Copyright (C) 2008 Apple Inc. All Rights Reserved. CGSize size_; BOOL discardFramebufferSupported_; - id touchDelegate_; + id touchDelegate_; //fsaa addition BOOL multisampling_; unsigned int requestedSamples_; } -/** creates an initializes an EAGLView with a frame and 0-bit depth buffer, and a RGB565 color buffer. */ +/** creates an initializes an CCGLView with a frame and 0-bit depth buffer, and a RGB565 color buffer. */ + (id) viewWithFrame:(CGRect)frame; -/** creates an initializes an EAGLView with a frame, a color buffer format, and 0-bit depth buffer. */ +/** creates an initializes an CCGLView with a frame, a color buffer format, and 0-bit depth buffer. */ + (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format; -/** creates an initializes an EAGLView with a frame, a color buffer format, and a depth buffer. */ +/** creates an initializes an CCGLView with a frame, a color buffer format, and a depth buffer. */ + (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth; -/** creates an initializes an EAGLView with a frame, a color buffer format, a depth buffer format, a sharegroup, and multisamping */ +/** creates an initializes an CCGLView with a frame, a color buffer format, a depth buffer format, a sharegroup, and multisamping */ + (id) viewWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)multisampling numberOfSamples:(unsigned int)samples; -/** Initializes an EAGLView with a frame and 0-bit depth buffer, and a RGB565 color buffer */ +/** Initializes an CCGLView with a frame and 0-bit depth buffer, and a RGB565 color buffer */ - (id) initWithFrame:(CGRect)frame; //These also set the current context -/** Initializes an EAGLView with a frame, a color buffer format, and 0-bit depth buffer */ +/** Initializes an CCGLView with a frame, a color buffer format, and 0-bit depth buffer */ - (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format; -/** Initializes an EAGLView with a frame, a color buffer format, a depth buffer format, a sharegroup and multisampling support */ +/** Initializes an CCGLView with a frame, a color buffer format, a depth buffer format, a sharegroup and multisampling support */ - (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained sharegroup:(EAGLSharegroup*)sharegroup multiSampling:(BOOL)sampling numberOfSamples:(unsigned int)nSamples; /** pixel format: it could be RGBA8 (32-bit) or RGB565 (16-bit) */ @@ -143,13 +142,19 @@ Copyright (C) 2008 Apple Inc. All Rights Reserved. @property(nonatomic,readwrite) BOOL multiSampling; /** touch delegate */ -@property(nonatomic,readwrite,assign) id touchDelegate; +@property(nonatomic,readwrite,assign) id touchDelegate; -/** EAGLView uses double-buffer. This method swaps the buffers */ +/** CCGLView uses double-buffer. This method swaps the buffers */ -(void) swapBuffers; +/** uses and locks the OpenGL context */ +-(void) lockOpenGLContext; + +/** unlocks the openGL context */ +-(void) unlockOpenGLContext; + - (CGPoint) convertPointFromViewToSurface:(CGPoint)point; - (CGRect) convertRectFromViewToSurface:(CGRect)rect; @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/EAGLView.m b/cocos2d/cocos2d/Platforms/iOS/CCGLView.m similarity index 85% rename from cocos2d/cocos2d/Platforms/iOS/EAGLView.m rename to cocos2d/cocos2d/Platforms/iOS/CCGLView.m index d5ead65..3347c5e 100755 --- a/cocos2d/cocos2d/Platforms/iOS/EAGLView.m +++ b/cocos2d/cocos2d/Platforms/iOS/CCGLView.m @@ -15,7 +15,7 @@ ===================== -File: EAGLView.m +File: CCGLView.m Abstract: Convenience class that wraps the CAEAGLLayer from CoreAnimation into a UIView subclass. @@ -61,15 +61,19 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE */ +/* + Modified for cocos2d project + */ + // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS #import -#import "EAGLView.h" -#import "ES1Renderer.h" +#import "CCGLView.h" +#import "CCES2Renderer.h" #import "../../CCDirector.h" #import "../../ccMacros.h" #import "../../CCConfiguration.h" @@ -78,12 +82,12 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //CLASS IMPLEMENTATIONS: -@interface EAGLView (Private) +@interface CCGLView (Private) - (BOOL) setupSurfaceWithSharegroup:(EAGLSharegroup*)sharegroup; - (unsigned int) convertPixelFormat:(NSString*) pixelFormat; @end -@implementation EAGLView +@implementation CCGLView @synthesize surfaceSize=size_; @synthesize pixelFormat=pixelformat_, depthFormat=depthFormat_; @@ -121,7 +125,7 @@ - (id) initWithFrame:(CGRect)frame return [self initWithFrame:frame pixelFormat:kEAGLColorFormatRGB565 depthFormat:0 preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0]; } -- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format +- (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format { return [self initWithFrame:frame pixelFormat:format depthFormat:0 preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0]; } @@ -135,11 +139,13 @@ - (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GL multiSampling_ = sampling; requestedSamples_ = nSamples; preserveBackbuffer_ = retained; - + if( ! [self setupSurfaceWithSharegroup:sharegroup] ) { [self release]; return nil; } + + CHECK_GL_ERROR_DEBUG(); } return self; @@ -148,11 +154,11 @@ - (id) initWithFrame:(CGRect)frame pixelFormat:(NSString*)format depthFormat:(GL -(id) initWithCoder:(NSCoder *)aDecoder { if( (self = [super initWithCoder:aDecoder]) ) { - - CAEAGLLayer* eaglLayer = (CAEAGLLayer*)[self layer]; - + + CAEAGLLayer* eaglLayer = (CAEAGLLayer*)[self layer]; + pixelformat_ = kEAGLColorFormatRGB565; - depthFormat_ = 0; // GL_DEPTH_COMPONENT24_OES; + depthFormat_ = 0; // GL_DEPTH_COMPONENT24; multiSampling_= NO; requestedSamples_ = 0; size_ = [eaglLayer bounds].size; @@ -161,34 +167,40 @@ -(id) initWithCoder:(NSCoder *)aDecoder [self release]; return nil; } + + CHECK_GL_ERROR_DEBUG(); } - + return self; } -(BOOL) setupSurfaceWithSharegroup:(EAGLSharegroup*)sharegroup { CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; - + eaglLayer.opaque = YES; eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:preserveBackbuffer_], kEAGLDrawablePropertyRetainedBacking, pixelformat_, kEAGLDrawablePropertyColorFormat, nil]; - - - renderer_ = [[ES1Renderer alloc] initWithDepthFormat:depthFormat_ + + // ES2 renderer only + renderer_ = [[CCES2Renderer alloc] initWithDepthFormat:depthFormat_ withPixelFormat:[self convertPixelFormat:pixelformat_] withSharegroup:sharegroup withMultiSampling:multiSampling_ withNumberOfSamples:requestedSamples_]; + + NSAssert( renderer_, @"OpenGL ES 2.0 is required"); + if (!renderer_) return NO; - + context_ = [renderer_ context]; - [context_ renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer]; discardFramebufferSupported_ = [[CCConfiguration sharedConfiguration] supportsDiscardFramebuffer]; - + + CHECK_GL_ERROR_DEBUG(); + return YES; } @@ -196,24 +208,24 @@ - (void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); - [renderer_ release]; [super dealloc]; } - (void) layoutSubviews { - size_ = [renderer_ backingSize]; - [renderer_ resizeFromLayer:(CAEAGLLayer*)self.layer]; + size_ = [renderer_ backingSize]; + // Issue #914 #924 CCDirector *director = [CCDirector sharedDirector]; [director reshapeProjection:size_]; - + // Avoid flicker. Issue #350 - [director performSelectorOnMainThread:@selector(drawScene) withObject:nil waitUntilDone:YES]; -} + NSThread *thread = [director runningThread]; + [director performSelector:@selector(drawScene) onThread:thread withObject:nil waitUntilDone:YES]; +} - (void) swapBuffers { @@ -222,90 +234,94 @@ - (void) swapBuffers // -> context_ MUST be the OpenGL context // -> renderbuffer_ must be the the RENDER BUFFER -#ifdef __IPHONE_4_0 - if (multiSampling_) { /* Resolve from msaaFramebuffer to resolveFramebuffer */ - //glDisable(GL_SCISSOR_TEST); - glBindFramebufferOES(GL_READ_FRAMEBUFFER_APPLE, [renderer_ msaaFrameBuffer]); - glBindFramebufferOES(GL_DRAW_FRAMEBUFFER_APPLE, [renderer_ defaultFrameBuffer]); + //glDisable(GL_SCISSOR_TEST); + glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, [renderer_ msaaFrameBuffer]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, [renderer_ defaultFrameBuffer]); glResolveMultisampleFramebufferAPPLE(); } - + if( discardFramebufferSupported_) - { + { if (multiSampling_) { if (depthFormat_) { - GLenum attachments[] = {GL_COLOR_ATTACHMENT0_OES, GL_DEPTH_ATTACHMENT_OES}; + GLenum attachments[] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT}; glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, attachments); } else { - GLenum attachments[] = {GL_COLOR_ATTACHMENT0_OES}; + GLenum attachments[] = {GL_COLOR_ATTACHMENT0}; glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 1, attachments); } - - glBindRenderbufferOES(GL_RENDERBUFFER_OES, [renderer_ colorRenderBuffer]); - - } - + + glBindRenderbuffer(GL_RENDERBUFFER, [renderer_ colorRenderBuffer]); + + } + // not MSAA else if (depthFormat_ ) { - GLenum attachments[] = { GL_DEPTH_ATTACHMENT_OES}; - glDiscardFramebufferEXT(GL_FRAMEBUFFER_OES, 1, attachments); + GLenum attachments[] = { GL_DEPTH_ATTACHMENT}; + glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments); } } - -#endif // __IPHONE_4_0 - - if(![context_ presentRenderbuffer:GL_RENDERBUFFER_OES]) + + if(![context_ presentRenderbuffer:GL_RENDERBUFFER]) CCLOG(@"cocos2d: Failed to swap renderbuffer in %s\n", __FUNCTION__); -#if COCOS2D_DEBUG - CHECK_GL_ERROR(); -#endif - // We can safely re-bind the framebuffer here, since this will be the // 1st instruction of the new main loop if( multiSampling_ ) - glBindFramebufferOES(GL_FRAMEBUFFER_OES, [renderer_ msaaFrameBuffer]); + glBindFramebuffer(GL_FRAMEBUFFER, [renderer_ msaaFrameBuffer]); + + CHECK_GL_ERROR_DEBUG(); +} + +-(void) lockOpenGLContext +{ + // unused on iOS +} + +-(void) unlockOpenGLContext +{ + // unused on iOS } - (unsigned int) convertPixelFormat:(NSString*) pixelFormat { // define the pixel format GLenum pFormat; - - - if([pixelFormat isEqualToString:@"EAGLColorFormat565"]) - pFormat = GL_RGB565_OES; - else + + + if([pixelFormat isEqualToString:@"EAGLColorFormat565"]) + pFormat = GL_RGB565; + else pFormat = GL_RGBA8_OES; - + return pFormat; } -#pragma mark EAGLView - Point conversion +#pragma mark CCGLView - Point conversion - (CGPoint) convertPointFromViewToSurface:(CGPoint)point { CGRect bounds = [self bounds]; - + return CGPointMake((point.x - bounds.origin.x) / bounds.size.width * size_.width, (point.y - bounds.origin.y) / bounds.size.height * size_.height); } - (CGRect) convertRectFromViewToSurface:(CGRect)rect { CGRect bounds = [self bounds]; - + return CGRectMake((rect.origin.x - bounds.origin.x) / bounds.size.width * size_.width, (rect.origin.y - bounds.origin.y) / bounds.size.height * size_.height, rect.size.width / bounds.size.width * size_.width, rect.size.height / bounds.size.height * size_.height); } // Pass the touches to the superview -#pragma mark EAGLView - Touch Delegate +#pragma mark CCGLView - Touch Delegate - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { @@ -340,4 +356,5 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED \ No newline at end of file + +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h b/cocos2d/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h old mode 100644 new mode 100755 index 20ba036..d8e254b --- a/cocos2d/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h +++ b/cocos2d/cocos2d/Platforms/iOS/CCTouchDelegateProtocol.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,22 +25,22 @@ // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS #import /** CCTargetedTouchDelegate. - + Using this type of delegate results in two benefits: 1. You don't need to deal with NSSets, the dispatcher does the job of splitting them. You get exactly one UITouch per call. 2. You can *claim* a UITouch by returning YES in ccTouchBegan. Updates of claimed touches are sent only to the delegate(s) that claimed them. So if you get a move/ - ended/cancelled update you're sure it's your touch. This frees you from doing a + ended/cancelled update you're sure it is your touch. This frees you from doing a lot of checks when doing multi-touch. - + (The name TargetedTouchDelegate relates to updates "targeting" their specific handler, without bothering the other handlers.) @since v0.8 @@ -60,7 +60,7 @@ /** CCStandardTouchDelegate. - + This type of delegate is the same one used by CocoaTouch. You will receive all the events (Began,Moved,Ended,Cancelled). @since v0.8 */ @@ -72,4 +72,4 @@ - (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/CCTouchDispatcher.h b/cocos2d/cocos2d/Platforms/iOS/CCTouchDispatcher.h old mode 100644 new mode 100755 index 9931189..9995865 --- a/cocos2d/cocos2d/Platforms/iOS/CCTouchDispatcher.h +++ b/cocos2d/cocos2d/Platforms/iOS/CCTouchDispatcher.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,11 +25,11 @@ // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS #import "CCTouchDelegateProtocol.h" -#import "EAGLView.h" +#import "CCGLView.h" typedef enum @@ -47,7 +47,7 @@ enum { kCCTouchMoved, kCCTouchEnded, kCCTouchCancelled, - + kCCTouchMax, }; @@ -58,22 +58,22 @@ struct ccTouchHandlerHelperData { }; /** CCTouchDispatcher. - Singleton that handles all the touch events. + Object that handles all the touch events. The dispatcher dispatches events to the registered TouchHandlers. There are 2 different type of touch handlers: - Standard Touch Handlers - Targeted Touch Handlers - + The Standard Touch Handlers work like the CocoaTouch touch handler: a set of touches is passed to the delegate. On the other hand, the Targeted Touch Handlers only receive 1 touch at the time, and they can "swallow" touches (avoid the propagation of the event). - + Firstly, the dispatcher sends the received touches to the targeted touches. These touches can be swallowed by the Targeted Touch Handlers. If there are still remaining touches, then the remaining touches will be sent to the Standard Touch Handlers. @since v0.8.0 */ -@interface CCTouchDispatcher : NSObject +@interface CCTouchDispatcher : NSObject { NSMutableArray *targetedHandlers; NSMutableArray *standardHandlers; @@ -86,14 +86,11 @@ struct ccTouchHandlerHelperData { BOOL toQuit; BOOL dispatchEvents; - + // 4, 1 for each type of event struct ccTouchHandlerHelperData handlerHelperData[kCCTouchMax]; } -/** singleton of the CCTouchDispatcher */ -+ (CCTouchDispatcher*)sharedDispatcher; - /** Whether or not the events are going to be dispatched. Default: YES */ @property (nonatomic,readwrite, assign) BOOL dispatchEvents; @@ -120,4 +117,4 @@ struct ccTouchHandlerHelperData { NSComparisonResult sortByPriority(id first, id second, void *context); @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/CCTouchDispatcher.m b/cocos2d/cocos2d/Platforms/iOS/CCTouchDispatcher.m old mode 100644 new mode 100755 index 1553b48..788e99d --- a/cocos2d/cocos2d/Platforms/iOS/CCTouchDispatcher.m +++ b/cocos2d/cocos2d/Platforms/iOS/CCTouchDispatcher.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,8 +25,8 @@ // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS #import "CCTouchDispatcher.h" @@ -36,37 +36,17 @@ @implementation CCTouchDispatcher @synthesize dispatchEvents; -static CCTouchDispatcher *sharedDispatcher = nil; - -+(CCTouchDispatcher*) sharedDispatcher -{ - @synchronized(self) { - if (sharedDispatcher == nil) - sharedDispatcher = [[self alloc] init]; // assignment not done here - } - return sharedDispatcher; -} - -+(id) allocWithZone:(NSZone *)zone -{ - @synchronized(self) { - NSAssert(sharedDispatcher == nil, @"Attempted to allocate a second instance of a singleton."); - return [super allocWithZone:zone]; - } - return nil; // on subsequent allocation attempts return nil -} - -(id) init { if((self = [super init])) { - + dispatchEvents = YES; targetedHandlers = [[NSMutableArray alloc] initWithCapacity:8]; standardHandlers = [[NSMutableArray alloc] initWithCapacity:4]; - + handlersToAdd = [[NSMutableArray alloc] initWithCapacity:8]; handlersToRemove = [[NSMutableArray alloc] initWithCapacity:8]; - + toRemove = NO; toAdd = NO; toQuit = NO; @@ -76,9 +56,9 @@ -(id) init handlerHelperData[kCCTouchMoved] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesMoved:withEvent:),@selector(ccTouchMoved:withEvent:),kCCTouchSelectorMovedBit}; handlerHelperData[kCCTouchEnded] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesEnded:withEvent:),@selector(ccTouchEnded:withEvent:),kCCTouchSelectorEndedBit}; handlerHelperData[kCCTouchCancelled] = (struct ccTouchHandlerHelperData) {@selector(ccTouchesCancelled:withEvent:),@selector(ccTouchCancelled:withEvent:),kCCTouchSelectorCancelledBit}; - + } - + return self; } @@ -100,14 +80,14 @@ -(void) dealloc -(void) forceAddHandler:(CCTouchHandler*)handler array:(NSMutableArray*)array { NSUInteger i = 0; - + for( CCTouchHandler *h in array ) { if( h.priority < handler.priority ) i++; - + NSAssert( h.delegate != handler.delegate, @"Delegate already added to touch dispatcher."); } - [array insertObject:handler atIndex:i]; + [array insertObject:handler atIndex:i]; } -(void) addStandardDelegate:(id) delegate priority:(int)priority @@ -137,27 +117,27 @@ -(void) addTargetedDelegate:(id) delegate priority:(int -(void) forceRemoveDelegate:(id)delegate { // XXX: remove it from both handlers ??? - + for( CCTouchHandler *handler in targetedHandlers ) { if( handler.delegate == delegate ) { [targetedHandlers removeObject:handler]; break; } } - + for( CCTouchHandler *handler in standardHandlers ) { if( handler.delegate == delegate ) { [standardHandlers removeObject:handler]; break; } - } + } } -(void) removeDelegate:(id) delegate { if( delegate == nil ) return; - + if( ! locked ) { [self forceRemoveDelegate:delegate]; } else { @@ -190,12 +170,21 @@ -(CCTouchHandler*) findHandler:(id)delegate return handler; } } - + for( CCTouchHandler *handler in standardHandlers ) { if( handler.delegate == delegate ) { return handler; } } + + if (toAdd) { + for( CCTouchHandler *handler in handlersToAdd ) { + if (handler.delegate == delegate) { + return handler; + } + } + } + return nil; } @@ -205,7 +194,7 @@ NSComparisonResult sortByPriority(id first, id second, void *context) return NSOrderedAscending; else if (((CCTouchHandler*)first).priority > ((CCTouchHandler*)second).priority) return NSOrderedDescending; - else + else return NSOrderedSame; } @@ -217,14 +206,14 @@ -(void) rearrangeHandlers:(NSMutableArray*)array -(void) setPriority:(int) priority forDelegate:(id) delegate { NSAssert(delegate != nil, @"Got nil touch delegate!"); - + CCTouchHandler *handler = nil; handler = [self findHandler:delegate]; - - NSAssert(handler != nil, @"Delegate not found!"); - + + NSAssert(handler != nil, @"Delegate not found!"); + handler.priority = priority; - + [self rearrangeHandlers:targetedHandlers]; [self rearrangeHandlers:standardHandlers]; } @@ -238,12 +227,12 @@ -(void) touches:(NSSet*)touches withEvent:(UIEvent*)event withTouchType:(unsigne id mutableTouches; locked = YES; - + // optimization to prevent a mutable copy when it is not necessary unsigned int targetedHandlersCount = [targetedHandlers count]; - unsigned int standardHandlersCount = [standardHandlers count]; + unsigned int standardHandlersCount = [standardHandlers count]; BOOL needsMutableSet = (targetedHandlersCount && standardHandlersCount); - + mutableTouches = (needsMutableSet ? [touches mutableCopy] : touches); struct ccTouchHandlerHelperData helper = handlerHelperData[idx]; @@ -253,24 +242,24 @@ -(void) touches:(NSSet*)touches withEvent:(UIEvent*)event withTouchType:(unsigne if( targetedHandlersCount > 0 ) { for( UITouch *touch in touches ) { for(CCTargetedTouchHandler *handler in targetedHandlers) { - + BOOL claimed = NO; if( idx == kCCTouchBegan ) { claimed = [handler.delegate ccTouchBegan:touch withEvent:event]; if( claimed ) [handler.claimedTouches addObject:touch]; - } - + } + // else (moved, ended, cancelled) else if( [handler.claimedTouches containsObject:touch] ) { claimed = YES; if( handler.enabledSelectors & helper.type ) [handler.delegate performSelector:helper.touchSel withObject:touch withObject:event]; - + if( helper.type & (kCCTouchSelectorCancelledBit | kCCTouchSelectorEndedBit) ) [handler.claimedTouches removeObject:touch]; } - + if( claimed && handler.swallowsTouches ) { if( needsMutableSet ) [mutableTouches removeObject:touch]; @@ -279,7 +268,7 @@ -(void) touches:(NSSet*)touches withEvent:(UIEvent*)event withTouchType:(unsigne } } } - + // // process standard handlers 2nd // @@ -291,22 +280,19 @@ -(void) touches:(NSSet*)touches withEvent:(UIEvent*)event withTouchType:(unsigne } if( needsMutableSet ) [mutableTouches release]; - + // // Optimization. To prevent a [handlers copy] which is expensive // the add/removes/quit is done after the iterations // locked = NO; - if( toRemove ) { - toRemove = NO; - for( id delegate in handlersToRemove ) - [self forceRemoveDelegate:delegate]; - [handlersToRemove removeAllObjects]; - } + + //issue 1084, 1139 first add then remove if( toAdd ) { toAdd = NO; + Class targetedClass = [CCTargetedTouchHandler class]; + for( CCTouchHandler *handler in handlersToAdd ) { - Class targetedClass = [CCTargetedTouchHandler class]; if( [handler isKindOfClass:targetedClass] ) [self forceAddHandler:handler array:targetedHandlers]; else @@ -314,6 +300,14 @@ -(void) touches:(NSSet*)touches withEvent:(UIEvent*)event withTouchType:(unsigne } [handlersToAdd removeAllObjects]; } + + if( toRemove ) { + toRemove = NO; + for( id delegate in handlersToRemove ) + [self forceRemoveDelegate:delegate]; + [handlersToRemove removeAllObjects]; + } + if( toQuit ) { toQuit = NO; [self forceRemoveAllDelegates]; @@ -327,7 +321,7 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - if( dispatchEvents ) + if( dispatchEvents ) [self touches:touches withEvent:event withTouchType:kCCTouchMoved]; } @@ -344,4 +338,4 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event } @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/CCTouchHandler.h b/cocos2d/cocos2d/Platforms/iOS/CCTouchHandler.h old mode 100644 new mode 100755 index 31a3e36..c448972 --- a/cocos2d/cocos2d/Platforms/iOS/CCTouchHandler.h +++ b/cocos2d/cocos2d/Platforms/iOS/CCTouchHandler.h @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,8 +25,8 @@ // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS /* * This file contains the delegates of the touches @@ -90,4 +90,4 @@ @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/CCTouchHandler.m b/cocos2d/cocos2d/Platforms/iOS/CCTouchHandler.m old mode 100644 new mode 100755 index a52103b..2aec6ee --- a/cocos2d/cocos2d/Platforms/iOS/CCTouchHandler.m +++ b/cocos2d/cocos2d/Platforms/iOS/CCTouchHandler.m @@ -9,10 +9,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,8 +26,8 @@ // Only compile this code on iOS. These files should NOT be included on your Mac project. // But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "../../ccMacros.h" +#ifdef __CC_PLATFORM_IOS /* * This file contains the delegates of the touches @@ -54,13 +54,13 @@ + (id)handlerWithDelegate:(id) aDelegate priority:(int)aPriority - (id)initWithDelegate:(id) aDelegate priority:(int)aPriority { NSAssert(aDelegate != nil, @"Touch delegate may not be nil"); - + if ((self = [super init])) { self.delegate = aDelegate; priority = aPriority; enabledSelectors_ = 0; } - + return self; } @@ -108,10 +108,10 @@ + (id)handlerWithDelegate:(id)aDelegate priority:(int)priority swallowsTouches:( - (id)initWithDelegate:(id)aDelegate priority:(int)aPriority swallowsTouches:(BOOL)swallow { - if ((self = [super initWithDelegate:aDelegate priority:aPriority])) { + if ((self = [super initWithDelegate:aDelegate priority:aPriority])) { claimedTouches = [[NSMutableSet alloc] initWithCapacity:2]; swallowsTouches = swallow; - + if( [aDelegate respondsToSelector:@selector(ccTouchBegan:withEvent:)] ) enabledSelectors_ |= kCCTouchSelectorBeganBit; if( [aDelegate respondsToSelector:@selector(ccTouchMoved:withEvent:)] ) @@ -121,7 +121,7 @@ - (id)initWithDelegate:(id)aDelegate priority:(int)aPriority swallowsTouches:(BO if( [aDelegate respondsToSelector:@selector(ccTouchCancelled:withEvent:)] ) enabledSelectors_ |= kCCTouchSelectorCancelledBit; } - + return self; } @@ -132,4 +132,4 @@ - (void)dealloc { @end -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED \ No newline at end of file +#endif // __CC_PLATFORM_IOS diff --git a/cocos2d/cocos2d/Platforms/iOS/glu.c b/cocos2d/cocos2d/Platforms/iOS/glu.c deleted file mode 100755 index 2e00d5f..0000000 --- a/cocos2d/cocos2d/Platforms/iOS/glu.c +++ /dev/null @@ -1,113 +0,0 @@ -// -// cocos2d (incomplete) GLU implementation -// -// gluLookAt and gluPerspective from: -// http://jet.ro/creations (San Angeles Observation) -// -// - -// Only compile this code on iOS. These files should NOT be included on your Mac project. -// But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - -#import -#import -#import "../../Support/OpenGL_Internal.h" -#include "glu.h" - -void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar) -{ - GLfloat xmin, xmax, ymin, ymax; - - ymax = zNear * (GLfloat)tanf(fovy * (float)M_PI / 360); - ymin = -ymax; - xmin = ymin * aspect; - xmax = ymax * aspect; - - glFrustumf(xmin, xmax, - ymin, ymax, - zNear, zFar); -} - -void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez, - GLfloat centerx, GLfloat centery, GLfloat centerz, - GLfloat upx, GLfloat upy, GLfloat upz) -{ - GLfloat m[16]; - GLfloat x[3], y[3], z[3]; - GLfloat mag; - - /* Make rotation matrix */ - - /* Z vector */ - z[0] = eyex - centerx; - z[1] = eyey - centery; - z[2] = eyez - centerz; - mag = (float)sqrtf(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]); - if (mag) { - z[0] /= mag; - z[1] /= mag; - z[2] /= mag; - } - - /* Y vector */ - y[0] = upx; - y[1] = upy; - y[2] = upz; - - /* X vector = Y cross Z */ - x[0] = y[1] * z[2] - y[2] * z[1]; - x[1] = -y[0] * z[2] + y[2] * z[0]; - x[2] = y[0] * z[1] - y[1] * z[0]; - - /* Recompute Y = Z cross X */ - y[0] = z[1] * x[2] - z[2] * x[1]; - y[1] = -z[0] * x[2] + z[2] * x[0]; - y[2] = z[0] * x[1] - z[1] * x[0]; - - /* cross product gives area of parallelogram, which is < 1.0 for - * non-perpendicular unit-length vectors; so normalize x, y here - */ - - mag = (float)sqrtf(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); - if (mag) { - x[0] /= mag; - x[1] /= mag; - x[2] /= mag; - } - - mag = (float)sqrtf(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]); - if (mag) { - y[0] /= mag; - y[1] /= mag; - y[2] /= mag; - } - -#define M(row,col) m[col*4+row] - M(0, 0) = x[0]; - M(0, 1) = x[1]; - M(0, 2) = x[2]; - M(0, 3) = 0.0f; - M(1, 0) = y[0]; - M(1, 1) = y[1]; - M(1, 2) = y[2]; - M(1, 3) = 0.0f; - M(2, 0) = z[0]; - M(2, 1) = z[1]; - M(2, 2) = z[2]; - M(2, 3) = 0.0f; - M(3, 0) = 0.0f; - M(3, 1) = 0.0f; - M(3, 2) = 0.0f; - M(3, 3) = 1.0f; -#undef M - - glMultMatrixf(m); - - - /* Translate Eye to Origin */ - glTranslatef(-eyex, -eyey, -eyez); -} - -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED diff --git a/cocos2d/cocos2d/Platforms/iOS/glu.h b/cocos2d/cocos2d/Platforms/iOS/glu.h deleted file mode 100644 index 86dcac7..0000000 --- a/cocos2d/cocos2d/Platforms/iOS/glu.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// cocos2d GLU implementation -// -// implementation of GLU functions -// -#ifndef __COCOS2D_GLU_H -#define __COCOS2D_GLU_H - -// Only compile this code on iOS. These files should NOT be included on your Mac project. -// But in case they are included, it won't be compiled. -#import -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - -#import - -/** - @file - cocos2d OpenGL GLU implementation - */ - -/** OpenGL gluLookAt implementation */ -void gluLookAt(float eyeX, float eyeY, float eyeZ, float lookAtX, float lookAtY, float lookAtZ, float upX, float upY, float upZ); -/** OpenGL gluPerspective implementation */ -void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar); - -#endif // __IPHONE_OS_VERSION_MAX_ALLOWED - -#endif /* __COCOS2D_GLU_H */ - diff --git a/cocos2d/cocos2d/Support/CCArray.h b/cocos2d/cocos2d/Support/CCArray.h old mode 100644 new mode 100755 index 0c7b2b8..5bbdfe4 --- a/cocos2d/cocos2d/Support/CCArray.h +++ b/cocos2d/cocos2d/Support/CCArray.h @@ -2,17 +2,17 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2010 ForzeField Studios S.L. http://forzefield.com - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -39,8 +39,8 @@ #define CCARRAY_FOREACH(__array__, __object__) \ if (__array__ && __array__->data->num > 0) \ -for(id *__arr__ = __array__->data->arr, *end = __array__->data->arr + __array__->data->num-1; \ - __arr__ <= end && ((__object__ = *__arr__) != nil || true); \ +for(const CC_ARC_UNSAFE_RETAINED id *__arr__ = __array__->data->arr, *end = __array__->data->arr + __array__->data->num-1; \ + __arr__ <= end && ((__object__ = *__arr__) != nil || true); \ __arr__++) @interface CCArray : NSObject @@ -69,6 +69,8 @@ for(id *__arr__ = __array__->data->arr, *end = __array__->data->arr + __array__- - (id) randomObject; - (id) lastObject; - (NSArray*) getNSArray; +/** @since 1.1 */ +- (BOOL) isEqualToArray:(CCArray*)otherArray; // Adding Objects @@ -94,13 +96,24 @@ for(id *__arr__ = __array__->data->arr, *end = __array__->data->arr + __array__- - (void) exchangeObject:(id)object1 withObject:(id)object2; - (void) exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)index2; +/** @since 1.1 */ +- (void) replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject; - (void) reverseObjects; - (void) reduceMemoryFootprint; +// Sorting Array +/** all since @1.1 */ +- (void) qsortUsingCFuncComparator:(int(*)(const void *, const void *))comparator; // c qsort is used for sorting +- (void) insertionSortUsingCFuncComparator:(int(*)(const void *, const void *))comparator; // insertion sort +- (void) mergesortLUsingCFuncComparator:(int(*)(const void *, const void *))comparator; // mergesort +- (void) insertionSort:(SEL)selector; // It sorts source array in ascending order +- (void) sortUsingFunction:(NSInteger (*)(id, id, void *))compare context:(void *)context; + // Sending Messages to Elements - (void) makeObjectsPerformSelector:(SEL)aSelector; - (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)object; - +/** @since 1.1 */ +- (void) makeObjectPerformSelectorWithArrayObjects:(id)object selector:(SEL)aSelector; @end diff --git a/cocos2d/cocos2d/Support/CCArray.m b/cocos2d/cocos2d/Support/CCArray.m old mode 100644 new mode 100755 index 87671fb..3237c96 --- a/cocos2d/cocos2d/Support/CCArray.m +++ b/cocos2d/cocos2d/Support/CCArray.m @@ -2,17 +2,17 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2010 ForzeField Studios S.L. http://forzefield.com - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,7 +25,6 @@ #import "CCArray.h" #import "../ccMacros.h" - @implementation CCArray + (id) array @@ -107,8 +106,8 @@ - (NSUInteger) indexOfObject:(id)object - (id) objectAtIndex:(NSUInteger)index { - NSAssert2( index < data->num, @"index out of range in objectAtIndex(%d), index %i", data->num, index ); - + NSAssert2( index < data->num, @"index out of range in objectAtIndex(%lu), index %lu", (unsigned long)data->num, (unsigned long)index ); + return data->arr[index]; } @@ -135,6 +134,17 @@ - (NSArray*) getNSArray return [NSArray arrayWithObjects:data->arr count:data->num]; } +- (BOOL) isEqualToArray:(CCArray*)otherArray { + for (int i = 0; i< [self count]; i++) + { + if (![[self objectAtIndex:i] isEqual: [otherArray objectAtIndex:i]]) + { + return NO; + } + } + return YES; +} + #pragma mark Adding Objects @@ -191,7 +201,7 @@ - (void) removeObjectsInArray:(CCArray*)otherArray - (void) removeLastObject { NSAssert( data->num > 0, @"no objects added" ); - + ccArrayRemoveObjectAtIndex(data, data->num-1); } @@ -209,7 +219,7 @@ - (void) exchangeObject:(id)object1 withObject:(id)object2 if(index1 == NSNotFound) return; NSUInteger index2 = ccArrayGetIndexOfObject(data, object2); if(index2 == NSNotFound) return; - + ccArraySwapObjectsAtIndexes(data, index1, index2); } @@ -218,14 +228,19 @@ - (void) exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger) ccArraySwapObjectsAtIndexes(data, index1, index2); } +- (void) replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { + ccArrayInsertObjectAtIndex(data, anObject, index); + ccArrayRemoveObjectAtIndex(data, index+1); +} + - (void) reverseObjects { if (data->num > 1) { //floor it since in case of a oneven number the number of swaps stays the same - int count = (int) floorf(data->num/2.f); + int count = (int) floorf(data->num/2.f); NSUInteger maxIndex = data->num - 1; - + for (int i = 0; i < count ; i++) { ccArraySwapObjectsAtIndexes(data, i, maxIndex); @@ -251,19 +266,132 @@ - (void) makeObjectsPerformSelector:(SEL)aSelector withObject:(id)object ccArrayMakeObjectsPerformSelectorWithObject(data, aSelector, object); } +- (void) makeObjectPerformSelectorWithArrayObjects:(id)object selector:(SEL)aSelector +{ + ccArrayMakeObjectPerformSelectorWithArrayObjects(data, aSelector, object); +} #pragma mark CCArray - NSFastEnumeration protocol - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len { if(state->state == 1) return 0; - + state->mutationsPtr = (unsigned long *)self; state->itemsPtr = &data->arr[0]; state->state = 1; return data->num; } +#pragma mark CCArray - sorting + +/** @since 1.1 */ +#pragma mark - +#pragma mark CCArray insertionSortUsingCFuncComparator + +- (void) insertionSortUsingCFuncComparator:(int(*)(const void *, const void *))comparator +{ + cc_insertionSort(data, comparator); +} + +#pragma mark CCArray qsortUsingCFuncComparator + +- (void) qsortUsingCFuncComparator:(cc_comparator)comparator { + + // stable c qsort is used - cost of sorting: best n*log(n), average n*log(n) + // qsort(void *, size_t, size_t, int (*)(const void *arg1, const void *arg2)); + + qsort(data->arr, data->num, sizeof (id), comparator); +} + +#pragma mark CCArray mergesortLUsingCFuncComparator + +- (void) mergesortLUsingCFuncComparator:(cc_comparator)comparator +{ + cc_mergesortL(data, sizeof (id), comparator); +} + +#pragma mark CCArray insertionSort with (SEL)selector + +- (void) insertionSort:(SEL)selector // It sorts source array in ascending order +{ + NSInteger i,j,length = data->num; + + id * x = data->arr; + id temp; + + // insertion sort + for(i=1; i0 && ( (int)([x[j-1] performSelector:selector withObject:x[j]]) == NSOrderedDescending) ) + { + temp = x[j]; + x[j] = x[j-1]; + x[j-1] = temp; + j--; + } + } +} + +static inline NSInteger selectorCompare(id object1,id object2,void *userData){ + SEL selector=userData; + + return (NSInteger)[object1 performSelector:selector withObject:object2]; +} + +-(void)sortUsingSelector:(SEL)selector { + [self sortUsingFunction:selectorCompare context:selector]; +} + +#pragma mark CCArray sortUsingFunction + +// using a comparison function +-(void)sortUsingFunction:(NSInteger (*)(id, id, void *))compare context:(void *)context +{ + NSInteger h, i, j, k, l, m, n = [self count]; + id A, *B = malloc( (n/2 + 1) * sizeof(id)); + + // to prevent retain counts from temporarily hitting zero. + for( i=0;iarr[i] retain]; + + + for (h = 1; h < n; h += h) + { + for (m = n - 1 - h; m >= 0; m -= h + h) + { + l = m - h + 1; + if (l < 0) + l = 0; + for (i = 0, j = l; j <= m; i++, j++) + B[i] = [self objectAtIndex:j]; + + for (i = 0, k = l; k < j && j <= m + h; k++) + { + A = [self objectAtIndex:j]; + if (compare(A, B[i], context) == NSOrderedDescending) + [self replaceObjectAtIndex:k withObject:B[i++]]; + else + { + [self replaceObjectAtIndex:k withObject:A]; + j++; + } + } + + while (k < j) + [self replaceObjectAtIndex:k++ withObject:B[i++]]; + } + } + + for(i=0;iarr[i] release]; + + free(B); +} #pragma mark CCArray - NSCopying protocol @@ -291,13 +419,13 @@ - (void) dealloc - (NSString*) description { - NSMutableString *ret = [NSMutableString stringWithFormat:@"<%@ = %08X> = ( ", [self class], self]; + NSMutableString *ret = [NSMutableString stringWithFormat:@"<%@ = %p> = ( ", [self class], self]; for( id obj in self) [ret appendFormat:@"%@, ",obj]; - + [ret appendString:@")"]; - + return ret; } diff --git a/cocos2d/cocos2d/Support/CCFileUtils.h b/cocos2d/cocos2d/Support/CCFileUtils.h old mode 100644 new mode 100755 index 0455202..9cf7359 --- a/cocos2d/cocos2d/Support/CCFileUtils.h +++ b/cocos2d/cocos2d/Support/CCFileUtils.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,37 +26,163 @@ #import - +#import "../ccTypes.h" /** Helper class to handle file operations */ @interface CCFileUtils : NSObject { + NSFileManager *fileManager_; + NSBundle *bundle_; + NSMutableDictionary *fullPathCache_; + NSMutableDictionary *removeSuffixCache_; + + +#ifdef __CC_PLATFORM_IOS + BOOL enableFallbackSuffixes_; + + NSString *iPhoneRetinaDisplaySuffix_; + NSString *iPadSuffix_; + NSString *iPadRetinaDisplaySuffix_; +#endif // __CC_PLATFORM_IOS } +/** NSBundle used by CCFileUtils. By default it uses [NSBundle mainBundle]. + @since v2.0 + */ +@property (nonatomic, readwrite, retain) NSBundle *bundle; + +/** NSFileManager used by CCFileUtils. By default it uses its own intance. + @since v2.0 + */ +@property (nonatomic, readwrite, retain) NSFileManager *fileManager; + +#ifdef __CC_PLATFORM_IOS +/** The iPhone RetinaDisplay suffixes to load resources. + By default it is "-hd" and "" in that order. + Only valid on iOS. Not valid for OS X. + + @since v1.1 + */ +@property (nonatomic,readwrite, copy, setter = setiPhoneRetinaDisplaySuffix:) NSString *iPhoneRetinaDisplaySuffix; + +/** The iPad suffixes to load resources. + By default it is "-ipad", "-hd", "", in that order. + Only valid on iOS. Not valid for OS X. + + @since v1.1 + */ +@property (nonatomic,readwrite, copy, setter = setiPadSuffix:) NSString *iPadSuffix; + + +/** Sets the iPad Retina Display suffixes to load resources. + By default it is "-ipadhd", "-ipad", "-hd", "", in that order. + Only valid on iOS. Not valid for OS X. + + @since v2.0 + */ +@property (nonatomic,readwrite, copy, setter = setiPadRetinaDisplaySuffix:) NSString *iPadRetinaDisplaySuffix; + +/** Whether of not the fallback sufixes is enabled. + When enabled it will try to search for the following suffixes in the following order until one is found: + * On iPad HD : iPad HD suffix, iPad suffix, iPhone HD suffix, Without suffix + * On iPad : iPad suffix, iPhone HD suffix, Without suffix + * On iPhone HD: iPhone HD suffix, Without suffix + + By default this functionality is off; +*/ +@property (nonatomic, readwrite) BOOL enableFallbackSuffixes; + + #endif // __CC_PLATFORM_IOS + +/** returns the shared file utils instance */ ++(CCFileUtils*) sharedFileUtils; + + +/** Purge cached entries. + Will be called automatically by the Director when a memory warning is received + */ +-(void) purgeCachedEntries; + /** Returns the fullpath of an filename. + + If in iPhoneRetinaDisplay mode, and a RetinaDisplay file is found, it will return that path. + If in iPad mode, and an iPad file is found, it will return that path. + + Examples: + + * In iPad mode: "image.png" -> "/full/path/image-ipad.png" (in case the -ipad file exists) + * In iPhone RetinaDisplay mode: "image.png" -> "/full/path/image-hd.png" (in case the -hd file exists) + * In iPad RetinaDisplay mode: "image.png" -> "/full/path/image-ipadhd.png" (in case the -ipadhd file exists) + + */ +-(NSString*) fullPathFromRelativePath:(NSString*) relPath; + + +/** Returns the fullpath of an filename including the resolution of the image. + + If in RetinaDisplay mode, and a RetinaDisplay file is found, it will return that path. + If in iPad mode, and an iPad file is found, it will return that path. - If this method is when Retina Display is enabled, then the - Retina Display suffix will be appended to the file (See ccConfig.h). + Examples: - If the Retina Display image doesn't exist, then it will return the "non-Retina Display" image + * In iPad mode: "image.png" -> "/full/path/image-ipad.png" (in case the -ipad file exists) + * In iPhone RetinaDisplay mode: "image.png" -> "/full/path/image-hd.png" (in case the -hd file exists) + * In iPad RetinaDisplay mode: "image.png" -> "/full/path/image-ipadhd.png" (in case the -ipadhd file exists) + If an iPad file is found, it will set resolution type to kCCResolutioniPad + If a RetinaDisplay file is found, it will set resolution type to kCCResolutionRetinaDisplay + + */ +-(NSString*) fullPathFromRelativePath:(NSString*)relPath resolutionType:(ccResolutionType*)resolutionType; + +#ifdef __CC_PLATFORM_IOS + +/** removes the suffix from a path + * On iPhone RetinaDisplay it will remove the -hd suffix + * On iPad it will remove the -ipad suffix + * On iPad RetinaDisplay it will remove the -ipadhd suffix + + Only valid on iOS. Not valid for OS X. + + @since v0.99.5 + */ +-(NSString *)removeSuffixFromFile:(NSString*) path; + +/** Returns whether or not a given path exists with the iPhone RetinaDisplay suffix. + Only available on iOS. Not supported on OS X. + @since v1.1 + */ +-(BOOL) iPhoneRetinaDisplayFileExistsAtPath:(NSString*)filename; + +/** Returns whether or not a given filename exists with the iPad suffix. + Only available on iOS. Not supported on OS X. + @since v1.1 */ -+(NSString*) fullPathFromRelativePath:(NSString*) relPath; +-(BOOL) iPadFileExistsAtPath:(NSString*)filename; + +/** Returns whether or not a given filename exists with the iPad RetinaDisplay suffix. + Only available on iOS. Not supported on OS X. + @since v2.0 + */ +-(BOOL) iPadRetinaDisplayFileExistsAtPath:(NSString*)filename; + +#endif // __CC_PLATFORM_IOS + @end + +#ifdef __cplusplus +extern "C" { +#endif + /** loads a file into memory. the caller should release the allocated buffer. - + @returns the size of the allocated buffer @since v0.99.5 */ NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out); - -/** removes the HD suffix from a path - - @returns NSString * without the HD suffix - @since v0.99.5 - */ -NSString *ccRemoveHDSuffixFromFile( NSString *path ); - +#ifdef __cplusplus +} +#endif diff --git a/cocos2d/cocos2d/Support/CCFileUtils.m b/cocos2d/cocos2d/Support/CCFileUtils.m old mode 100644 new mode 100755 index 6d33799..97c4a94 --- a/cocos2d/cocos2d/Support/CCFileUtils.m +++ b/cocos2d/cocos2d/Support/CCFileUtils.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -25,26 +25,32 @@ */ -#import #import "CCFileUtils.h" #import "../CCConfiguration.h" #import "../ccMacros.h" #import "../ccConfig.h" +#import "../ccTypes.h" -static NSFileManager *__localFileManager=nil; +enum { + kCCiPhone, + kCCiPhoneRetinaDisplay, + kCCiPad, + kCCiPadRetinaDisplay, +}; -// -NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out) -{ +#pragma mark - Helper free functions + +NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out) +{ NSCAssert( out, @"ccLoadFileIntoMemory: invalid 'out' parameter"); NSCAssert( &*out, @"ccLoadFileIntoMemory: invalid 'out' parameter"); - + size_t size = 0; FILE *f = fopen(filename, "rb"); - if( !f ) { + if( !f ) { *out = NULL; return -1; - } + } fseek(f, 0, SEEK_END); size = ftell(f); @@ -52,7 +58,7 @@ NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out) *out = malloc(size); size_t read = fread(*out, 1, size, f); - if( read != size ) { + if( read != size ) { free(*out); *out = NULL; return -1; @@ -63,107 +69,359 @@ NSInteger ccLoadFileIntoMemory(const char *filename, unsigned char **out) return size; } -NSString *ccRemoveHDSuffixFromFile( NSString *path ) -{ -#if CC_IS_RETINA_DISPLAY_SUPPORTED +#pragma mark - CCCacheValue - if( CC_CONTENT_SCALE_FACTOR() == 2 ) { - - NSString *name = [path lastPathComponent]; - - // check if path already has the suffix. - if( [name rangeOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX].location != NSNotFound ) { - - CCLOG(@"cocos2d: Filename(%@) contains %@ suffix. Removing it. See cocos2d issue #1040", path, CC_RETINA_DISPLAY_FILENAME_SUFFIX); +@interface CCCacheValue : NSObject +{ + NSString *fullpath_; + ccResolutionType resolutionType_; +} +@property (nonatomic, readwrite, retain) NSString *fullpath; +@property (nonatomic, readwrite ) ccResolutionType resolutionType; +@end - NSString *newLastname = [name stringByReplacingOccurrencesOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX withString:@""]; - - NSString *pathWithoutLastname = [path stringByDeletingLastPathComponent]; - return [pathWithoutLastname stringByAppendingPathComponent:newLastname]; - } +@implementation CCCacheValue +@synthesize fullpath = fullpath_, resolutionType = resolutionType_; +-(id) initWithFullPath:(NSString*)path resolutionType:(ccResolutionType)resolutionType +{ + if( (self=[super init]) ) + { + self.fullpath = path; + self.resolutionType = resolutionType; } + + return self; +} -#endif // CC_IS_RETINA_DISPLAY_SUPPORTED - - return path; +- (void)dealloc +{ + [fullpath_ release]; + [super dealloc]; } +@end +#pragma mark - CCFileUtils + +#ifdef __CC_PLATFORM_IOS +@interface CCFileUtils() +-(NSString *) removeSuffix:(NSString*)suffix fromPath:(NSString*)path; +-(BOOL) fileExistsAtPath:(NSString*)string withSuffix:(NSString*)suffix; +-(NSInteger) runningDevice; +@end +#endif // __CC_PLATFORM_IOS @implementation CCFileUtils -+(void) initialize +@synthesize fileManager=fileManager_, bundle=bundle_; +#ifdef __CC_PLATFORM_IOS +@synthesize iPhoneRetinaDisplaySuffix = iPhoneRetinaDisplaySuffix_; +@synthesize iPadSuffix = iPadSuffix_; +@synthesize iPadRetinaDisplaySuffix = iPadRetinaDisplaySuffix_; +@synthesize enableFallbackSuffixes = enableFallbackSuffixes_; +#endif // __CC_PLATFORM_IOS + ++ (id)sharedFileUtils { - if( self == [CCFileUtils class] ) - __localFileManager = [[NSFileManager alloc] init]; + static dispatch_once_t pred; + static CCFileUtils *fileUtils = nil; + dispatch_once(&pred, ^{ + fileUtils = [[self alloc] init]; + }); + return fileUtils; } -+(NSString*) getDoubleResolutionImage:(NSString*)path +-(id) init { -#if CC_IS_RETINA_DISPLAY_SUPPORTED + if( (self=[super init])) { + fileManager_ = [[NSFileManager alloc] init]; - if( CC_CONTENT_SCALE_FACTOR() == 2 ) - { + fullPathCache_ = [[NSMutableDictionary alloc] initWithCapacity:30]; + removeSuffixCache_ = [[NSMutableDictionary alloc] initWithCapacity:30]; + bundle_ = [[NSBundle mainBundle] retain]; + +#ifdef __CC_PLATFORM_IOS + iPhoneRetinaDisplaySuffix_ = @"-hd"; + iPadSuffix_ = @"-ipad"; + iPadRetinaDisplaySuffix_ = @"-ipadhd"; + + enableFallbackSuffixes_ = NO; +#endif // __CC_PLATFORM_IOS + + } + + return self; +} + +-(void) purgeCachedEntries +{ + [fullPathCache_ removeAllObjects]; + [removeSuffixCache_ removeAllObjects]; +} + +- (void)dealloc +{ + [fileManager_ release]; + [bundle_ release]; + [fullPathCache_ release]; + [removeSuffixCache_ release]; + +#ifdef __CC_PLATFORM_IOS + [iPhoneRetinaDisplaySuffix_ release]; + [iPadSuffix_ release]; + [iPadRetinaDisplaySuffix_ release]; +#endif // __CC_PLATFORM_IOS + + [super dealloc]; +} + +-(NSString*) pathForResource:(NSString*)resource ofType:(NSString *)ext inDirectory:(NSString *)subpath +{ + return [bundle_ pathForResource:resource + ofType:ext + inDirectory:subpath]; +} + +-(NSString*) getPath:(NSString*)path forSuffix:(NSString*)suffix +{ + NSString *newName = path; + + // only recreate filename if suffix is valid + if( suffix && [suffix length] > 0) + { NSString *pathWithoutExtension = [path stringByDeletingPathExtension]; NSString *name = [pathWithoutExtension lastPathComponent]; - + // check if path already has the suffix. - if( [name rangeOfString:CC_RETINA_DISPLAY_FILENAME_SUFFIX].location != NSNotFound ) { - - CCLOG(@"cocos2d: WARNING Filename(%@) already has the suffix %@. Using it.", name, CC_RETINA_DISPLAY_FILENAME_SUFFIX); - return path; - } + if( [name rangeOfString:suffix].location == NSNotFound ) { + + NSString *extension = [path pathExtension]; + + if( [extension isEqualToString:@"ccz"] || [extension isEqualToString:@"gz"] ) + { + // All ccz / gz files should be in the format filename.xxx.ccz + // so we need to pull off the .xxx part of the extension as well + extension = [NSString stringWithFormat:@"%@.%@", [pathWithoutExtension pathExtension], extension]; + pathWithoutExtension = [pathWithoutExtension stringByDeletingPathExtension]; + } + + + newName = [pathWithoutExtension stringByAppendingString:suffix]; + newName = [newName stringByAppendingPathExtension:extension]; + } else + CCLOGWARN(@"cocos2d: WARNING Filename(%@) already has the suffix %@. Using it.", name, suffix); + } + + NSString *ret = nil; + // only if it is not an absolute path + if( ! [path isAbsolutePath] ) { - NSString *extension = [path pathExtension]; - - if( [extension isEqualToString:@"ccz"] || [extension isEqualToString:@"gz"] ) - { - // All ccz / gz files should be in the format filename.xxx.ccz - // so we need to pull off the .xxx part of the extension as well - extension = [NSString stringWithFormat:@"%@.%@", [pathWithoutExtension pathExtension], extension]; - pathWithoutExtension = [pathWithoutExtension stringByDeletingPathExtension]; - } - + // pathForResource also searches in .lproj directories. issue #1230 + NSString *imageDirectory = [path stringByDeletingLastPathComponent]; - NSString *retinaName = [pathWithoutExtension stringByAppendingString:CC_RETINA_DISPLAY_FILENAME_SUFFIX]; - retinaName = [retinaName stringByAppendingPathExtension:extension]; + // If the file does not exist it will return nil. + ret = [self pathForResource:[newName lastPathComponent] + ofType:nil + inDirectory:imageDirectory]; + } + else if( [fileManager_ fileExistsAtPath:newName] ) + ret = newName; + + if( ! ret ) + CCLOGINFO(@"cocos2d: CCFileUtils: file not found: %@", [newName lastPathComponent] ); + + return ret; +} + +-(NSString*) fullPathFromRelativePath:(NSString*)relPath resolutionType:(ccResolutionType*)resolutionType +{ + NSAssert(relPath != nil, @"CCFileUtils: Invalid path"); + + CCCacheValue *value = [fullPathCache_ objectForKey:relPath]; + if( value ) { + *resolutionType = value.resolutionType; + return value.fullpath; + } + + // Initialize to non-nil + NSString *ret = @""; - if( [__localFileManager fileExistsAtPath:retinaName] ) - return retinaName; +#ifdef __CC_PLATFORM_IOS - CCLOG(@"cocos2d: CCFileUtils: Warning HD file not found: %@", [retinaName lastPathComponent] ); + NSInteger device = [self runningDevice]; + + // iPad HD ? + if( device == kCCiPadRetinaDisplay ) { + ret = [self getPath:relPath forSuffix:iPadRetinaDisplaySuffix_]; + *resolutionType = kCCResolutioniPadRetinaDisplay; + } + + // iPad ? + if( device == kCCiPad || (enableFallbackSuffixes_ && !ret) ) { + ret = [self getPath:relPath forSuffix:iPadSuffix_]; + *resolutionType = kCCResolutioniPad; + } + + // iPhone HD ? + if( device == kCCiPhoneRetinaDisplay || (enableFallbackSuffixes_ && !ret) ) { + ret = [self getPath:relPath forSuffix:iPhoneRetinaDisplaySuffix_]; + *resolutionType = kCCResolutioniPhoneRetinaDisplay; } + + // If it is not Phone HD, or if the previous "getPath" failed, then use iPhone images. + if( device == kCCiPhone || !ret ) + { + ret = [self getPath:relPath forSuffix:@""]; + *resolutionType = kCCResolutioniPhone; + } + +#elif defined(__CC_PLATFORM_MAC) + + *resolutionType = kCCResolutionMac; + + ret = [self getPath:relPath forSuffix:@""]; + +#endif // __CC_PLATFORM_MAC -#endif // CC_IS_RETINA_DISPLAY_SUPPORTED + if( ! ret ) { + CCLOGWARN(@"cocos2d: Warning: File not found: %@", relPath); + ret = relPath; + } + + value = [[CCCacheValue alloc] initWithFullPath:ret resolutionType:*resolutionType]; + [fullPathCache_ setObject:value forKey:relPath]; + [value release]; - return path; + return ret; } -+(NSString*) fullPathFromRelativePath:(NSString*) relPath +-(NSString*) fullPathFromRelativePath:(NSString*) relPath { - NSAssert(relPath != nil, @"CCFileUtils: Invalid path"); + ccResolutionType ignore; + return [self fullPathFromRelativePath:relPath resolutionType:&ignore]; +} - NSString *fullpath = nil; +#pragma mark CCFileUtils - Suffix (iOS only) + +#ifdef __CC_PLATFORM_IOS + +// XXX: Optimization: This should be called only once +-(NSInteger) runningDevice +{ + NSInteger ret=-1; + + if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) + { + if( CC_CONTENT_SCALE_FACTOR() == 2 ) + ret = kCCiPadRetinaDisplay; + else + ret = kCCiPad; + } + else + { + if( CC_CONTENT_SCALE_FACTOR() == 2 ) + ret = kCCiPhoneRetinaDisplay; + else + ret = kCCiPhone; + } + + return ret; +} + +-(NSString *) removeSuffix:(NSString*)suffix fromPath:(NSString*)path +{ + // quick return + if( ! suffix || [suffix length] == 0 ) + return path; + + NSString *name = [path lastPathComponent]; + + // check if path already has the suffix. + if( [name rangeOfString:suffix].location != NSNotFound ) { + + CCLOGINFO(@"cocos2d: Filename(%@) contains %@ suffix. Removing it. See cocos2d issue #1040", path, suffix); + + NSString *newLastname = [name stringByReplacingOccurrencesOfString:suffix withString:@""]; + + NSString *pathWithoutLastname = [path stringByDeletingLastPathComponent]; + return [pathWithoutLastname stringByAppendingPathComponent:newLastname]; + } + + // suffix was not removed + return nil; +} + +-(NSString*) removeSuffixFromFile:(NSString*) path +{ + NSString *withoutSuffix = [removeSuffixCache_ objectForKey:path]; + if( withoutSuffix ) + return withoutSuffix; + + // Initial value should be non-nil + NSString *ret = @""; + + NSInteger device = [self runningDevice]; + + if( device == kCCiPadRetinaDisplay ) + ret = [self removeSuffix:iPadRetinaDisplaySuffix_ fromPath:path]; + + if( device == kCCiPad || (enableFallbackSuffixes_ && !ret) ) + ret = [self removeSuffix:iPadSuffix_ fromPath:path]; + + if( device == kCCiPhoneRetinaDisplay || (enableFallbackSuffixes_ && !ret) ) + ret = [self removeSuffix:iPhoneRetinaDisplaySuffix_ fromPath:path]; + + if( device == kCCiPhone || !ret ) + ret = path; + + if( ret ) + [removeSuffixCache_ setObject:ret forKey:path]; + return ret; +} + +-(BOOL) fileExistsAtPath:(NSString*)relPath withSuffix:(NSString*)suffix +{ + NSString *fullpath = nil; + // only if it is not an absolute path - if( ! [relPath isAbsolutePath] ) - { + if( ! [relPath isAbsolutePath] ) { + // pathForResource also searches in .lproj directories. issue #1230 NSString *file = [relPath lastPathComponent]; NSString *imageDirectory = [relPath stringByDeletingLastPathComponent]; - - fullpath = [[NSBundle mainBundle] pathForResource:file + + fullpath = [bundle_ pathForResource:file ofType:nil inDirectory:imageDirectory]; + } - + if (fullpath == nil) fullpath = relPath; - - fullpath = [self getDoubleResolutionImage:fullpath]; - - return fullpath; + + NSString *path = [self getPath:fullpath forSuffix:suffix]; + + return ( path != nil ); +} + +-(BOOL) iPhoneRetinaDisplayFileExistsAtPath:(NSString*)path +{ + return [self fileExistsAtPath:path withSuffix:iPhoneRetinaDisplaySuffix_]; } +-(BOOL) iPadFileExistsAtPath:(NSString*)path +{ + return [self fileExistsAtPath:path withSuffix:iPadSuffix_]; +} + +-(BOOL) iPadRetinaDisplayFileExistsAtPath:(NSString*)path +{ + return [self fileExistsAtPath:path withSuffix:iPadRetinaDisplaySuffix_]; +} + +#endif // __CC_PLATFORM_IOS + + @end diff --git a/cocos2d/cocos2d/Support/CCProfiling.h b/cocos2d/cocos2d/Support/CCProfiling.h old mode 100644 new mode 100755 index b241fb9..9b645f1 --- a/cocos2d/cocos2d/Support/CCProfiling.h +++ b/cocos2d/cocos2d/Support/CCProfiling.h @@ -2,17 +2,17 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2010 Stuart Carnie - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,25 +29,61 @@ @class CCProfilingTimer; +/** CCProfiler + cocos2d builtin profiler. + + To use it, enable set the CC_ENABLE_PROFILERS=1 in the ccConfig.h file + */ @interface CCProfiler : NSObject { - NSMutableArray* activeTimers; +@public + NSMutableDictionary* activeTimers; } +/** shared instance */ + (CCProfiler*)sharedProfiler; -+ (CCProfilingTimer*)timerWithName:(NSString*)timerName andInstance:(id)instance; -+ (void)releaseTimer:(CCProfilingTimer*)timer; + +/** Creates and adds a new timer */ +- (CCProfilingTimer*) createAndAddTimerWithName:(NSString*)timerName; + +/** releases a timer */ +- (void)releaseTimer:(NSString*)timerName; + +/** releases all timers */ +- (void) releaseAllTimers; + +/** display the timers */ - (void)displayTimers; @end - +/** CCProfilingTimer +Profiling timers used by CCProfiler + */ @interface CCProfilingTimer : NSObject { - NSString* name; - struct timeval startTime; - double averageTime; + +@public + NSString *name; + struct timeval startTime; + double averageTime; + double minTime; + double maxTime; + double totalTime; + NSUInteger numberOfCalls; } +/** resets the timer properties */ +-(void) reset; @end -extern void CCProfilingBeginTimingBlock(CCProfilingTimer* timer); -extern void CCProfilingEndTimingBlock(CCProfilingTimer* timer); +extern void CCProfilingBeginTimingBlock(NSString *timerName); +extern void CCProfilingEndTimingBlock(NSString *timerName); +extern void CCProfilingResetTimingBlock(NSString *timerName); + +/* + * cocos2d profiling categories + * used to enable / disable profilers with granularity + */ + +extern BOOL kCCProfilerCategorySprite; +extern BOOL kCCProfilerCategoryBatchSprite; +extern BOOL kCCProfilerCategoryParticles; diff --git a/cocos2d/cocos2d/Support/CCProfiling.m b/cocos2d/cocos2d/Support/CCProfiling.m old mode 100644 new mode 100755 index 13c8c81..abd8979 --- a/cocos2d/cocos2d/Support/CCProfiling.m +++ b/cocos2d/cocos2d/Support/CCProfiling.m @@ -2,17 +2,17 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2010 Stuart Carnie - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -24,94 +24,163 @@ */ #import "../ccConfig.h" - -#if CC_ENABLE_PROFILERS +#import "../ccMacros.h" #import "CCProfiling.h" +#pragma mark - Profiling Categories + +/* set to NO the categories that you don't want to profile */ +BOOL kCCProfilerCategorySprite = NO; +BOOL kCCProfilerCategoryBatchSprite = NO; +BOOL kCCProfilerCategoryParticles = NO; + + @interface CCProfilingTimer() -- (id)initWithName:(NSString*)timerName andInstance:(id)instance; +- (id)initWithName:(NSString*)timerName; @end + +#pragma mark - CCProfiler + @implementation CCProfiler static CCProfiler* g_sharedProfiler; -+ (CCProfiler*)sharedProfiler { ++ (CCProfiler*)sharedProfiler +{ if (!g_sharedProfiler) g_sharedProfiler = [[CCProfiler alloc] init]; - + return g_sharedProfiler; } -+ (CCProfilingTimer*)timerWithName:(NSString*)timerName andInstance:(id)instance { - CCProfiler* p = [CCProfiler sharedProfiler]; - CCProfilingTimer* t = [[CCProfilingTimer alloc] initWithName:timerName andInstance:instance]; - [p->activeTimers addObject:t]; +- (CCProfilingTimer*) createAndAddTimerWithName:(NSString*)timerName +{ + CCProfilingTimer* t = [[CCProfilingTimer alloc] initWithName:timerName]; + [activeTimers setObject:t forKey:timerName]; [t release]; return t; } -+ (void)releaseTimer:(CCProfilingTimer*)timer { - CCProfiler* p = [CCProfiler sharedProfiler]; - [p->activeTimers removeObject:timer]; +- (void)releaseTimer:(NSString*)timerName +{ + [activeTimers removeObjectForKey:timerName]; } -- (id)init { - if (!(self = [super init])) return nil; - - activeTimers = [[NSMutableArray alloc] init]; - +- (void) releaseAllTimers +{ + [activeTimers removeAllObjects]; +} + +- (id)init +{ + if ((self = [super init])) { + activeTimers = [[NSMutableDictionary alloc] initWithCapacity:10]; + } + return self; } -- (void)dealloc { +- (void)dealloc +{ [activeTimers release]; [super dealloc]; } -- (void)displayTimers { - for (id timer in activeTimers) { +- (void)displayTimers +{ + NSArray *values = [activeTimers allValues]; + for (CCProfilingTimer *timer in values) { printf("%s\n", [[timer description] cStringUsingEncoding:[NSString defaultCStringEncoding]]); } } @end +#pragma mark - CCProfilingTimer + + @implementation CCProfilingTimer -- (id)initWithName:(NSString*)timerName andInstance:(id)instance { - if (!(self = [super init])) return nil; - - name = [[NSString stringWithFormat:@"%@ (0x%.8x)", timerName, instance] retain]; - +- (id)initWithName:(NSString*)timerName +{ + if ((self = [super init])) { + name = [timerName copy]; + numberOfCalls = 0; + averageTime = 0; + totalTime = 0; + minTime = 10000; + maxTime = 0; + gettimeofday(&startTime, NULL); + } + return self; } -- (void)dealloc { +- (void)dealloc +{ + CCLOGINFO(@"deallocing %@", self); [name release]; [super dealloc]; } -- (NSString*)description { - return [NSString stringWithFormat:@"%@ : avg time, %fms", name, averageTime]; +- (NSString*)description +{ + return [NSString stringWithFormat:@"%@ ::\tavg: %fms,\tmin: %fms,\tmax: %fms,\ttotal: %.2fs,\tnr calls: %ld", name, averageTime, minTime, maxTime, totalTime / 1000.0, (unsigned long)numberOfCalls]; +} + +-(void) reset +{ + numberOfCalls = 0; + averageTime = 0; + totalTime = 0; + minTime = 10000; + maxTime = 0; + gettimeofday(&startTime, NULL); } -void CCProfilingBeginTimingBlock(CCProfilingTimer* timer) { +@end + + +void CCProfilingBeginTimingBlock(NSString *timerName) +{ + CCProfiler* p = [CCProfiler sharedProfiler]; + CCProfilingTimer *timer = [p->activeTimers objectForKey:timerName]; + if( ! timer ) + timer = [p createAndAddTimerWithName:timerName]; + gettimeofday(&timer->startTime, NULL); + + timer->numberOfCalls++; } -typedef unsigned int uint32; -void CCProfilingEndTimingBlock(CCProfilingTimer* timer) { +void CCProfilingEndTimingBlock(NSString *timerName) +{ + CCProfiler* p = [CCProfiler sharedProfiler]; + CCProfilingTimer *timer = [p->activeTimers objectForKey:timerName]; + + NSCAssert1(timer, @"CCProfilingTimer %@ not found", timerName); + struct timeval currentTime; gettimeofday(¤tTime, NULL); timersub(¤tTime, &timer->startTime, ¤tTime); double duration = currentTime.tv_sec * 1000.0 + currentTime.tv_usec / 1000.0; - - // return in milliseconds + + // milliseconds timer->averageTime = (timer->averageTime + duration) / 2.0f; + timer->totalTime += duration; + timer->maxTime = MAX( timer->maxTime, duration); + timer->minTime = MIN( timer->minTime, duration); + } -@end +void CCProfilingResetTimingBlock(NSString *timerName) +{ + CCProfiler* p = [CCProfiler sharedProfiler]; + CCProfilingTimer *timer = [p->activeTimers objectForKey:timerName]; -#endif + NSCAssert1(timer, @"CCProfilingTimer %@ not found", timerName); + + [timer reset]; +} diff --git a/cocos2d/cocos2d/CCBlockSupport.h b/cocos2d/cocos2d/Support/CCVertex.h old mode 100644 new mode 100755 similarity index 68% rename from cocos2d/cocos2d/CCBlockSupport.h rename to cocos2d/cocos2d/Support/CCVertex.h index 339d5aa..8873fee --- a/cocos2d/cocos2d/CCBlockSupport.h +++ b/cocos2d/cocos2d/Support/CCVertex.h @@ -1,18 +1,18 @@ /* * cocos2d for iPhone: http://www.cocos2d-iphone.org * - * Copyright (c) 2010 Stuart Carnie - * + * Copyright (c) 2011 ForzeField Studios S.L. http://forzefield.com + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -20,32 +20,17 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - * - */ - - -#import - -/** @file - cocos2d blocks support */ -// To comply with Apple Objective C runtime (this is defined in NSObjCRuntime.h) -#if !defined(NS_BLOCKS_AVAILABLE) - #if __BLOCKS__ - #define NS_BLOCKS_AVAILABLE 1 - #else - #define NS_BLOCKS_AVAILABLE 0 - #endif -#endif - -#if NS_BLOCKS_AVAILABLE - -@interface NSObject(CCBlocksAdditions) +#import "ccTypes.h" -- (void)ccCallbackBlock; -- (void)ccCallbackBlockWithSender:(id)sender; +/** @file CCVertex.h */ -@end +/** converts a line to a polygon */ +void ccVertexLineToPolygon(CGPoint *points, float stroke, ccVertex2F *vertices, NSUInteger offset, NSUInteger nuPoints); -#endif // NS_BLOCKS_AVAILABLE +/** returns wheter or not the line intersects */ +BOOL ccVertexLineIntersect(float Ax, float Ay, + float Bx, float By, + float Cx, float Cy, + float Dx, float Dy, float *T); diff --git a/cocos2d/cocos2d/Support/CCVertex.m b/cocos2d/cocos2d/Support/CCVertex.m new file mode 100755 index 0000000..fcb83f3 --- /dev/null +++ b/cocos2d/cocos2d/Support/CCVertex.m @@ -0,0 +1,134 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2011 ForzeField Studios S.L. http://forzefield.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import "CCVertex.h" +#import "CGPointExtension.h" +#import "../ccMacros.h" + +void ccVertexLineToPolygon(CGPoint *points, float stroke, ccVertex2F *vertices, NSUInteger offset, NSUInteger nuPoints) +{ + nuPoints += offset; + if(nuPoints<=1) return; + + stroke *= 0.5f; + + NSUInteger idx; + NSUInteger nuPointsMinus = nuPoints-1; + + for(NSUInteger i = offset; i1.0f) + fixVertex = YES; + + if(fixVertex) + { + vertices[idx1] = p4; + vertices[idx1+1] = p3; + } + } +} + +BOOL ccVertexLineIntersect(float Ax, float Ay, + float Bx, float By, + float Cx, float Cy, + float Dx, float Dy, float *T) +{ + float distAB, theCos, theSin, newX; + + // FAIL: Line undefined + if ((Ax==Bx && Ay==By) || (Cx==Dx && Cy==Dy)) return NO; + + // Translate system to make A the origin + Bx-=Ax; By-=Ay; + Cx-=Ax; Cy-=Ay; + Dx-=Ax; Dy-=Ay; + + // Length of segment AB + distAB = sqrtf(Bx*Bx+By*By); + + // Rotate the system so that point B is on the positive X axis. + theCos = Bx/distAB; + theSin = By/distAB; + newX = Cx*theCos+Cy*theSin; + Cy = Cy*theCos-Cx*theSin; Cx = newX; + newX = Dx*theCos+Dy*theSin; + Dy = Dy*theCos-Dx*theSin; Dx = newX; + + // FAIL: Lines are parallel. + if (Cy == Dy) return NO; + + // Discover the relative position of the intersection in the line AB + *T = (Dx+(Cx-Dx)*Dy/(Dy-Cy))/distAB; + + // Success. + return YES; +} diff --git a/cocos2d/cocos2d/Support/CGPointExtension.h b/cocos2d/cocos2d/Support/CGPointExtension.h old mode 100644 new mode 100755 index 96edeb7..90510c4 --- a/cocos2d/cocos2d/Support/CGPointExtension.h +++ b/cocos2d/cocos2d/Support/CGPointExtension.h @@ -4,17 +4,17 @@ * Copyright (c) 2007 Scott Lembcke * * Copyright (c) 2010 Lam Pham - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -32,23 +32,23 @@ @file CGPoint extensions based on Chipmunk's cpVect file. These extensions work both with CGPoint and cpVect. - + The "ccp" prefix means: "CoCos2d Point" - + Examples: - ccpAdd( ccp(1,1), ccp(2,2) ); // preferred cocos2d way - ccpAdd( CGPointMake(1,1), CGPointMake(2,2) ); // also ok but more verbose - + - cpvadd( cpv(1,1), cpv(2,2) ); // way of the chipmunk - ccpAdd( cpv(1,1), cpv(2,2) ); // mixing chipmunk and cocos2d (avoid) - cpvadd( CGPointMake(1,1), CGPointMake(2,2) ); // mixing chipmunk and CG (avoid) */ -#import +#import "ccMacros.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#elif defined(__CC_PLATFORM_MAC) #import #endif @@ -57,7 +57,7 @@ #ifdef __cplusplus extern "C" { -#endif +#endif /** Helper macro that creates a CGPoint @return CGPoint @@ -196,6 +196,16 @@ ccpLengthSQ(const CGPoint v) return ccpDot(v, v); } +/** Calculates the square distance between two points (not calling sqrt() ) + @return CGFloat + @since v1.1 +*/ +static inline CGFloat +ccpDistanceSQ(const CGPoint p1, const CGPoint p2) +{ + return ccpLengthSQ(ccpSub(p1, p2)); +} + /** Calculates distance between point an origin @return CGFloat @since v0.7.2 @@ -293,27 +303,27 @@ float ccpAngle(CGPoint a, CGPoint b); CGPoint ccpRotateByAngle(CGPoint v, CGPoint pivot, float angle); /** A general line-line intersection test - @param p1 + @param p1 is the startpoint for the first line P1 = (p1 - p2) - @param p2 + @param p2 is the endpoint for the first line P1 = (p1 - p2) - @param p3 + @param p3 is the startpoint for the second line P2 = (p3 - p4) - @param p4 + @param p4 is the endpoint for the second line P2 = (p3 - p4) - @param s + @param s is the range for a hitpoint in P1 (pa = p1 + s*(p2 - p1)) @param t is the range for a hitpoint in P3 (pa = p2 + t*(p4 - p3)) - @return bool + @return bool indicating successful intersection of a line - note that to truly test intersection for segments we have to make + note that to truly test intersection for segments we have to make sure that s & t lie within [0..1] and for rays, make sure s & t > 0 the hit point is p3 + t * (p4 - p3); the hit point also is p1 + s * (p2 - p1); @since v0.99.1 */ -BOOL ccpLineIntersect(CGPoint p1, CGPoint p2, +BOOL ccpLineIntersect(CGPoint p1, CGPoint p2, CGPoint p3, CGPoint p4, float *s, float *t); diff --git a/cocos2d/cocos2d/Support/CGPointExtension.m b/cocos2d/cocos2d/Support/CGPointExtension.m old mode 100644 new mode 100755 index b06859d..8343a3f --- a/cocos2d/cocos2d/Support/CGPointExtension.m +++ b/cocos2d/cocos2d/Support/CGPointExtension.m @@ -11,10 +11,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -152,7 +152,7 @@ CGPoint ccpIntersectPoint(CGPoint A, CGPoint B, CGPoint C, CGPoint D) BOOL ccpLineIntersect(CGPoint A, CGPoint B, CGPoint C, CGPoint D, float *S, float *T) -{ +{ // FAIL: Line undefined if ( (A.x==B.x && A.y==B.y) || (C.x==D.x && C.y==D.y) ) return NO; @@ -169,9 +169,9 @@ BOOL ccpLineIntersect(CGPoint A, CGPoint B, *T = BAx*ACy - BAy*ACx; if (denom == 0) { - if (*S == 0 || *T == 0) { + if (*S == 0 || *T == 0) { // Lines incident - return YES; + return YES; } // Lines parallel and not incident return NO; diff --git a/cocos2d/cocos2d/Support/NSThread+performBlock.h b/cocos2d/cocos2d/Support/NSThread+performBlock.h new file mode 100755 index 0000000..bb5ec8d --- /dev/null +++ b/cocos2d/cocos2d/Support/NSThread+performBlock.h @@ -0,0 +1,22 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * + * Idea taken from: http://stackoverflow.com/a/3940757 + * + */ + +#import + +@interface NSThread (sendBlockToBackground) +/** performs a block on the thread. It won't wait until it is done. */ +- (void) performBlock:(void (^)(void))block; + +/** performs a block on the thread. */ +- (void) performBlock:(void (^)(void))block waitUntilDone:(BOOL)wait; + +/** performs a block on the thread. */ +- (void) performBlock:(void (^)(id param))block withObject:(id)object waitUntilDone:(BOOL)wait; + +@end diff --git a/cocos2d/cocos2d/Support/NSThread+performBlock.m b/cocos2d/cocos2d/Support/NSThread+performBlock.m new file mode 100755 index 0000000..2035161 --- /dev/null +++ b/cocos2d/cocos2d/Support/NSThread+performBlock.m @@ -0,0 +1,76 @@ +/* cocos2d for iPhone + * + * http://www.cocos2d-iphone.org + * + * + * Idea taken from: http://stackoverflow.com/a/3940757 + * + */ + + +#import "NSThread+performBlock.h" +#import "../ccMacros.h" + +typedef void (^BlockWithParam)(id param); +@interface CCObjectWith2Params : NSObject +{ +@public + BlockWithParam block; + id param; +} +@property (nonatomic,copy) BlockWithParam block; +@property (nonatomic,readwrite,retain) id param; +@end + +@implementation CCObjectWith2Params +@synthesize block, param; +- (void)dealloc { + CCLOG(@"cocos2d: deallocing %@", self); + [block release]; + [param release]; + + [super dealloc]; +} +@end + +@implementation NSThread (sendBlockToBackground) + +- (void) performBlock: (void (^)(void))block; +{ + return [self performBlock:block waitUntilDone:NO]; +} + +- (void) performBlock:(void (^)(void))block waitUntilDone:(BOOL)wait +{ + [self performSelector:@selector(executeBlock:) + onThread:self + withObject: [[block copy] autorelease] + waitUntilDone: wait]; +} + +- (void) performBlock:(void (^)(id param))block withObject:(id)object waitUntilDone:(BOOL)wait +{ + CCObjectWith2Params * obj = [[CCObjectWith2Params alloc] init]; + obj.block = block; + obj.param = object; + + [obj autorelease]; + + [self performSelector:@selector(executeBlock2:) + onThread:self + withObject:obj + waitUntilDone:wait]; +} + +- (void) executeBlock: (void (^)(void))block; +{ + block(); +} + +- (void) executeBlock2:(CCObjectWith2Params*)object +{ + BlockWithParam block = object.block; + block( object.param ); +} + +@end diff --git a/cocos2d/cocos2d/Support/OpenGL_Internal.h b/cocos2d/cocos2d/Support/OpenGL_Internal.h old mode 100644 new mode 100755 index 4789683..82dc473 --- a/cocos2d/cocos2d/Support/OpenGL_Internal.h +++ b/cocos2d/cocos2d/Support/OpenGL_Internal.h @@ -67,7 +67,13 @@ Copyright (C) 2008 Apple Inc. All Rights Reserved. /* EAGL and GL functions calling wrappers that log on error */ #define CALL_EAGL_FUNCTION(__FUNC__, ...) ({ EAGLError __error = __FUNC__( __VA_ARGS__ ); if(__error != kEAGLErrorSuccess) printf("%s() called from %s returned error %i\n", #__FUNC__, __FUNCTION__, __error); (__error ? NO : YES); }) //#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) printf("OpenGL error 0x%04X in %s\n", __error, __FUNCTION__); (__error ? NO : YES); }) -#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) printf("OpenGL error 0x%04X in %s\n", __error, __FUNCTION__); }) +#define CHECK_GL_ERROR() ({ GLenum __error = glGetError(); if(__error) printf("OpenGL error 0x%04X in %s %d\n", __error, __FUNCTION__, __LINE__); }) + +#if DEBUG +#define CHECK_GL_ERROR_DEBUG() ({ GLenum __error = glGetError(); if(__error) printf("OpenGL error 0x%04X in %s %d\n", __error, __FUNCTION__, __LINE__); }) +#else +#define CHECK_GL_ERROR_DEBUG() +#endif /* Optional delegate methods support */ #ifndef __DELEGATE_IVAR__ diff --git a/cocos2d/cocos2d/Support/TGAlib.h b/cocos2d/cocos2d/Support/TGAlib.h old mode 100644 new mode 100755 index 247084e..c52a2a5 --- a/cocos2d/cocos2d/Support/TGAlib.h +++ b/cocos2d/cocos2d/Support/TGAlib.h @@ -25,13 +25,13 @@ enum { typedef struct sImageTGA { int status; unsigned char type, pixelDepth; - + /** map width */ short int width; - + /** map height */ short int height; - + /** raw data */ unsigned char *imageData; int flipped; diff --git a/cocos2d/cocos2d/Support/TGAlib.m b/cocos2d/cocos2d/Support/TGAlib.m old mode 100644 new mode 100755 index 11303b4..c2766d4 --- a/cocos2d/cocos2d/Support/TGAlib.m +++ b/cocos2d/cocos2d/Support/TGAlib.m @@ -36,24 +36,24 @@ void tgaLoadHeader(FILE *file, tImageTGA *info) { fread(&info->pixelDepth, sizeof(unsigned char), 1, file); fread(&cGarbage, sizeof(unsigned char), 1, file); - + info->flipped = 0; if ( cGarbage & 0x20 ) info->flipped = 1; } // loads the image pixels. You shouldn't call this function directly void tgaLoadImageData(FILE *file, tImageTGA *info) { - + int mode,total,i; unsigned char aux; - + // mode equal the number of components for each pixel mode = info->pixelDepth / 8; // total is the number of unsigned chars we'll have to read total = info->height * info->width * mode; - + fread(info->imageData,sizeof(unsigned char),total,file); - + // mode=3 or 4 implies that the image is RGB(A). However TGA // stores it as BGR(A) so we'll have to swap R and B. if (mode >= 3) @@ -70,12 +70,12 @@ void tgaLoadRLEImageData(FILE *file, tImageTGA *info) unsigned int mode,total,i, index = 0; unsigned char aux[4], runlength = 0; unsigned int skip = 0, flag = 0; - + // mode equal the number of components for each pixel mode = info->pixelDepth / 8; // total is the number of unsigned chars we'll have to read total = info->height * info->width; - + for( i = 0; i < total; i++ ) { // if we have a run length pending, run it @@ -90,32 +90,32 @@ void tgaLoadRLEImageData(FILE *file, tImageTGA *info) // otherwise, read in the run length token if ( fread(&runlength,sizeof(unsigned char),1,file) != 1 ) return; - + // see if it's a RLE encoded sequence flag = runlength & 0x80; if ( flag ) runlength -= 128; skip = 0; } - + // do we need to skip reading this pixel? if ( !skip ) { // no, read in the pixel data if ( fread(aux,sizeof(unsigned char),mode,file) != mode ) return; - + // mode=3 or 4 implies that the image is RGB(A). However TGA // stores it as BGR(A) so we'll have to swap R and B. if ( mode >= 3 ) { unsigned char tmp; - + tmp = aux[0]; aux[0] = aux[2]; aux[2] = tmp; } } - + // add the pixel to our image memcpy(&info->imageData[index], aux, mode); index += mode; @@ -129,50 +129,50 @@ void tgaFlipImage( tImageTGA *info ) int rowbytes = info->width*mode; unsigned char *row = (unsigned char *)malloc(rowbytes); int y; - + if (row == NULL) return; - + for( y = 0; y < (info->height/2); y++ ) { memcpy(row, &info->imageData[y*rowbytes],rowbytes); memcpy(&info->imageData[y*rowbytes], &info->imageData[(info->height-(y+1))*rowbytes], rowbytes); memcpy(&info->imageData[(info->height-(y+1))*rowbytes], row, rowbytes); } - + free(row); info->flipped = 0; } // this is the function to call when we want to load an image tImageTGA * tgaLoad(const char *filename) { - + FILE *file; tImageTGA *info; int mode,total; - + // allocate memory for the info struct and check! info = (tImageTGA *)malloc(sizeof(tImageTGA)); if (info == NULL) return(NULL); - - + + // open the file for reading (binary mode) file = fopen(filename, "rb"); if (file == NULL) { info->status = TGA_ERROR_FILE_OPEN; return(info); } - + // load the header tgaLoadHeader(file,info); - + // check for errors when loading the header if (ferror(file)) { info->status = TGA_ERROR_READING_FILE; fclose(file); return(info); } - + // check if the image is color indexed if (info->type == 1) { info->status = TGA_ERROR_INDEXED_COLOR; @@ -185,7 +185,7 @@ void tgaFlipImage( tImageTGA *info ) fclose(file); return(info); } - + // mode equals the number of image components mode = info->pixelDepth / 8; // total is the number of unsigned chars to read @@ -193,7 +193,7 @@ void tgaFlipImage( tImageTGA *info ) // allocate memory for image pixels info->imageData = (unsigned char *)malloc(sizeof(unsigned char) * total); - + // check to make sure we have the memory required if (info->imageData == NULL) { info->status = TGA_ERROR_MEMORY; @@ -205,7 +205,7 @@ void tgaFlipImage( tImageTGA *info ) tgaLoadRLEImageData(file, info); else tgaLoadImageData(file,info); - + // check for errors when reading the pixels if (ferror(file)) { info->status = TGA_ERROR_READING_FILE; @@ -214,48 +214,48 @@ void tgaFlipImage( tImageTGA *info ) } fclose(file); info->status = TGA_OK; - + if ( info->flipped ) { tgaFlipImage( info ); if ( info->flipped ) info->status = TGA_ERROR_MEMORY; } - + return(info); } // converts RGB to greyscale void tgaRGBtogreyscale(tImageTGA *info) { - + int mode,i,j; - + unsigned char *newImageData; - + // if the image is already greyscale do nothing if (info->pixelDepth == 8) return; - + // compute the number of actual components mode = info->pixelDepth / 8; - + // allocate an array for the new image data - newImageData = (unsigned char *)malloc(sizeof(unsigned char) * + newImageData = (unsigned char *)malloc(sizeof(unsigned char) * info->height * info->width); if (newImageData == NULL) { return; } - + // convert pixels: greyscale = o.30 * R + 0.59 * G + 0.11 * B for (i = 0,j = 0; j < info->width * info->height; i +=mode, j++) - newImageData[j] = - (unsigned char)(0.30 * info->imageData[i] + + newImageData[j] = + (unsigned char)(0.30 * info->imageData[i] + 0.59 * info->imageData[i+1] + 0.11 * info->imageData[i+2]); - - + + //free old image data free(info->imageData); - + // reassign pixelDepth and type according to the new image type info->pixelDepth = 8; info->type = 3; @@ -265,7 +265,7 @@ void tgaRGBtogreyscale(tImageTGA *info) { // releases the memory used for the image void tgaDestroy(tImageTGA *info) { - + if (info != NULL) { if (info->imageData != NULL) free(info->imageData); diff --git a/cocos2d/cocos2d/Support/TransformUtils.h b/cocos2d/cocos2d/Support/TransformUtils.h old mode 100644 new mode 100755 index 49fde35..7cbb92e --- a/cocos2d/cocos2d/Support/TransformUtils.h +++ b/cocos2d/cocos2d/Support/TransformUtils.h @@ -2,17 +2,17 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2009 Valentin Milea - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,13 +23,12 @@ * */ -#import +#import "../ccMacros.h" +#import "../Platforms/CCGL.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import -#import -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) -#import +#elif defined(__CC_PLATFORM_MAC) #import #endif diff --git a/cocos2d/cocos2d/Support/TransformUtils.m b/cocos2d/cocos2d/Support/TransformUtils.m old mode 100644 new mode 100755 index 9caecf0..73e132f --- a/cocos2d/cocos2d/Support/TransformUtils.m +++ b/cocos2d/cocos2d/Support/TransformUtils.m @@ -2,17 +2,17 @@ * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2009 Valentin Milea - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -32,7 +32,7 @@ void CGAffineToGL(const CGAffineTransform *t, GLfloat *m) // | m[1] m[5] m[9] m[13] | | m12 m22 m32 m42 | | b d 0 ty | // | m[2] m[6] m[10] m[14] | <=> | m13 m23 m33 m43 | <=> | 0 0 1 0 | // | m[3] m[7] m[11] m[15] | | m14 m24 m34 m44 | | 0 0 0 1 | - + m[2] = m[3] = m[6] = m[7] = m[8] = m[9] = m[11] = m[14] = 0.0f; m[10] = m[15] = 1.0f; m[0] = t->a; m[4] = t->c; m[12] = t->tx; @@ -44,3 +44,4 @@ void GLToCGAffine(const GLfloat *m, CGAffineTransform *t) t->a = m[0]; t->c = m[4]; t->tx = m[12]; t->b = m[1]; t->d = m[5]; t->ty = m[13]; } + diff --git a/cocos2d/cocos2d/Support/ZipUtils.h b/cocos2d/cocos2d/Support/ZipUtils.h old mode 100644 new mode 100755 index 363f911..99034a9 --- a/cocos2d/cocos2d/Support/ZipUtils.h +++ b/cocos2d/cocos2d/Support/ZipUtils.h @@ -8,7 +8,7 @@ * * Some ideas were taken from: * http://themanaworld.org/ - * from the mapreader.cpp file + * from the mapreader.cpp file * */ @@ -19,8 +19,8 @@ #ifdef __cplusplus extern "C" { -#endif - +#endif + /* XXX: pragma pack ??? */ /** @struct CCZHeader */ @@ -31,19 +31,19 @@ extern "C" { uint32_t reserved; // Reserverd for users. uint32_t len; // size of the uncompressed file }; - + enum { CCZ_COMPRESSION_ZLIB, // zlib format. CCZ_COMPRESSION_BZIP2, // bzip2 format (not supported yet) CCZ_COMPRESSION_GZIP, // gzip format (not supported yet) CCZ_COMPRESSION_NONE, // plain (not supported yet) }; - + /** @file * Zip helper functions */ -/** +/** * Inflates either zlib or gzip deflated memory. The inflated memory is * expected to be freed by the caller. * @@ -54,7 +54,7 @@ extern "C" { */ int ccInflateMemory(unsigned char *in, unsigned int inLength, unsigned char **out); -/** +/** * Inflates either zlib or gzip deflated memory. The inflated memory is * expected to be freed by the caller. * @@ -66,7 +66,7 @@ int ccInflateMemory(unsigned char *in, unsigned int inLength, unsigned char **ou */ int ccInflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsigned char **out, unsigned int outLenghtHint ); - + /** inflates a GZip file into memory * * @returns the length of the deflated buffer @@ -82,10 +82,10 @@ int ccInflateGZipFile(const char *filename, unsigned char **out); * @since v0.99.5 */ int ccInflateCCZFile(const char *filename, unsigned char **out); - + #ifdef __cplusplus } -#endif - +#endif + #endif // __CC_ZIP_UTILS_H diff --git a/cocos2d/cocos2d/Support/ZipUtils.m b/cocos2d/cocos2d/Support/ZipUtils.m old mode 100644 new mode 100755 index ccd8bbc..d3a2379 --- a/cocos2d/cocos2d/Support/ZipUtils.m +++ b/cocos2d/cocos2d/Support/ZipUtils.m @@ -11,11 +11,9 @@ * * Some ideas were taken from: * http://themanaworld.org/ - * from the mapreader.cpp file + * from the mapreader.cpp file */ -#import - #import #import #import @@ -33,30 +31,30 @@ static int inflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsig { /* ret value */ int err = Z_OK; - + int bufferSize = outLenghtHint; *out = (unsigned char*) malloc(bufferSize); - - z_stream d_stream; /* decompression stream */ + + z_stream d_stream; /* decompression stream */ d_stream.zalloc = (alloc_func)0; d_stream.zfree = (free_func)0; d_stream.opaque = (voidpf)0; - + d_stream.next_in = in; d_stream.avail_in = inLength; d_stream.next_out = *out; d_stream.avail_out = bufferSize; - + /* window size to hold 256k */ if( (err = inflateInit2(&d_stream, 15 + 32)) != Z_OK ) return err; - + for (;;) { err = inflate(&d_stream, Z_NO_FLUSH); - + if (err == Z_STREAM_END) break; - + switch (err) { case Z_NEED_DICT: err = Z_DATA_ERROR; @@ -65,12 +63,12 @@ static int inflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsig inflateEnd(&d_stream); return err; } - + // not enough memory ? if (err != Z_STREAM_END) { - + unsigned char *tmp = realloc(*out, bufferSize * BUFFER_INC_FACTOR); - + /* not enough memory, ouch */ if (! tmp ) { CCLOG(@"cocos2d: ZipUtils: realloc failed"); @@ -79,14 +77,14 @@ static int inflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsig } /* only assign to *out if tmp is valid. it's not guaranteed that realloc will reuse the memory */ *out = tmp; - + d_stream.next_out = *out + bufferSize; d_stream.avail_out = bufferSize; bufferSize *= BUFFER_INC_FACTOR; } } - - + + *outLength = bufferSize - d_stream.avail_out; err = inflateEnd(&d_stream); return err; @@ -96,25 +94,25 @@ int ccInflateMemoryWithHint(unsigned char *in, unsigned int inLength, unsigned c { unsigned int outLength = 0; int err = inflateMemoryWithHint(in, inLength, out, &outLength, outLengthHint ); - + if (err != Z_OK || *out == NULL) { if (err == Z_MEM_ERROR) CCLOG(@"cocos2d: ZipUtils: Out of memory while decompressing map data!"); - + else if (err == Z_VERSION_ERROR) CCLOG(@"cocos2d: ZipUtils: Incompatible zlib version!"); - + else if (err == Z_DATA_ERROR) CCLOG(@"cocos2d: ZipUtils: Incorrect zlib compressed data!"); - + else CCLOG(@"cocos2d: ZipUtils: Unknown error while decompressing map data!"); - + free(*out); *out = NULL; outLength = 0; } - + return outLength; } @@ -128,7 +126,7 @@ int ccInflateGZipFile(const char *path, unsigned char **out) { int len; unsigned int offset = 0; - + NSCAssert( out, @"ccInflateGZipFile: invalid 'out' parameter"); NSCAssert( &*out, @"ccInflateGZipFile: invalid 'out' parameter"); @@ -137,17 +135,17 @@ int ccInflateGZipFile(const char *path, unsigned char **out) CCLOG(@"cocos2d: ZipUtils: error open gzip file: %s", path); return -1; } - + /* 512k initial decompress buffer */ int bufferSize = 512 * 1024; unsigned int totalBufferSize = bufferSize; - + *out = malloc( bufferSize ); if( ! out ) { CCLOG(@"cocos2d: ZipUtils: out of memory"); return -1; } - + for (;;) { len = gzread(inFile, *out + offset, bufferSize); if (len < 0) { @@ -158,9 +156,9 @@ int ccInflateGZipFile(const char *path, unsigned char **out) } if (len == 0) break; - + offset += len; - + // finish reading the file if( len < bufferSize ) break; @@ -175,10 +173,10 @@ int ccInflateGZipFile(const char *path, unsigned char **out) *out = NULL; return -1; } - + *out = tmp; } - + if (gzclose(inFile) != Z_OK) CCLOG(@"cocos2d: ZipUtils: gzclose failed"); @@ -195,8 +193,9 @@ int ccInflateCCZFile(const char *path, unsigned char **out) NSInteger fileLen = ccLoadFileIntoMemory( path, &compressed ); if( fileLen < 0 ) { CCLOG(@"cocos2d: Error loading CCZ compressed file"); + return -1; } - + struct CCZHeader *header = (struct CCZHeader*) compressed; // verify header @@ -205,7 +204,7 @@ int ccInflateCCZFile(const char *path, unsigned char **out) free(compressed); return -1; } - + // verify header version uint16_t version = CFSwapInt16BigToHost( header->version ); if( version > 2 ) { @@ -220,9 +219,9 @@ int ccInflateCCZFile(const char *path, unsigned char **out) free(compressed); return -1; } - + uint32_t len = CFSwapInt32BigToHost( header->len ); - + *out = malloc( len ); if(! *out ) { @@ -230,14 +229,14 @@ int ccInflateCCZFile(const char *path, unsigned char **out) free(compressed); return -1; } - - + + uLongf destlen = len; uLongf source = (uLongf) compressed + sizeof(*header); int ret = uncompress(*out, &destlen, (Bytef*)source, fileLen - sizeof(*header) ); free( compressed ); - + if( ret != Z_OK ) { CCLOG(@"cocos2d: CCZ: Failed to uncompress data"); @@ -245,7 +244,7 @@ int ccInflateCCZFile(const char *path, unsigned char **out) *out = NULL; return -1; } - - + + return len; } diff --git a/cocos2d/cocos2d/Support/base64.c b/cocos2d/cocos2d/Support/base64.c old mode 100644 new mode 100755 index 9aa52a6..1a7df47 --- a/cocos2d/cocos2d/Support/base64.c +++ b/cocos2d/cocos2d/Support/base64.c @@ -1,6 +1,6 @@ -/* +/* public domain BASE64 code - + modified for cocos2d-iphone: http://www.cocos2d-iphone.org */ @@ -45,7 +45,7 @@ int _base64Decode( unsigned char *input, unsigned int input_len, unsigned char * bits <<= 6; } } - + if( c == '=' ) { switch (char_count) { case 1: @@ -67,7 +67,7 @@ int _base64Decode( unsigned char *input, unsigned int input_len, unsigned char * errors++; } } - + *output_len = output_idx; return errors; } @@ -75,17 +75,17 @@ int _base64Decode( unsigned char *input, unsigned int input_len, unsigned char * int base64Decode(unsigned char *in, unsigned int inLength, unsigned char **out) { unsigned int outLength = 0; - + //should be enough to store 6-bit buffers in 8-bit buffers *out = malloc( inLength * 3.0f / 4.0f + 1 ); if( *out ) { int ret = _base64Decode(in, inLength, *out, &outLength); - + if (ret > 0 ) { printf("Base64Utils: error decoding"); free(*out); - *out = NULL; + *out = NULL; outLength = 0; } } diff --git a/cocos2d/cocos2d/Support/base64.h b/cocos2d/cocos2d/Support/base64.h old mode 100644 new mode 100755 index d30878e..095fc98 --- a/cocos2d/cocos2d/Support/base64.h +++ b/cocos2d/cocos2d/Support/base64.h @@ -1,6 +1,6 @@ -/* +/* public domain BASE64 code - + modified for cocos2d-iphone: http://www.cocos2d-iphone.org */ @@ -9,14 +9,14 @@ #ifdef __cplusplus extern "C" { -#endif - +#endif + /** @file base64 helper functions */ -/** +/** * Decodes a 64base encoded memory. The decoded memory is * expected to be freed by the caller. * @@ -28,6 +28,6 @@ int base64Decode(unsigned char *in, unsigned int inLength, unsigned char **out); #ifdef __cplusplus } -#endif +#endif #endif // __CC_BASE64_DECODE_H diff --git a/cocos2d/cocos2d/Support/ccCArray.h b/cocos2d/cocos2d/Support/ccCArray.h old mode 100644 new mode 100755 index 20d9633..daa8b4b --- a/cocos2d/cocos2d/Support/ccCArray.h +++ b/cocos2d/cocos2d/Support/ccCArray.h @@ -1,15 +1,15 @@ /* Copyright (c) 2007 Scott Lembcke - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -19,17 +19,17 @@ * SOFTWARE. */ -/** +/** @file - Based on Chipmunk cpArray. + based on Chipmunk cpArray. ccArray is a faster alternative to NSMutableArray, it does pretty much the same thing (stores NSObjects and retains/releases them appropriately). It's faster because: - - it uses a plain C interface so it doesn't incur Objective-c messaging overhead + - it uses a plain C interface so it doesn't incur Objective-c messaging overhead - it assumes you know what you're doing, so it doesn't spend time on safety checks (index out of bounds, required capacity etc.) - comparisons are done using pointer equality instead of isEqual - + There are 2 kind of functions: - ccArray functions that manipulates objective-c objects (retain and release are performanced) - ccCArray functions that manipulates values like if they were standard C structures (no retain/release is performed) @@ -43,240 +43,104 @@ #import #import +#import "../ccMacros.h" + +#ifdef __cplusplus +extern "C" { +#endif #pragma mark - #pragma mark ccArray for Objects -// Easy integration +// Easy integration #define CCARRAYDATA_FOREACH(__array__, __object__) \ __object__=__array__->arr[0]; for(NSUInteger i=0, num=__array__->num; iarr[i]) \ +#if defined(__has_feature) && __has_feature(objc_arc) + typedef __strong id CCARRAY_ID; +#else + typedef id CCARRAY_ID; +#endif typedef struct ccArray { NSUInteger num, max; - id *arr; + CCARRAY_ID *arr; } ccArray; -/** Allocates and initializes a new array with specified capacity */ -static inline ccArray* ccArrayNew(NSUInteger capacity) { - if (capacity == 0) - capacity = 1; - - ccArray *arr = (ccArray*)malloc( sizeof(ccArray) ); - arr->num = 0; - arr->arr = (id*) malloc( capacity * sizeof(id) ); - arr->max = capacity; - - return arr; -} +typedef int (*cc_comparator)(const void *, const void *); -static inline void ccArrayRemoveAllObjects(ccArray *arr); +/** Allocates and initializes a new array with specified capacity */ +ccArray* ccArrayNew(NSUInteger capacity); /** Frees array after removing all remaining objects. Silently ignores nil arr. */ -static inline void ccArrayFree(ccArray *arr) -{ - if( arr == nil ) return; - - ccArrayRemoveAllObjects(arr); - - free(arr->arr); - free(arr); -} +void ccArrayFree(ccArray *arr); /** Doubles array capacity */ -static inline void ccArrayDoubleCapacity(ccArray *arr) -{ - arr->max *= 2; - id *newArr = (id *)realloc( arr->arr, arr->max * sizeof(id) ); - // will fail when there's not enough memory - NSCAssert(newArr != NULL, @"ccArrayDoubleCapacity failed. Not enough memory"); - arr->arr = newArr; -} +void ccArrayDoubleCapacity(ccArray *arr); /** Increases array capacity such that max >= num + extra. */ -static inline void ccArrayEnsureExtraCapacity(ccArray *arr, NSUInteger extra) -{ - while (arr->max < arr->num + extra) - ccArrayDoubleCapacity(arr); -} +void ccArrayEnsureExtraCapacity(ccArray *arr, NSUInteger extra); /** shrinks the array so the memory footprint corresponds with the number of items */ -static inline void ccArrayShrink(ccArray *arr) -{ - NSUInteger newSize; - - //only resize when necessary - if (arr->max > arr->num && !(arr->num==0 && arr->max==1)) - { - if (arr->num!=0) - { - newSize=arr->num; - arr->max=arr->num; - } - else - {//minimum capacity of 1, with 0 elements the array would be free'd by realloc - newSize=1; - arr->max=1; - } - - arr->arr = (id*) realloc(arr->arr,newSize * sizeof(id) ); - NSCAssert(arr->arr!=NULL,@"could not reallocate the memory"); - } -} +void ccArrayShrink(ccArray *arr); /** Returns index of first occurence of object, NSNotFound if object not found. */ -static inline NSUInteger ccArrayGetIndexOfObject(ccArray *arr, id object) -{ - for( NSUInteger i = 0; i < arr->num; i++) - if( arr->arr[i] == object ) return i; - - return NSNotFound; -} +NSUInteger ccArrayGetIndexOfObject(ccArray *arr, id object); /** Returns a Boolean value that indicates whether object is present in array. */ -static inline BOOL ccArrayContainsObject(ccArray *arr, id object) -{ - return ccArrayGetIndexOfObject(arr, object) != NSNotFound; -} +BOOL ccArrayContainsObject(ccArray *arr, id object); /** Appends an object. Bahaviour undefined if array doesn't have enough capacity. */ -static inline void ccArrayAppendObject(ccArray *arr, id object) -{ - arr->arr[arr->num] = [object retain]; - arr->num++; -} +void ccArrayAppendObject(ccArray *arr, id object); /** Appends an object. Capacity of arr is increased if needed. */ -static inline void ccArrayAppendObjectWithResize(ccArray *arr, id object) -{ - ccArrayEnsureExtraCapacity(arr, 1); - ccArrayAppendObject(arr, object); -} +void ccArrayAppendObjectWithResize(ccArray *arr, id object); -/** Appends objects from plusArr to arr. Behaviour undefined if arr doesn't have - enough capacity. */ -static inline void ccArrayAppendArray(ccArray *arr, ccArray *plusArr) -{ - for( NSUInteger i = 0; i < plusArr->num; i++) - ccArrayAppendObject(arr, plusArr->arr[i]); -} +/** Appends objects from plusArr to arr. + Behaviour undefined if arr doesn't have enough capacity. */ +void ccArrayAppendArray(ccArray *arr, ccArray *plusArr); /** Appends objects from plusArr to arr. Capacity of arr is increased if needed. */ -static inline void ccArrayAppendArrayWithResize(ccArray *arr, ccArray *plusArr) -{ - ccArrayEnsureExtraCapacity(arr, plusArr->num); - ccArrayAppendArray(arr, plusArr); -} +void ccArrayAppendArrayWithResize(ccArray *arr, ccArray *plusArr); /** Inserts an object at index */ -static inline void ccArrayInsertObjectAtIndex(ccArray *arr, id object, NSUInteger index) -{ - NSCAssert(index<=arr->num, @"Invalid index. Out of bounds"); - - ccArrayEnsureExtraCapacity(arr, 1); - - NSUInteger remaining = arr->num - index; - if( remaining > 0) - memmove(&arr->arr[index+1], &arr->arr[index], sizeof(id) * remaining ); - - arr->arr[index] = [object retain]; - arr->num++; -} +void ccArrayInsertObjectAtIndex(ccArray *arr, id object, NSUInteger index); /** Swaps two objects */ -static inline void ccArraySwapObjectsAtIndexes(ccArray *arr, NSUInteger index1, NSUInteger index2) -{ - NSCAssert(index1 < arr->num, @"(1) Invalid index. Out of bounds"); - NSCAssert(index2 < arr->num, @"(2) Invalid index. Out of bounds"); - - id object1 = arr->arr[index1]; - - arr->arr[index1] = arr->arr[index2]; - arr->arr[index2] = object1; -} +void ccArraySwapObjectsAtIndexes(ccArray *arr, NSUInteger index1, NSUInteger index2); /** Removes all objects from arr */ -static inline void ccArrayRemoveAllObjects(ccArray *arr) -{ - while( arr->num > 0 ) - [arr->arr[--arr->num] release]; -} +void ccArrayRemoveAllObjects(ccArray *arr); /** Removes object at specified index and pushes back all subsequent objects. Behaviour undefined if index outside [0, num-1]. */ -static inline void ccArrayRemoveObjectAtIndex(ccArray *arr, NSUInteger index) -{ - [arr->arr[index] release]; - arr->num--; - - NSUInteger remaining = arr->num - index; - if(remaining>0) - memmove(&arr->arr[index], &arr->arr[index+1], remaining * sizeof(id)); -} +void ccArrayRemoveObjectAtIndex(ccArray *arr, NSUInteger index); /** Removes object at specified index and fills the gap with the last object, thereby avoiding the need to push back subsequent objects. Behaviour undefined if index outside [0, num-1]. */ -static inline void ccArrayFastRemoveObjectAtIndex(ccArray *arr, NSUInteger index) -{ - [arr->arr[index] release]; - NSUInteger last = --arr->num; - arr->arr[index] = arr->arr[last]; -} +void ccArrayFastRemoveObjectAtIndex(ccArray *arr, NSUInteger index); -static inline void ccArrayFastRemoveObject(ccArray *arr, id object) -{ - NSUInteger index = ccArrayGetIndexOfObject(arr, object); - if (index != NSNotFound) - ccArrayFastRemoveObjectAtIndex(arr, index); -} +void ccArrayFastRemoveObject(ccArray *arr, id object); /** Searches for the first occurance of object and removes it. If object is not found the function has no effect. */ -static inline void ccArrayRemoveObject(ccArray *arr, id object) -{ - NSUInteger index = ccArrayGetIndexOfObject(arr, object); - if (index != NSNotFound) - ccArrayRemoveObjectAtIndex(arr, index); -} +void ccArrayRemoveObject(ccArray *arr, id object); /** Removes from arr all objects in minusArr. For each object in minusArr, the first matching instance in arr will be removed. */ -static inline void ccArrayRemoveArray(ccArray *arr, ccArray *minusArr) -{ - for( NSUInteger i = 0; i < minusArr->num; i++) - ccArrayRemoveObject(arr, minusArr->arr[i]); -} +void ccArrayRemoveArray(ccArray *arr, ccArray *minusArr); /** Removes from arr all objects in minusArr. For each object in minusArr, all matching instances in arr will be removed. */ -static inline void ccArrayFullRemoveArray(ccArray *arr, ccArray *minusArr) -{ - NSUInteger back = 0; - - for( NSUInteger i = 0; i < arr->num; i++) { - if( ccArrayContainsObject(minusArr, arr->arr[i]) ) { - [arr->arr[i] release]; - back++; - } else - arr->arr[i - back] = arr->arr[i]; - } - - arr->num -= back; -} +void ccArrayFullRemoveArray(ccArray *arr, ccArray *minusArr); /** Sends to each object in arr the message identified by given selector. */ -static inline void ccArrayMakeObjectsPerformSelector(ccArray *arr, SEL sel) -{ - for( NSUInteger i = 0; i < arr->num; i++) - [arr->arr[i] performSelector:sel]; -} +void ccArrayMakeObjectsPerformSelector(ccArray *arr, SEL sel); -static inline void ccArrayMakeObjectsPerformSelectorWithObject(ccArray *arr, SEL sel, id object) -{ - for( NSUInteger i = 0; i < arr->num; i++) - [arr->arr[i] performSelector:sel withObject:object]; -} +void ccArrayMakeObjectsPerformSelectorWithObject(ccArray *arr, SEL sel, id object); + +void ccArrayMakeObjectPerformSelectorWithArrayObjects(ccArray *arr, SEL sel, id object); #pragma mark - @@ -284,164 +148,82 @@ static inline void ccArrayMakeObjectsPerformSelectorWithObject(ccArray *arr, SEL typedef ccArray ccCArray; -static inline void ccCArrayRemoveAllValues(ccCArray *arr); - /** Allocates and initializes a new C array with specified capacity */ -static inline ccCArray* ccCArrayNew(NSUInteger capacity) { - if (capacity == 0) - capacity = 1; - - ccCArray *arr = (ccCArray*)malloc( sizeof(ccCArray) ); - arr->num = 0; - arr->arr = (id*) malloc( capacity * sizeof(id) ); - arr->max = capacity; - - return arr; -} +ccCArray* ccCArrayNew(NSUInteger capacity); /** Frees C array after removing all remaining values. Silently ignores nil arr. */ -static inline void ccCArrayFree(ccCArray *arr) -{ - if( arr == nil ) return; - - ccCArrayRemoveAllValues(arr); - - free(arr->arr); - free(arr); -} +void ccCArrayFree(ccCArray *arr); /** Doubles C array capacity */ -static inline void ccCArrayDoubleCapacity(ccCArray *arr) -{ - ccArrayDoubleCapacity(arr); -} +void ccCArrayDoubleCapacity(ccCArray *arr); /** Increases array capacity such that max >= num + extra. */ -static inline void ccCArrayEnsureExtraCapacity(ccCArray *arr, NSUInteger extra) -{ - ccArrayEnsureExtraCapacity(arr,extra); -} +void ccCArrayEnsureExtraCapacity(ccCArray *arr, NSUInteger extra); /** Returns index of first occurence of value, NSNotFound if value not found. */ -static inline NSUInteger ccCArrayGetIndexOfValue(ccCArray *arr, void* value) -{ - for( NSUInteger i = 0; i < arr->num; i++) - if( arr->arr[i] == value ) return i; - return NSNotFound; -} +NSUInteger ccCArrayGetIndexOfValue(ccCArray *arr, CCARRAY_ID value); /** Returns a Boolean value that indicates whether value is present in the C array. */ -static inline BOOL ccCArrayContainsValue(ccCArray *arr, void* value) -{ - return ccCArrayGetIndexOfValue(arr, value) != NSNotFound; -} +BOOL ccCArrayContainsValue(ccCArray *arr, CCARRAY_ID value); /** Inserts a value at a certain position. Behaviour undefined if aray doesn't have enough capacity */ -static inline void ccCArrayInsertValueAtIndex( ccCArray *arr, void *value, NSUInteger index) -{ - NSCAssert( index < arr->max, @"ccCArrayInsertValueAtIndex: invalid index"); - - NSUInteger remaining = arr->num - index; - - // last Value doesn't need to be moved - if( remaining > 0) { - // tex coordinates - memmove( &arr->arr[index+1],&arr->arr[index], sizeof(void*) * remaining ); - } - - arr->num++; - arr->arr[index] = (id) value; -} +void ccCArrayInsertValueAtIndex( ccCArray *arr, CCARRAY_ID value, NSUInteger index); /** Appends an value. Bahaviour undefined if array doesn't have enough capacity. */ -static inline void ccCArrayAppendValue(ccCArray *arr, void* value) -{ - arr->arr[arr->num] = (id) value; - arr->num++; -} +void ccCArrayAppendValue(ccCArray *arr, CCARRAY_ID value); /** Appends an value. Capacity of arr is increased if needed. */ -static inline void ccCArrayAppendValueWithResize(ccCArray *arr, void* value) -{ - ccCArrayEnsureExtraCapacity(arr, 1); - ccCArrayAppendValue(arr, value); -} +void ccCArrayAppendValueWithResize(ccCArray *arr, CCARRAY_ID value); /** Appends values from plusArr to arr. Behaviour undefined if arr doesn't have enough capacity. */ -static inline void ccCArrayAppendArray(ccCArray *arr, ccCArray *plusArr) -{ - for( NSUInteger i = 0; i < plusArr->num; i++) - ccCArrayAppendValue(arr, plusArr->arr[i]); -} +void ccCArrayAppendArray(ccCArray *arr, ccCArray *plusArr); /** Appends values from plusArr to arr. Capacity of arr is increased if needed. */ -static inline void ccCArrayAppendArrayWithResize(ccCArray *arr, ccCArray *plusArr) -{ - ccCArrayEnsureExtraCapacity(arr, plusArr->num); - ccCArrayAppendArray(arr, plusArr); -} +void ccCArrayAppendArrayWithResize(ccCArray *arr, ccCArray *plusArr); /** Removes all values from arr */ -static inline void ccCArrayRemoveAllValues(ccCArray *arr) -{ - arr->num = 0; -} +void ccCArrayRemoveAllValues(ccCArray *arr); /** Removes value at specified index and pushes back all subsequent values. Behaviour undefined if index outside [0, num-1]. @since v0.99.4 */ -static inline void ccCArrayRemoveValueAtIndex(ccCArray *arr, NSUInteger index) -{ - for( NSUInteger last = --arr->num; index < last; index++) - arr->arr[index] = arr->arr[index + 1]; -} +void ccCArrayRemoveValueAtIndex(ccCArray *arr, NSUInteger index); /** Removes value at specified index and fills the gap with the last value, thereby avoiding the need to push back subsequent values. Behaviour undefined if index outside [0, num-1]. @since v0.99.4 */ -static inline void ccCArrayFastRemoveValueAtIndex(ccCArray *arr, NSUInteger index) -{ - NSUInteger last = --arr->num; - arr->arr[index] = arr->arr[last]; -} +void ccCArrayFastRemoveValueAtIndex(ccCArray *arr, NSUInteger index); /** Searches for the first occurance of value and removes it. If value is not found the function has no effect. @since v0.99.4 */ -static inline void ccCArrayRemoveValue(ccCArray *arr, void* value) -{ - NSUInteger index = ccCArrayGetIndexOfValue(arr, value); - if (index != NSNotFound) - ccCArrayRemoveValueAtIndex(arr, index); -} +void ccCArrayRemoveValue(ccCArray *arr, CCARRAY_ID value); /** Removes from arr all values in minusArr. For each Value in minusArr, the first matching instance in arr will be removed. @since v0.99.4 */ -static inline void ccCArrayRemoveArray(ccCArray *arr, ccCArray *minusArr) -{ - for( NSUInteger i = 0; i < minusArr->num; i++) - ccCArrayRemoveValue(arr, minusArr->arr[i]); -} +void ccCArrayRemoveArray(ccCArray *arr, ccCArray *minusArr); /** Removes from arr all values in minusArr. For each value in minusArr, all matching instances in arr will be removed. @since v0.99.4 */ -static inline void ccCArrayFullRemoveArray(ccCArray *arr, ccCArray *minusArr) -{ - NSUInteger back = 0; - - for( NSUInteger i = 0; i < arr->num; i++) { - if( ccCArrayContainsValue(minusArr, arr->arr[i]) ) { - back++; - } else - arr->arr[i - back] = arr->arr[i]; - } - - arr->num -= back; +void ccCArrayFullRemoveArray(ccCArray *arr, ccCArray *minusArr); + +// iterative mergesort arrd on +// http://www.inf.fh-flensburg.de/lang/algorithmen/sortieren/merge/mergiter.htm +int cc_mergesortL(ccCArray* array, size_t width, cc_comparator comparator); + +void cc_insertionSort(ccCArray* arr, cc_comparator comparator); + +void cc_pointerswap(void* a, void* b, size_t width); + +#ifdef __cplusplus } +#endif + + #endif // CC_ARRAY_H diff --git a/cocos2d/cocos2d/Support/ccCArray.m b/cocos2d/cocos2d/Support/ccCArray.m new file mode 100755 index 0000000..311031b --- /dev/null +++ b/cocos2d/cocos2d/Support/ccCArray.m @@ -0,0 +1,542 @@ +/* Copyright (c) 2007 Scott Lembcke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "CCArray.h" + +/** Allocates and initializes a new array with specified capacity */ +ccArray* ccArrayNew(NSUInteger capacity) { + if (capacity == 0) + capacity = 1; + + ccArray *arr = (ccArray*)malloc( sizeof(ccArray) ); + arr->num = 0; + arr->arr = (CCARRAY_ID *)calloc(capacity, sizeof(id)); + arr->max = capacity; + + return arr; +} + +/** Frees array after removing all remaining objects. Silently ignores nil arr. */ +void ccArrayFree(ccArray *arr) +{ + if( arr == nil ) return; + + ccArrayRemoveAllObjects(arr); + + free(arr->arr); + free(arr); +} + +void ccArrayDoubleCapacity(ccArray *arr) +{ + arr->max *= 2; + CCARRAY_ID *newArr = (CCARRAY_ID *)realloc( arr->arr, arr->max * sizeof(id) ); + // will fail when there's not enough memory + NSCAssert(newArr != NULL, @"ccArrayDoubleCapacity failed. Not enough memory"); + arr->arr = newArr; +} + +void ccArrayEnsureExtraCapacity(ccArray *arr, NSUInteger extra) +{ + while (arr->max < arr->num + extra) + ccArrayDoubleCapacity(arr); +} + +void ccArrayShrink(ccArray *arr) +{ + NSUInteger newSize; + + //only resize when necessary + if (arr->max > arr->num && !(arr->num==0 && arr->max==1)) + { + if (arr->num!=0) + { + newSize=arr->num; + arr->max=arr->num; + } + else + {//minimum capacity of 1, with 0 elements the array would be free'd by realloc + newSize=1; + arr->max=1; + } + + arr->arr = (CCARRAY_ID *) realloc(arr->arr,newSize * sizeof(id) ); + NSCAssert(arr->arr!=NULL,@"could not reallocate the memory"); + } +} + +/** Returns index of first occurence of object, NSNotFound if object not found. */ +NSUInteger ccArrayGetIndexOfObject(ccArray *arr, id object) +{ + NSUInteger i; + + for( i = 0; i < arr->num; i++) + if( arr->arr[i] == object ) return i; + + return NSNotFound; +} + +/** Returns a Boolean value that indicates whether object is present in array. */ +BOOL ccArrayContainsObject(ccArray *arr, id object) +{ + return ccArrayGetIndexOfObject(arr, object) != NSNotFound; +} + +/** Appends an object. Bahaviour undefined if array doesn't have enough capacity. */ +void ccArrayAppendObject(ccArray *arr, id object) +{ + arr->arr[arr->num] = CC_ARC_RETAIN(object); + arr->num++; +} + +/** Appends an object. Capacity of arr is increased if needed. */ +void ccArrayAppendObjectWithResize(ccArray *arr, id object) +{ + ccArrayEnsureExtraCapacity(arr, 1); + ccArrayAppendObject(arr, object); +} + +/** Appends objects from plusArr to arr. Behaviour undefined if arr doesn't have + enough capacity. */ +void ccArrayAppendArray(ccArray *arr, ccArray *plusArr) +{ + NSUInteger i; + + for( i = 0; i < plusArr->num; i++) + ccArrayAppendObject(arr, plusArr->arr[i]); +} + +/** Appends objects from plusArr to arr. Capacity of arr is increased if needed. */ +void ccArrayAppendArrayWithResize(ccArray *arr, ccArray *plusArr) +{ + ccArrayEnsureExtraCapacity(arr, plusArr->num); + ccArrayAppendArray(arr, plusArr); +} + +/** Inserts an object at index */ +void ccArrayInsertObjectAtIndex(ccArray *arr, id object, NSUInteger index) +{ + NSCAssert(index<=arr->num, @"Invalid index. Out of bounds"); + + ccArrayEnsureExtraCapacity(arr, 1); + + NSUInteger remaining = arr->num - index; + if( remaining > 0) + memmove((void *)&arr->arr[index+1], (void *)&arr->arr[index], sizeof(id) * remaining ); + + arr->arr[index] = CC_ARC_RETAIN(object); + arr->num++; +} + +/** Swaps two objects */ +void ccArraySwapObjectsAtIndexes(ccArray *arr, NSUInteger index1, NSUInteger index2) +{ + NSCAssert(index1 < arr->num, @"(1) Invalid index. Out of bounds"); + NSCAssert(index2 < arr->num, @"(2) Invalid index. Out of bounds"); + + id object1 = arr->arr[index1]; + + arr->arr[index1] = arr->arr[index2]; + arr->arr[index2] = object1; +} + +/** Removes all objects from arr */ +void ccArrayRemoveAllObjects(ccArray *arr) +{ + while( arr->num > 0 ) + CC_ARC_RELEASE(arr->arr[--arr->num]); +} + +/** Removes object at specified index and pushes back all subsequent objects. + Behaviour undefined if index outside [0, num-1]. */ +void ccArrayRemoveObjectAtIndex(ccArray *arr, NSUInteger index) +{ + CC_ARC_RELEASE(arr->arr[index]); + arr->num--; + + NSUInteger remaining = arr->num - index; + if(remaining>0) + memmove((void *)&arr->arr[index], (void *)&arr->arr[index+1], remaining * sizeof(id)); +} + +/** Removes object at specified index and fills the gap with the last object, + thereby avoiding the need to push back subsequent objects. + Behaviour undefined if index outside [0, num-1]. */ +void ccArrayFastRemoveObjectAtIndex(ccArray *arr, NSUInteger index) +{ + CC_ARC_RELEASE(arr->arr[index]); + NSUInteger last = --arr->num; + arr->arr[index] = arr->arr[last]; +} + +void ccArrayFastRemoveObject(ccArray *arr, id object) +{ + NSUInteger index = ccArrayGetIndexOfObject(arr, object); + if (index != NSNotFound) + ccArrayFastRemoveObjectAtIndex(arr, index); +} + +/** Searches for the first occurance of object and removes it. If object is not + found the function has no effect. */ +void ccArrayRemoveObject(ccArray *arr, id object) +{ + NSUInteger index = ccArrayGetIndexOfObject(arr, object); + if (index != NSNotFound) + ccArrayRemoveObjectAtIndex(arr, index); +} + +/** Removes from arr all objects in minusArr. For each object in minusArr, the + first matching instance in arr will be removed. */ +void ccArrayRemoveArray(ccArray *arr, ccArray *minusArr) +{ + NSUInteger i; + + for( i = 0; i < minusArr->num; i++) + ccArrayRemoveObject(arr, minusArr->arr[i]); +} + +/** Removes from arr all objects in minusArr. For each object in minusArr, all + matching instances in arr will be removed. */ +void ccArrayFullRemoveArray(ccArray *arr, ccArray *minusArr) +{ + NSUInteger back = 0; + NSUInteger i; + + for( i = 0; i < arr->num; i++) { + if( ccArrayContainsObject(minusArr, arr->arr[i]) ) { + CC_ARC_RELEASE(arr->arr[i]); + back++; + } else + arr->arr[i - back] = arr->arr[i]; + } + + arr->num -= back; +} + +/** Sends to each object in arr the message identified by given selector. */ +void ccArrayMakeObjectsPerformSelector(ccArray *arr, SEL sel) +{ + NSUInteger i; + + for( i = 0; i < arr->num; i++) +#pragma clang diagnostic push +#if defined(__has_feature) && __has_feature(objc_arc) +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + [arr->arr[i] performSelector:sel]; +#pragma clang diagnostic pop +} + +void ccArrayMakeObjectsPerformSelectorWithObject(ccArray *arr, SEL sel, id object) +{ + NSUInteger i; + + for( i = 0; i < arr->num; i++) +#pragma clang diagnostic push + +#if defined(__has_feature) && __has_feature(objc_arc) +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + [arr->arr[i] performSelector:sel withObject:object]; +#pragma clang diagnostic pop +} + +void ccArrayMakeObjectPerformSelectorWithArrayObjects(ccArray *arr, SEL sel, id object) +{ + NSUInteger i; + + for( i = 0; i < arr->num; i++) +#pragma clang diagnostic push + +#if defined(__has_feature) && __has_feature(objc_arc) +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + [object performSelector:sel withObject:arr->arr[i]]; +#pragma clang diagnostic pop +} + + +#pragma mark - +#pragma mark ccCArray for Values (c structures) + +/** Allocates and initializes a new C array with specified capacity */ +ccCArray* ccCArrayNew(NSUInteger capacity) +{ + if (capacity == 0) + capacity = 1; + + ccCArray *arr = (ccCArray*)malloc( sizeof(ccCArray) ); + arr->num = 0; + arr->arr = (CCARRAY_ID *) malloc( capacity * sizeof(id) ); + arr->max = capacity; + + return arr; +} + +/** Frees C array after removing all remaining values. Silently ignores nil arr. */ +void ccCArrayFree(ccCArray *arr) +{ + if( arr == nil ) return; + + ccCArrayRemoveAllValues(arr); + + free(arr->arr); + free(arr); +} + +/** Doubles C array capacity */ +void ccCArrayDoubleCapacity(ccCArray *arr) +{ + ccArrayDoubleCapacity(arr); +} + +/** Increases array capacity such that max >= num + extra. */ +void ccCArrayEnsureExtraCapacity(ccCArray *arr, NSUInteger extra) +{ + ccArrayEnsureExtraCapacity(arr,extra); +} + +/** Returns index of first occurence of value, NSNotFound if value not found. */ +NSUInteger ccCArrayGetIndexOfValue(ccCArray *arr, CCARRAY_ID value) +{ + NSUInteger i; + + for( i = 0; i < arr->num; i++) + if( [arr->arr[i] isEqual:value] ) return i; + return NSNotFound; +} + +/** Returns a Boolean value that indicates whether value is present in the C array. */ +BOOL ccCArrayContainsValue(ccCArray *arr, CCARRAY_ID value) +{ + return ccCArrayGetIndexOfValue(arr, value) != NSNotFound; +} + +/** Inserts a value at a certain position. Behaviour undefined if aray doesn't have enough capacity */ +void ccCArrayInsertValueAtIndex( ccCArray *arr, CCARRAY_ID value, NSUInteger index) +{ + NSCAssert( index < arr->max, @"ccCArrayInsertValueAtIndex: invalid index"); + + NSUInteger remaining = arr->num - index; + + // last Value doesn't need to be moved + if( remaining > 0) { + // tex coordinates + memmove((void *)&arr->arr[index+1], (void *)&arr->arr[index], sizeof(void*) * remaining ); + } + + arr->num++; + arr->arr[index] = value; +} + +/** Appends an value. Bahaviour undefined if array doesn't have enough capacity. */ +void ccCArrayAppendValue(ccCArray *arr, CCARRAY_ID value) +{ + arr->arr[arr->num] = (id) value; + arr->num++; +} + +/** Appends an value. Capacity of arr is increased if needed. */ +void ccCArrayAppendValueWithResize(ccCArray *arr, CCARRAY_ID value) +{ + ccCArrayEnsureExtraCapacity(arr, 1); + ccCArrayAppendValue(arr, value); +} + + +/** Appends values from plusArr to arr. Behaviour undefined if arr doesn't have + enough capacity. */ +void ccCArrayAppendArray(ccCArray *arr, ccCArray *plusArr) +{ + NSUInteger i; + + for( i = 0; i < plusArr->num; i++) + ccCArrayAppendValue(arr, plusArr->arr[i]); +} + +/** Appends values from plusArr to arr. Capacity of arr is increased if needed. */ +void ccCArrayAppendArrayWithResize(ccCArray *arr, ccCArray *plusArr) +{ + ccCArrayEnsureExtraCapacity(arr, plusArr->num); + ccCArrayAppendArray(arr, plusArr); +} + +/** Removes all values from arr */ +void ccCArrayRemoveAllValues(ccCArray *arr) +{ + arr->num = 0; +} + +/** Removes value at specified index and pushes back all subsequent values. + Behaviour undefined if index outside [0, num-1]. + @since v0.99.4 + */ +void ccCArrayRemoveValueAtIndex(ccCArray *arr, NSUInteger index) +{ + NSUInteger last; + + for( last = --arr->num; index < last; index++) + arr->arr[index] = arr->arr[index + 1]; +} + +/** Removes value at specified index and fills the gap with the last value, + thereby avoiding the need to push back subsequent values. + Behaviour undefined if index outside [0, num-1]. + @since v0.99.4 + */ +void ccCArrayFastRemoveValueAtIndex(ccCArray *arr, NSUInteger index) +{ + NSUInteger last = --arr->num; + arr->arr[index] = arr->arr[last]; +} + +/** Searches for the first occurance of value and removes it. If value is not found the function has no effect. + @since v0.99.4 + */ +void ccCArrayRemoveValue(ccCArray *arr, CCARRAY_ID value) +{ + NSUInteger index = ccCArrayGetIndexOfValue(arr, value); + if (index != NSNotFound) + ccCArrayRemoveValueAtIndex(arr, index); +} + +/** Removes from arr all values in minusArr. For each Value in minusArr, the first matching instance in arr will be removed. + @since v0.99.4 + */ +void ccCArrayRemoveArray(ccCArray *arr, ccCArray *minusArr) +{ + NSUInteger i; + + for( i = 0; i < minusArr->num; i++) + ccCArrayRemoveValue(arr, minusArr->arr[i]); +} + +/** Removes from arr all values in minusArr. For each value in minusArr, all matching instances in arr will be removed. + @since v0.99.4 + */ +void ccCArrayFullRemoveArray(ccCArray *arr, ccCArray *minusArr) +{ + NSUInteger i; + NSUInteger back = 0; + + for( i = 0; i < arr->num; i++) { + if( ccCArrayContainsValue(minusArr, arr->arr[i]) ) { + back++; + } else + arr->arr[i - back] = arr->arr[i]; + } + + arr->num -= back; +} + +//used by mergesortL +void cc_pointerswap(void* a, void* b, size_t width) +{ + void* tmp; + tmp = *(void**)a; + *(void**)a = *(void**)b; + *(void**)b = tmp; +} + +// iterative mergesort arrd on +// http://www.inf.fh-flensburg.de/lang/algorithmen/sortieren/merge/mergiter.htm +int cc_mergesortL(ccCArray* array, size_t width, cc_comparator comparator) +{ + CCARRAY_ID *arr = array->arr; + NSInteger i,j,k,s,m,n= array->num; + + CCARRAY_ID *B = (CCARRAY_ID*) malloc((n/2 + 1) * width); + for (s = 1; s < n; s += s) + { + for (m = n-1-s; m >= 0; m -= s+s) + { + NSInteger lo = MAX(m-(s+1),0); + NSInteger hi = m+s; + + j = lo; + + if (m-j > 0) + { + //triggers a warning when compiled with ARC, B needs to be strong typed, for compiling for obj-c++ + //memcpy aritmetics aren't allowed on void* types + //explicitely casting didn't work +#pragma clang diagnostic push +#if defined(__has_feature) && __has_feature(objc_arc) +#pragma clang diagnostic ignored "-Warc-non-pod-memaccess" +#endif + + memcpy(B, &arr[j], (m-j) * width); +#pragma clang diagnostic pop + } + + i = 0; + j = m; + k = lo; + + while (knum; + + CCARRAY_ID *x = arr->arr; + id temp; + + // insertion sort + for(i=1; i0 && ( comparator( &x[j-1], &x[j] ) == NSOrderedDescending) ) + { + temp = x[j]; + x[j] = x[j-1]; + x[j-1] = temp; + j--; + } + } +} diff --git a/cocos2d/cocos2d/Support/ccUtils.c b/cocos2d/cocos2d/Support/ccUtils.c old mode 100644 new mode 100755 index 39786ec..27a122d --- a/cocos2d/cocos2d/Support/ccUtils.c +++ b/cocos2d/cocos2d/Support/ccUtils.c @@ -17,4 +17,4 @@ unsigned long ccNextPOT(unsigned long x) x = x | (x >> 8); x = x | (x >>16); return x + 1; -} \ No newline at end of file +} diff --git a/cocos2d/cocos2d/Support/ccUtils.h b/cocos2d/cocos2d/Support/ccUtils.h old mode 100644 new mode 100755 index 783fc54..5f84ff3 --- a/cocos2d/cocos2d/Support/ccUtils.h +++ b/cocos2d/cocos2d/Support/ccUtils.h @@ -6,6 +6,10 @@ #ifndef __CC_UTILS_H #define __CC_UTILS_H +#ifdef __cplusplus +extern "C" { +#endif + /** @file ccUtils.h Misc free functions */ @@ -15,15 +19,18 @@ */ /** returns the Next Power of Two value. - + Examples: - If "value" is 15, it will return 16. - If "value" is 16, it will return 16. - If "value" is 17, it will return 32. - + @since v0.99.5 */ - unsigned long ccNextPOT( unsigned long value ); +#ifdef __cplusplus +} +#endif + #endif // ! __CC_UTILS_H diff --git a/cocos2d/cocos2d/Support/uthash.h b/cocos2d/cocos2d/Support/uthash.h old mode 100644 new mode 100755 index a4bdc18..e190ef7 --- a/cocos2d/cocos2d/Support/uthash.h +++ b/cocos2d/cocos2d/Support/uthash.h @@ -22,7 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef UTHASH_H -#define UTHASH_H +#define UTHASH_H #include /* memcmp,strlen */ #include /* ptrdiff_t */ @@ -48,7 +48,7 @@ do { char **_da_dst = (char**)(&(dst)); \ *_da_dst = (char*)(src); \ } while(0) -#else +#else #define DECLTYPE_ASSIGN(dst,src) \ do { \ (dst) = DECLTYPE(dst)(src); \ @@ -119,9 +119,9 @@ do { HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) #else -#define HASH_BLOOM_MAKE(tbl) -#define HASH_BLOOM_FREE(tbl) -#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) #define HASH_BLOOM_TEST(tbl,hashv) (1) #endif @@ -146,7 +146,7 @@ do { #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add) - + #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ do { \ unsigned _ha_bkt; \ @@ -298,10 +298,10 @@ do { } \ } while (0) #else -#define HASH_FSCK(hh,head) +#define HASH_FSCK(hh,head) #endif -/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to * the descriptor to which this macro is defined for tuning the hash function. * The app can #include to get the prototype for write(2). */ #ifdef HASH_EMIT_KEYS @@ -311,12 +311,12 @@ do { write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ write(HASH_EMIT_KEYS, keyptr, fieldlen); \ } while (0) -#else -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) #endif /* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ -#ifdef HASH_FUNCTION +#ifdef HASH_FUNCTION #define HASH_FCN HASH_FUNCTION #else #define HASH_FCN HASH_JEN @@ -333,7 +333,7 @@ do { } while (0) -/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ #define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ do { \ @@ -354,7 +354,7 @@ do { hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ bkt = hashv & (num_bkts-1); \ } while(0); - + #define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ do { \ unsigned _ho_i; \ @@ -484,14 +484,14 @@ do { /* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads. * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. * So MurmurHash comes in two versions, the faster unaligned one and the slower - * aligned one. We only use the faster one on CPU's where we know it's safe. + * aligned one. We only use the faster one on CPU's where we know it's safe. * * Note the preprocessor built-in defines can be emitted using: * * gcc -m64 -dM -E - < /dev/null (on gcc) * cc -## a.c (where a.c is a simple test file) (Sun Studio) */ -#if (defined(__i386__) || defined(__x86_64__)) +#if (defined(__i386__) || defined(__x86_64__)) #define HASH_MUR HASH_MUR_UNALIGNED #else #define HASH_MUR HASH_MUR_ALIGNED @@ -630,7 +630,7 @@ do { #endif /* HASH_USING_NO_STRICT_ALIASING */ /* key comparison function; return 0 if keys equal */ -#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) /* iterate over items in a known bucket to find desired item */ #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ @@ -671,36 +671,36 @@ do { } \ if (hh_del->hh_next) { \ hh_del->hh_next->hh_prev = hh_del->hh_prev; \ - } + } /* Bucket expansion has the effect of doubling the number of buckets * and redistributing the items into the new buckets. Ideally the * items will distribute more or less evenly into the new buckets * (the extent to which this is true is a measure of the quality of - * the hash function as it applies to the key domain). - * + * the hash function as it applies to the key domain). + * * With the items distributed into more buckets, the chain length * (item count) in each bucket is reduced. Thus by expanding buckets - * the hash keeps a bound on the chain length. This bounded chain + * the hash keeps a bound on the chain length. This bounded chain * length is the essence of how a hash provides constant time lookup. - * + * * The calculation of tbl->ideal_chain_maxlen below deserves some * explanation. First, keep in mind that we're calculating the ideal * maximum chain length based on the *new* (doubled) bucket count. * In fractions this is just n/b (n=number of items,b=new num buckets). - * Since the ideal chain length is an integer, we want to calculate + * Since the ideal chain length is an integer, we want to calculate * ceil(n/b). We don't depend on floating point arithmetic in this * hash, so to calculate ceil(n/b) with integers we could write - * + * * ceil(n/b) = (n/b) + ((n%b)?1:0) - * + * * and in fact a previous version of this hash did just that. * But now we have improved things a bit by recognizing that b is * always a power of two. We keep its base 2 log handy (call it lb), * so now we can write this with a bit shift and logical AND: - * + * * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) - * + * */ #define HASH_EXPAND_BUCKETS(tbl) \ do { \ @@ -752,7 +752,7 @@ do { /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ -/* Note that HASH_SORT assumes the hash handle name to be hh. +/* Note that HASH_SORT assumes the hash handle name to be hh. * HASH_SRT was added to allow the hash handle name to be passed in. */ #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) #define HASH_SRT(hh,head,cmpfcn) \ @@ -834,10 +834,10 @@ do { } \ } while (0) -/* This function selects items from one hash into another hash. - * The end result is that the selected items have dual presence - * in both hashes. There is no copy of the items made; rather - * they are added into the new hash through a secondary hash +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash * hash handle that must be present in the structure. */ #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ do { \ @@ -890,7 +890,7 @@ do { #ifdef NO_DECLTYPE #define HASH_ITER(hh,head,el,tmp) \ for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ - el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) #else #define HASH_ITER(hh,head,el,tmp) \ for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ @@ -898,7 +898,7 @@ for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); #endif /* obtain a count of items in the hash */ -#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_COUNT(head) HASH_CNT(hh,head) #define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) typedef struct UT_hash_bucket { @@ -907,7 +907,7 @@ typedef struct UT_hash_bucket { /* expand_mult is normally set to 0. In this situation, the max chain length * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If - * the bucket's chain exceeds this length, bucket expansion is triggered). + * the bucket's chain exceeds this length, bucket expansion is triggered). * However, setting expand_mult to a non-zero value delays bucket expansion * (that would be triggered by additions to this particular bucket) * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. @@ -915,7 +915,7 @@ typedef struct UT_hash_bucket { * multiplier is to reduce bucket expansions, since they are expensive, in * situations where we know that a particular bucket tends to be overused. * It is better to let its chain length grow to a longer yet-still-bounded - * value, than to do an O(n) bucket expansion too often. + * value, than to do an O(n) bucket expansion too often. */ unsigned expand_mult; @@ -941,7 +941,7 @@ typedef struct UT_hash_table { * hash distribution; reaching them in a chain traversal takes >ideal steps */ unsigned nonideal_items; - /* ineffective expands occur when a bucket doubling was performed, but + /* ineffective expands occur when a bucket doubling was performed, but * afterward, more than half the items in the hash had nonideal chain * positions. If this happens on two consecutive expansions we inhibit any * further expansion, as it's not helping; this happens when the hash diff --git a/cocos2d/cocos2d/Support/utlist.h b/cocos2d/cocos2d/Support/utlist.h old mode 100644 new mode 100755 index 34c725b..b160f1a --- a/cocos2d/cocos2d/Support/utlist.h +++ b/cocos2d/cocos2d/Support/utlist.h @@ -26,7 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define UTLIST_VERSION 1.9.1 -/* +/* * This file contains macros to manipulate singly and doubly-linked lists. * * 1. LL_ macros: singly-linked lists. @@ -36,7 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * To use singly-linked lists, your structure must have a "next" pointer. * To use doubly-linked lists, your structure must "prev" and "next" pointers. * Either way, the pointer to the head of the list must be initialized to NULL. - * + * * ----------------.EXAMPLE ------------------------- * struct item { * int id; @@ -83,7 +83,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } #define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } #define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } -#else +#else #define _SV(elt,list) #define _NEXT(elt,list) ((elt)->next) #define _NEXTASGN(elt,list,to) ((elt)->next)=(to) @@ -369,14 +369,14 @@ do { LL_FOREACH(head,out) { \ if ((out)->field == (val)) break; \ } \ -} while(0) +} while(0) #define LL_SEARCH(head,out,elt,cmp) \ do { \ LL_FOREACH(head,out) { \ if ((cmp(out,elt))==0) break; \ } \ -} while(0) +} while(0) /****************************************************************************** * doubly linked list macros (non-circular) * @@ -465,7 +465,7 @@ do { } while (0); #define CDL_FOREACH(head,el) \ - for(el=head;el;el=(el->next==head ? 0L : el->next)) + for(el=head;el;el=(el->next==head ? 0L : el->next)) #define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ @@ -477,14 +477,14 @@ do { CDL_FOREACH(head,out) { \ if ((out)->field == (val)) break; \ } \ -} while(0) +} while(0) #define CDL_SEARCH(head,out,elt,cmp) \ do { \ CDL_FOREACH(head,out) { \ if ((cmp(out,elt))==0) break; \ } \ -} while(0) +} while(0) #endif /* UTLIST_H */ diff --git a/cocos2d/cocos2d/ccConfig.h b/cocos2d/cocos2d/ccConfig.h old mode 100644 new mode 100755 index 55b4cd8..5eb6683 --- a/cocos2d/cocos2d/ccConfig.h +++ b/cocos2d/cocos2d/ccConfig.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,176 +23,148 @@ * THE SOFTWARE. */ - -#import - /** @file cocos2d (cc) configuration file */ +/** @def CC_ENABLE_GL_STATE_CACHE + If enabled, cocos2d will maintain an OpenGL state cache internally to avoid unnecessary switches. + In order to use them, you have to use the following functions, insead of the the GL ones: + - ccGLUseProgram() instead of glUseProgram() + - ccGLDeleteProgram() instead of glDeleteProgram() + - ccGLBlendFunc() instead of glBlendFunc() + + If this functionality is disabled, then ccGLUseProgram(), ccGLDeleteProgram(), ccGLBlendFunc() will call the GL ones, without using the cache. + + It is recommened to enable it whenever possible to improve speed. + If you are migrating your code from GL ES 1.1, then keep it disabled. Once all your code works as expected, turn it on. + + Default value: Disabled by default + + @since v2.0.0 + */ +#ifndef CC_ENABLE_GL_STATE_CACHE +#define CC_ENABLE_GL_STATE_CACHE 0 +#endif + +/** @def CC_ENABLE_DEPRECATED + If enabled, cocos2d will compile all deprecated methods, classes and free functions. Also, renamed constants will be active as well. + Enable it only when migrating a v1.0 or earlier v2.0 versions to the most recent cocdos2d version. + + Default value: Enabled by default + + @since v2.0.0 + */ +#ifndef CC_ENABLE_DEPRECATED +#define CC_ENABLE_DEPRECATED 1 +#endif + + /** @def CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL If enabled, the texture coordinates will be calculated by using this formula: - texCoord.left = (rect.origin.x*2+1) / (texture.wide*2); - texCoord.right = texCoord.left + (rect.size.width*2-2)/(texture.wide*2); - + The same for bottom and top. - + This formula prevents artifacts by using 99% of the texture. The "correct" way to prevent artifacts is by using the spritesheet-artifact-fixer.py or a similar tool. - + Affected nodes: - - CCSprite / CCSpriteBatchNode and subclasses: CCLabelBMFont, CCTMXTiledMap + - CCSprite / CCSpriteBatchNode and subclasses: CCLabelBMFont, CCTMXLayer - CCLabelAtlas - - CCQuadParticleSystem + - CCParticleSystemQuad - CCTileMap - + To enabled set it to 1. Disabled by default. - + @since v0.99.5 */ #ifndef CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL #define CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL 0 #endif - -/** @def CC_FONT_LABEL_SUPPORT - If enabled, FontLabel will be used to render .ttf files. - If the .ttf file is not found, then it will use the standard UIFont class - If disabled, the standard UIFont class will be used. - - To disable set it to 0. Enabled by default. +/** @def CC_DIRECTOR_STATS_INTERVAL + Seconds between stats updates. + 0.5 seconds, means that the stats will be updated every 0.5 seconds. + Having a bigger number means more stable stats - Only valid for cocos2d-ios. Not supported on cocos2d-mac + Default value: 0.1f */ -#ifndef CC_FONT_LABEL_SUPPORT -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED -#define CC_FONT_LABEL_SUPPORT 1 -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) -#define CC_FONT_LABEL_SUPPORT 0 -#endif +#ifndef CC_DIRECTOR_STATS_INTERVAL +#define CC_DIRECTOR_STATS_INTERVAL (0.1f) #endif -/** @def CC_DIRECTOR_FAST_FPS - If enabled, then the FPS will be drawn using CCLabelAtlas (fast rendering). - You will need to add the fps_images.png to your project. - If disabled, the FPS will be rendered using CCLabel (slow rendering) - - To enable set it to a value different than 0. Enabled by default. - */ -#ifndef CC_DIRECTOR_FAST_FPS -#define CC_DIRECTOR_FAST_FPS 1 -#endif +/** @def CC_DIRECTOR_STATS_POSITION + Position of the FPS -/** @def CC_DIRECTOR_FPS_INTERVAL - Senconds between FPS updates. - 0.5 seconds, means that the FPS number will be updated every 0.5 seconds. - Having a bigger number means a more reliable FPS - - Default value: 0.1f + Default: 0,0 (bottom-left corner) */ -#ifndef CC_DIRECTOR_FPS_INTERVAL -#define CC_DIRECTOR_FPS_INTERVAL (0.1f) +#ifndef CC_DIRECTOR_STATS_POSITION +#define CC_DIRECTOR_STATS_POSITION ccp(0,0) #endif -/** @def CC_DIRECTOR_DISPATCH_FAST_EVENTS - If enabled, and only when it is used with CCFastDirector, the main loop will wait 0.04 seconds to - dispatch all the events, even if there are not events to dispatch. - If your game uses lot's of events (eg: touches) it might be a good idea to enable this feature. - Otherwise, it is safe to leave it disabled. - - To enable set it to 1. Disabled by default. +/** @def CC_DIRECTOR_IOS_USE_BACKGROUND_THREAD + If enabled, cocos2d-ios will run on a background thread. If disabled cocos2d-ios will run the main thread. + + To enable set it to a 1, to disable it set to 0. Enabled by default. + + Only valid for cocos2d-ios. Not supported on cocos2d-mac. - @warning This feature is experimental + This is an EXPERIMENTAL feature. Do not use it unless you are a developer. + */ -#ifndef CC_DIRECTOR_DISPATCH_FAST_EVENTS -#define CC_DIRECTOR_DISPATCH_FAST_EVENTS 0 +#ifndef CC_DIRECTOR_IOS_USE_BACKGROUND_THREAD +#define CC_DIRECTOR_IOS_USE_BACKGROUND_THREAD 0 #endif -/** @def CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD - If enabled, cocos2d-mac will run on the Display Link thread. If disabled cocos2d-mac will run in its own thread. - - If enabled, the images will be drawn at the "correct" time, but the events might not be very responsive. - If disabled, some frames might be skipped, but the events will be dispatched as they arrived. - - To enable set it to a 1, to disable it set to 0. Enabled by default. +#define CC_MAC_USE_DISPLAY_LINK_THREAD 0 +#define CC_MAC_USE_OWN_THREAD 1 +#define CC_MAC_USE_MAIN_THREAD 2 + +/** @def CC_DIRECTOR_MAC_THREAD + cocos2d-mac can run on its own thread, on the Display Link thread, or in the main thread. + If you are developing a game, the Display Link or Own thread are the best alternatives. + If you are developing an editor that uses AppKit, you might need to use the Main Thread (only if you are lazy and don't want to create a sync queue). + + Options: + CC_MAC_USE_DISPLAY_LINK_THREAD (default) + CC_MAC_USE_OWN_THREAD + CC_MAC_USE_MAIN_THREAD + Only valid for cocos2d-mac. Not supported on cocos2d-ios. */ -#ifndef CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD -#define CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD 1 +#ifndef CC_DIRECTOR_MAC_THREAD +#define CC_DIRECTOR_MAC_THREAD CC_MAC_USE_DISPLAY_LINK_THREAD #endif -/** @def CC_COCOSNODE_RENDER_SUBPIXEL +/** @def CC_NODE_RENDER_SUBPIXEL If enabled, the CCNode objects (CCSprite, CCLabel,etc) will be able to render in subpixels. If disabled, integer pixels will be used. - + To enable set it to 1. Enabled by default. */ -#ifndef CC_COCOSNODE_RENDER_SUBPIXEL -#define CC_COCOSNODE_RENDER_SUBPIXEL 1 +#ifndef CC_NODE_RENDER_SUBPIXEL +#define CC_NODE_RENDER_SUBPIXEL 1 #endif /** @def CC_SPRITEBATCHNODE_RENDER_SUBPIXEL If enabled, the CCSprite objects rendered with CCSpriteBatchNode will be able to render in subpixels. If disabled, integer pixels will be used. - + To enable set it to 1. Enabled by default. */ #ifndef CC_SPRITEBATCHNODE_RENDER_SUBPIXEL #define CC_SPRITEBATCHNODE_RENDER_SUBPIXEL 1 #endif -/** @def CC_USES_VBO - If enabled, batch nodes (texture atlas and particle system) will use VBO instead of vertex list (VBO is recommended by Apple) - - To enable set it to 1. - Enabled by default on iPhone with ARMv7 processors, iPhone Simulator and Mac - Disabled by default on iPhone with ARMv6 processors. - - @since v0.99.5 - */ -#ifndef CC_USES_VBO -#if defined(__ARM_NEON__) || TARGET_IPHONE_SIMULATOR || defined(__MAC_OS_X_VERSION_MAX_ALLOWED) -#define CC_USES_VBO 1 -#else -#define CC_USES_VBO 0 -#endif -#endif - -/** @def CC_NODE_TRANSFORM_USING_AFFINE_MATRIX - If enabled, CCNode will transform the nodes using a cached Affine matrix. - If disabled, the node will be transformed using glTranslate,glRotate,glScale. - Using the affine matrix only requires 2 GL calls. - Using the translate/rotate/scale requires 5 GL calls. - But computing the Affine matrix is relative expensive. - But according to performance tests, Affine matrix performs better. - This parameter doesn't affect CCSpriteBatchNode nodes. - - To enable set it to a value different than 0. Enabled by default. - - */ -#ifndef CC_NODE_TRANSFORM_USING_AFFINE_MATRIX -#define CC_NODE_TRANSFORM_USING_AFFINE_MATRIX 1 -#endif - -/** @def CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA - If most of your imamges have pre-multiplied alpha, set it to 1 (if you are going to use .PNG/.JPG file images). - Only set to 0 if ALL your images by-pass Apple UIImage loading system (eg: if you use libpng or PVR images) - - To enable set it to a value different than 0. Enabled by default. - - @since v0.99.5 - */ -#ifndef CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA -#define CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA 1 -#endif - /** @def CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP Use GL_TRIANGLE_STRIP instead of GL_TRIANGLES when rendering the texture atlas. It seems it is the recommend way, but it is much slower, so, enable it at your own risk - + To enable set it to a value different than 0. Disabled by default. */ @@ -200,70 +172,37 @@ #define CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP 0 #endif -/** @def CC_TEXTURE_NPOT_SUPPORT - If enabled, NPOT textures will be used where available. Only 3rd gen (and newer) devices support NPOT textures. - NPOT textures have the following limitations: - - They can't have mipmaps - - They only accept GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T} +/** @def CC_TEXTURE_ATLAS_USE_VAO + By default, CCTextureAtlas (used by many cocos2d classes) will use VAO (Vertex Array Objects). + Apple recommends its usage but they might consume a lot of memory, specially if you use many of them. + So for certain cases, where you might need hundreds of VAO objects, it might be a good idea to disable it. - To enable set it to a value different than 0. Disabled by default. - - This value governs only the PNG, GIF, BMP, images. - This value DOES NOT govern the PVR (PVR.GZ, PVR.CCZ) files. If NPOT PVR is loaded, then it will create an NPOT texture ignoring this value. + To disable it set it to 0. Enabled by default. - @deprecated This value will be removed in 1.1 and NPOT textures will be loaded by default if the device supports it. - - @since v0.99.2 */ -#ifndef CC_TEXTURE_NPOT_SUPPORT -#define CC_TEXTURE_NPOT_SUPPORT 0 +#ifndef CC_TEXTURE_ATLAS_USE_VAO +#define CC_TEXTURE_ATLAS_USE_VAO 1 #endif -/** @def CC_RETINA_DISPLAY_SUPPORT - If enabled, cocos2d supports retina display. - For performance reasons, it's recommended disable it in games without retina display support, like iPad only games. - - To enable set it to 1. Use 0 to disable it. Enabled by default. - - @since v0.99.5 - */ -#ifndef CC_RETINA_DISPLAY_SUPPORT -#define CC_RETINA_DISPLAY_SUPPORT 1 -#endif -/** @def CC_RETINA_DISPLAY_FILENAME_SUFFIX - It's the suffix that will be appended to the files in order to load "retina display" images. +/** @def CC_USE_LA88_LABELS + If enabled, it will use LA88 (Luminance Alpha 16-bit textures) for CCLabelTTF objects. + If it is disabled, it will use A8 (Alpha 8-bit textures). + LA88 textures are 6% faster than A8 textures, but they will consume 2x memory. - On an iPhone4 with Retina Display support enabled, the file @"sprite-hd.png" will be loaded instead of @"sprite.png". - If the file doesn't exist it will use the non-retina display image. - - Platforms: Only used on Retina Display devices like iPhone 4. - - @since v0.99.5 - */ -#ifndef CC_RETINA_DISPLAY_FILENAME_SUFFIX -#define CC_RETINA_DISPLAY_FILENAME_SUFFIX @"-hd" -#endif - -/** @def CC_USE_LA88_LABELS_ON_NEON_ARCH - If enabled, it will use LA88 (16-bit textures) on Neon devices for CCLabelTTF objects. - If it is disabled, or if it is used on another architecture it will use A8 (8-bit textures). - On Neon devices, LA88 textures are 6% faster than A8 textures, but then will consume 2x memory. - - This feature is disabled by default. - - Platforms: Only used on ARM Neon architectures like iPhone 3GS or newer and iPad. + This feature is enabled by default. @since v0.99.5 */ -#ifndef CC_USE_LA88_LABELS_ON_NEON_ARCH -#define CC_USE_LA88_LABELS_ON_NEON_ARCH 0 +#ifndef CC_USE_LA88_LABELS +#define CC_USE_LA88_LABELS 1 #endif /** @def CC_SPRITE_DEBUG_DRAW - If enabled, all subclasses of CCSprite will draw a bounding box + If enabled, all subclasses of CCSprite will draw a bounding box. Useful for debugging purposes only. It is recommened to leave it disabled. - + + If the CCSprite is being drawn by a CCSpriteBatchNode, the bounding box might be a bit different. To enable set it to a value different than 0. Disabled by default: 0 -- disabled 1 -- draw bounding box @@ -273,30 +212,21 @@ #define CC_SPRITE_DEBUG_DRAW 0 #endif -/** @def CC_SPRITEBATCHNODE_DEBUG_DRAW - If enabled, all subclasses of CCSprite that are rendered using an CCSpriteBatchNode draw a bounding box. - Useful for debugging purposes only. It is recommened to leave it disabled. - - To enable set it to a value different than 0. Disabled by default. - */ -#ifndef CC_SPRITEBATCHNODE_DEBUG_DRAW -#define CC_SPRITEBATCHNODE_DEBUG_DRAW 0 -#endif /** @def CC_LABELBMFONT_DEBUG_DRAW If enabled, all subclasses of CCLabelBMFont will draw a bounding box Useful for debugging purposes only. It is recommened to leave it disabled. - + To enable set it to a value different than 0. Disabled by default. */ #ifndef CC_LABELBMFONT_DEBUG_DRAW #define CC_LABELBMFONT_DEBUG_DRAW 0 #endif -/** @def CC_LABELBMFONT_DEBUG_DRAW +/** @def CC_LABELATLAS_DEBUG_DRAW If enabled, all subclasses of CCLabeltAtlas will draw a bounding box Useful for debugging purposes only. It is recommened to leave it disabled. - + To enable set it to a value different than 0. Disabled by default. */ #ifndef CC_LABELATLAS_DEBUG_DRAW @@ -304,31 +234,12 @@ #endif /** @def CC_ENABLE_PROFILERS - If enabled, will activate various profilers withing cocos2d. This statistical data will be output to the console - once per second showing average time (in milliseconds) required to execute the specific routine(s). - Useful for debugging purposes only. It is recommened to leave it disabled. - + If enabled, it will activate various profilers within cocos2d. This statistical data will be saved in the CCProfiler singleton. + In order to display saved data, you have to call the CC_PROFILER_DISPLAY_TIMERS() macro. + Useful for profiling purposes only. If unsure, leave it disabled. + To enable set it to a value different than 0. Disabled by default. */ #ifndef CC_ENABLE_PROFILERS #define CC_ENABLE_PROFILERS 0 #endif - -// -// DON'T edit this macro. -// -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED - -#if CC_RETINA_DISPLAY_SUPPORT -#define CC_IS_RETINA_DISPLAY_SUPPORTED 1 -#else -#define CC_IS_RETINA_DISPLAY_SUPPORTED 0 -#endif - -#elif __MAC_OS_X_VERSION_MAX_ALLOWED - -#define CC_IS_RETINA_DISPLAY_SUPPORTED 0 - -#endif - - diff --git a/cocos2d/cocos2d/ccDeprecated.h b/cocos2d/cocos2d/ccDeprecated.h new file mode 100755 index 0000000..0b16933 --- /dev/null +++ b/cocos2d/cocos2d/ccDeprecated.h @@ -0,0 +1,300 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2012 Ricardo Quesada + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import "ccConfig.h" + +#if CC_ENABLE_DEPRECATED + +#import "ccTypes.h" +#import "ccMacros.h" +#import "CCMenu.h" +#import "CCDirector.h" +#import "CCSprite.h" +#import "CCGLProgram.h" +#import "CCAnimation.h" +#import "CCScheduler.h" +#import "CCActionManager.h" +#import "CCActionInterval.h" +#import "CCRenderTexture.h" +#import "CCSpriteFrameCache.h" +#import "CCLabelTTF.h" +#import "CCTexture2D.h" +#import "Support/CCFileUtils.h" +#import "Platforms/Mac/CCDirectorMac.h" +#import "Platforms/iOS/CCTouchDispatcher.h" +#import "Platforms/iOS/CCDirectorIOS.h" + + +/* + * + * IMPORTANT + * + * See the ccDrepecated.m file to see the name of the new methods + * + */ + +// CCLOGERROR is no longer supported. +#define CCLOGERROR CCLOGWARN + +// ccTypes.h +enum { +#ifdef __CC_PLATFORM_IOS + kCCResolutionStandard DEPRECATED_ATTRIBUTE = kCCResolutioniPhone, + kCCResolutionRetinaDisplay DEPRECATED_ATTRIBUTE = kCCResolutioniPhoneRetinaDisplay, +#endif // __CC_PLATFORM_IOS + kCCMenuTouchPriority DEPRECATED_ATTRIBUTE = kCCMenuHandlerPriority, +}; + +// CCRenderTexture.h +enum { + kCCImageFormatJPG DEPRECATED_ATTRIBUTE = kCCImageFormatJPEG, + kCCImageFormatRawData UNAVAILABLE_ATTRIBUTE, +}; + +enum { + CCTextAlignmentLeft DEPRECATED_ATTRIBUTE = kCCTextAlignmentLeft, + CCTextAlignmentCenter DEPRECATED_ATTRIBUTE = kCCTextAlignmentCenter, + CCTextAlignmentRight DEPRECATED_ATTRIBUTE = kCCTextAlignmentRight, + + CCVerticalTextAlignmentTop DEPRECATED_ATTRIBUTE = kCCVerticalTextAlignmentTop, + CCVerticalTextAlignmentMiddle DEPRECATED_ATTRIBUTE = kCCVerticalTextAlignmentCenter, + CCVerticalTextAlignmentBottom DEPRECATED_ATTRIBUTE = kCCVerticalTextAlignmentBottom, + + CCLineBreakModeWordWrap DEPRECATED_ATTRIBUTE = kCCLineBreakModeWordWrap, + CCLineBreakModeCharacterWrap DEPRECATED_ATTRIBUTE = kCCLineBreakModeCharacterWrap, + CCLineBreakModeClip DEPRECATED_ATTRIBUTE = kCCLineBreakModeClip, + CCLineBreakModeHeadTruncation DEPRECATED_ATTRIBUTE = kCCLineBreakModeHeadTruncation, + CCLineBreakModeTailTruncation DEPRECATED_ATTRIBUTE = kCCLineBreakModeTailTruncation, + CCLineBreakModeMiddleTruncation DEPRECATED_ATTRIBUTE = kCCLineBreakModeMiddleTruncation, +}; + +//DEPRECATED_ATTRIBUTE typedef ccTextAlignment CCTextAlignment; +// +//DEPRECATED_ATTRIBUTE typedef ccVerticalTextAlignment CCVerticalTextAlignment; + +// Free functions +void ccGLUniformModelViewProjectionMatrix(CCGLProgram* program) DEPRECATED_ATTRIBUTE; + +// Renamed classes +DEPRECATED_ATTRIBUTE @interface EAGLView : CCGLView +@end + +DEPRECATED_ATTRIBUTE @interface MacView : CCGLView +@end + +// hack to prevent "incopatible pointer type" +#define GLProgram CCGLProgram + +// Extensions +@interface CCScheduler (Deprecated) +// new: [director scheduler] ++(CCScheduler*) sharedScheduler DEPRECATED_ATTRIBUTE; +@end + +@interface CCActionManager (Deprecated) +// new: [director actionManager] ++(CCActionManager*) sharedManager DEPRECATED_ATTRIBUTE; +@end + +#if __CC_PLATFORM_IOS +@interface CCTouchDispatcher (Deprecated) +// new: [director touchDispatcher] ++(CCTouchDispatcher*) sharedDispatcher DEPRECATED_ATTRIBUTE; +@end +#elif __CC_PLATFORM_MAC +@interface CCEventDispatcher (Deprecated) +// new: [director eventDispatcher] ++(CCEventDispatcher*) sharedDispatcher DEPRECATED_ATTRIBUTE; +@end +#endif // __CC_PLATFORM_MAC + +@interface CCDirector (Deprecated) +// new: setView: +-(void) setOpenGLView:(CCGLView*)view DEPRECATED_ATTRIBUTE; +// new: view +-(CCGLView*) openGLView DEPRECATED_ATTRIBUTE; +// new: setDisplayStats: +-(void) setDisplayFPS:(BOOL)display DEPRECATED_ATTRIBUTE; +@end + +@interface CCNode (Deprecated) +-(void) setIsRelativeAnchorPoint:(BOOL)value DEPRECATED_ATTRIBUTE; +-(BOOL) isRelativeAnchorPoint DEPRECATED_ATTRIBUTE; +@end + +@interface CCSprite (Deprecated) +// new: spriteWithTexture:rect: ++(id) spriteWithBatchNode:(CCSpriteBatchNode*)node rect:(CGRect)rect DEPRECATED_ATTRIBUTE; +// new: initWithTexture:rect: +-(id) initWithBatchNode:(CCSpriteBatchNode*)node rect:(CGRect)rect DEPRECATED_ATTRIBUTE; +// displayFrame +-(CCSpriteFrame*) displayedFrame DEPRECATED_ATTRIBUTE; +@end + +@interface CCMenuItemAtlasFont (Deprecated) +// new itemWithStirng:charmapFile:itemWidth:itemHeight:startCharMap ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap DEPRECATED_ATTRIBUTE; +// new itemWithStirng:charmapFile:itemWidth:itemHeight:startCharMap:target:selector ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb DEPRECATED_ATTRIBUTE; +// new itemWithStirng:charmapFile:itemWidth:itemHeight:startCharMap:block ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block DEPRECATED_ATTRIBUTE; +// new initWithStirng:charmapFile:itemWidth:itemHeight:startCharMap:target:selector +-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb DEPRECATED_ATTRIBUTE; +// new initWithStirng:charmapFile:itemWidth:itemHeight:startCharMap:block +-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block DEPRECATED_ATTRIBUTE; +@end + + +@interface CCMenuItemFont (Deprecated) +// new: itemWithString: ++(id) itemFromString: (NSString*) value DEPRECATED_ATTRIBUTE; +// new: itemWithString:target:selector ++(id) itemFromString: (NSString*) value target:(id) r selector:(SEL) s DEPRECATED_ATTRIBUTE; +// new: itemWithString:block: ++(id) itemFromString: (NSString*) value block:(void(^)(id sender))block DEPRECATED_ATTRIBUTE; +// new: initWithString:target:selector +-(id) initFromString: (NSString*) value target:(id) r selector:(SEL) s DEPRECATED_ATTRIBUTE; +// new: initWithString:block: +-(id) initFromString: (NSString*) value block:(void(^)(id sender))block DEPRECATED_ATTRIBUTE; +@end + +@interface CCMenuItemSprite (Deprecated) ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite DEPRECATED_ATTRIBUTE; ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite target:(id)target selector:(SEL)selector DEPRECATED_ATTRIBUTE; ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector DEPRECATED_ATTRIBUTE; ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite block:(void(^)(id sender))block DEPRECATED_ATTRIBUTE; ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block DEPRECATED_ATTRIBUTE; + +-(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector DEPRECATED_ATTRIBUTE; +-(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block DEPRECATED_ATTRIBUTE; +@end + +@interface CCMenuItemImage (Deprecated) +// new: itemWithNormalImage:selectedImage: ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 DEPRECATED_ATTRIBUTE; +// new: itemWithNormalImage:selectedImage:target:selector ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) r selector:(SEL) s DEPRECATED_ATTRIBUTE; +// new: itemWithNormalImage:selectedImage:disabledImage:target:selector ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s DEPRECATED_ATTRIBUTE; +// new: itemWithNormalImage:selectedImage:block ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block DEPRECATED_ATTRIBUTE; +// new: itemWithNormalImage:selectedImage:disabledImage:block ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block DEPRECATED_ATTRIBUTE; +// new: initWithNormalImage:selectedImage:disabledImage:target:selector +-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s DEPRECATED_ATTRIBUTE; +// new: initWithNormalImage:selectedImage:disabledImage:block +-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block DEPRECATED_ATTRIBUTE; +@end + +@interface CCAnimation (Deprecated) ++(id) animationWithFrames:(NSArray*)arrayOfSpriteFrameNames DEPRECATED_ATTRIBUTE; ++(id) animationWithFrames:(NSArray*)arrayOfSpriteFrameNames delay:(float)delay DEPRECATED_ATTRIBUTE; +-(id) initWithFrames:(NSArray*)arrayOfSpriteFrameNames DEPRECATED_ATTRIBUTE; +-(id) initWithFrames:(NSArray *)arrayOfSpriteFrameNames delay:(float)delay DEPRECATED_ATTRIBUTE; +-(void) addFrame:(CCSpriteFrame*)frame DEPRECATED_ATTRIBUTE; +-(void) addFrameWithFilename:(NSString*)filename DEPRECATED_ATTRIBUTE; +-(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect DEPRECATED_ATTRIBUTE; +@end + +@interface CCAnimate (Deprecated) +// new: actionWithAnimation: ++(id) actionWithAnimation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)restoreOriginalFrame DEPRECATED_ATTRIBUTE; +// new: actiontWithAnimation: ++(id) actionWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)restoreOriginalFrame DEPRECATED_ATTRIBUTE; +// new: initWithAnimation: +-(id) initWithAnimation:(CCAnimation*) a restoreOriginalFrame:(BOOL)restoreOriginalFrame DEPRECATED_ATTRIBUTE; +// new: initWithAnimation: +-(id) initWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)restoreOriginalFrame DEPRECATED_ATTRIBUTE; +@end + +@interface CCSequence (Deprecated) +// new: actionWithArray ++(id) actionsWithArray: (NSArray*) actions DEPRECATED_ATTRIBUTE; +@end + +@interface CCSpawn (Deprecated) +// new: actionWithArray ++(id) actionsWithArray: (NSArray*) actions DEPRECATED_ATTRIBUTE; +@end + + +@interface CCRenderTexture (Deprecated) +// new: saveToFile: +-(BOOL)saveBuffer:(NSString*)name DEPRECATED_ATTRIBUTE; +// new: saveToFile:format: +-(BOOL)saveBuffer:(NSString*)name format:(int)format DEPRECATED_ATTRIBUTE; +// new: -- not implemented on v2.0 +-(NSData*)getUIImageAsDataFromBuffer:(int) format UNAVAILABLE_ATTRIBUTE; +#if __CC_PLATFORM_IOS +// new: getUIImage +-(UIImage *)getUIImageFromBuffer DEPRECATED_ATTRIBUTE; +#endif +@end + +@interface CCFileUtils (Deprecated) + +// new: -(NSString*) fullPathFromRelativePath: (instance method, not class method) ++(NSString*) fullPathFromRelativePath:(NSString*) relPath DEPRECATED_ATTRIBUTE; +// new: -(NSString*) fullPathFromRelativePath:resolutionType (instance method, not class method) ++(NSString*) fullPathFromRelativePath:(NSString*)relPath resolutionType:(ccResolutionType*)resolutionType DEPRECATED_ATTRIBUTE; + +#ifdef __CC_PLATFORM_IOS +// new: -(NSString*) removeSuffixFromFile: (instance method, not class method) ++(NSString *)removeSuffixFromFile:(NSString*) path DEPRECATED_ATTRIBUTE; +// new: -(BOOL) iPhoneRetinaDisplayFileExistsAtPath: (instance method, not class method) ++(BOOL) iPhoneRetinaDisplayFileExistsAtPath:(NSString*)filename DEPRECATED_ATTRIBUTE; +// new: -(BOOL) iPadFileExistsAtPath: (instance method, not class method) ++(BOOL) iPadFileExistsAtPath:(NSString*)filename DEPRECATED_ATTRIBUTE; +// new: -(BOOL) iPadRetinaDisplayFileExistsAtPath: (instance method, not class method) ++(BOOL) iPadRetinaDisplayFileExistsAtPath:(NSString*)filename DEPRECATED_ATTRIBUTE; +// new: -(void) setiPhoneRetinaDisplaySuffix: (instance method, not class method) ++(void) setRetinaDisplaySuffix:(NSString*)suffix DEPRECATED_ATTRIBUTE; +#endif //__CC_PLATFORM_IOS +@end + + +@interface CCSpriteFrameCache (Deprecated) +-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary textureFile:(NSString*)filename DEPRECATED_ATTRIBUTE; +-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)filename DEPRECATED_ATTRIBUTE; +@end + + +@interface CCLabelTTF (Deprecated) +// new: + (id) labelWithString:(NSString*)string dimensions:hAlignment:fontName:fontSize: ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size DEPRECATED_ATTRIBUTE; +// new: + (id) labelWithString:(NSString*)string dimensions:hAlignment:lineBreakMode:fontName:fontSize: ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size DEPRECATED_ATTRIBUTE; +// new: + (id) initWithString:(NSString*)string dimensions:hAlignment:fontName:fontSize: +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size DEPRECATED_ATTRIBUTE; +// new: + (id) initWithString:(NSString*)string dimensions:hAlignment:lineBreakMode:fontName:fontSize: +- (id) initWithString:(NSString*)str dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size DEPRECATED_ATTRIBUTE; +@end + +@interface CCTexture2D (Deprecated) +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size DEPRECATED_ATTRIBUTE; +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size DEPRECATED_ATTRIBUTE; +@end + +#endif // CC_ENABLE_DEPRECATED + diff --git a/cocos2d/cocos2d/ccDeprecated.m b/cocos2d/cocos2d/ccDeprecated.m new file mode 100755 index 0000000..34b0645 --- /dev/null +++ b/cocos2d/cocos2d/ccDeprecated.m @@ -0,0 +1,422 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2012 Ricardo Quesada + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import "ccDeprecated.h" + +#if CC_ENABLE_DEPRECATED + +#import "CCSpriteBatchNode.h" + +// Free functions +void ccGLUniformModelViewProjectionMatrix( CCGLProgram* program ) +{ + [program setUniformForModelViewProjectionMatrix]; +} + +@implementation CCScheduler (Deprecated) ++(CCScheduler*) sharedScheduler +{ + return [[CCDirector sharedDirector] scheduler]; +} +@end + +@implementation CCActionManager (Deprecated) ++(CCActionManager*) sharedManager +{ + return [[CCDirector sharedDirector] actionManager]; +} +@end + +#if __CC_PLATFORM_IOS +@implementation CCTouchDispatcher (Deprecated) ++(CCTouchDispatcher*) sharedDispatcher +{ + return [[CCDirector sharedDirector] touchDispatcher]; +} +@end +#elif __CC_PLATFORM_MAC +@implementation CCEventDispatcher (Deprecated) ++(CCEventDispatcher*) sharedDispatcher +{ + return [[CCDirector sharedDirector] eventDispatcher]; +} +@end +#endif // __CC_PLATFORM_MAC + +#pragma mark - CCDirector + +@implementation CCDirector (Deprecated) +-(void) setDisplayFPS:(BOOL)display +{ + [self setDisplayStats:display]; +} + +-(void) setOpenGLView:(CCGLView*)view +{ + [self setView:view]; +} + +-(CCGLView*) openGLView +{ + return (CCGLView*)view_; +} +@end + +@implementation CCNode (Deprecated) +-(void) setIsRelativeAnchorPoint:(BOOL)value +{ + [self setIgnoreAnchorPointForPosition:!value]; +} +-(BOOL) isRelativeAnchorPoint +{ + return ! self.ignoreAnchorPointForPosition; +} +@end + +@implementation CCSprite (Deprecated) + ++(id) spriteWithBatchNode:(CCSpriteBatchNode*)node rect:(CGRect)rect +{ + id ret = [self spriteWithTexture:node.texture rect:rect]; + [ret setBatchNode:node]; + return ret; +} + +-(id) initWithBatchNode:(CCSpriteBatchNode*)node rect:(CGRect)rect +{ + self = [self initWithTexture:node.texture rect:rect]; + [self setBatchNode:node]; + return self; +} + +-(CCSpriteFrame*) displayedFrame +{ + return [self displayFrame]; +} +@end + +@implementation CCMenuItemAtlasFont (Deprecated) ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap +{ + return [self itemWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap]; +} ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb +{ + return [self itemWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:rec selector:cb]; +} ++(id) itemFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block +{ + return [self itemWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap block:block]; +} +-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap target:(id) rec selector:(SEL) cb +{ + return [self initWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap target:rec selector:cb]; +} +-(id) initFromString: (NSString*) value charMapFile:(NSString*) charMapFile itemWidth:(int)itemWidth itemHeight:(int)itemHeight startCharMap:(char)startCharMap block:(void(^)(id sender))block +{ + return [self initWithString:value charMapFile:charMapFile itemWidth:itemWidth itemHeight:itemHeight startCharMap:startCharMap block:block]; +} +@end + +@implementation CCMenuItemFont (Deprecated) ++(id) itemFromString: (NSString*) value +{ + return [self itemWithString:value]; +} ++(id) itemFromString: (NSString*) value target:(id) r selector:(SEL) s +{ + return [self itemWithString:value target:r selector:s]; +} ++(id) itemFromString: (NSString*) value block:(void(^)(id sender))block +{ + return [self itemWithString:value block:block]; +} +-(id) initFromString: (NSString*) value target:(id) r selector:(SEL) s +{ + return [self initWithString:value target:r selector:s]; +} +-(id) initFromString: (NSString*) value block:(void(^)(id sender))block +{ + return [self initWithString:value block:block]; +} +@end + +@implementation CCMenuItemSprite (Deprecated) ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite +{ + return [self itemWithNormalSprite:normalSprite selectedSprite:selectedSprite]; +} ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite target:(id)target selector:(SEL)selector +{ + return [self itemWithNormalSprite:normalSprite selectedSprite:selectedSprite target:target selector:selector]; +} ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector +{ + return [self itemWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:target selector:selector]; +} ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite block:(void(^)(id sender))block +{ + return [self itemWithNormalSprite:normalSprite selectedSprite:selectedSprite block:block]; +} ++(id) itemFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block +{ + return [self itemWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite block:block]; +} + +-(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite target:(id)target selector:(SEL)selector +{ + return [self initWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite target:target selector:selector]; +} +-(id) initFromNormalSprite:(CCNode*)normalSprite selectedSprite:(CCNode*)selectedSprite disabledSprite:(CCNode*)disabledSprite block:(void(^)(id sender))block +{ + return [self initWithNormalSprite:normalSprite selectedSprite:selectedSprite disabledSprite:disabledSprite block:block]; +} +@end + +@implementation CCMenuItemImage (Deprecated) ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 +{ + return [self itemWithNormalImage:value selectedImage:value2]; +} ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 target:(id) r selector:(SEL) s +{ + return [self itemWithNormalImage:value selectedImage:value2 target:r selector:s]; +} ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s +{ + return [self itemWithNormalImage:value selectedImage:value2 disabledImage:value3 target:r selector:s]; +} ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 block:(void(^)(id sender))block +{ + return [self itemWithNormalImage:value selectedImage:value2 block:block]; +} ++(id) itemFromNormalImage: (NSString*)value selectedImage:(NSString*) value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block +{ + return [self itemWithNormalImage:value selectedImage:value2 disabledImage:value3 block:block]; +} +-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 target:(id) r selector:(SEL) s +{ + return [self initWithNormalImage:value selectedImage:value2 disabledImage:value3 target:r selector:s]; +} +-(id) initFromNormalImage: (NSString*) value selectedImage:(NSString*)value2 disabledImage:(NSString*) value3 block:(void(^)(id sender))block +{ + return [self initWithNormalImage:value selectedImage:value2 disabledImage:value3 block:block]; +} +@end + + +@implementation CCAnimation (Deprecated) ++(id) animationWithFrames:(NSArray*)arrayOfSpriteFrameNames +{ + return [self animationWithSpriteFrames:arrayOfSpriteFrameNames]; +} ++(id) animationWithFrames:(NSArray*)arrayOfSpriteFrameNames delay:(float)delay +{ + return [self animationWithSpriteFrames:arrayOfSpriteFrameNames delay:delay]; +} +-(id) initWithFrames:(NSArray*)arrayOfSpriteFrameNames +{ + return [self initWithSpriteFrames:arrayOfSpriteFrameNames]; +} +-(id) initWithFrames:(NSArray *)arrayOfSpriteFrameNames delay:(float)delay +{ + return [self initWithSpriteFrames:arrayOfSpriteFrameNames delay:delay]; +} +-(void) addFrame:(CCSpriteFrame*)frame +{ + [self addSpriteFrame:frame]; +} +-(void) addFrameWithFilename:(NSString*)filename +{ + [self addSpriteFrameWithFilename:filename]; +} +-(void) addFrameWithTexture:(CCTexture2D*)texture rect:(CGRect)rect +{ + [self addSpriteFrameWithTexture:texture rect:rect]; +} +@end + +@implementation CCAnimate (Deprecated) ++(id) actionWithAnimation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)restoreOriginalFrame +{ + CCAnimation *anim = [[animation copy] autorelease]; + anim.restoreOriginalFrame = restoreOriginalFrame; + + return [[[self alloc] initWithAnimation:anim] autorelease]; +} ++(id) actionWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)restoreOriginalFrame +{ + CCAnimation *anim = [[animation copy] autorelease]; + anim.restoreOriginalFrame = restoreOriginalFrame; + anim.delayPerUnit = duration / animation.frames.count; + + return [[[self alloc] initWithAnimation:anim] autorelease]; +} +-(id) initWithAnimation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)restoreOriginalFrame +{ + CCAnimation *anim = [[animation copy] autorelease]; + anim.restoreOriginalFrame = restoreOriginalFrame; + + return [self initWithAnimation:anim]; +} +-(id) initWithDuration:(ccTime)duration animation:(CCAnimation*)animation restoreOriginalFrame:(BOOL)restoreOriginalFrame +{ + CCAnimation *anim = [[animation copy] autorelease]; + anim.restoreOriginalFrame = restoreOriginalFrame; + anim.delayPerUnit = duration / animation.frames.count; + + return [self initWithAnimation:anim]; +} +@end + +@implementation CCSequence (Deprecated) +// new: actionWithArray ++(id) actionsWithArray: (NSArray*) actions +{ + return [self actionWithArray:actions]; +} +@end + +@implementation CCSpawn (Deprecated) +// new: actionWithArray ++(id) actionsWithArray: (NSArray*) actions +{ + return [self actionWithArray:actions]; +} +@end + +@implementation CCRenderTexture (Deprecated) +-(BOOL)saveBuffer:(NSString*)name +{ + return [self saveToFile:name]; +} +-(BOOL)saveBuffer:(NSString*)name format:(int)format +{ + return [self saveToFile:name format:format]; +} + +-(NSData*)getUIImageAsDataFromBuffer:(int) format +{ + NSAssert(NO, @"NOT IMPLEMENTED IN V2.0"); + + return nil; +} +#if __CC_PLATFORM_IOS +-(UIImage *)getUIImageFromBuffer +{ + return [self getUIImage]; +} +#endif +@end + +@implementation CCFileUtils (Deprecated) ++(NSString*) fullPathFromRelativePath:(NSString*) relPath +{ + return [[self sharedFileUtils] fullPathFromRelativePath:relPath]; +} + ++(NSString*) fullPathFromRelativePath:(NSString*)relPath resolutionType:(ccResolutionType*)resolutionType +{ + return [[self sharedFileUtils] fullPathFromRelativePath:relPath resolutionType:resolutionType]; +} + +#if __CC_PLATFORM_IOS ++(void) setRetinaDisplaySuffix:(NSString*)suffix +{ + return [[self sharedFileUtils] setiPhoneRetinaDisplaySuffix:suffix]; +} ++(NSString *)removeSuffixFromFile:(NSString*) path +{ + return [[self sharedFileUtils] removeSuffixFromFile:path]; +} ++(BOOL) iPhoneRetinaDisplayFileExistsAtPath:(NSString*)filename +{ + return [[self sharedFileUtils] iPhoneRetinaDisplayFileExistsAtPath:filename]; +} ++(BOOL) iPadFileExistsAtPath:(NSString*)filename +{ + return [[self sharedFileUtils] iPadFileExistsAtPath:filename]; +} ++(BOOL) iPadRetinaDisplayFileExistsAtPath:(NSString*)filename +{ + return [[self sharedFileUtils] iPadRetinaDisplayFileExistsAtPath:filename]; +} +#endif +@end + +@implementation CCSpriteFrameCache (Deprecated) +-(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary textureFile:(NSString*)filename +{ + NSAssert(NO, @"unimplemented. Use addSpriteFramesWithFile:textureFile: instead"); +// [self addSpriteFramesWithDictionary:dictionary textureFilename:filename]; +} + +-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)filename +{ + [self addSpriteFramesWithFile:plist textureFilename:filename]; +} +@end + +@implementation CCLabelTTF (Deprecated) ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [self labelWithString:string dimensions:dimensions hAlignment:alignment fontName:name fontSize:size]; +} ++ (id) labelWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [self labelWithString:string dimensions:dimensions hAlignment:alignment lineBreakMode:lineBreakMode fontName:name fontSize:size]; +} +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [self initWithString:string dimensions:dimensions hAlignment:alignment fontName:name fontSize:size]; +} +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [self initWithString:string dimensions:dimensions hAlignment:alignment lineBreakMode:lineBreakMode fontName:name fontSize:size]; +} +@end + +@implementation CCTexture2D (Deprecated) +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment lineBreakMode:(CCLineBreakMode)lineBreakMode fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [self initWithString:string dimensions:dimensions hAlignment:alignment vAlignment:kCCVerticalTextAlignmentTop lineBreakMode:lineBreakMode fontName:name fontSize:size]; +} +- (id) initWithString:(NSString*)string dimensions:(CGSize)dimensions alignment:(CCTextAlignment)alignment fontName:(NSString*)name fontSize:(CGFloat)size +{ + return [self initWithString:string dimensions:dimensions hAlignment:alignment vAlignment:kCCVerticalTextAlignmentTop fontName:name fontSize:size]; +} +@end + +#if __CC_PLATFORM_IOS +@implementation EAGLView +@end + +#elif __CC_PLATFORM_MAC + +@implementation MacView +@end + +#endif // __CC_PLATFORM_MAC + +#endif // CC_ENABLE_DEPRECATED diff --git a/cocos2d/cocos2d/ccGLStateCache.h b/cocos2d/cocos2d/ccGLStateCache.h new file mode 100755 index 0000000..1356829 --- /dev/null +++ b/cocos2d/cocos2d/ccGLStateCache.h @@ -0,0 +1,138 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2011 Ricardo Quesada + * Copyright (c) 2011 Zynga Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import +#import "ccMacros.h" +#import "Platforms/CCGL.h" + +@class CCGLProgram; + +/** vertex attrib flags */ +enum { + kCCVertexAttribFlag_None = 0, + + kCCVertexAttribFlag_Position = 1 << 0, + kCCVertexAttribFlag_Color = 1 << 1, + kCCVertexAttribFlag_TexCoords = 1 << 2, + + kCCVertexAttribFlag_PosColorTex = ( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_Color | kCCVertexAttribFlag_TexCoords ), +}; + +/** GL server side states */ +typedef enum { +// CC_GL_SCISSOR_TEST = 1 << 0, +// CC_GL_STENCIL_TEST = 1 << 1, +// CC_GL_DEPTH_TEST = 1 << 2, + CC_GL_BLEND = 1 << 3, +// CC_GL_DITHER = 1 << 4, + +// CC_GL_ALL = ( CC_GL_SCISSOR_TEST | CC_GL_STENCIL_TEST | CC_GL_DEPTH_TEST | CC_GL_BLEND | CC_GL_DITHER ), + CC_GL_ALL = ( CC_GL_BLEND ), + +} ccGLServerState; + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file ccGLStateCache.h +*/ + +/** Invalidates the GL state cache. + If CC_ENABLE_GL_STATE_CACHE it will reset the GL state cache. + @since v2.0.0 + */ +void ccGLInvalidateStateCache( void ); + +/** Uses the GL program in case program is different than the current one. + If CC_ENABLE_GL_STATE_CACHE is disabled, it will the glUseProgram() directly. + @since v2.0.0 + */ +void ccGLUseProgram( GLuint program ); + +/** Deletes the GL program. If it is the one that is being used, it invalidates it. + If CC_ENABLE_GL_STATE_CACHE is disabled, it will the glDeleteProgram() directly. + @since v2.0.0 + */ +void ccGLDeleteProgram( GLuint program ); + +/** Uses a blending function in case it not already used. + If CC_ENABLE_GL_STATE_CACHE is disabled, it will the glBlendFunc() directly. + @since v2.0.0 + */ +void ccGLBlendFunc(GLenum sfactor, GLenum dfactor); + +/** sets the projection matrix as dirty + @since v2.0.0 + */ +void ccSetProjectionMatrixDirty( void ); + +/** Will enable the vertex attribs that are passed as flags. + Possible flags: + + * kCCVertexAttribFlag_Position + * kCCVertexAttribFlag_Color + * kCCVertexAttribFlag_TexCoords + + These flags can be ORed. The flags that are not present, will be disabled. + + @since v2.0.0 + */ +void ccGLEnableVertexAttribs( unsigned int flags ); + +/** If the active texture is not textureEnum, then it will active it. + If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glActiveTexture() directly. + @since v2.0.0 + */ +void ccGLActiveTexture(GLenum textureEnum ); + +/** Returns the active texture. + If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glGetIntegerv(GL_ACTIVE_TEXTURE); + @since v2.0.0 + */ +GLenum ccGLGetActiveTexture( void ); + + +/** If the texture is not already bound, it binds it. + If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glBindTexture() directly. + @since v2.0.0 + */ +void ccGLBindTexture2D(GLuint textureId ); + +/** It will delete a given texture. If the texture was bound, it will invalidate the cached. + If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glDeleteTextures() directly. + @since v2.0.0 + */ +void ccGLDeleteTexture(GLuint textureId); + +/** It will enable / disable the server side GL states. + If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glEnable() directly. + @since v2.0.0 + */ +void ccGLEnable( ccGLServerState flags ); + +#ifdef __cplusplus +} +#endif diff --git a/cocos2d/cocos2d/ccGLStateCache.m b/cocos2d/cocos2d/ccGLStateCache.m new file mode 100755 index 0000000..0918db0 --- /dev/null +++ b/cocos2d/cocos2d/ccGLStateCache.m @@ -0,0 +1,227 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2011 Ricardo Quesada + * Copyright (c) 2011 Zynga Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import "ccGLStateCache.h" +#import "CCGLProgram.h" +#import "CCDirector.h" +#import "ccConfig.h" + +// extern +#import "kazmath/GL/matrix.h" +#import "kazmath/kazmath.h" + +static GLuint _ccCurrentProjectionMatrix = -1; +static BOOL _vertexAttribPosition = NO; +static BOOL _vertexAttribColor = NO; +static BOOL _vertexAttribTexCoords = NO; + +#if CC_ENABLE_GL_STATE_CACHE +#define kCCMaxActiveTexture 16 +static GLuint _ccCurrentShaderProgram = -1; +static GLuint _ccCurrentBoundTexture[kCCMaxActiveTexture] = {-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, }; +static GLenum _ccCurrentActiveTexture = (GL_TEXTURE0 - GL_TEXTURE0); +static GLenum _ccBlendingSource = -1; +static GLenum _ccBlendingDest = -1; +static ccGLServerState _ccGLServerState = 0; +#endif // CC_ENABLE_GL_STATE_CACHE + +#pragma mark - GL State Cache functions + +void ccGLInvalidateStateCache( void ) +{ + kmGLFreeAll(); + + _ccCurrentProjectionMatrix = -1; + _vertexAttribPosition = NO; + _vertexAttribColor = NO; + _vertexAttribTexCoords = NO; + +#if CC_ENABLE_GL_STATE_CACHE + _ccCurrentShaderProgram = -1; + for( NSInteger i=0; i < kCCMaxActiveTexture; i++ ) + _ccCurrentBoundTexture[i] = -1; + _ccCurrentActiveTexture = (GL_TEXTURE0 - GL_TEXTURE0); + _ccBlendingSource = -1; + _ccBlendingDest = -1; + _ccGLServerState = 0; +#endif +} + +void ccGLDeleteProgram( GLuint program ) +{ +#if CC_ENABLE_GL_STATE_CACHE + if( program == _ccCurrentShaderProgram ) + _ccCurrentShaderProgram = -1; +#endif // CC_ENABLE_GL_STATE_CACHE + + glDeleteProgram( program ); +} + +void ccGLUseProgram( GLuint program ) +{ +#if CC_ENABLE_GL_STATE_CACHE + if( program != _ccCurrentShaderProgram ) { + _ccCurrentShaderProgram = program; + glUseProgram(program); + } +#else + glUseProgram(program); +#endif // CC_ENABLE_GL_STATE_CACHE +} + + +void ccGLBlendFunc(GLenum sfactor, GLenum dfactor) +{ +#if CC_ENABLE_GL_STATE_CACHE + if( sfactor != _ccBlendingSource || dfactor != _ccBlendingDest ) { + _ccBlendingSource = sfactor; + _ccBlendingDest = dfactor; + glBlendFunc( sfactor, dfactor ); + } +#else + glBlendFunc( sfactor, dfactor ); +#endif // CC_ENABLE_GL_STATE_CACHE +} + +GLenum ccGLGetActiveTexture( void ) +{ +#if CC_ENABLE_GL_STATE_CACHE + return _ccCurrentActiveTexture + GL_TEXTURE0; +#else + GLenum activeTexture; + glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&activeTexture); + return activeTexture; +#endif +} + +void ccGLActiveTexture( GLenum textureEnum ) +{ +#if CC_ENABLE_GL_STATE_CACHE + NSCAssert1( (textureEnum - GL_TEXTURE0) < kCCMaxActiveTexture, @"cocos2d ERROR: Increase kCCMaxActiveTexture to %d!", (textureEnum-GL_TEXTURE0) ); + if( (textureEnum - GL_TEXTURE0) != _ccCurrentActiveTexture ) { + _ccCurrentActiveTexture = (textureEnum - GL_TEXTURE0); + glActiveTexture( textureEnum ); + } +#else + glActiveTexture( textureEnum ); +#endif +} + + +void ccGLBindTexture2D( GLuint textureId ) +{ +#if CC_ENABLE_GL_STATE_CACHE + if( _ccCurrentBoundTexture[ _ccCurrentActiveTexture ] != textureId ) + { + _ccCurrentBoundTexture[ _ccCurrentActiveTexture ] = textureId; + glBindTexture(GL_TEXTURE_2D, textureId ); + } +#else + glBindTexture(GL_TEXTURE_2D, textureId ); +#endif +} + + +void ccGLDeleteTexture( GLuint textureId ) +{ +#if CC_ENABLE_GL_STATE_CACHE + if( textureId == _ccCurrentBoundTexture[ _ccCurrentActiveTexture ] ) + _ccCurrentBoundTexture[ _ccCurrentActiveTexture ] = -1; +#endif + glDeleteTextures(1, &textureId ); +} + +void ccGLEnable( ccGLServerState flags ) +{ +#if CC_ENABLE_GL_STATE_CACHE + + BOOL enabled = NO; + + /* GL_BLEND */ + if( (enabled=(flags & CC_GL_BLEND)) != (_ccGLServerState & CC_GL_BLEND) ) { + if( enabled ) { + glEnable( GL_BLEND ); + _ccGLServerState |= CC_GL_BLEND; + } else { + glDisable( GL_BLEND ); + _ccGLServerState &= ~CC_GL_BLEND; + } + } + +#else + if( flags & CC_GL_BLEND ) + glEnable( GL_BLEND ); + else + glDisable( GL_BLEND ); +#endif +} + +#pragma mark - GL Vertex Attrib functions + +void ccGLEnableVertexAttribs( unsigned int flags ) +{ + /* Position */ + BOOL enablePosition = flags & kCCVertexAttribFlag_Position; + + if( enablePosition != _vertexAttribPosition ) { + if( enablePosition ) + glEnableVertexAttribArray( kCCVertexAttrib_Position ); + else + glDisableVertexAttribArray( kCCVertexAttrib_Position ); + + _vertexAttribPosition = enablePosition; + } + + /* Color */ + BOOL enableColor = flags & kCCVertexAttribFlag_Color; + + if( enableColor != _vertexAttribColor ) { + if( enableColor ) + glEnableVertexAttribArray( kCCVertexAttrib_Color ); + else + glDisableVertexAttribArray( kCCVertexAttrib_Color ); + + _vertexAttribColor = enableColor; + } + + /* Tex Coords */ + BOOL enableTexCoords = flags & kCCVertexAttribFlag_TexCoords; + + if( enableTexCoords != _vertexAttribTexCoords ) { + if( enableTexCoords ) + glEnableVertexAttribArray( kCCVertexAttrib_TexCoords ); + else + glDisableVertexAttribArray( kCCVertexAttrib_TexCoords ); + + _vertexAttribTexCoords = enableTexCoords; + } +} + +#pragma mark - GL Uniforms functions + +void ccSetProjectionMatrixDirty( void ) +{ + _ccCurrentProjectionMatrix = -1; +} diff --git a/cocos2d/cocos2d/ccMacros.h b/cocos2d/cocos2d/ccMacros.h old mode 100644 new mode 100755 index 4e08725..93032f4 --- a/cocos2d/cocos2d/ccMacros.h +++ b/cocos2d/cocos2d/ccMacros.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -35,6 +35,12 @@ cocos2d helper macros */ +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#define __CC_PLATFORM_IOS 1 +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +#define __CC_PLATFORM_MAC 1 +#endif + /* * if COCOS2D_DEBUG is not defined, or if it is 0 then * all CCLOGXXX macros will be disabled @@ -47,24 +53,34 @@ * if COCOS2D_DEBUG==2 or higher then: * CCLOG() will be enabled * CCLOGERROR() will be enabled - * CCLOGINFO() will be enabled + * CCLOGINFO() will be enabled */ + + +#define __CCLOGWITHFUNCTION(s, ...) \ +NSLog(@"%s : %@",__FUNCTION__,[NSString stringWithFormat:(s), ##__VA_ARGS__]) + +#define __CCLOG(s, ...) \ +NSLog(@"%@",[NSString stringWithFormat:(s), ##__VA_ARGS__]) + + #if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0 #define CCLOG(...) do {} while (0) +#define CCLOGWARN(...) do {} while (0) #define CCLOGINFO(...) do {} while (0) -#define CCLOGERROR(...) do {} while (0) #elif COCOS2D_DEBUG == 1 -#define CCLOG(...) NSLog(__VA_ARGS__) -#define CCLOGERROR(...) NSLog(__VA_ARGS__) +#define CCLOG(...) __CCLOG(__VA_ARGS__) +#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__) #define CCLOGINFO(...) do {} while (0) #elif COCOS2D_DEBUG > 1 -#define CCLOG(...) NSLog(__VA_ARGS__) -#define CCLOGERROR(...) NSLog(__VA_ARGS__) -#define CCLOGINFO(...) NSLog(__VA_ARGS__) +#define CCLOG(...) __CCLOG(__VA_ARGS__) +#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__) +#define CCLOGINFO(...) __CCLOG(__VA_ARGS__) #endif // COCOS2D_DEBUG + /** @def CC_SWAP simple macro that swaps 2 variables */ @@ -94,75 +110,40 @@ simple macro that swaps 2 variables */ #define CC_RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__) * 57.29577951f) // PI * 180 +#define kCCRepeatForever UINT_MAX -1 /** @def CC_BLEND_SRC default gl blend src function. Compatible with premultiplied alpha images. */ -#if CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA #define CC_BLEND_SRC GL_ONE #define CC_BLEND_DST GL_ONE_MINUS_SRC_ALPHA -#else -#define CC_BLEND_SRC GL_SRC_ALPHA -#define CC_BLEND_DST GL_ONE_MINUS_SRC_ALPHA -#endif // ! CC_OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA - -/** @def CC_ENABLE_DEFAULT_GL_STATES - GL states that are enabled: - - GL_TEXTURE_2D - - GL_VERTEX_ARRAY - - GL_TEXTURE_COORD_ARRAY - - GL_COLOR_ARRAY - */ -#define CC_ENABLE_DEFAULT_GL_STATES() { \ - glEnableClientState(GL_VERTEX_ARRAY); \ - glEnableClientState(GL_COLOR_ARRAY); \ - glEnableClientState(GL_TEXTURE_COORD_ARRAY); \ - glEnable(GL_TEXTURE_2D); \ -} - -/** @def CC_DISABLE_DEFAULT_GL_STATES - Disable default GL states: - - GL_TEXTURE_2D - - GL_VERTEX_ARRAY - - GL_TEXTURE_COORD_ARRAY - - GL_COLOR_ARRAY - */ -#define CC_DISABLE_DEFAULT_GL_STATES() { \ - glDisable(GL_TEXTURE_2D); \ - glDisableClientState(GL_TEXTURE_COORD_ARRAY); \ - glDisableClientState(GL_COLOR_ARRAY); \ - glDisableClientState(GL_VERTEX_ARRAY); \ -} /** @def CC_DIRECTOR_INIT - - Initializes an EAGLView with 0-bit depth format, and RGB565 render buffer. - - The EAGLView view will have multiple touches disabled. - - It will create a UIWindow and it will assign it the 'window' variable. 'window' must be declared before calling this marcro. - - It will parent the EAGLView to the created window - - If the firmware >= 3.1 it will create a Display Link Director. Else it will create an NSTimer director. + - Initializes an CCGLView with 0-bit depth format, and RGB565 render buffer. + - The CCGLView view will have multiple touches disabled. + - It will create a UIWindow and it will assign it the 'window_' ivar. 'window_' must be declared before calling this marcro. + - It will create a UINavigationController and it will assign it the 'navigationController_' ivar. 'navController_' must be declared before using this macro. + - The director_ will be the root view controller of the navController. + - It will connect the CCGLView to the Director + - It will connect the UINavController view to the UIWindow. - It will try to run at 60 FPS. - - The FPS won't be displayed. - - The orientation will be portrait. - - It will connect the director with the EAGLView. + - It will connect the director with the CCGLView. IMPORTANT: If you want to use another type of render buffer (eg: RGBA8) or if you want to use a 16-bit or 24-bit depth buffer, you should NOT - use this macro. Instead, you should create the EAGLView manually. - + use this macro. Instead, you should create the CCGLView manually. + @since v0.99.4 */ -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #define CC_DIRECTOR_INIT() \ do { \ - window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; \ - if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] ) \ - [CCDirector setDirectorType:kCCDirectorTypeNSTimer]; \ - CCDirector *__director = [CCDirector sharedDirector]; \ - [__director setDeviceOrientation:kCCDeviceOrientationPortrait]; \ - [__director setDisplayFPS:NO]; \ - [__director setAnimationInterval:1.0/60]; \ - EAGLView *__glView = [EAGLView viewWithFrame:[window bounds] \ + window_ = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; \ + director_ = (CCDirectorIOS*)[CCDirector sharedDirector]; \ + [director_ setDisplayStats:NO]; \ + [director_ setAnimationInterval:1.0/60]; \ + CCGLView *__glView = [CCGLView viewWithFrame:[window_ bounds] \ pixelFormat:kEAGLColorFormatRGB565 \ depthFormat:0 /* GL_DEPTH_COMPONENT24_OES */ \ preserveBackbuffer:NO \ @@ -170,49 +151,66 @@ do { \ multiSampling:NO \ numberOfSamples:0 \ ]; \ - [__director setOpenGLView:__glView]; \ - [window addSubview:__glView]; \ - [window makeKeyAndVisible]; \ + [director_ setView:__glView]; \ + [director_ setDelegate:self]; \ + director_.wantsFullScreenLayout = YES; \ + if( ! [director_ enableRetinaDisplay:YES] ) \ + CCLOG(@"Retina Display Not supported"); \ + navController_ = [[UINavigationController alloc] initWithRootViewController:director_]; \ + navController_.navigationBarHidden = YES; \ + [window_ addSubview:navController_.view]; \ + [window_ makeKeyAndVisible]; \ } while(0) -#elif __MAC_OS_X_VERSION_MAX_ALLOWED - -#import "Platforms/Mac/MacWindow.h" +#elif __CC_PLATFORM_MAC #define CC_DIRECTOR_INIT(__WINSIZE__) \ do { \ NSRect frameRect = NSMakeRect(0, 0, (__WINSIZE__).width, (__WINSIZE__).height); \ - self.window = [[MacWindow alloc] initWithFrame:frameRect fullscreen:NO]; \ - self.glView = [[MacGLView alloc] initWithFrame:frameRect shareContext:nil]; \ + window_ = [[CCWindow alloc] initWithFrame:frameRect fullscreen:NO]; \ + glView_ = [[CCGLView alloc] initWithFrame:frameRect shareContext:nil]; \ [self.window setContentView:self.glView]; \ - CCDirector *__director = [CCDirector sharedDirector]; \ - [__director setDisplayFPS:NO]; \ - [__director setOpenGLView:self.glView]; \ - [(CCDirectorMac*)__director setOriginalWinSize:__WINSIZE__]; \ + director_ = (CCDirectorMac*) [CCDirector sharedDirector]; \ + [director_ setDisplayStats:NO]; \ + [director_ setView:self.glView]; \ + [director_ setOriginalWinSize:__WINSIZE__]; \ [self.window makeMainWindow]; \ [self.window makeKeyAndOrderFront:self]; \ + [self.window center]; \ } while(0) #endif - +/** @def CC_NODE_DRAW_SETUP + Helpful macro that setups the GL server state, the correct GL program and sets the Model View Projection matrix + @since v2.0 + */ +#define CC_NODE_DRAW_SETUP() \ +do { \ + ccGLEnable( glServerState_ ); \ + NSAssert1(shaderProgram_, @"No shader program set for node: %@", self); \ + [shaderProgram_ use]; \ + [shaderProgram_ setUniformForModelViewProjectionMatrix]; \ +} while(0) + + /** @def CC_DIRECTOR_END Stops and removes the director from memory. - Removes the EAGLView from its parent - + Removes the CCGLView from its parent + @since v0.99.4 */ #define CC_DIRECTOR_END() \ do { \ CCDirector *__director = [CCDirector sharedDirector]; \ - CC_GLVIEW *__view = [__director openGLView]; \ - [__view removeFromSuperview]; \ [__director end]; \ } while(0) -#if CC_IS_RETINA_DISPLAY_SUPPORTED + + +#if __CC_PLATFORM_IOS /****************************/ /** RETINA DISPLAY ENABLED **/ @@ -222,25 +220,50 @@ do { \ On Mac it returns 1; On iPhone it returns 2 if RetinaDisplay is On. Otherwise it returns 1 */ -#import "Platforms/iOS/CCDirectorIOS.h" +extern float __ccContentScaleFactor; #define CC_CONTENT_SCALE_FACTOR() __ccContentScaleFactor /** @def CC_RECT_PIXELS_TO_POINTS Converts a rect in pixels to points */ -#define CC_RECT_PIXELS_TO_POINTS(__pixels__) \ - CGRectMake( (__pixels__).origin.x / CC_CONTENT_SCALE_FACTOR(), (__pixels__).origin.y / CC_CONTENT_SCALE_FACTOR(), \ - (__pixels__).size.width / CC_CONTENT_SCALE_FACTOR(), (__pixels__).size.height / CC_CONTENT_SCALE_FACTOR() ) +#define CC_RECT_PIXELS_TO_POINTS(__rect_in_pixels__) \ + CGRectMake( (__rect_in_pixels__).origin.x / CC_CONTENT_SCALE_FACTOR(), (__rect_in_pixels__).origin.y / CC_CONTENT_SCALE_FACTOR(), \ + (__rect_in_pixels__).size.width / CC_CONTENT_SCALE_FACTOR(), (__rect_in_pixels__).size.height / CC_CONTENT_SCALE_FACTOR() ) /** @def CC_RECT_POINTS_TO_PIXELS Converts a rect in points to pixels */ -#define CC_RECT_POINTS_TO_PIXELS(__points__) \ - CGRectMake( (__points__).origin.x * CC_CONTENT_SCALE_FACTOR(), (__points__).origin.y * CC_CONTENT_SCALE_FACTOR(), \ - (__points__).size.width * CC_CONTENT_SCALE_FACTOR(), (__points__).size.height * CC_CONTENT_SCALE_FACTOR() ) +#define CC_RECT_POINTS_TO_PIXELS(__rect_in_points_points__) \ + CGRectMake( (__rect_in_points_points__).origin.x * CC_CONTENT_SCALE_FACTOR(), (__rect_in_points_points__).origin.y * CC_CONTENT_SCALE_FACTOR(), \ + (__rect_in_points_points__).size.width * CC_CONTENT_SCALE_FACTOR(), (__rect_in_points_points__).size.height * CC_CONTENT_SCALE_FACTOR() ) + +/** @def CC_POINT_PIXELS_TO_POINTS + Converts a rect in pixels to points + */ +#define CC_POINT_PIXELS_TO_POINTS(__pixels__) \ +CGPointMake( (__pixels__).x / CC_CONTENT_SCALE_FACTOR(), (__pixels__).y / CC_CONTENT_SCALE_FACTOR()) + +/** @def CC_POINT_POINTS_TO_PIXELS + Converts a rect in points to pixels + */ +#define CC_POINT_POINTS_TO_PIXELS(__points__) \ +CGPointMake( (__points__).x * CC_CONTENT_SCALE_FACTOR(), (__points__).y * CC_CONTENT_SCALE_FACTOR()) + +/** @def CC_POINT_PIXELS_TO_POINTS + Converts a rect in pixels to points + */ +#define CC_SIZE_PIXELS_TO_POINTS(__size_in_pixels__) \ +CGSizeMake( (__size_in_pixels__).width / CC_CONTENT_SCALE_FACTOR(), (__size_in_pixels__).height / CC_CONTENT_SCALE_FACTOR()) + +/** @def CC_POINT_POINTS_TO_PIXELS + Converts a rect in points to pixels + */ +#define CC_SIZE_POINTS_TO_PIXELS(__size_in_points__) \ +CGSizeMake( (__size_in_points__).width * CC_CONTENT_SCALE_FACTOR(), (__size_in_points__).height * CC_CONTENT_SCALE_FACTOR()) -#else // retina disabled + +#elif defined(__CC_PLATFORM_MAC) /*****************************/ /** RETINA DISPLAY DISABLED **/ @@ -249,5 +272,82 @@ do { \ #define CC_CONTENT_SCALE_FACTOR() 1 #define CC_RECT_PIXELS_TO_POINTS(__pixels__) __pixels__ #define CC_RECT_POINTS_TO_PIXELS(__points__) __points__ +#define CC_SIZE_PIXELS_TO_POINTS(__pixels__) __pixels__ +#define CC_SIZE_POINTS_TO_PIXELS(__points__) __points__ +#define CC_POINT_PIXELS_TO_POINTS(__pixels__) __pixels__ +#define CC_POINT_POINTS_TO_PIXELS(__points__) __points__ + + +#endif // __CC_PLATFORM_MAC + + +/**********************/ +/** Profiling Macros **/ +/**********************/ +#if CC_ENABLE_PROFILERS + +#define CC_PROFILER_DISPLAY_TIMERS() [[CCProfiler sharedProfiler] displayTimers] +#define CC_PROFILER_PURGE_ALL() [[CCProfiler sharedProfiler] releaseAllTimers] + +#define CC_PROFILER_START(__name__) CCProfilingBeginTimingBlock(__name__) +#define CC_PROFILER_STOP(__name__) CCProfilingEndTimingBlock(__name__) +#define CC_PROFILER_RESET(__name__) CCProfilingResetTimingBlock(__name__) + +#define CC_PROFILER_START_CATEGORY(__cat__, __name__) do{ if(__cat__) CCProfilingBeginTimingBlock(__name__); } while(0) +#define CC_PROFILER_STOP_CATEGORY(__cat__, __name__) do{ if(__cat__) CCProfilingEndTimingBlock(__name__); } while(0) +#define CC_PROFILER_RESET_CATEGORY(__cat__, __name__) do{ if(__cat__) CCProfilingResetTimingBlock(__name__); } while(0) + +#define CC_PROFILER_START_INSTANCE(__id__, __name__) do{ CCProfilingBeginTimingBlock( [NSString stringWithFormat:@"%08X - %@", __id__, __name__] ); } while(0) +#define CC_PROFILER_STOP_INSTANCE(__id__, __name__) do{ CCProfilingEndTimingBlock( [NSString stringWithFormat:@"%08X - %@", __id__, __name__] ); } while(0) +#define CC_PROFILER_RESET_INSTANCE(__id__, __name__) do{ CCProfilingResetTimingBlock( [NSString stringWithFormat:@"%08X - %@", __id__, __name__] ); } while(0) -#endif // CC_IS_RETINA_DISPLAY_SUPPORTED + +#else + +#define CC_PROFILER_DISPLAY_TIMERS() do {} while (0) +#define CC_PROFILER_PURGE_ALL() do {} while (0) + +#define CC_PROFILER_START(__name__) do {} while (0) +#define CC_PROFILER_STOP(__name__) do {} while (0) +#define CC_PROFILER_RESET(__name__) do {} while (0) + +#define CC_PROFILER_START_CATEGORY(__cat__, __name__) do {} while(0) +#define CC_PROFILER_STOP_CATEGORY(__cat__, __name__) do {} while(0) +#define CC_PROFILER_RESET_CATEGORY(__cat__, __name__) do {} while(0) + +#define CC_PROFILER_START_INSTANCE(__id__, __name__) do {} while(0) +#define CC_PROFILER_STOP_INSTANCE(__id__, __name__) do {} while(0) +#define CC_PROFILER_RESET_INSTANCE(__id__, __name__) do {} while(0) + +#endif + +/*****************/ +/** ARC Macros **/ +/*****************/ +#if defined(__has_feature) && __has_feature(objc_arc) +// ARC (used for inline functions) +#define CC_ARC_RETAIN(value) value +#define CC_ARC_RELEASE(value) value = 0 +#define CC_ARC_UNSAFE_RETAINED __unsafe_unretained + +#else +// No ARC +#define CC_ARC_RETAIN(value) [value retain] +#define CC_ARC_RELEASE(value) [value release] +#define CC_ARC_UNSAFE_RETAINED +#endif + +/** @def CC_INCREMENT_GL_DRAWS_BY_ONE + Increments the GL Draws counts by one. + The number of calls per frame are displayed on the screen when the CCDirector's stats are enabled. + */ +extern NSUInteger __ccNumberOfDraws; +#define CC_INCREMENT_GL_DRAWS(__n__) __ccNumberOfDraws += __n__ + +/*******************/ +/** Notifications **/ +/*******************/ +/** @def CCAnimationFrameDisplayedNotification + Notification name when a CCSpriteFrame is displayed + */ +#define CCAnimationFrameDisplayedNotification @"CCAnimationFrameDisplayedNotification" diff --git a/cocos2d/cocos2d/ccShader_PositionColor_frag.h b/cocos2d/cocos2d/ccShader_PositionColor_frag.h new file mode 100755 index 0000000..2c28489 --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionColor_frag.h @@ -0,0 +1,12 @@ +" \n\ +#ifdef GL_ES \n\ +precision lowp float; \n\ +#endif \n\ + \n\ +varying vec4 v_fragmentColor; \n\ + \n\ +void main() \n\ +{ \n\ + gl_FragColor = v_fragmentColor; \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_PositionColor_vert.h b/cocos2d/cocos2d/ccShader_PositionColor_vert.h new file mode 100755 index 0000000..815e161 --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionColor_vert.h @@ -0,0 +1,17 @@ +" \n\ +attribute vec4 a_position; \n\ +attribute vec4 a_color; \n\ +uniform mat4 u_MVPMatrix; \n\ + \n\ +#ifdef GL_ES \n\ +varying lowp vec4 v_fragmentColor; \n\ +#else \n\ +varying vec4 v_fragmentColor; \n\ +#endif \n\ + \n\ +void main() \n\ +{ \n\ + gl_Position = u_MVPMatrix * a_position; \n\ + v_fragmentColor = a_color; \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_PositionTextureA8Color_frag.h b/cocos2d/cocos2d/ccShader_PositionTextureA8Color_frag.h new file mode 100755 index 0000000..0b6cd10 --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionTextureA8Color_frag.h @@ -0,0 +1,16 @@ +" \n\ +#ifdef GL_ES \n\ +precision lowp float; \n\ +#endif \n\ + \n\ +varying vec4 v_fragmentColor; \n\ +varying vec2 v_texCoord; \n\ +uniform sampler2D u_texture; \n\ + \n\ +void main() \n\ +{ \n\ + gl_FragColor = vec4( v_fragmentColor.rgb, // RGB from uniform \n\ + v_fragmentColor.a * texture2D(u_texture, v_texCoord).a // A from texture & uniform \n\ + ); \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_PositionTextureA8Color_vert.h b/cocos2d/cocos2d/ccShader_PositionTextureA8Color_vert.h new file mode 100755 index 0000000..b844932 --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionTextureA8Color_vert.h @@ -0,0 +1,21 @@ +" \n\ +attribute vec4 a_position; \n\ +attribute vec2 a_texCoord; \n\ +attribute vec4 a_color; \n\ +uniform mat4 u_MVPMatrix; \n\ + \n\ +#ifdef GL_ES \n\ +varying lowp vec4 v_fragmentColor; \n\ +varying mediump vec2 v_texCoord; \n\ +#else \n\ +varying vec4 v_fragmentColor; \n\ +varying vec2 v_texCoord; \n\ +#endif \n\ + \n\ +void main() \n\ +{ \n\ + gl_Position = u_MVPMatrix * a_position; \n\ + v_fragmentColor = a_color; \n\ + v_texCoord = a_texCoord; \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_PositionTextureColorAlphaTest_frag.h b/cocos2d/cocos2d/ccShader_PositionTextureColorAlphaTest_frag.h new file mode 100755 index 0000000..253c5e8 --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionTextureColorAlphaTest_frag.h @@ -0,0 +1,23 @@ +" \n\ +#ifdef GL_ES \n\ +precision lowp float; \n\ +#endif \n\ + \n\ +varying vec4 v_fragmentColor; \n\ +varying vec2 v_texCoord; \n\ +uniform sampler2D u_texture; \n\ +uniform float u_alpha_value; \n\ + \n\ +void main() \n\ +{ \n\ + vec4 texColor = texture2D(u_texture, v_texCoord); \n\ + \n\ + // mimic: glAlphaFunc(GL_GREATER) \n\ + // pass if ( incoming_pixel >= u_alpha_value ) => fail if incoming_pixel < u_alpha_value \n\ + \n\ + if ( texColor.a <= u_alpha_value ) \n\ + discard; \n\ + \n\ + gl_FragColor = texColor * v_fragmentColor; \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_PositionTextureColor_frag.h b/cocos2d/cocos2d/ccShader_PositionTextureColor_frag.h new file mode 100755 index 0000000..aba7e21 --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionTextureColor_frag.h @@ -0,0 +1,14 @@ +" \n\ +#ifdef GL_ES \n\ +precision lowp float; \n\ +#endif \n\ + \n\ +varying vec4 v_fragmentColor; \n\ +varying vec2 v_texCoord; \n\ +uniform sampler2D u_texture; \n\ + \n\ +void main() \n\ +{ \n\ + gl_FragColor = v_fragmentColor * texture2D(u_texture, v_texCoord); \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_PositionTextureColor_vert.h b/cocos2d/cocos2d/ccShader_PositionTextureColor_vert.h new file mode 100755 index 0000000..cf2361d --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionTextureColor_vert.h @@ -0,0 +1,22 @@ +" \n\ +attribute vec4 a_position; \n\ +attribute vec2 a_texCoord; \n\ +attribute vec4 a_color; \n\ + \n\ +uniform mat4 u_MVPMatrix; \n\ + \n\ +#ifdef GL_ES \n\ +varying lowp vec4 v_fragmentColor; \n\ +varying mediump vec2 v_texCoord; \n\ +#else \n\ +varying vec4 v_fragmentColor; \n\ +varying vec2 v_texCoord; \n\ +#endif \n\ + \n\ +void main() \n\ +{ \n\ + gl_Position = u_MVPMatrix * a_position; \n\ + v_fragmentColor = a_color; \n\ + v_texCoord = a_texCoord; \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_PositionTexture_frag.h b/cocos2d/cocos2d/ccShader_PositionTexture_frag.h new file mode 100755 index 0000000..d7af10e --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionTexture_frag.h @@ -0,0 +1,13 @@ +" \n\ +#ifdef GL_ES \n\ +precision lowp float; \n\ +#endif \n\ + \n\ +varying vec2 v_texCoord; \n\ +uniform sampler2D u_texture; \n\ + \n\ +void main() \n\ +{ \n\ + gl_FragColor = texture2D(u_texture, v_texCoord); \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_PositionTexture_uColor_frag.h b/cocos2d/cocos2d/ccShader_PositionTexture_uColor_frag.h new file mode 100755 index 0000000..019f2ef --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionTexture_uColor_frag.h @@ -0,0 +1,16 @@ +" \n\ +#ifdef GL_ES \n\ +precision lowp float; \n\ +#endif \n\ + \n\ +uniform vec4 u_color; \n\ + \n\ +varying vec2 v_texCoord; \n\ + \n\ +uniform sampler2D u_texture; \n\ + \n\ +void main() \n\ +{ \n\ + gl_FragColor = texture2D(u_texture, v_texCoord) * u_color; \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_PositionTexture_uColor_vert.h b/cocos2d/cocos2d/ccShader_PositionTexture_uColor_vert.h new file mode 100755 index 0000000..ca36ff0 --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionTexture_uColor_vert.h @@ -0,0 +1,18 @@ +" \n\ +attribute vec4 a_position; \n\ +attribute vec2 a_texCoord; \n\ + \n\ +uniform mat4 u_MVPMatrix; \n\ + \n\ +#ifdef GL_ES \n\ +varying mediump vec2 v_texCoord; \n\ +#else \n\ +varying vec2 v_texCoord; \n\ +#endif \n\ + \n\ +void main() \n\ +{ \n\ + gl_Position = u_MVPMatrix * a_position; \n\ + v_texCoord = a_texCoord; \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_PositionTexture_vert.h b/cocos2d/cocos2d/ccShader_PositionTexture_vert.h new file mode 100755 index 0000000..98ceab5 --- /dev/null +++ b/cocos2d/cocos2d/ccShader_PositionTexture_vert.h @@ -0,0 +1,17 @@ +" \n\ +attribute vec4 a_position; \n\ +attribute vec2 a_texCoord; \n\ +uniform mat4 u_MVPMatrix; \n\ + \n\ +#ifdef GL_ES \n\ +varying mediump vec2 v_texCoord; \n\ +#else \n\ +varying vec2 v_texCoord; \n\ +#endif \n\ + \n\ +void main() \n\ +{ \n\ + gl_Position = u_MVPMatrix * a_position; \n\ + v_texCoord = a_texCoord; \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_Position_uColor_frag.h b/cocos2d/cocos2d/ccShader_Position_uColor_frag.h new file mode 100755 index 0000000..48e5ae6 --- /dev/null +++ b/cocos2d/cocos2d/ccShader_Position_uColor_frag.h @@ -0,0 +1,12 @@ +" \n\ +#ifdef GL_ES \n\ +precision lowp float; \n\ +#endif \n\ + \n\ +varying vec4 v_fragmentColor; \n\ + \n\ +void main() \n\ +{ \n\ + gl_FragColor = v_fragmentColor; \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShader_Position_uColor_vert.h b/cocos2d/cocos2d/ccShader_Position_uColor_vert.h new file mode 100755 index 0000000..9f1ba38 --- /dev/null +++ b/cocos2d/cocos2d/ccShader_Position_uColor_vert.h @@ -0,0 +1,19 @@ +" \n\ +attribute vec4 a_position; \n\ +uniform mat4 u_MVPMatrix; \n\ +uniform vec4 u_color; \n\ +uniform float u_pointSize; \n\ + \n\ +#ifdef GL_ES \n\ +varying lowp vec4 v_fragmentColor; \n\ +#else \n\ +varying vec4 v_fragmentColor; \n\ +#endif \n\ + \n\ +void main() \n\ +{ \n\ + gl_Position = u_MVPMatrix * a_position; \n\ + gl_PointSize = u_pointSize; \n\ + v_fragmentColor = u_color; \n\ +} \n\ +"; diff --git a/cocos2d/cocos2d/ccShaders.h b/cocos2d/cocos2d/ccShaders.h new file mode 100755 index 0000000..84b113f --- /dev/null +++ b/cocos2d/cocos2d/ccShaders.h @@ -0,0 +1,45 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2012 Zynga Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import "Platforms/CCGL.h" + +extern const GLchar * ccPosition_uColor_frag; +extern const GLchar * ccPosition_uColor_vert; + +extern const GLchar * ccPositionColor_frag; +extern const GLchar * ccPositionColor_vert; + +extern const GLchar * ccPositionTexture_frag; +extern const GLchar * ccPositionTexture_vert; + +extern const GLchar * ccPositionTextureA8Color_frag; +extern const GLchar * ccPositionTextureA8Color_vert; + +extern const GLchar * ccPositionTextureColor_frag; +extern const GLchar * ccPositionTextureColor_vert; + +extern const GLchar * ccPositionTextureColorAlphaTest_frag; + +extern const GLchar * ccPositionTexture_uColor_frag; +extern const GLchar * ccPositionTexture_uColor_vert; diff --git a/cocos2d/cocos2d/ccShaders.m b/cocos2d/cocos2d/ccShaders.m new file mode 100755 index 0000000..36ca949 --- /dev/null +++ b/cocos2d/cocos2d/ccShaders.m @@ -0,0 +1,65 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2012 Zynga Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#import "ccShaders.h" + +// +const GLchar * ccPosition_uColor_frag = +#import "ccShader_Position_uColor_frag.h" +const GLchar * ccPosition_uColor_vert = +#import "ccShader_Position_uColor_vert.h" + +// +const GLchar * ccPositionColor_frag = +#import "ccShader_PositionColor_frag.h" +const GLchar * ccPositionColor_vert = +#import "ccShader_PositionColor_vert.h" + +// +const GLchar * ccPositionTexture_frag = +#import "ccShader_PositionTexture_frag.h" +const GLchar * ccPositionTexture_vert = +#import "ccShader_PositionTexture_vert.h" + +// +const GLchar * ccPositionTextureA8Color_frag = +#import "ccShader_PositionTextureA8Color_frag.h" +const GLchar * ccPositionTextureA8Color_vert = +#import "ccShader_PositionTextureA8Color_vert.h" + +// +const GLchar * ccPositionTextureColor_frag = +#import "ccShader_PositionTextureColor_frag.h" +const GLchar * ccPositionTextureColor_vert = +#import "ccShader_PositionTextureColor_vert.h" + +// +const GLchar * ccPositionTextureColorAlphaTest_frag = +#import "ccShader_PositionTextureColorAlphaTest_frag.h" + +// +const GLchar * ccPositionTexture_uColor_frag = +#import "ccShader_PositionTexture_uColor_frag.h" +const GLchar * ccPositionTexture_uColor_vert = +#import "ccShader_PositionTexture_uColor_vert.h" diff --git a/cocos2d/cocos2d/ccTypes.h b/cocos2d/cocos2d/ccTypes.h old mode 100644 new mode 100755 index 46917b3..730aa93 --- a/cocos2d/cocos2d/ccTypes.h +++ b/cocos2d/cocos2d/ccTypes.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -29,10 +29,10 @@ cocos2d (cc) types */ -#import #import +#import "ccMacros.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import // CGPoint #endif @@ -97,12 +97,19 @@ ccc4(const GLubyte r, const GLubyte g, const GLubyte b, const GLubyte o) /** RGBA color composed of 4 floats @since v0.8 */ -typedef struct _ccColor4F { +struct ccColor4F { GLfloat r; GLfloat g; GLfloat b; GLfloat a; -} ccColor4F; +}; +typedef struct ccColor4F ccColor4F; + +//! helper that creates a ccColor4f type +static inline ccColor4F ccc4f(const GLfloat r, const GLfloat g, const GLfloat b, const GLfloat a) +{ + return (ccColor4F){r, g, b, a}; +} /** Returns a ccColor4F from a ccColor3B. Alpha will be 1. @since v0.99.1 @@ -146,7 +153,7 @@ typedef struct _ccVertex3F GLfloat y; GLfloat z; } ccVertex3F; - + /** A texcoord composed of 2 floats: u, y @since v0.8 */ @@ -155,7 +162,7 @@ typedef struct _ccTex2F { GLfloat v; } ccTex2F; - + //! Point Sprite component typedef struct _ccPointSprite { @@ -218,6 +225,30 @@ typedef struct _ccV2F_C4F_T2F ccTex2F texCoords; } ccV2F_C4F_T2F; +//! a Point with a vertex point, a tex coord point and a color 4F +typedef struct _ccV3F_C4F_T2F +{ + //! vertices (3F) + ccVertex3F vertices; + //! colors (4F) + ccColor4F colors; + //! tex coords (2F) + ccTex2F texCoords; +} ccV3F_C4F_T2F; + +//! 4 ccV3F_C4F_T2F +typedef struct _ccV3F_C4F_T2F_Quad +{ + //! top left + ccV3F_C4F_T2F tl; + //! bottom left + ccV3F_C4F_T2F bl; + //! top right + ccV3F_C4F_T2F tr; + //! bottom right + ccV3F_C4F_T2F br; +} ccV3F_C4F_T2F_Quad; + //! a Point with a vertex point, a tex coord point and a color 4B typedef struct _ccV3F_C4B_T2F { @@ -281,7 +312,63 @@ typedef struct _ccBlendFunc GLenum dst; } ccBlendFunc; +//! ccResolutionType +typedef enum +{ + //! Unknonw resolution type + kCCResolutionUnknown, +#ifdef __CC_PLATFORM_IOS + //! iPhone resolution type + kCCResolutioniPhone, + //! RetinaDisplay resolution type + kCCResolutioniPhoneRetinaDisplay, + //! iPad resolution type + kCCResolutioniPad, + //! iPad Retina Display resolution type + kCCResolutioniPadRetinaDisplay, + +#elif defined(__CC_PLATFORM_MAC) + //! Mac resolution type + kCCResolutionMac, + + //! Mac RetinaDisplay resolution type (???) + kCCResolutionMacRetinaDisplay, +#endif // platform + +} ccResolutionType; + +// XXX: If any of these enums are edited and/or reordered, udpate CCTexture2D.m +//! Vertical text alignment type +typedef enum +{ + kCCVerticalTextAlignmentTop, + kCCVerticalTextAlignmentCenter, + kCCVerticalTextAlignmentBottom, +} CCVerticalTextAlignment; + +// XXX: If any of these enums are edited and/or reordered, udpate CCTexture2D.m +//! Horizontal text alignment type +typedef enum +{ + kCCTextAlignmentLeft, + kCCTextAlignmentCenter, + kCCTextAlignmentRight, +} CCTextAlignment; + +// XXX: If any of these enums are edited and/or reordered, udpate CCTexture2D.m +//! Line break modes +typedef enum { + kCCLineBreakModeWordWrap, + kCCLineBreakModeCharacterWrap, + kCCLineBreakModeClip, + kCCLineBreakModeHeadTruncation, + kCCLineBreakModeTailTruncation, + kCCLineBreakModeMiddleTruncation +} CCLineBreakMode; + //! delta time type //! if you want more resolution redefine it as a double typedef float ccTime; //typedef double ccTime; + +typedef float ccMat4[16]; diff --git a/cocos2d/cocos2d/cocos2d.h b/cocos2d/cocos2d/cocos2d.h old mode 100644 new mode 100755 index 600fb00..87fda39 --- a/cocos2d/cocos2d/cocos2d.h +++ b/cocos2d/cocos2d/cocos2d.h @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -39,10 +39,9 @@ */ // 0x00 HI ME LO -// 00 01 00 01 -#define COCOS2D_VERSION 0x00010001 +// 00 02 00 00 +#define COCOS2D_VERSION 0x00020000 -#import // // all cocos2d include files @@ -62,6 +61,7 @@ #import "CCActionGrid.h" #import "CCActionProgressTimer.h" #import "CCActionPageTurn3D.h" +#import "CCActionCatmullRom.h" #import "CCAnimation.h" #import "CCAnimationCache.h" @@ -75,9 +75,9 @@ #import "CCLabelAtlas.h" #import "CCParticleSystem.h" -#import "CCParticleSystemPoint.h" #import "CCParticleSystemQuad.h" #import "CCParticleExamples.h" +#import "CCParticleBatchNode.h" #import "CCTexture2D.h" #import "CCTexturePVR.h" @@ -86,7 +86,7 @@ #import "CCTransition.h" #import "CCTransitionPageTurn.h" -#import "CCTransitionRadial.h" +#import "CCTransitionProgress.h" #import "CCTMXTiledMap.h" #import "CCTMXLayer.h" @@ -100,10 +100,10 @@ #import "CCDrawingPrimitives.h" #import "CCScene.h" #import "CCScheduler.h" -#import "CCBlockSupport.h" #import "CCCamera.h" #import "CCProtocols.h" #import "CCNode.h" +#import "CCNode+Debug.h" #import "CCDirector.h" #import "CCAtlasNode.h" #import "CCGrabber.h" @@ -113,27 +113,39 @@ #import "CCMotionStreak.h" #import "CCConfiguration.h" +// Shaders +#import "CCGLProgram.h" +#import "ccGLStateCache.h" +#import "CCShaderCache.h" +#import "ccShaders.h" + // // cocos2d macros // #import "ccTypes.h" #import "ccMacros.h" +// +// Deprecated methods/classes/functions since v1.0 +// +#import "ccDeprecated.h" // Platform common #import "Platforms/CCGL.h" #import "Platforms/CCNS.h" -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #import "Platforms/iOS/CCTouchDispatcher.h" #import "Platforms/iOS/CCTouchDelegateProtocol.h" #import "Platforms/iOS/CCTouchHandler.h" -#import "Platforms/iOS/EAGLView.h" +#import "Platforms/iOS/CCGLView.h" #import "Platforms/iOS/CCDirectorIOS.h" -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) -#import "Platforms/Mac/MacGLView.h" +#elif defined(__CC_PLATFORM_MAC) +#import "Platforms/Mac/CCGLView.h" #import "Platforms/Mac/CCDirectorMac.h" +#import "Platforms/Mac/CCWindow.h" +#import "Platforms/Mac/CCEventDispatcher.h" #endif // @@ -145,16 +157,21 @@ #import "Support/ccCArray.h" #import "Support/CCArray.h" #import "Support/ccUtils.h" - -#if CC_ENABLE_PROFILERS +#import "Support/TransformUtils.h" #import "Support/CCProfiling.h" -#endif // CC_ENABLE_PROFILERS + +// +// external +// +#import "kazmath/kazmath.h" +#import "kazmath/GL/matrix.h" + // free functions NSString * cocos2dVersion(void); -#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#ifdef __CC_PLATFORM_IOS #ifndef __IPHONE_4_0 #error "If you are targeting iPad, you should set BASE SDK = 4.0 (or 4.1, or 4.2), and set the 'iOS deploy target' = 3.2" #endif diff --git a/cocos2d/cocos2d/cocos2d.m b/cocos2d/cocos2d/cocos2d.m old mode 100644 new mode 100755 index 2b17055..db52e21 --- a/cocos2d/cocos2d/cocos2d.m +++ b/cocos2d/cocos2d/cocos2d.m @@ -3,17 +3,17 @@ * * Copyright (c) 2008-2010 Ricardo Quesada * Copyright (c) 2011 Zynga Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -26,9 +26,9 @@ #import #import "cocos2d.h" -static NSString *version = @"cocos2d v1.0.1"; +static NSString *version = @"cocos2d v2.0.0"; NSString *cocos2dVersion() -{ +{ return version; } From 9dda815d03d0e839421f60bf5c32508cf3b2782d Mon Sep 17 00:00:00 2001 From: Greg Harding Date: Fri, 7 Sep 2012 01:30:57 +1200 Subject: [PATCH 2/3] updated ObjectALDemo to use cocos2d 2.0 --- ObjectAL.xcodeproj/project.pbxproj | 1829 +++++++++++------ ObjectALDemo/AppDelegate.h | 16 +- ObjectALDemo/AppDelegate.m | 229 +-- ObjectALDemo/Demos/AudioTrackDemo.m | 1 + ObjectALDemo/GameConfig.h | 44 - ObjectALDemo/IntroLayer.h | 20 + ObjectALDemo/IntroLayer.m | 64 + ObjectALDemo/MainScene.h | 13 +- ObjectALDemo/MainScene.m | 11 +- .../Resources/Default-Landscape~ipad.png | Bin 0 -> 434094 bytes ObjectALDemo/Resources/Default.png | Bin 27477 -> 37342 bytes ObjectALDemo/Resources/Icon-72.png | Bin 0 -> 7112 bytes ObjectALDemo/Resources/Icon-Small-50.png | Bin 0 -> 4007 bytes ObjectALDemo/Resources/Icon-Small.png | Bin 0 -> 1781 bytes ObjectALDemo/Resources/Icon-Small@2x.png | Bin 0 -> 5054 bytes ObjectALDemo/Resources/Icon.png | Bin 3641 -> 4903 bytes ObjectALDemo/Resources/Icon@2x.png | Bin 0 -> 13076 bytes ObjectALDemo/Resources/Info.plist | 18 +- .../{ => ObjectALDemo}/AssetSources.txt | 0 .../Resources/{ => ObjectALDemo}/Back.png | Bin .../Resources/{ => ObjectALDemo}/ColdFunk.caf | Bin .../Resources/{ => ObjectALDemo}/Exit.png | Bin .../Resources/{ => ObjectALDemo}/Ganymede.png | Bin .../{ => ObjectALDemo}/HappyAlley.caf | Bin .../Resources/{ => ObjectALDemo}/Jupiter.png | Bin .../Resources/{ => ObjectALDemo}/Next.png | Bin .../Resources/{ => ObjectALDemo}/Pew.caf | Bin .../{ => ObjectALDemo}/PlanetKiller.mp3 | Bin .../Resources/{ => ObjectALDemo}/Pow.caf | Bin .../{ => ObjectALDemo}/RocketShip.png | Bin .../Resources/{ => ObjectALDemo}/panel-bg.png | Bin .../{ => ObjectALDemo}/panel-lamp-off.png | Bin .../{ => ObjectALDemo}/panel-lamp-on.png | Bin .../{ => ObjectALDemo}/panel-slider-knob.png | Bin .../panel-slider-track-long.png | Bin .../{ => ObjectALDemo}/panel-slider-track.png | Bin .../{ => ObjectALDemo}/panel-trim-horiz.png | Bin .../{ => ObjectALDemo}/panel-trim-vert.png | Bin .../Resources/{ => ObjectALDemo}/vu-face.png | Bin .../{ => ObjectALDemo}/vu-needle.png | Bin .../Resources/{ => ObjectALDemo}/vu-shell.png | Bin ObjectALDemo/Resources/fps_images-hd.png | Bin 0 -> 23443 bytes ObjectALDemo/Resources/fps_images-ipadhd.png | Bin 0 -> 23443 bytes ObjectALDemo/Resources/fps_images.png | Bin 6203 -> 6953 bytes ObjectALDemo/Resources/iTunesArtwork | Bin 0 -> 71770 bytes ObjectALDemo/RootViewController.h | 15 - ObjectALDemo/RootViewController.m | 152 -- ObjectALDemo/Support/Button.m | 2 +- ObjectALDemo/Support/Slider.m | 2 +- ObjectALDemo/Support/TouchableNode.m | 6 +- ObjectALDemo/main.m | 7 +- cocos2d/LICENSE_Kazmath.txt | 33 + .../kazmath/include/kazmath/GL/mat4stack.h | 51 + cocos2d/kazmath/include/kazmath/GL/matrix.h | 58 + cocos2d/kazmath/include/kazmath/aabb.h | 53 + cocos2d/kazmath/include/kazmath/kazmath.h | 39 + cocos2d/kazmath/include/kazmath/mat3.h | 75 + cocos2d/kazmath/include/kazmath/mat4.h | 93 + .../include/kazmath/neon_matrix_impl.h | 41 + cocos2d/kazmath/include/kazmath/plane.h | 70 + cocos2d/kazmath/include/kazmath/quaternion.h | 113 + cocos2d/kazmath/include/kazmath/ray2.h | 50 + cocos2d/kazmath/include/kazmath/utility.h | 74 + cocos2d/kazmath/include/kazmath/vec2.h | 64 + cocos2d/kazmath/include/kazmath/vec3.h | 68 + cocos2d/kazmath/include/kazmath/vec4.h | 68 + cocos2d/kazmath/src/CMakeLists.txt | 14 + cocos2d/kazmath/src/ChangeLog | 738 +++++++ cocos2d/kazmath/src/GL/mat4stack.c | 74 + cocos2d/kazmath/src/GL/matrix.c | 191 ++ cocos2d/kazmath/src/aabb.c | 63 + cocos2d/kazmath/src/mat3.c | 372 ++++ cocos2d/kazmath/src/mat4.c | 789 +++++++ cocos2d/kazmath/src/neon_matrix_impl.c | 97 + cocos2d/kazmath/src/plane.c | 175 ++ cocos2d/kazmath/src/quaternion.c | 582 ++++++ cocos2d/kazmath/src/ray2.c | 184 ++ cocos2d/kazmath/src/utility.c | 59 + cocos2d/kazmath/src/vec2.c | 118 ++ cocos2d/kazmath/src/vec3.c | 310 +++ cocos2d/kazmath/src/vec4.c | 154 ++ 81 files changed, 6252 insertions(+), 1047 deletions(-) mode change 100755 => 100644 ObjectALDemo/AppDelegate.h mode change 100755 => 100644 ObjectALDemo/AppDelegate.m delete mode 100644 ObjectALDemo/GameConfig.h create mode 100644 ObjectALDemo/IntroLayer.h create mode 100644 ObjectALDemo/IntroLayer.m mode change 100755 => 100644 ObjectALDemo/MainScene.m create mode 100644 ObjectALDemo/Resources/Default-Landscape~ipad.png mode change 100755 => 100644 ObjectALDemo/Resources/Default.png create mode 100644 ObjectALDemo/Resources/Icon-72.png create mode 100644 ObjectALDemo/Resources/Icon-Small-50.png create mode 100644 ObjectALDemo/Resources/Icon-Small.png create mode 100644 ObjectALDemo/Resources/Icon-Small@2x.png mode change 100755 => 100644 ObjectALDemo/Resources/Icon.png create mode 100644 ObjectALDemo/Resources/Icon@2x.png rename ObjectALDemo/Resources/{ => ObjectALDemo}/AssetSources.txt (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/Back.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/ColdFunk.caf (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/Exit.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/Ganymede.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/HappyAlley.caf (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/Jupiter.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/Next.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/Pew.caf (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/PlanetKiller.mp3 (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/Pow.caf (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/RocketShip.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/panel-bg.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/panel-lamp-off.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/panel-lamp-on.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/panel-slider-knob.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/panel-slider-track-long.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/panel-slider-track.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/panel-trim-horiz.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/panel-trim-vert.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/vu-face.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/vu-needle.png (100%) rename ObjectALDemo/Resources/{ => ObjectALDemo}/vu-shell.png (100%) create mode 100644 ObjectALDemo/Resources/fps_images-hd.png create mode 100644 ObjectALDemo/Resources/fps_images-ipadhd.png create mode 100644 ObjectALDemo/Resources/iTunesArtwork delete mode 100644 ObjectALDemo/RootViewController.h delete mode 100644 ObjectALDemo/RootViewController.m create mode 100755 cocos2d/LICENSE_Kazmath.txt create mode 100755 cocos2d/kazmath/include/kazmath/GL/mat4stack.h create mode 100755 cocos2d/kazmath/include/kazmath/GL/matrix.h create mode 100755 cocos2d/kazmath/include/kazmath/aabb.h create mode 100755 cocos2d/kazmath/include/kazmath/kazmath.h create mode 100755 cocos2d/kazmath/include/kazmath/mat3.h create mode 100755 cocos2d/kazmath/include/kazmath/mat4.h create mode 100755 cocos2d/kazmath/include/kazmath/neon_matrix_impl.h create mode 100755 cocos2d/kazmath/include/kazmath/plane.h create mode 100755 cocos2d/kazmath/include/kazmath/quaternion.h create mode 100755 cocos2d/kazmath/include/kazmath/ray2.h create mode 100755 cocos2d/kazmath/include/kazmath/utility.h create mode 100755 cocos2d/kazmath/include/kazmath/vec2.h create mode 100755 cocos2d/kazmath/include/kazmath/vec3.h create mode 100755 cocos2d/kazmath/include/kazmath/vec4.h create mode 100755 cocos2d/kazmath/src/CMakeLists.txt create mode 100755 cocos2d/kazmath/src/ChangeLog create mode 100755 cocos2d/kazmath/src/GL/mat4stack.c create mode 100755 cocos2d/kazmath/src/GL/matrix.c create mode 100755 cocos2d/kazmath/src/aabb.c create mode 100755 cocos2d/kazmath/src/mat3.c create mode 100755 cocos2d/kazmath/src/mat4.c create mode 100755 cocos2d/kazmath/src/neon_matrix_impl.c create mode 100755 cocos2d/kazmath/src/plane.c create mode 100755 cocos2d/kazmath/src/quaternion.c create mode 100755 cocos2d/kazmath/src/ray2.c create mode 100755 cocos2d/kazmath/src/utility.c create mode 100755 cocos2d/kazmath/src/vec2.c create mode 100755 cocos2d/kazmath/src/vec3.c create mode 100755 cocos2d/kazmath/src/vec4.c diff --git a/ObjectAL.xcodeproj/project.pbxproj b/ObjectAL.xcodeproj/project.pbxproj index a6db81e..1ae0c11 100644 --- a/ObjectAL.xcodeproj/project.pbxproj +++ b/ObjectAL.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 390E99371482C4BE002E14E7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398C16A4147D5533000EFEDF /* Foundation.framework */; }; 390E993D1482C4BE002E14E7 /* ObjectAL.m in Sources */ = {isa = PBXBuildFile; fileRef = 390E993C1482C4BE002E14E7 /* ObjectAL.m */; }; 390E99411482C4CB002E14E7 /* OALAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D8C2147D58F00010C4A2 /* OALAction.m */; }; 390E99421482C508002E14E7 /* OALActionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D8C4147D58F00010C4A2 /* OALActionManager.m */; }; @@ -35,88 +34,6 @@ 390E99591482C508002E14E7 /* OALTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D8FE147D58F00010C4A2 /* OALTools.m */; }; 390E995A1482C508002E14E7 /* OALSimpleAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D8D4147D58F00010C4A2 /* OALSimpleAudio.m */; }; 390E995B1482C52D002E14E7 /* libObjectAL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 390E99361482C4BE002E14E7 /* libObjectAL.a */; }; - 3914D74F147D57540010C4A2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398C16A4147D5533000EFEDF /* Foundation.framework */; }; - 3914D811147D57D60010C4A2 /* CCAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D75B147D57D60010C4A2 /* CCAction.m */; }; - 3914D813147D57D60010C4A2 /* CCActionCamera.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D75D147D57D60010C4A2 /* CCActionCamera.m */; }; - 3914D815147D57D60010C4A2 /* CCActionEase.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D75F147D57D60010C4A2 /* CCActionEase.m */; }; - 3914D817147D57D60010C4A2 /* CCActionGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D761147D57D60010C4A2 /* CCActionGrid.m */; }; - 3914D819147D57D60010C4A2 /* CCActionGrid3D.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D763147D57D60010C4A2 /* CCActionGrid3D.m */; }; - 3914D81B147D57D60010C4A2 /* CCActionInstant.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D765147D57D60010C4A2 /* CCActionInstant.m */; }; - 3914D81D147D57D60010C4A2 /* CCActionInterval.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D767147D57D60010C4A2 /* CCActionInterval.m */; }; - 3914D81F147D57D60010C4A2 /* CCActionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D769147D57D60010C4A2 /* CCActionManager.m */; }; - 3914D821147D57D60010C4A2 /* CCActionPageTurn3D.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D76B147D57D60010C4A2 /* CCActionPageTurn3D.m */; }; - 3914D823147D57D60010C4A2 /* CCActionProgressTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D76D147D57D60010C4A2 /* CCActionProgressTimer.m */; }; - 3914D825147D57D60010C4A2 /* CCActionTiledGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D76F147D57D60010C4A2 /* CCActionTiledGrid.m */; }; - 3914D827147D57D60010C4A2 /* CCActionTween.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D771147D57D60010C4A2 /* CCActionTween.m */; }; - 3914D829147D57D60010C4A2 /* CCAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D773147D57D60010C4A2 /* CCAnimation.m */; }; - 3914D82B147D57D60010C4A2 /* CCAnimationCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D775147D57D60010C4A2 /* CCAnimationCache.m */; }; - 3914D82D147D57D60010C4A2 /* CCAtlasNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D777147D57D60010C4A2 /* CCAtlasNode.m */; }; - 3914D82F147D57D60010C4A2 /* CCBlockSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D779147D57D60010C4A2 /* CCBlockSupport.m */; }; - 3914D831147D57D60010C4A2 /* CCCamera.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D77B147D57D60010C4A2 /* CCCamera.m */; }; - 3914D834147D57D60010C4A2 /* CCConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D77E147D57D60010C4A2 /* CCConfiguration.m */; }; - 3914D836147D57D60010C4A2 /* CCDirector.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D780147D57D60010C4A2 /* CCDirector.m */; }; - 3914D838147D57D60010C4A2 /* CCDrawingPrimitives.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D782147D57D60010C4A2 /* CCDrawingPrimitives.m */; }; - 3914D83A147D57D60010C4A2 /* CCGrabber.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D784147D57D60010C4A2 /* CCGrabber.m */; }; - 3914D83C147D57D60010C4A2 /* CCGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D786147D57D60010C4A2 /* CCGrid.m */; }; - 3914D83E147D57D60010C4A2 /* CCLabelAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D788147D57D60010C4A2 /* CCLabelAtlas.m */; }; - 3914D840147D57D60010C4A2 /* CCLabelBMFont.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D78A147D57D60010C4A2 /* CCLabelBMFont.m */; }; - 3914D842147D57D60010C4A2 /* CCLabelTTF.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D78C147D57D60010C4A2 /* CCLabelTTF.m */; }; - 3914D844147D57D60010C4A2 /* CCLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D78E147D57D60010C4A2 /* CCLayer.m */; }; - 3914D847147D57D60010C4A2 /* CCMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D791147D57D60010C4A2 /* CCMenu.m */; }; - 3914D849147D57D60010C4A2 /* CCMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D793147D57D60010C4A2 /* CCMenuItem.m */; }; - 3914D84B147D57D60010C4A2 /* CCMotionStreak.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D795147D57D60010C4A2 /* CCMotionStreak.m */; }; - 3914D84D147D57D60010C4A2 /* CCNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D797147D57D60010C4A2 /* CCNode.m */; }; - 3914D84F147D57D60010C4A2 /* CCParallaxNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D799147D57D60010C4A2 /* CCParallaxNode.m */; }; - 3914D851147D57D60010C4A2 /* CCParticleExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D79B147D57D60010C4A2 /* CCParticleExamples.m */; }; - 3914D853147D57D60010C4A2 /* CCParticleSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D79D147D57D60010C4A2 /* CCParticleSystem.m */; }; - 3914D855147D57D60010C4A2 /* CCParticleSystemPoint.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D79F147D57D60010C4A2 /* CCParticleSystemPoint.m */; }; - 3914D857147D57D60010C4A2 /* CCParticleSystemQuad.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7A1147D57D60010C4A2 /* CCParticleSystemQuad.m */; }; - 3914D859147D57D60010C4A2 /* CCProgressTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7A3147D57D60010C4A2 /* CCProgressTimer.m */; }; - 3914D85C147D57D60010C4A2 /* CCRenderTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7A6147D57D60010C4A2 /* CCRenderTexture.m */; }; - 3914D85E147D57D60010C4A2 /* CCRibbon.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7A8147D57D60010C4A2 /* CCRibbon.m */; }; - 3914D860147D57D60010C4A2 /* CCScene.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7AA147D57D60010C4A2 /* CCScene.m */; }; - 3914D862147D57D60010C4A2 /* CCScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7AC147D57D60010C4A2 /* CCScheduler.m */; }; - 3914D864147D57D60010C4A2 /* CCSprite.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7AE147D57D60010C4A2 /* CCSprite.m */; }; - 3914D866147D57D60010C4A2 /* CCSpriteBatchNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7B0147D57D60010C4A2 /* CCSpriteBatchNode.m */; }; - 3914D868147D57D60010C4A2 /* CCSpriteFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7B2147D57D60010C4A2 /* CCSpriteFrame.m */; }; - 3914D86A147D57D60010C4A2 /* CCSpriteFrameCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7B4147D57D60010C4A2 /* CCSpriteFrameCache.m */; }; - 3914D86C147D57D60010C4A2 /* CCTexture2D.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7B6147D57D60010C4A2 /* CCTexture2D.m */; }; - 3914D86E147D57D60010C4A2 /* CCTextureAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7B8147D57D60010C4A2 /* CCTextureAtlas.m */; }; - 3914D870147D57D60010C4A2 /* CCTextureCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7BA147D57D60010C4A2 /* CCTextureCache.m */; }; - 3914D872147D57D60010C4A2 /* CCTexturePVR.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7BC147D57D60010C4A2 /* CCTexturePVR.m */; }; - 3914D874147D57D60010C4A2 /* CCTileMapAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7BE147D57D60010C4A2 /* CCTileMapAtlas.m */; }; - 3914D876147D57D60010C4A2 /* CCTMXLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7C0147D57D60010C4A2 /* CCTMXLayer.m */; }; - 3914D878147D57D60010C4A2 /* CCTMXObjectGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7C2147D57D60010C4A2 /* CCTMXObjectGroup.m */; }; - 3914D87A147D57D60010C4A2 /* CCTMXTiledMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7C4147D57D60010C4A2 /* CCTMXTiledMap.m */; }; - 3914D87C147D57D60010C4A2 /* CCTMXXMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7C6147D57D60010C4A2 /* CCTMXXMLParser.m */; }; - 3914D87E147D57D60010C4A2 /* CCTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7C8147D57D60010C4A2 /* CCTransition.m */; }; - 3914D880147D57D60010C4A2 /* CCTransitionPageTurn.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7CA147D57D60010C4A2 /* CCTransitionPageTurn.m */; }; - 3914D882147D57D60010C4A2 /* CCTransitionRadial.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7CC147D57D60010C4A2 /* CCTransitionRadial.m */; }; - 3914D885147D57D60010C4A2 /* cocos2d.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7CF147D57D60010C4A2 /* cocos2d.m */; }; - 3914D889147D57D60010C4A2 /* CCDirectorIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7D5147D57D60010C4A2 /* CCDirectorIOS.m */; }; - 3914D88C147D57D60010C4A2 /* CCTouchDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7D8147D57D60010C4A2 /* CCTouchDispatcher.m */; }; - 3914D88E147D57D60010C4A2 /* CCTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7DA147D57D60010C4A2 /* CCTouchHandler.m */; }; - 3914D890147D57D60010C4A2 /* EAGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7DC147D57D60010C4A2 /* EAGLView.m */; }; - 3914D892147D57D60010C4A2 /* ES1Renderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7DE147D57D60010C4A2 /* ES1Renderer.m */; }; - 3914D894147D57D60010C4A2 /* glu.c in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7E0147D57D60010C4A2 /* glu.c */; }; - 3914D897147D57D60010C4A2 /* CCDirectorMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7E4147D57D60010C4A2 /* CCDirectorMac.m */; }; - 3914D899147D57D60010C4A2 /* CCEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7E6147D57D60010C4A2 /* CCEventDispatcher.m */; }; - 3914D89B147D57D60010C4A2 /* MacGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7E8147D57D60010C4A2 /* MacGLView.m */; }; - 3914D89D147D57D60010C4A2 /* MacWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7EA147D57D60010C4A2 /* MacWindow.m */; }; - 3914D89E147D57D60010C4A2 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7EC147D57D60010C4A2 /* base64.c */; }; - 3914D8A1147D57D60010C4A2 /* CCArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7EF147D57D60010C4A2 /* CCArray.m */; }; - 3914D8A4147D57D60010C4A2 /* CCFileUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7F2147D57D60010C4A2 /* CCFileUtils.m */; }; - 3914D8A6147D57D60010C4A2 /* CCProfiling.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7F4147D57D60010C4A2 /* CCProfiling.m */; }; - 3914D8A7147D57D60010C4A2 /* ccUtils.c in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7F5147D57D60010C4A2 /* ccUtils.c */; }; - 3914D8AA147D57D60010C4A2 /* CGPointExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7F8147D57D60010C4A2 /* CGPointExtension.m */; }; - 3914D8AD147D57D60010C4A2 /* TGAlib.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7FB147D57D60010C4A2 /* TGAlib.m */; }; - 3914D8AF147D57D60010C4A2 /* TransformUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D7FD147D57D60010C4A2 /* TransformUtils.m */; }; - 3914D8B3147D57D60010C4A2 /* ZipUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D801147D57D60010C4A2 /* ZipUtils.m */; }; - 3914D8B5147D57D60010C4A2 /* FontLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D804147D57D60010C4A2 /* FontLabel.m */; }; - 3914D8B7147D57D60010C4A2 /* FontLabelStringDrawing.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D806147D57D60010C4A2 /* FontLabelStringDrawing.m */; }; - 3914D8B9147D57D60010C4A2 /* FontManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D808147D57D60010C4A2 /* FontManager.m */; }; - 3914D8BB147D57D60010C4A2 /* ZAttributedString.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D80A147D57D60010C4A2 /* ZAttributedString.m */; }; - 3914D8BE147D57D60010C4A2 /* ZFont.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D80D147D57D60010C4A2 /* ZFont.m */; }; 3914D8BF147D57FE0010C4A2 /* libcocos2d.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3914D74E147D57540010C4A2 /* libcocos2d.a */; }; 3914D996147D5B570010C4A2 /* AudioSessionDemo.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D967147D5B570010C4A2 /* AudioSessionDemo.m */; }; 3914D998147D5B570010C4A2 /* AudioTrackDemo.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D969147D5B570010C4A2 /* AudioTrackDemo.m */; }; @@ -141,46 +58,275 @@ 3914D9BE147D5B570010C4A2 /* TargetedAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D990147D5B570010C4A2 /* TargetedAction.m */; }; 3914D9C0147D5B570010C4A2 /* TouchableNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D992147D5B570010C4A2 /* TouchableNode.m */; }; 3914D9C2147D5B570010C4A2 /* VUMeter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3914D994147D5B570010C4A2 /* VUMeter.m */; }; - 3914D9D9147D5BDF0010C4A2 /* Back.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9C3147D5BDF0010C4A2 /* Back.png */; }; - 3914D9DA147D5BDF0010C4A2 /* ColdFunk.caf in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9C4147D5BDF0010C4A2 /* ColdFunk.caf */; }; - 3914D9DB147D5BDF0010C4A2 /* Exit.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9C5147D5BDF0010C4A2 /* Exit.png */; }; - 3914D9DC147D5BDF0010C4A2 /* Ganymede.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9C6147D5BDF0010C4A2 /* Ganymede.png */; }; - 3914D9DD147D5BDF0010C4A2 /* HappyAlley.caf in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9C7147D5BDF0010C4A2 /* HappyAlley.caf */; }; - 3914D9DE147D5BDF0010C4A2 /* Jupiter.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9C8147D5BDF0010C4A2 /* Jupiter.png */; }; - 3914D9DF147D5BDF0010C4A2 /* Next.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9C9147D5BDF0010C4A2 /* Next.png */; }; - 3914D9E0147D5BDF0010C4A2 /* panel-bg.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9CA147D5BDF0010C4A2 /* panel-bg.png */; }; - 3914D9E1147D5BDF0010C4A2 /* panel-lamp-off.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9CB147D5BDF0010C4A2 /* panel-lamp-off.png */; }; - 3914D9E2147D5BDF0010C4A2 /* panel-lamp-on.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9CC147D5BDF0010C4A2 /* panel-lamp-on.png */; }; - 3914D9E3147D5BDF0010C4A2 /* panel-slider-knob.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9CD147D5BDF0010C4A2 /* panel-slider-knob.png */; }; - 3914D9E4147D5BDF0010C4A2 /* panel-slider-track-long.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9CE147D5BDF0010C4A2 /* panel-slider-track-long.png */; }; - 3914D9E5147D5BDF0010C4A2 /* panel-slider-track.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9CF147D5BDF0010C4A2 /* panel-slider-track.png */; }; - 3914D9E6147D5BDF0010C4A2 /* panel-trim-horiz.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9D0147D5BDF0010C4A2 /* panel-trim-horiz.png */; }; - 3914D9E7147D5BDF0010C4A2 /* panel-trim-vert.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9D1147D5BDF0010C4A2 /* panel-trim-vert.png */; }; - 3914D9E8147D5BDF0010C4A2 /* Pew.caf in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9D2147D5BDF0010C4A2 /* Pew.caf */; }; - 3914D9E9147D5BDF0010C4A2 /* PlanetKiller.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9D3147D5BDF0010C4A2 /* PlanetKiller.mp3 */; }; - 3914D9EA147D5BDF0010C4A2 /* Pow.caf in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9D4147D5BDF0010C4A2 /* Pow.caf */; }; - 3914D9EB147D5BDF0010C4A2 /* RocketShip.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9D5147D5BDF0010C4A2 /* RocketShip.png */; }; - 3914D9EC147D5BDF0010C4A2 /* vu-face.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9D6147D5BDF0010C4A2 /* vu-face.png */; }; - 3914D9ED147D5BDF0010C4A2 /* vu-needle.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9D7147D5BDF0010C4A2 /* vu-needle.png */; }; - 3914D9EE147D5BDF0010C4A2 /* vu-shell.png in Resources */ = {isa = PBXBuildFile; fileRef = 3914D9D8147D5BDF0010C4A2 /* vu-shell.png */; }; 39169B5314D381EE00082A89 /* ReverbDemo.m in Sources */ = {isa = PBXBuildFile; fileRef = 39169B5114D381ED00082A89 /* ReverbDemo.m */; }; 398C16C1147D555E000EFEDF /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398C16C0147D555E000EFEDF /* QuartzCore.framework */; }; 398C16C3147D555E000EFEDF /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398C16C2147D555E000EFEDF /* OpenGLES.framework */; }; 398C16C5147D555E000EFEDF /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398C16C4147D555E000EFEDF /* OpenAL.framework */; }; 398C16C7147D555E000EFEDF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398C16C6147D555E000EFEDF /* AudioToolbox.framework */; }; 398C16C9147D555E000EFEDF /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398C16C8147D555E000EFEDF /* AVFoundation.framework */; }; - 398C16CA147D555E000EFEDF /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398C16A2147D5533000EFEDF /* UIKit.framework */; }; - 398C16CB147D555E000EFEDF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398C16A4147D5533000EFEDF /* Foundation.framework */; }; 398C16CD147D555E000EFEDF /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 398C16CC147D555E000EFEDF /* CoreGraphics.framework */; }; - 398C16D1147D555E000EFEDF /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 398C16D0147D555E000EFEDF /* Default.png */; }; - 398C16D3147D555E000EFEDF /* fps_images.png in Resources */ = {isa = PBXBuildFile; fileRef = 398C16D2147D555E000EFEDF /* fps_images.png */; }; - 398C16DD147D555E000EFEDF /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 398C16DC147D555E000EFEDF /* Icon.png */; }; 398C181F147D5560000EFEDF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 398C181E147D5560000EFEDF /* main.m */; }; 398C1822147D5560000EFEDF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 398C1821147D5560000EFEDF /* AppDelegate.m */; }; - 398C1825147D5560000EFEDF /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 398C1824147D5560000EFEDF /* RootViewController.m */; }; 398C1828147D5560000EFEDF /* MainScene.m in Sources */ = {isa = PBXBuildFile; fileRef = 398C1827147D5560000EFEDF /* MainScene.m */; }; 39C48DF8151657D700BC57ED /* OALAction+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 39C48DF6151657D600BC57ED /* OALAction+Private.h */; }; 39F89E9F14831C3500E40CFE /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 39F89E9E14831C3500E40CFE /* libz.dylib */; }; + 8CE8688115F8C3940060E004 /* CCAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867BF15F8C3940060E004 /* CCAction.h */; }; + 8CE8688215F8C3940060E004 /* CCAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867C015F8C3940060E004 /* CCAction.m */; }; + 8CE8688315F8C3940060E004 /* CCActionCamera.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867C115F8C3940060E004 /* CCActionCamera.h */; }; + 8CE8688415F8C3940060E004 /* CCActionCamera.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867C215F8C3940060E004 /* CCActionCamera.m */; }; + 8CE8688515F8C3940060E004 /* CCActionCatmullRom.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867C315F8C3940060E004 /* CCActionCatmullRom.h */; }; + 8CE8688615F8C3940060E004 /* CCActionCatmullRom.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867C415F8C3940060E004 /* CCActionCatmullRom.m */; }; + 8CE8688715F8C3940060E004 /* CCActionEase.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867C515F8C3940060E004 /* CCActionEase.h */; }; + 8CE8688815F8C3940060E004 /* CCActionEase.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867C615F8C3940060E004 /* CCActionEase.m */; }; + 8CE8688915F8C3940060E004 /* CCActionGrid.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867C715F8C3940060E004 /* CCActionGrid.h */; }; + 8CE8688A15F8C3940060E004 /* CCActionGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867C815F8C3940060E004 /* CCActionGrid.m */; }; + 8CE8688B15F8C3940060E004 /* CCActionGrid3D.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867C915F8C3940060E004 /* CCActionGrid3D.h */; }; + 8CE8688C15F8C3940060E004 /* CCActionGrid3D.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867CA15F8C3940060E004 /* CCActionGrid3D.m */; }; + 8CE8688D15F8C3940060E004 /* CCActionInstant.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867CB15F8C3940060E004 /* CCActionInstant.h */; }; + 8CE8688E15F8C3940060E004 /* CCActionInstant.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867CC15F8C3940060E004 /* CCActionInstant.m */; }; + 8CE8688F15F8C3940060E004 /* CCActionInterval.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867CD15F8C3940060E004 /* CCActionInterval.h */; }; + 8CE8689015F8C3940060E004 /* CCActionInterval.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867CE15F8C3940060E004 /* CCActionInterval.m */; }; + 8CE8689115F8C3940060E004 /* CCActionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867CF15F8C3940060E004 /* CCActionManager.h */; }; + 8CE8689215F8C3940060E004 /* CCActionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867D015F8C3940060E004 /* CCActionManager.m */; }; + 8CE8689315F8C3940060E004 /* CCActionPageTurn3D.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867D115F8C3940060E004 /* CCActionPageTurn3D.h */; }; + 8CE8689415F8C3940060E004 /* CCActionPageTurn3D.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867D215F8C3940060E004 /* CCActionPageTurn3D.m */; }; + 8CE8689515F8C3940060E004 /* CCActionProgressTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867D315F8C3940060E004 /* CCActionProgressTimer.h */; }; + 8CE8689615F8C3940060E004 /* CCActionProgressTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867D415F8C3940060E004 /* CCActionProgressTimer.m */; }; + 8CE8689715F8C3940060E004 /* CCActionTiledGrid.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867D515F8C3940060E004 /* CCActionTiledGrid.h */; }; + 8CE8689815F8C3940060E004 /* CCActionTiledGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867D615F8C3940060E004 /* CCActionTiledGrid.m */; }; + 8CE8689915F8C3940060E004 /* CCActionTween.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867D715F8C3940060E004 /* CCActionTween.h */; }; + 8CE8689A15F8C3940060E004 /* CCActionTween.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867D815F8C3940060E004 /* CCActionTween.m */; }; + 8CE8689B15F8C3940060E004 /* CCAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867D915F8C3940060E004 /* CCAnimation.h */; }; + 8CE8689C15F8C3940060E004 /* CCAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867DA15F8C3940060E004 /* CCAnimation.m */; }; + 8CE8689D15F8C3940060E004 /* CCAnimationCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867DB15F8C3940060E004 /* CCAnimationCache.h */; }; + 8CE8689E15F8C3940060E004 /* CCAnimationCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867DC15F8C3940060E004 /* CCAnimationCache.m */; }; + 8CE8689F15F8C3940060E004 /* CCAtlasNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867DD15F8C3940060E004 /* CCAtlasNode.h */; }; + 8CE868A015F8C3940060E004 /* CCAtlasNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867DE15F8C3940060E004 /* CCAtlasNode.m */; }; + 8CE868A115F8C3940060E004 /* CCCamera.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867DF15F8C3940060E004 /* CCCamera.h */; }; + 8CE868A215F8C3940060E004 /* CCCamera.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867E015F8C3940060E004 /* CCCamera.m */; }; + 8CE868A315F8C3940060E004 /* ccConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867E115F8C3940060E004 /* ccConfig.h */; }; + 8CE868A415F8C3940060E004 /* CCConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867E215F8C3940060E004 /* CCConfiguration.h */; }; + 8CE868A515F8C3940060E004 /* CCConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867E315F8C3940060E004 /* CCConfiguration.m */; }; + 8CE868A615F8C3940060E004 /* ccDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867E415F8C3940060E004 /* ccDeprecated.h */; }; + 8CE868A715F8C3940060E004 /* ccDeprecated.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867E515F8C3940060E004 /* ccDeprecated.m */; }; + 8CE868A815F8C3940060E004 /* CCDirector.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867E615F8C3940060E004 /* CCDirector.h */; }; + 8CE868A915F8C3940060E004 /* CCDirector.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867E715F8C3940060E004 /* CCDirector.m */; }; + 8CE868AA15F8C3940060E004 /* CCDrawingPrimitives.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867E815F8C3940060E004 /* CCDrawingPrimitives.h */; }; + 8CE868AB15F8C3940060E004 /* CCDrawingPrimitives.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867E915F8C3940060E004 /* CCDrawingPrimitives.m */; }; + 8CE868AC15F8C3940060E004 /* CCGLProgram.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867EA15F8C3940060E004 /* CCGLProgram.h */; }; + 8CE868AD15F8C3940060E004 /* CCGLProgram.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867EB15F8C3940060E004 /* CCGLProgram.m */; }; + 8CE868AE15F8C3940060E004 /* ccGLStateCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867EC15F8C3940060E004 /* ccGLStateCache.h */; }; + 8CE868AF15F8C3940060E004 /* ccGLStateCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867ED15F8C3940060E004 /* ccGLStateCache.m */; }; + 8CE868B015F8C3940060E004 /* CCGrabber.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867EE15F8C3940060E004 /* CCGrabber.h */; }; + 8CE868B115F8C3940060E004 /* CCGrabber.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867EF15F8C3940060E004 /* CCGrabber.m */; }; + 8CE868B215F8C3940060E004 /* CCGrid.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867F015F8C3940060E004 /* CCGrid.h */; }; + 8CE868B315F8C3940060E004 /* CCGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867F115F8C3940060E004 /* CCGrid.m */; }; + 8CE868B415F8C3940060E004 /* CCLabelAtlas.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867F215F8C3940060E004 /* CCLabelAtlas.h */; }; + 8CE868B515F8C3940060E004 /* CCLabelAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867F315F8C3940060E004 /* CCLabelAtlas.m */; }; + 8CE868B615F8C3940060E004 /* CCLabelBMFont.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867F415F8C3940060E004 /* CCLabelBMFont.h */; }; + 8CE868B715F8C3940060E004 /* CCLabelBMFont.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867F515F8C3940060E004 /* CCLabelBMFont.m */; }; + 8CE868B815F8C3940060E004 /* CCLabelTTF.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867F615F8C3940060E004 /* CCLabelTTF.h */; }; + 8CE868B915F8C3940060E004 /* CCLabelTTF.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867F715F8C3940060E004 /* CCLabelTTF.m */; }; + 8CE868BA15F8C3940060E004 /* CCLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867F815F8C3940060E004 /* CCLayer.h */; }; + 8CE868BB15F8C3940060E004 /* CCLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867F915F8C3940060E004 /* CCLayer.m */; }; + 8CE868BC15F8C3940060E004 /* ccMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867FA15F8C3940060E004 /* ccMacros.h */; }; + 8CE868BD15F8C3940060E004 /* CCMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867FB15F8C3940060E004 /* CCMenu.h */; }; + 8CE868BE15F8C3940060E004 /* CCMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867FC15F8C3940060E004 /* CCMenu.m */; }; + 8CE868BF15F8C3940060E004 /* CCMenuItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867FD15F8C3940060E004 /* CCMenuItem.h */; }; + 8CE868C015F8C3940060E004 /* CCMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE867FE15F8C3940060E004 /* CCMenuItem.m */; }; + 8CE868C115F8C3940060E004 /* CCMotionStreak.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE867FF15F8C3940060E004 /* CCMotionStreak.h */; }; + 8CE868C215F8C3940060E004 /* CCMotionStreak.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8680015F8C3940060E004 /* CCMotionStreak.m */; }; + 8CE868C315F8C3940060E004 /* CCNode+Debug.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8680115F8C3940060E004 /* CCNode+Debug.h */; }; + 8CE868C415F8C3940060E004 /* CCNode+Debug.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8680215F8C3940060E004 /* CCNode+Debug.m */; }; + 8CE868C515F8C3940060E004 /* CCNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8680315F8C3940060E004 /* CCNode.h */; }; + 8CE868C615F8C3940060E004 /* CCNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8680415F8C3940060E004 /* CCNode.m */; }; + 8CE868C715F8C3940060E004 /* CCParallaxNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8680515F8C3940060E004 /* CCParallaxNode.h */; }; + 8CE868C815F8C3940060E004 /* CCParallaxNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8680615F8C3940060E004 /* CCParallaxNode.m */; }; + 8CE868C915F8C3940060E004 /* CCParticleBatchNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8680715F8C3940060E004 /* CCParticleBatchNode.h */; }; + 8CE868CA15F8C3940060E004 /* CCParticleBatchNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8680815F8C3940060E004 /* CCParticleBatchNode.m */; }; + 8CE868CB15F8C3940060E004 /* CCParticleExamples.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8680915F8C3940060E004 /* CCParticleExamples.h */; }; + 8CE868CC15F8C3940060E004 /* CCParticleExamples.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8680A15F8C3940060E004 /* CCParticleExamples.m */; }; + 8CE868CD15F8C3940060E004 /* CCParticleSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8680B15F8C3940060E004 /* CCParticleSystem.h */; }; + 8CE868CE15F8C3940060E004 /* CCParticleSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8680C15F8C3940060E004 /* CCParticleSystem.m */; }; + 8CE868CF15F8C3940060E004 /* CCParticleSystemQuad.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8680D15F8C3940060E004 /* CCParticleSystemQuad.h */; }; + 8CE868D015F8C3940060E004 /* CCParticleSystemQuad.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8680E15F8C3940060E004 /* CCParticleSystemQuad.m */; }; + 8CE868D115F8C3940060E004 /* CCProgressTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8680F15F8C3940060E004 /* CCProgressTimer.h */; }; + 8CE868D215F8C3940060E004 /* CCProgressTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8681015F8C3940060E004 /* CCProgressTimer.m */; }; + 8CE868D315F8C3940060E004 /* CCProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681115F8C3940060E004 /* CCProtocols.h */; }; + 8CE868D415F8C3940060E004 /* CCRenderTexture.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681215F8C3940060E004 /* CCRenderTexture.h */; }; + 8CE868D515F8C3940060E004 /* CCRenderTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8681315F8C3940060E004 /* CCRenderTexture.m */; }; + 8CE868D615F8C3940060E004 /* CCScene.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681415F8C3940060E004 /* CCScene.h */; }; + 8CE868D715F8C3940060E004 /* CCScene.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8681515F8C3940060E004 /* CCScene.m */; }; + 8CE868D815F8C3940060E004 /* CCScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681615F8C3940060E004 /* CCScheduler.h */; }; + 8CE868D915F8C3940060E004 /* CCScheduler.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8681715F8C3940060E004 /* CCScheduler.m */; }; + 8CE868DA15F8C3940060E004 /* ccShader_Position_uColor_frag.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681815F8C3940060E004 /* ccShader_Position_uColor_frag.h */; }; + 8CE868DB15F8C3940060E004 /* ccShader_Position_uColor_vert.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681915F8C3940060E004 /* ccShader_Position_uColor_vert.h */; }; + 8CE868DC15F8C3940060E004 /* ccShader_PositionColor_frag.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681A15F8C3940060E004 /* ccShader_PositionColor_frag.h */; }; + 8CE868DD15F8C3940060E004 /* ccShader_PositionColor_vert.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681B15F8C3940060E004 /* ccShader_PositionColor_vert.h */; }; + 8CE868DE15F8C3940060E004 /* ccShader_PositionTexture_frag.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681C15F8C3940060E004 /* ccShader_PositionTexture_frag.h */; }; + 8CE868DF15F8C3940060E004 /* ccShader_PositionTexture_uColor_frag.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681D15F8C3940060E004 /* ccShader_PositionTexture_uColor_frag.h */; }; + 8CE868E015F8C3940060E004 /* ccShader_PositionTexture_uColor_vert.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681E15F8C3940060E004 /* ccShader_PositionTexture_uColor_vert.h */; }; + 8CE868E115F8C3940060E004 /* ccShader_PositionTexture_vert.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8681F15F8C3940060E004 /* ccShader_PositionTexture_vert.h */; }; + 8CE868E215F8C3940060E004 /* ccShader_PositionTextureA8Color_frag.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682015F8C3940060E004 /* ccShader_PositionTextureA8Color_frag.h */; }; + 8CE868E315F8C3940060E004 /* ccShader_PositionTextureA8Color_vert.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682115F8C3940060E004 /* ccShader_PositionTextureA8Color_vert.h */; }; + 8CE868E415F8C3940060E004 /* ccShader_PositionTextureColor_frag.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682215F8C3940060E004 /* ccShader_PositionTextureColor_frag.h */; }; + 8CE868E515F8C3940060E004 /* ccShader_PositionTextureColor_vert.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682315F8C3940060E004 /* ccShader_PositionTextureColor_vert.h */; }; + 8CE868E615F8C3940060E004 /* ccShader_PositionTextureColorAlphaTest_frag.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682415F8C3940060E004 /* ccShader_PositionTextureColorAlphaTest_frag.h */; }; + 8CE868E715F8C3940060E004 /* CCShaderCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682515F8C3940060E004 /* CCShaderCache.h */; }; + 8CE868E815F8C3940060E004 /* CCShaderCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8682615F8C3940060E004 /* CCShaderCache.m */; }; + 8CE868E915F8C3940060E004 /* ccShaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682715F8C3940060E004 /* ccShaders.h */; }; + 8CE868EA15F8C3940060E004 /* ccShaders.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8682815F8C3940060E004 /* ccShaders.m */; }; + 8CE868EB15F8C3940060E004 /* CCSprite.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682915F8C3940060E004 /* CCSprite.h */; }; + 8CE868EC15F8C3940060E004 /* CCSprite.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8682A15F8C3940060E004 /* CCSprite.m */; }; + 8CE868ED15F8C3940060E004 /* CCSpriteBatchNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682B15F8C3940060E004 /* CCSpriteBatchNode.h */; }; + 8CE868EE15F8C3940060E004 /* CCSpriteBatchNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8682C15F8C3940060E004 /* CCSpriteBatchNode.m */; }; + 8CE868EF15F8C3940060E004 /* CCSpriteFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682D15F8C3940060E004 /* CCSpriteFrame.h */; }; + 8CE868F015F8C3940060E004 /* CCSpriteFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8682E15F8C3940060E004 /* CCSpriteFrame.m */; }; + 8CE868F115F8C3940060E004 /* CCSpriteFrameCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8682F15F8C3940060E004 /* CCSpriteFrameCache.h */; }; + 8CE868F215F8C3940060E004 /* CCSpriteFrameCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8683015F8C3940060E004 /* CCSpriteFrameCache.m */; }; + 8CE868F315F8C3940060E004 /* CCTexture2D.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8683115F8C3940060E004 /* CCTexture2D.h */; }; + 8CE868F415F8C3940060E004 /* CCTexture2D.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8683215F8C3940060E004 /* CCTexture2D.m */; }; + 8CE868F515F8C3940060E004 /* CCTextureAtlas.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8683315F8C3940060E004 /* CCTextureAtlas.h */; }; + 8CE868F615F8C3940060E004 /* CCTextureAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8683415F8C3940060E004 /* CCTextureAtlas.m */; }; + 8CE868F715F8C3940060E004 /* CCTextureCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8683515F8C3940060E004 /* CCTextureCache.h */; }; + 8CE868F815F8C3940060E004 /* CCTextureCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8683615F8C3940060E004 /* CCTextureCache.m */; }; + 8CE868F915F8C3940060E004 /* CCTexturePVR.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8683715F8C3940060E004 /* CCTexturePVR.h */; }; + 8CE868FA15F8C3940060E004 /* CCTexturePVR.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8683815F8C3940060E004 /* CCTexturePVR.m */; }; + 8CE868FB15F8C3940060E004 /* CCTileMapAtlas.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8683915F8C3940060E004 /* CCTileMapAtlas.h */; }; + 8CE868FC15F8C3940060E004 /* CCTileMapAtlas.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8683A15F8C3940060E004 /* CCTileMapAtlas.m */; }; + 8CE868FD15F8C3940060E004 /* CCTMXLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8683B15F8C3940060E004 /* CCTMXLayer.h */; }; + 8CE868FE15F8C3940060E004 /* CCTMXLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8683C15F8C3940060E004 /* CCTMXLayer.m */; }; + 8CE868FF15F8C3940060E004 /* CCTMXObjectGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8683D15F8C3940060E004 /* CCTMXObjectGroup.h */; }; + 8CE8690015F8C3940060E004 /* CCTMXObjectGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8683E15F8C3940060E004 /* CCTMXObjectGroup.m */; }; + 8CE8690115F8C3940060E004 /* CCTMXTiledMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8683F15F8C3940060E004 /* CCTMXTiledMap.h */; }; + 8CE8690215F8C3940060E004 /* CCTMXTiledMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8684015F8C3940060E004 /* CCTMXTiledMap.m */; }; + 8CE8690315F8C3940060E004 /* CCTMXXMLParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8684115F8C3940060E004 /* CCTMXXMLParser.h */; }; + 8CE8690415F8C3940060E004 /* CCTMXXMLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8684215F8C3940060E004 /* CCTMXXMLParser.m */; }; + 8CE8690515F8C3940060E004 /* CCTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8684315F8C3940060E004 /* CCTransition.h */; }; + 8CE8690615F8C3940060E004 /* CCTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8684415F8C3940060E004 /* CCTransition.m */; }; + 8CE8690715F8C3940060E004 /* CCTransitionPageTurn.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8684515F8C3940060E004 /* CCTransitionPageTurn.h */; }; + 8CE8690815F8C3940060E004 /* CCTransitionPageTurn.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8684615F8C3940060E004 /* CCTransitionPageTurn.m */; }; + 8CE8690915F8C3940060E004 /* CCTransitionProgress.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8684715F8C3940060E004 /* CCTransitionProgress.h */; }; + 8CE8690A15F8C3940060E004 /* CCTransitionProgress.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8684815F8C3940060E004 /* CCTransitionProgress.m */; }; + 8CE8690B15F8C3940060E004 /* ccTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8684915F8C3940060E004 /* ccTypes.h */; }; + 8CE8690C15F8C3940060E004 /* cocos2d.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8684A15F8C3940060E004 /* cocos2d.h */; }; + 8CE8690D15F8C3940060E004 /* cocos2d.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8684B15F8C3940060E004 /* cocos2d.m */; }; + 8CE8690E15F8C3940060E004 /* CCGL.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8684D15F8C3940060E004 /* CCGL.h */; }; + 8CE8690F15F8C3940060E004 /* CCNS.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8684E15F8C3940060E004 /* CCNS.h */; }; + 8CE8691015F8C3940060E004 /* CCDirectorIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8685015F8C3940060E004 /* CCDirectorIOS.h */; }; + 8CE8691115F8C3940060E004 /* CCDirectorIOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8685115F8C3940060E004 /* CCDirectorIOS.m */; }; + 8CE8691215F8C3940060E004 /* CCES2Renderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8685215F8C3940060E004 /* CCES2Renderer.h */; }; + 8CE8691315F8C3940060E004 /* CCES2Renderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8685315F8C3940060E004 /* CCES2Renderer.m */; }; + 8CE8691415F8C3940060E004 /* CCESRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8685415F8C3940060E004 /* CCESRenderer.h */; }; + 8CE8691515F8C3940060E004 /* CCGLView.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8685515F8C3940060E004 /* CCGLView.h */; }; + 8CE8691615F8C3940060E004 /* CCGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8685615F8C3940060E004 /* CCGLView.m */; }; + 8CE8691715F8C3940060E004 /* CCTouchDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8685715F8C3940060E004 /* CCTouchDelegateProtocol.h */; }; + 8CE8691815F8C3940060E004 /* CCTouchDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8685815F8C3940060E004 /* CCTouchDispatcher.h */; }; + 8CE8691915F8C3940060E004 /* CCTouchDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8685915F8C3940060E004 /* CCTouchDispatcher.m */; }; + 8CE8691A15F8C3940060E004 /* CCTouchHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8685A15F8C3940060E004 /* CCTouchHandler.h */; }; + 8CE8691B15F8C3940060E004 /* CCTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8685B15F8C3940060E004 /* CCTouchHandler.m */; }; + 8CE8691C15F8C3940060E004 /* CCDirectorMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8685D15F8C3940060E004 /* CCDirectorMac.h */; }; + 8CE8691D15F8C3940060E004 /* CCDirectorMac.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8685E15F8C3940060E004 /* CCDirectorMac.m */; }; + 8CE8691E15F8C3940060E004 /* CCEventDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8685F15F8C3940060E004 /* CCEventDispatcher.h */; }; + 8CE8691F15F8C3940060E004 /* CCEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8686015F8C3940060E004 /* CCEventDispatcher.m */; }; + 8CE8692015F8C3940060E004 /* CCGLView.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8686115F8C3940060E004 /* CCGLView.h */; }; + 8CE8692115F8C3940060E004 /* CCGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8686215F8C3940060E004 /* CCGLView.m */; }; + 8CE8692215F8C3940060E004 /* CCWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8686315F8C3940060E004 /* CCWindow.h */; }; + 8CE8692315F8C3940060E004 /* CCWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8686415F8C3940060E004 /* CCWindow.m */; }; + 8CE8692415F8C3940060E004 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8686615F8C3940060E004 /* base64.c */; }; + 8CE8692515F8C3940060E004 /* base64.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8686715F8C3940060E004 /* base64.h */; }; + 8CE8692615F8C3940060E004 /* CCArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8686815F8C3940060E004 /* CCArray.h */; }; + 8CE8692715F8C3940060E004 /* CCArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8686915F8C3940060E004 /* CCArray.m */; }; + 8CE8692815F8C3940060E004 /* ccCArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8686A15F8C3940060E004 /* ccCArray.h */; }; + 8CE8692915F8C3940060E004 /* ccCArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8686B15F8C3940060E004 /* ccCArray.m */; }; + 8CE8692A15F8C3940060E004 /* CCFileUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8686C15F8C3940060E004 /* CCFileUtils.h */; }; + 8CE8692B15F8C3940060E004 /* CCFileUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8686D15F8C3940060E004 /* CCFileUtils.m */; }; + 8CE8692C15F8C3940060E004 /* CCProfiling.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8686E15F8C3940060E004 /* CCProfiling.h */; }; + 8CE8692D15F8C3940060E004 /* CCProfiling.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8686F15F8C3940060E004 /* CCProfiling.m */; }; + 8CE8692E15F8C3940060E004 /* ccUtils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8687015F8C3940060E004 /* ccUtils.c */; }; + 8CE8692F15F8C3940060E004 /* ccUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8687115F8C3940060E004 /* ccUtils.h */; }; + 8CE8693015F8C3940060E004 /* CCVertex.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8687215F8C3940060E004 /* CCVertex.h */; }; + 8CE8693115F8C3940060E004 /* CCVertex.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8687315F8C3940060E004 /* CCVertex.m */; }; + 8CE8693215F8C3940060E004 /* CGPointExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8687415F8C3940060E004 /* CGPointExtension.h */; }; + 8CE8693315F8C3940060E004 /* CGPointExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8687515F8C3940060E004 /* CGPointExtension.m */; }; + 8CE8693415F8C3940060E004 /* NSThread+performBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8687615F8C3940060E004 /* NSThread+performBlock.h */; }; + 8CE8693515F8C3940060E004 /* NSThread+performBlock.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8687715F8C3940060E004 /* NSThread+performBlock.m */; }; + 8CE8693615F8C3940060E004 /* OpenGL_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8687815F8C3940060E004 /* OpenGL_Internal.h */; }; + 8CE8693715F8C3940060E004 /* TGAlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8687915F8C3940060E004 /* TGAlib.h */; }; + 8CE8693815F8C3940060E004 /* TGAlib.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8687A15F8C3940060E004 /* TGAlib.m */; }; + 8CE8693915F8C3940060E004 /* TransformUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8687B15F8C3940060E004 /* TransformUtils.h */; }; + 8CE8693A15F8C3940060E004 /* TransformUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8687C15F8C3940060E004 /* TransformUtils.m */; }; + 8CE8693B15F8C3940060E004 /* uthash.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8687D15F8C3940060E004 /* uthash.h */; }; + 8CE8693C15F8C3940060E004 /* utlist.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8687E15F8C3940060E004 /* utlist.h */; }; + 8CE8693D15F8C3940060E004 /* ZipUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8687F15F8C3940060E004 /* ZipUtils.h */; }; + 8CE8693E15F8C3940060E004 /* ZipUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8688015F8C3940060E004 /* ZipUtils.m */; }; + 8CE8694015F8C42B0060E004 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CE8693F15F8C42B0060E004 /* Foundation.framework */; }; + 8CE8694115F8C4590060E004 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CE8693F15F8C42B0060E004 /* Foundation.framework */; }; + 8CE8694215F8C4590060E004 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CE8693F15F8C42B0060E004 /* Foundation.framework */; }; + 8CE8694415F8C4860060E004 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CE8694315F8C4860060E004 /* UIKit.framework */; }; + 8CE8696815F8C5880060E004 /* aabb.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8694815F8C5880060E004 /* aabb.h */; }; + 8CE8696915F8C5880060E004 /* mat4stack.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8694A15F8C5880060E004 /* mat4stack.h */; }; + 8CE8696A15F8C5880060E004 /* matrix.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8694B15F8C5880060E004 /* matrix.h */; }; + 8CE8696B15F8C5880060E004 /* kazmath.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8694C15F8C5880060E004 /* kazmath.h */; }; + 8CE8696C15F8C5880060E004 /* mat3.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8694D15F8C5880060E004 /* mat3.h */; }; + 8CE8696D15F8C5880060E004 /* mat4.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8694E15F8C5880060E004 /* mat4.h */; }; + 8CE8696E15F8C5880060E004 /* neon_matrix_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8694F15F8C5880060E004 /* neon_matrix_impl.h */; }; + 8CE8696F15F8C5880060E004 /* plane.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8695015F8C5880060E004 /* plane.h */; }; + 8CE8697015F8C5880060E004 /* quaternion.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8695115F8C5880060E004 /* quaternion.h */; }; + 8CE8697115F8C5880060E004 /* ray2.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8695215F8C5880060E004 /* ray2.h */; }; + 8CE8697215F8C5880060E004 /* utility.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8695315F8C5880060E004 /* utility.h */; }; + 8CE8697315F8C5880060E004 /* vec2.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8695415F8C5880060E004 /* vec2.h */; }; + 8CE8697415F8C5880060E004 /* vec3.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8695515F8C5880060E004 /* vec3.h */; }; + 8CE8697515F8C5880060E004 /* vec4.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CE8695615F8C5880060E004 /* vec4.h */; }; + 8CE8697615F8C5880060E004 /* aabb.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8695815F8C5880060E004 /* aabb.c */; }; + 8CE8697715F8C5880060E004 /* mat4stack.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8695C15F8C5880060E004 /* mat4stack.c */; }; + 8CE8697815F8C5880060E004 /* matrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8695D15F8C5880060E004 /* matrix.c */; }; + 8CE8697915F8C5880060E004 /* mat3.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8695E15F8C5880060E004 /* mat3.c */; }; + 8CE8697A15F8C5880060E004 /* mat4.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8695F15F8C5880060E004 /* mat4.c */; }; + 8CE8697B15F8C5880060E004 /* neon_matrix_impl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8696015F8C5880060E004 /* neon_matrix_impl.c */; }; + 8CE8697C15F8C5880060E004 /* plane.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8696115F8C5880060E004 /* plane.c */; }; + 8CE8697D15F8C5880060E004 /* quaternion.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8696215F8C5880060E004 /* quaternion.c */; }; + 8CE8697E15F8C5880060E004 /* ray2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8696315F8C5880060E004 /* ray2.c */; }; + 8CE8697F15F8C5880060E004 /* utility.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8696415F8C5880060E004 /* utility.c */; }; + 8CE8698015F8C5880060E004 /* vec2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8696515F8C5880060E004 /* vec2.c */; }; + 8CE8698115F8C5880060E004 /* vec3.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8696615F8C5880060E004 /* vec3.c */; }; + 8CE8698215F8C5880060E004 /* vec4.c in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8696715F8C5880060E004 /* vec4.c */; }; + 8CE86DCD15F8D9420060E004 /* Default-Landscape~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DA915F8D9420060E004 /* Default-Landscape~ipad.png */; }; + 8CE86DCE15F8D9420060E004 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DAA15F8D9420060E004 /* Default.png */; }; + 8CE86DCF15F8D9420060E004 /* fps_images-hd.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DAB15F8D9420060E004 /* fps_images-hd.png */; }; + 8CE86DD015F8D9420060E004 /* fps_images-ipadhd.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DAC15F8D9420060E004 /* fps_images-ipadhd.png */; }; + 8CE86DD115F8D9420060E004 /* fps_images.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DAD15F8D9420060E004 /* fps_images.png */; }; + 8CE86DD215F8D9420060E004 /* Icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DAE15F8D9420060E004 /* Icon-72.png */; }; + 8CE86DD315F8D9420060E004 /* Icon-Small-50.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DAF15F8D9420060E004 /* Icon-Small-50.png */; }; + 8CE86DD415F8D9420060E004 /* Icon-Small.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DB015F8D9420060E004 /* Icon-Small.png */; }; + 8CE86DD515F8D9420060E004 /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DB115F8D9420060E004 /* Icon-Small@2x.png */; }; + 8CE86DD615F8D9420060E004 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DB215F8D9420060E004 /* Icon.png */; }; + 8CE86DD715F8D9420060E004 /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DB315F8D9420060E004 /* Icon@2x.png */; }; + 8CE86DD815F8D9420060E004 /* iTunesArtwork in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DB415F8D9420060E004 /* iTunesArtwork */; }; + 8CE86DD915F8D9420060E004 /* AssetSources.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DB615F8D9420060E004 /* AssetSources.txt */; }; + 8CE86DDA15F8D9420060E004 /* Back.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DB715F8D9420060E004 /* Back.png */; }; + 8CE86DDB15F8D9420060E004 /* ColdFunk.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DB815F8D9420060E004 /* ColdFunk.caf */; }; + 8CE86DDC15F8D9420060E004 /* Exit.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DB915F8D9420060E004 /* Exit.png */; }; + 8CE86DDD15F8D9420060E004 /* Ganymede.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DBA15F8D9420060E004 /* Ganymede.png */; }; + 8CE86DDE15F8D9420060E004 /* HappyAlley.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DBB15F8D9420060E004 /* HappyAlley.caf */; }; + 8CE86DDF15F8D9420060E004 /* Jupiter.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DBC15F8D9420060E004 /* Jupiter.png */; }; + 8CE86DE015F8D9420060E004 /* Next.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DBD15F8D9420060E004 /* Next.png */; }; + 8CE86DE115F8D9420060E004 /* panel-bg.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DBE15F8D9420060E004 /* panel-bg.png */; }; + 8CE86DE215F8D9420060E004 /* panel-lamp-off.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DBF15F8D9420060E004 /* panel-lamp-off.png */; }; + 8CE86DE315F8D9420060E004 /* panel-lamp-on.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DC015F8D9420060E004 /* panel-lamp-on.png */; }; + 8CE86DE415F8D9420060E004 /* panel-slider-knob.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DC115F8D9420060E004 /* panel-slider-knob.png */; }; + 8CE86DE515F8D9420060E004 /* panel-slider-track-long.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DC215F8D9420060E004 /* panel-slider-track-long.png */; }; + 8CE86DE615F8D9420060E004 /* panel-slider-track.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DC315F8D9420060E004 /* panel-slider-track.png */; }; + 8CE86DE715F8D9420060E004 /* panel-trim-horiz.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DC415F8D9420060E004 /* panel-trim-horiz.png */; }; + 8CE86DE815F8D9420060E004 /* panel-trim-vert.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DC515F8D9420060E004 /* panel-trim-vert.png */; }; + 8CE86DE915F8D9420060E004 /* Pew.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DC615F8D9420060E004 /* Pew.caf */; }; + 8CE86DEA15F8D9420060E004 /* PlanetKiller.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DC715F8D9420060E004 /* PlanetKiller.mp3 */; }; + 8CE86DEB15F8D9420060E004 /* Pow.caf in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DC815F8D9420060E004 /* Pow.caf */; }; + 8CE86DEC15F8D9420060E004 /* RocketShip.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DC915F8D9420060E004 /* RocketShip.png */; }; + 8CE86DED15F8D9420060E004 /* vu-face.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DCA15F8D9420060E004 /* vu-face.png */; }; + 8CE86DEE15F8D9420060E004 /* vu-needle.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DCB15F8D9420060E004 /* vu-needle.png */; }; + 8CE86DEF15F8D9420060E004 /* vu-shell.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CE86DCC15F8D9420060E004 /* vu-shell.png */; }; + 8CE86DF215F8D9620060E004 /* IntroLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE86DF115F8D9620060E004 /* IntroLayer.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -190,183 +336,7 @@ 390E993C1482C4BE002E14E7 /* ObjectAL.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjectAL.m; sourceTree = ""; }; 3914D74E147D57540010C4A2 /* libcocos2d.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcocos2d.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3914D752147D57540010C4A2 /* cocos2d-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "cocos2d-Prefix.pch"; sourceTree = ""; }; - 3914D75A147D57D60010C4A2 /* CCAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCAction.h; sourceTree = ""; }; - 3914D75B147D57D60010C4A2 /* CCAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAction.m; sourceTree = ""; }; - 3914D75C147D57D60010C4A2 /* CCActionCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionCamera.h; sourceTree = ""; }; - 3914D75D147D57D60010C4A2 /* CCActionCamera.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionCamera.m; sourceTree = ""; }; - 3914D75E147D57D60010C4A2 /* CCActionEase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionEase.h; sourceTree = ""; }; - 3914D75F147D57D60010C4A2 /* CCActionEase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionEase.m; sourceTree = ""; }; - 3914D760147D57D60010C4A2 /* CCActionGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionGrid.h; sourceTree = ""; }; - 3914D761147D57D60010C4A2 /* CCActionGrid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionGrid.m; sourceTree = ""; }; - 3914D762147D57D60010C4A2 /* CCActionGrid3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionGrid3D.h; sourceTree = ""; }; - 3914D763147D57D60010C4A2 /* CCActionGrid3D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionGrid3D.m; sourceTree = ""; }; - 3914D764147D57D60010C4A2 /* CCActionInstant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionInstant.h; sourceTree = ""; }; - 3914D765147D57D60010C4A2 /* CCActionInstant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionInstant.m; sourceTree = ""; }; - 3914D766147D57D60010C4A2 /* CCActionInterval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionInterval.h; sourceTree = ""; }; - 3914D767147D57D60010C4A2 /* CCActionInterval.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionInterval.m; sourceTree = ""; }; - 3914D768147D57D60010C4A2 /* CCActionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionManager.h; sourceTree = ""; }; - 3914D769147D57D60010C4A2 /* CCActionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionManager.m; sourceTree = ""; }; - 3914D76A147D57D60010C4A2 /* CCActionPageTurn3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionPageTurn3D.h; sourceTree = ""; }; - 3914D76B147D57D60010C4A2 /* CCActionPageTurn3D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionPageTurn3D.m; sourceTree = ""; }; - 3914D76C147D57D60010C4A2 /* CCActionProgressTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionProgressTimer.h; sourceTree = ""; }; - 3914D76D147D57D60010C4A2 /* CCActionProgressTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionProgressTimer.m; sourceTree = ""; }; - 3914D76E147D57D60010C4A2 /* CCActionTiledGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionTiledGrid.h; sourceTree = ""; }; - 3914D76F147D57D60010C4A2 /* CCActionTiledGrid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionTiledGrid.m; sourceTree = ""; }; - 3914D770147D57D60010C4A2 /* CCActionTween.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCActionTween.h; sourceTree = ""; }; - 3914D771147D57D60010C4A2 /* CCActionTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionTween.m; sourceTree = ""; }; - 3914D772147D57D60010C4A2 /* CCAnimation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCAnimation.h; sourceTree = ""; }; - 3914D773147D57D60010C4A2 /* CCAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAnimation.m; sourceTree = ""; }; - 3914D774147D57D60010C4A2 /* CCAnimationCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCAnimationCache.h; sourceTree = ""; }; - 3914D775147D57D60010C4A2 /* CCAnimationCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAnimationCache.m; sourceTree = ""; }; - 3914D776147D57D60010C4A2 /* CCAtlasNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCAtlasNode.h; sourceTree = ""; }; - 3914D777147D57D60010C4A2 /* CCAtlasNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAtlasNode.m; sourceTree = ""; }; - 3914D778147D57D60010C4A2 /* CCBlockSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCBlockSupport.h; sourceTree = ""; }; - 3914D779147D57D60010C4A2 /* CCBlockSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCBlockSupport.m; sourceTree = ""; }; - 3914D77A147D57D60010C4A2 /* CCCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCCamera.h; sourceTree = ""; }; - 3914D77B147D57D60010C4A2 /* CCCamera.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCCamera.m; sourceTree = ""; }; - 3914D77C147D57D60010C4A2 /* ccConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ccConfig.h; sourceTree = ""; }; - 3914D77D147D57D60010C4A2 /* CCConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCConfiguration.h; sourceTree = ""; }; - 3914D77E147D57D60010C4A2 /* CCConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCConfiguration.m; sourceTree = ""; }; - 3914D77F147D57D60010C4A2 /* CCDirector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCDirector.h; sourceTree = ""; }; - 3914D780147D57D60010C4A2 /* CCDirector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDirector.m; sourceTree = ""; }; - 3914D781147D57D60010C4A2 /* CCDrawingPrimitives.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCDrawingPrimitives.h; sourceTree = ""; }; - 3914D782147D57D60010C4A2 /* CCDrawingPrimitives.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDrawingPrimitives.m; sourceTree = ""; }; - 3914D783147D57D60010C4A2 /* CCGrabber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCGrabber.h; sourceTree = ""; }; - 3914D784147D57D60010C4A2 /* CCGrabber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGrabber.m; sourceTree = ""; }; - 3914D785147D57D60010C4A2 /* CCGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCGrid.h; sourceTree = ""; }; - 3914D786147D57D60010C4A2 /* CCGrid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGrid.m; sourceTree = ""; }; - 3914D787147D57D60010C4A2 /* CCLabelAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCLabelAtlas.h; sourceTree = ""; }; - 3914D788147D57D60010C4A2 /* CCLabelAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLabelAtlas.m; sourceTree = ""; }; - 3914D789147D57D60010C4A2 /* CCLabelBMFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCLabelBMFont.h; sourceTree = ""; }; - 3914D78A147D57D60010C4A2 /* CCLabelBMFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLabelBMFont.m; sourceTree = ""; }; - 3914D78B147D57D60010C4A2 /* CCLabelTTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCLabelTTF.h; sourceTree = ""; }; - 3914D78C147D57D60010C4A2 /* CCLabelTTF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLabelTTF.m; sourceTree = ""; }; - 3914D78D147D57D60010C4A2 /* CCLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCLayer.h; sourceTree = ""; }; - 3914D78E147D57D60010C4A2 /* CCLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLayer.m; sourceTree = ""; }; - 3914D78F147D57D60010C4A2 /* ccMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ccMacros.h; sourceTree = ""; }; - 3914D790147D57D60010C4A2 /* CCMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCMenu.h; sourceTree = ""; }; - 3914D791147D57D60010C4A2 /* CCMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCMenu.m; sourceTree = ""; }; - 3914D792147D57D60010C4A2 /* CCMenuItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCMenuItem.h; sourceTree = ""; }; - 3914D793147D57D60010C4A2 /* CCMenuItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCMenuItem.m; sourceTree = ""; }; - 3914D794147D57D60010C4A2 /* CCMotionStreak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCMotionStreak.h; sourceTree = ""; }; - 3914D795147D57D60010C4A2 /* CCMotionStreak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCMotionStreak.m; sourceTree = ""; }; - 3914D796147D57D60010C4A2 /* CCNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCNode.h; sourceTree = ""; }; - 3914D797147D57D60010C4A2 /* CCNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCNode.m; sourceTree = ""; }; - 3914D798147D57D60010C4A2 /* CCParallaxNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCParallaxNode.h; sourceTree = ""; }; - 3914D799147D57D60010C4A2 /* CCParallaxNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParallaxNode.m; sourceTree = ""; }; - 3914D79A147D57D60010C4A2 /* CCParticleExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCParticleExamples.h; sourceTree = ""; }; - 3914D79B147D57D60010C4A2 /* CCParticleExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleExamples.m; sourceTree = ""; }; - 3914D79C147D57D60010C4A2 /* CCParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCParticleSystem.h; sourceTree = ""; }; - 3914D79D147D57D60010C4A2 /* CCParticleSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleSystem.m; sourceTree = ""; }; - 3914D79E147D57D60010C4A2 /* CCParticleSystemPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCParticleSystemPoint.h; sourceTree = ""; }; - 3914D79F147D57D60010C4A2 /* CCParticleSystemPoint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleSystemPoint.m; sourceTree = ""; }; - 3914D7A0147D57D60010C4A2 /* CCParticleSystemQuad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCParticleSystemQuad.h; sourceTree = ""; }; - 3914D7A1147D57D60010C4A2 /* CCParticleSystemQuad.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleSystemQuad.m; sourceTree = ""; }; - 3914D7A2147D57D60010C4A2 /* CCProgressTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCProgressTimer.h; sourceTree = ""; }; - 3914D7A3147D57D60010C4A2 /* CCProgressTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCProgressTimer.m; sourceTree = ""; }; - 3914D7A4147D57D60010C4A2 /* CCProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCProtocols.h; sourceTree = ""; }; - 3914D7A5147D57D60010C4A2 /* CCRenderTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCRenderTexture.h; sourceTree = ""; }; - 3914D7A6147D57D60010C4A2 /* CCRenderTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCRenderTexture.m; sourceTree = ""; }; - 3914D7A7147D57D60010C4A2 /* CCRibbon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCRibbon.h; sourceTree = ""; }; - 3914D7A8147D57D60010C4A2 /* CCRibbon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCRibbon.m; sourceTree = ""; }; - 3914D7A9147D57D60010C4A2 /* CCScene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCScene.h; sourceTree = ""; }; - 3914D7AA147D57D60010C4A2 /* CCScene.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCScene.m; sourceTree = ""; }; - 3914D7AB147D57D60010C4A2 /* CCScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCScheduler.h; sourceTree = ""; }; - 3914D7AC147D57D60010C4A2 /* CCScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCScheduler.m; sourceTree = ""; }; - 3914D7AD147D57D60010C4A2 /* CCSprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCSprite.h; sourceTree = ""; }; - 3914D7AE147D57D60010C4A2 /* CCSprite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSprite.m; sourceTree = ""; }; - 3914D7AF147D57D60010C4A2 /* CCSpriteBatchNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCSpriteBatchNode.h; sourceTree = ""; }; - 3914D7B0147D57D60010C4A2 /* CCSpriteBatchNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSpriteBatchNode.m; sourceTree = ""; }; - 3914D7B1147D57D60010C4A2 /* CCSpriteFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCSpriteFrame.h; sourceTree = ""; }; - 3914D7B2147D57D60010C4A2 /* CCSpriteFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSpriteFrame.m; sourceTree = ""; }; - 3914D7B3147D57D60010C4A2 /* CCSpriteFrameCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCSpriteFrameCache.h; sourceTree = ""; }; - 3914D7B4147D57D60010C4A2 /* CCSpriteFrameCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSpriteFrameCache.m; sourceTree = ""; }; - 3914D7B5147D57D60010C4A2 /* CCTexture2D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTexture2D.h; sourceTree = ""; }; - 3914D7B6147D57D60010C4A2 /* CCTexture2D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTexture2D.m; sourceTree = ""; }; - 3914D7B7147D57D60010C4A2 /* CCTextureAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTextureAtlas.h; sourceTree = ""; }; - 3914D7B8147D57D60010C4A2 /* CCTextureAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTextureAtlas.m; sourceTree = ""; }; - 3914D7B9147D57D60010C4A2 /* CCTextureCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTextureCache.h; sourceTree = ""; }; - 3914D7BA147D57D60010C4A2 /* CCTextureCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTextureCache.m; sourceTree = ""; }; - 3914D7BB147D57D60010C4A2 /* CCTexturePVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTexturePVR.h; sourceTree = ""; }; - 3914D7BC147D57D60010C4A2 /* CCTexturePVR.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTexturePVR.m; sourceTree = ""; }; - 3914D7BD147D57D60010C4A2 /* CCTileMapAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTileMapAtlas.h; sourceTree = ""; }; - 3914D7BE147D57D60010C4A2 /* CCTileMapAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTileMapAtlas.m; sourceTree = ""; }; - 3914D7BF147D57D60010C4A2 /* CCTMXLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTMXLayer.h; sourceTree = ""; }; - 3914D7C0147D57D60010C4A2 /* CCTMXLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXLayer.m; sourceTree = ""; }; - 3914D7C1147D57D60010C4A2 /* CCTMXObjectGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTMXObjectGroup.h; sourceTree = ""; }; - 3914D7C2147D57D60010C4A2 /* CCTMXObjectGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXObjectGroup.m; sourceTree = ""; }; - 3914D7C3147D57D60010C4A2 /* CCTMXTiledMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTMXTiledMap.h; sourceTree = ""; }; - 3914D7C4147D57D60010C4A2 /* CCTMXTiledMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXTiledMap.m; sourceTree = ""; }; - 3914D7C5147D57D60010C4A2 /* CCTMXXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTMXXMLParser.h; sourceTree = ""; }; - 3914D7C6147D57D60010C4A2 /* CCTMXXMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXXMLParser.m; sourceTree = ""; }; - 3914D7C7147D57D60010C4A2 /* CCTransition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTransition.h; sourceTree = ""; }; - 3914D7C8147D57D60010C4A2 /* CCTransition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransition.m; sourceTree = ""; }; - 3914D7C9147D57D60010C4A2 /* CCTransitionPageTurn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTransitionPageTurn.h; sourceTree = ""; }; - 3914D7CA147D57D60010C4A2 /* CCTransitionPageTurn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransitionPageTurn.m; sourceTree = ""; }; - 3914D7CB147D57D60010C4A2 /* CCTransitionRadial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTransitionRadial.h; sourceTree = ""; }; - 3914D7CC147D57D60010C4A2 /* CCTransitionRadial.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransitionRadial.m; sourceTree = ""; }; - 3914D7CD147D57D60010C4A2 /* ccTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ccTypes.h; sourceTree = ""; }; - 3914D7CE147D57D60010C4A2 /* cocos2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = cocos2d.h; sourceTree = ""; }; - 3914D7CF147D57D60010C4A2 /* cocos2d.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cocos2d.m; sourceTree = ""; }; - 3914D7D1147D57D60010C4A2 /* CCGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCGL.h; sourceTree = ""; }; - 3914D7D2147D57D60010C4A2 /* CCNS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCNS.h; sourceTree = ""; }; - 3914D7D4147D57D60010C4A2 /* CCDirectorIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCDirectorIOS.h; sourceTree = ""; }; - 3914D7D5147D57D60010C4A2 /* CCDirectorIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDirectorIOS.m; sourceTree = ""; }; - 3914D7D6147D57D60010C4A2 /* CCTouchDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTouchDelegateProtocol.h; sourceTree = ""; }; - 3914D7D7147D57D60010C4A2 /* CCTouchDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTouchDispatcher.h; sourceTree = ""; }; - 3914D7D8147D57D60010C4A2 /* CCTouchDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTouchDispatcher.m; sourceTree = ""; }; - 3914D7D9147D57D60010C4A2 /* CCTouchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCTouchHandler.h; sourceTree = ""; }; - 3914D7DA147D57D60010C4A2 /* CCTouchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTouchHandler.m; sourceTree = ""; }; - 3914D7DB147D57D60010C4A2 /* EAGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = EAGLView.h; sourceTree = ""; }; - 3914D7DC147D57D60010C4A2 /* EAGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EAGLView.m; sourceTree = ""; }; - 3914D7DD147D57D60010C4A2 /* ES1Renderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ES1Renderer.h; sourceTree = ""; }; - 3914D7DE147D57D60010C4A2 /* ES1Renderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ES1Renderer.m; sourceTree = ""; }; - 3914D7DF147D57D60010C4A2 /* ESRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ESRenderer.h; sourceTree = ""; }; - 3914D7E0147D57D60010C4A2 /* glu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = glu.c; sourceTree = ""; }; - 3914D7E1147D57D60010C4A2 /* glu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = glu.h; sourceTree = ""; }; - 3914D7E3147D57D60010C4A2 /* CCDirectorMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCDirectorMac.h; sourceTree = ""; }; - 3914D7E4147D57D60010C4A2 /* CCDirectorMac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDirectorMac.m; sourceTree = ""; }; - 3914D7E5147D57D60010C4A2 /* CCEventDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCEventDispatcher.h; sourceTree = ""; }; - 3914D7E6147D57D60010C4A2 /* CCEventDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCEventDispatcher.m; sourceTree = ""; }; - 3914D7E7147D57D60010C4A2 /* MacGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = MacGLView.h; sourceTree = ""; }; - 3914D7E8147D57D60010C4A2 /* MacGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MacGLView.m; sourceTree = ""; }; - 3914D7E9147D57D60010C4A2 /* MacWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = MacWindow.h; sourceTree = ""; }; - 3914D7EA147D57D60010C4A2 /* MacWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MacWindow.m; sourceTree = ""; }; - 3914D7EC147D57D60010C4A2 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = ""; }; - 3914D7ED147D57D60010C4A2 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = base64.h; sourceTree = ""; }; - 3914D7EE147D57D60010C4A2 /* CCArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCArray.h; sourceTree = ""; }; - 3914D7EF147D57D60010C4A2 /* CCArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCArray.m; sourceTree = ""; }; - 3914D7F0147D57D60010C4A2 /* ccCArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ccCArray.h; sourceTree = ""; }; - 3914D7F1147D57D60010C4A2 /* CCFileUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCFileUtils.h; sourceTree = ""; }; - 3914D7F2147D57D60010C4A2 /* CCFileUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCFileUtils.m; sourceTree = ""; }; - 3914D7F3147D57D60010C4A2 /* CCProfiling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CCProfiling.h; sourceTree = ""; }; - 3914D7F4147D57D60010C4A2 /* CCProfiling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCProfiling.m; sourceTree = ""; }; - 3914D7F5147D57D60010C4A2 /* ccUtils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ccUtils.c; sourceTree = ""; }; - 3914D7F6147D57D60010C4A2 /* ccUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ccUtils.h; sourceTree = ""; }; - 3914D7F7147D57D60010C4A2 /* CGPointExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = CGPointExtension.h; sourceTree = ""; }; - 3914D7F8147D57D60010C4A2 /* CGPointExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CGPointExtension.m; sourceTree = ""; }; - 3914D7F9147D57D60010C4A2 /* OpenGL_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = OpenGL_Internal.h; sourceTree = ""; }; - 3914D7FA147D57D60010C4A2 /* TGAlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = TGAlib.h; sourceTree = ""; }; - 3914D7FB147D57D60010C4A2 /* TGAlib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGAlib.m; sourceTree = ""; }; - 3914D7FC147D57D60010C4A2 /* TransformUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = TransformUtils.h; sourceTree = ""; }; - 3914D7FD147D57D60010C4A2 /* TransformUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TransformUtils.m; sourceTree = ""; }; - 3914D7FE147D57D60010C4A2 /* uthash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = uthash.h; sourceTree = ""; }; - 3914D7FF147D57D60010C4A2 /* utlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = utlist.h; sourceTree = ""; }; - 3914D800147D57D60010C4A2 /* ZipUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ZipUtils.h; sourceTree = ""; }; - 3914D801147D57D60010C4A2 /* ZipUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZipUtils.m; sourceTree = ""; }; - 3914D803147D57D60010C4A2 /* FontLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = FontLabel.h; sourceTree = ""; }; - 3914D804147D57D60010C4A2 /* FontLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FontLabel.m; sourceTree = ""; }; - 3914D805147D57D60010C4A2 /* FontLabelStringDrawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = FontLabelStringDrawing.h; sourceTree = ""; }; - 3914D806147D57D60010C4A2 /* FontLabelStringDrawing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FontLabelStringDrawing.m; sourceTree = ""; }; - 3914D807147D57D60010C4A2 /* FontManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = FontManager.h; sourceTree = ""; }; - 3914D808147D57D60010C4A2 /* FontManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FontManager.m; sourceTree = ""; }; - 3914D809147D57D60010C4A2 /* ZAttributedString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ZAttributedString.h; sourceTree = ""; }; - 3914D80A147D57D60010C4A2 /* ZAttributedString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZAttributedString.m; sourceTree = ""; }; - 3914D80B147D57D60010C4A2 /* ZAttributedStringPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ZAttributedStringPrivate.h; sourceTree = ""; }; - 3914D80C147D57D60010C4A2 /* ZFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ZFont.h; sourceTree = ""; }; - 3914D80D147D57D60010C4A2 /* ZFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZFont.m; sourceTree = ""; }; 3914D80E147D57D60010C4A2 /* LICENSE_cocos2d.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE_cocos2d.txt; sourceTree = ""; }; - 3914D80F147D57D60010C4A2 /* LICENSE_FontLabel.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE_FontLabel.txt; sourceTree = ""; }; 3914D8C1147D58F00010C4A2 /* OALAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = OALAction.h; sourceTree = ""; }; 3914D8C2147D58F00010C4A2 /* OALAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OALAction.m; sourceTree = ""; }; 3914D8C3147D58F00010C4A2 /* OALActionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = OALActionManager.h; sourceTree = ""; }; @@ -475,36 +445,12 @@ 3914D992147D5B570010C4A2 /* TouchableNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TouchableNode.m; sourceTree = ""; }; 3914D993147D5B570010C4A2 /* VUMeter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = VUMeter.h; sourceTree = ""; }; 3914D994147D5B570010C4A2 /* VUMeter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VUMeter.m; sourceTree = ""; }; - 3914D9C3147D5BDF0010C4A2 /* Back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Back.png; path = Resources/Back.png; sourceTree = ""; }; - 3914D9C4147D5BDF0010C4A2 /* ColdFunk.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = ColdFunk.caf; path = Resources/ColdFunk.caf; sourceTree = ""; }; - 3914D9C5147D5BDF0010C4A2 /* Exit.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Exit.png; path = Resources/Exit.png; sourceTree = ""; }; - 3914D9C6147D5BDF0010C4A2 /* Ganymede.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Ganymede.png; path = Resources/Ganymede.png; sourceTree = ""; }; - 3914D9C7147D5BDF0010C4A2 /* HappyAlley.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = HappyAlley.caf; path = Resources/HappyAlley.caf; sourceTree = ""; }; - 3914D9C8147D5BDF0010C4A2 /* Jupiter.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Jupiter.png; path = Resources/Jupiter.png; sourceTree = ""; }; - 3914D9C9147D5BDF0010C4A2 /* Next.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Next.png; path = Resources/Next.png; sourceTree = ""; }; - 3914D9CA147D5BDF0010C4A2 /* panel-bg.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "panel-bg.png"; path = "Resources/panel-bg.png"; sourceTree = ""; }; - 3914D9CB147D5BDF0010C4A2 /* panel-lamp-off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "panel-lamp-off.png"; path = "Resources/panel-lamp-off.png"; sourceTree = ""; }; - 3914D9CC147D5BDF0010C4A2 /* panel-lamp-on.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "panel-lamp-on.png"; path = "Resources/panel-lamp-on.png"; sourceTree = ""; }; - 3914D9CD147D5BDF0010C4A2 /* panel-slider-knob.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "panel-slider-knob.png"; path = "Resources/panel-slider-knob.png"; sourceTree = ""; }; - 3914D9CE147D5BDF0010C4A2 /* panel-slider-track-long.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "panel-slider-track-long.png"; path = "Resources/panel-slider-track-long.png"; sourceTree = ""; }; - 3914D9CF147D5BDF0010C4A2 /* panel-slider-track.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "panel-slider-track.png"; path = "Resources/panel-slider-track.png"; sourceTree = ""; }; - 3914D9D0147D5BDF0010C4A2 /* panel-trim-horiz.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "panel-trim-horiz.png"; path = "Resources/panel-trim-horiz.png"; sourceTree = ""; }; - 3914D9D1147D5BDF0010C4A2 /* panel-trim-vert.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "panel-trim-vert.png"; path = "Resources/panel-trim-vert.png"; sourceTree = ""; }; - 3914D9D2147D5BDF0010C4A2 /* Pew.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = Pew.caf; path = Resources/Pew.caf; sourceTree = ""; }; - 3914D9D3147D5BDF0010C4A2 /* PlanetKiller.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; name = PlanetKiller.mp3; path = Resources/PlanetKiller.mp3; sourceTree = ""; }; - 3914D9D4147D5BDF0010C4A2 /* Pow.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = Pow.caf; path = Resources/Pow.caf; sourceTree = ""; }; - 3914D9D5147D5BDF0010C4A2 /* RocketShip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = RocketShip.png; path = Resources/RocketShip.png; sourceTree = ""; }; - 3914D9D6147D5BDF0010C4A2 /* vu-face.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "vu-face.png"; path = "Resources/vu-face.png"; sourceTree = ""; }; - 3914D9D7147D5BDF0010C4A2 /* vu-needle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "vu-needle.png"; path = "Resources/vu-needle.png"; sourceTree = ""; }; - 3914D9D8147D5BDF0010C4A2 /* vu-shell.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "vu-shell.png"; path = "Resources/vu-shell.png"; sourceTree = ""; }; 39169B5014D381ED00082A89 /* ReverbDemo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = ReverbDemo.h; sourceTree = ""; }; 39169B5114D381ED00082A89 /* ReverbDemo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReverbDemo.m; sourceTree = ""; }; 398C1694147D5533000EFEDF /* ObjectAL-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ObjectAL-Info.plist"; sourceTree = ""; }; 398C1696147D5533000EFEDF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 398C1698147D5533000EFEDF /* ObjectAL-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ObjectAL-Prefix.pch"; sourceTree = ""; }; 398C16A0147D5533000EFEDF /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; - 398C16A2147D5533000EFEDF /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; - 398C16A4147D5533000EFEDF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 398C16AB147D5533000EFEDF /* ObjectALTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ObjectALTests-Info.plist"; sourceTree = ""; }; 398C16AD147D5533000EFEDF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 398C16AF147D5533000EFEDF /* ObjectALTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.objj.h; path = ObjectALTests.h; sourceTree = ""; }; @@ -516,21 +462,274 @@ 398C16C6147D555E000EFEDF /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 398C16C8147D555E000EFEDF /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 398C16CC147D555E000EFEDF /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - 398C16D0147D555E000EFEDF /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = Resources/Default.png; sourceTree = ""; }; - 398C16D2147D555E000EFEDF /* fps_images.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = fps_images.png; path = Resources/fps_images.png; sourceTree = ""; }; - 398C16DC147D555E000EFEDF /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon.png; path = Resources/Icon.png; sourceTree = ""; }; 398C16E0147D555E000EFEDF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Resources/Info.plist; sourceTree = ""; }; 398C181D147D5560000EFEDF /* Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; 398C181E147D5560000EFEDF /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 398C1820147D5560000EFEDF /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.objj.h; path = AppDelegate.h; sourceTree = ""; }; 398C1821147D5560000EFEDF /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 398C1823147D5560000EFEDF /* RootViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.objj.h; path = RootViewController.h; sourceTree = ""; }; - 398C1824147D5560000EFEDF /* RootViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RootViewController.m; sourceTree = ""; }; 398C1826147D5560000EFEDF /* MainScene.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.objj.h; path = MainScene.h; sourceTree = ""; }; 398C1827147D5560000EFEDF /* MainScene.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MainScene.m; sourceTree = ""; }; - 398C1829147D5560000EFEDF /* GameConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.objj.h; path = GameConfig.h; sourceTree = ""; }; 39C48DF6151657D600BC57ED /* OALAction+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OALAction+Private.h"; sourceTree = ""; }; 39F89E9E14831C3500E40CFE /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + 8CE867BF15F8C3940060E004 /* CCAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAction.h; sourceTree = ""; }; + 8CE867C015F8C3940060E004 /* CCAction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAction.m; sourceTree = ""; }; + 8CE867C115F8C3940060E004 /* CCActionCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionCamera.h; sourceTree = ""; }; + 8CE867C215F8C3940060E004 /* CCActionCamera.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionCamera.m; sourceTree = ""; }; + 8CE867C315F8C3940060E004 /* CCActionCatmullRom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionCatmullRom.h; sourceTree = ""; }; + 8CE867C415F8C3940060E004 /* CCActionCatmullRom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionCatmullRom.m; sourceTree = ""; }; + 8CE867C515F8C3940060E004 /* CCActionEase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionEase.h; sourceTree = ""; }; + 8CE867C615F8C3940060E004 /* CCActionEase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionEase.m; sourceTree = ""; }; + 8CE867C715F8C3940060E004 /* CCActionGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionGrid.h; sourceTree = ""; }; + 8CE867C815F8C3940060E004 /* CCActionGrid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionGrid.m; sourceTree = ""; }; + 8CE867C915F8C3940060E004 /* CCActionGrid3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionGrid3D.h; sourceTree = ""; }; + 8CE867CA15F8C3940060E004 /* CCActionGrid3D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionGrid3D.m; sourceTree = ""; }; + 8CE867CB15F8C3940060E004 /* CCActionInstant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionInstant.h; sourceTree = ""; }; + 8CE867CC15F8C3940060E004 /* CCActionInstant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionInstant.m; sourceTree = ""; }; + 8CE867CD15F8C3940060E004 /* CCActionInterval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionInterval.h; sourceTree = ""; }; + 8CE867CE15F8C3940060E004 /* CCActionInterval.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionInterval.m; sourceTree = ""; }; + 8CE867CF15F8C3940060E004 /* CCActionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionManager.h; sourceTree = ""; }; + 8CE867D015F8C3940060E004 /* CCActionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionManager.m; sourceTree = ""; }; + 8CE867D115F8C3940060E004 /* CCActionPageTurn3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionPageTurn3D.h; sourceTree = ""; }; + 8CE867D215F8C3940060E004 /* CCActionPageTurn3D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionPageTurn3D.m; sourceTree = ""; }; + 8CE867D315F8C3940060E004 /* CCActionProgressTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionProgressTimer.h; sourceTree = ""; }; + 8CE867D415F8C3940060E004 /* CCActionProgressTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionProgressTimer.m; sourceTree = ""; }; + 8CE867D515F8C3940060E004 /* CCActionTiledGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionTiledGrid.h; sourceTree = ""; }; + 8CE867D615F8C3940060E004 /* CCActionTiledGrid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionTiledGrid.m; sourceTree = ""; }; + 8CE867D715F8C3940060E004 /* CCActionTween.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionTween.h; sourceTree = ""; }; + 8CE867D815F8C3940060E004 /* CCActionTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionTween.m; sourceTree = ""; }; + 8CE867D915F8C3940060E004 /* CCAnimation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAnimation.h; sourceTree = ""; }; + 8CE867DA15F8C3940060E004 /* CCAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAnimation.m; sourceTree = ""; }; + 8CE867DB15F8C3940060E004 /* CCAnimationCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAnimationCache.h; sourceTree = ""; }; + 8CE867DC15F8C3940060E004 /* CCAnimationCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAnimationCache.m; sourceTree = ""; }; + 8CE867DD15F8C3940060E004 /* CCAtlasNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAtlasNode.h; sourceTree = ""; }; + 8CE867DE15F8C3940060E004 /* CCAtlasNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAtlasNode.m; sourceTree = ""; }; + 8CE867DF15F8C3940060E004 /* CCCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCCamera.h; sourceTree = ""; }; + 8CE867E015F8C3940060E004 /* CCCamera.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCCamera.m; sourceTree = ""; }; + 8CE867E115F8C3940060E004 /* ccConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccConfig.h; sourceTree = ""; }; + 8CE867E215F8C3940060E004 /* CCConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCConfiguration.h; sourceTree = ""; }; + 8CE867E315F8C3940060E004 /* CCConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCConfiguration.m; sourceTree = ""; }; + 8CE867E415F8C3940060E004 /* ccDeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccDeprecated.h; sourceTree = ""; }; + 8CE867E515F8C3940060E004 /* ccDeprecated.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ccDeprecated.m; sourceTree = ""; }; + 8CE867E615F8C3940060E004 /* CCDirector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCDirector.h; sourceTree = ""; }; + 8CE867E715F8C3940060E004 /* CCDirector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDirector.m; sourceTree = ""; }; + 8CE867E815F8C3940060E004 /* CCDrawingPrimitives.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCDrawingPrimitives.h; sourceTree = ""; }; + 8CE867E915F8C3940060E004 /* CCDrawingPrimitives.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDrawingPrimitives.m; sourceTree = ""; }; + 8CE867EA15F8C3940060E004 /* CCGLProgram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCGLProgram.h; sourceTree = ""; }; + 8CE867EB15F8C3940060E004 /* CCGLProgram.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGLProgram.m; sourceTree = ""; }; + 8CE867EC15F8C3940060E004 /* ccGLStateCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccGLStateCache.h; sourceTree = ""; }; + 8CE867ED15F8C3940060E004 /* ccGLStateCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ccGLStateCache.m; sourceTree = ""; }; + 8CE867EE15F8C3940060E004 /* CCGrabber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCGrabber.h; sourceTree = ""; }; + 8CE867EF15F8C3940060E004 /* CCGrabber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGrabber.m; sourceTree = ""; }; + 8CE867F015F8C3940060E004 /* CCGrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCGrid.h; sourceTree = ""; }; + 8CE867F115F8C3940060E004 /* CCGrid.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGrid.m; sourceTree = ""; }; + 8CE867F215F8C3940060E004 /* CCLabelAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCLabelAtlas.h; sourceTree = ""; }; + 8CE867F315F8C3940060E004 /* CCLabelAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLabelAtlas.m; sourceTree = ""; }; + 8CE867F415F8C3940060E004 /* CCLabelBMFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCLabelBMFont.h; sourceTree = ""; }; + 8CE867F515F8C3940060E004 /* CCLabelBMFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLabelBMFont.m; sourceTree = ""; }; + 8CE867F615F8C3940060E004 /* CCLabelTTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCLabelTTF.h; sourceTree = ""; }; + 8CE867F715F8C3940060E004 /* CCLabelTTF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLabelTTF.m; sourceTree = ""; }; + 8CE867F815F8C3940060E004 /* CCLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCLayer.h; sourceTree = ""; }; + 8CE867F915F8C3940060E004 /* CCLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCLayer.m; sourceTree = ""; }; + 8CE867FA15F8C3940060E004 /* ccMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccMacros.h; sourceTree = ""; }; + 8CE867FB15F8C3940060E004 /* CCMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCMenu.h; sourceTree = ""; }; + 8CE867FC15F8C3940060E004 /* CCMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCMenu.m; sourceTree = ""; }; + 8CE867FD15F8C3940060E004 /* CCMenuItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCMenuItem.h; sourceTree = ""; }; + 8CE867FE15F8C3940060E004 /* CCMenuItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCMenuItem.m; sourceTree = ""; }; + 8CE867FF15F8C3940060E004 /* CCMotionStreak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCMotionStreak.h; sourceTree = ""; }; + 8CE8680015F8C3940060E004 /* CCMotionStreak.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCMotionStreak.m; sourceTree = ""; }; + 8CE8680115F8C3940060E004 /* CCNode+Debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CCNode+Debug.h"; sourceTree = ""; }; + 8CE8680215F8C3940060E004 /* CCNode+Debug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CCNode+Debug.m"; sourceTree = ""; }; + 8CE8680315F8C3940060E004 /* CCNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCNode.h; sourceTree = ""; }; + 8CE8680415F8C3940060E004 /* CCNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCNode.m; sourceTree = ""; }; + 8CE8680515F8C3940060E004 /* CCParallaxNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCParallaxNode.h; sourceTree = ""; }; + 8CE8680615F8C3940060E004 /* CCParallaxNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParallaxNode.m; sourceTree = ""; }; + 8CE8680715F8C3940060E004 /* CCParticleBatchNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCParticleBatchNode.h; sourceTree = ""; }; + 8CE8680815F8C3940060E004 /* CCParticleBatchNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleBatchNode.m; sourceTree = ""; }; + 8CE8680915F8C3940060E004 /* CCParticleExamples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCParticleExamples.h; sourceTree = ""; }; + 8CE8680A15F8C3940060E004 /* CCParticleExamples.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleExamples.m; sourceTree = ""; }; + 8CE8680B15F8C3940060E004 /* CCParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCParticleSystem.h; sourceTree = ""; }; + 8CE8680C15F8C3940060E004 /* CCParticleSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleSystem.m; sourceTree = ""; }; + 8CE8680D15F8C3940060E004 /* CCParticleSystemQuad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCParticleSystemQuad.h; sourceTree = ""; }; + 8CE8680E15F8C3940060E004 /* CCParticleSystemQuad.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCParticleSystemQuad.m; sourceTree = ""; }; + 8CE8680F15F8C3940060E004 /* CCProgressTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCProgressTimer.h; sourceTree = ""; }; + 8CE8681015F8C3940060E004 /* CCProgressTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCProgressTimer.m; sourceTree = ""; }; + 8CE8681115F8C3940060E004 /* CCProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCProtocols.h; sourceTree = ""; }; + 8CE8681215F8C3940060E004 /* CCRenderTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCRenderTexture.h; sourceTree = ""; }; + 8CE8681315F8C3940060E004 /* CCRenderTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCRenderTexture.m; sourceTree = ""; }; + 8CE8681415F8C3940060E004 /* CCScene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCScene.h; sourceTree = ""; }; + 8CE8681515F8C3940060E004 /* CCScene.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCScene.m; sourceTree = ""; }; + 8CE8681615F8C3940060E004 /* CCScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCScheduler.h; sourceTree = ""; }; + 8CE8681715F8C3940060E004 /* CCScheduler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCScheduler.m; sourceTree = ""; }; + 8CE8681815F8C3940060E004 /* ccShader_Position_uColor_frag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_Position_uColor_frag.h; sourceTree = ""; }; + 8CE8681915F8C3940060E004 /* ccShader_Position_uColor_vert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_Position_uColor_vert.h; sourceTree = ""; }; + 8CE8681A15F8C3940060E004 /* ccShader_PositionColor_frag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionColor_frag.h; sourceTree = ""; }; + 8CE8681B15F8C3940060E004 /* ccShader_PositionColor_vert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionColor_vert.h; sourceTree = ""; }; + 8CE8681C15F8C3940060E004 /* ccShader_PositionTexture_frag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionTexture_frag.h; sourceTree = ""; }; + 8CE8681D15F8C3940060E004 /* ccShader_PositionTexture_uColor_frag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionTexture_uColor_frag.h; sourceTree = ""; }; + 8CE8681E15F8C3940060E004 /* ccShader_PositionTexture_uColor_vert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionTexture_uColor_vert.h; sourceTree = ""; }; + 8CE8681F15F8C3940060E004 /* ccShader_PositionTexture_vert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionTexture_vert.h; sourceTree = ""; }; + 8CE8682015F8C3940060E004 /* ccShader_PositionTextureA8Color_frag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionTextureA8Color_frag.h; sourceTree = ""; }; + 8CE8682115F8C3940060E004 /* ccShader_PositionTextureA8Color_vert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionTextureA8Color_vert.h; sourceTree = ""; }; + 8CE8682215F8C3940060E004 /* ccShader_PositionTextureColor_frag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionTextureColor_frag.h; sourceTree = ""; }; + 8CE8682315F8C3940060E004 /* ccShader_PositionTextureColor_vert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionTextureColor_vert.h; sourceTree = ""; }; + 8CE8682415F8C3940060E004 /* ccShader_PositionTextureColorAlphaTest_frag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShader_PositionTextureColorAlphaTest_frag.h; sourceTree = ""; }; + 8CE8682515F8C3940060E004 /* CCShaderCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCShaderCache.h; sourceTree = ""; }; + 8CE8682615F8C3940060E004 /* CCShaderCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCShaderCache.m; sourceTree = ""; }; + 8CE8682715F8C3940060E004 /* ccShaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccShaders.h; sourceTree = ""; }; + 8CE8682815F8C3940060E004 /* ccShaders.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ccShaders.m; sourceTree = ""; }; + 8CE8682915F8C3940060E004 /* CCSprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCSprite.h; sourceTree = ""; }; + 8CE8682A15F8C3940060E004 /* CCSprite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSprite.m; sourceTree = ""; }; + 8CE8682B15F8C3940060E004 /* CCSpriteBatchNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCSpriteBatchNode.h; sourceTree = ""; }; + 8CE8682C15F8C3940060E004 /* CCSpriteBatchNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSpriteBatchNode.m; sourceTree = ""; }; + 8CE8682D15F8C3940060E004 /* CCSpriteFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCSpriteFrame.h; sourceTree = ""; }; + 8CE8682E15F8C3940060E004 /* CCSpriteFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSpriteFrame.m; sourceTree = ""; }; + 8CE8682F15F8C3940060E004 /* CCSpriteFrameCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCSpriteFrameCache.h; sourceTree = ""; }; + 8CE8683015F8C3940060E004 /* CCSpriteFrameCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSpriteFrameCache.m; sourceTree = ""; }; + 8CE8683115F8C3940060E004 /* CCTexture2D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTexture2D.h; sourceTree = ""; }; + 8CE8683215F8C3940060E004 /* CCTexture2D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTexture2D.m; sourceTree = ""; }; + 8CE8683315F8C3940060E004 /* CCTextureAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTextureAtlas.h; sourceTree = ""; }; + 8CE8683415F8C3940060E004 /* CCTextureAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTextureAtlas.m; sourceTree = ""; }; + 8CE8683515F8C3940060E004 /* CCTextureCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTextureCache.h; sourceTree = ""; }; + 8CE8683615F8C3940060E004 /* CCTextureCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTextureCache.m; sourceTree = ""; }; + 8CE8683715F8C3940060E004 /* CCTexturePVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTexturePVR.h; sourceTree = ""; }; + 8CE8683815F8C3940060E004 /* CCTexturePVR.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTexturePVR.m; sourceTree = ""; }; + 8CE8683915F8C3940060E004 /* CCTileMapAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTileMapAtlas.h; sourceTree = ""; }; + 8CE8683A15F8C3940060E004 /* CCTileMapAtlas.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTileMapAtlas.m; sourceTree = ""; }; + 8CE8683B15F8C3940060E004 /* CCTMXLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTMXLayer.h; sourceTree = ""; }; + 8CE8683C15F8C3940060E004 /* CCTMXLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXLayer.m; sourceTree = ""; }; + 8CE8683D15F8C3940060E004 /* CCTMXObjectGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTMXObjectGroup.h; sourceTree = ""; }; + 8CE8683E15F8C3940060E004 /* CCTMXObjectGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXObjectGroup.m; sourceTree = ""; }; + 8CE8683F15F8C3940060E004 /* CCTMXTiledMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTMXTiledMap.h; sourceTree = ""; }; + 8CE8684015F8C3940060E004 /* CCTMXTiledMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXTiledMap.m; sourceTree = ""; }; + 8CE8684115F8C3940060E004 /* CCTMXXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTMXXMLParser.h; sourceTree = ""; }; + 8CE8684215F8C3940060E004 /* CCTMXXMLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTMXXMLParser.m; sourceTree = ""; }; + 8CE8684315F8C3940060E004 /* CCTransition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTransition.h; sourceTree = ""; }; + 8CE8684415F8C3940060E004 /* CCTransition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransition.m; sourceTree = ""; }; + 8CE8684515F8C3940060E004 /* CCTransitionPageTurn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTransitionPageTurn.h; sourceTree = ""; }; + 8CE8684615F8C3940060E004 /* CCTransitionPageTurn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransitionPageTurn.m; sourceTree = ""; }; + 8CE8684715F8C3940060E004 /* CCTransitionProgress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTransitionProgress.h; sourceTree = ""; }; + 8CE8684815F8C3940060E004 /* CCTransitionProgress.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTransitionProgress.m; sourceTree = ""; }; + 8CE8684915F8C3940060E004 /* ccTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccTypes.h; sourceTree = ""; }; + 8CE8684A15F8C3940060E004 /* cocos2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocos2d.h; sourceTree = ""; }; + 8CE8684B15F8C3940060E004 /* cocos2d.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cocos2d.m; sourceTree = ""; }; + 8CE8684D15F8C3940060E004 /* CCGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCGL.h; sourceTree = ""; }; + 8CE8684E15F8C3940060E004 /* CCNS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCNS.h; sourceTree = ""; }; + 8CE8685015F8C3940060E004 /* CCDirectorIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCDirectorIOS.h; sourceTree = ""; }; + 8CE8685115F8C3940060E004 /* CCDirectorIOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDirectorIOS.m; sourceTree = ""; }; + 8CE8685215F8C3940060E004 /* CCES2Renderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCES2Renderer.h; sourceTree = ""; }; + 8CE8685315F8C3940060E004 /* CCES2Renderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCES2Renderer.m; sourceTree = ""; }; + 8CE8685415F8C3940060E004 /* CCESRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCESRenderer.h; sourceTree = ""; }; + 8CE8685515F8C3940060E004 /* CCGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCGLView.h; sourceTree = ""; }; + 8CE8685615F8C3940060E004 /* CCGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGLView.m; sourceTree = ""; }; + 8CE8685715F8C3940060E004 /* CCTouchDelegateProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTouchDelegateProtocol.h; sourceTree = ""; }; + 8CE8685815F8C3940060E004 /* CCTouchDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTouchDispatcher.h; sourceTree = ""; }; + 8CE8685915F8C3940060E004 /* CCTouchDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTouchDispatcher.m; sourceTree = ""; }; + 8CE8685A15F8C3940060E004 /* CCTouchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCTouchHandler.h; sourceTree = ""; }; + 8CE8685B15F8C3940060E004 /* CCTouchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCTouchHandler.m; sourceTree = ""; }; + 8CE8685D15F8C3940060E004 /* CCDirectorMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCDirectorMac.h; sourceTree = ""; }; + 8CE8685E15F8C3940060E004 /* CCDirectorMac.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCDirectorMac.m; sourceTree = ""; }; + 8CE8685F15F8C3940060E004 /* CCEventDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCEventDispatcher.h; sourceTree = ""; }; + 8CE8686015F8C3940060E004 /* CCEventDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCEventDispatcher.m; sourceTree = ""; }; + 8CE8686115F8C3940060E004 /* CCGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCGLView.h; sourceTree = ""; }; + 8CE8686215F8C3940060E004 /* CCGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCGLView.m; sourceTree = ""; }; + 8CE8686315F8C3940060E004 /* CCWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCWindow.h; sourceTree = ""; }; + 8CE8686415F8C3940060E004 /* CCWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCWindow.m; sourceTree = ""; }; + 8CE8686615F8C3940060E004 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = ""; }; + 8CE8686715F8C3940060E004 /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = ""; }; + 8CE8686815F8C3940060E004 /* CCArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCArray.h; sourceTree = ""; }; + 8CE8686915F8C3940060E004 /* CCArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCArray.m; sourceTree = ""; }; + 8CE8686A15F8C3940060E004 /* ccCArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccCArray.h; sourceTree = ""; }; + 8CE8686B15F8C3940060E004 /* ccCArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ccCArray.m; sourceTree = ""; }; + 8CE8686C15F8C3940060E004 /* CCFileUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCFileUtils.h; sourceTree = ""; }; + 8CE8686D15F8C3940060E004 /* CCFileUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCFileUtils.m; sourceTree = ""; }; + 8CE8686E15F8C3940060E004 /* CCProfiling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCProfiling.h; sourceTree = ""; }; + 8CE8686F15F8C3940060E004 /* CCProfiling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCProfiling.m; sourceTree = ""; }; + 8CE8687015F8C3940060E004 /* ccUtils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ccUtils.c; sourceTree = ""; }; + 8CE8687115F8C3940060E004 /* ccUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ccUtils.h; sourceTree = ""; }; + 8CE8687215F8C3940060E004 /* CCVertex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCVertex.h; sourceTree = ""; }; + 8CE8687315F8C3940060E004 /* CCVertex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCVertex.m; sourceTree = ""; }; + 8CE8687415F8C3940060E004 /* CGPointExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CGPointExtension.h; sourceTree = ""; }; + 8CE8687515F8C3940060E004 /* CGPointExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CGPointExtension.m; sourceTree = ""; }; + 8CE8687615F8C3940060E004 /* NSThread+performBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSThread+performBlock.h"; sourceTree = ""; }; + 8CE8687715F8C3940060E004 /* NSThread+performBlock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSThread+performBlock.m"; sourceTree = ""; }; + 8CE8687815F8C3940060E004 /* OpenGL_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenGL_Internal.h; sourceTree = ""; }; + 8CE8687915F8C3940060E004 /* TGAlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGAlib.h; sourceTree = ""; }; + 8CE8687A15F8C3940060E004 /* TGAlib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGAlib.m; sourceTree = ""; }; + 8CE8687B15F8C3940060E004 /* TransformUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TransformUtils.h; sourceTree = ""; }; + 8CE8687C15F8C3940060E004 /* TransformUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TransformUtils.m; sourceTree = ""; }; + 8CE8687D15F8C3940060E004 /* uthash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uthash.h; sourceTree = ""; }; + 8CE8687E15F8C3940060E004 /* utlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utlist.h; sourceTree = ""; }; + 8CE8687F15F8C3940060E004 /* ZipUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZipUtils.h; sourceTree = ""; }; + 8CE8688015F8C3940060E004 /* ZipUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZipUtils.m; sourceTree = ""; }; + 8CE8693F15F8C42B0060E004 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 8CE8694315F8C4860060E004 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 8CE8694815F8C5880060E004 /* aabb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aabb.h; sourceTree = ""; }; + 8CE8694A15F8C5880060E004 /* mat4stack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mat4stack.h; sourceTree = ""; }; + 8CE8694B15F8C5880060E004 /* matrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = matrix.h; sourceTree = ""; }; + 8CE8694C15F8C5880060E004 /* kazmath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kazmath.h; sourceTree = ""; }; + 8CE8694D15F8C5880060E004 /* mat3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mat3.h; sourceTree = ""; }; + 8CE8694E15F8C5880060E004 /* mat4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mat4.h; sourceTree = ""; }; + 8CE8694F15F8C5880060E004 /* neon_matrix_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = neon_matrix_impl.h; sourceTree = ""; }; + 8CE8695015F8C5880060E004 /* plane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = plane.h; sourceTree = ""; }; + 8CE8695115F8C5880060E004 /* quaternion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = quaternion.h; sourceTree = ""; }; + 8CE8695215F8C5880060E004 /* ray2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ray2.h; sourceTree = ""; }; + 8CE8695315F8C5880060E004 /* utility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utility.h; sourceTree = ""; }; + 8CE8695415F8C5880060E004 /* vec2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vec2.h; sourceTree = ""; }; + 8CE8695515F8C5880060E004 /* vec3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vec3.h; sourceTree = ""; }; + 8CE8695615F8C5880060E004 /* vec4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vec4.h; sourceTree = ""; }; + 8CE8695815F8C5880060E004 /* aabb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aabb.c; sourceTree = ""; }; + 8CE8695915F8C5880060E004 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = ""; }; + 8CE8695A15F8C5880060E004 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 8CE8695C15F8C5880060E004 /* mat4stack.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mat4stack.c; sourceTree = ""; }; + 8CE8695D15F8C5880060E004 /* matrix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = matrix.c; sourceTree = ""; }; + 8CE8695E15F8C5880060E004 /* mat3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mat3.c; sourceTree = ""; }; + 8CE8695F15F8C5880060E004 /* mat4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mat4.c; sourceTree = ""; }; + 8CE8696015F8C5880060E004 /* neon_matrix_impl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = neon_matrix_impl.c; sourceTree = ""; }; + 8CE8696115F8C5880060E004 /* plane.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = plane.c; sourceTree = ""; }; + 8CE8696215F8C5880060E004 /* quaternion.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = quaternion.c; sourceTree = ""; }; + 8CE8696315F8C5880060E004 /* ray2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ray2.c; sourceTree = ""; }; + 8CE8696415F8C5880060E004 /* utility.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utility.c; sourceTree = ""; }; + 8CE8696515F8C5880060E004 /* vec2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vec2.c; sourceTree = ""; }; + 8CE8696615F8C5880060E004 /* vec3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vec3.c; sourceTree = ""; }; + 8CE8696715F8C5880060E004 /* vec4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vec4.c; sourceTree = ""; }; + 8CE8698315F8C5930060E004 /* LICENSE_Kazmath.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE_Kazmath.txt; sourceTree = ""; }; + 8CE86DA915F8D9420060E004 /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-Landscape~ipad.png"; path = "Resources/Default-Landscape~ipad.png"; sourceTree = ""; }; + 8CE86DAA15F8D9420060E004 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = Resources/Default.png; sourceTree = ""; }; + 8CE86DAB15F8D9420060E004 /* fps_images-hd.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "fps_images-hd.png"; path = "Resources/fps_images-hd.png"; sourceTree = ""; }; + 8CE86DAC15F8D9420060E004 /* fps_images-ipadhd.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "fps_images-ipadhd.png"; path = "Resources/fps_images-ipadhd.png"; sourceTree = ""; }; + 8CE86DAD15F8D9420060E004 /* fps_images.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = fps_images.png; path = Resources/fps_images.png; sourceTree = ""; }; + 8CE86DAE15F8D9420060E004 /* Icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon-72.png"; path = "Resources/Icon-72.png"; sourceTree = ""; }; + 8CE86DAF15F8D9420060E004 /* Icon-Small-50.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon-Small-50.png"; path = "Resources/Icon-Small-50.png"; sourceTree = ""; }; + 8CE86DB015F8D9420060E004 /* Icon-Small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon-Small.png"; path = "Resources/Icon-Small.png"; sourceTree = ""; }; + 8CE86DB115F8D9420060E004 /* Icon-Small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon-Small@2x.png"; path = "Resources/Icon-Small@2x.png"; sourceTree = ""; }; + 8CE86DB215F8D9420060E004 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon.png; path = Resources/Icon.png; sourceTree = ""; }; + 8CE86DB315F8D9420060E004 /* Icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon@2x.png"; path = "Resources/Icon@2x.png"; sourceTree = ""; }; + 8CE86DB415F8D9420060E004 /* iTunesArtwork */ = {isa = PBXFileReference; lastKnownFileType = file; name = iTunesArtwork; path = Resources/iTunesArtwork; sourceTree = ""; }; + 8CE86DB615F8D9420060E004 /* AssetSources.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AssetSources.txt; sourceTree = ""; }; + 8CE86DB715F8D9420060E004 /* Back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Back.png; sourceTree = ""; }; + 8CE86DB815F8D9420060E004 /* ColdFunk.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = ColdFunk.caf; sourceTree = ""; }; + 8CE86DB915F8D9420060E004 /* Exit.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Exit.png; sourceTree = ""; }; + 8CE86DBA15F8D9420060E004 /* Ganymede.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Ganymede.png; sourceTree = ""; }; + 8CE86DBB15F8D9420060E004 /* HappyAlley.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = HappyAlley.caf; sourceTree = ""; }; + 8CE86DBC15F8D9420060E004 /* Jupiter.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Jupiter.png; sourceTree = ""; }; + 8CE86DBD15F8D9420060E004 /* Next.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Next.png; sourceTree = ""; }; + 8CE86DBE15F8D9420060E004 /* panel-bg.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "panel-bg.png"; sourceTree = ""; }; + 8CE86DBF15F8D9420060E004 /* panel-lamp-off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "panel-lamp-off.png"; sourceTree = ""; }; + 8CE86DC015F8D9420060E004 /* panel-lamp-on.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "panel-lamp-on.png"; sourceTree = ""; }; + 8CE86DC115F8D9420060E004 /* panel-slider-knob.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "panel-slider-knob.png"; sourceTree = ""; }; + 8CE86DC215F8D9420060E004 /* panel-slider-track-long.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "panel-slider-track-long.png"; sourceTree = ""; }; + 8CE86DC315F8D9420060E004 /* panel-slider-track.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "panel-slider-track.png"; sourceTree = ""; }; + 8CE86DC415F8D9420060E004 /* panel-trim-horiz.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "panel-trim-horiz.png"; sourceTree = ""; }; + 8CE86DC515F8D9420060E004 /* panel-trim-vert.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "panel-trim-vert.png"; sourceTree = ""; }; + 8CE86DC615F8D9420060E004 /* Pew.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Pew.caf; sourceTree = ""; }; + 8CE86DC715F8D9420060E004 /* PlanetKiller.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = PlanetKiller.mp3; sourceTree = ""; }; + 8CE86DC815F8D9420060E004 /* Pow.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Pow.caf; sourceTree = ""; }; + 8CE86DC915F8D9420060E004 /* RocketShip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RocketShip.png; sourceTree = ""; }; + 8CE86DCA15F8D9420060E004 /* vu-face.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "vu-face.png"; sourceTree = ""; }; + 8CE86DCB15F8D9420060E004 /* vu-needle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "vu-needle.png"; sourceTree = ""; }; + 8CE86DCC15F8D9420060E004 /* vu-shell.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "vu-shell.png"; sourceTree = ""; }; + 8CE86DF015F8D9620060E004 /* IntroLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntroLayer.h; sourceTree = ""; }; + 8CE86DF115F8D9620060E004 /* IntroLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntroLayer.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -538,7 +737,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 390E99371482C4BE002E14E7 /* Foundation.framework in Frameworks */, + 8CE8694215F8C4590060E004 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -546,7 +745,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3914D74F147D57540010C4A2 /* Foundation.framework in Frameworks */, + 8CE8694015F8C42B0060E004 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -562,9 +761,9 @@ 398C16C5147D555E000EFEDF /* OpenAL.framework in Frameworks */, 398C16C7147D555E000EFEDF /* AudioToolbox.framework in Frameworks */, 398C16C9147D555E000EFEDF /* AVFoundation.framework in Frameworks */, - 398C16CA147D555E000EFEDF /* UIKit.framework in Frameworks */, - 398C16CB147D555E000EFEDF /* Foundation.framework in Frameworks */, + 8CE8694415F8C4860060E004 /* UIKit.framework in Frameworks */, 398C16CD147D555E000EFEDF /* CoreGraphics.framework in Frameworks */, + 8CE8694115F8C4590060E004 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -592,10 +791,10 @@ 3914D750147D57540010C4A2 /* cocos2d */ = { isa = PBXGroup; children = ( - 3914D759147D57D60010C4A2 /* cocos2d */, - 3914D802147D57D60010C4A2 /* FontLabel */, + 8CE867BE15F8C3940060E004 /* cocos2d */, + 8CE8694515F8C5880060E004 /* kazmath */, 3914D80E147D57D60010C4A2 /* LICENSE_cocos2d.txt */, - 3914D80F147D57D60010C4A2 /* LICENSE_FontLabel.txt */, + 8CE8698315F8C5930060E004 /* LICENSE_Kazmath.txt */, 3914D751147D57540010C4A2 /* Supporting Files */, ); path = cocos2d; @@ -609,227 +808,6 @@ name = "Supporting Files"; sourceTree = ""; }; - 3914D759147D57D60010C4A2 /* cocos2d */ = { - isa = PBXGroup; - children = ( - 3914D75A147D57D60010C4A2 /* CCAction.h */, - 3914D75B147D57D60010C4A2 /* CCAction.m */, - 3914D75C147D57D60010C4A2 /* CCActionCamera.h */, - 3914D75D147D57D60010C4A2 /* CCActionCamera.m */, - 3914D75E147D57D60010C4A2 /* CCActionEase.h */, - 3914D75F147D57D60010C4A2 /* CCActionEase.m */, - 3914D760147D57D60010C4A2 /* CCActionGrid.h */, - 3914D761147D57D60010C4A2 /* CCActionGrid.m */, - 3914D762147D57D60010C4A2 /* CCActionGrid3D.h */, - 3914D763147D57D60010C4A2 /* CCActionGrid3D.m */, - 3914D764147D57D60010C4A2 /* CCActionInstant.h */, - 3914D765147D57D60010C4A2 /* CCActionInstant.m */, - 3914D766147D57D60010C4A2 /* CCActionInterval.h */, - 3914D767147D57D60010C4A2 /* CCActionInterval.m */, - 3914D768147D57D60010C4A2 /* CCActionManager.h */, - 3914D769147D57D60010C4A2 /* CCActionManager.m */, - 3914D76A147D57D60010C4A2 /* CCActionPageTurn3D.h */, - 3914D76B147D57D60010C4A2 /* CCActionPageTurn3D.m */, - 3914D76C147D57D60010C4A2 /* CCActionProgressTimer.h */, - 3914D76D147D57D60010C4A2 /* CCActionProgressTimer.m */, - 3914D76E147D57D60010C4A2 /* CCActionTiledGrid.h */, - 3914D76F147D57D60010C4A2 /* CCActionTiledGrid.m */, - 3914D770147D57D60010C4A2 /* CCActionTween.h */, - 3914D771147D57D60010C4A2 /* CCActionTween.m */, - 3914D772147D57D60010C4A2 /* CCAnimation.h */, - 3914D773147D57D60010C4A2 /* CCAnimation.m */, - 3914D774147D57D60010C4A2 /* CCAnimationCache.h */, - 3914D775147D57D60010C4A2 /* CCAnimationCache.m */, - 3914D776147D57D60010C4A2 /* CCAtlasNode.h */, - 3914D777147D57D60010C4A2 /* CCAtlasNode.m */, - 3914D778147D57D60010C4A2 /* CCBlockSupport.h */, - 3914D779147D57D60010C4A2 /* CCBlockSupport.m */, - 3914D77A147D57D60010C4A2 /* CCCamera.h */, - 3914D77B147D57D60010C4A2 /* CCCamera.m */, - 3914D77C147D57D60010C4A2 /* ccConfig.h */, - 3914D77D147D57D60010C4A2 /* CCConfiguration.h */, - 3914D77E147D57D60010C4A2 /* CCConfiguration.m */, - 3914D77F147D57D60010C4A2 /* CCDirector.h */, - 3914D780147D57D60010C4A2 /* CCDirector.m */, - 3914D781147D57D60010C4A2 /* CCDrawingPrimitives.h */, - 3914D782147D57D60010C4A2 /* CCDrawingPrimitives.m */, - 3914D783147D57D60010C4A2 /* CCGrabber.h */, - 3914D784147D57D60010C4A2 /* CCGrabber.m */, - 3914D785147D57D60010C4A2 /* CCGrid.h */, - 3914D786147D57D60010C4A2 /* CCGrid.m */, - 3914D787147D57D60010C4A2 /* CCLabelAtlas.h */, - 3914D788147D57D60010C4A2 /* CCLabelAtlas.m */, - 3914D789147D57D60010C4A2 /* CCLabelBMFont.h */, - 3914D78A147D57D60010C4A2 /* CCLabelBMFont.m */, - 3914D78B147D57D60010C4A2 /* CCLabelTTF.h */, - 3914D78C147D57D60010C4A2 /* CCLabelTTF.m */, - 3914D78D147D57D60010C4A2 /* CCLayer.h */, - 3914D78E147D57D60010C4A2 /* CCLayer.m */, - 3914D78F147D57D60010C4A2 /* ccMacros.h */, - 3914D790147D57D60010C4A2 /* CCMenu.h */, - 3914D791147D57D60010C4A2 /* CCMenu.m */, - 3914D792147D57D60010C4A2 /* CCMenuItem.h */, - 3914D793147D57D60010C4A2 /* CCMenuItem.m */, - 3914D794147D57D60010C4A2 /* CCMotionStreak.h */, - 3914D795147D57D60010C4A2 /* CCMotionStreak.m */, - 3914D796147D57D60010C4A2 /* CCNode.h */, - 3914D797147D57D60010C4A2 /* CCNode.m */, - 3914D798147D57D60010C4A2 /* CCParallaxNode.h */, - 3914D799147D57D60010C4A2 /* CCParallaxNode.m */, - 3914D79A147D57D60010C4A2 /* CCParticleExamples.h */, - 3914D79B147D57D60010C4A2 /* CCParticleExamples.m */, - 3914D79C147D57D60010C4A2 /* CCParticleSystem.h */, - 3914D79D147D57D60010C4A2 /* CCParticleSystem.m */, - 3914D79E147D57D60010C4A2 /* CCParticleSystemPoint.h */, - 3914D79F147D57D60010C4A2 /* CCParticleSystemPoint.m */, - 3914D7A0147D57D60010C4A2 /* CCParticleSystemQuad.h */, - 3914D7A1147D57D60010C4A2 /* CCParticleSystemQuad.m */, - 3914D7A2147D57D60010C4A2 /* CCProgressTimer.h */, - 3914D7A3147D57D60010C4A2 /* CCProgressTimer.m */, - 3914D7A4147D57D60010C4A2 /* CCProtocols.h */, - 3914D7A5147D57D60010C4A2 /* CCRenderTexture.h */, - 3914D7A6147D57D60010C4A2 /* CCRenderTexture.m */, - 3914D7A7147D57D60010C4A2 /* CCRibbon.h */, - 3914D7A8147D57D60010C4A2 /* CCRibbon.m */, - 3914D7A9147D57D60010C4A2 /* CCScene.h */, - 3914D7AA147D57D60010C4A2 /* CCScene.m */, - 3914D7AB147D57D60010C4A2 /* CCScheduler.h */, - 3914D7AC147D57D60010C4A2 /* CCScheduler.m */, - 3914D7AD147D57D60010C4A2 /* CCSprite.h */, - 3914D7AE147D57D60010C4A2 /* CCSprite.m */, - 3914D7AF147D57D60010C4A2 /* CCSpriteBatchNode.h */, - 3914D7B0147D57D60010C4A2 /* CCSpriteBatchNode.m */, - 3914D7B1147D57D60010C4A2 /* CCSpriteFrame.h */, - 3914D7B2147D57D60010C4A2 /* CCSpriteFrame.m */, - 3914D7B3147D57D60010C4A2 /* CCSpriteFrameCache.h */, - 3914D7B4147D57D60010C4A2 /* CCSpriteFrameCache.m */, - 3914D7B5147D57D60010C4A2 /* CCTexture2D.h */, - 3914D7B6147D57D60010C4A2 /* CCTexture2D.m */, - 3914D7B7147D57D60010C4A2 /* CCTextureAtlas.h */, - 3914D7B8147D57D60010C4A2 /* CCTextureAtlas.m */, - 3914D7B9147D57D60010C4A2 /* CCTextureCache.h */, - 3914D7BA147D57D60010C4A2 /* CCTextureCache.m */, - 3914D7BB147D57D60010C4A2 /* CCTexturePVR.h */, - 3914D7BC147D57D60010C4A2 /* CCTexturePVR.m */, - 3914D7BD147D57D60010C4A2 /* CCTileMapAtlas.h */, - 3914D7BE147D57D60010C4A2 /* CCTileMapAtlas.m */, - 3914D7BF147D57D60010C4A2 /* CCTMXLayer.h */, - 3914D7C0147D57D60010C4A2 /* CCTMXLayer.m */, - 3914D7C1147D57D60010C4A2 /* CCTMXObjectGroup.h */, - 3914D7C2147D57D60010C4A2 /* CCTMXObjectGroup.m */, - 3914D7C3147D57D60010C4A2 /* CCTMXTiledMap.h */, - 3914D7C4147D57D60010C4A2 /* CCTMXTiledMap.m */, - 3914D7C5147D57D60010C4A2 /* CCTMXXMLParser.h */, - 3914D7C6147D57D60010C4A2 /* CCTMXXMLParser.m */, - 3914D7C7147D57D60010C4A2 /* CCTransition.h */, - 3914D7C8147D57D60010C4A2 /* CCTransition.m */, - 3914D7C9147D57D60010C4A2 /* CCTransitionPageTurn.h */, - 3914D7CA147D57D60010C4A2 /* CCTransitionPageTurn.m */, - 3914D7CB147D57D60010C4A2 /* CCTransitionRadial.h */, - 3914D7CC147D57D60010C4A2 /* CCTransitionRadial.m */, - 3914D7CD147D57D60010C4A2 /* ccTypes.h */, - 3914D7CE147D57D60010C4A2 /* cocos2d.h */, - 3914D7CF147D57D60010C4A2 /* cocos2d.m */, - 3914D7D0147D57D60010C4A2 /* Platforms */, - 3914D7EB147D57D60010C4A2 /* Support */, - ); - path = cocos2d; - sourceTree = ""; - }; - 3914D7D0147D57D60010C4A2 /* Platforms */ = { - isa = PBXGroup; - children = ( - 3914D7D1147D57D60010C4A2 /* CCGL.h */, - 3914D7D2147D57D60010C4A2 /* CCNS.h */, - 3914D7D3147D57D60010C4A2 /* iOS */, - 3914D7E2147D57D60010C4A2 /* Mac */, - ); - path = Platforms; - sourceTree = ""; - }; - 3914D7D3147D57D60010C4A2 /* iOS */ = { - isa = PBXGroup; - children = ( - 3914D7D4147D57D60010C4A2 /* CCDirectorIOS.h */, - 3914D7D5147D57D60010C4A2 /* CCDirectorIOS.m */, - 3914D7D6147D57D60010C4A2 /* CCTouchDelegateProtocol.h */, - 3914D7D7147D57D60010C4A2 /* CCTouchDispatcher.h */, - 3914D7D8147D57D60010C4A2 /* CCTouchDispatcher.m */, - 3914D7D9147D57D60010C4A2 /* CCTouchHandler.h */, - 3914D7DA147D57D60010C4A2 /* CCTouchHandler.m */, - 3914D7DB147D57D60010C4A2 /* EAGLView.h */, - 3914D7DC147D57D60010C4A2 /* EAGLView.m */, - 3914D7DD147D57D60010C4A2 /* ES1Renderer.h */, - 3914D7DE147D57D60010C4A2 /* ES1Renderer.m */, - 3914D7DF147D57D60010C4A2 /* ESRenderer.h */, - 3914D7E0147D57D60010C4A2 /* glu.c */, - 3914D7E1147D57D60010C4A2 /* glu.h */, - ); - path = iOS; - sourceTree = ""; - }; - 3914D7E2147D57D60010C4A2 /* Mac */ = { - isa = PBXGroup; - children = ( - 3914D7E3147D57D60010C4A2 /* CCDirectorMac.h */, - 3914D7E4147D57D60010C4A2 /* CCDirectorMac.m */, - 3914D7E5147D57D60010C4A2 /* CCEventDispatcher.h */, - 3914D7E6147D57D60010C4A2 /* CCEventDispatcher.m */, - 3914D7E7147D57D60010C4A2 /* MacGLView.h */, - 3914D7E8147D57D60010C4A2 /* MacGLView.m */, - 3914D7E9147D57D60010C4A2 /* MacWindow.h */, - 3914D7EA147D57D60010C4A2 /* MacWindow.m */, - ); - path = Mac; - sourceTree = ""; - }; - 3914D7EB147D57D60010C4A2 /* Support */ = { - isa = PBXGroup; - children = ( - 3914D7EC147D57D60010C4A2 /* base64.c */, - 3914D7ED147D57D60010C4A2 /* base64.h */, - 3914D7EE147D57D60010C4A2 /* CCArray.h */, - 3914D7EF147D57D60010C4A2 /* CCArray.m */, - 3914D7F0147D57D60010C4A2 /* ccCArray.h */, - 3914D7F1147D57D60010C4A2 /* CCFileUtils.h */, - 3914D7F2147D57D60010C4A2 /* CCFileUtils.m */, - 3914D7F3147D57D60010C4A2 /* CCProfiling.h */, - 3914D7F4147D57D60010C4A2 /* CCProfiling.m */, - 3914D7F5147D57D60010C4A2 /* ccUtils.c */, - 3914D7F6147D57D60010C4A2 /* ccUtils.h */, - 3914D7F7147D57D60010C4A2 /* CGPointExtension.h */, - 3914D7F8147D57D60010C4A2 /* CGPointExtension.m */, - 3914D7F9147D57D60010C4A2 /* OpenGL_Internal.h */, - 3914D7FA147D57D60010C4A2 /* TGAlib.h */, - 3914D7FB147D57D60010C4A2 /* TGAlib.m */, - 3914D7FC147D57D60010C4A2 /* TransformUtils.h */, - 3914D7FD147D57D60010C4A2 /* TransformUtils.m */, - 3914D7FE147D57D60010C4A2 /* uthash.h */, - 3914D7FF147D57D60010C4A2 /* utlist.h */, - 3914D800147D57D60010C4A2 /* ZipUtils.h */, - 3914D801147D57D60010C4A2 /* ZipUtils.m */, - ); - path = Support; - sourceTree = ""; - }; - 3914D802147D57D60010C4A2 /* FontLabel */ = { - isa = PBXGroup; - children = ( - 3914D803147D57D60010C4A2 /* FontLabel.h */, - 3914D804147D57D60010C4A2 /* FontLabel.m */, - 3914D805147D57D60010C4A2 /* FontLabelStringDrawing.h */, - 3914D806147D57D60010C4A2 /* FontLabelStringDrawing.m */, - 3914D807147D57D60010C4A2 /* FontManager.h */, - 3914D808147D57D60010C4A2 /* FontManager.m */, - 3914D809147D57D60010C4A2 /* ZAttributedString.h */, - 3914D80A147D57D60010C4A2 /* ZAttributedString.m */, - 3914D80B147D57D60010C4A2 /* ZAttributedStringPrivate.h */, - 3914D80C147D57D60010C4A2 /* ZFont.h */, - 3914D80D147D57D60010C4A2 /* ZFont.m */, - ); - path = FontLabel; - sourceTree = ""; - }; 3914D8C0147D58F00010C4A2 /* Actions */ = { isa = PBXGroup; children = ( @@ -1054,8 +1032,8 @@ children = ( 39F89E9E14831C3500E40CFE /* libz.dylib */, 398C16A0147D5533000EFEDF /* SenTestingKit.framework */, - 398C16A2147D5533000EFEDF /* UIKit.framework */, - 398C16A4147D5533000EFEDF /* Foundation.framework */, + 8CE8694315F8C4860060E004 /* UIKit.framework */, + 8CE8693F15F8C42B0060E004 /* Foundation.framework */, 398C16C0147D555E000EFEDF /* QuartzCore.framework */, 398C16C2147D555E000EFEDF /* OpenGLES.framework */, 398C16C4147D555E000EFEDF /* OpenAL.framework */, @@ -1092,11 +1070,10 @@ 3914D97A147D5B570010C4A2 /* Support */, 398C1820147D5560000EFEDF /* AppDelegate.h */, 398C1821147D5560000EFEDF /* AppDelegate.m */, - 398C1823147D5560000EFEDF /* RootViewController.h */, - 398C1824147D5560000EFEDF /* RootViewController.m */, + 8CE86DF015F8D9620060E004 /* IntroLayer.h */, + 8CE86DF115F8D9620060E004 /* IntroLayer.m */, 398C1826147D5560000EFEDF /* MainScene.h */, 398C1827147D5560000EFEDF /* MainScene.m */, - 398C1829147D5560000EFEDF /* GameConfig.h */, 398C16CF147D555E000EFEDF /* Resources */, 398C181C147D5560000EFEDF /* Supporting Files */, ); @@ -1106,32 +1083,20 @@ 398C16CF147D555E000EFEDF /* Resources */ = { isa = PBXGroup; children = ( - 398C16D0147D555E000EFEDF /* Default.png */, - 398C16D2147D555E000EFEDF /* fps_images.png */, - 398C16DC147D555E000EFEDF /* Icon.png */, + 8CE86DB515F8D9420060E004 /* ObjectALDemo */, + 8CE86DA915F8D9420060E004 /* Default-Landscape~ipad.png */, + 8CE86DAA15F8D9420060E004 /* Default.png */, + 8CE86DAB15F8D9420060E004 /* fps_images-hd.png */, + 8CE86DAC15F8D9420060E004 /* fps_images-ipadhd.png */, + 8CE86DAD15F8D9420060E004 /* fps_images.png */, + 8CE86DAE15F8D9420060E004 /* Icon-72.png */, + 8CE86DAF15F8D9420060E004 /* Icon-Small-50.png */, + 8CE86DB015F8D9420060E004 /* Icon-Small.png */, + 8CE86DB115F8D9420060E004 /* Icon-Small@2x.png */, + 8CE86DB215F8D9420060E004 /* Icon.png */, + 8CE86DB315F8D9420060E004 /* Icon@2x.png */, + 8CE86DB415F8D9420060E004 /* iTunesArtwork */, 398C16E0147D555E000EFEDF /* Info.plist */, - 3914D9C3147D5BDF0010C4A2 /* Back.png */, - 3914D9C4147D5BDF0010C4A2 /* ColdFunk.caf */, - 3914D9C5147D5BDF0010C4A2 /* Exit.png */, - 3914D9C6147D5BDF0010C4A2 /* Ganymede.png */, - 3914D9C7147D5BDF0010C4A2 /* HappyAlley.caf */, - 3914D9C8147D5BDF0010C4A2 /* Jupiter.png */, - 3914D9C9147D5BDF0010C4A2 /* Next.png */, - 3914D9CA147D5BDF0010C4A2 /* panel-bg.png */, - 3914D9CB147D5BDF0010C4A2 /* panel-lamp-off.png */, - 3914D9CC147D5BDF0010C4A2 /* panel-lamp-on.png */, - 3914D9CD147D5BDF0010C4A2 /* panel-slider-knob.png */, - 3914D9CE147D5BDF0010C4A2 /* panel-slider-track-long.png */, - 3914D9CF147D5BDF0010C4A2 /* panel-slider-track.png */, - 3914D9D0147D5BDF0010C4A2 /* panel-trim-horiz.png */, - 3914D9D1147D5BDF0010C4A2 /* panel-trim-vert.png */, - 3914D9D2147D5BDF0010C4A2 /* Pew.caf */, - 3914D9D3147D5BDF0010C4A2 /* PlanetKiller.mp3 */, - 3914D9D4147D5BDF0010C4A2 /* Pow.caf */, - 3914D9D5147D5BDF0010C4A2 /* RocketShip.png */, - 3914D9D6147D5BDF0010C4A2 /* vu-face.png */, - 3914D9D7147D5BDF0010C4A2 /* vu-needle.png */, - 3914D9D8147D5BDF0010C4A2 /* vu-shell.png */, ); name = Resources; sourceTree = ""; @@ -1145,6 +1110,342 @@ name = "Supporting Files"; sourceTree = ""; }; + 8CE867BE15F8C3940060E004 /* cocos2d */ = { + isa = PBXGroup; + children = ( + 8CE867BF15F8C3940060E004 /* CCAction.h */, + 8CE867C015F8C3940060E004 /* CCAction.m */, + 8CE867C115F8C3940060E004 /* CCActionCamera.h */, + 8CE867C215F8C3940060E004 /* CCActionCamera.m */, + 8CE867C315F8C3940060E004 /* CCActionCatmullRom.h */, + 8CE867C415F8C3940060E004 /* CCActionCatmullRom.m */, + 8CE867C515F8C3940060E004 /* CCActionEase.h */, + 8CE867C615F8C3940060E004 /* CCActionEase.m */, + 8CE867C715F8C3940060E004 /* CCActionGrid.h */, + 8CE867C815F8C3940060E004 /* CCActionGrid.m */, + 8CE867C915F8C3940060E004 /* CCActionGrid3D.h */, + 8CE867CA15F8C3940060E004 /* CCActionGrid3D.m */, + 8CE867CB15F8C3940060E004 /* CCActionInstant.h */, + 8CE867CC15F8C3940060E004 /* CCActionInstant.m */, + 8CE867CD15F8C3940060E004 /* CCActionInterval.h */, + 8CE867CE15F8C3940060E004 /* CCActionInterval.m */, + 8CE867CF15F8C3940060E004 /* CCActionManager.h */, + 8CE867D015F8C3940060E004 /* CCActionManager.m */, + 8CE867D115F8C3940060E004 /* CCActionPageTurn3D.h */, + 8CE867D215F8C3940060E004 /* CCActionPageTurn3D.m */, + 8CE867D315F8C3940060E004 /* CCActionProgressTimer.h */, + 8CE867D415F8C3940060E004 /* CCActionProgressTimer.m */, + 8CE867D515F8C3940060E004 /* CCActionTiledGrid.h */, + 8CE867D615F8C3940060E004 /* CCActionTiledGrid.m */, + 8CE867D715F8C3940060E004 /* CCActionTween.h */, + 8CE867D815F8C3940060E004 /* CCActionTween.m */, + 8CE867D915F8C3940060E004 /* CCAnimation.h */, + 8CE867DA15F8C3940060E004 /* CCAnimation.m */, + 8CE867DB15F8C3940060E004 /* CCAnimationCache.h */, + 8CE867DC15F8C3940060E004 /* CCAnimationCache.m */, + 8CE867DD15F8C3940060E004 /* CCAtlasNode.h */, + 8CE867DE15F8C3940060E004 /* CCAtlasNode.m */, + 8CE867DF15F8C3940060E004 /* CCCamera.h */, + 8CE867E015F8C3940060E004 /* CCCamera.m */, + 8CE867E115F8C3940060E004 /* ccConfig.h */, + 8CE867E215F8C3940060E004 /* CCConfiguration.h */, + 8CE867E315F8C3940060E004 /* CCConfiguration.m */, + 8CE867E415F8C3940060E004 /* ccDeprecated.h */, + 8CE867E515F8C3940060E004 /* ccDeprecated.m */, + 8CE867E615F8C3940060E004 /* CCDirector.h */, + 8CE867E715F8C3940060E004 /* CCDirector.m */, + 8CE867E815F8C3940060E004 /* CCDrawingPrimitives.h */, + 8CE867E915F8C3940060E004 /* CCDrawingPrimitives.m */, + 8CE867EA15F8C3940060E004 /* CCGLProgram.h */, + 8CE867EB15F8C3940060E004 /* CCGLProgram.m */, + 8CE867EC15F8C3940060E004 /* ccGLStateCache.h */, + 8CE867ED15F8C3940060E004 /* ccGLStateCache.m */, + 8CE867EE15F8C3940060E004 /* CCGrabber.h */, + 8CE867EF15F8C3940060E004 /* CCGrabber.m */, + 8CE867F015F8C3940060E004 /* CCGrid.h */, + 8CE867F115F8C3940060E004 /* CCGrid.m */, + 8CE867F215F8C3940060E004 /* CCLabelAtlas.h */, + 8CE867F315F8C3940060E004 /* CCLabelAtlas.m */, + 8CE867F415F8C3940060E004 /* CCLabelBMFont.h */, + 8CE867F515F8C3940060E004 /* CCLabelBMFont.m */, + 8CE867F615F8C3940060E004 /* CCLabelTTF.h */, + 8CE867F715F8C3940060E004 /* CCLabelTTF.m */, + 8CE867F815F8C3940060E004 /* CCLayer.h */, + 8CE867F915F8C3940060E004 /* CCLayer.m */, + 8CE867FA15F8C3940060E004 /* ccMacros.h */, + 8CE867FB15F8C3940060E004 /* CCMenu.h */, + 8CE867FC15F8C3940060E004 /* CCMenu.m */, + 8CE867FD15F8C3940060E004 /* CCMenuItem.h */, + 8CE867FE15F8C3940060E004 /* CCMenuItem.m */, + 8CE867FF15F8C3940060E004 /* CCMotionStreak.h */, + 8CE8680015F8C3940060E004 /* CCMotionStreak.m */, + 8CE8680115F8C3940060E004 /* CCNode+Debug.h */, + 8CE8680215F8C3940060E004 /* CCNode+Debug.m */, + 8CE8680315F8C3940060E004 /* CCNode.h */, + 8CE8680415F8C3940060E004 /* CCNode.m */, + 8CE8680515F8C3940060E004 /* CCParallaxNode.h */, + 8CE8680615F8C3940060E004 /* CCParallaxNode.m */, + 8CE8680715F8C3940060E004 /* CCParticleBatchNode.h */, + 8CE8680815F8C3940060E004 /* CCParticleBatchNode.m */, + 8CE8680915F8C3940060E004 /* CCParticleExamples.h */, + 8CE8680A15F8C3940060E004 /* CCParticleExamples.m */, + 8CE8680B15F8C3940060E004 /* CCParticleSystem.h */, + 8CE8680C15F8C3940060E004 /* CCParticleSystem.m */, + 8CE8680D15F8C3940060E004 /* CCParticleSystemQuad.h */, + 8CE8680E15F8C3940060E004 /* CCParticleSystemQuad.m */, + 8CE8680F15F8C3940060E004 /* CCProgressTimer.h */, + 8CE8681015F8C3940060E004 /* CCProgressTimer.m */, + 8CE8681115F8C3940060E004 /* CCProtocols.h */, + 8CE8681215F8C3940060E004 /* CCRenderTexture.h */, + 8CE8681315F8C3940060E004 /* CCRenderTexture.m */, + 8CE8681415F8C3940060E004 /* CCScene.h */, + 8CE8681515F8C3940060E004 /* CCScene.m */, + 8CE8681615F8C3940060E004 /* CCScheduler.h */, + 8CE8681715F8C3940060E004 /* CCScheduler.m */, + 8CE8681815F8C3940060E004 /* ccShader_Position_uColor_frag.h */, + 8CE8681915F8C3940060E004 /* ccShader_Position_uColor_vert.h */, + 8CE8681A15F8C3940060E004 /* ccShader_PositionColor_frag.h */, + 8CE8681B15F8C3940060E004 /* ccShader_PositionColor_vert.h */, + 8CE8681C15F8C3940060E004 /* ccShader_PositionTexture_frag.h */, + 8CE8681D15F8C3940060E004 /* ccShader_PositionTexture_uColor_frag.h */, + 8CE8681E15F8C3940060E004 /* ccShader_PositionTexture_uColor_vert.h */, + 8CE8681F15F8C3940060E004 /* ccShader_PositionTexture_vert.h */, + 8CE8682015F8C3940060E004 /* ccShader_PositionTextureA8Color_frag.h */, + 8CE8682115F8C3940060E004 /* ccShader_PositionTextureA8Color_vert.h */, + 8CE8682215F8C3940060E004 /* ccShader_PositionTextureColor_frag.h */, + 8CE8682315F8C3940060E004 /* ccShader_PositionTextureColor_vert.h */, + 8CE8682415F8C3940060E004 /* ccShader_PositionTextureColorAlphaTest_frag.h */, + 8CE8682515F8C3940060E004 /* CCShaderCache.h */, + 8CE8682615F8C3940060E004 /* CCShaderCache.m */, + 8CE8682715F8C3940060E004 /* ccShaders.h */, + 8CE8682815F8C3940060E004 /* ccShaders.m */, + 8CE8682915F8C3940060E004 /* CCSprite.h */, + 8CE8682A15F8C3940060E004 /* CCSprite.m */, + 8CE8682B15F8C3940060E004 /* CCSpriteBatchNode.h */, + 8CE8682C15F8C3940060E004 /* CCSpriteBatchNode.m */, + 8CE8682D15F8C3940060E004 /* CCSpriteFrame.h */, + 8CE8682E15F8C3940060E004 /* CCSpriteFrame.m */, + 8CE8682F15F8C3940060E004 /* CCSpriteFrameCache.h */, + 8CE8683015F8C3940060E004 /* CCSpriteFrameCache.m */, + 8CE8683115F8C3940060E004 /* CCTexture2D.h */, + 8CE8683215F8C3940060E004 /* CCTexture2D.m */, + 8CE8683315F8C3940060E004 /* CCTextureAtlas.h */, + 8CE8683415F8C3940060E004 /* CCTextureAtlas.m */, + 8CE8683515F8C3940060E004 /* CCTextureCache.h */, + 8CE8683615F8C3940060E004 /* CCTextureCache.m */, + 8CE8683715F8C3940060E004 /* CCTexturePVR.h */, + 8CE8683815F8C3940060E004 /* CCTexturePVR.m */, + 8CE8683915F8C3940060E004 /* CCTileMapAtlas.h */, + 8CE8683A15F8C3940060E004 /* CCTileMapAtlas.m */, + 8CE8683B15F8C3940060E004 /* CCTMXLayer.h */, + 8CE8683C15F8C3940060E004 /* CCTMXLayer.m */, + 8CE8683D15F8C3940060E004 /* CCTMXObjectGroup.h */, + 8CE8683E15F8C3940060E004 /* CCTMXObjectGroup.m */, + 8CE8683F15F8C3940060E004 /* CCTMXTiledMap.h */, + 8CE8684015F8C3940060E004 /* CCTMXTiledMap.m */, + 8CE8684115F8C3940060E004 /* CCTMXXMLParser.h */, + 8CE8684215F8C3940060E004 /* CCTMXXMLParser.m */, + 8CE8684315F8C3940060E004 /* CCTransition.h */, + 8CE8684415F8C3940060E004 /* CCTransition.m */, + 8CE8684515F8C3940060E004 /* CCTransitionPageTurn.h */, + 8CE8684615F8C3940060E004 /* CCTransitionPageTurn.m */, + 8CE8684715F8C3940060E004 /* CCTransitionProgress.h */, + 8CE8684815F8C3940060E004 /* CCTransitionProgress.m */, + 8CE8684915F8C3940060E004 /* ccTypes.h */, + 8CE8684A15F8C3940060E004 /* cocos2d.h */, + 8CE8684B15F8C3940060E004 /* cocos2d.m */, + 8CE8684C15F8C3940060E004 /* Platforms */, + 8CE8686515F8C3940060E004 /* Support */, + ); + path = cocos2d; + sourceTree = ""; + }; + 8CE8684C15F8C3940060E004 /* Platforms */ = { + isa = PBXGroup; + children = ( + 8CE8684D15F8C3940060E004 /* CCGL.h */, + 8CE8684E15F8C3940060E004 /* CCNS.h */, + 8CE8684F15F8C3940060E004 /* iOS */, + 8CE8685C15F8C3940060E004 /* Mac */, + ); + path = Platforms; + sourceTree = ""; + }; + 8CE8684F15F8C3940060E004 /* iOS */ = { + isa = PBXGroup; + children = ( + 8CE8685015F8C3940060E004 /* CCDirectorIOS.h */, + 8CE8685115F8C3940060E004 /* CCDirectorIOS.m */, + 8CE8685215F8C3940060E004 /* CCES2Renderer.h */, + 8CE8685315F8C3940060E004 /* CCES2Renderer.m */, + 8CE8685415F8C3940060E004 /* CCESRenderer.h */, + 8CE8685515F8C3940060E004 /* CCGLView.h */, + 8CE8685615F8C3940060E004 /* CCGLView.m */, + 8CE8685715F8C3940060E004 /* CCTouchDelegateProtocol.h */, + 8CE8685815F8C3940060E004 /* CCTouchDispatcher.h */, + 8CE8685915F8C3940060E004 /* CCTouchDispatcher.m */, + 8CE8685A15F8C3940060E004 /* CCTouchHandler.h */, + 8CE8685B15F8C3940060E004 /* CCTouchHandler.m */, + ); + path = iOS; + sourceTree = ""; + }; + 8CE8685C15F8C3940060E004 /* Mac */ = { + isa = PBXGroup; + children = ( + 8CE8685D15F8C3940060E004 /* CCDirectorMac.h */, + 8CE8685E15F8C3940060E004 /* CCDirectorMac.m */, + 8CE8685F15F8C3940060E004 /* CCEventDispatcher.h */, + 8CE8686015F8C3940060E004 /* CCEventDispatcher.m */, + 8CE8686115F8C3940060E004 /* CCGLView.h */, + 8CE8686215F8C3940060E004 /* CCGLView.m */, + 8CE8686315F8C3940060E004 /* CCWindow.h */, + 8CE8686415F8C3940060E004 /* CCWindow.m */, + ); + path = Mac; + sourceTree = ""; + }; + 8CE8686515F8C3940060E004 /* Support */ = { + isa = PBXGroup; + children = ( + 8CE8686615F8C3940060E004 /* base64.c */, + 8CE8686715F8C3940060E004 /* base64.h */, + 8CE8686815F8C3940060E004 /* CCArray.h */, + 8CE8686915F8C3940060E004 /* CCArray.m */, + 8CE8686A15F8C3940060E004 /* ccCArray.h */, + 8CE8686B15F8C3940060E004 /* ccCArray.m */, + 8CE8686C15F8C3940060E004 /* CCFileUtils.h */, + 8CE8686D15F8C3940060E004 /* CCFileUtils.m */, + 8CE8686E15F8C3940060E004 /* CCProfiling.h */, + 8CE8686F15F8C3940060E004 /* CCProfiling.m */, + 8CE8687015F8C3940060E004 /* ccUtils.c */, + 8CE8687115F8C3940060E004 /* ccUtils.h */, + 8CE8687215F8C3940060E004 /* CCVertex.h */, + 8CE8687315F8C3940060E004 /* CCVertex.m */, + 8CE8687415F8C3940060E004 /* CGPointExtension.h */, + 8CE8687515F8C3940060E004 /* CGPointExtension.m */, + 8CE8687615F8C3940060E004 /* NSThread+performBlock.h */, + 8CE8687715F8C3940060E004 /* NSThread+performBlock.m */, + 8CE8687815F8C3940060E004 /* OpenGL_Internal.h */, + 8CE8687915F8C3940060E004 /* TGAlib.h */, + 8CE8687A15F8C3940060E004 /* TGAlib.m */, + 8CE8687B15F8C3940060E004 /* TransformUtils.h */, + 8CE8687C15F8C3940060E004 /* TransformUtils.m */, + 8CE8687D15F8C3940060E004 /* uthash.h */, + 8CE8687E15F8C3940060E004 /* utlist.h */, + 8CE8687F15F8C3940060E004 /* ZipUtils.h */, + 8CE8688015F8C3940060E004 /* ZipUtils.m */, + ); + path = Support; + sourceTree = ""; + }; + 8CE8694515F8C5880060E004 /* kazmath */ = { + isa = PBXGroup; + children = ( + 8CE8694615F8C5880060E004 /* include */, + 8CE8695715F8C5880060E004 /* src */, + ); + path = kazmath; + sourceTree = ""; + }; + 8CE8694615F8C5880060E004 /* include */ = { + isa = PBXGroup; + children = ( + 8CE8694715F8C5880060E004 /* kazmath */, + ); + path = include; + sourceTree = ""; + }; + 8CE8694715F8C5880060E004 /* kazmath */ = { + isa = PBXGroup; + children = ( + 8CE8694815F8C5880060E004 /* aabb.h */, + 8CE8694915F8C5880060E004 /* GL */, + 8CE8694C15F8C5880060E004 /* kazmath.h */, + 8CE8694D15F8C5880060E004 /* mat3.h */, + 8CE8694E15F8C5880060E004 /* mat4.h */, + 8CE8694F15F8C5880060E004 /* neon_matrix_impl.h */, + 8CE8695015F8C5880060E004 /* plane.h */, + 8CE8695115F8C5880060E004 /* quaternion.h */, + 8CE8695215F8C5880060E004 /* ray2.h */, + 8CE8695315F8C5880060E004 /* utility.h */, + 8CE8695415F8C5880060E004 /* vec2.h */, + 8CE8695515F8C5880060E004 /* vec3.h */, + 8CE8695615F8C5880060E004 /* vec4.h */, + ); + path = kazmath; + sourceTree = ""; + }; + 8CE8694915F8C5880060E004 /* GL */ = { + isa = PBXGroup; + children = ( + 8CE8694A15F8C5880060E004 /* mat4stack.h */, + 8CE8694B15F8C5880060E004 /* matrix.h */, + ); + path = GL; + sourceTree = ""; + }; + 8CE8695715F8C5880060E004 /* src */ = { + isa = PBXGroup; + children = ( + 8CE8695815F8C5880060E004 /* aabb.c */, + 8CE8695915F8C5880060E004 /* ChangeLog */, + 8CE8695A15F8C5880060E004 /* CMakeLists.txt */, + 8CE8695B15F8C5880060E004 /* GL */, + 8CE8695E15F8C5880060E004 /* mat3.c */, + 8CE8695F15F8C5880060E004 /* mat4.c */, + 8CE8696015F8C5880060E004 /* neon_matrix_impl.c */, + 8CE8696115F8C5880060E004 /* plane.c */, + 8CE8696215F8C5880060E004 /* quaternion.c */, + 8CE8696315F8C5880060E004 /* ray2.c */, + 8CE8696415F8C5880060E004 /* utility.c */, + 8CE8696515F8C5880060E004 /* vec2.c */, + 8CE8696615F8C5880060E004 /* vec3.c */, + 8CE8696715F8C5880060E004 /* vec4.c */, + ); + path = src; + sourceTree = ""; + }; + 8CE8695B15F8C5880060E004 /* GL */ = { + isa = PBXGroup; + children = ( + 8CE8695C15F8C5880060E004 /* mat4stack.c */, + 8CE8695D15F8C5880060E004 /* matrix.c */, + ); + path = GL; + sourceTree = ""; + }; + 8CE86DB515F8D9420060E004 /* ObjectALDemo */ = { + isa = PBXGroup; + children = ( + 8CE86DB615F8D9420060E004 /* AssetSources.txt */, + 8CE86DB715F8D9420060E004 /* Back.png */, + 8CE86DB815F8D9420060E004 /* ColdFunk.caf */, + 8CE86DB915F8D9420060E004 /* Exit.png */, + 8CE86DBA15F8D9420060E004 /* Ganymede.png */, + 8CE86DBB15F8D9420060E004 /* HappyAlley.caf */, + 8CE86DBC15F8D9420060E004 /* Jupiter.png */, + 8CE86DBD15F8D9420060E004 /* Next.png */, + 8CE86DBE15F8D9420060E004 /* panel-bg.png */, + 8CE86DBF15F8D9420060E004 /* panel-lamp-off.png */, + 8CE86DC015F8D9420060E004 /* panel-lamp-on.png */, + 8CE86DC115F8D9420060E004 /* panel-slider-knob.png */, + 8CE86DC215F8D9420060E004 /* panel-slider-track-long.png */, + 8CE86DC315F8D9420060E004 /* panel-slider-track.png */, + 8CE86DC415F8D9420060E004 /* panel-trim-horiz.png */, + 8CE86DC515F8D9420060E004 /* panel-trim-vert.png */, + 8CE86DC615F8D9420060E004 /* Pew.caf */, + 8CE86DC715F8D9420060E004 /* PlanetKiller.mp3 */, + 8CE86DC815F8D9420060E004 /* Pow.caf */, + 8CE86DC915F8D9420060E004 /* RocketShip.png */, + 8CE86DCA15F8D9420060E004 /* vu-face.png */, + 8CE86DCB15F8D9420060E004 /* vu-needle.png */, + 8CE86DCC15F8D9420060E004 /* vu-shell.png */, + ); + name = ObjectALDemo; + path = Resources/ObjectALDemo; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1160,6 +1461,127 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 8CE8688115F8C3940060E004 /* CCAction.h in Headers */, + 8CE8688315F8C3940060E004 /* CCActionCamera.h in Headers */, + 8CE8688515F8C3940060E004 /* CCActionCatmullRom.h in Headers */, + 8CE8688715F8C3940060E004 /* CCActionEase.h in Headers */, + 8CE8688915F8C3940060E004 /* CCActionGrid.h in Headers */, + 8CE8688B15F8C3940060E004 /* CCActionGrid3D.h in Headers */, + 8CE8688D15F8C3940060E004 /* CCActionInstant.h in Headers */, + 8CE8688F15F8C3940060E004 /* CCActionInterval.h in Headers */, + 8CE8689115F8C3940060E004 /* CCActionManager.h in Headers */, + 8CE8689315F8C3940060E004 /* CCActionPageTurn3D.h in Headers */, + 8CE8689515F8C3940060E004 /* CCActionProgressTimer.h in Headers */, + 8CE8689715F8C3940060E004 /* CCActionTiledGrid.h in Headers */, + 8CE8689915F8C3940060E004 /* CCActionTween.h in Headers */, + 8CE8689B15F8C3940060E004 /* CCAnimation.h in Headers */, + 8CE8689D15F8C3940060E004 /* CCAnimationCache.h in Headers */, + 8CE8689F15F8C3940060E004 /* CCAtlasNode.h in Headers */, + 8CE868A115F8C3940060E004 /* CCCamera.h in Headers */, + 8CE868A315F8C3940060E004 /* ccConfig.h in Headers */, + 8CE868A415F8C3940060E004 /* CCConfiguration.h in Headers */, + 8CE868A615F8C3940060E004 /* ccDeprecated.h in Headers */, + 8CE868A815F8C3940060E004 /* CCDirector.h in Headers */, + 8CE868AA15F8C3940060E004 /* CCDrawingPrimitives.h in Headers */, + 8CE868AC15F8C3940060E004 /* CCGLProgram.h in Headers */, + 8CE868AE15F8C3940060E004 /* ccGLStateCache.h in Headers */, + 8CE868B015F8C3940060E004 /* CCGrabber.h in Headers */, + 8CE868B215F8C3940060E004 /* CCGrid.h in Headers */, + 8CE868B415F8C3940060E004 /* CCLabelAtlas.h in Headers */, + 8CE868B615F8C3940060E004 /* CCLabelBMFont.h in Headers */, + 8CE868B815F8C3940060E004 /* CCLabelTTF.h in Headers */, + 8CE868BA15F8C3940060E004 /* CCLayer.h in Headers */, + 8CE868BC15F8C3940060E004 /* ccMacros.h in Headers */, + 8CE868BD15F8C3940060E004 /* CCMenu.h in Headers */, + 8CE868BF15F8C3940060E004 /* CCMenuItem.h in Headers */, + 8CE868C115F8C3940060E004 /* CCMotionStreak.h in Headers */, + 8CE868C315F8C3940060E004 /* CCNode+Debug.h in Headers */, + 8CE868C515F8C3940060E004 /* CCNode.h in Headers */, + 8CE868C715F8C3940060E004 /* CCParallaxNode.h in Headers */, + 8CE868C915F8C3940060E004 /* CCParticleBatchNode.h in Headers */, + 8CE868CB15F8C3940060E004 /* CCParticleExamples.h in Headers */, + 8CE868CD15F8C3940060E004 /* CCParticleSystem.h in Headers */, + 8CE868CF15F8C3940060E004 /* CCParticleSystemQuad.h in Headers */, + 8CE868D115F8C3940060E004 /* CCProgressTimer.h in Headers */, + 8CE868D315F8C3940060E004 /* CCProtocols.h in Headers */, + 8CE868D415F8C3940060E004 /* CCRenderTexture.h in Headers */, + 8CE868D615F8C3940060E004 /* CCScene.h in Headers */, + 8CE868D815F8C3940060E004 /* CCScheduler.h in Headers */, + 8CE868DA15F8C3940060E004 /* ccShader_Position_uColor_frag.h in Headers */, + 8CE868DB15F8C3940060E004 /* ccShader_Position_uColor_vert.h in Headers */, + 8CE868DC15F8C3940060E004 /* ccShader_PositionColor_frag.h in Headers */, + 8CE868DD15F8C3940060E004 /* ccShader_PositionColor_vert.h in Headers */, + 8CE868DE15F8C3940060E004 /* ccShader_PositionTexture_frag.h in Headers */, + 8CE868DF15F8C3940060E004 /* ccShader_PositionTexture_uColor_frag.h in Headers */, + 8CE868E015F8C3940060E004 /* ccShader_PositionTexture_uColor_vert.h in Headers */, + 8CE868E115F8C3940060E004 /* ccShader_PositionTexture_vert.h in Headers */, + 8CE868E215F8C3940060E004 /* ccShader_PositionTextureA8Color_frag.h in Headers */, + 8CE868E315F8C3940060E004 /* ccShader_PositionTextureA8Color_vert.h in Headers */, + 8CE868E415F8C3940060E004 /* ccShader_PositionTextureColor_frag.h in Headers */, + 8CE868E515F8C3940060E004 /* ccShader_PositionTextureColor_vert.h in Headers */, + 8CE868E615F8C3940060E004 /* ccShader_PositionTextureColorAlphaTest_frag.h in Headers */, + 8CE868E715F8C3940060E004 /* CCShaderCache.h in Headers */, + 8CE868E915F8C3940060E004 /* ccShaders.h in Headers */, + 8CE868EB15F8C3940060E004 /* CCSprite.h in Headers */, + 8CE868ED15F8C3940060E004 /* CCSpriteBatchNode.h in Headers */, + 8CE868EF15F8C3940060E004 /* CCSpriteFrame.h in Headers */, + 8CE868F115F8C3940060E004 /* CCSpriteFrameCache.h in Headers */, + 8CE868F315F8C3940060E004 /* CCTexture2D.h in Headers */, + 8CE868F515F8C3940060E004 /* CCTextureAtlas.h in Headers */, + 8CE868F715F8C3940060E004 /* CCTextureCache.h in Headers */, + 8CE868F915F8C3940060E004 /* CCTexturePVR.h in Headers */, + 8CE868FB15F8C3940060E004 /* CCTileMapAtlas.h in Headers */, + 8CE868FD15F8C3940060E004 /* CCTMXLayer.h in Headers */, + 8CE868FF15F8C3940060E004 /* CCTMXObjectGroup.h in Headers */, + 8CE8690115F8C3940060E004 /* CCTMXTiledMap.h in Headers */, + 8CE8690315F8C3940060E004 /* CCTMXXMLParser.h in Headers */, + 8CE8690515F8C3940060E004 /* CCTransition.h in Headers */, + 8CE8690715F8C3940060E004 /* CCTransitionPageTurn.h in Headers */, + 8CE8690915F8C3940060E004 /* CCTransitionProgress.h in Headers */, + 8CE8690B15F8C3940060E004 /* ccTypes.h in Headers */, + 8CE8690C15F8C3940060E004 /* cocos2d.h in Headers */, + 8CE8690E15F8C3940060E004 /* CCGL.h in Headers */, + 8CE8690F15F8C3940060E004 /* CCNS.h in Headers */, + 8CE8691015F8C3940060E004 /* CCDirectorIOS.h in Headers */, + 8CE8691215F8C3940060E004 /* CCES2Renderer.h in Headers */, + 8CE8691415F8C3940060E004 /* CCESRenderer.h in Headers */, + 8CE8691515F8C3940060E004 /* CCGLView.h in Headers */, + 8CE8691715F8C3940060E004 /* CCTouchDelegateProtocol.h in Headers */, + 8CE8691815F8C3940060E004 /* CCTouchDispatcher.h in Headers */, + 8CE8691A15F8C3940060E004 /* CCTouchHandler.h in Headers */, + 8CE8691C15F8C3940060E004 /* CCDirectorMac.h in Headers */, + 8CE8691E15F8C3940060E004 /* CCEventDispatcher.h in Headers */, + 8CE8692015F8C3940060E004 /* CCGLView.h in Headers */, + 8CE8692215F8C3940060E004 /* CCWindow.h in Headers */, + 8CE8692515F8C3940060E004 /* base64.h in Headers */, + 8CE8692615F8C3940060E004 /* CCArray.h in Headers */, + 8CE8692815F8C3940060E004 /* ccCArray.h in Headers */, + 8CE8692A15F8C3940060E004 /* CCFileUtils.h in Headers */, + 8CE8692C15F8C3940060E004 /* CCProfiling.h in Headers */, + 8CE8692F15F8C3940060E004 /* ccUtils.h in Headers */, + 8CE8693015F8C3940060E004 /* CCVertex.h in Headers */, + 8CE8693215F8C3940060E004 /* CGPointExtension.h in Headers */, + 8CE8693415F8C3940060E004 /* NSThread+performBlock.h in Headers */, + 8CE8693615F8C3940060E004 /* OpenGL_Internal.h in Headers */, + 8CE8693715F8C3940060E004 /* TGAlib.h in Headers */, + 8CE8693915F8C3940060E004 /* TransformUtils.h in Headers */, + 8CE8693B15F8C3940060E004 /* uthash.h in Headers */, + 8CE8693C15F8C3940060E004 /* utlist.h in Headers */, + 8CE8693D15F8C3940060E004 /* ZipUtils.h in Headers */, + 8CE8696815F8C5880060E004 /* aabb.h in Headers */, + 8CE8696915F8C5880060E004 /* mat4stack.h in Headers */, + 8CE8696A15F8C5880060E004 /* matrix.h in Headers */, + 8CE8696B15F8C5880060E004 /* kazmath.h in Headers */, + 8CE8696C15F8C5880060E004 /* mat3.h in Headers */, + 8CE8696D15F8C5880060E004 /* mat4.h in Headers */, + 8CE8696E15F8C5880060E004 /* neon_matrix_impl.h in Headers */, + 8CE8696F15F8C5880060E004 /* plane.h in Headers */, + 8CE8697015F8C5880060E004 /* quaternion.h in Headers */, + 8CE8697115F8C5880060E004 /* ray2.h in Headers */, + 8CE8697215F8C5880060E004 /* utility.h in Headers */, + 8CE8697315F8C5880060E004 /* vec2.h in Headers */, + 8CE8697415F8C5880060E004 /* vec3.h in Headers */, + 8CE8697515F8C5880060E004 /* vec4.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1267,31 +1689,41 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 398C16D1147D555E000EFEDF /* Default.png in Resources */, - 398C16D3147D555E000EFEDF /* fps_images.png in Resources */, - 398C16DD147D555E000EFEDF /* Icon.png in Resources */, - 3914D9D9147D5BDF0010C4A2 /* Back.png in Resources */, - 3914D9DA147D5BDF0010C4A2 /* ColdFunk.caf in Resources */, - 3914D9DB147D5BDF0010C4A2 /* Exit.png in Resources */, - 3914D9DC147D5BDF0010C4A2 /* Ganymede.png in Resources */, - 3914D9DD147D5BDF0010C4A2 /* HappyAlley.caf in Resources */, - 3914D9DE147D5BDF0010C4A2 /* Jupiter.png in Resources */, - 3914D9DF147D5BDF0010C4A2 /* Next.png in Resources */, - 3914D9E0147D5BDF0010C4A2 /* panel-bg.png in Resources */, - 3914D9E1147D5BDF0010C4A2 /* panel-lamp-off.png in Resources */, - 3914D9E2147D5BDF0010C4A2 /* panel-lamp-on.png in Resources */, - 3914D9E3147D5BDF0010C4A2 /* panel-slider-knob.png in Resources */, - 3914D9E4147D5BDF0010C4A2 /* panel-slider-track-long.png in Resources */, - 3914D9E5147D5BDF0010C4A2 /* panel-slider-track.png in Resources */, - 3914D9E6147D5BDF0010C4A2 /* panel-trim-horiz.png in Resources */, - 3914D9E7147D5BDF0010C4A2 /* panel-trim-vert.png in Resources */, - 3914D9E8147D5BDF0010C4A2 /* Pew.caf in Resources */, - 3914D9E9147D5BDF0010C4A2 /* PlanetKiller.mp3 in Resources */, - 3914D9EA147D5BDF0010C4A2 /* Pow.caf in Resources */, - 3914D9EB147D5BDF0010C4A2 /* RocketShip.png in Resources */, - 3914D9EC147D5BDF0010C4A2 /* vu-face.png in Resources */, - 3914D9ED147D5BDF0010C4A2 /* vu-needle.png in Resources */, - 3914D9EE147D5BDF0010C4A2 /* vu-shell.png in Resources */, + 8CE86DCD15F8D9420060E004 /* Default-Landscape~ipad.png in Resources */, + 8CE86DCE15F8D9420060E004 /* Default.png in Resources */, + 8CE86DCF15F8D9420060E004 /* fps_images-hd.png in Resources */, + 8CE86DD015F8D9420060E004 /* fps_images-ipadhd.png in Resources */, + 8CE86DD115F8D9420060E004 /* fps_images.png in Resources */, + 8CE86DD215F8D9420060E004 /* Icon-72.png in Resources */, + 8CE86DD315F8D9420060E004 /* Icon-Small-50.png in Resources */, + 8CE86DD415F8D9420060E004 /* Icon-Small.png in Resources */, + 8CE86DD515F8D9420060E004 /* Icon-Small@2x.png in Resources */, + 8CE86DD615F8D9420060E004 /* Icon.png in Resources */, + 8CE86DD715F8D9420060E004 /* Icon@2x.png in Resources */, + 8CE86DD815F8D9420060E004 /* iTunesArtwork in Resources */, + 8CE86DD915F8D9420060E004 /* AssetSources.txt in Resources */, + 8CE86DDA15F8D9420060E004 /* Back.png in Resources */, + 8CE86DDB15F8D9420060E004 /* ColdFunk.caf in Resources */, + 8CE86DDC15F8D9420060E004 /* Exit.png in Resources */, + 8CE86DDD15F8D9420060E004 /* Ganymede.png in Resources */, + 8CE86DDE15F8D9420060E004 /* HappyAlley.caf in Resources */, + 8CE86DDF15F8D9420060E004 /* Jupiter.png in Resources */, + 8CE86DE015F8D9420060E004 /* Next.png in Resources */, + 8CE86DE115F8D9420060E004 /* panel-bg.png in Resources */, + 8CE86DE215F8D9420060E004 /* panel-lamp-off.png in Resources */, + 8CE86DE315F8D9420060E004 /* panel-lamp-on.png in Resources */, + 8CE86DE415F8D9420060E004 /* panel-slider-knob.png in Resources */, + 8CE86DE515F8D9420060E004 /* panel-slider-track-long.png in Resources */, + 8CE86DE615F8D9420060E004 /* panel-slider-track.png in Resources */, + 8CE86DE715F8D9420060E004 /* panel-trim-horiz.png in Resources */, + 8CE86DE815F8D9420060E004 /* panel-trim-vert.png in Resources */, + 8CE86DE915F8D9420060E004 /* Pew.caf in Resources */, + 8CE86DEA15F8D9420060E004 /* PlanetKiller.mp3 in Resources */, + 8CE86DEB15F8D9420060E004 /* Pow.caf in Resources */, + 8CE86DEC15F8D9420060E004 /* RocketShip.png in Resources */, + 8CE86DED15F8D9420060E004 /* vu-face.png in Resources */, + 8CE86DEE15F8D9420060E004 /* vu-needle.png in Resources */, + 8CE86DEF15F8D9420060E004 /* vu-shell.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1335,87 +1767,102 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3914D811147D57D60010C4A2 /* CCAction.m in Sources */, - 3914D813147D57D60010C4A2 /* CCActionCamera.m in Sources */, - 3914D815147D57D60010C4A2 /* CCActionEase.m in Sources */, - 3914D817147D57D60010C4A2 /* CCActionGrid.m in Sources */, - 3914D819147D57D60010C4A2 /* CCActionGrid3D.m in Sources */, - 3914D81B147D57D60010C4A2 /* CCActionInstant.m in Sources */, - 3914D81D147D57D60010C4A2 /* CCActionInterval.m in Sources */, - 3914D81F147D57D60010C4A2 /* CCActionManager.m in Sources */, - 3914D821147D57D60010C4A2 /* CCActionPageTurn3D.m in Sources */, - 3914D823147D57D60010C4A2 /* CCActionProgressTimer.m in Sources */, - 3914D825147D57D60010C4A2 /* CCActionTiledGrid.m in Sources */, - 3914D827147D57D60010C4A2 /* CCActionTween.m in Sources */, - 3914D829147D57D60010C4A2 /* CCAnimation.m in Sources */, - 3914D82B147D57D60010C4A2 /* CCAnimationCache.m in Sources */, - 3914D82D147D57D60010C4A2 /* CCAtlasNode.m in Sources */, - 3914D82F147D57D60010C4A2 /* CCBlockSupport.m in Sources */, - 3914D831147D57D60010C4A2 /* CCCamera.m in Sources */, - 3914D834147D57D60010C4A2 /* CCConfiguration.m in Sources */, - 3914D836147D57D60010C4A2 /* CCDirector.m in Sources */, - 3914D838147D57D60010C4A2 /* CCDrawingPrimitives.m in Sources */, - 3914D83A147D57D60010C4A2 /* CCGrabber.m in Sources */, - 3914D83C147D57D60010C4A2 /* CCGrid.m in Sources */, - 3914D83E147D57D60010C4A2 /* CCLabelAtlas.m in Sources */, - 3914D840147D57D60010C4A2 /* CCLabelBMFont.m in Sources */, - 3914D842147D57D60010C4A2 /* CCLabelTTF.m in Sources */, - 3914D844147D57D60010C4A2 /* CCLayer.m in Sources */, - 3914D847147D57D60010C4A2 /* CCMenu.m in Sources */, - 3914D849147D57D60010C4A2 /* CCMenuItem.m in Sources */, - 3914D84B147D57D60010C4A2 /* CCMotionStreak.m in Sources */, - 3914D84D147D57D60010C4A2 /* CCNode.m in Sources */, - 3914D84F147D57D60010C4A2 /* CCParallaxNode.m in Sources */, - 3914D851147D57D60010C4A2 /* CCParticleExamples.m in Sources */, - 3914D853147D57D60010C4A2 /* CCParticleSystem.m in Sources */, - 3914D855147D57D60010C4A2 /* CCParticleSystemPoint.m in Sources */, - 3914D857147D57D60010C4A2 /* CCParticleSystemQuad.m in Sources */, - 3914D859147D57D60010C4A2 /* CCProgressTimer.m in Sources */, - 3914D85C147D57D60010C4A2 /* CCRenderTexture.m in Sources */, - 3914D85E147D57D60010C4A2 /* CCRibbon.m in Sources */, - 3914D860147D57D60010C4A2 /* CCScene.m in Sources */, - 3914D862147D57D60010C4A2 /* CCScheduler.m in Sources */, - 3914D864147D57D60010C4A2 /* CCSprite.m in Sources */, - 3914D866147D57D60010C4A2 /* CCSpriteBatchNode.m in Sources */, - 3914D868147D57D60010C4A2 /* CCSpriteFrame.m in Sources */, - 3914D86A147D57D60010C4A2 /* CCSpriteFrameCache.m in Sources */, - 3914D86C147D57D60010C4A2 /* CCTexture2D.m in Sources */, - 3914D86E147D57D60010C4A2 /* CCTextureAtlas.m in Sources */, - 3914D870147D57D60010C4A2 /* CCTextureCache.m in Sources */, - 3914D872147D57D60010C4A2 /* CCTexturePVR.m in Sources */, - 3914D874147D57D60010C4A2 /* CCTileMapAtlas.m in Sources */, - 3914D876147D57D60010C4A2 /* CCTMXLayer.m in Sources */, - 3914D878147D57D60010C4A2 /* CCTMXObjectGroup.m in Sources */, - 3914D87A147D57D60010C4A2 /* CCTMXTiledMap.m in Sources */, - 3914D87C147D57D60010C4A2 /* CCTMXXMLParser.m in Sources */, - 3914D87E147D57D60010C4A2 /* CCTransition.m in Sources */, - 3914D880147D57D60010C4A2 /* CCTransitionPageTurn.m in Sources */, - 3914D882147D57D60010C4A2 /* CCTransitionRadial.m in Sources */, - 3914D885147D57D60010C4A2 /* cocos2d.m in Sources */, - 3914D889147D57D60010C4A2 /* CCDirectorIOS.m in Sources */, - 3914D88C147D57D60010C4A2 /* CCTouchDispatcher.m in Sources */, - 3914D88E147D57D60010C4A2 /* CCTouchHandler.m in Sources */, - 3914D890147D57D60010C4A2 /* EAGLView.m in Sources */, - 3914D892147D57D60010C4A2 /* ES1Renderer.m in Sources */, - 3914D894147D57D60010C4A2 /* glu.c in Sources */, - 3914D897147D57D60010C4A2 /* CCDirectorMac.m in Sources */, - 3914D899147D57D60010C4A2 /* CCEventDispatcher.m in Sources */, - 3914D89B147D57D60010C4A2 /* MacGLView.m in Sources */, - 3914D89D147D57D60010C4A2 /* MacWindow.m in Sources */, - 3914D89E147D57D60010C4A2 /* base64.c in Sources */, - 3914D8A1147D57D60010C4A2 /* CCArray.m in Sources */, - 3914D8A4147D57D60010C4A2 /* CCFileUtils.m in Sources */, - 3914D8A6147D57D60010C4A2 /* CCProfiling.m in Sources */, - 3914D8A7147D57D60010C4A2 /* ccUtils.c in Sources */, - 3914D8AA147D57D60010C4A2 /* CGPointExtension.m in Sources */, - 3914D8AD147D57D60010C4A2 /* TGAlib.m in Sources */, - 3914D8AF147D57D60010C4A2 /* TransformUtils.m in Sources */, - 3914D8B3147D57D60010C4A2 /* ZipUtils.m in Sources */, - 3914D8B5147D57D60010C4A2 /* FontLabel.m in Sources */, - 3914D8B7147D57D60010C4A2 /* FontLabelStringDrawing.m in Sources */, - 3914D8B9147D57D60010C4A2 /* FontManager.m in Sources */, - 3914D8BB147D57D60010C4A2 /* ZAttributedString.m in Sources */, - 3914D8BE147D57D60010C4A2 /* ZFont.m in Sources */, + 8CE8688215F8C3940060E004 /* CCAction.m in Sources */, + 8CE8688415F8C3940060E004 /* CCActionCamera.m in Sources */, + 8CE8688615F8C3940060E004 /* CCActionCatmullRom.m in Sources */, + 8CE8688815F8C3940060E004 /* CCActionEase.m in Sources */, + 8CE8688A15F8C3940060E004 /* CCActionGrid.m in Sources */, + 8CE8688C15F8C3940060E004 /* CCActionGrid3D.m in Sources */, + 8CE8688E15F8C3940060E004 /* CCActionInstant.m in Sources */, + 8CE8689015F8C3940060E004 /* CCActionInterval.m in Sources */, + 8CE8689215F8C3940060E004 /* CCActionManager.m in Sources */, + 8CE8689415F8C3940060E004 /* CCActionPageTurn3D.m in Sources */, + 8CE8689615F8C3940060E004 /* CCActionProgressTimer.m in Sources */, + 8CE8689815F8C3940060E004 /* CCActionTiledGrid.m in Sources */, + 8CE8689A15F8C3940060E004 /* CCActionTween.m in Sources */, + 8CE8689C15F8C3940060E004 /* CCAnimation.m in Sources */, + 8CE8689E15F8C3940060E004 /* CCAnimationCache.m in Sources */, + 8CE868A015F8C3940060E004 /* CCAtlasNode.m in Sources */, + 8CE868A215F8C3940060E004 /* CCCamera.m in Sources */, + 8CE868A515F8C3940060E004 /* CCConfiguration.m in Sources */, + 8CE868A715F8C3940060E004 /* ccDeprecated.m in Sources */, + 8CE868A915F8C3940060E004 /* CCDirector.m in Sources */, + 8CE868AB15F8C3940060E004 /* CCDrawingPrimitives.m in Sources */, + 8CE868AD15F8C3940060E004 /* CCGLProgram.m in Sources */, + 8CE868AF15F8C3940060E004 /* ccGLStateCache.m in Sources */, + 8CE868B115F8C3940060E004 /* CCGrabber.m in Sources */, + 8CE868B315F8C3940060E004 /* CCGrid.m in Sources */, + 8CE868B515F8C3940060E004 /* CCLabelAtlas.m in Sources */, + 8CE868B715F8C3940060E004 /* CCLabelBMFont.m in Sources */, + 8CE868B915F8C3940060E004 /* CCLabelTTF.m in Sources */, + 8CE868BB15F8C3940060E004 /* CCLayer.m in Sources */, + 8CE868BE15F8C3940060E004 /* CCMenu.m in Sources */, + 8CE868C015F8C3940060E004 /* CCMenuItem.m in Sources */, + 8CE868C215F8C3940060E004 /* CCMotionStreak.m in Sources */, + 8CE868C415F8C3940060E004 /* CCNode+Debug.m in Sources */, + 8CE868C615F8C3940060E004 /* CCNode.m in Sources */, + 8CE868C815F8C3940060E004 /* CCParallaxNode.m in Sources */, + 8CE868CA15F8C3940060E004 /* CCParticleBatchNode.m in Sources */, + 8CE868CC15F8C3940060E004 /* CCParticleExamples.m in Sources */, + 8CE868CE15F8C3940060E004 /* CCParticleSystem.m in Sources */, + 8CE868D015F8C3940060E004 /* CCParticleSystemQuad.m in Sources */, + 8CE868D215F8C3940060E004 /* CCProgressTimer.m in Sources */, + 8CE868D515F8C3940060E004 /* CCRenderTexture.m in Sources */, + 8CE868D715F8C3940060E004 /* CCScene.m in Sources */, + 8CE868D915F8C3940060E004 /* CCScheduler.m in Sources */, + 8CE868E815F8C3940060E004 /* CCShaderCache.m in Sources */, + 8CE868EA15F8C3940060E004 /* ccShaders.m in Sources */, + 8CE868EC15F8C3940060E004 /* CCSprite.m in Sources */, + 8CE868EE15F8C3940060E004 /* CCSpriteBatchNode.m in Sources */, + 8CE868F015F8C3940060E004 /* CCSpriteFrame.m in Sources */, + 8CE868F215F8C3940060E004 /* CCSpriteFrameCache.m in Sources */, + 8CE868F415F8C3940060E004 /* CCTexture2D.m in Sources */, + 8CE868F615F8C3940060E004 /* CCTextureAtlas.m in Sources */, + 8CE868F815F8C3940060E004 /* CCTextureCache.m in Sources */, + 8CE868FA15F8C3940060E004 /* CCTexturePVR.m in Sources */, + 8CE868FC15F8C3940060E004 /* CCTileMapAtlas.m in Sources */, + 8CE868FE15F8C3940060E004 /* CCTMXLayer.m in Sources */, + 8CE8690015F8C3940060E004 /* CCTMXObjectGroup.m in Sources */, + 8CE8690215F8C3940060E004 /* CCTMXTiledMap.m in Sources */, + 8CE8690415F8C3940060E004 /* CCTMXXMLParser.m in Sources */, + 8CE8690615F8C3940060E004 /* CCTransition.m in Sources */, + 8CE8690815F8C3940060E004 /* CCTransitionPageTurn.m in Sources */, + 8CE8690A15F8C3940060E004 /* CCTransitionProgress.m in Sources */, + 8CE8690D15F8C3940060E004 /* cocos2d.m in Sources */, + 8CE8691115F8C3940060E004 /* CCDirectorIOS.m in Sources */, + 8CE8691315F8C3940060E004 /* CCES2Renderer.m in Sources */, + 8CE8691615F8C3940060E004 /* CCGLView.m in Sources */, + 8CE8691915F8C3940060E004 /* CCTouchDispatcher.m in Sources */, + 8CE8691B15F8C3940060E004 /* CCTouchHandler.m in Sources */, + 8CE8691D15F8C3940060E004 /* CCDirectorMac.m in Sources */, + 8CE8691F15F8C3940060E004 /* CCEventDispatcher.m in Sources */, + 8CE8692115F8C3940060E004 /* CCGLView.m in Sources */, + 8CE8692315F8C3940060E004 /* CCWindow.m in Sources */, + 8CE8692415F8C3940060E004 /* base64.c in Sources */, + 8CE8692715F8C3940060E004 /* CCArray.m in Sources */, + 8CE8692915F8C3940060E004 /* ccCArray.m in Sources */, + 8CE8692B15F8C3940060E004 /* CCFileUtils.m in Sources */, + 8CE8692D15F8C3940060E004 /* CCProfiling.m in Sources */, + 8CE8692E15F8C3940060E004 /* ccUtils.c in Sources */, + 8CE8693115F8C3940060E004 /* CCVertex.m in Sources */, + 8CE8693315F8C3940060E004 /* CGPointExtension.m in Sources */, + 8CE8693515F8C3940060E004 /* NSThread+performBlock.m in Sources */, + 8CE8693815F8C3940060E004 /* TGAlib.m in Sources */, + 8CE8693A15F8C3940060E004 /* TransformUtils.m in Sources */, + 8CE8693E15F8C3940060E004 /* ZipUtils.m in Sources */, + 8CE8697615F8C5880060E004 /* aabb.c in Sources */, + 8CE8697715F8C5880060E004 /* mat4stack.c in Sources */, + 8CE8697815F8C5880060E004 /* matrix.c in Sources */, + 8CE8697915F8C5880060E004 /* mat3.c in Sources */, + 8CE8697A15F8C5880060E004 /* mat4.c in Sources */, + 8CE8697B15F8C5880060E004 /* neon_matrix_impl.c in Sources */, + 8CE8697C15F8C5880060E004 /* plane.c in Sources */, + 8CE8697D15F8C5880060E004 /* quaternion.c in Sources */, + 8CE8697E15F8C5880060E004 /* ray2.c in Sources */, + 8CE8697F15F8C5880060E004 /* utility.c in Sources */, + 8CE8698015F8C5880060E004 /* vec2.c in Sources */, + 8CE8698115F8C5880060E004 /* vec3.c in Sources */, + 8CE8698215F8C5880060E004 /* vec4.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1425,7 +1872,6 @@ files = ( 398C181F147D5560000EFEDF /* main.m in Sources */, 398C1822147D5560000EFEDF /* AppDelegate.m in Sources */, - 398C1825147D5560000EFEDF /* RootViewController.m in Sources */, 398C1828147D5560000EFEDF /* MainScene.m in Sources */, 3914D996147D5B570010C4A2 /* AudioSessionDemo.m in Sources */, 3914D998147D5B570010C4A2 /* AudioTrackDemo.m in Sources */, @@ -1451,6 +1897,7 @@ 3914D9C0147D5B570010C4A2 /* TouchableNode.m in Sources */, 3914D9C2147D5B570010C4A2 /* VUMeter.m in Sources */, 39169B5314D381EE00082A89 /* ReverbDemo.m in Sources */, + 8CE86DF215F8D9620060E004 /* IntroLayer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1594,6 +2041,7 @@ GCC_PREFIX_HEADER = "cocos2d/cocos2d-Prefix.pch"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "\"cocos2d/kazmath/include\""; }; name = Debug; }; @@ -1610,6 +2058,7 @@ GCC_PREFIX_HEADER = "cocos2d/cocos2d-Prefix.pch"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + USER_HEADER_SEARCH_PATHS = "\"cocos2d/kazmath/include\""; }; name = Release; }; @@ -1627,6 +2076,8 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", + "CC_ENABLE_GL_STATE_CACHE=1", + "CC_ENABLE_DEPRECATED=0", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; @@ -1634,7 +2085,7 @@ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 3.0; + IPHONEOS_DEPLOYMENT_TARGET = 4.0; SDKROOT = iphoneos; }; name = Debug; @@ -1649,11 +2100,15 @@ ); COPY_PHASE_STRIP = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + "CC_ENABLE_GL_STATE_CACHE=1", + "CC_ENABLE_DEPRECATED=0", + ); GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 3.0; + IPHONEOS_DEPLOYMENT_TARGET = 4.0; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; @@ -1678,8 +2133,11 @@ "COCOS2D_DEBUG=1", ); INFOPLIST_FILE = ObjectALDemo/Resources/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 5.1; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = 1; + USER_HEADER_SEARCH_PATHS = "\"cocos2d/kazmath/include\""; WRAPPER_EXTENSION = app; }; name = Debug; @@ -1699,9 +2157,12 @@ GCC_PREFIX_HEADER = ObjectALDemo/Prefix.pch; GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; INFOPLIST_FILE = ObjectALDemo/Resources/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 5.1; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = 1; + USER_HEADER_SEARCH_PATHS = "\"cocos2d/kazmath/include\""; VALIDATE_PRODUCT = YES; WRAPPER_EXTENSION = app; }; diff --git a/ObjectALDemo/AppDelegate.h b/ObjectALDemo/AppDelegate.h old mode 100755 new mode 100644 index 9ff5798..7128f5e --- a/ObjectALDemo/AppDelegate.h +++ b/ObjectALDemo/AppDelegate.h @@ -1,19 +1,23 @@ // // AppDelegate.h -// ObjectAL +// ObjectALDemo // -// Created by Karl Stenerud on 11/23/11. +// Created by Monkey on 7/09/12. // #import +#import "cocos2d.h" -@class RootViewController; +@interface AppController : NSObject +{ + UIWindow *window_; + UINavigationController *navController_; -@interface AppDelegate : NSObject { - UIWindow *window; - RootViewController *viewController; + CCDirectorIOS *director_; // weak ref } @property (nonatomic, retain) UIWindow *window; +@property (readonly) UINavigationController *navController; +@property (readonly) CCDirectorIOS *director; @end diff --git a/ObjectALDemo/AppDelegate.m b/ObjectALDemo/AppDelegate.m old mode 100755 new mode 100644 index db382eb..c504527 --- a/ObjectALDemo/AppDelegate.m +++ b/ObjectALDemo/AppDelegate.m @@ -1,165 +1,148 @@ // // AppDelegate.m -// ObjectAL +// ObjectALDemo // -// Created by Karl Stenerud on 11/23/11. +// Created by Monkey on 7/09/12. // #import "cocos2d.h" #import "AppDelegate.h" -#import "GameConfig.h" -#import "RootViewController.h" +#import "IntroLayer.h" -#import "CCLayer+Scene.h" -#import "MainScene.h" -#import "ObjectAL.h" +@implementation AppController +@synthesize window=window_, navController=navController_, director=director_; -@implementation AppDelegate +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + // Create the main window + window_ = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; -@synthesize window; -- (void) removeStartupFlicker -{ - // - // THIS CODE REMOVES THE STARTUP FLICKER - // - // Uncomment the following code if you Application only supports landscape mode - // -#if GAME_AUTOROTATION == kGameAutorotationUIViewController - -// CC_ENABLE_DEFAULT_GL_STATES(); -// CCDirector *director = [CCDirector sharedDirector]; -// CGSize size = [director winSize]; -// CCSprite *sprite = [CCSprite spriteWithFile:@"Default.png"]; -// sprite.position = ccp(size.width/2, size.height/2); -// sprite.rotation = -90; -// [sprite visit]; -// [[director openGLView] swapBuffers]; -// CC_ENABLE_DEFAULT_GL_STATES(); - -#endif // GAME_AUTOROTATION == kGameAutorotationUIViewController -} -- (void) applicationDidFinishLaunching:(UIApplication*)application -{ - // Init the window - window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - - // Try to use CADisplayLink director - // if it fails (SDK < 3.1) use the default director - if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] ) - [CCDirector setDirectorType:kCCDirectorTypeDefault]; - - - CCDirector *director = [CCDirector sharedDirector]; - - // Init the View Controller - viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil]; - viewController.wantsFullScreenLayout = YES; - - // - // Create the EAGLView manually - // 1. Create a RGB565 format. Alternative: RGBA8 - // 2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition - // - // - EAGLView *glView = [EAGLView viewWithFrame:[window bounds] - pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8 - depthFormat:0 // GL_DEPTH_COMPONENT16_OES - ]; - + // Create an CCGLView with a RGB565 color buffer, and a depth buffer of 0-bits + CCGLView *glView = [CCGLView viewWithFrame:[window_ bounds] + pixelFormat:kEAGLColorFormatRGBA8 // kEAGLColorFormatRGB565 + depthFormat:0 // GL_DEPTH_COMPONENT24_OES + preserveBackbuffer:NO + sharegroup:nil + multiSampling:NO + numberOfSamples:0]; + + director_ = (CCDirectorIOS*) [CCDirector sharedDirector]; + + director_.wantsFullScreenLayout = YES; + + // Display FSP and SPF + [director_ setDisplayStats:NO]; + + // set FPS at 60 + [director_ setAnimationInterval:1.0/60]; + // attach the openglView to the director - [director setOpenGLView:glView]; - -// // Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices -// if( ! [director enableRetinaDisplay:YES] ) -// CCLOG(@"Retina Display Not supported"); - - // - // VERY IMPORTANT: - // If the rotation is going to be controlled by a UIViewController - // then the device orientation should be "Portrait". - // - // IMPORTANT: - // By default, this template only supports Landscape orientations. - // Edit the RootViewController.m file to edit the supported orientations. - // -#if GAME_AUTOROTATION == kGameAutorotationUIViewController - [director setDeviceOrientation:kCCDeviceOrientationPortrait]; -#else - [director setDeviceOrientation:kCCDeviceOrientationLandscapeLeft]; -#endif - - [director setAnimationInterval:1.0/60]; - [director setDisplayFPS:YES]; - - - // make the OpenGLView a child of the view controller - [viewController setView:glView]; - - // make the View Controller a child of the main window - [window addSubview: viewController.view]; - - [window makeKeyAndVisible]; - + [director_ setView:glView]; + + // for rotation and other messages + [director_ setDelegate:self]; + + // 2D projection + [director_ setProjection:kCCDirectorProjection2D]; + + // Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices + //if( ! [director_ enableRetinaDisplay:YES] ) + // CCLOG(@"Retina Display Not supported"); + // Default texture format for PNG/BMP/TIFF/JPEG/GIF images // It can be RGBA8888, RGBA4444, RGB5_A1, RGB565 // You can change anytime. [CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888]; - // Removes the startup flicker - [self removeStartupFlicker]; + // If the 1st suffix is not found and if fallback is enabled then fallback suffixes are going to searched. If none is found, it will try with the name without suffix. + // On iPad HD : "-ipadhd", "-ipad", "-hd" + // On iPad : "-ipad", "-hd" + // On iPhone HD: "-hd" + CCFileUtils *sharedFileUtils = [CCFileUtils sharedFileUtils]; + [sharedFileUtils setEnableFallbackSuffixes:NO]; // Default: NO. No fallback suffixes are going to be used + [sharedFileUtils setiPhoneRetinaDisplaySuffix:@"-hd"]; // Default on iPhone RetinaDisplay is "-hd" + [sharedFileUtils setiPadSuffix:@"-ipad"]; // Default on iPad is "ipad" + [sharedFileUtils setiPadRetinaDisplaySuffix:@"-ipadhd"]; // Default on iPad RetinaDisplay is "-ipadhd" + + // Assume that PVR images have premultiplied alpha + [CCTexture2D PVRImagesHavePremultipliedAlpha:YES]; + + // and add the scene to the stack. The director will run it when it automatically when the view is displayed. + [director_ pushScene: [IntroLayer scene]]; + + // Create a Navigation Controller with the Director + navController_ = [[UINavigationController alloc] initWithRootViewController:director_]; + navController_.navigationBarHidden = YES; + + // set the Navigation Controller as the root view controller + [window_ setRootViewController:navController_]; + + // make main window visible + [window_ makeKeyAndVisible]; - // Kick off audio initialization before our main scene begins. - // Note: This default init will cause OALSimpleAudio to take all 32 sources. - [OALSimpleAudio sharedInstance]; + return YES; +} - // Run the intro Scene - [[CCDirector sharedDirector] runWithScene: [MainLayer scene]]; +// Supported orientations: Landscape. Customize it for your own needs +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + return UIInterfaceOrientationIsLandscape(interfaceOrientation); } -- (void)applicationWillResignActive:(UIApplication *)application { - [[CCDirector sharedDirector] pause]; +// getting a call, pause the game +-(void) applicationWillResignActive:(UIApplication *)application +{ + if( [navController_ visibleViewController] == director_ ) + [director_ pause]; } -- (void)applicationDidBecomeActive:(UIApplication *)application { - [[CCDirector sharedDirector] resume]; +// call got rejected +-(void) applicationDidBecomeActive:(UIApplication *)application +{ + if( [navController_ visibleViewController] == director_ ) + [director_ resume]; } -- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { - [[CCDirector sharedDirector] purgeCachedData]; +-(void) applicationDidEnterBackground:(UIApplication*)application +{ + if( [navController_ visibleViewController] == director_ ) + [director_ stopAnimation]; } --(void) applicationDidEnterBackground:(UIApplication*)application { - [[CCDirector sharedDirector] stopAnimation]; +-(void) applicationWillEnterForeground:(UIApplication*)application +{ + if( [navController_ visibleViewController] == director_ ) + [director_ startAnimation]; } --(void) applicationWillEnterForeground:(UIApplication*)application { - [[CCDirector sharedDirector] startAnimation]; +// application will be killed +- (void)applicationWillTerminate:(UIApplication *)application +{ + CC_DIRECTOR_END(); } -- (void)applicationWillTerminate:(UIApplication *)application { - CCDirector *director = [CCDirector sharedDirector]; - - [[director openGLView] removeFromSuperview]; - - [viewController release]; - - [window release]; - - [director end]; +// purge memory +- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application +{ + [[CCDirector sharedDirector] purgeCachedData]; } -- (void)applicationSignificantTimeChange:(UIApplication *)application { +// next delta time will be zero +-(void) applicationSignificantTimeChange:(UIApplication *)application +{ [[CCDirector sharedDirector] setNextDeltaTimeZero:YES]; } -- (void)dealloc { - [[CCDirector sharedDirector] end]; - [window release]; +- (void) dealloc +{ + [window_ release]; + [navController_ release]; + [super dealloc]; } - @end + diff --git a/ObjectALDemo/Demos/AudioTrackDemo.m b/ObjectALDemo/Demos/AudioTrackDemo.m index 4e2f0fd..d5beb99 100644 --- a/ObjectALDemo/Demos/AudioTrackDemo.m +++ b/ObjectALDemo/Demos/AudioTrackDemo.m @@ -8,6 +8,7 @@ #import "AudioTrackDemo.h" #import "MainScene.h" #import "CCLayer+Scene.h" +#import "ImageButton.h" #import "ImageAndLabelButton.h" #import "Slider.h" #import "ObjectAL.h" diff --git a/ObjectALDemo/GameConfig.h b/ObjectALDemo/GameConfig.h deleted file mode 100644 index a41e72d..0000000 --- a/ObjectALDemo/GameConfig.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// GameConfig.h -// ObjectAL -// -// Created by Karl Stenerud on 11/23/11. -// - -#ifndef __GAME_CONFIG_H -#define __GAME_CONFIG_H - -// -// Supported Autorotations: -// None, -// UIViewController, -// CCDirector -// -#define kGameAutorotationNone 0 -#define kGameAutorotationCCDirector 1 -#define kGameAutorotationUIViewController 2 - -// -// Define here the type of autorotation that you want for your game -// - -// 3rd generation and newer devices: Rotate using UIViewController. Rotation should be supported on iPad apps. -// TIP: -// To improve the performance, you should set this value to "kGameAutorotationNone" or "kGameAutorotationCCDirector" -#if defined(__ARM_NEON__) || TARGET_IPHONE_SIMULATOR -#define GAME_AUTOROTATION kGameAutorotationUIViewController - -// ARMv6 (1st and 2nd generation devices): Don't rotate. It is very expensive -#elif __arm__ -#define GAME_AUTOROTATION kGameAutorotationNone - - -// Ignore this value on Mac -#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) - -#else -#error(unknown architecture) -#endif - -#endif // __GAME_CONFIG_H - diff --git a/ObjectALDemo/IntroLayer.h b/ObjectALDemo/IntroLayer.h new file mode 100644 index 0000000..cd5e532 --- /dev/null +++ b/ObjectALDemo/IntroLayer.h @@ -0,0 +1,20 @@ +// +// IntroLayer.h +// ObjectALDemo +// +// Created by Monkey on 7/09/12. +// + + +// When you import this file, you import all the cocos2d classes +#import "cocos2d.h" + +// HelloWorldLayer +@interface IntroLayer : CCLayer +{ +} + +// returns a CCScene that contains the HelloWorldLayer as the only child ++(CCScene *) scene; + +@end diff --git a/ObjectALDemo/IntroLayer.m b/ObjectALDemo/IntroLayer.m new file mode 100644 index 0000000..3aa3fcc --- /dev/null +++ b/ObjectALDemo/IntroLayer.m @@ -0,0 +1,64 @@ +// +// IntroLayer.m +// ObjectALDemo +// +// Created by Monkey on 7/09/12. +// + + +// Import the interfaces +#import "IntroLayer.h" +#import "MainScene.h" + + +#pragma mark - IntroLayer + +// HelloWorldLayer implementation +@implementation IntroLayer + +// Helper class method that creates a Scene with the HelloWorldLayer as the only child. ++(CCScene *) scene +{ + // 'scene' is an autorelease object. + CCScene *scene = [CCScene node]; + + // 'layer' is an autorelease object. + IntroLayer *layer = [IntroLayer node]; + + // add layer as a child to scene + [scene addChild: layer]; + + // return the scene + return scene; +} + +// +-(void) onEnter +{ + [super onEnter]; + + // ask director for the window size + CGSize size = [[CCDirector sharedDirector] winSize]; + + CCSprite *background; + + if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ) { + background = [CCSprite spriteWithFile:@"Default.png"]; + background.rotation = 90; + } else { + background = [CCSprite spriteWithFile:@"Default-Landscape~ipad.png"]; + } + background.position = ccp(size.width/2, size.height/2); + + // add the label as a child to this Layer + [self addChild: background]; + + // In one second transition to the new scene + [self scheduleOnce:@selector(makeTransition:) delay:1]; +} + +-(void) makeTransition:(ccTime)dt +{ + [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[MainLayer scene] withColor:ccWHITE]]; +} +@end diff --git a/ObjectALDemo/MainScene.h b/ObjectALDemo/MainScene.h index c65bb8a..c66d7dc 100644 --- a/ObjectALDemo/MainScene.h +++ b/ObjectALDemo/MainScene.h @@ -1,23 +1,24 @@ // // MainLayer.h -// ObjectAL +// ObjectALDemo // -// Created by Karl Stenerud on 10-05-29. +// Created by Monkey on 7/09/12. // - #import "cocos2d.h" + +#import "CCLayer+Scene.h" #import "ImageButton.h" + /** * Main layer to the ObjectAL demo program. * Contains a menu which redirects to various scenes.
* * Be sure to look at the header files for the various demos as they give important information. */ -@interface MainLayer : CCLayer -{ - NSMutableArray* sceneNames; +@interface MainLayer : CCLayer { + NSMutableArray* sceneNames; NSMutableArray* scenes; ImageButton* previousButton; ImageButton* nextButton; diff --git a/ObjectALDemo/MainScene.m b/ObjectALDemo/MainScene.m old mode 100755 new mode 100644 index 7d94ab9..9c951b0 --- a/ObjectALDemo/MainScene.m +++ b/ObjectALDemo/MainScene.m @@ -1,12 +1,11 @@ // // MainLayer.m -// ObjectAL +// ObjectALDemo // -// Created by Karl Stenerud on 10-05-29. +// Created by Monkey on 7/09/12. // #import "MainScene.h" -#import "CCLayer+Scene.h" #import "SingleSourceDemo.h" #import "TwoSourceDemo.h" @@ -206,7 +205,7 @@ - (void) setStartIndex:(uint) newIndex nil], [CCCallFunc actionWithTarget:self selector:@selector(onMenuSlideComplete)], nil]; - [CCTouchDispatcher sharedDispatcher].dispatchEvents = NO; + [[CCDirector sharedDirector] touchDispatcher].dispatchEvents = NO; [self runAction:action]; } } @@ -216,7 +215,7 @@ - (void) onMenuSlideComplete [oldMenu removeFromParentAndCleanup:YES]; oldMenu = nil; - [CCTouchDispatcher sharedDispatcher].dispatchEvents = YES; + [[CCDirector sharedDirector] touchDispatcher].dispatchEvents = YES; } - (void) onSceneSelect:(IndexedMenuItemLabel*) item @@ -245,4 +244,4 @@ - (void) onNext:(id) sender [self setStartIndex:newIndex]; } -@end +@end \ No newline at end of file diff --git a/ObjectALDemo/Resources/Default-Landscape~ipad.png b/ObjectALDemo/Resources/Default-Landscape~ipad.png new file mode 100644 index 0000000000000000000000000000000000000000..e37b762dfe25cb2a1f62cc0bc1ef132d0256494e GIT binary patch literal 434094 zcmV(pK=8kbP)8Z-RH$EhY5B|^p{$KUwKd9pT<>$Xx#OELU`>+1n_do1E{rSgW z{|NaZ_5C;g``37Xz~^sKemXvXwez;>)$^Ef}dah`~Pvjg+EW0Kf?1b^_Su2xAXp5*f+Kh|9J+Fhkc*Jd5A#q z?>AsS{_&{(_{ZlN@chp6sA*50^CX@>px@_x>hsRz-_jTG@2P!$jeOAaedT4{zXA9v zy^mb~-ujyMug%ZSkN@wV{O3_)wg~lQ`Wrnz@-A`Ocj^!I_%-n7eb#BMPyhKi{pJ7j zvF;=J_m}LS3Fz|w{Na9RYr%h>1^r>2ANBj)Y3t;7HSqU$*f-$)mir0&8;CXMzj!{0 zZu)r}b#}zvW}h7f`E#GwbvKSY+IfjQ@2r0g0U{Sf=uIVa~?!n45co4&tPZjk4xOJk#En~39N4w&iRyD;80(u{9gExKsk>--#Q#Eb&Z(kzzkG$?BI3S4QjZFK zuX--ubtB7qmVxh#{LjxH)gZM9zfb!9Pp|$tw=QJ=-(wN$y#S25|JxWh$LD)N3ZQd7eO?Y_{}OqBTLH$iMLwSu_nSS+?)&NS@5|la z@$Uijdp*ayUDNvC=N0twIWPU+@3lJObBFytZtgW{3x75sD`u6}#35VL{ z46RE0(tFH)yPFJsdEVzCb+2+y z(o=PRuBceXb(VXd-)B#7O4rZ-(oIChg&wPl_iN#vsK19^U+m}bzRLtPD@mR#`c#9z z=juxey5b>MFj&4PAwNLsk5BD=ekJ@8J&(Q5?9cbG^98W~x_^B)Uhf@wC%10sReEb_ zV!kvu)qmfbQ$lDJO08R@WT2JHlv>O0!?`=CC)@n{bNSoaKTqLoXK%wOO9gV9>mQ7L zNhr?3L;_$P2xn2C*Dm`Eumip1Y(0C0s|7my@*Xg=8P1!hZ~l49ce&$!6v!cVmosI@ z&2`C&ZLy_c0AF0V{gXLe7(Tb_^6DkOKqr z&ZwvH>1$Eh&&Zw1fA4=V5%%#o+Z3;z^;r(AqIV>})!sR->KRLz&74CSr!d5{9n4wMIuob7tUWi`rDz~{ch!Hk@b7BU;xCG*$3zN# za-6%5QV+n0}V&t`seYqXFMPEyqa^x z<8C}3X3YlBiqV2*vG*g4oWq*h3bNvgaBMDu{kUYtGz znrB!(2rWO9?>Q?~>b`}e3)RK}Yv2%Dsa|zIAJ>f@K%6hcK8AG811pQWyS#4=**vXv zfV&9q`j&5a6B(((b_yk-vO8W}D>#E=EZLaRaK>v!knxOM~>Nmb%^=v-c5w}WWvY1dFZ|5-OY42<5S+#E$ zEB)a(pfpG80HJ`*2a7|t5R{j`(9?6``U>sFN=r^P%^dW|Bld95wvYFo_P~sM zaN7j!YVOx-kmgiqcZNERD|D9Jx9}XIa{zazG1uvg-H1d|wLd{1{H*ubX}yHo<$Y>K zXa|qy+4`x^DaZ3{Wm^3q-IO9(pKEbYDQ)dwXC0^i2A>u9ZGA|cZ=S9I4SEdVNh*BA zdq)Upi~m6bfZLN{j*;J2FXujNXl>OI6jJ$vZ34&KR5u{4U(d5^!o6C ztBilZ*}8hWo})_orVqG06A8OLOW;AyeD7=$FwS2^Qg5@Mw`GIWue;G&m4>DaDm_)p z#Rqs16AE^828SRPz&OjTUkD7`JP*V0Yb+KP&$qt^BYf1T(|voK1Jce?`W*1LpLmdN zbkatFM%QC(#LF**LY6@fmYQKIgwh#qMLf5DPoe1J4CpQ8*SL1e_Cw{7HAXl8NbyKa z#ScAb?rqu?n}RWCjY3FPfwWQ{v(%xGY5iOv1ft9d7>gxHk(|8N>(^ZF3f(&St$z@D z=6$!JAy4eVI}OzR{ENEx07oCGbRz8cg6UGIy$XyKpclT*7w^h#Sjc3S#fvF$?950shU+P&Y&vqe= zi)r?#j4w9g+-*FNlXSKhDgIo>cILo04(g3o(00L`7g%puI6^~%(Z&-C7-7`ae8GG7 zp8b4V&(2qDk8nkobY*0>l%3hQTW^>r0@kk)Y zQv)bKUM3U?6a;8IbLm@M!X`t~LUL#<2Bu!}wcT-v10ZB{_ zG1w~f7QY>osU1vaK`9pd6uI|;U({MS#sqgZ?WVjtcm!MV(6N*531Bt+f?m!AAWH@P z`TFvfAqigHFb({n09qWl?Dv)3)3lc(aK;~AYtujNV-g2+Gk8u?_@cA26ED`3mfiPb zQtRx~3QH>mP8W*c34B}b(rERtgQd-E->HpqfjMrIZ}StP7GnTjj{dO^V0$}06xK$cwhia85idm#?0_yG zu-$Rl!L1=woI6pm;`!_)VmJ%?FjY>uz&*hj=^}-Jf8=dzqcWix9*CEt7V9$}oGx5z z0;cMQHfo0P8EC@9KIRS(t57K%YZS8hy)F=I@D5|EUQFq}T`#@YDD9t>hp3QIn+${$ z*mU!&fE;X+1yg$n(t%m|OH7{I!N(bhdo|E}9faD71Aa9_Ut@@vRh7|G@8;@kQqWqv(@8m*^s= zk%0#M{J{X4LYkFItax{4%b~2urV*1V@{(y$Z{wn8DEs>FUjle<$=6zvlLJEp(2h$> z*vpXSkgaf=KYpJqL5?ua!kr7BJ;dxLGl++(JDut%3dW%06LV8F05Wtjh`3Q_;%7YZ z3w<$3?^IGhe2A?IEvO_516#0>ED?WRESzsq&U&sg0wW*VA!8E{Zq^J|M1f6#u{i3_ zfd+x68pNtl0j}U&x_fT$$|_FJXdw5i#H^tuX@$!vSCBfBTW%o=pKi+O2Zb`o*kS}2 zUIrh2Lb(EQd=3-$2<-Wr zyVrQI6HstEwpEsS$cBuENu<@UgAHgJvBO7*@JG$wfH0y77i?+D6R>MK1Zv1iK?WG} z4~p3Vy9w>B)zdL>YoAcEg$(%DC*kk>p%M?36gUGzG`=;+Gix}fR9vU3YQWYOsXSjU?458IRvzR&6x%2i$-J|o#`DIvR1ypeL16LJ^2*oSntyrF`9LfBu z_5IBA9#yzoNBu)Zx5W%+U$kHI8WOX#4AQS%QDR@qVQTDsftAm&j{P z@t3>{=;gQxf|zubTaGrj2}Tezq`N2S_j(-YhY$W7XZZ{((Owf}ESF?xIqINg+XKkv z27?2#NPf$Zft#HLaHt=598CpD1JDPy1R@5MZ74vgyW~;d184aDDLsN7ajGAM4FfN? zY4+5Cc!cmXHI2169BvQgbZBB5qpZXXSSV7z1W~-Bo((Y6a0T%2ZopCHa3J#lzF+4e zuQclK#oCk|hZ<5@V`^B+Dro!H#jBiSW!4g~lQpLceDhL_r-g)74wAv=sBpa#0VAoS z!v&2B3eUR3 z^{O1T3*{=@qd0c!jJwUzY_UL<`v&xjQAFVwY2_^Ii$Bl!uz$T;H1J9UjV$))@Z?6K zb+?Xv&g!pmvfB+6rVaQ`Ay2Nd?h2~D4scrt^!3cGUsG&vZWkDL2FD=^TYDZNB>F=DH+SNPTf=N|CR6!3o7F^J{244(tVmPh4+*3n|T8=x@l#`a1(h6i1JpI@2Q0pgwjqZ8X;YEbDn_JbjPBk{b0H}P`8^?*W@w) zUOtY~0O-F?86RFg1l!h8-6w>?*65k&_g-IT7L3-I(1Sq8PEtWZ+A`!#OTEhN)=@%M zs~B6CPN-h#+d_JcsyX-X4&-x(-Ctz0-$P0B0;dDOuM{-XJ+cKea@1KuP0?K8SG5yxL2O68=r$%d6w zNIxa>%o}O0b7?*|eS!p^c^rb;35A2Mv)cS=|_`!zV#h#kNzo62JDPCSZ)e45uCB$J!pm=OaHHM(pjPu>x z@mG)Gn)7(K!4P@BP|Y~M?GCJ$PY9k$*~XyBFt~x8?yov7Dooj6d!R)tiwW~4z<|W5 zC;`H>f#!rJWMf6g5PFzPdgKh2#^;4bExS|C*|?;xE$-nZt3of>_!E_?KFl}5_6ptd z-5f^vqUy~l#i19BBhEeNnQa18{eDqErCuR`5a0i}@T;@{1dLfBYhq9^>L@aqmx*pz zV|S;@!tE7W+AUD&&C+Hen#PFt*%QXSuz4J3mPWOUf;sC?13*6|rdb|Z#1`BRjj2CO zn`U9rIn>^=!ksJ|DD^CWu35~rja1~b83x!C*V4tIB}X+(VqfuS zW-g4#Kc~P9;rL*8!-f=({-AY#7&ucqu!BqTf%hA%u}*}C&l^8JKT3G%CeD>Ruz}7d z#E2Gzrf4v~K|vr4vle17AArihYn9-DN-bnk0p>(V*&dwzb_u34=GW039{m1myr+yB zU@<_nMMi`^F9mwa40Zesw>03nr2Z=yfjY+4v}%jg1z&qK?YZ-E7}f?=hP z(4bWpDI*yaSx%)VAG~O^OGT-&K{1fZilU)_aYJNG_bUu=uw=VRU!{$Lu$bKL%mSp> z(t~<}HVanxPGv4ZVIUN3g?OevO=KuDvO zYZ9jJbAo+f=2l^ecmvv4GG`_1yF-Ez6-FE#KlY`N?T!6C6kg*PTbTi4^k$u zt!5mH=TY#E|C{MAyvH;@{otB5f4qf9vvmdG|55@zY?>&P35s`v{&c+Va40>TR-r-h zuTr!lSw}OR@Wd&DQ)nt>Qqg)nNpih^CYgZDJFgm<<#afsC!`yliG7+;0cF(QRnY2d zK7qtMljhlWHGq2r(|oWca11MTjBD_T#ZbOA`;TS=Oc@O!`3QR9K6xCIOCD}zT5{du zYW~<5?B}&F;WBQYFO=NGzq9tBdXOn+2qj0F=&oO#RIBFW9Bg#amBsE4L+1v?@(u=(QRvoG|E}qK_ zjvfVC!OjUnOBCh;-)^sM8j0IiGAm8_U8uz6e6iq{%LqDFLnpw5T9ji$n6V2Ob{?p- z9$IVxo019;GS#VIsV7WD$_Yw+XCJ8*buTA8G+Bpro|oI(pp0 z8812xwod$ve6FVh-EV*sWoG%Pbg!5A>%rSY;oF9U^vZ`BbFilMeGNssYatI?W8$*obX6x@bbi{A1W zXEx8{|9V&>l$d5~PD;*$O$~*>MY+$oT=0FZlPh~TT3N(2oKZW$Cw`Iczzr{tXlde7 znl|K$AuWKBKLsq1M-^ic-#Axq@~M82-%FF02%pG-W~Q1i9UCi`QgUvL=A1FA z8@d)ql$j7II?d~K9xNA@L_2onj2#IMUb54>lBlC2Vdsl92>yshCh(xO=FQGDs=tN% zGVy-+b^#J4ew4N2${7e+kAqKqo$CloJ2xw)A5 zP-ws?Xxh3VHT+DqGOPSU^~fFk@TU(ys~MIIb3J!1yxLXI$4g(}${3mF1|cI6#+2or zGI|r=yyXvQ0SJkvNjcx}zB^%=^dKq>x8gs=3|T#Cu)>|VajeR+GMKyc0cylA8Lc8( zMCQQ66oeR_nD;9%Q3odbS57WE0Q2ZLNt;OG5%JZQ(_3>bcXdyQ5jYtx0be`8NLExK~Md1)@RRaqC2zKg{K|NJ@71M18f8r<4-L-6~gLZJmyJ$ zslfBl51NFTqC#U_I4bWgLxgU7wb^ds9IXt1_cL7j!+yA-^o>eb zZ$HFqX6W^%LvMB$wyWot|A@UWN5fF8HL~o4xH%xsLu=Q?NbZ?)?|RUlA9dy_y2U81 z18=z9oluRG6{8X1C9XmJKxX(ptMbmxX|)v)>h)grtbU)mCVIS}787GBFmzTe`g7nI zx{xPr$x<~QFVbJQB!A$N<0y2{pyAyPVKVgJphnsKo^eoCD={)fCNd*YD?yIy}b6q1xmdS6IW)P6#0Z>s~=ZeahE3 zt0^X-svVPL0dda7A6iTX`4Ee`(1AJU+d~0Ulf6K_=(ul7ZAS2(rHKFPIROmx9j^YP z0#<4_6FzrMQCWvK@vOAfMhg$Dr0J0_U7K<~>Ca?(na-c#RA-7{mn*9ql7?UWFcpjfo<;(-ImrvmL-Q5A-s{u&J6G3Tmnn@u2|S z`sEztqstglTWFW4L|D)ddaU89q^ygvEQ6~c5j|9YHwjB~BQ{nTg1Fr>)6o?Zr|s@V z_4hhVnLR}bsj&%ydi`jt z8=hoSlro=H1b94nfZEEDi#TB+s$*8!akIiuXA@^B9CUAnkObOF=6~3$h3Bp-R^d^? zX0x8*XvWI99}|^1Uw8^Kk|wKDDIHn};#0!-w}jG7U%37edWDR(Nh)ERQgPhIdIZET zGck-j$)2aQ5EDCz0lTb5ge9I0O9qojy%}X~-kVB&JWZc!oEK2_z$TysOZzXzDEIljEkB%XK4KS|V(yOg5MT&GP(m`{MeJHVxZn^c zB|Ytiw=0byR0gd=ofq44a=o7~;|(I`{#vMKgP$r+`u=vCF*y}rTv$0*F=8NcmsHKd z_J{{L%fuf*h#^DiWV@T89x#5=*yotrR=yT-7aC}xC^3NMINh=RDbNGwgBGJX6i4Gg z;|LqO#77LC!cd+ZLWdYEP-hvYuP<&18U>SK)WoA=EUxj+EsZ9{IOK)u|UzZB(kk`C-O3ny{(Xb?|OF|7la?1lmfA)mo3 z&kU|ltn{aSnI|_?K`Q;_D=t$CKu;S>qhV@!L}!jr2V-iu%uTUc`F!oeC8XshlEJd2 zh1;M!VbKhu!j_@1J$!XM)d7CD-TJwx9!ERXCOT9Vv24?6{HQLwE-{%)pmw!&Uu#-r zSz0PPfWo|6AJ~ps3NZl&s{c9YvLhA617DWRphpK8D=3LU6^=slU%E(ayorvmBvB0z z(|`-CiCgM9(ag{fDkY-sWIXsRF!$n$E3>wOUowm&8fzmaW)*8$eM>|iqye2&7j2Go zXpVSXc(Y>og^P4oq$LKJExmOJKnP4sJ-4`Yq@9!vzjV6E?cBBXTCRIT4VSi6p>H0$ zyLncm>P|w#M4_AZ=Abpmp4*{0oukP{pvhfGB3QEnchPw5vfO!-Sm;AlI#Pu+k_B`C zX681h@gcrgC~uh~9X*CQRfBG?2lEHOLw<}VOdFq)}ZNvu6HBz<*=W#c=pwA^_UQMfM2uE*b7dq^Zde8|! zh~zbd`S=BpTB7KzY+XNqj;ofvy^m+1aaKEU+(sNGNVs;GQR%~%7Ohd8)`^TnugFSl zZ=VTk2=4+QsAxR^!9c=J>_Z-xo*^AA{-zwlhm0Dv@Ur$o+lzEn=HsS;)^%BHI2n2u zQ+%s1GuI|D9A3hI{_M}e6K#NhX9aLDO#;!7-z^_)vVxsa*P7d^=pc|eb5sZsj~v01 zhyitqa+7U3HckjPO=P5BR7)k1hf{f7bskOI;^{xmIbMpA#hCL_{B-H))Pc{KO9FF= z$5{9kDu@*z!w0c%`7?BWYA5NQ^Nb?p#vc@Uz-&vR+?1mB`ak#AmN*+Z%wUs;#UGe44*Ox}ZjYWYZ(b zWyL{3DmO^IZZpd?Zy^}5-2=K8v3$e^2*qlFGWEQZ9-u*01uLczpt;IQs{`c(TXYIM zv#TFh5V5 z^t5|cZA*>HanL*U7qZ#V(9*Fp1{ z$gqESi>>Uy0KPD3WnYHk)r#8LTJ6(K#I4rZ09k4x3ZJqbn))n4|FgU<6cCqu%7FbbUh8mhuy@CKRJgY!=T%A7j87A`I!1qpnMZ*rWQm!!{Y)ppiH%m zaZ2PQGehEnv8UvqTC}DAuU?D;C9vef9A?vEtZ4$uK1u*0rEnG2*F^2F1RrU zCB;TlYi?hBIo#f-qV{;RabIEy<3W2F0v=KrAJ_U>{ODq3qz_OAA`eMRR0R;VokUeT zDHVQ^Zxux|ngAQ=8_Z|Y4q8U&P4^p`7yaD1Q;J%;5V`-tKnb~h5XcFA7k_CPq09{i zf)zeE?%@b1fG583uDF@8bJMxi;#B4C@RFUIR4Rlcovyg|J=DF(@*A%9At#oR_%ehq z4hXal3T_>$!jblY|cDv6rdcLZaNdYBRI>SxFrqcLLqwX^}>>)VCa9KVp zXSuom$%^8U?wVeh1K5LLp@NK=cF{W?LtEGE0Tgp zrySPOnENK_x3(`Pv0N9}ltVVCKhI1OwgLwUi?|*b% zV20q-q8$-WvzBjk<|elGXI^c`4x%1zHJJE4#NnQ-I4a}=W$X{;4IM_((`*rb2}rva z`Gvi+=(*!vTzfzaoQt;!kc~>y{bWLXCPAb=*o11VPJvFnN??%K-6NnIJ`PWUIo;PN zf!j+w^>~daBZn>H*#iBpFEvn%Y;7>Em+P{K2_32ua~9T!G{h2qsF;t>fj&Emko-AL zD4+OjoHxKD8p?*WIlypA5kfS+rhv&}8%j>LI>ePIxjn zW3clmqQ``+tIRmQ(;c9fG)*0{Iy`$KFwX@D?^N{4FhQFrho8@KG+$m>mk$OT8r^n) zT1$|z$ZyZ)rmv&oK%2y4>6XJ%-G`-8TS~pbV|@*a(uN-JDs%vBVvs(9k;vC@zOu$F z|7z%BPd}zjm=&1~9>SB-n;CP5PBp@-PGjUIX7htJTPQ^$dbVjy)@8VJy(=ZdOagwx z&Y53)_u2Lr4^2dwhcnBt6zv@peS$gA4xLz=Yz_$twLh(q0;0Dc;Q3BP>7D;EyyUc# z*E;au-}$+l5U}VzGWXb|%R$4sT*N8%1kPp?gBYmaEH0-6BS)9{P^!rTZ(`rrC1G1p z!-EZ{j(mRfId~j91=VprN+ycAAi}z}6KlK+HJ=H3AJk(M&;=!ff!|_)i#x`&uP@W; zD8Z7Z(xG}*Xb33G;0UKAC6W1Pd?GiHLWhYVTlng{wFDYiE%4S%YZ<>Jb}KpnZ`ViZ zMVo}PZI7-k1}g=E7ET4@Q&P9!WP1UxDOe&(57{s8__3}h$Za1noG;i2?eT++C+L1! zD?A8R!2uq`tkR6&Fj=bIpy4L;q+njoU949BA6znL4Wpy<3|$R7BGG3=Zn-puN<2nSt~z`%^_nD1|rDI&<5g=N-pU#U%p$^ji) z;sU(YV*veiK?6yAjOkGh&@lm@k%WecLBdezsokhX)44RSy~#U(!#13TX3Lu3!UK^J zJg7ap46J1o_>h)c=TRim^&MI&jkXKnhe&k+4>^ORV{GuToZa9!6dxWsTs>lvR`DMD zB2svHO`$mI)`PiFMJvi#Te&sCoDsXMYU?Zb;PQG=^gT7Xc3StRw@UPd64)9aja(I#zgk8qg!E2d}uwW zf*t894s1gY6&b=q5J5K%?ZA+67>p4HpM4uNFxNpa`oNRCI^z2yek2V>`Wc!@pMA*0ObLQM9*H$w9{E)Dc=vRQ-Zr~q86C|Gs447JOu8|HD#F|j8m2JG zcM!vT;DMuxs|Lr>1}lzO-T74hrG&3bo3-lH&upzr-%pJkYM&%+F-1NVgXf;D%drZ$ zZM{`KDYJu8DF)Ik_1hPo1log3MsLp^jedJGc=v;H0dK%o^FASLG|HK9&Z!aORwB2X3d=?^Q4!!+X+w9KlunQNdbsrYJkS952e z^cY0BiNuwZi1?r0RbZ0(Udca1WW&BRUhn>kA)eG=bfwXh=xQzpYa&Kl$KA5aPR<(J zRF|0$5#MWZp3XOe(Ra)*GY%$N?99gL&0uiW=q*bAjrtg zagZ8MJs!fFS}7>mvC{-vJbVsatvas9l#EN7Pz7t~22P-r&tJIdp-LC#Di&9_Jfyb7 zQ%$X57wP^MFiI>W70TpfnU2tOTyAJS^ky}#6NX2-Ks-n=pz>hVp|$GUgcCMWKb&fe zW1kqCQ9OI788N7b!|R7@px(kTT?M2qH-jihN}>n}jG;c}ax$XgUvSGN=q<9eLU}U} ze+CZ2SKf?uRLl-|>I`EV!?LU-D89h4;SDOlYAquO2xeA(DK^SK+|9D>#<7wvS*~jU z$?1REDhC>Bcba3;QMw)t;!IhZxJ+zWOEMPZ*Cki1Axe8X=6pK!rcePB<6calo{EX` zf+kuW01rD#`3qOQZOY#wW%z;mTmAf$Fd64nE&D`P!zz|RoQY+b?PQ^LZJ{JtKU4`4 zuu%WEr8!10xyaYU36Q{Tvh!^y?2Q+&)?FXQfrZZ$l7zVKqydz>_NBfVS`@!er1XZ; z2MtYFcqiRk4BCymMsS;oVSssW;NfN8G1WkJwcUOy84YwX89knjuy)tPACjm|(^$UU zzU`f_;5lxh=1j6A2DL|Y&crMAI2Kuit9{@ZFL{YR=J6x%;P_E%Y=C_&@o#ZYSX=Ux zu1n7#SXPF_XjvsgBg|OK>mGM7Kdjo6SJADxL*jIxG-o#ifClv+T@a-o!bDE`eSWjT z{jg$iM6C`9@0*tOV`Bm}yOT4LOD>^H2rT%; zUMJpo{|R$L+cS?+nZY6_sY#1hy4i;~BRSV{hiFlSH)1y#HEP}1g5nhh3Sc-vmcY=V z8%UKZC~|H*=f0M<$Vx@5vF1+VeChv!06EpWqfs9hg!^T}#Op!>?ruY03w*?ty-(0> zOn^X{v>;vSY&4rDm{h1mf6yqz_HY&gA!cCsVJ|MV(pY4AW6A-{FF@6H{~n$tez()m ziRe%`7~}&+K=7brxTWe|jsyp6Egc+{GHO)_V^FHa@LI#ogbHwU-i?HX$*wUDYQu@W z!-zyxpyoTQSJlO2;&LJYV68}xfl{Y6$e*`Qt(aw50K$w`RGui!g1z4~S;2ClD$zHp z&WmM?5kkpGQqglkq&8O#Zw*qoq8>zQBP`mK8bjw+pLurHppI6i_0!x@_G1Nq+Q4b!I6Go^TXrj{%OGif# z*i94c@g(;=ScZB9kZPcQ00E7&d>OKv2#o$W%mWf%P%30ytx>W~*bRKGJTJGNFV9@rBi(dn<;4QQO^)NfgW7!<{mN5PnmU zUaYo7l+ddd6&5Cn%M!94ctojcc11S2sb2&Gwf6&5>HxrUx0>fQhY%)(QZjG&1geJp za3pCO4B_7nBZGq=us&!e!eznbN0)-!sTrU;?o9l#ThYOUM)nbtT8;?Re)anMcA@fQ z>{}?c+v~(Z%Aq;1Z5(U%n`+hNJ*5MWO36z))5)mG;NO^D6}?{B<4T4_$AilH0VFa~~1hC!?SqjUFxPrm-x6j&4&IB=i8O zq=$Xg@u4Qfdisb&E(j|UL6ubKW3V=Ky8`VWiB3wtMQi3un>K;5>DBJ^RlI}*VrRm- z1)c52ApdKpJ2(MHFuMF2r0HOsm$82R+Gq{P=m^MFF2wCijvtT^Mi9j{p5O~BQtD~8 z7|{F@35$qYu1qCNVUTh0HXomh)imV@hLmGfrpL5LE3F_?j9_CuW-d|J`M8V>O13VX zh-i)REb&L75mgT72pR6>P)X1U*pP@{Pob$!+hSSZ6qoN{pwuNS5vMF2NiKT6$WgpO z`{~#2_ct^WvB7it-VnJOjr5!{Fr~80>{fE2EjsK2iHz%88IjtQzI~?~%l1LHE+IK>Nsx_ChPC70Mk9$6HII> z>xtYnM_+iM7%|{9L)xo;HIwhpOasR2nh3~oUuu?`Fnc)@bB*PM&5yvyJHHk%BRHTq z(M?~(_tTgkTxA{${1Pm3(XVTK0UO-}c}z~>J1Mx=5*)V!^w_e*%14k2>p}drHZsIZ zC=We=@fi{J3tjP%Do>yl{xI)t{WJ=+KBNN*#k%%X_p44FXxGH1#!m?WY0=!6G#G7g z+f_yLX-R_~b%5+Yf;dxBQXXb1S$0?T{26x4)6NXhu;`GOmQ}1b$c(-SK^7R(o9OAo zJ5~-9ToXFfi!oWoO6f_&_2zY`{~@nqKWa5FU{hikQNU{s+j9W6<1JKr=1yQk3+S7I zc7-%p{82Tx?Ho?}IYaI>qaevJF#+FGHs26Bgl@@8KjQ1@GB85=%oz_A4UGp~2D^8P z0t}lI%d;I#Ka*c@*j{D9&)>eG9Us%h+VT)ii~n)ywH+@q}x`LQKO(tv!+K5-22fI=C*k@)YC?iRGTd z8iqTUe1vFFP!CrcxmssarW=es=?3?+^BN)cToyhLrpevp>LT>9MWaOrM|*y6w?0cY zoyg$m?pySzPGg}kbWs$iZVAL5;i8>&a3z|7iFabkay|U&A{RQs`J(qyBFqhupp0(2 zK%$Lj;g?sCUBr!yQtZx|F#B=U9WdAEQbCH?WVdT^x%ZiKEa6#xj44mrv8sGD4r6z) zRgHO=BI9sZfHkfJY(^)sIx=ymjXGxF4R4vp zJBG`8=>`uHlF7y4t<~lZ{xz#yf-|$aq^SaFvl{Gid_MdGfDkG-;uw9V`9}KQ!`T?L zcOOdJ`H`-Hj8eR2p?6#|Y)DG=NX_r3!9#7Xtek^*BHse8Ky zFh#j!Vi1h^K<5v>l6ayfY`+}n#kFKk+T}sQ5AEOk22`pXV{S~!(}c%oRAz*))|Qg6 z2;*Pq4Ca2uA7RJOVJ++(YJE|LHYNFMi7eD}zSFG4Zv9?rT*YNZyC+Bfm7_GCH4U>g zth>0|IZh!i{qW3~c$-bCK?MEyIsXc^92dGgQA;~gZeajAF+#G>)Frb+P9zg;3`vub zDp0E?(R)6pK*OIH5!?iEvER-aLdWK5-n--Tv+3a|%^HT^>{e0wnEBYuoRH~MtFMwA zCl|30N|ij)kRi@mDWjdBPi0vJ11-TF!C7DE>YY?~IB#Hpc5nv_3Xuvg`M=G|hS9Bu zE8$l7S!P2AH$gYJy|0GdK-yAdHwvscvJVM+nrYo!tb&jn~GcdqSUUi*2yq>j>VfmeDcQ zbxJAYaA#IZ#AcdX&y7!b^q(vyv8A|fk7E>wkvb^Flb-7qmK@>)O3xxPQZ_AG)bD9( z|B2y2oSWl5YKjE=(uXiXqrh|B+Tuh6YMJ!}HN~icZPy+IHv!2>;_<+fLH%PdNxE zqzVflKSQ5^NdG=)g4M8;Nk^#Ojzz z$V5u1~2LR6PnM?&UgxxHU0+n1BFXfP5tgeg(^P`r_+XwFg2zSTs4p}*)|X{A0!14bP4nP0Y`J_S(+0uo zT2_Eq3sa^Nga@)yQQLW`tAR*=;+A?F*=B}^N-ISUSNJqSmMjR0mDfNN!HJX8-LPHF zgM~VGkx0PMERIzwWEGb6C?4{3cA6Pu4rZVBx54v=yBN3m-FC_-kE#oRVY;K}VYT;} z`|xzVj60DUY@+EYoN^o&?lH-Avy_>_)$qoj#ZAX_x`|{OC0w7pNv49>2FO`BZ%TUH z`{4=sQ(|R@nLEP9jwQiF%}bCRglxP4Z1V^{q>9b&xbuCE%NMWxF;>8OyQ^B?s554o zm2^GMUr0XVYp``o>nWITya9YnvAWQfPehE4`i>c6NAe&M8R~@V&go?sQ})8;RJ#E; zXNHYRM_-3rnt=el@BmZ$hJ|?KUil2IjeenN)EfCnx$xXvh<>r7-s-~Ua<+Z?B)Sw; zz%J7rF}lHH0K(-3c;7j{AQU=5n#QwLZ9|XQLL<0;G+SUhXBxFG1KVt%8HrNV(K7}Z zS=z}h<7QL!fV556Ii6C*rNN;!15?Dpswe$6eNs?TK;YyYzwe!}}|B4IAAcym1 zvn$Xp)jAS4Sm$9%!y9TStadS#q^}8TST!!}<2WDFpUZ^Q*Ck&j(hrv$?d(m2&H&Ck z7`#htqcT>8%j<1UQ}KvbOB_u)%4;B6NS5@oM>?1%OY^Y}ZST}Rzi$fSCM zd|MP}4Yd=!u~Y^MLRExCm;^#P>2l4vkBmcKDqj^!sS;^3wKf`Lhq})}gm)%#^24`@ zn#Kjc;xxBtSl0z+{z8g3*=;jBr`tZQ5a`yx3kxIWA_zU|$36$==!0aHCnzjtn$U3M zVX8w~&x%fy?UNn%C|zU!=`lJkOo=h}Jex|Lp^BO0)5moYhSDknEZ` z-XJ3yrpsrvoj6j(h#%SwK8QW~=z!?I?Su!nk0}>PE7##qD&mOSu=c-x%2gAqjF>(`Ds28r2roA8HRyPh7eKZ0Kq}< z{YzKfB<+=6F1HyO9Npbqe#D)c44tfx$ly3cqOrO0N)R!vC|g7eI(M=oOy?~Ts7J~K zD|Cv4Id>>!()uy?0g9+c81p@jOM35c^AjToy}}zpyMqhQgJ{F{-`=iC)Z{`1%N_7R zVNh5wEPVX!`U}eBCM+GRbrW;VOz12ZlCQco&_ddQ%;thwFs0JtGV!7GUxe+roR@NH z8#4{Y$)+sXGUIlF;aey?1aUQJKQ$c3BC1O#cF0SONhaU6ucg5Y zk=Cn7In})d8CGqWmc)O~r&Jcn93>d!Q%}%W&i9OrO^Z;*)u2`nc#tL$0nv#2!Zx>Y zoG?2MX2Y^Ff191}mz-o9uDP zN)Sv>Z8_MCtd4bpU6HGR(wd#6=*&u(sw5AV>3L<2UXYwb3ag_$WdwM(W+TBpVN{d+%J;oHQ; zQW_^?n6*cRJtu^UV^D5(Pty`ek}|Vk_9!pQ9*C-|TosMd(5hD4EyV{M(^eO^F@At~ zR#Zuv?!ki?&G?&?Wkee{kP}Gs+-z}|>ePXznaAV6q0!J5HxjM>zUL{&G!}W;GA-6U zeneOx{Rqd8rTk_BE;)xCHzAHf!!T@+r~T1jvp+G>bTwKSa6yycj0Fv7=DPQJ^qbx! zDE#W~FIAiwmJ%aL>A}|!lq0>#qMuVTF{>U984zxdV>upOFWj;+ombo7t4CjKTQVg~ z&LyDs|C^zw!creNWmH0`w@`qDRHBM`)b})U{G1ApBjF8Xw7xV+#j5GxKG zYQPE%inbw`)cdk5I01V{=Idqh>-^Ys5l*0Mmdt%ifkr5k+A^*U?8lJ`gUB^;ahmKJ zzNoGKvK4r(5v`fCtK9tUd)+`EL94>2&k;i zwR@XN-^daK70agFkQ*_p208E81gQR*XwlXLE)J~(I z!ODIXjV;ysE9uV~csuElGrh*k=TYJC-#f48C!5L}CMK9krnD@SL@8(y%2N5ENgLWp zJYD`qb9Y%Tkbq9SfZ8Bn2@E8jq%FNg1vK|-0}2d+W{oV5->OXC5E3Cgh|_6)HiVR6 zl(ICrEW*$ofYxpWGC-!zRhXSy$Oy8W3+TANCh{1e+MFs?=QPY?&k%ic9=b4uoFTtD ziKETYllD}ubQ^tNWZOYw3JwjVwV*hM5i@WqYV;_TFB)SD6J#^!@Z~s;-Y%_77siz* zLI^tbop}KjP(lOsGK%K|xsuUA2AE9ehR+DrB`t5Knh>hs%snAln;eff;W~CKjwJt4 z*zCr1obplpH^Nt|OjPRURu2={*8ZQBM$=}9VOa~auUESoX_kie1km|#rd>c%#MFBT zdztDG`g%A-J%Qt(Fe^OSYajkG9y5{yt85a2{{4Nk5xVwp!#SvSUH1qd&t%amQI>Gm zs_HDlr(=NR4OC8|ln#|VwzLXL00a-|`qLQWi^bq`KreU1HpSc$va(@uAEz>zr^CZT z?Z1o#vHC-3EZR2nm=*}nxeXrc6wjdh z3^GSMgT)fwzVXrzl*7!idgX%*eW@>yR{lSK^^+T#WqM@w3~;~%`^zw80~lcDG>5$6 zu%pAA#1{H^ff)+Vjmew+0@n&1ISXiK4D7AYz%e=-bt0^+qOobMvBKco?-r8s=Vb&D z?tch)MdObbfJa@uW}Uv3o_s)E^dXN{Vnb@Bgg~_Dq@ge^u#9WqZOCgt>y#@!frt6R z7qeH!!^2^QA&hCg?*W-;nVsBy*ZpOsB-O+vBS!+!B29g&TxW1(F_{6T=V#Ch5iJFK z5hEE8|M@#Vbmh@x6t+8hMDyl3qulXQ|ItOR7$jX-3*ZKZS}S_}79%D_skr0>{r)mj z??tZ;V^1R{%gK`oTkf_bY~IjmjK;c)D!$0&jrL5Xgy!O#dkOJ{WE>c@Fec4f2%Bz6 z^T1Ie)i{4q- zd>mzC;7M_}$JoX7r{tUZ++T-F26KgVLc)stq%YcK*1FMV+glLT6m*DT|^?J%IH zF!kx&JN6?y7-*2vG)>?fUG-G$t$?QcUVcuSQ^0G6F7ym5x+FaJswSe**%Kz}6vZ-6 z+_$XrjrvyP>PV>2<}id{(b_k1#|@Im3{jMbn*N7m-$O7vgA0rQ z$%REZuCgEq+=44ukYtsmKpx$bbP#=+vUNRnq0YsUgn8dA~ z*`7)TW#1PcA3s9-MxoLak}IKs9AOWlJRd{?39e>)pf_g4CrT}0Yz4uCF8tt-lo%ZG zY3;B}Y0>s;c6hye4m~N#04!Wihh?4Wu-Bs6(=LdW8YTtkXt5#(My52tmmcS)5Ku}9 z9hIWx+a-@zDX@IV^ER;eEmmsd#ZN#!Nk2bLtYG24(G}keuSdVJ!&UL6&%@&PHnDRv zmNCQ>8a8N*T>;wJ&OuzUfl|#xNLIy=G^)Y83}N ztrQT=;Vbu)8=4!4z^{2>ei?&yR}BZVu4-K=tpx0JYHrLWfM-q{hjOABX~_2~-oBvw z@ffVg3XS)fVX^@NnFqe9Q${X?89rYXC+W!rnkk-A_;GBzSdV4{*~Ui7CV}w9Mpt6R`fxAEykT?pOK|{y?tkcnPdFFe+k%uW@U=CFFdRSRJ&iTR=J(bZa=*4cE1lid|o4kO44jQnhwy|^c%z(FC z#Fx1tbf&-+Md;#LdICc-TXJ~_Arzi?nxn0?UPN!gNs*#h1Qyq^ZnAboT(5EwZl}>R z3hWrX=B7rru(B)>{vLz;aM|)xZy5SCd$`$Bf7a)Ir0A9+$_vt@fkme;EX9 z6#x7G{(Btpt<}gVW78wMO_zsFSusr;MlwcIqF3(K0u3WVKDdxxGu0SKB;k%TRcYS_ zg|JUK!Q_|vsew&@wp;vfPxdC>mDQgY$@9$KGTYn_6cy}P+ZHR;4_ZGdGegh78G>@e zW5Gwymj)W)6naTw6uBo*{T8mk*PdAlD5?58(Xq#aFr0%9?`qKGA0l zdg3M4VOdcqA_V~$v&PStWdlg%grD*3ot)I3bGhT%gPLbLj49eF;0K1^)28^kKP$Y! z3@CzQCeN$6CvZxLZHBzg?UaMf@MY)2ay5xR?m4Ccu`OUyh@ z-D5P14kCZp2ckM7G7UYy-M1{a8HeVWs8_p3(O}_b`k4qZOz^~>@&*v8Zt;;XEv58k z3HHJ#U@EqO9q!H`)}o4}0hrX!FY%VO^>dwH-@!{*89v@|Y$vei1oF$$`iAhUAvP4I7@b~{6a_hu*ID|=mIwuyrS-)p00hR!3V-n_c2P&r3?yvTeYSUDNs#C z1<_hVPxizNX|YzPp?vbi#Z}}3?qIK{gzdVu3rHm%VQd8>Sx&(B z(`s?Hwwp>l8k_Oio`ciP+&MC=7Mq@ zIPMW^MjtT%RDK6%SUJZmMX2i#%A++WzCu&YR0T% zo&kDesACb_6qBBR0yDMQu0__TW4~pK$rTf7Qc0WqfbqB1b~hHlaB^$+E}-`bl0^$C8JD&WDY&xkLH>o zPE2QT{qQ7sVbbebHwK4e{q&VzzuKv|=^y^wutz~1hoCfTy&HKdCskzGoWrg}R#?Hb z3fv-cJ}Io35W!CBWk9Iqwtol9VoxS*632v6!~yJ(oS zZ4aKt7$Pp0RY}Pj?Qxm4^00)p@sQFZZSFjruYZ@*|0HKJrENKVl={|D$; zH}&@pwEtN0Gm~Oy8WTl%FVQwaHEV5 zcZd5@IMPUP*f*Tf{VQ_BA#2+Olnk*2)bHq;Z%lQ;7uS`*le)_DD3W_LIzAe4bOnst zSQcZVu18s`GcWidfh9V(@W{Q1{9U|%O7O1%Zf>30$rES7Gg(b)!vW_?DCHYeGlJ^5 zD~G_NXvV~(+$kQ%gLy@;W@!h322cb-j)Z}*?i7I$QqMRXyBHZzpoH(aKT~XAHvr__ z_-4*ktySmsm(b&qIy4+>e%cg?G@k9l08MLGot2_n^G~@(yntkiDH2v#Il?6lEbJ({ zjJ?DI_pP!R81=WB>4g#x5iXR;D}L-)1o(7#`(ux*DfT{-qTab{=xX_9PB;;hWefbpFnTu$T>SLPI$*slsr z*^*RVjqnwln6n_bCQ;VbJFvG7*hE`S32i_noq2Cn1|qE71cQ8t!D}WORW2CCC>{i? zf{gd(M0Fow(hCA!^!N%(MZRGU=5T)+ptNUzAlmpwt?w27Qn5~fws4#UW~Qi&t{ztJ&+ zl7@noy)G|4a0u3LMdbbKHY z=?7#j4i6=qzOoeZuGHsRCmwzR^m(CBOc>0RUZt?B1?lx+?V4EuhVAY0lBQ68K*+RE zV?ldqY!lJWnZX}*o7xzoMh}De_uF}p$D86Zs=+we5NrpeY<$PH!AJ(as>F)K0+7d4 zwVf7(vQOCNfV<$Tgd+?;*M#K7IA)-i6{S1FJlV6I>4djU1win^IIOgT@!(@yx9-Ls4G$usIsgu~Co_8<#UM z3Wk`|o?wGwX=}{eUk#~{)WLCA0XJP#jbw%dc!&+I~L*vAoRERv0kZeS=}?AeB{`+BNdUs-6;ondI@dM8%G_=CiB1|yx;#&2Y&8(a zC(Z-f=^Klpp4FbAQ{#Wn4fF$^_bM*VPU>JS9gj(pOz&{&3Du<#YS$Fr4b-R5$n7Kw zYLw)#1cxV&c_?{zMT(DImnDN4LI4)_KpajMLxm3xa#THRP2er=7odgK!!_NNG;=bc z8bDM<*3qcnhP5ia?AS{XHMUPo7v@RoHr7Ux9M$lw9Hy**?*nh22XEK;=Sb%KU4oV{uJq1~k02H;2LC&Ig1 zmfAFA7<@nwIpQ^Sq&53W-^Ro#!ZyFs^l;kMng_0u z0H1u0VhCcJ-Q*LCpHNTR@PTTgcWWdpEJK|(1bS`8*eJItzQGEjdlnEPY`%o}P2=rC z44^)((dQ)RYB)?Svm1AP<42_RDaGt|zTwMlRH5dCura{0dE0?ZdwTzNU4xrOYr32i z282SmfyM|)FV+359izq@H8{59IM#|hSSTy-PBL0z>I6oWoR&`dA>B=&6=x-nZg%l?PYYZD{YfY2gF-8lJNjT=%Gs@XKi9danu+%U7)+tQ+FTCW$@;r#I>olml zcW1kxoq2;4F?0H3&B+qL#Hl zJv-F!Y`Fe#bg{<8C8@?wrpGY~ zw*{J?0O$g%65d&r0GAi}tuwooEIb||So1DJRP|ukb zF2Vx!_M<-&GAUMCViB;RnebS@d});D*(0ZE^KmI5izASt>@y@-t$dh>d0_%_a2mK6 z*l4gOeYhW_u_24XB}64Y;O>Ek&7^CFTL2>^(w`0TMP}4&j#c&!H+BBtUCuQk; zxBK8Cnsr0-;1tyJ0{=5LN_|6FX_GnrPqZQt>aK}S1G5DexxvbmcIZ$=PpV*NkJFVr zZ*)=k-pVNqKQh^X|=3omuJ$Ff6rDRa28WR6Q7WvB9U3i*DGs;|{4^vHg z*?c&d)g*k5gw>V}W{;O`f5I44PSDJlnNJ$e-JYU}mLCRq;>;9)W}=W7ypa4z{bR2%q<^^O~tN+y^nBcow#+xdZ)vqa}GNhNb~Au z=1(rEhdyO3+zkoBv>4bEHF0BQxv#6yZR8GSCr}TaX}m_hl;#Rfe?MzjMM4&tXX2^D*1n;l)vllW zMDEJ`VwYvnCCvE=oq{$Y&9u%$>q};+Jh;IrWN_v-xj-K2Yos1iJD&77&u-4bqc77C91U zEJ9HsT@p{PeT<>7`4*_kZW7MWHh)x8r?M1`JF2>~DAnnP8if{yP;CK4tsu}wpEUHY zqv>z9#C^j)NNB)qkJm^|($I!u5|)~EdTjyv?x7Yqa9h4>8@8;yA|dK8X*XUsy_utp z?Ia*`%XVui$+&2)giQFC z^Gjy!r2!{<4yChTd^0yNhyxP+1&eTm5SFLo%D9M1)W|Kt5D#?)21ssKyDSYp1w)y2 zJ5Uu?*ZG1RPDst5O9z4%Mumojjk;r}gE7j!srd0?htL#D2duhy3)R&pTj})|3UfEL znB_inM$bd3Vk(+ijMHrlavCAzg?5iaif^#KF3bmZdHBi2gdZIaT5O6!O$`=ZXLpku zU=pSvZ_yHC1R`71H-Hh|#V;}@vinej;XD+9#4^k8EvD|>`4%&nQesSJjir@hyyx` zM4>YWV1JTid3=UZ9he``qSM=i~AyTleHm}myF7>(amy5(`H zs+)mX<6iHZ$-;sUkwtLvtm< zTfoJUdJzx6woTqN9S3#@CoYR?xr!ub#PEBp zb1ZmxMN2|)37;UvC|le!W~b+|@LLud{#qOc+u6XYM2@zCb?mP#;%QIkejb8K?_P0ABB?ye<+K)$kf6 z0y93Q3FBA%ujVNzjl+&F;~8_DVk5MxXS_ux4H1#E0t6J>YmVY|>OiN8(7Lu+1Q;4l zX{SL7VVWr+G7YFvq7HsK<%_uN%D24Qn_CJ4rs+0>yED;^h^PfcIm%dRwkJKfIX-A~ zkcL3u>%rCiN!x%PsPGJY`IcCS{Dw*iFLw*RK+eihwID3-woe=u(pF04cGU2uHeb?k zyccqJ2hxnzSbb9HR>?)3y`01P#VlHbt<$dRrG}{3mGE%s5Vx;(DnUdGtdrYu3D7i0 zmvo4`OIZx?glkkL^7ug>OlHPZwu$denO*fGwU(jk3`8QCl`mWlnrL7Ch&iqpt z^He=KP*k&lGD=z)X99ScK}=a6d`+yH*`t9buJlepSe7~lET;{jRtqq}NrqGapo@!# z;k$b`OVADReIQ_7FRq7kNbQV_CncVmF)o(NpDqR3I%$MwxPz&lTrziujN0TxS7WdT zFvX?Mv}x*N*4Ce}7KS|GsdJH%b8n7Ijw^6Nj(`R6h1frQ09I3mDts{wBV8WNCX{)S zH0qHkTahYc%oK<~_ zM)6Ml!B|iw9Gy^voq`H zR7%F!jTZW*D`<}!+TxqyR5)eowI#ZIBxs_eMdJl#loAzhbHal9wc#7{zbuZ!!o}}B zyJ#3wvz3jiX;W?bpfji+EAk4yzsxuy(oo&;Le=MZAW!teB4!cN^}$8V(~3((s=W<$ zM49uh(^{6?Nvt+>Zz6|9y4*RN`J&FGaOgf51rWj?T4K;E(FEqvu9iGL(7NY=h=>BI97I4MQ>=b>dOp zj$Dq_#_9sc0?tHX^Fc8wn=-IQdhNcZGs{Tgu2iWf_RBWC2yG&f^+uS%yRpT+h8`1? z1gFFUll8XSjlw49vuOkXe zYdJ5M&O#i+F`UyLPzHAIZty|0G?A0@U^{uo$J9!i?!Td97d)=XkiVI~noNZgbiYkV zc3Af^pIwR|>gcq&Hrvn(1FHAbE9C1%?z-Rn^JH(way_kY;sWZG#A@TsM<;#8>uBNq z=;E#_R2Zt4eJXQP09(&+r*~YjFtW+DpZSpEwMpoe>Z7e zt((R}7YVcb#N!Dk&wdmkt`iZ>lDUN-Y@)-8UHQm&Mn}m^Ry+w5HvAZ!H0=_6>n0>> z&?+O8u%-gFxjH!rUwg~_poS5g*f_F56jMV7$zR75p^xo875K3e#Ly%&%WLXCRhvH^ z3=nGwmeCy_tgR+$gk2a{&nd?#F8OhKa6H;(DxbMOAk7Z|8KL!oaJtRy6y9`P)`Ch0 zN)y8q`B25+UPAEz6Rpu-d^$g;Py{jNI=s}vLq{8OWq3(zP8xQUR|5VPGJ0hxeAuhR zA?KMX;gV!u62a}CNs8o=A=nQkQ5SHhIjFAUZ5QGuh%;n4Jj)Mu*|f(c@|SnhxUJ;K zX5MCf(__GB^`bz5=|xa8rZZ3QI1H_YG?yyTrno!ILyfK=_f#XISUl#zSY8y+shTk! z85RJoq?^bB&3AMwaXeU^krRan4-l{@h%3ownq@S0MzdRnMryY?;p7}?Vq-gLYeO;7 zSHXt6RcTBcP}@%4=;Ly!426X2aR}T30R$?%Clm=oxM<)D6!E3a*p-W(0V!C0!%Z9_ zpc<|8N}rT%Sb%2Jdku=a+VNpsQd|Nxec9~+S%1`Fi(ZXj^$0q1$Jj8j6XN_1UDT2> z)A=$rxi&IkQ||I1G~f}t86;K&Odn_-jKpt9)opwcd4r>Py77z9OOr13Gc8hS9FxoY z$Z20I$@@mt6JZ{Bxg}5^BB89L$H?ycyg_5Pqhl5&xjz))6R$tR4a|VWAlqDOqQ}<& zco9^`LEy?T?sWia=jU7mZrIH!vlaU0vV7=S+q^7=PTW;NlGHvgE_15P6v&~*{9fTx zwf{XPI<2B)D3wOhD+qDH!%P)VOD@K>l;fuD#>Z8vp2kOZ&^A{EG)+u+>y#6DtHEb~ zgqq|lb^_RGE|aP0>e>9rWc%-tz!E8$0|>d{O<5WpR?re#LRWuj`mbQqYX4NiXQc8+`_VYYzg@v{tGF5gas09 z{1kiqVWk#{ha@hMe(k9}1+?Pud=sts8ozMsv&vPjdVIqTKAV(C)8AhF{DgD2_I*Fw zv<^axd8ZJl$uP^WTuCL@dOYuTRG?`WFdkzgvU&d(OjUjhSZa)D<}{@dyRL5smyb{@ zO{DD2NsW)f-?{;s!%ok#NELS2Ki0H|@gaAu4I;a*8m_N!HEm!7Y4RzQ@kO6dR{7PD zb7p&pT%*mn7Y5MaFrPz;;)jTOMBohB{ev5a71K-*;Qp&`05*JDJC z!tx-Dan8GS9}W?9T-%iV=X_?NWC(+8Y!DZUJ<%@nVgDh>f>9uNVhFE*Y$B~YDAAJ@ zC^aGfXjK-Clr8t6)Ratz96y`QcZCzq9d%4rRUe+-9r&r{o3Jz&8vg^vAHAo=&jS{X z;DO*3>1Ba*#w!^Vy%>}GtQ=(sHl{lB1eVf4VSWJ)|G_0h(~449WyyTB(8`jz#;O>_ zeuBw}3NvKp2*&J&)@=fx@h2EWha@1mr-x1<@oFe1-_?q15aa;`!3kk>o74NXNi}bx zP&nbL(|_ktB{yU0c4flr3BxI`c3tNCCO1A7qgBaU$ouW)UJe0GZpLz3{f%Nsl+zd( zif<<)To$6pOe0FDbwXv+@@a@}zz}i_KYr0MM00ySXBm%sX-?pC)-a#0Q(EH2Yc)&q zKuR+hMImL49CU7KR#^bvRDMJQ@kD2_ifeBqTf=?xLT!J_^@d^5VKi(trB65E!-jt3 zlII2gNHU2o*Mm0c9bJ~!W_gZ1Wzff^vO)j|iHLdPl{?CDQplWAe7A$TKF%ngFAv8u zZqk+$#{fr|EP#`L#BGX6%bq7fuNR039sD#8WKces89<9lo-e`Snnisd2CLc@NMIFT zdh3%=g$SR5NVjM?Bb))|Wnv#q`shV%EL2~Moac$85`PZoPj755IR%J4#GKKG^N*-) z1OZ*=#gHP+?`2=j(=d@og3-Dg2k+`3o*mA}JZVah{U=k30}c2FXK0MX-0l$%>s+I9 zna^!`gwbujzNKP4TzMpIy0a#*6V~Yi6z*V6oihtn1WIYN#CbTx;6F^O5Qa+|+tHXh z{qD$d^+Scm{K9+kT^ihz9%O}TroGyR)#N6)7KxlbD0wl_-HpW`EbQBboMp+;N?~v) z=?V(~aZ?dSVFu0&AfhL+7*zjAfTIuNOmiVOvhHxvx%h3B_)w*k7FP>rv?1$OFp<6J zq~96G`)j9yBp$nRk3gpwsg>Dy@+sHdZzOJS!uFRKA*0GMC$bR%cy2(<63Gx*gqg8& z&w(+67}1YD%;(UWGW+8_!zATFUTkTCnfcq0!*ReXF*Q0Hz?~V+ZOa*nW5RVL+HIek zunLmtdksd3fp__SNz7t~#LR4kN$>A?M97VY746V~8_MausJ-(=tH z%bkAme;IohEm@M>#+Eq$|IfI0I^9SBWYmnbB(=J$>SRW^oACi`bn<{+bx@u*3`&TWe^cs2H|=L7Od- zwmzf4KOcnSb!e1T4l4J}De2Q^*xD4VY7Lv^fW<#%7=z#=o_6NTh?6jn9?VrurKXkJ znm?2h9mpiB`?`8l{*(!YPX}g(eP}Z_K2!}&UHY-q*~XOew_v$HxTn$>r`p8H6I0^u!&s5kHJi?_V5LC4P6J3)q6;oPT;wlkplGW@I7h+p%#74ELs4xA3lJAn$dWm~~auDE6q8umsr_%KubSSbW1b^pMAE)483wc?IqOqoz zwFp~TsC1@8j{pg*)MKw*`{o>l6hosN106BWBz7B_@wx*|*D_5gDR@zch7&H+9rZa!pEy^`goQC@R>{pNQHII*2c1@0hghSZ%$l}Wkn3d-Y2e4AufUsZ) z%LaFxdHKccB z(r&X{;IB!KsINHm5-8YgdREQ(u$ZX>sh#?LaixI%vA3X|;{ zuT*Vbw;&AfXwf}bJ()n$IjW;;o1ypc=Y^Fjray8{Sc47lO-R{cmK3ofWm-+CKJoBa zZ^WU-7U74&>)$H9^T*>?R&u&EzB#3V$AR-?trnJ9VR;Hk3nl(Uu-sW%!fhN=mrumb z2?PF1g$6sX`W0w13CzaBDLu&HC6T^0<$tvw-$B+oKK9bnw2NxiiYVG&qUW`8U!&d_ zcR_LA@{PDu9Wn88M};q@?6iXxeB1?sbAkHY`&$P*hpU7)u~yccb@YO(AFVWe=P3t; zHe8sL05&|eW)+&cyqy#_n$-8tu^f0|5<`R`YOkn`dvi?RjJmP5ljuPKB<}2s7AJrB zgghQ_;z_t|Kv2tS?`q`El|OfNs|@0ats%TdZ?6i67rbWF)llN+dKe|_de2o^*iOU| zQBlV~C=FFb&4U#9T<`EPbUohI+`CwJPlBCh5q@%#Bh}%*ipcG2% zCJU}V+So_YA864GJA`7B3cEr7t}$w&B5F&>j^olYxQxEr-zJfU6+F3G3)Bbc%`H&5 z1j3wi%qkmS;4{n;-P}=*#{mICq$zMEnhN!r{$zYv*G8@#g4?)RiPqA1vSI$xvfY8W@fndHp7b9l3c+w~_6sU;Fzf-s>uvEe*sBd|p{h!JnODt2 zR^lfgIwm6jG6Wwk)MhZ7SnF1#fpJBcG&ajgNFZcGAZe%L6=Sd<#alDl0RDVN*Eh9= zfDF*G>|oS921j{#6$g{y&>J3F^hTLD)_~0CXcKmM(h_dMI{`*ECnGz25>%YBZd&}} zAz?GE?ep7F!opw)b8(_@8e}#l8I@*+GF5Qz{N47b?0_&sfTs~!e z#{Q`^LM{~6iZ_bkY7aFiiJxUPxQ_*%yGSSP`&vT&!ZU6{nNbp30?(WgXe9^i3Yp&2 zs9sT1!CVAP&zk)|fB!#K+F`1Im&60kzOCrt5RikD+hqy*=(RMwp2E9pCB}(gqnasv z2&39ZjTDM064!Qdlu@$>XGEpRSZqQF^bP6dgtYfIIY*!6v5J7D z`skL&qh`SUZ%HOtw{28u7;J>O(rHzz(~YFMXAux5>L*B_Rg{k@DP_1!0}vbj4yyh@ z10x)MCnNQfw`7ea4l>XqACPhA?0zd}BtSVgj!x}!HiH(9K!z!o`(Jr$kbkHtX4r1x zb-s4R@|eQb!w2fG$!@?1VhGSk*LLgZACvS9#P_R%elnsZKb4r7pEN#E?$(zCe9Gi4 zGN4Nw_sWcmaYfTJzC~vnXa)~V-KByf3IOZdvk>JJ@PXKM@Frx@QM#YV>0yNm2p;~i z1b#R)z81-e*i0XAML4ByZI&Hz;!Ud86&e3}DY2nr=0;T(F?KlEP$uhwyV zTFA6TFhp}BSeuF#cDW*P8=|g>B*6ugkYO47fk2mN^i?&|f;s~~C>X(qWwWiK0CWPe zCVpsBG-^6BMMWoMvO3VIqT@DDm7AcRth+s0fGw<{Lk8hGpBRAhgSN#WlS-=bZ;UO% z@IQ%dXJ?sUMF-?#?}W(JNzGt+9H>nSf~|Uv4npiBP<1`ryM^*xg^_Dq3d@S`pQ*N< zdhQd-y=4Z zp8IsYg-l7%kTcaVnFEJv1r2L~L=H1B(tV@RNJX?a8CnF0f_2hm>L&8a$*oa6lkUfB zbIwxJ+7nxFAl^uT7!G_nsJ;YEL;9QETt?3H)dbS$an^f5KQ$0nKG8Ef%$v?QTi|5S%j5p{SBaO2s)J!$cjh?UG9mxA;SNL@|UW6*Ux?D8G7}LGoq41qLknY zXGYOXtn}WU_Q{#PPfQCIwkLZ>?DYe3Gfz&iy8L4p%NGXDS#|p#kIG$WFo7pDi?QRI zeFJL~i{`2X2O8_3cUJi~<|Iojw*y#n2_DKPqreZsSwsCmABfE2H)TfZ| zcK}14hYk6i)GUMyg|R4YIAU+5D3xlcTy2kykdu1C*A*^u2NW9y_3|w1zj+%h6U~)| zBbJ~ziONC=aA+2#0T0?7SRdT{&X^bGt>s)&r^IB$#g2Gp#5tkD$)^Jrc`$xJf&s& z+9Jp0=mx4+camS=PJv3NttA3NQTxgmo`(&Q){8ez*d>yvDA#EbtJ<87wP}H##{3r? z0#2|%1G=HnmNwl~<73=CB0J}7%htTNqgE(yu*M?ADD2H>0R<0Lx%+_ICdmCb8xFpe zxwB|!Tt3nkW7Hk&{jG>cVTO8GdD)Ooj4%o8Rg)10c&9Uuze&Rgv{oNAE@rj?h&_{@ z`;b8dIK)aQGk$2k4RyHB2a?jQJ=If;SaaRLI`ei_<`GugUR9q40+Fo%Vq<_A_oUMK z8Xn&{Yb8#9x=|UGil>~x&D0wvAmoHNhySg=FpIs_AvtE1FD(e;wOrz^-EaU%SmQXpC8ePy*pf9xW#n5Hx zltXCjapeydo?H}?8LE4Yz}Y~qOH<_-u!a#`%K640ustl$G{h~P;i5XykgSJ( z$7KD^bn)1(p#~xQ*ZElaO`k%iZGi6?^9tC-+RKb2RQJHV(gxjbwZH&rPkIejN=hHIE*&vQyF(^tFZrDwd3%VR7E1cY;>iD3Mv{Y(pAGIr^uf zxz#=!Iayp%={aR-p94C@wXlSRqbwM?{M7B#FF+g+s4cs#TaCnT4*XfGp4to&(5UzF z%V}!Vyv(1+_G#8SeO1?fAq?J>xxjw^XmwvaSRnqRq{&ABSFAE@X7LJQY(0wgQ5*)7 zx!}x0)Rs)Vs5adp#w7w+F-Y$ad}Cu*TW!VwX)iq`=M{*v^i8B7n;EF0kI?SaG6Zq- z>UqDbYhjWx-aF6PnTN|7HhPvN!tg3#%KK4%2__z==I*xTkf+bg#7FG~qD^X$-51nT zaqM<}MX_~~L#a-a+|u8Jefo5$r1yL#_W9{YqO?lwGJr67Xy2#TL~TYJVb3cy07pQ$ zza*#Rd=1t1L{Bq^&&5CG7(5YDKwGTKZ}C=NcMkw1@yCX~$7uXj{u~_|cN8?TD3!%& zDfdV=H!%79fxX*0Gdae3>D}5+Wl&WF%$h%3G4pB*a+kY!>F8MxQdG&0P^>!5C@N%p z?GLyjksBc(^ec^(7!&Z4*GIG0TU$hINDTd;+X$z)d;Bi$tTJmY_+Kq}`IfQF{pa@= zMVL&v@+bM(P3pgIUC^eWB|`m|YXR}9S4tKuUoa6T!vf-?EchZ&`i!7Gde}kl$0Xiv zJB#`PA~Bnf|2OOeX4=NnR)gmY&AbYfK>;BzE;|ymw|-M@)(TjH{xMfxa#O>f>x(i& zrnL?#cpj$iNEJW{w=%KcQNpf(2yHx zlw?7j?lQ~IiV0@i^Sw?}ffMTzk%ZD1Ec)RS9t>P1q{V}N%pKb+?iU4(Klq^3RlWlW zt8jRtUqs*$`1_wucbI+STVoouxwCQ2j?|}>zh{6AZ>2n8oADmRjqqKL_bn^oAYBb% z5_rzBjv_PiJ)TJyqP%!MQ|VvV01&g#fM)LBn$LQfnw%a&Qecd@i~yz->2Sw4 zaxdtx5G%!5H~81hRl}VT7-Kd2U-@K^n~b~7!4X$zSvXRx&c%77P@K_UptRTW0-f?($MYx zr3$Kgo<(=%*0BJc$_FIRj5sxl5|!PH&Ka5Mo7pooBZsysG~R7W|5F>gj7krgCkt2Nc!w0t{ zmx@nOTe#b^=8vaT9iFMns_?&$*6F?c-B&is#g0;CuyU+cwB#5fgrSo(APR??sOy;{ z>EQRPX8wf+2hFwP#NCr2zzck@&e}wtS_9#fH;~B`=oVT?qg;Lkv-pacex6Wv2E$3^ zGsl;Pj;)LtV|?e}?j2aO^DAh&TiDJAmi^s61G=9VUBEkBa@@w4zULZ0gp%=_s#edf zVb~kVkJIqBl2G!8gPF8$QLGQW3M+X|(cV8&wGtGm;~EQ#oeEDdK{`=sX+Dol>F4Z( zsny!;DcU3~Yf864IrrfV@`i;BQ!i5@`IDe_$sM}+-pc+*rSx|tHX?8 z&O+N@9JfrS;c6y^$Nnsq3j`DfNG^-2oSARMO*cU?vKkop3mXq!=J(d*Iu`(iCz{HAazp zankS~=iao-0W;f{9*2F4khiN7zekmtW)G9KzB?$wjOVS2)Gy1i(W&m7-89Xdre@{* z@-SV}BT*|dFT!32h|soA{de4H64<=+lRW#No5HSv5A^);0(XN)%{3Z5wP?-`VlnbD zRsDO@I-$!bH%K=ANRD^-MMPPZLnI$ZIpj)ylx+l>)cavDRaDL@7yrj{1CYD^o}`O@ zf{FznaF0|8(8MVC)ZNk!Bj0J5Z>+^WjV;#VSu- zi38D^ekl+x2-1Bg;biCXrbwj1y1978?_bsg2dJ-N-sR>g0Ba{2kxwo*at|X@OD0r-?!mEz>^39klD|i@==A}S z8*~xuLR|5snwx#3z?4!G67?Q(p2AEps8N^VLQKT^F-USX$6h0~O@b~>$!SwbyV@v7 zs@Lp1Xh8AAVM8S4)CJ0A0pTT!={;^w9UeGX@m9N*)@UN)1-~b23~q!%!_e#oW|O+i z5=P09t|2B;QCF-COb?|6PEbCN!J&mcN*e^rv(!H>#=bUf5F6nD^==#NShDk%a3 zir(8S(}Uw>*JGi7p)sn>WJ4X1I`CWgl|7(c2Fl6n0qxRKr=F8kdL`m7DiX97!*QK! zRn)EYY{uH5_fS!B>EXHy`aNxc%cSizoJmXM2#7)GWyT#V(F#s3u75w)%|`pjQu52e zG=<00Xo{c+6Sh@8{+~jGF&a^a(cTu(SYRE44OcVwM5jL!b`q!_rq^oc$B3OK4Or3vSk&c9Z)lh`?dp2%saAc#dceYTN|o+jJ6lPg_M_A>EvdGew}1?Ek1yzm^1PYX?r?G1j0DvO``7r%Y;7qZc5(I z9JuD18s;Z<$QIUbt1q5S(v6Kv!3wWcy*>1wMcF4K*y3ev9w_=D4%T_q-emrZiHZvB zz>1jF$q7Bx`33&c!9M?C6>;G50K*a%@DzAvqsGNGv!6?{K0y)x2vc5S|MboKmjaYG zXx3kt^TV)&Wufko!0HuDAC@9=3~r4r-y_zpX)kQ3!>+Em1O{d^57Xf`xf1ro!)C7D z73#)JG%cm0WCVK}MlWM56elA|*jwqe!id++T!`^WW32NR4jCA;utiY{Fr^`%)2@6RR=f^xq7VMifEP*04c0jkEOT#_C_@x)Rz@ed+8 z!dylq_@AXqbZZDwDf9-(TA(crmfWFG?T$4AvLDTjubB*>S_(4+*coD8U1Y?cPOY${ zs+ugq|Bwu*&dYt~rTMz{x$1u(UsM__sNzA!N24hfE-;jC+F?8opMp$T}vrrYq zfxVHq2NsC?J1S1va_Kd2VsDqOPzY<)C@d?t9y&447TCVqdS&?(YY4@zcpZVuxS;}W zBx<~$A$UPD0!{0tm^T>^8;(_YF_l zF2J1xO#|Ke_|>+y>~Gly>1$NN;bmgTEh3HJ(&^ZnCV6O6Hi1sg^i^%RcHnAuNT6Gq zqRE4^QIdt&g+_B_PC3Q;!h*O?rJz%F1qWATrgrE-9tUa0%E+8?$mnoeHf}i8i*p6A zy7o`kaT`yHE;oE59aJv$OQ^*)JG>DP=wDA8E3j~g?P9b@9ny9{Uj(sq4Jnt-T**W# zV8#ME`e}IR`dgWDL@Po8DrZe`DiSZtD9v1HY-ZJpOSNz;e=#hzNE)#Za?hS3JNb^I z7E(n*FBtd~Q#O0b42N%9O52;2C)3IL5&|=6?*NQZ^%?~o$=j;fh4`;=Wy_=E)qCS& zQo!}jJjq49<%FB_ZkbYEp8)D4M{)5Mbv^z(-DTj$eVPFM{Zlx{cfYH`bjiw*y_(11 z3$gdk*smIfNB|1v?& zHR^;hg|Cce=TN*19qMDlCFT0>URu0(sJ#Z8E2Rf| zbVMKRPenMPw5MZ)m8wTy`Z)e<`y)boTCG?b%)*B~a}4)8bDdPt7PsSTjDW4&^QrE zTSM~qno#zdk}Gj^+Mz%beRoCT4^KwiOq4}d3+v_0#5FiJG^mQAJq|fO8$Do{zF+f} zC`?5bDKQ@3+G*BwXw*18RZ1Gn9?w!`2=EWhPKOSv6;DlZ?~64#0CN#9x#fb)(YUf-ic0YP(;|uooi(1nP|h)0i^IXn>-K5Z6|aIteA<+6(sA~pPq2v&$2SNw!%!f+ z&A{N>BFGfM%rnHo6xgVC_K{K~2h6N8J_Z2*~b6F=HTry$}m zXfSpJ8Xt?7aIb}uhXg-$dCRZIDoSL{)LOPzSmRi+f26?JCMnwRZ4>V}f?!OUYj{y~*v0_nR zr5pL9z?D@_wPj+p!|;UrP+Vy`n5KcUOaale8QLV|{e=G}7p5i_j%~ZFl7NQ_ zXvQts>ga%Pm*BZ^rldp$mP*hSGgxOd$88~hUL9y~VT34qDzdC-5${*H(sa?F%nVE1 zFw{ATGE{@FJfMrdoF`GHO*ATC%Q{hqj4n4FpO1qj%($(gk>oM@7)~xwtqpym5a3$N znl0hA&@mD#Q#O+G{GTdJiG{6O!sl3wd`m1Mrkx_PgvlM*iKKgkGjf!86AckY39!N> zDRa2CFt{6M%fo2Ne2rvZL0>>NEksKV9H3>BJW8wWY$v-us5^vlk|6@ZULf)B(@wV* zPyqo&!_T(+KJM`=CIFj-1NMvaCe%QytR|b&@}G5>#;REzZX1q%m-!#_IE!(#{#p}9 zCiL<`7?hh!#pkN~!2Zn`nZ38<9`E6ZczoKfW$H(&9ZJ;Ym|4fx##3AvCK!MI`CKw@ zmD_xe3|j=V=c~;;-E%(hxVSLGT})m?*N(sxa80K`;9TLa?x5XXez2`rpRRGL$f^2} zb%}cxuVyl9GbsXT%0$4E?&mEz&S1?)8f3Op1O8WHjPCnnzGI~xgzH7oKjv_OA+jbF zNz`RzYJO-|bVChl+AToCzi*tz^Vt2x?u>Mkmb8ky!G<0h9n$NkdYkyTj)OO{>M_xP zGKiMrG2mcze2w&W`kk=NGuLEQPIRnVf-%clq|A}OpGcae`~!7$^cIW7pK?eBO;Pt# z?5sY;gx7Gu!VOIOfqWGmOm>6z!;%DN_? z{gNGOWfIbVsyGCP0EwsS70X7|lMgItBL5?m1XAse6=}T3 zTCcj#@W9Jh@zj$rXDS4R$tjF5x1eFcWQkpR;qVE_NeEgefPy$Gv4ARZv zRwzZENBWBzdznfuj4xlQ9cjr1_l>S0-$UOHuT9alJC?7(XMAP?(^+1OCv5hnAL6N| zCKgpj*SwjL;rpVL`tJ7;G@OvmL*MNTjeT+xJ?)vhqLR#EBc&MnvgWaa)B_E<1x#j^ zzp=ap>#V~Fjk~~|&z?s?2Ss~V;jwtUS>EhLlI$ZPS%DgQ^@}t{QYt>ZDmD@oymX^u-}aFwy38(hD+}O zruxgctl?rhgeM~5`dtp1&aSR&_*tG3Pjx&)3k9e$z}K(Bt7FRGDSOKJz*)6p#jw+E z{vg0WG;&i)=#yY;X`v;c{L(Nzn7w<3Zm3asFG2z1#l$4jj`NT(FQ({+GVD8s`&cPg za)N6tE`?>LgN#}PnFF$bSy-4WQkfH77RW)}Wgn#xgOi#U7oC<{(a)CnF5j{pZeZrl z>~dAP(GM3dkU>rKoM?4i9y>gmz;-4%CanjQ4XU&zRo7zl0=4$Jh?$*9I$hY6eiuJUcXM+NCj!DY}jSVF4AJY-z~R7hc;6cC4i zFjyw8SycdJ+BIl(B~-A|!|55nGpLSdBC`N&8WS|cyZmuPx?-Q00M5cYaa2*GH+)v3 zKMx6da7TA{TtLDOr|A-_*QH3O12eC%CT6dzrOyI2zq~7>d|W1zg#)$V*YxxddU$=S zB{c!s>gXs_efA+4M>7wj=pP|2kRO}#hz^F8(CBH}Xj3$Cjgo9S2x&Jjr7Fx48D{OH zEQ*p4lq{HJ8OS9L=W&k2mD@|7YiFpUegwQi=fy}(YV3MR4Z4C%UM)gw2#}IfX$>@V zLdIE|nJixZj44DNY1sUkWda}lF3HgQZR-wW$x=i|L=(h3-EB?9eH>ZgGn{sN)< z2Iqi2;9})0f!k!U6z($r{lHNqvVNzT2j3Lp{qx9hp|F<9E1}L&iwhBPhPH`vB;FKG zg0m=gix=1m2a(jytdxm1C3qkYHCathcGiHpbt;ABpFVa$48buqB?96Bq9RXpKT`s% z(O3c6WOp?qA_Z`&ShIy6$P5)Z3pS2 zd@!hvnZwd}9YoMXRvOM&@gWPXmBV9NdEolbe!`$QJ?&b%3H{eDV`fog`lcoWVZ}fr zvm`kqaWIOwD8Ik73K~V{$NWZ{Y!2aZUX(UiLktZ7=~y^DN6ooSZ)@}!QVA=J*>B}A z12KDKBveU8ljhg6nzQzIO!Lk8oZD}HjgtK68p7{N`lQZcMdtgLztom(0T9sJ@Ri)T z4%a95&(RQ>6iox6NksFN6ZOmS`aV_)p+HN;R(h{Bm1s|BWHlIPpF7|V(;d9467B*m z(sa2!G<_sVdYxmbui#7>DU}7^l*<`ji?!rHFtzYb8%Natdie)XJ5#Cy?i&lNr}W6-&RW ziExR<+(yWjATC}&&v8Kf}v&=VU6gfZXy3=iDix-+5Jke`My)fy2Q88lg`fCb6W`$%D?lm zTZMSCBxCGuAt3T9?nLbb`BasT?t#ks1$uL+|O!c(U4lSgNK^7;l&e^ zrOpm~9ew7P9%X#XWic#afDN0&7>nzr)dFaqTs_Zpd<8RTO^_mBe)*56W%&YdshA$^ zNW`L!!P&Ezj_~udYj#n370hy!nrmU|Is9Kl6$m1ByS{|cM>{sqD_~Ccfl!YC+|uS= zT{3OfZ(#>Jk?2ORgPHX`2RW}_LY!LCv$j#(YBD>v=B1r|FYWPg ziH`3II!k1J8qd0w(2-`!<0P6H-mrT3sM$B2od!E12D+Z z0EPx^qmQN^cTZiNVydKFw~iREs-2J)kHHexUc^4a=#8KiY}J$&~%CnsIqaLX3}4N9|FX2Iysk43EhVA z=KL9b3Tv<}ML@wzVT4ikaDsMZ5CIrD7e*$N)hRPL`h~5N+ycKq8&a=rS*}WS#Pn3$ zr8D7D5UUp3qYuKlW93RO`dP0Vl}ZP|o@f*BoQ6{={ayrXO;-FfMlg-$d9#w5WOJgb ze=63&025<>CPB`1LDlGqvvbi*@c}W=eAnLB0AcCteS%G8N1J7`BFIDDhZm>jPtwo$ zb?wC8Cuhw&Lj=7Do3Qn-c^g9gzby4qVzw<&#$&NH_I;m~b=Tay$`kJYZY}VwUS<_& zU;WvCijrT?KS3O6BsR9#HHY1bt%ulCbj@wV-tQHlD8ibs)}f86ESjH~vStg*Pnmhf zo+lk=g;b)qPZ;%2**?*?OlTht)hZ6ZMo%JlJgek8a0#p|*Fv}$W?G_D6pVMBafamD z=bX8U7ReK`pAGfvM!q70dQy)ZhJ>dZCX@_=(tspl_uJA5ZyupVWlf0n0lu{IZz@Ab zY-b#c^vV>gc|Uv8K1vbJRrt;H$hT8}Uw~#W0Eu{QI}XOxK^=b>-0YZCmujtcm<3co z^lBpzYx~VSjfsq`8|wVb7@jO|B*LDt)d`+}3Ipfg`TM*`)`}WVi5j@XzK{o&Sy&ue zW|dhrQ;9V=coS5=0UvaQIu4C9TcwUIQecfA0avyIL=b|m_`ORPR4@iuZ{xTNq+zoo zrXp1S8FtA*vA|KegG}AtWNam+`TA!6!%=583d~vHJbJ~HM&!LzTcPZ zs7H!11~@@5NK6W0ls)h=PxTuQ9qutoAf@jD38CL&4*3|SRxAu{1Uo@U$7o$%(s*)m z{wCAL$hkIeG7JX0v6Am0a{v)@VkH&4&uHMfI4XbhCFWj{ToYBdIr+|4DaV|?7?IMz zR1P(>o+c30MGa$a25~v+NA3rKoAk<%u5gLN|Gx8olnj#odR&*HIM8e8z*xfOGGBxU z`0xbHX8$#sHvxpuvA863T@?EL^VUqU(tAx^g5A&Qoh6R0G%RE@;IeT!nUI%E1$(k6LT34c$EZFX0J%sWpQRmDwEoL81 z@9V?3!ae1b#qW$_Xyt1!>{F?zygEp(8XYz`o-2%DrAs^@jbbezPHlJaVhR;il?bE} z<+rkHQGUzxCXSIjv9cwbXY+m?YBCIPuBD@$O}!8Ofdnm~-c6{J03+nZ(#5QrKfHMD zQ!lt_uc(7i9+E1r8BSQw&(<|FynyruK?G{@4Z`y_AzIywQp7QuJTtEx1w3jd8qbQl zvVs?j6CMHo4##)~)jri0SQ+?Fx+;!B8B`*JpwkyN&3ZM?4u@u(K~|I@pF!!%Y+cENAk2(sPe!;n7|nkfvVfLS>$wAOGjU}y-McNwSLYUVU9-`kq$Pll6i(hn5vd={GQB+D+^FL`owyk_Ra7RfXC{)veu?XftATt z%t*MwOuCHraf@vz+5EYLOCa>lF7lW-Xg{qscstS~I#7q;infO;F)6fIiqKWOq;Sd5_Dr!m&6!*OC} ztnM1-@gW#mH=o*IlmG?nr(mbzC_F{j29Qt&s$>+quGFzE>>Lh`Q8CS4kin)xIiJ|W zxbAFK>etbj1Zn#Mm2>t|q0u&Q3IG2-`G4arBCIhqofLvR%g{v0Y&m4yq@5jj z6ALH?3%;cZC?a8kQVcp!RKQM?K!3RlWQ{t&wK!yZ=y?~O9EbdoU#rGv{r?d(-b&vD zbx*@U9@a_+ib!Cq$i;27@l)S)u5;FVj$@9#vavS8nQHDXs4l*NY8t<}-VMm4Y+6yd zLPPhk8yP<-_;5RXqhK;@BdpB=);T3}@dwKNJ7Rk^T>t5Bh!x9K)9yknfI4P?g-oyC zPB6XsKtI^gOp$|kM=V4<=5pyU(k5=|epM~Tb*%cuvZ9G}fZzMy+X5f;LPK|vaC4eR zjV?2lT%gwhOrftKH}|UDhE=oG6sfT}T+$3jQG~v{?`dZI&l_m&dk5s6c*D%1NPS2% zH`Fc!%|<65yKX!=pTx$o(LBUmdQ((7J$)$I*s;--?IX}hHzcx=YmG`;^cFP%MV)2? z3q>V@q^{Q7l{t-4x0;SByOCH#lbF_j)1EN-D@e z#<)~KC4oTHrGwLasaq|B+Hli(Av!m zsK!c!3No&63BwXUB1A{jSS8)x{e2t%a!vLI#I&DEeqDR+C!KP6tUEy)npcxHULoh)`!{noP6-PVcAvc(LGEDT9*F3?_`^T z70i|7L;EF~=7qR=J+{8Jv!05KWQTFdbIw1P!#jxV%<=5drSn{&d!^?pBd}s^%}^=f zgE%s@!u73;YOiT7JY{uoC4u|uz)X%*pEmZy`gNpwtUs9N3>qM?=YEd1Jh( zJ2*2tR~gL|1VZL~<^SM#_`3YU1gE_^N8Yd|vhs-kxHSGfIF@+tcB;taZ;;Ls)H1-l zJ?#6jA&>fA-}nBw$Imi`4PpOLmqyF4K||TD6cFSgP;F}Wb_TR`Ur2>eFW}i!@(-U! zL5x0Y0epi~zKOz1WR)qvr~z94j>_mysjcx$tMW!LEyUAn%H~6NnOMo*Pi`bUr_m}> zSB}piIgTDIk3-zS^!a2&$n&P@B)=IxAve%I$Kj{1<{}9vtJ41l?F6T78>lYYK>`dY zAGVvnx9eIK>1fAnUz2WWb84pl{apm~5k{xM^H?*U%p8PM1MI%+Fh@b4+I%OI)ihkG zJ-7eK})5-A#qBCl4Mj8wK(bItM{3QFh zXyAw{Z=Xv90g14j`XTm+?gDENWAMxG%aW1~$0hOn687@DHoU3jlEn$`J|z}i9GA0$ zU1(hDeyIk`CEXTsf<3vCltu2n4L~(0?J+Y+FM?L30y+B+%emwo9!!JOyrcfbzxC$O zRR1_4zz-2R6mv`3z1$_ilWD&tKx0L7?PMtz^cMr;xLt^03cYAFlT2IB9Hd@Cv8d@B z^5K)SU5&|Q94(hN@0>W;(>?mpug;=>UHO&|RECz#aen8c6nmC)vcaoPoPv4Lj!i_< z>LwpL&UHln$rUvjTPRd2S-g;kj?p-hh)*^2ieq^p+uTE^I}3ppX&xtXZRa7v6Q4Nt zrCze>INdpvzF&H4MVa@>E!QEd@NCc*>|EW0YH7XZJVL0kX1moEv@Ei_6R;AGqd|Nm zdO%Y|`*)FDt)xfrioFZH>sZI|-2~{}Ax1$MS|nR*v+iEAVq5R0LIVRn8;xmj0*DXk zsJ^b3Tp8$6{j=b9Gfi@8n z`I+C3Us#u5Y{HpN`Q#j>es3gPjmzz$^yC`DLYfeJ_B5ncLG9U^vrBTm3I#zp%s9TL%6<3=ceATMOqGIr9H!i72vX1 z29`kxETuXEm`jG6nE8Mo;L5(z= zJT}*xPraf(=PdgnUwALdPa=yj;+OP6Ny!i{R=2X@*V3Egz|c@3QfxOB``Mb27d15S z4FYYwDio>9OH;?Ty;ltqM%wwED5i0>YiZ}9;);0+eR0&zH1^c1WqSKdwr_-*$t13R zd4GF_0h%Ou_Fqt=nlCPlJizqhQs|K;A21gTm`14dR$QX$mQ|=a;zjtB?uEWg&zTCH zw0x|Fysb)jGQkp*7%duRFW24LeVnkVQo-sCdSmvMKpb12xM$ovJ-b=##T7^A0Ua2y ze1r9EQrKZfkj*6>1q3l5qTHVixGm^?WJ=t8^iF7Vs~eW@ER~kd!d*hvI$4pN=psrF z&N?<84FcNp6rP3;Uj8 z^mq|=EyvVbZ!}R9r=9M-hY-qq-)|5(g}Z4Js>=`t%|Hvp?WORLI5aZ#alK%{Ca6fh z$~l+rzq*d`g9!+-e&6P%v*=*bBlV1^4?Mn>4T zpAdJ?yh)LJ>pVbPMS%&UwGUs$2qKG0?{wm5xua5iDvjPqiF-Qc-#+`oU^$aWWXV2! zb`UZah2=3yo+#lc-Mht5uI(JD{dN3{#PQgu|74BX)B?V@WTte^=k%tT(E#tTSBlz6 zKROCwRHT4^LVe7H1TuKbg^FB_IxL!Q`#}sfelArS5_YqBC&BA7=cp%a@#TG}VN^Tl zsjesY(Du>5&#qpBg5K2jNcM74;F=OZ3xqi)9y}7XTUGC7-&~kV{V+TM+&@c4ioXTT z%=F+}JU^Q3?3JOjqULL_wIC!jvu{gdN`|O`ioIL;!AF9dJT@HAwd_LC!BG-Zm<%&P zMmYnHQj=0W8hPhm|0Pe?5ZxiejJfMZqQphnkqHR@O9A8rHj3_lwyg)0#W8GQp9Hpg zEF%SsAweo#4psZj|H>y?VnXPJJvvtjbIeh%UeaUnm0*+)t<;7&UHDM7vGh7cGgHPd zAY>I(rbg0co^wXoxuS-p--}L8f@TffO7i%o`=y`z%+<8EQ&zW)trQ+ANHZ!#A~m7* z9`$OrI$aDiX&-XmA>6@M*YdI#X-Y9t^ZHXUufeQA=y*atI+(1{5!Ad|$o3f6X2eq| z7r3~KM9k~V3?m$^8t8QKFT7zaj%1kfoxik4ZHQ4Fgax2Jb$pT)){I^)B?vraCLR>< zz%}vNa~TlK;U?Qtx*#m^DU_B3Ynn@!$1zS9xuSG@&F8GeB|F~C2>ooEr$y+EpE-$Q z{*ylh*CqGvD8j(mBP5*!^4YgSvg52h{^N? zBmdG>vemit&W|?jN!(gQOwb#3`Dva6P~v9R(Mx#dPgU4q!!>1rsd4CX@-8`z+h+gF zniR>Xkly6S^YjYkX4AlIrx(07rWRDVb8FhE75m?#M7X}oNPLTnaN`*$j(M8j0Gh+C z(a0)eAmk1uwRYAJZsxM#nb*-5$#~tS4rcT&N_%d34bonOXytTGpBmxvL{fM$-l}yA zru5W`gux|@WR|awrAWC-tQe06!<2Tj$CtXuL4!D-#k7v~WopeoXMKd3dzC?Kw(=j| zv-mV^j9QkaDz@+=jev?2uC=9iW0o+`=nC1f3=J3SO&njr42hUf5UXxPwf)oNWID}8 z_nj0IEz~vjR={$vK#e=18v~YbWfm!a|KNYH5rGwhPMefBK= zYl=(Z8#O^2R(}#%wJL=KN8VCKp(^8*f;{jIR}H|aiucgsq2zsZ}He8{!ldxilo}hr;y=v#IHGh4fYQmO56H?vI zCqa+ciH%goQYVh6?yeYUe>7NV-+=en~uHvXvL)oni9Of$A%aA3f#H1S|Pcu^{ zL#Eh!kAW50K~ZVF#o@m)fE!wd7FO7AP9~#nnBp}HD=#YfBAvz%Nu;t=S|tYSf%;Vs z?ofpiU>Ma-#eIr(*=iK2o!{iGgDq7p%F2wo1-xFjW~oT zc;uLPI%iRrY9ARMQ#wKO#Muzt+yYi&S7%GWZwUN|08Gn-G=HR?f3mPSZG%J4rcOsR zABtFg`|9D}}Pon`gD6T3m2-z$EX!fF70v)`?YyC=Fgi9Tve3hqp z=ZQl^ZhVh@;M%*>VPGa#^M$kruV76akd8J_k!Hvx1nf2DGA%NO+ywEvNSK$G5E!B_ zF+Q@vjp~#D$59!E8WZs#6S+&L%;;2=uU_yEFqoO<$%9xgnx9%zkqK$satw5+`19-+ z%CFFZGivRx2X6^J&ULfZ-_=4r;^sLmkq93Zz2#mT0$JJ%{c_&j*X4Y8i7Rl;LruNoT$x9d@4 zkfQ#t0p+x((c)f|1}VVd3Ub}t?l>i`maY55rbOgtwo<&t0+OG#K9 zxP0;>;ps5}M8mw2H$^-OSh)zXUdOHfS2NLapy0Y|e@FYDrV63S^QoB~v_cnQ+JXv* z;7g>d;s=b~{UG5K3gRkfmZrV1R?#`H(X_JnJ@NH3;9=%ayODxG=&*Vp;aG^BcKOov zQ|}S+x3D``o(JlK-Ox7vlw)RPE@zRUo?C{leM|7@cdN9B@+8cjj5egUOPP+ZGCM9! ztD@Hc%8Wy5w!H>}S|UMt)a}nU>nJXs#KP|r<^=m+C4znpjUEKG1?RoKW7h;(te$CX z;G(@#m$pE=R+j~#{D><>J<@vW0qrRp1Xa5Pd2+KBIpR4uXV>qi^u$Wa1y_UF5)L=nhxxz8lGtGyR@01$t={IpwF%?D7cnr z!Xfq74{1D9MnCOLz_mJnPZGvw%awm~L_6u(Uds%?+Ntmpyt7)UQ8>P|Gwed@(n3h? zn5?v?zZmQxw3e#6Tf)?SyfL1hNld+fNgWF`gB?~v{M0yn>k)80LGwd9GeOr(0(~3r z3u)I1<}n3aTKA0b#(qO&YGQ+iCCO8F`|r~2v^FZf;S^r-5~s%cZ)PBvPgqh>`t{{} zn6$JREZ>(nEmETK#vsiFNVt}cwnHP0%s=rHr&s2r$geq3hIE{C|1~rEp>5fSkZ3^0 z#68J-+ZSwG2TlsMaEM{uB7Q0+ropC4#VJz7Tah=EABvKK5<%`du)~~gnoy|x%3Z+U zXzJe#A#C*7UM@#=m@7quB`(9DVBq*R0LDto4=-peMzjZyqFCNF8MN*kg5~c}E1F_l zdu1cII2CHpdXeC^Al~1f4Ja4{1JCDsw;keMLV!Le0K2KrqCg85b<7*ED(^7m@0JpWY3y2x%4uutC z4&e({gB7^#w-ha_o~t^73PkuLRweiLK4{Lix3-8=%VZeXd#xm)1iQJM?CR9Q>GliA3Y%wE{J)rao8lE(n-cSQ7S9 z9O0I!F__8^4sNS4u8)RmCy9xj5*WyTVxcJfD&1K6^2}1}O=aBnV zls-Z|K%h{dA~vk5nzAX=#Ihe+a7<9Ch7$^R2!ff(offTV6J;DVocw%5DVrs@*ZuV+ zn62-jwuSQ4MsSX6SL<_%;XMfFU*dtMmNqM__D`E?r=^}w6TZ!N>&vQ^aap#Tu;CU& z(BBIc~QK^ zKee~9Zmd~}uxXw^s*P^J^!Xq~R2B*!QXxYzuIl>j3_TD>k zwP&@B^?AS`|6v~Aj8LfjRa&agUr27NN|Y%8b&Dqy$W75&H7}2(#?jh+hU1>{)MWW2 z;m|&_L8a^rj+gm$&5`}T{6PPQD+HKg_J z{gop?amp$#s)RapW$B-3@(^UX5V;e>2vs5c2mBb2@?FigdzUE25E4mbu$FFN8B(@_Zq(OF?uwg{nMOTYBWe1{;^O$E2 z39pekw9o#P#gvZl+&zm#ov?On$aS2Z;kf+Ztkkg!jS)!lC=hCK`rr@yp+)3S3m#YH zBraV3cwWe{V(>TeJ%vpOQ0~w4n}E`TPz2tO@4%FjROJyQEf!{$rp4oWQ$;84NW2mpS8){gOK}O?J0JqR_01G@ynuFCP*s23Ds9bZ0JeHp z{{YYF;0Y9iE241a8GG^USY-xK1V1+`KbO=RyQ@YY9oHdxI!5oBxjr;!nP&^Fx4`UL zW$-#koHpWY>uC?Dxr#bNaLxQ1*MnZ))D^lG`ifPa33$jk3OVmz!yf!cCqTbxs7z5uP?kXx7hOGE zGDeOvnRtX+)PH16o95BTn8M$m^qXh!(D&JNOK?gg#j1O?sjPwXwzE1Ne9IN?9N0By1HuO)=C4qvHOy~+lSHOLJ0yW&HJ(A`i zS-J>LA8*=sugH2`f2!Y)+y!rxC{d&&O2#4N=U7aPyxJ?VZ9D`K$A{M0Y}?;bi^Q7Y39)5 z6>9}9NtzguKb@dM6q1z=Q(}zIN~>sk0H{s?RX#&Bsd$Vv1nc+bZ9 zWvdybhC~)XWD#HlsJ|CP5!T*PbH}-#*m}Nk5Zx{*f_+6menwG|pHleydNKY4eKXx_ zkY5rbZl$D!lQRPj}F9(%mfl%NY`06jp$zqilSWB5i>*Ecn{IyzgHDUUGFQ%N*q z7mftcB~L4=M4&6F8+3V0u+ehD=s^M=gLX*;4!(A+xE?EUz5i$B@yrNppPmFL6INq$t@AE>#981OpGPCJ3A8HI%k^~{qc z+qO2np}rmJH^p>ih<|AJ@vtHbCjReokfMOP2J1|S<|Nme76J6be-4YAJ zXWJ(VCx%hq%N4rFZk{v<*_{%TZ_w^hjvm`?*Ajoi(UkSFHj;{CGy+52&}7BUsMZO| zg0i@&uU+SIN|RtHr@JkD5PiS-xbMzpUkl`&F}@=-qXY$ozI_uKvF#XdgNx@N8{8J` zb_6|`F>VIlr+fUk-y*L=yElKv8msT}n)V64Fogiq(nc)TEhcG=I-;h7L?RjY09ZaF z+S?P6sGItogH57|AC^shGGHT~+LIseXA_JESh+$U+nMKV>>6$A zl4t-+bAoqqOG*-GjoDcGR*YISu}J;6WcI}B_*)=m->I?dL9lrCaOzM)C_CN_p_c(} zhsLSG)v97`K@?k{%W`33Ngr1c4oB}UL%=!{RO-p*eKVBl3n-LL{h;Ug5G#X{a%Zzb zG9>G=%ab()lnDmCtDjsis#qCwE@`A9B|e*?AdSiu-uTerw+n7C7*RQGS(*Gq*GEb0 z#smBE&43~4^uB+x(an}}T9nv3FGXHK`YywD-}PIM2ov~~wb8zCraodJRfFQ<1iAFd z8sPChTXp6UC(^rng9OoFeM^Mf@apSzc=$D4tjSi-b{NsA z%~k$_vIT}k6?jxcXA2R+nIMoW0kJ((W{IBCR!}(wM(D1T*r;e7%`_6H1tx{y<86@r zq^<=q2Er^1U_}$b8b*ilabo%KTs5Bwsri^@>^c)5^g7|lxe>wu??hrQrxFaA2==vc z)`q2`IrS%ioqsXO*rr&UzW99y|2V)ARpMXUEkR3DSt7MYH&5L>w#6{^Mn^wdY%Cvl zeDbf~z*66+unR~bi|Y+0jFB;uv4x<2ZBbd25cMzn^$QZJlK6uVz~#p$uAz`8=}v&Q zLKHkf4|C0YKlDO`=pVPx92yyBf)qm`_$)|$?Ju8_Svfg-6_)-Wso1U2SoR~Dv-Kg) zJ{jIhqdA1;Gp>U|W!wGd!6N0>bG4$ZWG!oQxONrt^Jk5t+U$(`VohtTZVhDfuc_1LNn99`&=_$lubO$n4#+dhQL78ZGCbN)tJ z(_jlnNX+lnmRQEd^PUfcF?eca0I&c_nbAmt~??LNS;7Y zei5-lVb=Uzc%W{1uMJCCWhl~9x5oyw0FyAo%jjgUGq-rzzHwAU4%mW>8V_kF+kkdF zl2=p7DSvfoX{2O5pF?q`+P8u+oJ|lWbHiQz1MUMHIwfL^bcb~)5A{DCQ9wY9OP-`j zsnGN|t75;GxACX6EOOQXF7+obh7J_9u?%eScv%`ZTmEL6*-AVr0~$leIqIHQN+9TI ztT_&}5}p<&HafhChakk*0YQd&=9XwQ=gDRrl&WXgpx2~)fa4fPAnPj;8N}6(aJJP- z0tyu?Xx1mVj9?Ex6$=FH+^4?*2@klARePMZHVi7pp#7+E5Tc0=#YDl0kTshRCxPRr zw3osf93+1au!Z}#_g3BdkDwJg%hC5)8<*8Y`nCCH?}NQiip{sufJ-VQW7OpSq9i3c z=%#APx}n%w|MHzVfvh70Zr!~&DXm6NSE!zm_d54}SBUPDjipOP`#=?|x>oc2!N;gy z2RQTQIsW3=mdGHujm3nS8DrSy$FQ^csTGH;GhNeG>eT^bKX6QcRU47Sdu ziYK;dXL60DGjvUZ$oXH%Au++NMj+}^wpB)r(G0dUe&Y9PtH+p{)I!N+-s7FsA#5kqDPUU8L0CJ@Il!!~w&7>0lak zzMDq#S7df$Zq$+~1UdWPX`MBrO2Xr(KxX0hHaxU=BSbuJ)o-7F$5iAsh`J&mNouPgCb=aSM(4;2?y1E+1ip$b}4*J%-P>%dnWZOOuFB=Xr~0YVt1>{)-}4Dk zHMUShDpz)rJl}BSh~6oV)eIj%vjhk79pZsViu!Wc&E zRG)qdg67zR<@pqt-^om8hf3eo!G`l*DD|#b>T=?^_@?@h2$Qc%qQod` z38OKIFCFZ?OgNl-hzk-Y0~0;PO29a@%LNBR@r>rG|~jL zu!hg9z{MNjM?A%XY+8J)C=8KO2hO6$2HnI;UYgVts!8GCu0)3bO zX3L|VQ=LLV#H`=4Kb6F?eejsLZMt8=fS7KxuCt4l@SIGg51oM9Gii;k8H72dTR5XQ zEHtcrEHA{Ckx3zgiY)>~RALlSy;4yQW*(OxN;7$qJ5;MhgGxQ)QDQB1vGGM|3q!3^ z;4%a~G-a~t8$=y%`Klo69MmMF(|s8++n=cJKAke~c+S!=E^;+m;1;So9dV1{OS>!}5Nusih9l~;+-Flk}LX%txM4>R@x3$04ZtAQr# z{=#dw1mjbuQNHUg3L$QuXg8O%gj+n<{LK}yBg+Yi6u|nOr;eBGDA9h~k-pq$^6^ke zKB~sngewbo(WmU4@giW5Rk-M4kfDSrhEw0X!SCIwv`-@PvCm!&*pnodMB;W%-23(3@y5-(rx5%Q`V* ztoI$YhkDSX#p6+{`>xoR$(Uh!BZpqFwi{GpWRb04(_cQ395^<_;p7Rc6SuZac`ne= zb9I=bi8b6*SQnOhHF)bQ$6QBriklith!q3W&IZN^APODq#l0fy zAdy#3rW`l-hPp}8J11c5U}@>dQMRjgNMtwa&(=6wsddA!2A*n+zMt|fisoeSLKxK= zOnmEwfgGf;*Jcq^jhC^y3P&Y@u{jJDSBqZQLb_y7sQRx`HdD#F1r9R9+IobF3`e9( z_6fR&g!H8`5ub12RfC$%x480s?ElL$2co@>I#l9~SFTNKL%%2+0jA)vH-ME|zG3t; zx$#mKkU8FpS2}vFwsd=F`zR{i{9u9*-M&&xiOI=8zO!R1`j(;=8CgGoVS45{mK?4q z)$fxIgq5U14WHCXN({Z~ce3B*RcobUH9E3e%LHR8VRDL=+d zO)!#(RaXTp$$6_J=07v@f@a5#BXir`*2D{;VW+*xfQT|-V-MBYQepoMU7f!IzowhP z{XnZrIvKYgE!iC5s*W(bMplMLQle;(&W=Njk)P60--xD^jVuu{?Wjz)egB2OeVSt> zzwwYzh@Fi2G9g6em!^Nqe`|TuK;gxx3!;A{(5+O;S!H7_EZ>QewkV65{Cp{(LQl1F zG1ek6Njrp0b%tvCWVu5?A8a+t53j`dQ*F}%RjlHm5H(enMp)%FCtviLEC$vo4>|O5 z#IecaF%;vNn!h8t$Ls z2~br)M~$lqXhWruPA!`!>R7OMPP30U)yDq1IywDKgh4V?s9SNr9xNECSmr7%4@l*> z{wCmJ%B>OvM%#rfK8zhtysA%OjkUW3KXJYpeQ=Y8_)KVWcE8tOqO2y1dfX1;j6FG7SZj({~5ALj=3A z$`y(nc-+sdrBH{oc&z`9n(Zs#0}Rvmx$J2sIbD=nq-J2(71qB{_j&`8VqV0wQ{t3K z7|wG%uSechw_hj)`cQ|sG$vWY6G1X|D#R%ToCccLM)?7M$bqV+r;*JUxBdC`V8>3? zW~o&ALqO|XBRg0k;8GIy&nZxtwqH@2an=Q~TDsS+`zX$ISllM)zZ|)h>`NQUpC1iB zh=5^{vBLr!y+=*%Y2yP927-JtKzcqMsoV zmJXH(=6N*B;%iG5rJLJ!N5b=Zl_Ap|0k=|P;UKYiLcK)rOJ@lS8A>98uTX1LH`!TI z>d6>t%^7tRuc}#^^8No7V{ApWo{%~Dd{-gI#s35coU5r|6IG*mH-o+5I_mb4w?BSF z2F2i9LRVRFBvYV_R+ExXLBiW^@OjGKpn0xD{~0c}aEMJYWe!!3*^6?E#Nw8dH@X!z zQMBuaK`EaPfbQn^K;(U?q%qJ@X^PZ zC+G>a1F%#Rv5ohQ0oTzIomDu5b zsNF#i^uT62%kJ@i8vs9y7x8*928MG%6!dS6%ick-Qdz`#_vhzJ_=e?0sC2ieOu#MJ zh@XDwf8lNXZhy)mYe#@LHKLj8fCf`>nOI)$1I`ifj(et|5BAv^P$%kaj3f$f6Q+D} z-&jQ&=JMkmj;Ujr|{#!Otr#nK)q8sy~| z!wBNLO}Vni#UI~n$oelN&iB3%g8G97fguyPY$oZ6OlDDp4`37|sGTHznR7m`zu1kU z(eq$VPYF|ehD*I}vsfiLHAW`L`~fO8m@|w81us>R)&LViis#ATedgaVTkX;UEx;LK zc8n22BN0(gG!J974?ByV!)so|M_y3nS zY_Aw3q6x7fmVeMc-+;WeKZUs|;(KSU3|38!E}FYLe(v)ig09LetXcxS7<^71{H(GO zi{V#rO08wkrQRKID$pzL1X0(_v<)gikUH_m$|(m@w!|6^{6uWK1r$a4fVjsfplz_I z$iY<)P{6TcC#NJYQ8u0lgi}ojv;(>?G|yR*%WP<62cT2yLkgYL(Q+CJp8glP9%W(zxbIGY~E4JJ!0b} zQ!Gvm{)QcUfLQGyi~JS9b)U zvV`e5*)BQ;!9C7hz+Tz#Fe1(G+0+|N!_*xibisdAZ@69sm&Nj$KGSef7WBY#p&dWh zxO6m_LSz)^6QPsWI2omU7|W*+0VKUNebKy#ulLVpK`;<=F0@;x4~`1bxM`|y%AO#C z%=DD#>%kDW9GBGg1AWKzKt#S3A=Dh)21t{@$f3$H&Re_}{C2^jU%H2pbR=m_WT1zD2saxR#!|D6TUri2_bx62%L@F zB|J5>SbaQZucGr1g!*wf{vb6R%3o<{)IyHtJ=t%_pkC&xC1U9UFb(DWbGRFc~H;3uCxE!4Q8EphFJ)3IqBE^qXkBmWz-VbUb>*t_)yA9~nQAz6PL8C1(Ws zoHM?8`9O&bxfM8bX|faH?SQ0*!Z32>A(yRP+#Up=uCC}l=v_*F!1TKVHqJr*mAy3< zc1kNF>#`{N$^nx}Bv>%jDZ@HQ*b-I}!JD$YWHK@lRgnC+g&rqPHJ)Zk0e z=;P5?t_b)`^~N<_=NjdR$x}vV67J%GW77qxH*jCqGvQ6>6YlhJ1I|HI8n{U83IYY} z|5ZzA`2>0m?Cm$}CkFNerz3|KE=R)#7#M9_fxkG!Zty9r@VJ6%h8!2b7=Dl01DksB z%39*Of|pYezb~j0esto0A&71Cs$PeXm9ypX|d@M0gs#SVL)l^;*G&0!|ZW&zV6t- zI4m)PE1WCBvt~mjK7w3{9_qYkFz=5bSmg zzWx{)+du}*sYOe6r5t(C7{}`>Q&iK}){}d!O&8NS;6)G|e}Ce?9M?F=V4p1FmH|Y; zPED1^;d4b^d5EQ;+J8-WSE2;ej@K7(DpEAws(8Czq!5QGaQdk{)=D-!NghXDb6 z2qu>Az+*h=a&`l!Z3WpSG@9UBH3`pYB6!H9`}h0-g(LO9rcs&AGcfOlpq6+|3z>iW z-)l6av)3L|^$^UIGo-R=TsB9t5=QA(Iko%4eHpY668Ata-B^TKBb7`U*VyjSONqFjTsKV#grJJ zh#}VO*+CaGP?<#~Tns^4xRVCto+qe|)vY8ZRop2$164kCq{5?!eZoJ5$gh{^^a~NG zW}~fY=cS{JNV$fB1G($Cig&3K2YXo_s=?Mr#X;Rq&orffHuQ=^)<$&Rrg5cBLnud( zhfbsdPhOEsA#|m5ztWPG=^IWxBEEICcs!Y4M=TN5MwV0M5u$hR<`AB3S=n^@k%o>T zWG->dy^SNSf?Q9V#vIO%L2vZ2wm!@zQ|p*K^hsf3M0yo>e0ZtIc!;p~KC>LTI25Av*%$!ZsuS+U-u+Trllw<{}gNsWtuKP<~r% zip2!U(&ZyB;_Oir8Vr*yxVYNVxzp#x5#S8-vdkd@QBorX+P25k$gNo1sPS}!e*)fO zq%v41eZV&<)(zdif5 zhZH~lICJG=tllQOfhSzj z!=EXd`59(`fkRPovx#7JEDx;O0ZpP6lrTpJ<)BID9zANg#W3KE%Z5j?!`6GcVgR`C z5N}Z?Z>>l?dJGP3*$!8p{GWe9vssyal&gU`;GhJll}CJhV|2aI=et5Fw6Gs+ISJv} zq_3go;J^ljC6bq()FV6ArT=s-)t{#Ee;-Hg=6wnTF8=}<#wR_WaC#5cSoy0VJOIVs z(xrwkio+s@1pnjMG>jEIJvJ`REn9|E4k^PG3>A-R$gss6Nqo!X56%T^x5Jk59~C$w z38hs8wB39XVfZ+;)J!eVq`W0yGF%dkO*t&F3tk>I>lmBZ@qIt?4G}IJ@z_mBM&GOA zq8|8|qQ((tekGvUfY2l@4n=RQ!3r1C#0Rlyq%Z<-*{u(=I*reRPRptOB|}ZBy5pH_ zFlFce6re4)>u^14a2&7rQgdPWrkQyhA6qLaIU1G+^HOMs` zOzh*EVnthpWf2TY0uAFpJDphw6GCmHT2Ouf_p$!*gADFdYYD+@i$?ZIEW)J9dQ^@N zT1twoh15ZA8)J_Hdu^MjUs~E^fy4bfV&x4_m`j0Mj?xkf40!B&ho3^!K4wHZCt4?1 zTazTB82!^p-+V$f``8klJmTd!MrD#D^cib&sR~o-^t7npv^O`{ygk||N<$;8SHYNo zc{z$EZdUVJJbeIqpO9`CXH|&Pvj-O)fQm$5?G2YIa*KE6srq!tA4!&~7GF3*ZrzNA zg4;dLI?+}8kd(xFhuvg?!``=SGG;dzbJ zK$3EMO2ZZ1=7JAAqnBhXfwa6@*(A}5^NFqS-7o?qrprPQHp38Hcl*CcVSQqqG&>JV zst@xozAA&jWqzI$j&c~-X(CBUR|VO-D8MJnV^|O!syOj5yN; zKC?ry;9rC_{1vX0^rkJmHDn%s;5)GhSn8eT>nwqJ_B;slYXW3 zu%Un6*#h>`QO;mdb5V5;zH=GrsIj>L92h^_16#vl)hXahDN(+*iRACqS7Q$?MS@ov zW(6!d+hVSTw`9M)NH?U-3!aM&9tQiRbpx!2@^w99n6IKp`mIc*0dSj_8iGFt(f^OK zx6zhkxp8y}z5hGoURxO?0J7vBuUXUG_OY~gRb~W#fDe;)^xv@uVW)s|U(8YJYOtQi zDH|DSSsUWNOE@stT1f8n6RCo?I0s9-ukEI))u7ySgBkQG45?X{bB^=9?6B+=9C}k88(WWCdnwYT(OcR_4_6Zl{tm50 zxF3ZjFZi~by@;ee*GuLnN12D>fGLy{n*!$cp}sFsHp1{S4DjV6!%&k;Eu{rBsvu7} zS@l4Sg2?cKU#aW=_Gp`2l^7`hx#0OE7tiJJ)1aG3}bL19#CfNXrp& z>M17e!iO_+hvSYc-%jk1`Qy{0ii+~qZ|IVn$?iQ7M6v?vVS zt87??87a+dd-%boWk!x7>3OEIi>dhtl^lD$xm{|SAW)}&Kd3G6HV#uWLF=dJhUI6& z4Lwh01TB%waynl=0H7G9}%i(JK zhGnv-L3ZS?^=2r>73ve<21*&ZNAl&eYw0US2Tz%?7sw7>@GU_|tdUmtM`0imBw zW~|)uTzOfrAWlfR|BWX)LW&fC!-Otyi7erK%DF`bX;_lhR?_10d_8)p?(+xBc7)zS z#Om`OxdY{F&SF4i{UC#nv{N9ILC5+*_E=^fBkdk~Eg(c5s6-V;C}O1O>TuVhY?^^7 z;7cXR{16^I4vFs6Ar>c*&S&mk4l-q8^8zq*Y6kg2xm)|@>^QL+UQlz*%k9n^iBHfe z=Zq@)Hqp#=bi!N7QoJui)o;B#u_A5$LB=Wu|vt*K`WETk(UMCG~422f5)|QTqvR8;)be3^_^wXpk zn$?%E>H0luf%olXy2y2VNK&f}zcSq4PTi5}2C+GoRQfPIT$OIm>fj&(a~E*nSz8Eq)B0>^MhKHpq0ikL3*b zph|I9zgG?`fStVolAt;7G{qi=YyMWli_i#Dz~Zn`=k&&Rm~h!=dHrZk=-J{33a&d! z8ELH%X?ciemm0C0^SQ9R^l-y44cUaAUr#@SG2cQ{>m5UvN;?>5tX#;N2ucJ}4GDT@ zklUM+$eAjRlL;jXIHXkIWkCy)RP+v#D8sTn>0?xz1Pvuk<1D%QTJBCgc|HlyQ7$MC;S68^1tTB7_{`9>;dAa6YMH!j)rN&1fROh5 zY)*31GMGZ0%4XlMG)?%Ta*TNh_24c28iAMds z^8d6d&!8YrdaD766of|$QlA(s#XQG_=qtEUNS0c%aq!HIT5tLEYN3!?>OB**=mK{V zApNbIHiY`)Q(T4Qc(-j=I)`3D6>48m+;jTTTS{O_;$W*5lDeI~Y3MqEPz9eD`d)4$ zAd#?ZnAC1$w+_eRi)C8$d0|3%l_WET8^I!=)PF+dL)o5J1r(ZAZ>Ra{-X9OoFou2# zkNJWzzoZX65a(Wu9a1to_Ga-$U0jqM-=dsd?nkyNA`I2i`W1hXM~X?a7>tByy=w;x zXbHy7A`#&jG7F0nQW$!&Z`{CUtNvE{EE9r4&SzT>*fK$sgXWsz^4X4=m0|ZFcWjE* zY^6xx7oUgOspP#^i@%@sKUS0#u0uX?`QOwSZWZsb z^COOGb-rWe;Qo|lqpG~?tf5U2l02aj6>7?zZ@n`E0eIkx7Z7pKaxKj;@n*)tVXqt; z#5>pcD@Zs|j$iWakj(^l;0Jb&#VmyadiXLY3cOO5VbRlW6~Q@4yjA9rT!WoKjjB}9l|M86Fm80wCdkUbGsx|u!JFI`!C2k3Ev?dDeAb`7_rOLrCH1}zodjOhw16h-0uw2hIqOZlUIs8X8pz`>^m-%V}swo zQnf3rW@hcJ)Tb3fI+Vn0-~)uXRo#rVLDIwLQ;SYLKT}-!2QErM3NmKu%Q8o{+{qeA z6+wO@p0@*STv;ZI=)u1PWXy;$v&>Y~!tihwGZsTsr|K%hFCULcoUCS_eI6k%3}Pi? z-5`=wPb04|@3G$V;?cUK3ZK9fKTLd2CrN{-BgMppS={}KGSDjuEk%?wWm%uQ3e?mO zej&%U7j*qD(k`N0SQBb!B7q1UjrHTIrRtQvqpQoso(Pd1-|4f9!l({!f}*9WQ6}HR z!!}xTO0N2MVL7hF=Q74b)(FX&bs~eN8CJ$d!xy2MQfA4l;+k4#l*hAA3_cL;Nh&jk z<*w)3y8^vL^cb;nJ?*+G169IuPl5+G^TnAG;sT*}cqmP`bV@s};iLg^6pw-O zKj$Q0L=)tU9Yx6-28aW;-VifM)%OfI=W_ZSob z!SkpL^o|3{_At`$r#jDOM;&BP(}}-~0}TjAy=VE$Z=b=dv~Gq_+M?yX4uB7~l>kFg z=%?2U6Vy>+NBr^^^xZ@KN{NYTQ0Y>f8-cY?k~kC)miSRnU5ey*#a-*nB2#PFBScIh zCxU3+T#o?-GpfYSiiHa+8vIPI*k_?mMXV+C0G7>!(!jN-USLiD zrV&?4UXDa;B%^t=bUCK`E9^$`B`=r@gPNVdL%H^nP~B4VC*IAWi?X{B;K(JnG>f0l zRVXYv1lxT&;jy6Lr8Hra$~|48=O`BRvV}GTi-)ay1MAR8ZAi>qLMZYAWtGp7b`vvh z1qlGh=0w{_GmbsiKBM~JN*P8kk|#UK_jEmXGt5f9B*I_>B@__|z;&4dAsUkDww8I-i# zQT7dC7zVg>QxR+?F*S7jtWRJ~NQ)$Ki;&Yx39<~=ozvsYA@-7~R35`42RMA0N+bMhb1J@ zLu7-x*~DT0RMO06=f|jFeg54ZCL8QU6&68IG9^?{I%`X#DitG=T;+L4Y7OE#Slx&dpjUb+ojw*wDw)n5t_Lobar>avV(n=*o%Nu2j{htm)Wo+>6>%|O@? zcKpkKN5&&`z1SL`RJ8C7)#yoTRtK7{Ydm@}FpRzBczI+C?v6lxrbQi0s{TNeUpyxn z!MCr?d(PY%{#be%OMSLnxf*3?b^z1B5=r z@<5 zP@DCZ8q>T8s6D-8CLXs#3R;zNge0`zx9v;1hsk-rms+;tLkfT@&9+sZIzkP4=co9> z8|WQDntQz@PbQsYSswKHwoYJZY*jb@U18$}!xU(Q8iCu5F3ri`WTfZL&;pVc>^T4D z@~saRFivutgSsHIUp7<@Q!;c4JwEWs3^2*J4al-6TS#c}z*smNU&shb!P=u8rkXZ- zyFgI*qLum?%@0!s?7*|z;}1gujb;8XW(DffLT!<_(IUeF63yqswZ1RxyRrNx?s5Mm zc$@fY+VIB9m80>6=F|RVl}t3DX%uu><@^R=%*=oO#vL!rSrni=mAsWSqy9)NE_Q@1 zGR+hKblc{u<;m{=fZO2`ebgJ@Z~hKV$CNp*DZ`q7OPw*sgkbHKjc&BQFs+^*qy!q# z$FJ+&R|#VMt6&UeXt$2QDp82D=|SR%k)8c&q1-lD!x(LN7)Mh$qZXU0dh(*LZlFqC zNI}hkjKBXi)y1rMlOD0!Le@j}Nh3w|phFdUM+2)8lT~3pbt7T&KV|js%`X!~c5L>6 zv}}GauZS+{hicwL?|;muBZvacxR5|AbOcAY9-*~zA&dxPnW5)$v5d{tApS05kxqMR zu;9wO74?hhVeD{}Cv2WV9$4;WKcYa4@Zw4Sq z4Jci~9_rsi+%byml5F3RY(s?ixK!XlZ-OfLw|Ts>+oJiWIRk-L)HvBfWu3GL6dC=_ zFHFiB@LtD*x`KlyR8u_vgWs#sd+U)+%6!>Vqs&j`885N#ffsA4pDUdtLT0Aoi55Ih z&;J7ggsn@^Qw*5>KAXe~IOjDg^!j=WOt~PfVUEUo*$KYv8Hmu!qr~HI z-bIG$$Y@FEz0Tp@TZOnuJ_3Z6bPo@+;GGAIQd5nD62oXz@lA;aM8?2VG|MNc-SVoK z$C3QNyXw&sEAV^Rif)f9hhb1ZJ23e9DiS7n-0f^PEtkN2qE{p27h=;YDb=*}B4Qp# zkK=((w*9z>M`wB3sD(3oifXe&5rvZr?M6{unE$Psd!=zUogV$vW;mZQSh5r&h>?dk zMOFZKvOjtNmh9$@VF?h;IwTTA}#GcSzK)rQzSwX5P}{9`KGY6YMF z^aC*Dvtd9Zs6d`8&<_|S*RT@Vx8QjM-_rKSx}5=_A{0 zUbmT@c<-(KdkK-uZO9_JB3QvN3bAp(r%%=o7hx#~E!mvCPy-3IL@M`e8c~8N1rmk; zLv{u8u0Y`LO^0jr7PYwZ0GgH&X)laJ*Ko`?rcRqFsI^i|#+pyeYG-1;KaUOvwLKzS ztLA$l%=CZaD*3-8u=MdPB&%4Gk~pbM+E59uTi8;vl~PHtp#Ea}VRk#lVqwiS5b&=DbQV22D*Ri~$=M0cJE3^-2Bg zJ-+CbYm@_vwJFV~452O9!o+t(zXT~5N{$`@!$T=3-JBLOr+XIIVhOEC-VV-_u27cY zi8>esGn;OLFwNWqmXZSn18-5TO42jbVbzq89mEJM+-EL+H@6x} ziB8;}K$WxoJ)0?sSJZ2>oVCYjpd*=UR{WX((dW)gV$(uEN^lD{3v4bAaohVpt%Xh_ z8+vj4^Sqb%6!K|ZCgg>w(<`lgznWvCf=2&@xu(Lv}s9e%)0}iM&G3^(RJZQrk+N%NWSlhgX@#8e zz5Hzk+`FPJH#14rli)LW87*ZqcM6p9K~mG&_NJH<(h1Q9JiyQEe#XDQNkhi>`DY3L zzxse{mcsyRD4q2)|$!Do;QxrRV# zrv(S=3Jvd_!vd*DyQU&&`PZwn#F@b-tAkCgL^Ndqyd&v(QIK+Wa{mF)!qu0*QaZQ! z0(*p?y|hmGThI&?_Bf<(lL(c1TSk&~Ku0n!&U#UH{GxIRX_jtIVuZ0GqTmXtjCKX2 zyKQxUA^cz*<1@8*Ow1kahL&ejrdfLX>F7Qz;bOi@@hz-5LMW*ix)!~g+)Zc+%`(z_ zSy4JDlK30h)q9F(S4Sts{E6N$yk|lXJBMmySn~Y40eHGz-x)C0Q!ayt;d_XWgz^s}naNQg z=Pi?)k|7g=GNoO$rfU%dDdqyhLDCegY7!jnMO1tyC(2y2y$q%E01#XwfgvxCQ0#oJ z?b4|1o~dR)Ypu*8*TJa&HyIpMidh=B#p<6iH z5Sv!$LO2p2O8N7W?^`7A{Wq|>0l|NJ|D1a8z0 zAR&HAHVngW3V`@s{7ZT`*c7dDfN__PI<=Xx;yqKM^n|eRZ-A+ebt9 zoXZPTP}0^rj5sVJ%H?$pqeh<;qQAhQKv`o8_EM%Cv4iHqY_qEBgw3HKu6&n0^;bJoE=zDWe4w}K)|G`S|qW8g@b3K_h9IFi7I*dACI>f`;8>r2FuLs$aik#Q}( zPlPQ6Kus{WOxRIdA<#zHX15>Dp={jqEW}i@DCgZLd0-515-~w{u^evzkFjG-df=#g zY_%NE)gzOlE6s?CxjG`)4$54BHM2mNfRAATEuX3x)uMc-KZU-VH^$YZa1<~~il2=f z1w^)0nDPux7er!EBG2-1!N?#JOk7mU(^qMe*i(1} z<7jA}kj}*L%6OwOK>L`D{6fpo8&;dhp;?wBPAq&r>By~&7eCUyY8X!C;{dk;%0{du zX@7)7Il{V_TXJO%@BU+t_n>+9GR2^NCeP(4cPWcD)IZhqHoTt=)=L4FJr+kf$K-QT zc-0o!&NYg#FkBJsi$Z~x8|6ckOL2}ZBFA4$VCTd~Od2fm-Zd$LAEFDg$)U|r9x!+& zq>trr4XKnQ^prodC5HF`;k9SfWH1^fuizSZZmop(HLQbP!J}n<^AlX*M`gQe2h&+L z`W~z(z!Ed~(U*j)P3MkE&=>kE`+JuMzu!y)<`@qh2vU9gKrGf_Zeg0P3ejGf1QowNDlW$%x5k0g6X%fj_W^6}8 z2gMgz^J#G2f|u|A4afB4Uuqwg`ooB&s`;3O1dspz4tCgKX511;V;lChq=I7n+7>YW zgntX3);8iQ&HNvLIe)6xzt1KHaVd@6Kt}h}-8;u+QoiJSDKDn=c zae-Y?N$lXk#qh}8zezp4v`{=hBF&1vn@n70Tl?Q}0UcvA1UH2k#Z6I|-jB zfXZ;@8HxCIaiF*3`&Ay9Avr164LTIUm%5kGOkVm@Bjm~Mm+Cw5j6-S~)b! zjQgKr_!s!z@tuzo9)Nv&jexO|5JEDf_)io!$qIL?`c3B{khr8s&}-e~WdD(N0?n)v zhO#zH<+*Ift7xb9lO>7;0mZ6qvrn6U819fkC6hTN;p|xbYyD_jR+qKhGHDcg{qfMeX!=_hlT02ZLTVfe{N|E}bIgBiLMSGG$l z9Qi2m74yZ?Qb=uFvXS?ek=H#&pFSPx@g>)_J6756@uO15RrpauoALst3YZi^5fF=j z^4nFuUAY3aa9l$sT=OZWdMJMXi%jSCR*1cL=CbRjg??Ir$!o-mv4vptY5Ng5T_8KE6%nO- zeDN$NV4hy_8!D80`~l*ce9^Y@Fp|s!L}=p8g$0pmfj`3vFv61P%CH!ak>-3GDSf5JwD#6Al*pLSy< z%?;j*F5I+xaq{cSi?`1i^Hd=L6)E z*mz${Ya?laDO$4VB!)ZUI{xYjT?|1KD#(Sn z1cr1ql_s-#9XIKm7sQrkQjD!5+_208D}xkP+oX%JLaqDRD+Hn=YTX&Kpg!|U{t@{C zq%+No%px5DJ!UM#fY!IYWS%0US~6=4`tQLuBIo5nb=AfbPkZ>h!OH4+zceg~nRJxo z*CtVHOTK6dS}a5#VIirHC_JyuWU6nO=jS|RI_F^Wq$l%p=e6>joCKVJiq~M!d@evO zg@qnsPZ;rFrBtH=j~A}<)nNbL+6{$fdC*iEN<7N%wz zO--UYJ`lS=3Irg9Ao+MN2CjYHuM}}?<%ovGAsS|czl9p*h*uN&aWdT#C?T4A{(s=Z ze{0}>hIqeDdqlt;Pg+DU)0)@kkM@&qiLbUgX#&(Nu+&9?Z(izY3jYX z>UmfMNZAm8`JG9Qj{NW(ydraGa>%m(WtBK0A}3{VmgH>|?j8}WNfLTNH=p=RW&sOY zp~S1&%hU4Re-FFiq90jh;{3%XaKM=%B56_{sTiaQYaJ-s)FFz|IlC4gB)bL`i9Mj9 zC?&cIhgi1ht+?01?T#3pv#?4p{h~2`oj;_Lw3aRe$$JVVb)t>jbN)kHccv{$Le5M8 zJc{LSiOSoWt4}kx-n|BXiAxfQ&grG)3uo68|5s2x4n>-$TRw^547t{)bewpu8>Eg< zOZMfUDKvp8a~;jhh(@FZX0LvLLuYxUF5*C8mKq-`nVqGHznhrVrIaSd+HO4OQH;w* z)80Y(gh~PA!snoo5^JM+q>c6yc*S^AJ*WTu)BdOm5DQi>3vCng&1ns*{Zj%^WyT!Y z^;m=TUwIy3vDxk>SJsy@9Af{=87xP1M%fBduCalL7&x?yCRDp#JL~e9&$q!hXvPkttE@8uT zgND`Tb5GMpW6q0j9w21vhs{|7M_FM)P5~7n_9l@2Z_q6LGFh8E55(@5VOY1vci};` zLCOcwL}3zeA>CLGGTy3{KgPhi;Bsy#248Dr#!DASY03pWTD} z_s$0$1-sTtsWgi8 zmp+sn=hG`#QqEnzs%Ml343><^##Fqco4YS)Zi1i@7gQWQ zj3Ww&aaSWgJlRhm7uhvH5=u-Fuxsz@Z^3{)#h#&iwKiILK0zyswZtZ%7LL?8#*>F~ zfX|R`I5~JstuY2%*~RjMlv(K|B%pU#Rmj5YiZ?e69r2q99zOjEL)A82wf?TfFIzn-)CfN z9j8A2j}`nPI6r%_4om#~QhjJrDWU(|S9RpV#n`3*rFjfYJE+ zqA6`^PA9R)hh`2so<>3 zmuG;AEaWxp3Ql8V-b5UsxCa&O``f*vM?CeV8#7uVMmzDl&+pfgsso@HvP13?p%}9< zg7vAkd!IZU$vFS5;O6$f4Vji}gFXuqR^byToU?f~mFWH+Ch&VPgH`FIM zqv48+bFNtY)z(wg{ZIu%4x2_?x~370$e7q9s@mU60QpkU)B%eeX}SksAn?9oJQ!(G zDE>591wWYnDCyW+cUJhh-11V4s&iN5YkU2m=9@eI%uaKxY(yzibV~Is-NF`PEXLk{ zK$Ystz4NYF42qp3d9jpz*j(bsCOOJus*bSpFH@hTS)!WeWf0f%H5BmfOni0xMYQ3x z)%Ea2D~~p?1hqRq;)l}aO?Dxzz23q)y?GE#;LK^hJap&=?M<4wp73ugLv_$BcGP)QWFt<~boKbgjBC2X)6;JwP+0?OF7EVuzJ z0aARgxJIAA%q5=xJDF<#5+P3O!SIU-escj@_MWFwYrzAR$IA_(nJ-Y`g4r=caYxkJ zr`LTTmgx{L_iAz;p&^lmtifZXLktEq$@P;ZjV-(1317CkqNuro&1P}TE@Lb%^jk4S z4b7B2xKhOio7VleZWr!WKdc(2`ZX$V0H7pkQ4k3Y#Zd8fF%T{jo|5e(@45}Q3|Gm} z`ci^TT){hWL*|mEFg7lI1X%lVn}En6_(Qe~Ka<&%=NgNC3WZYW^&(O3^6T|H8hh-T zR`nLgjsw*E#u@+)n!%)7rVQm6ccvLc|6aC5AvJ>k$g<{Omo4zi$%Vs6eZbiXOnt^(5}i40Shp(b5IgwHRsO8<1@w3~IHOudyA-1aUn)5#mhy_f`2W4i1LsM-xI@1m+O;<>IL!fWzIToR}IwIJ0J| zkW%$E-ziPmL>SJf6Fl_-pN##+e(2x;y2HW?pNzn?g}oAkK?uaPP51D~ z&>TG8VIc6gsPH>sR5|o84tZ#Hw`FSX<}*wp#CS{rOYbyCajHE3m%4vz=gK%ZfNW1D zAzG9&L&qX~xGIyL^(5>IS!O<0*zqg_U-qgCW7pjmG-yJ{=gliM~KN~6ui z6l;*}kJk6R27(rK*Q+oVp-N<$xO5k+{JpRdK@3fX^nQA!gZC0ALEm=oz5&z=>x)>Q zc`oQG;8Lj-#v@3dPT-UlttdVE6~-7(VV#mTd*nLsmF;VFDYWa_%KthoThn4#)X4k<(S7wq!1q~G`yQl6Ya(J<_! z$I7=31~D8T>xRNvrs1ybI-e(snw@K$p0e!YE>n7R0(dG>;DF*z0`-%m3Y@iHN_>js z>5giJ28Wg*UGEwwBZ*$W@DfbG%c?bopcyur`;B2`X5K~0cJ&eycsm|y^R3b$?^=J+ z${3yfI2L8ZD-R9C@Qn)3+w2egE^P^OC+uMm7aT}sAh)P^SDFgmXxS*x=_@Qes%&?XP9dg;j6MO(9i(4=&;?OLxw)`}RO z$AL@C_WI>91#X+6Y8_$9XAvdge5&Srq374zo*oyZE3P5j>JGo!pdvl}KcE`Kclt(a zN!{OU4m@>`4LV;rw(0t$L-r`2@QGmx#lD>==P&41AslSXDfm!hQ)=SoEX98XLdA~r zNT?%(h1x3=40}dY;$zXQmY5{)Wy@IW5}T@3dh`@E5S|@}DxzVPZv?UqCUl${M40^F z6*)2R)SQE>oL|~;UKR`bu5hAP`V>aZ+@w?>Ke!9p&Iu+apvloSw9a$1X?yaS!yuzB zt>!~5@Gn-0G#g2o(U~toeKkU=he9fkawnp03qsfJjl-gW@Ymc9|Fe6;|A?9u#R_F5 z;xD;$W-_~ip8cwDf>~ggQcJ2JjDzw5^2OdLF;&Gs0OE?vrG}tWh)q7P(pfQv84Yy~ z+s&|Yz2(<<=Z0iYVJP_5AywwyQGsNg1N%LB>#2At?O4OY%fjG#T=41;;g1I}zdUzw z6nb!#E4{sd39HbjKmD!pdk#+9#`}dxQ}iGgDSQC@Sx0156ANJ<*CA*h%+4A%a|C~z z<8jQqv$H4(thdjYg7&brsm4KSI{ccx3zJX8!Z7zt*_-NzLwFgf>GOVXcL2d7vxl>p zUchr)G{S%Wp+6qfBB7vy$D~gXuJJcZ z=1yY@sg6K?SZq|Q7siaYHVw~ zIji!@T)?Uor@khfpac;LAm049YD{8C6j_GtACXAAK$9+!2cOZrD1SMzX!JjvS7W9i zJ0^v-aY=ycSI3?UjY zLj3_0N5V2t1_c&|bQVC3TWo_?H73!W&I z8IF|#;&M>|v?lD81yG#`efl-7!lKSyg-?@D^SG)Y5}-@XFb1xWC>+{Bo#o9S|Hzq~ z?nWbr-{ygy!WJR#l*Armmj|=(pZ_xNfNF9yAgErA%s2p4q0s|L9Q&n3_%~u9WDztn zi@bCY4gg%b7MpZJj=XQ)1E|#UZD!zE4X*h4MH*IZ(ohUI^%~&BK2SUfGLJEP9;wF0 zt^aVbQHfQmt*wPETOmsMmAWQDlEX6uf^q@sE{QY@DRoPxsH%zcBQCt* zRxP8SK|Yf_#9tH#y|~kD9p@i629u^hjy{kBEr?Ci2DJN=`0`=eFFB*@$}rnvIQhs< zZ?moR`}aPK2I%zv;~$cT(hHqOgtscHs^tNR-;uJ9eqJ^G;v5OjUIj(|g5PUB-G(EA z96QR78buuyQQ0Qm+e)4vEpG1bgdJMC&yKc+Hy_K**p1S5{8^?^HM~<#GLX)2L+G3~ zo=N_e4UlWo{6^g{P?O-pE}`xP>?%^skeKJ3e(8*ntGiJ4^r>vrgJtI`afB5VzmMH_ zjd&uqwy3>_RQeKISu{rfSRA2jZaRdaHZ$UQUYGwz+!yQ^G_e@7Dnnudj6VVlsEGoX z>1LI(lp5rtGJ-8ZqpZ$5AFH=rpf;#n#j(2K&5>Pew@q)~W{7}3WQ0Q*N01_tYDDR? z#Zop2hLt%(#8ENvPEg(PK`t_ZM{vp^{*6^QyL#CuLcA0qK(=aE!csBE8dgGv))nwF zYV-m%7P~$@D0{Cne@A91k~|4nmCf)#jYA}y8-2PY{38sD3k-@7-Wv`Ap@$7$H!Q7w zn_<)t$UN~2|HGwn-e*60dde9p(P0=^?cbqb<8UZkU16&tkf=6-cq*1mW`{2}VMFlU z)n*mK2Q6vBR1*7wgw9P5`3YL8AB3z1EF-p7LD|;L>KE+vl1URTt<2)G@a>7s6{Kx5 z;bTy&(Jc=iuN#s^Z_uw#C?*>;9K|RhoF0P}8z+@BJ=P@OA-g4Q`6ms7XpyQzkycP` z`wgHH-EmBcS&AlUAVfs*H@*HtS@Q<5jNjrNGH*PPDCDq!< zF2tV^dFD|gdFK9;I0^vzqQ_mu4ovnPE{w%iKLE%cw4{j@&zSu2L!ZxprT~y%RVA^xW*ALgTS<^r`uD;tzM)FPy7827}t~!dQ8- zzvW#)PsdC$GE(Ua7m6#|2Kv3cpJ9#!G;j{K_R84r9y?)Qh_n6?3nL;at0yJ)NuVwB zt2Qf3S*;Qk83cwYv?C36uTu$AfNutdJQd?C(K8K*9OjtownAgpy{bfB{SXOYYb`GhK7yZ4sFRZN+tq4t^Vo=0UJ^J*HHPaz$1Fsjib;&=HV)$V&XhFs!*26Wm%mRhsZV3McVzrX^#Nr zFcsUA^zIEQo}#M8x(v^3-Tpn)22Fc{4ShJ($E>EbhS?de?Wa^B)RCla3>1MnLmImF&GX7dix>+P?E$tto{e8s1`hHafTLo9rN7CiCmLcu2wsR5 zusAR3K_VqsPf1d~+o0u|6_Gg|+d2np-rD>#CmEQvlL~g=fa{jd`V7RqNw9(L9L4;5 zaej9+lqyP@VGA(wGCcow) znP7ItP~dDS`a2g~F1m`o!zmMQ<>?F0PeCxENp^_jF%vT9cvt^`iCht%RF&!tXIy8N zqpL*xCMNVluj@t0{Jppde60CSVv^b$S8pS;Mc4!78YQ!(usSD8XYgkZEmMzR<~6GD z1F5yb5Mz#E)Yf&dItXbTY6JI|%@rXKzQ9&)w5Y2WDD#i#+BKtGnLpF%yeo*5a^*2QIY>{U9Q2D-jve*30Yn(+XB;; zKF*M`2Z^}q=6{*M;rdRgeNC)@@nbfz>zPtJx28-ySJ=DZsN>-banv%!GI8=h&Ouwo z!}qj!V{(<)v2*iG5G`NvWBveF_0RY4&xbYXmw9XVjG{#Sgcio}3Hlz1ZLc4wsRtO~ z1}2~qY9eWVPLJ;3LGDy_9Zvg#wY2?sdhvwsgC%t`gt8fFe3c8RU{g9#kcz2~x@Z>PG_!iXJ+i=x`<4wsY1!`7=e|KFE}ogQ4nDx$ukxx34j0gL20TbWlJ! zO33=7j$&csifSad-gnDCOJ?@*q6+G92}rWgXse1xYp*5mi<(^roiy|sNng6UplL4y z9A{Tt!vw0#Gt`F{xAuZGc8R2D=d`1LScfD8D8H6_B(8}VZ?@RuskSJ#L*~Mer3Ij9 zmVSsMN){HzBl)Jh^5sTZxN4l4z`KXh3V!K^1jhb7yuL?9@xnCJJ=lZbsAMSEBVNy< zVA1~>=M$VV0RkE}{}MA?|DWBe+5wqsUjB$QU`X(B+KFY?4`AiB@BwL1?mLPfEHtP& znesq)goUP3ryfo_4O_AmpO=XP=Nm8HgX`jFiro1)Gq#sB=H|CEp(>2uJpPSMix?@! z=oT=?47B|l*B9)5-!Xn#1Do!I>s$Y77vaZ5L=xcU-Z}Y)IuA?1f%Jf~s-bLUE~TAr z$i0R~XqjbhHgS`!MUW>!w?aB43*e&~G^N33vu(!*$an~5)1Ry1RsohJR<)>lrL zEJK%3NCOX|Ci4{2)5@>UCgRFFUvQYA*K!kk081;8>jf&^CAs|$5ltLORYB1W4!>iU z->^Os^khcdk;?I4th_zd>M3gjk2JPU)4<#wY9Lr{W=6ssO=~IdEhA9)0i% zF;v#dxMZv1sH8=WJ?N|5-yz#5WAMeJO2yU0DGb4znH=#mzYBZ)&4RxDl_zMZTdq^i zfB@zBUYRiOr|rHk{q}JnqZB45+EdB@70mLhgeZ}bn|hn$uE5`@?Q;}RNHGNV@e4>b zFWNZmckK8Y=QzCtKmo9$qNXZs{S!^|+TT(K4lVLz9=|wbyF} zki;!js?OpK6U!?@YIgY6ciJ^x^V@Wb{24TOac^JTzJmx7%!39>xrbZkIVm3&@=3X- zRU8I2Si-Je;!^WK_iV6`k#^;`hl=@B`Y4Lm!jIOqIPUDNB&4?Zi}D(50n2}XSl6Rh zEI5lpIE4drcmhZM(?}jlxld8Kq(6LXXpfq*k|Ml2a)mi{0y zlo4@@XM1jnpL#8eMONGl)fxi(jC8#QS&^Hn#4gW62vL$Ioem8V#!}@fjIB_9e}O+o zmvdZY(kn~f5_uxU$jXG1qreb1O9?nXZhqA-{hezNnyoGtiCVGBofNC;Pf`_$Dpl(? z4Z69AQ56wls8R4zDw9cv_GBcHTVpZ@r-xJU1kbm?j2aUmNiTvC+mk-X(Q*h4EUc>`y(11I;8<4~QTXZXJC z7J{9Bb3b>hmy_{3W_>IOJlU1CoU0cHySK5FdW4>LiOS<9Z#qV_xFJk}cPJCqZcS*L6ncZm zHM-CGtNn&=r|H3Rqf3^dO=KATOH4Mk3PRS6@k>i`xen|{5a_K59Rfgc93wH=C=c>D zY%g`*gjgpN496sWP083NlWI-d&)VQ_Tub4aoH3^Y-m5}l=qQX6_bC_iJ*#D6*s6wb z*9EUpXy%CT`q$J6*)k&XrgX_WoY#3To)fTSH&rI6fQxp#;{|mF(~`4C8)mzdfJ?bK zId z{#+%=4x=no(})-uC-h`+P9lPVO(W_aGC&XlI{efFh9;+k&(?lvblM=x3B;Bs2LCYu zn^W4>#zC{i??_{-m8z1RT@@o(lY}hlQVBo1pI-WZ$Wb`Zoiixd$D%?Ou5H5tC)3e? zHuB=$V?QfOX z)1D5EjUXYtmV8Rg;ORn5;-rklslrkVY+|bZX(lTsyajT0y?$GLRe;4)O4MThdIp(f zCrjNoDnJ-UTgUh#@D;8r)hCmZ5yo zIYCUelF6>zM!x?9MwF(EKgx62y96#v&n$PoNzm30p*B2;8%Vm-Zwb4oXv(0Z4^afK zN(cNY`WE%jN|}sJRvuPO#akeYA5l3*dY1<`GS^r(_n@zoG{)LHFiw+$3>#m|)2VO7 zKPos*V#BCsT_!od$28o{8gQ|`L>~!Hu5~^7CIbO=ps$OuIX^Co&&)+c?=|G13gr^w zR<*L_hs{64GgJXxr%Ea$5#SCeTO(9iA?P|D^S7vqgBmsh{zK-kBPx|EJjrO77tjlG zV)4qXDHFx_!l7@_&1(r`fhOwcHO_ zpP=vhTCjronx$H5zvXbEGE-SBkF6nfCD@+Kzbdm;RBUw!K<0mglD1mh zd?gJTh$3BzMGNh_P_5Me361=<$O1Zdjc>f8!qotR6h>++v*W*RRlfC~j)v}eiL0J6 z;k8pnIsgUnqn3xHEqDQRr9D47oS%b$Odi8$Z!!J-mQ7y=rIl#+k7Q<-621$HGedAK zfF`&PPCvrlq+mOiLN-g}WK#bXz%dA1Q(~}&!1^!u+pwUh6QX9~;13>{x{X{kC`8Q}&Dw7lb| z=mj;5uH6VhOL#&k0H7E);_%Da%~9eerguLzlIvshiG9G{!^0Q-W<(N!bd^~f=`liP z^LRp~iSMM!yW#NVhL~rx^f`E+2Hmn&p@Xfy557SsgjU`&Lmr+d=`o2_cj=e(7F`M+ z#$Z=pnmkO3@WxhzqKkXxM{V~0KD)%ME;&?vD#|KGAvC(K5_q~Q(%=uV+tFjXjwZQ( zYn_#LTxn|^;4WParIvashBr7wH&wNne@(0rAk+D0iVPMrsVMi+fpx|a?mXWz5KFecA<~CZW3})QT``7Sb{;d{ zCXc&xT&B249afmmklql&CY>{rREr&Y9Q^2hzuklU{&=cC5|Y>@5}wQig-O7(39o1K zto7F;=AiHqhDx|wYDSk}iAaSO1k>FM$21ay?#g0;>zMWR5!yUHM>a3=$9h@WlYaUK zfJRn!$ze4lbm^|Sl%U9eshI;V=~Ej$MfG;yBK?mOn`**&GpKBTD`vgat6JuMb1}T8 zyY-3TU8VN#Q{ee#3&(HrZfJgOAiHe-uLnRiwlk4K`eUy4LcYxkqW=TW^*9h*OFPuz ze|fVd$(xifS5S>pgT`&m3Oo`*LuFMpAGs3k0>S7c`gP`&ylDia?22aBq#V5*V5*i( z&n&DKWMT${hDUK~XcI>RV>?!ok>JE#>V4@g5{F30ZXi~dp44g}{J+<&BqK~h4rZj- z#&gJ6?;yEWKu1%D^olybDW_BaP@V&-tE8F47{?ZKf$8Uf)ap;Hq0I;md^>%m(|T$$ z62QGva5iV%;DtL)8y|#r$w}`fGCC5}2w0K}`U}<(z$ePfI#oKRsxb1H*jR_lB4sfo zXVlav^vs*^Xb%%zD~kND=r=r!33<_d^=UZ)yS<{}Lg^z8X!Psr5j2PD>xLkZl1QB0PWov(eY{9_F@Wv_ln z7-k9!i73~qY*2`uz{)*68p2#FvUnGvRxrR#6L7Svvj&q9qG~`aDLG{gZzVP~e#O*Z z1bvHU4PGlM2)6-iFckmVRVJ@cs78sQ7R-HdsTb$bQMr>iX@>qa%Qar&hJrDAI z!++oj%&L+_a3yz zf=XfmET$FunNUpvB6E10NBFwXm^0%NA(svx8k9jfRcSaMow7|{XQcc<_IG-f!)JZh zQD4c52ij$@mn79lUTSkiQ{Ag+p~oK`DKP2>VkaGXy2`AwN-X!DoRHHDhzWLt2)bYr zMaZktutPDt17V*V6Jz>$Z2-xHp>AFz*Jw%-b(&QpIVEcrsWuhuUlYT=qRrkUsoU{! z6WEkXGh9?QT6Uo@27>aF17S8{9hq{F6J;WyC3s*ktn91cxI0$q;|?6F5@6j8=`NtJ z*ASbpfP&q>1gsn`oE^rtcg=L^4f|UA>tTHp`07V$%HH$(0L>h1xfQF>$*b zj0k_Ex?m`qdCUXt;Q=x^LXxp$$)joU)Xf2VF8B5Xnoq2ndMM6om>;z<(DHOY>6D%^ zjLZ>ZiFg(&HD3Wg;2Xb`K`{wca76>u*@|U-ie#0#{$L>bWWpl4QDjIZwd*Y=?bNGD z8d{#zm?(pGr8r``4d(K}s>q4n=-OGN5!Y*&kYfm$|zN zH|CoXigWWN+E^hDPlSchk$R%Yk%dhHSgq+Uqt1$eK&}~)u&^12F8Xdt zhpGEikz>GeL$eNv)F`glzpy1mg}!(sRXRuaQ0MG=mmm$8e0>ph$tS z%4(>S>q*@|suD`CkhXQ}yni$qj46^OSCDdn}V&W7YMg$(SrPYor+)vU9H za-S?MZPrmJB?Oiim)krCmM-KQQZw3(ql&_wN3`fw^2GkmRW;U)7{b>(jfKaRjmCi# zlCxMw`z41nWK$fHC6B$lbnQN z`?9fYbR@izO<*V5L<~yOr)*!Trhme|4LG0yP@XF^yo&F<_c^opcSJXCF-FL32-*K1 zBGK+=M*!sJT)qwTB`wPpOfu3S)_&S^Fbh~Z8w|vu1u9?Q?Dy+CV$hy45FxDeX8ozY zAlPHpb}2$$is5a3(`i!MQp6}m?!~`F%DW5rmN|CJUwc9E13guRWXsxgU2O+4Ue?k1 z;yJ9yUaWLrjuFB~poagc+ChIJv)06|R5-+;{S+UM0Z?Ti`QLUC>LCL7WbME$s{dxv z+Z$C&HQ#Ve`)ur@LuD0!;SuGjn;f{d*+1@>Z@zGgzr$q=0{xcKqbpn01=0I@nCs*9 z^JxH?+IdPxV-Yl5P-G2d_YXO1yR?UQ$z=4Lh(?i=fi zTZ8bKY0Vn#U4lx03d75aNNFPj8=NrVTd>v;Hg=!?T-^pCD14i%UayNT?aGExN3aV_ zxKRKaR6RR1nln%2=PAXsAgm!VqH8WZVLf!0N9;R>DK^@FQzCWze87*KU2%ILl89D# zyCf}1%xV{Y^n0P)acNP4t3YG9F_{l$0Xq7WFz1I{yYTk%hdUd3(Hg&-LxqtLWP@k2 zZ1xY@LXKG^zyVTb{(F8NQGKC2U%oqRJ0@iUN@L-}COb*)g{`vGVdlnRL{yc8DsoyP z=wMHO1O^A{AD5E~&U7OTPn?w0IJ+*qa96eYy)RiKnAf5Jk8zT z1Hh)NBiulHUkA^Eus2Vtm>t2uP`#IJh25A_E2u2HQ!gPIa_!y|7;$hx;^Avp(Ba6h z>5`}gPGKXcKTM#eBxf1hgolYZ$}SB+XiF4?R$J{pHQzk>e}Dyx)v9^)Wp!U2<-ik}MoC z4ZgjE(2enPAp`4|Ud*XTu#Yi=5M0>~dk*s>7EdvRU-&NwQ%E-DhNnC#^H{+vfYQR+ znExzZqihLU*7;tvhNsX2Gfa_pA z9`FwNJX0kGJKPmLHLOJ|cu=ELw4hK2ZD*3X`M1FogV@ly0tI2E)nFsCDHGq@?o(m> z=9iKFpa0{}c(gtv zhGwmVol>`86EP&M$zNrkq>D$z8(5BSPWE%(kdkdNF#Ho$>71Y??GD+WI%pxn$P*r3 zdymLaaa-=Kvb)A2MvKd=;}`VH3UJ+2G$dkErZ@{#lDBa+JapGbwM4i#6}mHI8+Dw@ zC7=--Y5%M|6PN$(;0QWX5$`vI)-TijTKq%|=w*ow#CBLtZRVP($Ir|qtF6=3S|VG5 za%D+Vrk>@G1uJTQS|H(asV}g00yuLDY+shdW@1nNSvTm9;vLrqo|0viYkhDsuu|N= zQ!QBnbgzhOf}ra?Wsgdg9V-wS0&48%O!@ieNdDSeoNdxtFR0dlhwM1D;GulNA}^9A zQ7{Fy2I**BxU2=H4%KZzM^;+-g>qC3Mi%#oXKa=Bo(aN#U|+=v=PC$D@)%CpZnTy9 zAqvWnMM{ZJt!EL6?KkP;1HTo=Zjtk2&*kaKj&SfQET|x2Wz5jl8P+fL& z9t$V7X{?lTFI-;|OU28p_SaE36P}5u!q!ZmoLOgGQo;x|B^+f?YH$`$+ViAW13nWG zsc_#q^y>{qb2yBb5v(Q^6DC##+GeDKtRpJsT`6v%t1OW_ahZEdV1lr%e`NI@xhszHnuL`PDj9(g5 z5ipI{_AV0Ivqed5<1wOjty;;c@pR}dll=qgj0oK>;@n~4Va|o!eJsA_R~`zV>qlzk zRQZv-{1ek=EJR~ihv;@IMDOCtAnIcG&G@h3pJH*TlKZl=Rj1ZUZHBvleEEmyN&JVJ zIsfy8TvjKTen5v1^HwSx*#|L2VTfT)RIb=PdEzORG-s}glX(xfbVP}wqay|P|G7A>$}!A>$M>p%eTP9eutv{gmG5hCo% z=g2yNnmkCjw6~}!OGm7ojD$36#JW?$ZMqC!JqP+ELn2DDm*W4Nwovt>*~60lRZaO0 zOQ?ihOfws!OixE0C)Bf2HMp)0(AKQ4f9sse2y7)ny&Xt=9%GtaXRBR#U3vV$4DS3X z8uI){Kn!(6?~Z}R?8CL6t`)_BAKzW1d5lRF$MX-7%=JjBVbr4s;ep4Lj1Ov3?PYh0 zAgi#lS-`z+yG*Z7g}izw#7!2&nVp@y8X6n2OGON~#ukd9g|;ecW?+_6=P9c{Zuv~~ zegDH88~qX}IvJKRi26}s`eP`47<-`#79%|=+^`txrS>&kNK)?7h&qm^IQgH*q|~C4 zj>lGALNRN$egKE1VUw@B67JNvhI;WhM7_K8MOhxxax1Flll$Ydvr(jEtS!EOw8C;BqQwr9bwpv7MW0Ld zr($BU+7Gx2?k_6E25DY{AzmX4`5Ix6?oI`~s83mDqC|iU3fUh!rHxT+et`K2IE3x2 zX4S;s%X&L`y2_v)2H@aa;G)9TpnYc>MttF56dJBlG<8DG1?`oHlqX~51HWb9Yn>ET z8zIRjmi%i>)Z;pMslJ`G&Rhz|{9i9l$lPEttIA&bC|Jzzh_f{Rg=kuI3;s|(Ln$ND zd*FG26=NHIZBCo0=|w=;yMRp(Sw|}sL`VIJBn(qu21AVfgVs$mX21-JR2%YJTHU{B ze4uzbIKh$(44KSw9HFiN5Aa>r0 z+`BdJw_2=B!hJGWJ#2AzjOfI-9Cay`e1FxV+F%^eE`LTT6wOZq#x=rbU(mDpW~a)r>QMwsZx;E&Frr5jAf0}{m|E~Y)Lt3ThLFGNEb!`$ zKLUmAtl)lASHH^c(o`1`U*ek0zuY8e8n8`|ytmwbLR*Rrz}yZ?9}&>mx;;HY@3j%; z|NmqD>8`F9cT`7`{an!bT7S@$PBYJ1`g9T=y_WB!p>M`1&9Zxiu9fbQ8+ycs@y4Po z8^ALXyXt!db<5a~)Hf+w9C0H?5b;6$wL7yK>AEunk*7s%gN@t>8o?zrCmMQuR(qCd z8i!~g14Z}rQbGqsc#L6{uxpVnmuy|HcfJJ+9s>kJi8647T%j7#_^VQ+F6?N8a_m*% zr}}KbDezNTwTNU0=Uvz(490)Y&Js@1$WvVDk*9Lkk>1kxI zp>FY2vqt#Tj#qGB9niyK-9Sf{<*p+&^4?fQwoVrkdTs@aczt0AlMY;qX6URmcEz{J z0A}%L;m=BX?w&o@`QZ%uy3FY(#@o);8V!YLM?0=glyVR>sV?kcfM<;nc_%#(0*ihE z{jj1(b62i<^LDXGHJcX(xUz)-2Cb@7i$0p^=dz`ww+LW6VFmq!AfT3mGGl9#bm(I& zD;`eV4v|BZ@v%!k&z|aofKLc{ER$YSBoNBcR;oWP4m?_aQfeO`a^FBEC2&1e95jN| z3aORD?Daq7Ow>HzUNNNm1zwU^A|(l_q1F?Pt`F(lx5&cCm45?KiP~@ynbSY_2|L}+ zqvl?F7KRQq!cw;N)3}QzMy11C7Jb_uGJ;tqyijIuK)ZtXiWNMnU_F2a^ruo>bl;Mx zZe6zmIUo;*LL-RKwKD}?uH5dI8W8NL+>uHKUyVtK_){-(Y`Sw+a%tx0%;iO`HA~*^a$< z+P;Ow<%G~ThJEu&g>bA)b^04s$xM7XktWpnGpsd=NIkY)lU;4dE{Oih{u-1E0@96R z&pEB|&?4gvC-!Z+T?L?;)-%6IBp5V|=)`Mi;53i0%Pjio)S3ulW~5BA%0*JT$X(n) z8|OmjWOKn!xC78gVu0RU%zF^NBCF-Eti26@q~E^g6!>A; zx_73lR{}7R3-v5XM)i*Y(DXLu-*J@i%V8U(x#t3>gbNXAJis9HGK?Z(@E#ccZtvG4?=IO0&iv=fO% z)a(TqCMda|w_t7cm-2|NRd-`9wPa}fo8Ol=>;s!cQ&qtbuMst4>|>q)36sdK6Ek*j*Nj8;sAY?!+4BZ z(DBR{Wn4@QE~XWjhG0SkXqNPZi3~A1`;Ae%RK8c z88LCKH}iDGKa{CqKQ!a9w`Ufc?Z0x|Oe~9LN~fQ<-4`X&-yTsdUaXBPr9P3V0&F_% z;#o*aVkrROUVEz^1v;E(e4OU)l|HIxR5WG27jeY#ms6c3Rx3(kv>mvbJY3^jg82tD z6}qP-?SZic zjXqYDA4ck7h}F?{kt2}X2;`*{PCeP6yye%EjRxPQ0FA|ptRXIpp+cz z;k6zVgzO|p2sT3V0p7Uc0gGn&K4VG#6iG<#pTN9mXKt&QKc@oe!Zdx1L)%FoY4$Bd z+nYla4YhQi?1b>VLd%&WHa@T{PnG9@ ze|-`SQ?0lk#1i^3DOF|RTbI;;!2bLPXq88de%>RRvvrbf`_nVY-v_hK4?V~=UiJ2Zm-sbRn?L)F4UjyklIcjIHjW z?+Mj|dpczX9w~@v4E-<|y3?lb{9X>US<;0H~tj$r?}*Si(yvw{pNM z0IH&p;H1t7awc|VJdbJ#Hd#@R?%WH!R3~I&ixRcRvi~Ax%G!4|bhS-*6hrHXT=i=? zcyffj^h|P6i3+I~o2;EBi3URpX;mG1QCDS7=R0=bP=r85j!56N6uG7PvwHh?2BU*; z{PBMe^*^*w^VI$lEPf_j7wu++pGj+5Bc*3l-umF&eg6DX3j9<+7BM?( ze6HS`E%$#Fg2_u4hQ{g&3El~53Y>u^T)Wp8aeiA$v!5!1AE6#kj`_}bht^y4r6m7g zuM}X8hP*nz!~)U>_46xHy@YTPBIovM+BFZtdV(d<7t5x~bnzt82Rw z7bC-DLUmcyF9Q5D(p{D5h24bVzlHL2s&I!CWYp}iP0fAz?b!5I)k3IZa#7}j6`y^cD~hQc-8bbk`6m&=W*q*x-q`g# z$*g0XXo?lzS9X73x%4Ei;WSXRHByhj zPsibG);Q;~9uxvD*WbpF{}abYcY`@*e^?aS#vby>8g0^tADDPOmQXsLJ5IWk1+VIhNH^nWZoeiEUB+| z5@a8)sEYc|K)0Q@N5r+C945fHH`S~x2lzOP@7hy zUFqt&8(KHwNRX=t<6xtKZd`(zt3!U>cka|XiZwWRuoxq-6^2kvCAR3K_O=r`v(qNM zo@d;Wrf8q9@`y6##I{)U5*HN->{>htmk%BE1z9^vggNP7!I(rbaJ=Av)UEuSZa`r(n+%Zc(!yCGM$HDj!war(4>@CAA*He<&JAJvXw5 z&=?B}w!&@*KkU7&y0k92tlHi6y^_YyYRj12Skwi^5*QOY6(R4=Lnp698QmSDJDL@V zqv={_V(|)dq@z?L2Q8$~sENufc_}m1uuFS+VADc~H005s7`bl4DRC8bcY!mTVIt%1q zxtPiC$N1xVm^S0RHjHVUiOU1Iuota9n7SDi#L0=f#&Y(u?c!k}FU5QRnrn2%^oHVD zRRUfaMz)Zp&R!YVwP`_ZC|$@UzYT4Tzm=sI46lr?k^WN6!LByfdbmg7Je6Zu z!F#QY7p0A!E3L37_-G)Q4FV_R5SwME#y3!9S*H$$%+p&em)8R+K<;-#Jw)PJgPuDO z+{+Zk_#}W%XWJp^@DorjB8cIOT2-nhs*GrPZQzZ=S#p~}>^z#rYFm$6Ua7=>N=9A; zHSY1=dT+>9%8Rgi0vc@I%SxK}#e`lxxarVvwT;sU$EE2p=?_}$0hv1ff#89;d`TKX zr1k~o#|tfoscN(7O=HwCYY|m(blI=Lk<=1~mJ=vppNQ~G6XN$Kb|Q(ykATEn9*vp0 zc?|6YY1}*29eUPenZ4*$LGt^{Y{R1)NgReuRcu)=w-%4_ElcxD_!%Mjv+(P(1kICtiDDR8&P!Y<E|#lu)HOZ#g}!16^Am@#SIhNoiZ$gWpCPaoL~48JutI6#Pc<}I^|Di}z*2wi<)v~I^T zW{=-Hup*y0B&Joafv0-R@%~k^;_;lj_=F{=9lb2EbXHXxw3q#@AFhmb-qpXr243sr z7C{va_Zg9alH4XTjVYV~0$mUw6HZEkn@SAf`gwgwoVo3!MHhslgJ8JUqVS-x_x{=P ziIi+A8;#U74E2`08l?-d0f3?B&@N5{=RJ_dss&{;Uq_upn)vxy_dhACFIT1RzFKX! z#p})A#Q=s3X%q`{HOcX^H242KDgGEWyy8Ie--{aatyucfRVgS@aTZLglVywIWyc+G zK(V5#^8&BjU7lIbn*2vRK1ePpR|O#b^Ko+qv^E}UKpo_3IgNcGrl*ZcV&_QY_^}<=Rwa4f8t`5 zqW5ix73DDh?K3*@M?PljF-WmFhP_NCU`qrc3TffWBM-)1vv)2MjzX#~6~K|3hT311 z4yPp^fWii(8R^Z>`u-+R2l!m9?ut;1VZ%Ymuu5`51s*Yfjo$x#8$Y&jhPoUu%xw96 zsl(0I%r{l$rs7eLDZT52)cNK>aN5lK)darCE|l&Q1YuGQq4@RlAu|SS=KRvi>KBVx zc>eT-AY7m}Ty%uR8}$Pf3D0YtQWknNmgLPi*CtZe{RGz*i|1L4;>sxdJ4K)V8`n{o zjuKK>%EVUd+wWY!hC9{S89z>|woq*~Zv zVj4n9>}=z?jGA~U`IS2DW~f8SOhEO$;%i_vf{NTr$hQZ_Pu=O8*!;%?TotYdWM%1a z00Al!`fvBM@JTB;;eV;w94}jA&l#H0j(`Lc**@wAWHB985aLiMM)%hZOC;miWzoeX z1KMI&GhHh~8Nx-jOsLDO!BdfUhZ?Fz#Dj+ve?eU$1jo!AE1cVhv@^`FQnAV9$3iTR z(3*Q-SM{u1kV5ynw6)b__RU1KUrx9`!S(Kp-oGmAuC*WW#DbR_)O$Livn`d@>yykJ* zbtR0N`(zk^edd~97^?aiI{4@Ha9dMnnd|>3EW`CdCqyg*hbh*^iG( z=g87~Z8y!AbWU!gy;siwj33x)Ql8UaThRB|0&1idkh5$C;?xsrvismP8ROLulHW?W zlm-z#H;0QQxzxC+=}tY4A_BJ!z3X($+^gB97pL0Bi`3(aVe_yLMK|UT>eFf6wPL#L zd$F#SE&HA!-tsu9t4>B_I>X>^c_Jo%1m8hOdH!u#r+?cE`5~|B3$+;Ly1$)qw&2Dy z&}zyRl7Y|lJLEjR=uIgoxQO8wm;xG?!xa!qfWr_eawrSMdlx3EB0}RxK4~rcV4WVg zqLe^ue|?YFr!W%r9clqoRoMG%KX{=j+-bd|244mqsL!2I?9Q5$G{$BBafm&kKNW2{ z=FH*6)n)q!SrGn24D=)T=khIBm!}dOp@ZZ`kfK7NRkK~7V3`W62hdQ3N~^Ij+_i50IMQwG{f@fBMm0c=}ZWeL_f`ssGG~rl`b?zriY@ioZ-(@VR^jdp5D$bc0AKE z3q4Lm5gJkwOsI7je(a=qY+12jzx~Z5Y?OxCmO_o$Z*-xi@4M1reOaKwu(u|(ouXY4 zs;1iEA;vrv%N&sEY^ql0>k+KLGoCFen@a>NcZ!knHQ=|81F19S3Z1kV%s zgWPDqIV3fCCdXwP+~y^?w*2_(^2TS41u66gKg{=R3>^K7(S=s9+)E|Q51puu0_x(^0H5afYn~`uIrlB4|IAi zUblya-4XJ`!#aSk7M{;bB}2F+K&DxA$k2vFQhX0Ra*|U5+e|T&1FU(d=_WC9j=vul ziNmUBtqfD05*L=kMIMuN9&;7m;aEFB(n>hXQre9~Y3{)Z76E0Y_R^oQVDw zP){OKsx~6^%T+o2oqQZeOiUGn1S%hjCVdfeGkLu)0M)vZ7I^Ku$@hg2V)Wt`i4oV6 zL1R-Cp)S4g>>QVIxPdU^b|j_SO&%b(UmuPG(Ux4q( z{4_j_L3WW=GQk$e1v`%$^T_4;+gCNd^CRfDp~zPStqFU@FoDF~W>9hooNPHk z^~D!cL5m%^N28_gy(9ON5q$Ob)h;!zT?WfTj_mIr)QsN3gQn$IdOHI}I_$3@K z)Q1Bn_$VN=FMYWzuM3DGnc~kLGFB__9|Oc&qVG8S%$gmlMGZJ_`u-eRZ)pZ740#X> zww^{@M-(tJbnVY$K`oRi`S0-Z2K+)lpk5KM~xRk+vgVDf$m^TU#Wn1hjnC`KyVA(nFB-Y1p|YnanYPb^o4;b~--n3IRq5@{WZ593F6Cld z2&bBMV$<9ZmSNEbRMerC!@s3XgXd&X?DW%>CFP)Ljqh4P3}$2DHS?^I=Zvi3g|pL* zB1RB}u=)m&l9b2RO|jn>D#p($bT&jy#YUXoQ&v#AH|r_KBN@_$1oNk2Or%E5G@Ep# zW6`1%7+?ftlxQMiP03LU+|S_LaOX%3c=HfhL(MWEsl zn;q$Wh*OlNq*g9y92JNEIPzJSVL(hbou>*p1H{XLtDvdPXM9F#81=h9jyHFN)QCDm zFQeZZY)G70CV_G-_8hSgjFhccSs}PC?d3&nrASNbQCJq11w(d1wrET}UGGBNnZOyD zsGg@rUy0cDGq- zTLsT`14AD799|_{gB7+xs;}V=Q7lHxg?kiCo2{ZWg<9#)Z#R%IcG-3^Sj6pUmDF@L zoA34!{n+d))NT3zijIpSYd1#>$`Z{mEv>uD=&{k4tBSDbC77n#HpuUQp({X8BCt&4 z4Tg<7(n+dm#J1t;-KMSe=P?IrNrthUQsPwZ+VvIe@OnR0N%8AXvA%Vy*!mDD8D0+s z%2wCue-Taiovo7bDXk!tHbZsiBL_v7hGA^{VCpZ#m<`z-^2HDg8`Oy7bT`Ah&1?o6 zGtNE=aqGGmX8^*D2Ng$5SYBt@-9jo80hi%(MaoyDdOcFd~anmW~iY+<-w`zU~+t zyEiGpbX8a@$oW>tn8do0Cyq5+=rIq{kB`LPK1)-;N}eW#tf`UU6#)oPNfC}mx@^5` zdK1h*ApN$kW`X2R1Jkg92Q^PXCltB9Oc!J9O?k<&#g~&XR{Q?_ZeP&F=T(+wD*`w{ z)`R26L7;<`H7qab!Bl;i((apqoe!rHTAley8XWVscpI&p<_$i7O2hYH}&qVoA%C#(F=QJu(HHd z3Pp*ne44s=AtrpLN3A7^0icv*%GsTDs=YBf35AT+VpB+u5MxcCjwy3tS!o`nN?YN? z1Ra-Plcj=5&Mw4bOQ21cMTzNi%gc>ueJMsJ^eMaIq_$L@i%ad2d9>0s?4y$&_A6?t zJFiR2zWyR*K<9+TdYn01W%rUf==I3e%z6Q#GBpe+@0Wi^B9$THF7e)jTc~qHVNFiC z*xapsh9zWbU*ZXya9XCSwkOM2ZnNR!4kIOk%MhnAToC@WE3?eM|QZ&4@*p;T4FTO49l$nZP=5dsTO6 z9GsEOSdpP;yM${MP{)Sf6(Sn&Xq47j2~jg-pxbuf_qv|{$f_-wq-vhnMl8`GEeQ}F z&6%9Fwbnrf?Oo3GE3;S3AWRQZSWo&baVHT46!RA=)XpZ-mm-FQquTV3vrc;=S;?T$ zYuWw#Tol3~s=*x5=7O|(^I;2wd37B4@$!W2;xpCGl|p5AyR~dKKi83{aZF;zk^%e? zKwFKYaVs@~q@tqvYR|GdYy{f^FvG90@*uIOv1FX30VfwW~iDz6wI@Au; zG=2eQQZ-d;3~se$RH_WOt>)G94W*K)m5#aW@Ahg(Zz_7+6~SwckpDiBRtX?$G5IXk zX?GT=H|<*x@LL`WYos5iH!|1!6ChC+dCtP3w*9EbL{jN=8ZJj;zg7D2ZL{8>DQKfV zQic3eSZMfk@H7ybHj2wxW_;a~oFTf(@S=OK__f)6L4zXS01Q4nqZ&>Zr5PYp>=6-y zTB0H;&YUo>Ux3#AEqx`N4v)$$GrYG*NH&}kBOlHRyQ3iT+u*l5a$q>>N4Gg56-k|RAzMR#{EY=hQ)Jr_qjDf}Lq&it*68aP@-F%Ks z>)&X%_kEu8o4li;2c->8BB2B6m3zN7#a^ec+NHJ}bumTvQeQx!j0059+dkbIBHbV~ zjnoedH)I~_nh)?sGW~!3Xk4U_CbpxrI`e^SK`(2}MDdY@>BINOopSX|l|XrL!u$a# zGgz&aPpL7=66FAeIfV%2Giy9enM_IPwxRVvC41@b1#DvSfr#6y5!9rrqkO%)5D*l>fbngzC-AdvuczefWx zXmWsM$pcL#AbcerZbV{OEff3|6JUQ~q<4WOYQX(FBcGASy|XGf`>&-WCVg^6mhY@r z`N`=LC|C-~=VQpWJp2kX#u5fQtfd?n4I_T9+-2|=?JbnJw`K0>F2#S+OTu33S$l+4 z$YW6jXjQO4G94BOD{=zdmT&$PbQpm{Yp~B(T*tqpnlWSVbDT1^v}lx$e04rCO_cE} zv9Tg)oA(XZQsDrhQD|z_Jb3bvl>66V1bv`z2m6{wN06IfM$HSXir0sH3^V0jVkMTd z#6NS4>=O^K$PxQjskTo(*>gYeUEOHS{DljQFptrwsCED@4hK zLL$s`8!o#v8-39h%`z`8i4~)TkZ`d@`pIPoe7sU5Jy2ASc1Rh`@x(S*nL`fwUQQ8R z=%yN@`DP44Md2mv?WXro=9xQQ4pu(0Vs-dF9XhCijk;-lCo1fJ@?*U&VoDuJwe6Fw z!KqXG?4Vhh^5?V;zS2N2dVA-qR$P(nHp?kCWGgn+Xv1Ma9mYMt*0l4L|r&jB&de=E~BS%Gr4Xm>EpHDQ1A zl$(;shy2mOZQqB3=O~R5=Z}bA01TLsQ&at3!#86I)qQFK#Tb)i6BU!K5jUGFE?n!W z1+N~+()2&@E5U+oN#{cH?oK*sqeRI!+acv|Cm=`V$>0v*=8n067;gqG#l{v+n*b)mz}OY<0zI?LoApoI88KV4iT8Q& zhTvE?d6WkJMkaVKs=!AedHXofzesuFPT9e?P@(&vo4wpxa~lIgrjfezYF*%7TtAj( z>EHo%XuEb5)*qK{9VE&KDL>a$iMD?u`2Q_Lvk@(x%NZ86vw%SAsOYC=>Z-cApJ)T5 z2t)rW3thcRh(}Gfv$zZjRtaEvc*;nevnPKd?k5s_HEE=^m^U6fJ@~x{pf{KltuAJ^ z1G(czaQ80cj|8ofO!T6eTZfzfI%Ig64)bWZo|qh0xHnW3YB{D5P!MH7t%ANKL*2Jm zY0HYre?}fSX_{b@7K1E>0#{$Lzp5Y161_>;= zD~+b&Up%J6Gd=InR-4(-{u)3Sr`_^PU4N^fabekvI-8l zI^jiCsL)XNk!2T%DuB$+Jyr|YZ#FU`m!x<9zC zi3c+P#Y@L=CAy zJug*51&0hSeI+6~e8Q7#T-%WrF4hBXcMIi$)rXKb9<5-`myWuDKd}(jxfX1(J8i*^ zHQm&z>+a7XX=7@IoEJ|M9-7DFJwt2Bw4N~w?XLd01*4NJCX)#Ty-e5QT03SzT$i(! zlE*u@P9s3!x`U-scfTOcHZb)9P-V3ikM)=m3WQ<4$ zB7&;`lbnIfj={p>Qgf^zIt9h7j9Qtr$%rZmWY>a7W48mEDk$-EL}3ZweUxP^6n{m^ z3+=?jWCGUE*I~GEx*gsn)rffti@Y+&*m=lpNu#Jfq4LLbQ>te;UhpXS+n!S+ltoIF zC6HB20d z1Xk)KCfvpaDGtF)2+#C@4N~RPkCse_75@RD4;nBjP&8@L2ZM=w)p?yUU8X%mN2z? z{F)C#?y*u&U@H!gsA@58aEVRoNO}c0nMdxh!XM^JX0v})hA4|WH`H=K5X_lc zMtfYD)=GQOxr$g^G9D4Ccjj^)`h*j1or%42xXy=19&v$67PcL&@GPT0n>U%g03zEV zV$)5rZ!{O}FTSORDC?zL4p$`uuRSqTS!7@Pke5uyAUDjvt7Jx~s#i$xm{!3u%EQbO zYm4*J%}`Lat$#Dt@L<&Aavk*7*^5BX?j+0`F6_p=w`2_TIS?XbDJ98VudO2q;y)L% z!q|3l&p=ja0}-Z((hPyS*Nxxl6T{7R&X;y`(2MQ zj!g_rm<_tF2;JUhz@nxR3x@!D2|AjkUR}KT2Wm62?&o>TJBWglYdUWTJJ(pu`_?JU za(j~b8zT6w2;S33K;U7l8EV62v7q%H83qYaGz-!e8qN4t7LbfM%<5eDk%m^bQ$gmR z3O86V*fnkCEsC_pJ(mKwY%8mM28YLIkAU37EuxswQ&(bWUU!~o(VAOH`{qioPWxh9 zAQMz!R{8uw0a zY&9@DgIci@YC}u7LbKi#{%A@&z}PSdy>9}9;ES}6h&y3BDkG9EPdFUpempAdu%xK> zD8oTlgy-H8Lo&h7BoLZD_zT`PJoIQcswz0Nhl{k2+?N|lceyUQ=1xejo9|b_XS8P% zu9*|Fgx|FDAE|#D%TEpztE}!A1L#=!#|xDI z;Ceoe19qCNOmnbl-H=5aMG(6D2#;vRk52jMaks+ajmayI~`U;M;SICLiQ?GFd%ch#t6F(zo_ZTOjWIh`cI zIt!vS;y?GjOZgv3KFb=Xy$pd@2VJ>Y>*0b0V+JDJ=dHGeYMCor7^zuVea%1`Pti=3 z;X+_lkvznX`hX0%kYjm3rX{2^1aK|kI$gUIUTA|}(>r1NjI~!0>c^I6>1e>!2d$WZ zgs2>A27ab2n3T-z73-jyfT_9s8Om~{V%Y_ZSikByKQAppxlX$W>h85C;`n>-{g+c< zU?4s)Vn)FTv#E;gOr4#75JA*XT!d(M;9%zq4zvO-hZf`Voe?6`H*k6P%V|)h zvG8=&YCqysc@_4bd1%uwq)8x_q`3aqV#|v}riA$zx@Vw~3!|@e)K1^o(dtG%1bzA2uvQ_)PjTV9k^Ue~dY0R5@b=dma zH_mfmxXwst9NrzRvY(iHJdT6cWYk(cc&w*ovcW?n?ITGE5Joi#6K(n3)n6888oE3s z>Mz=+&>maiLHgKGJ95O9 zyTde*cYV)PCfu@tBotx-LIz2PLP}ny?7r}pcSrvX6CtB;NZQE|LcSY|y+noRAH+(p zvuxm$6Aj~HA#HXZXue7sb_d#UId@FoP~*KpFHDlh4#k2_zlvDb=6e7~i4D;?1cpxj zFgqF;5LX#yRD8|7%hN{e-{34YPWB~qjzmAyUi4R6hh@rnZfGb7_?XvyPMPS-+4IlzS_<9iwjC9|2JGoU- za7}2hO(BAi$0hQOqXcf1*LWvWQ{U)DA00}jm+^ODeFaUXvber+PF$I-NLsUDfpLEuUZp?^XCT)b`tLPi}>kEG*l8(x^W zd{yfpDgK1k?30eEu8BGXsm`%g)|!yS4Tv^p*?N5d-f3=Kpaj2o&^3-FCF~nOlUQG_}2OTtf0(RINvQ~{q??> ze=*w~{YyV~#t;CUUNx}Gr>ES*-ks0#Ce(0A3%s+L&-tUs(_gNpa28Cy_C%r(KMh#x zJ?14IP;GzCWm!WK9mJyW9he!3v>d_*h-+ChO!qz0Uu#TY+~YzLm4ai}r|1Kc$;IWX zlfcH!WVxd0_to*>h*aK_ z13fmk!gs>-5XsFht)2YvST?qzh3N_>8ug@qjkfCL9f?Mw5D;&$`q~)s9o*^h?kAMctoe=2vY57 z)_m@|4Pa|dKSciq>D+FQlLV}3nx2u7kwm}rdn_d$V(Jo!bp4OxVMHUc`$FXZ#A1pA zbQyrV=U3WSu21i9i!5dwmjr|1pDj~$>y~!-rj%J5;?$@AjXII~{7h$ACZ#pjZd-!iT8uh@KZ$ahMc`WhWXirZ|o_l_mooX}?;d)gu1+F2O%&uWCvdh#a%TtuN<&rw8 z?~j@|V6UK{6$b;Yr-zH;uz7b{?{Wu>z(SJeHJC*s^WsJ4n8SBP_*y5&RLOz%B6V^dG7`ZSEDB49YV>KSX2e zf(+4C9|__SJm#{FS#l{8=N606h&Yg57pig?%Etu#lqo5qGJ1h?OL}0&LI_O2((#6B z55MLa18GyF4)|kESmzO}hkcjWji>Yt@l1>b>}wRSPx%ccfaJ5sWcDq`p^QHR6bV_) zq@G1tAaSp)O78>&%br0;G zhaVbce$;2%mDv|o!4Q!{eQ=oz}*75-Dt)f~)vC6ff)M@lixC!zheR)PK7y{u6-B&>?I&vW}A zGNMmsL=v<*+Z;|$pgj+c;0}6%q1g-(RzWCO{f(6YSpkn!hT|7P37k+Kiq`}>;#y9* z@7Fh6qi`&^i5WhkX>;LhglH$W1YYSnPbEZ+h1G^_wj(sKvw1yTBa zP^F;^z_B-9+1FC^?5wUOM-FWaVCCP9x_7FRB%T7L39xO zuHG@94X>0-x4b3+I8eICB(7@#83T+6uN;NX+ZDE|PRgI3c3)!%J@)O3?}Yi~)1NYw z4$7ay!H@EcXz-71?DJBi9N(+x6p=QK>CuY5ifvB}{GBS_{Oqu;=WtPypOy3Jn?3!1^RM%@PE>*=dy6Z;{b( zksCEiI_BhMC>6PO7~}%a?qThk*!p>D80_r$sc!>54LD)5+VWsyL=%sEol84@`G$>! zD61R|%?@~^%anZa@|vHHw8E2Q5O+T$w;`Zw5MH(%o6CMvo0oC_sSTd`EO;AW_z6{O zfOGE|jSwXj+~Wdp@J z$A|Ka+f64V0|Z@^Uatt^vs4&AK8utgNPLoSe_{E-UP#L15m3E2zfC!jcc z=v0br1c2C_yN~!54~}yB=kJ1f%|-;oFqvOy&%+)<%3y`M%A&v^Fa_*9BEd3-(`k;? za=id4SUy!~(Q0{aRwjtYZ=vi@Kk#IZ#)x^GA-0flOSIil`n0|p*t;ViEm2|@7xfR; zDdCODT4E6zd<)&G2nwUWn>#>8-u5oqkk%zv=k58QsU0-+IX%%`0{WVaXTw;bH&!-h znW20Ws{w<_gF*s8_FX!mlN@l2dL)?kv-9m4FP2ojL&%Y#!l=zkzVC{baO>iBXv~c@ z)We-W#{W7P)`t|2zJf3g*K&TEFaT!qy&pD&k|f?BnDVmufzsAXptdpETPgcRF?@^> zvjg{shebhU<(SxY<1;uWkLNu#wq|ZmJ%v3W+GOLM@FVqs*LUFULt*$!Y$TUTgz<*IZb!MOA_o1nR?rjno4Q;Xy!T5Qe zadj%4n+r<}?QVv6-1}M2p&|fha>Jkhc-=v$#9~=4cu^FwxC8PMumy;j65MEO^olJw zSvRLsZ$ZlR)_!QuN&O-gXiuPn(AP@ALZID3EtVM@Cu)Ln)D8cBsa-4v39DiEjm{b_ zt9^A^-_}u3p2QdO4w%{@q~CFGS@tbUhbwF|smP(zz!g3&9cp{sZrAe%0HMbh^=o>i z06_aWxLS-6r427uWKW=f{7aEZmAjX2>rfk@1TGN)EBt<1G#@fIrR8=0x_wUkAw8{Q zqgsJu_g)R8);*8ZKrf8zT3PUIVt1$=V$T5W4Rir?RpF&O1Gd}@!?9vTHZZ0sn_HVs z(u0Xj#NOvQA1R)!NL*kz>48W{Gdy9NMs&w6+w@1>eG1vKRUoog@G*$=K416#L{k`F z_MvRCfP%}>>&CxOzq*_<$ga@3O?c$yaBIS--rkMg7QfE6a<vno_kU#!C-vrz-~pTN6mVtD$dx7()pig43NonM(g|DU!Rj z8hbHBzt5PoPq6RE8YYAI8kkeYsDI^J*0R*qzRc^0hzfDOT?hJTupW9UH9}lysUNF< zRii=+d)xIty*7v`_Y{^Qw;qiv(`m8rXk_{5*Np0QLU5HXVgOaP+CWF1bP7T$XR!fL zGsr3N07$OJH~M7BNRa55 zS!+huoyf2xH;8X>UK=vLmp_^b-HYp84A(t7N@8*LIoAZKQ?a+hEz{5G2G3MLu8UNjB#^ z$!hylo)w`^=CGov2Eg?UDEGXXk^z3ll@tHO9a=ls3SL(NasO)kdXL%A^HDHzcR=f~ zS5SR8#fSTQ-+^aFU#6eQ=;HF&)2T`R2AQ$TVY6tNAp}&Ar3s zbl_fv8+tdQx+`ip(9ptvefPwk>yxD25!cJsJe=27X^6uy+i7R5use&!Sm_FYKCH;} zjeVx^N8m+S$`vIBXP%hM6{Y${X&+uK5SJk?6q{aH6BN3Q6GNL@tOyV9HlN0TdMxug zuO}Ckg6f80eoB>&Y8WQKLyIJoki(F?#YfYHUFbMU+Qh>xr6zc&5 z^jLKjuejlSoY+IxL$K!k@~8_&v1s~LDnZMBhktezb-75AcBIZ)i{a3NbK2n=iC&GH zK@cM`1C~OtbdYAFa$JM?nPurND8{$60XqhF)6}fSjWP5=hFa$sOC-qtt+uqX)_z<# zH7tZ~1(wcCR3gAdwuO7G`o{h2VGLCV%GlvJIfQB`(+&+Wy`r592nC(%k4l=9Pj=)#(dMmuMeJr8CDi9$HUA%{o4fxHKwq;`^!L zJ-dwTG~MC)wXwEJMH%Nx zJy0u48xB;)-qjg!?{)62K_Ej1kM*w%5>jZl6>9ViT)z5^I|&p?87>cNGba#23UI&j z|4#xmR%3-hU}2*>IRvx&vDTnG>(dgIf@L;=%cDc6W9Mr6RE;>rwobMTxOva2ImQ;+ zMz&ynne*~d|GjK@VEOA%13zpb+mXNY?bGOKIi%mxB6LfR7i*QWb^}lg1pwEs=GY8s zFo0oN#AEoB)_M{f5R9G`F)kFY!DGK}3{ntPJ3N)l;Mtx?eowrY@$9=d@D_2b6%aBR zZU(p#JNA)%9XLl)tNvzW){rG?CbOD>luN{Jbw*YFJsObpP>$hg85`4jNW#MVb(I}? zpV!j6K9dgR7}uVZ>v0-a30hJB&`Kl0eJq;sYX4Qm{lyZF`IUlAMZ$r4Ydd=)N)~+& zgVZmXwwteQLq=>)q7m!ZhHTmQdvk`=(Sr<%0Pj5_y(#?>>Xo7<{xSgkP75>f@ABI% zYeEF#>Im9URq#dO)u|u{+2&>x#?W!WtiXY7HBz8thUoxBK)Szubt_HzqqXEz1RO*{ zieELwYKG5jta;GS$3?UNanj@}r&;q1ti2izJ|W8r5xR29$ZBH@J2f0WfCg_*pOo{Q zXLsy$n1!>MPmo+HJar>^jlNO7-e=L{N&q#L#6UzTF)aI|{VWr6t}YE{z<6IN3Wa|R ztLRc+tvq@gxzcHzG1?x1Ct*5w19&4oW9kaabHFLt6{wgym|7A+>THt-B>AE9aGafw zlGwHLDSEi=0Y37Wh|36zDi2(A4ekjs4tX*-<|LXLiqZm6kCx7jO)p8s&J0SE96kl( z=^9suiW^mNG8Y^=5@BHIHdC1c8&;lb@$?-Fr02Gz{!Y+vpGNdw8~riiS;j~8rUN$F z3I4uJDq_cqxAoTu*0z9B(=_P*97y?sYw{C|Xj+6cf8l#Wa&Tx&=hVh|P;z!cZmKim zf&QfQF{!KX%^#qd;jK~IP9Ph?QXWQ5v5R!FQQ7cVn^_`D_$N}D1(Ry&Xwi!^U@@gk z#>Vt^jX(5i@kjR z8lI&_vIkCVPzZgQ!`Sz6ViA4YS3@~m z+`bJ=ah2Sa3dIr7O7k`6GG*oM@1O2Qlbz1+Yjw~^{7t|m#GZ6SktFa^Ma*n<1bjlx z#K|{IJ6MPxY($R$-p5yNSHdyyKb<=&xGIe%Um@1(F^NZoGcM&q7<4`g=QCR3w%YI} z5&M~eg&x#|IH-*nUzM}Z8ZpR|6}Cj&W8<`(V1o;2mo+Z53sM;NVnlVX@izf~k%oVY zB}Q078wLC@`|bf9RJg&WuAug*q+}MNw_1O^tVQrqiak>Rw%ScmJ;T_+Z1)LqEC@kK zx)?D8%}oe9o@B*aYUyGq$d3?0p!V`pl z*e5DYDu~1OhGRv6&9(!x{d$^bWZrN49t{z zWv&z}QvdJY^RM3k&Oukoj#XYv2e?gu*c5j39=pHKYJE z?i+nZ{_=&O_EJ;wMCKTqlnDDy=^-nXV^iAm%2yN98|N`=O$+r24nOZGh^u6Tk3zBN zqu0J*NP95S`0_~kBd5H8R)Y69jus5v`95G244D(XW)5w!_!OL(NHlj5@j9iMXDNdk zKbt1Yi-4dxzu|5#i8UQI8AW6iN#=8;ffTV-^NJl~?(99okeVP1UwoB0aFx<5f(Mkw z;%TBgvZp?R54+8SFdf^s#T9gwgLEN#g`LQ-K7}I=%Zgt5;G0&6!Y8<09E@O2!;}f# znN)NK4CjuN>hfh8j6qjbSkN3;>(NT=D{Kl&NmT*9fK0q+)J~nfJ>kK^mTT{cSY9`I z`6JVzO`2cVf*9(+jtWN&GDZeVH4YobVy!i3;k$%n2!CKNBK!qP+lIgbwDTr^oQpA= zR{(zv=aru#h@Q5`kYvF84KDzvR}x;xky}^=W37U2^?6+;BXxpXd^KlM=R(g+Lk9?{ zu%F1zzJJ&*iS^-eWaiyBumnkK|C~#ICt9WIzZF{ggE2Gf2RH#*ure01$m1Nv z${J_4y=U7@*gSF_=SavsY_#c_!b>ge-b~oKL~8;Wu-JnmYUJ-x{^!JCx4o%{yr1sK zKxCXJZd@C8S7!>lai6mPKHXd8q%}8`^>Da=XsErVG$*-0On5pj=!sBJWR56!=|p#& z6R!>k4c5a9?JW=vr2$H{x8Eg>xEaE|${3FANEhoPuKJK0$Qx}QF3hQgewh2gLk9F| zxV8~;+0Er2P3HTT#R~tfdrtj3fnd59vgI6O>HAmAl6_y%?)xdbIJ9e$a6L^ZaodVG z`5P2h20aqCN9j~KdXl;AmmVXYXoE*=;%T+;JPIncS=qx6`i}aK#mU<=ks9Y?D>v^` znUb_J#c7Ksfs5jUwK(V2;UUJ3$rw#;y8fAW3q5=Wj?Ciy=ip)xwQ-k)&9E29wiAM??gNb^(#N3%BIaDdwtY(AI-P0f`vS#as%-yENo2dTd$JSMp++zs+=t(Bb^x zpBjLoeDGW4?TJ0C_BPvs!#`EG9ZyHn3WN7j++l5~oeisKhQWC}%ANI8F2@<<_XD1j z+0>%Ud%0%;RPC;4Nzj^9ZZ`|8+nKh4!iL4$3`HuR%*SOu^KL$#dpDU(ww0Uccr7+m z6W1oA!!BL6&m zVh_9B5sI^%(l1HV^opKEs@l`{cl*4d>A4(+v|h@Lhx(1h1h&UC779DyO-*3su5G5> zH{I#Tyn65HX8p4!<`kYGstAa36%9ca)<3;ku_QBz;4bls#B7S$OxDVbpMX38pSDI2 z&i%CCg=)4tww@Vdi*PAY0qYu} zj2fLBNA~v|c2GJ0OLBd8HViSZ(cdcF!T_nZ074eP_PNMSKgKs8xk|MuKddArE33R` z%v2wWz+0(GWmE&Gkk$`~p(~Hn#OUb|=DBg-iDc1U<6it8M)~ zy`cbS%c4QbEp4KT9GkC3e>^S2X^XOL6EV_C$ zwr_%Gb}@8`ltgGC#<&A|=D5UJ$;aQP7#n#0)S@tpA1oj)6QJ~MjbAh4z2BV*$05O3 zMa0-mWPf5g8M8J;{c;+%nv^9{xSlTH*Q%KsZu@as0Acx?SOE) z4_+@7cGXQDRzuA9A<=9{sN?uj-*4L@d(PDifJ@;Go2#hTCAZ3O7>j>)%_BGtr%)Gu@qTWe1( zTIr8zw{`RQcPW>HKVo2MYc%_>AmSBOu2`smOKD!@j_hr}*o1r3Vh=t@7mOp;xq+X= zl1azorRabIMWY~C3p>H~waA#R#cpytmDmaBSdLGhuu|x}VM~z?VR>dD$d)-77~ZvT zZ$u7+PjYfLxmLq65GBnTkGFuOzp4;mHI;`3$s-b*6?J#H-QJWOHSc7p<7T^{0TG;F z@y?qPJe4yp@~vz6-w_(gxfawNyj2KK5{FA-t7Kv|Y>z3duj@Jsb>$sKD}$0C2F^vK z995S@m<~JvuoYu^=36~MggdAgIT3@>er2DTxDvOH7~lt#`#1~pis zZir%J_2HN)Tr8N6_X^^8V>#29ItH@alZkMpjbw(`s1Hq!P3MfHoD~O(W|hX&hE0;4 zycCt8Gc11+aW?PAI9&zkp}!D2Xp5WJ#dJv~Rv7B1`@^C)>j$o%2=`rE74T|?VW#U~ zF6APImGT8NL-8MbrD<_oV#K50#r^A*jVDy;5{qryDu6-7(VZ#KFMaPNJ6t@}=9o2(>&sRkl^ zvgzPl@)#r z%z}|BX$+C>NdRDB1AqSMG><^x5u0|MqbL6C8F?rup~6-~$~(wWBlh|IUUNu>XWrz& zR9M$6x0)@ortsmrpqO!){{p!;tTHLDjgu@*PB%u8(N6Vb^^I=!Wwr^N3h&(+B4uzm z(6Xh)6^<2WtXUri1_-cJQr3T(YvpS)P2-#Nj%rqJb`kusZABct-Z+Aql*@r9@ma;x zrBCXk)J*(Y&9*`F%|#qRI!QFs1zuv&1hoJuq{*z^IBR#THp6GjAo>Yu?EAQBf(>G| z=A{c2+5nw>ZDRH$Frhhffk25Sn($)_WwAjpFEiFOIAZ*1zihn!b@zetCMZtJ{A^1+;5>LCw2(qssZb3V^ zZZ0{gcLLvpoCUHw2Wq)*DA&ih1pZFm9^rqgCE_d{a7^~P_Qkc4T7AR+``i8zub_So zml&%$)JET_@V)3Hmx}DyZ8-?nvA+)vCO|Q$rOs|m`k5xdc&Kp2x2r{>u%kTlh|Ci9Hq#3vgfC!iubH)hUdBJh=+RTjCXwENVfCA=0OezW?T9e|0-G+a2GS z@4Z#JQk^;N$VVD*N|{Q(1v$OX(|_OdGgx>5aXnAC=1sOF8yCm~gUff#f}tjenES?N zK_u_>C?hp;!@PKP8!eM*I2Iqg)^oL^(8RRVrj~hg0m<;`P%X)~R!;~S>uLy@pY>mz z0A>yN9e|Xdi27{o{y~zXUuQI#eK{NNaMNyJI2?M2T!pYUaEJ?7J$sc~?n!qYI!f9W zuO&F#{t0H6aD!B&?>S49O7R&wu zH{--t`|KeB5>gklSEVXwoLiS~MXQt^41wfsj95@7vYoB|aAQ3N3-?BbHgi!7R$_}URz zfA7<0BeW%5okj}VF~K9lw5Sqj%FzM$uuFT(9YNqAH=v@GU%W=h1i6j>AWL5^!K+Xk`_wNV%BgYSz9kj+3j6Dt%!mT_pq?k>r|!(k%Q23q6=+XaCJ zicA>Zx9xh;y2qx%2Zw&%ZhrbuSV%T!&Oeb);*H@KGvBFC--_B&J>EIShpP-o2Wj35 zsMIAgZO4x`AMU>c(ren14k45GT!%&$egYE}2zMLjaAPj|wC#9)Z;!mDk!6b4(WO%l z!#o2SBW!?+HnD$^F5NS<0F_e{wO)LaD2{mA4>nw>^;+44KMG~~0UfwSV+B{qZG->1 zIq_q8HVEonblRV%JcK;(Ie-sS`P`!Tm9ysgNXY!8LpzF6cZmh09YdBjoX-(UDAh_0 z-i(xK(tFzxc%luG!Y7_bUrZ5?Al@}Gj(Y`U2Xc0b1lNw zG!WK2)wgOulYZX+%{vQwUyUIZGz`SIlOnO#!3!^vx>AXyJl0rEb=ePL5ld0yx1p!> za7>5|jbwE^HHAz?A$uX=ADf;I=XMm1bkVgihky_RL2Aw{b5VLpYjk1Wz+YB4_ODeD z!c2sU`7i}nCcK|84v0OFq1m-eqtG}`)GtK!L|DnjC_-&87o2m@Ql4n(j{ z$njNii)?6-Nz}^{R#(+)OcUR&EGj;r;B#;wKG;D{y}T4ma)|}^@;}sQCF{ykcac+u zwZVm)fs7WzCH3xR7#AINoT)^t*9bJg;#tta+T9Ib3EgQmV=SU_WxAj0K&o~~QgeuC zNGTET4Cias=8>OjWUD`<3LKn|?%k%;?n-I!Jbe5X&ib~wxw?Vo6<>{Tq6GRz3Qnld zBeVTxh(9%omn0&MasPWbw&q3({TdPOlELbMg8#4$qUAv?p}Up)&<6ie@OQ!HT2D|h85GqUDwMRe z+R$)*uHM|%1P=*&(S9=ZH0pH$+q0R~{EdVv-l)?F6Rtpw50)%=ZdIpsm|e4$b7Bzl z^Uuxz@dfMXOcQf3>T1Xofz}o&AL#XK9XPQ1_gsh|qW4Dism%6SuD`wP->>kGLbTJa z$K)`PCv9aqBTA*gcr=)i6H4W^QHQGPPAAu7uU3Uf0m`q!kjuklqfOTtynbrvID;d; z_V(MU@|1Rz3GQm=ZjC}#qocPZ*-Lo1PCtI#x8&{6Q1U90VIs#xnP6`JnxJXXO~%Dw zO1qEHQE`X?lD2z3$V;3<8y53w^$mdE<0N$IYzR^!bi!siPMG~YK&R6$duq-ha*yq;l$78R4aJeh@*kD`LKR(><#b)K|mMb-6EA zN_XX249jQ!?JH(iuY)|H!DynaOgoQI1JJf6b^*+Q74oGs_1hEiI`X%nKCGm0q)Skx zH*any1~%9oARVyOA8$0=;yVN83*pE9v=k368fJ}%IT>JWPd1)ewo6pb+JXiw7I`3E z>qzy@^6Sr3BS__h_bf{O3Shx5QZkZ^IG63Q;T^8atLan5=WxGEEjHyjeK0^%!TR7z zXAee>ugVnNdC`g(L?FhLcZ|po)=|w*24#7iQV(%w?EyWur9yA)ho-Ld02sdiJ3duV zGn-hFzJlHnY4{n;{-H<^O~x@99Xuf;4Z}2Ovp3j2t{;C7LQ#$0Jfe)I=*x=A$FIbF zZTFL9PUvn@RBi`~4D_&RT6Tm)f#yh1F0e`K#Kd0>guKI{s1Mn55h^7-AVX99^@IXl zEK6En2kJN+m*t2s(QdA>uBdn>riYb1sPeF{5Ib_mENdvLb|J7e_!2FzLGm-)VAfC% ze?Y1TKN1BYR$R?kHCIrN#=3qd?#GW|iO3NN20`svgp8@>KfS`VKATG^G?JkUR+0GX zZLz%a8kSBTNe|;+BX&NCXUI`y+Qjp}N)o)*+)n`$FzaqE2uVTx;8cOdG z^;1SzD1)XqU@`Ev^?V%A@`KrPaYD?&871^L$H#C zKR&?)fTJo%{`U#Osmg0oEKUYP-yS6I7jWAgExu6VMKtSFGMu*dKD>?CH2O=)* zB`|#RAW!MasOR5Z`=OF%D-%z-SH@in=dkMY4AKXSAqk!%*ba0spSYl+f5jQ`Lvazv0>tZ?GWpSZ=xa87{&eWCHf58fLcXMFV% z9Ve23b^8Gu7HxzY{}bqjI%RA6fBhRF0(KYijE=7^Bt^uCoVEFminG;FF41fhfP;a? zS#G!L*n|}Fck7UfCIk){uYPMtvWE^YAqT)rD8NVpJ2Yvmq*rtq{dYe?>JrE*1QRbB z<7gxvMvOEwtbPA8ZKXMdXdQ33b*wQIi-Y$?G+jKDbN6$f@uf=jo`tX>dVg)xHxW8E zd^<&EX5jzEX&HUQoxjSejH5#Kwp8IR@mkSutSM^q;Gpo&Y~P@ zifgs^HxTIkH-~cq&y_iFbYGh^0uyU<@w0RvvQqOtXhIreZSmUAN!jCp3;NeMfuFFu znNHNf@tq<4TTA;46?hh9!s1!sz+oG~SgkW9)ONy((Hes##F;EZtrDKg6OVfYZO<~o z>INca2!|jhmY&LZWD@RXL*6>XNDPT^Hd~rYABv3rW>c6Y>8nL9&$l_u!8vpQS#Q<2 zpsjth$2^|l{aX%AG)1Pr&5-hBE@-7Ihyxp*?;9394;@30PWXxj_N_>(|-@}PX=v6zTXtY|> zwnie_(;e{x^!7xcRl_(fssuw~>J2os2@e^f08u?xKmQf^K{$sh20 z{%Kw^owM(oHYZ#Lx&Ixf|I9EA!OOK-dI?td1=?3|3>5JU9`C7mVr%J` zkms^P729^x^bkt&>p^Vt;W4+=%hWVgIbQMF%=xS6^dP7I!R3b9*{Sv%$2|nwDSW#U zRnM^E7;8qD?3KAx9x3{wCsFAl1}1uwIqRo;>6H4nWRP?)Sl-vnav?Jd`QbeMdK=(S zTN|ZzJUR)%N#OYyaEC!$8QF9VYc8xs<=Xe#XI`ZUdu(VWaYcZ__Eg2{uWM`3wn=E_ zhFUceD=ros$to(90?O%tD9Ht`2}_Lp+!IfwH}m}-X_1DJGgTuyGC{KwAXmOJQKbexx*-pe5rA1ja+WxHHe4Fld_NJoJ3hc#Fp)AFQqWgoUk#JK>3FS?| z)|jCPSv^QwZ3fyzYC>M=7*MseoJ38!MmJ`0G;}CuX6~&Hx3~bKbO3Wvws$lgHL4sx zk>X>gLMoQY1~8^YT0~UV%O6A7?G$NjjJ_M>gvp zzwGa?{-fZVbsj>S0{KO7gmalhMXlzwBjXSg{m>aoRANjNb?i!!{G&xzYIU+~*-^`k zb~?)~#Z*!tyx)V+nB^@!_1yU>BB^dlseJhK+@}m;D{J8CcCuAw!UQy{?(4G8GSh!c zBf{u($wVy&MToV*GD?T0n(>7CI_y6om5IUg;PXiNF5}Bl&O+*Oz49f)sT*BvVPC{C zz^)Q@L#Pn3#`5U%mUx%gZcmUx*Sfjyhlr>f``2?GdU<*d<=DGqVyxob;;!f##kg|( z!tECu1~NK9fasBv^I`JQq-_lXza|69LA5#t?SrnEO(PR zNv6&<>eTr}FrU09i0dV}(%=W0N#P|zHEv|8Kr;$CV?4`7`WPproEM)i36MmNCDaXT zoCvO{VK<7fFy?DjkKAA`%@rP0O6MdsIdbW<5v?1`E$_y6qpHEgm-!8)*n9!U7vxDp zH};pAfJU*Rdx-hzpQbwJI(u?o|$63`@jG1U-P9oDdV$R|KOTf>L*-`N(jpoCFYXh zl+x0v{8)sKYa>%wuX2R1#Ntc6zob?frFGoO7w*ZZ#?KY3csRP}szk<8iAb`c!^Kzh zsn)ww5tI^=BeDVFH%Gs}PaMPS}!-^Bg%Oy^hbF4ukt+^Gq_9 zqK7n2RtBx+1k=3~VZms3L_p8~WU4reF&xTM=>rI>wl06!k|=+wv7w_yTz7l#&3O%X zLimJ6xz^^j%J>$UD}YQE z+kcNfh*jrcP`SOkXN-1Z&m`~3`$~H#w{Y8TK9d)eB?5IL0yCei(U0182Ik?Dk4$YN zKQ36!)Da1Hkg;iw33Hh0{Qyxr@RPMja;{uP6pqi#Wshx3Z(?AvRBPYzPxHLxgBm7l zF*&L%-+89q#w?u!H-!=SVA8>OtSB2_`rdF7lS@B{LyH}cJk&Wutcn?s5hAx3qI#H< zqXTmf=nCJHyeu9CPi4p>gPrLOdNG|QK+1G3cO%&_@OFs8{wYBnF=Uck<>Nvat^jlT zq=^#aChbh~|C&$YvgO>O&MupA7Xj8ZzB~{y{z=a$DI8o0PTf+x3IjPl9ZR>2sNqRi zs1$5Z?y%cVBxy4Ci`9P6rY@+Ij#VpnimHwTB7ACgcvTXS(OdID`KDeG`3RmBaXARnfHXa$h&ZF#e z`49lgIHG~&-cN>|yq1N&D}eB0R&U?aazglF%cC@hFG6&t52WfIUXY=XOt2#!knp!z zq2^{EB@6d@__-J{zDiQSW>^I?0hKC3yX394gt>!4#viRE7`7h?8&U3(?&t7hlfd#cWLun!Ftmn zsc>}ONJ^C)eKP(Fe(KmuAUxA2-Nf)J&7a&8)=sf$8R;k5W;y|k#A1_kyd$9WwUx&Z zoHzT7gf0vDl4Ck}W}M;|q|PL8DEv`K1||W!-BoJp##HSNwvrbnGY0Ho`5G|Ny4w%D zr|l7^t8#uEXaaS**{FH@!dwFl@;7t(UruOhu+!|26i^&>W1A&iVSTuwd}H~HOfUR_ ztFT~5P>x50b^$m8hLfe<3u%)RP3}&XHV#}e@ieK?QHaV(L!({<-;)6F<+MuPnB96h z{DT06e`KL3unOu#q%RJa4oi`DW2#)|A~7Q1QG$sm{JB2g!Ow1U0Kch+VO$bvf)%!` zJ8F-m}{;KUt@CT0;vzF63p` ziV0cVX(*sEf+@(%tqtUHk=2H$vl3d2q>LCv9%7khMlwIeMCZ{%hqL|r85iaGsvlQC z%K?p96oahUQZ%qIA#tHM=C1^+Rj{dlXsQVPuCi&IyVd++Su10-Z24rU& z9kj1*+5EX~8lCwAaFebH&neCTB1#SfhW4_8Mw&_=js(UXabPrX z+7wd3!|Wb?iz&bV?h{6h37g?V(KqPIoOb#@rChI7W z>Ddkg6=uCKm(Mo-ZQB6Xa``@CK|$a)xH8+!XXK?>>xhu7F0olDl#&|IvtOcrGy+Qd zd6NeStvI-NUeEGoxdk>`wM;VG%8&ES2!ru3v&VDPp!vDDE(kzy*RHMNj78ru!4(`= zZv7KRBxSDh?9m!9or;WuQJ)XXp5tlD^pak_ya@OThE`n8zN3!9RCX-(^I@;A=$dRA zz6rH<3{61QVGm6?LOLtplm8b(-M*r%n`I7bZ1m+7;1U%ITe_^#HK9@RsOkvXFkW6j zq$SB-FkVAPNU*}X6uKO+fcd`9bhC(CU@>DtNa&ucfVG_IVB6jmPNn4G-o=$Nwk$mF zpOcxZR2P)HP713rd=OS$V@NfgV#umIn)+Ok86e)ZsvCUmhoau@ITxCjZe1>qkqN2#9g()2l$)!40XI4Su2(=HEDgBr)b2^o(J^0 zLj*!}YY4>57Nd-UD2c~2o2EXL#0sIYhndTRmMK#spkXf=z)9}nxoual5+CM{>b6)s zl7`J_Xg4Ybii^IWOCIVCVu*D|z>|!Jzjpc)6thpL`y&WbzoIE=8C&-4YEKQ9FAxFO zmg#NzyEI;Sdq2LJ8kmD%B~?+2BPtp(gmWXhvKE7kx>gF}(#@Yow+2=Ao#ue8?V>R= z<;O3uR@<`Oi}C8>fASIdraFpgY_%VKnj~(c@GZkA`c$C}e7EB1C0*;Rn2h^+F6*wO*o7DV}*UJF<%M4C|kH72eW6O+cVby5hd$ZVEelxasD_uOSMh9B4GTiot!oQ z|IpFVM85_D9m}o&MlGi6Eu^#-?#;K1bs}cG+#QiyF?$f)2PBWMiA1=1N#_GmU)=$& z2}^u-Pp|F#ihLS?@J$H`neqMC|Cr9>Vls*CKu3}?m6XEag}XL%f-+DiT;J0hKNt0Z zzU>U^txgFcN24tCXFJ2=&tkLYbMA4Ry}I>x_1dJ)H?!k`+a?&Ks~WtNg$^$c%QB7E z|FAhwW}87i2A{?_S-+2$f$VT}FgmYG#zK81NWkUHIan4h=s9h)*)1gYxz&NBAOX4! zqwv}YyLznE@0qGtt8m4H$ylFjp2cIQ&ag@b&3t`N`uh@UG|-q3Sp{%JeYA8jsbh~= zn2)PMQnz)`;WhP|^rH+5gR(TN{52lCIfJPoP=Pl5K8yy z_}a$QmroS(toZDo7OLf1WrFSN+*M zt7->v(33U}fUQ5LBEud6mC!@^9z83m>4|_jKV#6Bc4FDD-*TS!nk3}aKv zCS*7P-7CUvHL+O>vk4m@RzVt<4Ne*EMQUF(EkTtYl=~W)pz#unhiJ+l9-(MYRR@>` z5gEVo=tk<^2ATlNW{XfsyV5$S>DEkXfhUa23I(16r#`2F;*JrQxeHKPCD?;#xrsa+ zF9e9gK7qrx)}qu?F7jyS@aX`v-!b}G30yCoFqWEJF&{Qm#OqW#8HEglM8OnK79=ns zQ+!?;fzVv)11IsS7*3b9!#NtWaSUWJsrXQQx!rT~NZfs_owyI4lSv=Cd#-81eevk$ z1YaX|G%s8p1RfL3iDT*v#Ab@oK9tZw!QmAuC@PUiB_7n8KhxE}io}y0Nx$l#kz0|`9WBD#nEBfqLn(LgbFnB%;C?O9`r|+sjwT))* zheGBO<*Ab_o=sDw(WY7&2q=gwMl(pYi1?KM?s%pEC9XzcT6i!Lg;3P-(>CyW-b3gj zvN8|XYzIt@aLLA&L1mf=-)1L;yN&CT+ywD~=(A@lEzl<=03cNwIt1JdddKex;ryF9 zL-m^OZ{Ue0hpxy;%>v>6JrRaa2eU`hF(!GvetXfc(|3DuZ8o@0Y5K(SC5?;6L%ubb zK*O5Pb~v!I79mJsZ6So*ep=D21w9rt<2gS(OYsyjjF}$x^UQ!hpiXF)QchQFU1QN} zd5u?-LjX5d{Qa2!Xj5?A*>iyGRWZ%mMlYq?QTOdaIjt4DF3Bpi$ck&1sujD0VOdA+ zhXcb`MdADm6iOJzJu(5ZA!Y^0Qk)N3XOE&5mbnpQ5eSr_?)|OrKW0emE=l#W_5>OV z`ESQPq~wt&T-mB`$4i@~E~gQ7#b@7c3#kSV5EBS0V$0V}Oa5cT2b%wx#i1AZN@lXR z5vam^K)SVkINldVpm2`rBqf-dhTEvT>lzMAW7Z3Z?)R0&e-}vXxA9*9KE@{ai?h*H z7Gv|_i#|1IXkNV+l0J+x`mKl$sKqj(GlV*wl>(qCnq*y{A+DLSkCa*TXhJV(VHcsc zOX!%izim6}OgF})zqiMUzSyi^I>TJ8AR0M(EaU}Vq|f7(X*<#-Yby~3CXrPD9$@e? zV?NV)U)X#i3c3cT;Z8jThv*?uYgfkzS-Fe>r&)LXHKbEeTW2+q&BtTG(%-#Gs#-)Y zT;qb%=6vqdMmSn&Cu<4tE?q=BYOzScnX^@o0fis0v24Z+a+Xde z(ScVS?wW))bvm&Ie6xq*l?hi`mvnvcr#j~$Wk#|`D|yc|I!DKX^#nd7&nx*H2M-w7 zCV9^LFn_>9K%x>NLEgzYQrFpv4(7~XG3Sb{#VA^6`tMdIJEM;pv+A%+!09Gza@do* zt{QCFniF$QsZp+TA>u{{MsC$aWOqXIG-yq*B%)1h#@MIP_!nk{nHX9%Dsiz?r>4ha z%SSd44e`TJ+hLs!(2Qy8Q+PWfC}N)2C1d&qnuQKwo+zc~Aw6CIrTB>oX;t7g>xg?= z6WHFDXA?WxVFzVxatVYL{EH6Zg4aSuJ91%vuo)SmE~dT_i>TXp6W;3Occ!$6N&=La zV^&c=LIRC{UHT>kDmE&zOnt!AyE2#9h)t)?@Mg?Id1$Q#8rawfEk@G1w_{PIUTA zH_MGfwE6jS&u|4g<+2;<1n9)12_ubaAiz@u+<^qrHlLq)STHl>GOQ&{5%A6BT4-!< z4N4c_HDRqXP?Y9*W=>v&a@qN26nGbp)$mk|P{T3S z)St&;VDq#6CZOp-bR$o{NPm|oyWW{Q32;-0bU4*kxLSgKZ}drl|s=7g$&D`Cl65lb#=gQzKFEL{=fd6DE> zDv<04nNje;fQqyzK>*gy8;h9CH8Cg~G2)ku(4|t(fLfmU979UQr~pfVVU4Y3Lf2&O zLOrpL6p$gctr7qg9JIG!oRIo>bIo+a^Dd-plZAuraAwnFe|j8StNDQwD0Xe1J2Ajy z8BfF|f~ryxpY3s}fxXy%_R(r9o++3Z=KOoMcMj7WM++wNiIunMMGq$``LNH(6U3FX zCtFR>Az5rBwO?2^U5!&)WY^&sEM`ne=)ChPg7@M!HKp{cW%77MmC z0r>0Av~egawo4V^j6L*}&m8BB)Ix$WoXEF|h4{&7*wB56;2m1uT`a0}QrB>0^f4KS zlMD}|pBHUw#_W{}bc6!C-mfWIfHf|N+=i0Bo9Dtz@_@z34je3X-ab*Lwczh_8eTI) zWf7*3M+?KGp!R?gTZ9n^1HKmbE2PLho|{MEfesA-hzvmXq`xd@m6Sd>hpzq^&|Y<3 z>5oO31I`0DSw5sS^MWT@J>D-tq`HH{<*0R0O@rj1<5DF(+Aj^rqm$*9L<8c*9a>ci z@nX;oz}0t+)RzqrH*^6Khx|bI+~w(rO8$xsDgaYPEGN-Iuv+`Li>Y2PcsV84)EA!Q z{AMYH*~wUCVJ6qy6W+1tZ2nyRSFChmINV!7d=(l%+m=~Y5G9nLPbV-$NR@EX{xdvK{PYf&9)B=ipAa{_qJPi>8gIzf4K1pIh$Lb%LfRqw?eI)dh(fx9#zT0BT zbM1rOfYBh(jPK79xnQ}lQlnLEGMSQPtkHjHbEi`&SOzpKu^h@MqVZHVw1x^^O)45v z*){ulc=`8W&?XV8WH^*wG^q`97c9qs^6Xj#zn9jQ2hp%Sy?VdP?;m-{1xYnX(Cc+r z8t)2pmsVuJn!P+bjNulET%NWfP;IEunjP#!p}8i+3NiM|Vbaqm!}>v%FJ;a#o=yM= z*K&z4np!9tS&*Vl;>DjBc*~%UCm)_N`z@CTz_ok&QEJ=6u50JL8d2(6)N|>wrfp;H zEK{3QA>)3yb-}xzIc2@LhZxSax`4vSnKL^e7auw}7!9Y$@CBbe+3+xmAdONJN*tBb zK^Mqy=>6EZhluph(E7FPwWO*cgo{1_?C~j7i~>X?1wA3+hM_6MeoPTZK&8B(HpDoH z42z_QLq!E`&dSuvQ27PuiAXepn^m_H=md&41y-^V9mES^=C$@L92q=!R=DuE@aD^6m0xc>*0Cvs#E84?@H+tiw(@(WKfV%@S+ zgFuShW#vIp0h7LQ@_M?@I$%POam55Gb6CS%RWa;BCS@Hbn&DA#&ih4%B?nRb_v}t#SC;RAW zvvgDi30Zl)XXumv5B^v0*<<_s%{V}5uKnCn-9Wi@b$M$jr<2S@AvQ^daZd=0h|y$5 zP$;!(f?U58*T+G%3I~h9w1G)xfo1WoE8ZUKz zSu7w#E(8-bz=F#LHn|lHfw*rO+MMuAr<1sgV$tTQH#VJm;TI2MXdylmKSBX%PO=y?SFoRfs@nNFI~p_m9^Wj(c@+<2zso zjRS)zS#p~Nn7hc%>X`xY2^l832Gbr;9ZA1N{k#|33O!zT*?^^#excc-Qb)$#d&0}?+(|&^u~wwo z7;I%0&0dpu2em_N`@Uy+W7zNj5jH}4*P9pu1`7>3YpN(uP_#s>cDghRm~nY;P0-8) zI7NJkWwqRP2pfheKLem>lk})GCN_F?5?R6TMK(8T{F9$u^KDgBAt}0OmdJYmRwi;H zuGGvy?xbw`nI)c!asd#cz$(L(o3#Okt(O>3z9W?iie~J`;IUYeiapWvpI%F*(kjfy z5zDuHS0JarGe-@n0Rf1`S=ra?;};0w#)$zHA_T@Fw<(Nq(~vIM&Z_451{Xv@!B=9) zw`cDA!T+VdjB24cXI_fs4&X!o4j>@K=gfjEaA=|uX=QOVYIrB)WJi&(z8AMZXY}jC zh}elus&WrMcB-_HK0ZXBWQiy2WRe_)zweA&1fk?!P)^C|ls0-{=0N< z(!Eg@{oq9eumF3dTM$<7&acE7cRY?n8cAv!urxzC80EAb*%UIO;CHc^mBpl8-7+>{ z*+ztBR)WIL;RzNhnV6PSviF>9ODo{{MFhQ;54XYPg_u01&Yv>mlWx<55Fxh1JXv@l zU8QFArK_M%>t#}2Pj;OHE(T-7{AKFulm8T>=}ZjF26 ziRsTciNPgPMi4#8q6QL4Fj97kW+0)-`fJYE<O>8;f2~L-;Al#gtNb-e z)2k1f^a-E z|DPgLhpbpLA9zeoCFPwufp_t(5FG$5MeaghT{^CUW~~OsVjetmSf-g9SgpXRbT-m& zyB<<%WZGb1p;b{MqV*eqU4lS9JzBw%SfZ5^pQ60(;qMx=`cssJo$xi(_~3^rRdnC)b{`e4z<(FSjYh zmngP{Ga?AZq5#`y#6*AEIJV}_uSk?i{I4)%%FIS~iva0c1rw}G6c}B3j>##)sMFB8 z#2hHf{0Wj-0AoO$zYz_N^s=Z~fL|JAYbSzVD)Q+L1~7?=EL67Wlg1j9Q8C3kO?B5_ zSk|cGOP%qPRjdMMqH-e>P8+Z21U>0C8^+9=%e+tB*}=xaIk@!&B& z&QNWnSs*BM!}lk;#D1l1qwuzC8`BThn;O1^x*i$|3LMfpEoR9M!^) z^}TUurmE;-!A4hRNo##f)po4m<85g!|{8Lk9iVidseNp{eI{`XE0i(GpS z3LQv|*xSYr%P;};!hgSJQo~Us4DZ~64)n2>{Ai#|Lvxz@kK^PgdVuCl#~USl|jna zLOnbQYu%70&P@MMEY;zq4lm`OIW^EuAEGo@k}+TGVBjIP3q<|MC@V2rsVjvyFHX9J zkGNyn3^%oWr6+M_BLt_mggdOG`bmFe2bWQS1U}F22UDvpIGx6X6)QE(W*1D8$zho4 zIcP_UW2x(;C$LINEk{p5AX{@9v zSXh9#L>ZV_WreG43hiaLTW50-Q~0p(%>s&@xR|*6RVQ!9P-AvU%OS6xn+-|E@%Z-` zA8$xChx07nGNtDGELMjKsBkzbkyqZV>B&Mv7#=4=BoxrvEy#9s0?0g#e8hudA3!8) zkqVUzo#?7Vn4@V2oGWgyRvQodM~b$jQ)EEQ{4BeXS_Z%DA)-G*A;5FX!iE_^dPPGi zBg&t~UP8|oQ^%l!Qh};qKJZoD1c!WPl1`g6Pm;*^-DzVFHfIPfm=EhB#F&5`a7@{d zB+ibu?eZrRteL}RE)Xwj@_G=6!)5g7b$-*CGe9OnCiECfWdv7FYe$?Ciev}%;+)4? zx-e2Et0+!m#DySZ{0@lJ>ZDo~3fb6~mr!D?7|II@4L_9N%9J{B%-|jn$!*>`Hx>)= zAeKlFmumZLcA?_hn)UTOMCQyoIyCb6U~GS3RU_ssFg6SNc&Dn1Z7P{~R4CzO28~ce zdwoF<=|fBG0dG{@<%MWTaXO;o^q#Y{XOKwEET4xG0K#1Hs8=Ra*YseF93C2!k#QO- zabG;gM!k&_%Y|&V(1WRHqse=)6ma$^0 zl^{N_mz43e>yWQunVz=)3_W7|5%;TbkA|R{l9omZBRuOx(t=n{b|WQ)m!4&GWY+K4 zG4MM?OvZB>YEfgk#UqFBt_)URC$>bd`+;f8lPglonSKQw1eImz=01XzLs?C%nK6J3 zD5(i{|MIdcHo;|xY7soO0U2#rs^ptm0VFsV%2;$l$Bj|arH8gfoDh{1?rynTROXtN zz$PQoO5A#8hnQEUp<+~>A7x-7Gy7P2jloo(t>EfMhp^*8bYmuQIG|UHw+{-!Qx)898z}7Y4tMl`|GHguWrZ#940!o zZVokiIwg1H!{gZGOZNdc>#6vWkb8h8!q=*@RH$FT3({Ci)jdL>&ucPQLbM<%9r{eM z^-2P_bNoAn499Aeo>na9A$_}$QJE0u5`vh=i~Qs0!Q-ie?`+V$JB5d-Y?590+4%tF zj+}K2p5x_2HLI&%tnx5ry|1WrogA5}$Af7dl@&@|`Gbm2eNv;5dYG_QvTjcT-HJm2 zW#U$yI=-wlu25!i5sx4=(fl&eKv?HPy5n#`mx*GK`)zTOS3H_L8YU#Pv&yodC3I78 zy?#iZIEy&l=b&jgujsgi=m}6D2H}7vKM{~8u{nh`P)!S$fFx8VP3N_&r|p{Np0Otj zZh=>Rdmbe`0SXpIwlA|X+KOS+p@yo*~Q()7`m%)P#U!m z9(j*o!37DnlW~$g#Pvv?-e)v(@H8nIjUzKk^MGE$R&wQmka*yHt;jse*jcbBDstMa za33U&ednkyH^^kxl7&4+ZSq5nL-J_4gcRM9OKG8_ptP&Ob6>gRH>=hlGE6ZalO)#4 zfqkWqO?u5V#s!U*)J?V^s+A|LY4u6D-WHhOWU#Iwk^af*df7mgbUHtRo*F3_hj1{c zqbeT4=+|CieHcgM)1o@3HjOnlYHC>>Xqrz;@1yVp@!5pAtdFBalkALMl)`xph5{Nk zPkv=Cv%qB;e?i5VHl>tmw7|{?r;`tf)sV3{4drxObV6qzmB1>_LkhBJMm9b4$I$P- zSr<#f$Z<7#OD1zxRBl#He-#p!gF!k(Hlv|g8Qh)NjZTfos;e_tWpRdPigY3i)n3;u zIY-fDJQWFvNo`p?L*%pJA{_z=VxZ7NsJJtOjDm=vrKz!loatD`YI74Ogh1G7WfFxe zhfSRF#ic5~FbHrF_5&&dei|zPW~d!Wp{!08`+)>M4;pz(! zcv#?YNRblxmRVLp>9aDr3fZ1KAqO=y5NZsoZj9gC1G57)-{Hsy=%^O%+0gMHEY#xA z!3_j0aZ7MSgZw0DQ65Dgm4L182_`PB&>?&e$GVRx>tNLsq|#TFXT$;xnI|ZX8o*2{ z)JgxbE%N%&GPgIO60K1D2`c)L*R{%R?KtSoEO;VXMw&ekLiJMWG%}rE1ZpsWY2LHu z_v&*=aCdwUR;3!4N}kHuq7f~R{8PH)?a%{*91!4vt=ynQLNB^>a{5$O%xxG5>?Rc` zCwwMrBTrX5Eq?Y9u#!a%EqYv-7BrF-yI`X|+T)QjsAKiKnP}J)vgGqZGyzi-P4fPu z6JRHI_^O=>PG&*UOhoacqc0Yp665HXSX`#S;?o)gq`X9?0(XQ&iUZ9&#-ldT^jJ59 z_#agDEXj&!NA@^i7Ot^LHF2{&LM40tnQCV##};^iHfhE4n?u^%Q^$*_kF`d>V8Ws;>b^K8Lv(1}re+{YcL&#AC^u2pI}{n~=)^ za}uadGDM>ok_6;=c0sPisX0=iHSi>!$Ze~LF|3U8K^2hjiXR;-`Z$i&x-wN0g489B z5*G5%n>lZ~*FrMA5*0C=01eN*HI*s*DF~}f7g1O*SOq>l=&&rn#L)^JqRdJaR>gzA zDz^c~Y;vTc$Tx}@k?sr;O3)W`Qyfd;)bH6<_eGmg=(neXqEMEDXX{~dY8(h*uJe=) z92SWV<`xSk64~Toz*Q*Lq|6KoZ_#o20m)=C3yeS_Z<2&Z?iTw3nY)_sLu+JdmZx<` z7Bkq1k*W#M`6m9{7NodV4njh|Pbrk5E}@VYXZ>1=J+!cCa%5bX6=>DR8REX(8j8%s zq6U%{gie#3v18IK)6SGxlcr+Dz~GLSNS2|L)G%e)+#{qkc-4Q-UPIYh=6-X8=#h|8 zX%NjM(yqMdlKTXUH?Dq#-Y8-xgm?jP+?vB>^r;}+kIn_A4h@@5q{2?;6BzKgfdsdX zM&{_5tUk_*1Z2w7y{SyzMxeIFohHH$Mv+-zK8M7~F`jp6G4`WylIX;GFy#l5q(zb2 z?y|Vr7uv{J0sWvW(TLvDt-4|=K}IiN#}0Ppv!wEEKhP*Xz?f1Rb&rU_X0b@7JftJJ z-%|MT)X^E3W@~x_N*;S2={ryTxB>w2a*gVh8tR$U4lG1CD{~_1+p_H^gd@^z(!7J_ z9fZCdNjZ^PiCeMn#hOrZV~=+!kAXPKmN6Zq{CG&WVG#!IK1$Zaqe2qPkEM0A8M7Q= z9#xtA`4ETWB)vC9&D7~J6D7NJGPrZ4wAs)FZiF4@SC-+mdueuD?3|3m^6kzi?y{}dDXIicd3%}xO!!CDVuQi#A{YeG=}r7?peRP+eqNi`cB(37 zIVC-jjt@IH=)<%h0_f@Rc`aaK4;pOIyFJt5sxYY-%=s$WCWIx!#zy1a6dP4%pv*>P zUse`nI_J`PGBKjVIqw-y2LzX>idF<9%IW4uGkkwOl5|3r-7&~a4?dtwDNfCE^}vpc z8K-BP>`dM6L}EZWpXs2q_Um?A+orVJ_7@wwo(=cEr-OIX;rsb;vF`7k7A=jM?X$Dx z&!5b`d^CSNS@nB7qQGYubF3yLmN2A5%$>MUfIO(r@ee`{@&z#@9fgs+2Y~}_ZUF0hZQy&y77MigU zh(J`JNfvy`M8y>wbV~bHoQhQ5Icf+5{aZ2=Qa)O19Qxo4VKDy*RIHdtgt{vfo*6yu zyJt-Tz7h~Ni7s@P(O}}N@Wbg+I|Bk}<<5;JjQ+ejlrn`(iftPJ7d!cRqpB*&`P8W_ z%~hn2fCo4P1tg$eU`7fDXuO}Kr+1}t1cRBkS8n~en`O8d(!x{ABgbD=Pb#lsos)~P zU6v*BMyzy}mPcqHW1SQ^rY70s+EP&kv(-Y~J7G%qXLUTQH5RGHK8VwvN-=ZuoYT_STl0P^)ez}2*Z{x%L-_Q^WrJq|p&`J?4|aHgEZ zLxx31$2vuPJ+1h7AXSh6hG1+;0)s&hh^sQ`8fZW%l11A>3TE-zFC(nzw!p5*^%JOT zf-+0oq#jIEE%Os=UcTud1T;>}s0kHdG?)Q|I?^q_0Kp=slXK^-kE3)2o{}*=uX`*}C_BHhe!DTu+DZX2aV>TeaKiXmdJPpA7cJ`k=S#8+tvK z`Lxx4Z?PG)b^mJryFXhGX6t z*y__Q7ds$Tvbc;bCtdF29k^-zLD(#1s8mc|x>OkFHgqQ^`G0UAkRF2efL1a$Jv8@c zB2fqO77P!IJqx@>Ku(%fYo4h3E7?4O|w$GzcPkzw=gULX+mLnjxqV_Lb#zAt;_HPabDro{l78yjqc=Sa70d z584wlsD;BZ?A4Lo>MaGmx$$_z!mPIgY>*1+Bxsah0%PIta;pdn05iLK@Fq-H7sSvh z74t4x7>5|mm`lPUwLCG&!*5G_{kyLaxRde1(NJMnL^oBnhx4gDOl(LE7V-@XGRW?4?gIGh3HQ&H?LG;u_8l_ z%8nXG2}XU0>v%jM3ZCr?fp06KwCV#0CFmsebnf~Sm~tbE35$0!G*fVn98(~v-1nWX zE4k5)NkAS|Bnd?}0B2To^a-RzkX4iV5gOUseExid`q+ikmPiGRQ;twrP@-)x5}<;q3RfgP#rTf<{XL zj(uB>Z=J z-Y}nzG9A9!dy92sk$Fdg`rHt#O{%6 zYxj3|!*6dU-@hNfoetYhV7lzLwfJJwyT|_1)3fE5kLF)q&7MqFC&SHI+YAi1Mjc&S z_usBguji-Ni}Ux3Nn7r>4M5ultTw$3`kSq7cD>cM*R~nh9ZP=)N3m^Ng2qmo{%+7) z5Bu9ue|DOns9pHC!@B&q_{{v0EMPLj$2x!i4-EkYR!kXdW*u}S=4S}Wk)Wgr^g&z! z-k6ZNw0a;1(TKWnh3FD8(FE@dK|iR84=MQmMNb-w1W7Wmv!gMOxI1LZkwqR_o)84p zI1)_2csVh;)dY(s;lwwo(EbZ2E+2Q~fbmR$BxVO<#yqN2bfRK7bHdq4te8g-Ue>T( zu)&R29T7JdxGS5fgRIK}3s4?h8uh5)DJjRzDN4aON>(3a-f_g(Iq;ELMO0i{rc=nI z;lb+PX#7ZJ5s!o{QN^hnfQemf?3WD>1e6|nAaqH9`7SU8+bU$&uwVq43*NH6HNshb zaV{n9Kh{NdzzY)41W&wSI%PO4zNH$XN(3T;G|W>W$gL~TNl>PEkW~f}j|#XCbSW2cbL;h%m|<`k6LglmJv%zPhw?s1Wukzlmq|{88$9^H5U7`wP!}SDZq>Q zI@ut@bU>o5Sn5Q>owU*-kxiyU-P0jFg3QBVVipe}jJ_eIz;>1$0&MDoFi^>An`29g z&cFpS0~JDWM{w+d?WwLpSA1It+B7}aYn54uJSN^Vve2dO_+<)8nuXn)ssrhtG6;zL ziTqL<5>hGMgR@|D0AeMDN4ymY^5HN@kliP_BLOic(RoDO1V5Q%l|#wwPBQAYv5)iS zcG+CdhOcf%-@c#xaC6c&0LxA9h^qE*FB#* z-?T3>X&ZodhX!Cio~{O)-To5pq}*@+41cqKvEA*G1 zGD$FI#ZiIIxu>vK|HuDs(!i9ws~S^_s%RsZMvYaA-Am_yC>oqVEZ}np4@0D}(nB1s z^T91lLY5L(6g{f4!D4OX*U?F_D!x=JOBD7@Mo)O`*u0`cCo|+u8*;rJ+4rmESC>H~ z{y`AwL|9>E3`VJjAo7wB;!0YNJ85)emwG6N;FcBI2o!_kS55(+s{>3a4Cd%8AviRJ zEm9Lry>K+1H-Tm{iTPtxWYVE#8G11Lo17FRUf7nofHkR1Vi`uEd$6(=F9C7Q2}Xq( z?S8sMeUG}qoUIUIUZ5{$M1C`r{c3d-68DSl!U&7bDjEnY2jEDB@4s#mTmdB!;Q)gB z1bvbfNw_H3x8%T-#btbkm{HLv&b6I;c_PL>mwYH^{HV|uWpr5mE^UbeiGyjnMBb|a zTuHu|)M1bzzFG9w=mye}ne99vqz9}KRJ>swmkAH5K5*=IK>Vt_$I_*Yd6uHD2VAbN~ zoy@XBkb$^E)=jPx1QB5P-_am2gd{y)P;3RPgAUDxiNu(OsN*FxPvGunCurC_D+&}3@f>6YBxn< zZ%qml3ApjOWI0*dH1#anPI04(Wc8RO2oh51??U<*4f`YKG2*2HLR;ddlib!IH;< z1j|}Ywc~9gA1s!~7#?gzW35tp6x_+hTctamit--R%^;x$qi4lMQbw1_>1}jMgSKVp z6Ba6cc9Zd77&e+DK8mqp5CTMqKp4)$4~w=1=Aya~fW|T~2u8_u90C>{F7kO zln0lR)nhU8-TyOo4oos=36^zc9$pX=V$X@KAryE^V!#rXH9Q+SY$i&XY0{?H~P4oyqRCT znLTxtkvDvinso`tMtX?fyHrZL6?t|N4Gxu)nQsJJJ4r zi}CGp)HW5XUH@lSAFjsB`!|!~FgW*#45uLoy{rNQRP@oIQYIy$1=(i|CatisMX;Gj z_iH4YcLkB9QYvRo(PQdGV2|)#V5}BQ+QH`@lvo)hJC6BzxkfaJHHMe9r7k}Xq>Mss zAX{z6!qKO?;IW|E0q4T0Vg9&?$=OPmyxk)h}U2t~D?j@Fv-Mf}pA9qCoZu zQ8DcNVq%gI4q#d{rZV{l<>f{Y#|TCbWE2?GR2AtO#<1#9q;d2nHNT~ifTJ~Jh2;?; zInC1T;QCJC&H8d@cF+M?p&H~$0$i&2US=X}ge8$NlQg>OFP*vOF$EcMe3Z!cNLhB4 zyaGY`o8u8ci)7>cSalPi-aXD-tSToqWuJwbhb{4l?xkkWNwM1_5r%xI#B?bs=}mx5 zGehCCMrb-yqp6U#DxPjp2~iQW8C-_!x5V#!w>V61Df1B_-4*+MSu{!l=RTN&=QRH- z+2i?mJaUJSf`TV-DN{^@WOl6{v)U~XX}8*>!=>u1H}|60a|bQtGMT!GD5-!Pt3bP@ zXupoLHHdII3H;aXOR3bhC@+|HePfenF$fAl?R?oF*>mc9RrC!^Bx3dw?}9__>bGG`MTL`?H7+fJe|z?y-0VpL=NZ0 z{D}wxOtz)HSz;dH_=lE-k%M5_JH6Z$Oh!&*=?)8_CvgUzLpqW?=%|cCS=`<|%qDCx zrg0`;$==CCs)>WG;Lzf%$!Ka~VPLi)wliD9#-eY2a-*Zzm?9k}1(Zuj?Nyi8xtB)S6;UDiRb@Og7`p z9Y15ZW`z(ogk%c3#pV}gfgdBp77OnrqL6SODr4?;NDyZhf$15R+SJ6gq4x6}D zL_)=xC$j`iylVMLGwOyoOoh5rJdnCX-m%GLY$!~HsOYV5xhq_Al#ZCf@gtW2K0m!` zwvr45lS9g)KL(Tz?EoZKS2xSacITvvw&=g#_x9!gXgC;;#^dpDG#CyBP1E$7e!tiGQXHv4yFJ>!-n75g z%jIUVT1@AQ`C?}MX1!YNwwv9)FW8`~ux=N_Z*~`lCDpgx)nwVQ=ovpBKNw)(NlQ2sb&EN%s&W5LaKlrY@XFrOlhrMHba5tWE^y0OIgL`J`ezfAH&v2p}G{L z6CAc&fYn}nbQ*%H*ohAp@it-^l+l<}#82{Fp7Cg9uc8c8vJP-xG57f<$0XWIkSuZm z1K3?`Jzy@7DU54O3-uW#m8r)Lv6L-?^L_K!TC2aH{0OF>GTnDbtJGde&%yS8JcT_< z#H|xGT2Y(p;|!wrgxf1FtxJcBK5YPDFd=8N@ovACJe@8;uK`|qpeYPnc07Ff>P{vcnQ>1y!T>x&(%>1}^$ zAFfVTy_{j?CSQd7fQlBlw{7Ohnfw>-FF}@V!c1{P`H-)X|OIDvM6+Dw8k3Wxu_Rx*T>J*{JN9F)#$}*%mrO5Kk zyg;dQiacX3yBm=)2EqwS?+nkOBgf4A+<-N%68&4yUPAnNxoF9CM-TVUi(n3&v7U>Z)HviB5rokwjgF0&M5XKwBN3klQ6Mc=WIi1W zL2G7A@~}WQFWAu?slcBcwbDs3N|Z402XNWMnQq{|I%v*{^o{PkB{q#%^>X!}`+FKQ zrGkMHEiAKTtr8U+rR>On5+FgtxI8u>Bk%|E^{5N<&5x%GiLnNmwg-xgD`KnT#7LeH zI~zjf!Hht`4?=9aZo*!eDjg%F!=-FH&6~0X5tH%b2Jljsr~)Ie{s(mHV(ehD&4J{J zzBoeep9LKRKOsPj#*tBKWNeA!zAC=Sd8J~fN*$AhnO4yDQYIZ|?-he@&uOX+h|0Nq zX)ck63Sk#^?sO`bmZ$9$n7qV;v9bwXrF%$#Q?{Z(PKtRu9I>XsQ|21BFa=@40Yq!c z#s*%~#9o^`$9arIQLnzCAQ%-uJ8z0sLxp6WIPN)#-!AW`sBJskMHj>)2#&7Phi-HQ z)w-}+vD?tIGgag#ki$DcqE@H79)D6d@c~C-hhQIJFF}$h{Fdc@%Kfy6nqlmUleW9w zY}Nnf-Q-_>y7<$($#mK9HJl8$Pbc$VJe~dKlj$#>&M(KS!^hd|n!C;9dU^HJ^yyVWq$0Q>#6q;LOdMx)W`>EvuOIXxY>)&I$;84vrDQFAhEPDcGf(;xJD{rzw2 zC%fnV?B@XMRoeg@;Q!5hvAUfv?iQ=N#qw@C`|$DZWB;!G0BwV?X&VJ>5AXACHTdr4 zY_zuu-Hn1lwEP{nnU$AFS!#D%$D zO^F>XTX#K^(PYVtvq>U4?|qCK)@ZXwGjSBCyHUt9&o7`ZKJ5Q#Rv402ibx|Q8cLEX zTm{KJ!%((K!Sv23tHF|lV;Gyg7(L-8GP2I2+n^9!3K3o;5UwZ>GM&s4#Ge<0qZ5*d zGHMzD=>x>oFxUI0+2o>8=2TTKM+l%~kzjN|O2O=?%XuS5T@ylyDb>r70;3Qn9^E3! z9LrE#6|>}%ObPjH$SqU@Gf${9&6d_#0F(Q=o+0$z3VbOuiM3O52{XWPjO=j!M}*1C zm|(#K0o47r4$r5Kw#+v*gj6s-8RdD0fM^$g#ArnwwQluK5xi*8=n80x4`+P ze|=B>`SaV;Sp5&SKYKj;)zjNwKc9VaxjG-M4{LOb-u3F@aF&^VxLP_65uB=12$nxE%cH-NksYxjI>#9o{NS z0~H%L#c7pS%}yFlNA7Xe2hz@qQJ)dl!VNFF)s4b6M1Qs??zp-=g&_p9#H>QG@k-sz z33U&%q}ES`tA3uJ)@U-7o|%UGu2Lx+QUDdyXDL}N1dzP2{_B~uokMJ+3|cp-c< zgirDaBT<)a&c3u@4iY^?3OTx4^g=kef#}6OKsN_AP0|2yV`AV!5Q0i{ntQ9~8jTwW zp;H`pAJ=6-D{GCsXL;cvs7zc)9{6Q~QX;Tr1WKe<5Bb<-F@#knYfz}q9!Nz@N(A)k zSSAC(z~4e*9%->Tj3dy=iDn_aRfc&OHHDN$B`BbAc#`_U!v^rzQZSUV`J>A$lad5F z%Z$8ycKAejgxvdu5ndYksYaM6&{m2uBPjBb6Qqc#w!f<$@aP+#z zQC1h4d87p!@pxotFvvX435^6)GGmK64r02Ai(DCi0oKIv4hHZ`yB5zqx4>OU6j{oU z8Nr&#O7BFTRUlZvxZDgWBsCOvl$i_W6LgViP!>@+gLjj*`~P+`Yu?{d1-t0=4ygZNG#a0s zot~Xe&d(;NlflLL@X^`u(Zz5w8Z=Gs-UoCK^S5>TW{vfFxBtD}Za2Go$?tA=c)8s+ z?HtEzKkK+@E+^ymuTDmzyZPj9de^kw#NF*|H@l|;?LXJeV)XL%^v(3-`PqCl*!75U zkA##8sXM^d8A<4L1)x+q6BM@RxoOR5s2-bAlWS*-OUes0=I9iPIyb;&7MdDXi`_f1 zu8aEAr@ZBmtq1 z7E$9At<|WtFTk5>4qSKg0DRt)+bhV1!K^|~45GzSvFjuOO4^FGg)={e?H5}39g(n3b0;Io=VM~BqY4tL2*11uXe@j>Ttq-V=YzH+Ae*m0Oz199_1+E)K>?uK`Y{htoE zzkE9T&GXy8dp>=BzB*L@d#26$?8oURKTMzhG<))4dAi&*2Yddu@E;CGgF#!Iw?+TS zqsx=WSCc1KC+BC)$*36)dc&r7U;H0GMO)}k=j-WgbvIqjmz(zYa-vd{Kn)W$Ha#sfQvf+Gv6o_K{CFr(eExKCb*10r99rCClUpwYBIaZ zdGPs;=W{A&2AGjB!(9xRu#>4QS<08ugZ#7n3LIS4*HHgv!SaHw~4%nvaf>Tx(_(z=~&onKX;V1k`>|O zUot$62}FPjJaHVpOr1#;0s$O5QilA z59Lf;tl@QPk19rK+ShrKxq&qxV5J678HggL(jDjO0#P9<8PsqRSz;y#6HOW2?-8QG zzW>j+i+#=)hkW%6m*dqhpH6@K>GavzvftZ(%QiK)%hMmH&%U|)?A5%j{?G2yeD|SU z_Zr{P$;ssO^z6}P`@3xbMvpItk1mIklV;GrPYKwj%?69*cDu3F8q?YG`u&IN54Rs~ zr?bU+wchT3Z`#tocTfzsFMeOvZ`*HedNyjB$#8f!9!`dP8`06AIXfA*Ey8NOUMv@z z&1SV)9XRcaP55`s+v#w=ZeMhJb+S4gZjcynq25c3Zzw-42BRQk zLt9-$n?nS>tis=(j)NynmaTgWehFD|n+275*aw3C)|fx!Ng8TK!Lmn(fJ0CT3Mhw$ z6XqMsF-5s}#;Ld$qhrR%NH-7hq-NbQbUvtX&fzh87D6INx_RyjskK9eSLe(U%ti$OM#fwrl%uJCzC)Y&R%^H!I zebpQrS2y^gf|ah_Qn+VHNEYN9UK7iO(MKQ$7mQX_q!M+Fvoa6Cm#f}FF4JZs>jA40{@eBe)yL!P~ zGw3aPH$t)yUnO%Sp`p8XGc>~JpRt+>JZC8&r)92jP6wQ4?*f(rm*ZwX3W8C{pc4XK&6`5eGx1EE z_&cd4XiA(n=@Z#*SmZu4-E0+tqh~@2BG)f^bLMB#=hcx39l1tAbW_N6QQ=p>LL6Bz9Q2A*%E=E;sc~u(Q<93d zN{7@SRVoQS3+EI*FgaIRPYt8$H5~GMYp1LJeAVAl127)!+PeSCtJ$-&Rlj%qzPr`Q zU++Hq%ZJb2%&!*P(ILY3;PZ2GaymXaJv%+UdUX2f^V27f#}{YKc+?*a_eTG{`%B!m z3X9d|=5~4eaeMQzt^RLby?OcW{p;J0x9iOs=pD%a!{OxY^wG)5dB5LuIl4Fzi%s+LcJg#G|Jmb@7iqmidU<>Dr*~(4 z!*8D5{`~3mWU!&_RniEd{Qg{R!)n-5XXU^Jm}D>o{%FBWi$%Kd*ULga(Zs0>8Za39 z;m0D$@z!9@U{;XWD2LM$2`G{}j3GKk1R;RQcGM;4rb4SEA*l$|NIzk^kB}RFnsr#k z!lAHozSjU)HV-$UHpKi_RU{e3NVt+x5?Ug9g_0NE&5ptD4`6#8%2KTf{yeY)DAHQ8 z`L#usT7`C9=HoFHmW7j@DVFl#0;xVL7!?^|C)qqfAfAZy(#DNc5XFlxgcUusNw5iG z+Dw$qEtd@Nzt_fx+De@*5s#0P?cWS1q zP0cS7r;AGL4cmOx7<9*@!HrEipNKH6;1~o{;c`Yh+(!j*t3>cS`J$*ArYIvrp)*Sq5C9XvTLl%!gwgBiMZ7!N)v`qv+5}6QZ&O{$E zc-XzC1IB4KA5VGy%}s;Ycy>$F2>Q!hU}TrI$m>)YOPCWe!O8O5uKCt-M?%bhd1sJ( z7)1IIza7i!BvUpd>Y2xg5olEOxp76^+tc~3-tr;SXX7P{=ekcPq4RV?O}pT3+vUF`<gsdxODnJU%-+ zySTVKdvY~-`sC#K)A8lSU_9O%`?Rm$*7mc-X1UzW=bO9P_WEY_`pt*$UcUVCr|)0A z{{H5}>)Gt%ekhQ7{eExIw6Au0dGX}(^4Z13lhet?a5&yyf_)zFZcp?9R_o2E*-Z8h zqI*Ws$#8gicD~tcmaApk0JQIFyX)O9hHqylH?!f>$)f$N>uvAV?aA$a%F#9?>8q>h z>2O21K|WGkEz6GtDnmSw00)I1jD?(QbYtlU{Fvb&6qB1po7s7Vi8110yB17)Y z=&}!jF@Zx7>?foA{BTSnAYRE6dh~dAaY2a}$sJ9RRazvBrX*qYB_$;o$AW@S8T-rS z6MH;Bwq&wWs=7=VN!D`e1{hV<8n=t;vjC4-@3_C12HIu&b%I_P{QlC+b#VOA}X6%R(zXOS1khSWZ+*F1@$NsRq=6xeY5& znj)F1(kuN6g;D03$`XRymZps1ehKlvB#*F~sc7d-nIZ01US0BZ~oR~%$u@bVW6&lo*N@f(0E~85568extpvyf5g0$Pj=7Fv7lUa(&*c^E;au63< z(a5P5ja%ris9ES%HDm(8pmgGB1rtvPPgv~4K>0{FcUEEYx1TDgm9)WjEjydXBN$~E z3@0QV`+Ts+1d6rFC>I+zK-%ma`I{0dfx%!FoS6rHp%}3Qunx5O%0iMxnG8$(Tto%c zH6q~)F&KZ#7@^x&&5@7jckEvOqBJm8Ux%`>NYM}V72KT;s0j$y?`)jsu44P(ic6NDwetB_z_Vnq=XD?2lJRVLa&7iS+ zsc&lQ_Qi7h;lt|s!|MIb{Nr@}{^rAvKYsJ?fBBamUjF&^_T6%|*lbpZyVvVkfA0v| z8;wpbE}nk!?Ca;BeEsC=#b|iav);D-iCNLw_7%JB;fNa#hG~-8(*NV^ZoXLTJB5Vx zu6HvZy`D}!J)fVA*N-R5tI6{9bo8&UF1EYk0fL?X@S}9Bst^Y5BiEKe@|;-qmz?DXE@r$XhkumsP;3gXLlx4eyI4! z(mA?SLN_9S*R2T(|3O|-yygpcfD)+MKd8ZWj2PJ{TO^oXP_K7U5hxN9lg2J(y!6cpctyzwOKNp5 zrs^omA>*>pRW#lnS}dj?mR7Pau@TpupmrkEGx4|L-&Expk*v;`08_f(X7rb!4jAh! z$}n8lM$!z&Nc(GU7AVPTk|UtD@>J-tMWEB?9BGXbe^uXPf;tU54AJ_<{zg$Cw!s;o zNOBCw>%hpZfL1|w z>O@4%VLDDj4qdz7p%$BdTm5GW(6HG(I@w%|*Ztn{CAPc%hvnJLs;&Nq$72C|)$eFH zIzKyq`sC5mCnwLI4WB=6|Fuc|y&dRogXMB}_i??i{@*WeZr9g0)0>+QFJJxjn{WQ* zFMs{#n-8!0eLFilJAZO@dY=){{-niXdHeBpI=y@I_HH)&IA6}O!_&u~wJpJYRDks@ z?JY!m_aA(RwrSWCfri7$XgnF8%;s|}cDubBDek>ZN4JZ?*=YUjZ1vUE?5Eq4?>b@{)8Bh47!=K`on(H|jRrYFa?|DXmgvyZMRg4;i1q9+`a|p6& zi^^N8D_`07r5KUGilLGSVtEqEAeAHuq1<$e^NrGbRZ$g937p;<-m1mdWYM0nCxV6e zDmNz;BNO_`Rrp$meyKX?H};m5bp|1>JgB`U z+B!mDPH{;RtvGtyAU?#khNXupyk30!*y`xO~*d2}j_yYW?cae6d#pFW1ZV9keaY&0=u97(F>#9{fx{IbXcG8^4{7{`mId(PZ6rIFC*`J17IOYE!I~ZY$p?6vco)-M!2A)>t~n)+az;>$kz?x5{eN#v>5wLfZ%X>zR$O^h4#)J(N)P4{^X-p?Wk!~xjA~5F%5;AI}SfRg7R#9D=%qJ;P)dDJoMBdnL zOk%A6u+qxFcsLeKlu2t&T}W13IZ>fdPl)lBQE(1HVO*|JBriA>8N4wQ5$;4uYg7 zl@U{2p95}1%)n9F_F%glyaH*rf}pn+!46YX6TybS1wO||F_q4WX^09xXxwa8S8t+J z^H&VsWVWk@iwu98mfDG$1C4cxNyP+ibcGEO&>YQ$I!I;75_nLGipV~yND+dg*{cp= zzJd64e{1ferZ17hrSXGwkGVI6U+0{)#IyxHCW9V|d!|gm^T96rYchZb>2J;ZRWg)v zNnJ|ZxjK0ZMG^%9JxQ;VfFc&8iZM@w9C{pcBexa(>{^!S<6!1qEk2$j#U*KmlcgdF zJRcWz(ViPrbUn<$(X&Jegvrc)ZYF9A322bkU~+zB5ba6-)AhUS)$3RDS8wLC*>1hwtk;VV zA6~zF`N!{n_`~hTH;*2je*M+YfBTzX{pwf0`0C3qK6~-v>C?yU_r81g{>PUuzxnq2 zfBUz8``3T_Z*Sh-yn6kYrr$LE;o$M7qvIPo(&_t$2H?Js8Vv?#CzI*P>~1#2YHv=o z-tJjI*VEDBQGar>d2+h^>~j9s_osKu=GDjX4>yyaKbh_u0JTbIdjoL-E__LlDt42+ zLzM5Rxg^*e%5W<~Gjg57pvPn3zq<7?CJS%(= zeaRsT{f#|#bgj|&!dZl2X3RxkjC_#+N4n1esMt4T=-h%hD>8$u<2Rxb_#`fTq>eDB zd^qMv3-6sMMoxg#l@Wz1`$|I3L-0w{n|>>Ni(MdDX%grF@%!Nd}{0py6TuK8M8nt#`x~t-!!9_Y09>T!AOUzBMN%etg=*&bs#QurdeN|FA&t*4GJ?B z(nh0_Dx02GqdH`O3b>^_9%#}DdAa9}-7WjmRg<-9O%Er7?a6R=^#9rC{`O~^;cPuv zZ?d(U_501q$>`#Icy`(!4v*iv+2F(N>dl+^PcP@!@0YXL#(Hh_Wz*U1Pe1+n+wcDH z;ltbWv(w-G?zjKbfBYYR_xHbk@#!ZQmlxyq*U7m3_Qy}JPR}OKpFjKL`LnC5NB{Ku zfByE{mp}gW=eE%}Ihph)mxDd~=lJ~GGlKTNt=DTCfYb5GWPEZ+258^oa?^V^9le{5 z=c~S*q_gqn`T1g>WSmas>;9YR=yu+$FM4|xkV$YMKz3-sgv2C(hEua|0-n)1TXq5 z+;4kGJ_|(|s6#5E;LO-#)x=iOJ*`u)ekHMsRpdbCK2O1QCMC&gg?bxXpm9WwiTGz3 zs}>K}BI|Hjo9jJL(lNhPc>fyqK>5m+r$6~vMmBM#*^dR!9CleS{&14T#)hE~u2`NT zWyOmrA3P-%{-m1Z7b&}i>PXTdnVRg_5|a}cx&mwPJ$*ss&4F-7gJq(4lsaeGY6Q&A z17mMN%(eaIFDy(9hTF3^l|WrKl4q9KvEeo1Y~^7nsacE&9T1S5Fb(HkBlG8VdKo%p z#K{PoKNqE0@-W2cM_i_6RD^6+T>m_YG#F{A(hx86ojXy*dJ@?QTPK{nyvcFP62#u0EW9^f0rMQ zwgFgdhKud+ez3VcXmNYJ{%ABfI~zXk{r=JA)6rl&=ug_8oG;*>2z39i!+tX!4%^>@rr9%v_Cdqe zb~QJP;e4~le;eDKk2V+MOp?JE=ZW-B2`SGckJ0%YjdVoCW9& zx9p*Di$wHMVE`%mr>Q+tIg!!D)q^Eio#=Qhu;|S3_i*golj`pgQV~tmNV;o z;*>?b07qozX!6XFgJymm9)AlRmOVf_IlQE0A)G-g4~t{+z-93Xn1$JFZuwAi*X+70 zA_n4Uy+YxMMrQLBo1}v2!EegkeFbJ@qbt~I5+lNZ)pAJE#cBK7RtRf2ik=G&iI+7SuZHE59|YK$>j^2ot~&HG!A}CotB#no{a|; z-IHMwb#7c#S`tSPwx|!qF;ME6OlogLw5Gxi0(+1kg;?<*lXAbXE}n`UpFsjrX2x+j zjX;(G!DPlENLD$0g90W6b7khakko^Ipq0Aulm)dU95<_=bH^t=$AyrOT7Ck$-_?Mw zSnhEoOClROa%piq%0TIw-LB_xXvd|Z1D3G$!>PjA)RtP<2iU&dNtk`Ctjtj+`|uA?tal$juHU?#x7Gi< zcZ>OAdw4tB?Pk8XyZP|u=H}IAGrzjJ{M9di{@dUD`m3+LxVSiPi}lTVeS7=y$Cp2S z|NRebxj&oD+Sh;d=<=7p{Mm1Q{i{ztd6sP3e*5*!tHpBaEQBu2{eA#o&HWHyJRA*& zqo&!L5VbF|+S=W!Y1@Fq#~wACM<=Vc4Y0lQ>EMT(lOI2fmz%!%(3n9KgCLYvq3Oji zp1f*8a)|J-YBA>mhDrYh33W*wTg{wd-w+fD489bBZX6AB?i=NKS*LP&4f{05$kc_d81k`8*@_Ood8;2f(xp zjVX${Ku2j)pcR|$+g8SD8BlkJ=MWl`^t18;8fSgFz?*~1%i1E7;Powr7-x|_fc=ks ztLJ;bVXLD|DP}8OSLN@+FdtqU}RSO}|4PmXV!TLn;FY9|j&NgPOiZAQ!?66Rbrj4bXy!v%7B0h` z5sdw3!I-5Pw_*gMYi_FvV^`+C1fC=nAs?%KcFOfZ3|OuY^EfTGWj=yt;lh#!UUr+U zg)pUBsuST45c--z(MBmPEFL0ZL+U(=5UhGRm#8zPA^M{g9+FTRED57DP}7odki&#r zMID_1IgrHXTl{^1R0H(r8LQdj6v?M@XN}x6!Z<}>*_7098x;k%hLIpeqkXm!NtGEi zi<%@JE>5}%>^9|adjMs&G3WytLWQAFM0CMoNVM$%di{JC_W#>#ZO3X?+q(b_hs|iz z{<%lt*VEmHo7I~)i}&xB^Z8y<&@{c>4(rYGZu;To=Iv~LHy)3^`24e9{NiVye)`GT z>7;$%*VorS{q*{q?|yjw`ptU1IzK=E;`0|@eDV3`pMQ3CcJ|`Mi!ZRCf&!%^` zw{I7VuW-444?CSx9aeL{*Bji=0fxAG8=|3*x&3aqjS}*KdidoiQ_#NBO(^Wo*5?raU`A8 zLP4j&2bj(g6c#|1l!k0U?;ea}eWMBhE7;PY7WG&Y+eVz8P?q-1twN9 z2sC8`XPmdRnfOavOu<8=I|#}yu*ETviN5@t^yNDos4)z11=oYx*O#;hVfgyIANGb*=>$IH3r3#M}W0xx1AzQP{ z_|dnA4tOY^?*ixXOQ1+w%|A~QMc?TvqSU0a6ZiQ8)i&7N@6@7w+SV6oWV+$?WyR*S`^YZ7{9 zyVYv(;r8wOo7aoQ?DTBX7WZF%{`tlEKInJ5*}VMe)qnXf|L5=j`Cs0>yI!uAqtW;` zzy9U__P_qmXQ!u+9$mIy_Ttk|A3wgFPJf!tZkMY?%E_YWX87G%)V`1Q?VJopZ4a;y z1>0`hcfH#7rpxBza`5Dz&#gG`p8xaC5h z+i?d$>5bmA9?v)*l)MQN9*Dp_E{le!&|$|^PH&FKawNU*6>_Qq zXrYB9DxC_HX4Wnb2^FOe4IdZ^2Q2R zDjV(=^SmqoHSsqu^Gd*_GLyimfXz>yjO#>yADLx^z0w0SxFiK@`5iE_)?ulrC9%sg zSGTdEm)bIxu(7@rCLwbV(Pg+pH6qY-9h$=vh2d@HXhdq>F{H;lF4Qd8os#t3Y5!t6 z3s$`HV`y5ei>D=0O@U_mAUV!mQs>y~LY5D#Sneuy(Nwo?y=H_Cl>(qtF_0=Iq+Zhg zLOz!YHfKtPD~b!CK~YI*2a0Q9!D%WokPBIqn2QyzPNH)#uj;JGJQmFu-}@4Flsz0w zJk_j-fHOv`7uTIeXQT(CZ0>13-iWQxEVqMQhq~{{^}T)0*P!VS2K|2T_>!9q?xySM zbiLc{dOgRX+XmqF_WJhr-Fm$`8J|CS{J5>`o2FT>H`BZ64?n#8{XhS||Mee#e|I-Q~&p(aCDuZ10xMeE;`@<+|5JutDcPiR?ctL>d(exK2(HXFn7$ zpV)y=>Q+R-30rVMI35taJ*7RtNYNu%=9F+zRR6xaT9`;X1;O*+kyHSeQOTlQqt?`D za|3UO1tka?927k^vQb+ii$LDcWf^O7XwL|1BjW| z!;T#t#IzEH)=#K|#sY(Un*eQrs5~%4O0}V&=`dJI&N-sCu}X3gKU})p$=|FaoLb4a zBq#!8vfn)l{>*{`%P4?=O(#K{AVX=;SWK?s>O5K~$J@NnWIV>SoU;_|nDdTVw(0q~ zqXXbr*c;e7o_Aqpx+}AsHJ&~n%2Lvt>XxiBH5cr{D$lO*Z4}DX5!>vn3P|W(%9GeH zL~|pPiVz=iyY0C7*@(eN&(IM9;qL^68XzP9`Y_aFC$VIaS}3(VTw;dtI-W)9Fq7yZwhfYiKka zjfQRC(0<+B-QD%|`}glZwEwVLt+3l|ciZW7_U^j<>zn0jbujziPat+}cd*${B5?FL zI6gwhvTXmW-@c`$f3ko1wgKoL-os|wyIT$JmV@=ynjWqut4HJYu-P51$8^=dTQ%+1 zAjIk%leoe@;A(UW;+w!@frMv@1+(N)G4d7!DO~QM7>XQ>73p{Gix zQ12d|Aiq~ga%2_?II4coQ6rE0iQ6$-2og_ST`h*Go+jVvb-ME?rwkYhS7XP!vyG}C zM@Z#Vlf%I|{LU5aU5PO40|g)0COGA9jKQIfg9h zR+FnHWLN1-1#-PLs{4v{-Hbo8m9Y#xIM$8%0E1fYLlHH(X@J&xNDsDV06PHWNB>CB zvy9_-ML?F0kwh(+bSh~QRZWIlCqbBohr-3c!efiaLjIw~W`%hs0JFe1w(MSMVL5;` z)A_(omh+Eg2wA8xYfL-Ii1o{CiJpH0WI7RbPe}AmY>G>A`!`vA?6PhJ0WRPU!0@P1MY;AYB-a zmS=%~ppQeTN-Uiw=?Wkfn(^dM5mNEJ@@hvpw1v_i>478~DdU}a1`4w2+7mM=Xn;Z= zj%W-)cs*hxXiT+4w`F(q#^hRAH+O{!P*Ndcq?Y;qtB*B=v0dKrh5-@DaKT>m$cxw< zeS=5LWT~5#j%;9RhPi8`O4%dQR4T0wKVgTo+BJ*KU<(lnTW^o5_6}dUO`GLzx!kVT zZ3Ta1=H!pN-L99b`Et3~?lwlhYAhPx=J$mDW}n|Wo6Ya;rmNLj0~6w6ZQoqmi?j{K zp#9Y!^m@m4yWZ_}z#rQNVAt;%P6wOw@n+CFjx&~#ECMiPtkpIJhz6lpa`3Q%#1qvdIknR4=aE?t2yM42qaDjIG1XVyh~3k}#r+4#k&fIbq0BF(3vI8r*or2z#U8%Z?M z`W&u>v%oNC`x+}87L|<*lq_|@<2(Glvk^6eP=F1@>erbM`?IKDUM4?4tV9^PKGL3! z<9?qqS;e%*KQ21&b<2dMT1l^tTzNT^X&zLOm>anmJ9I!%TbFGn6Rul%*vQBq)@{BD(V9hlF=E6qZ%XV1X3p9@77%W$W}F1<H+uqk?If zzUVMB6_H)04+DY1O~^!I0DQo?RW?>M?~|IV2iMN4abB}%Ss;+9-41g2v7XM z1QkqRbf@9aiY2GMkzFbuBlbX*=}$W3bde2}Ccj}lnnha@E5L$oNGi@r>@XxQyhcY6 z{2cZMRf{eGKP}e^TKu6b4!q5^A}Zp6%7{1D%=dOv<6`?%q`>3uDY3!}f%Je%R8f&5 z@W0y~Vt}pZk9yj!u~=;9^UY?xJwEw+7SWD~`o~w??sn_ddbuAHY@4PzJ3o7T)&9CX zJ3AW=hkIkuUjKA5dHUpW`>Xw%>1_7)?Yryu?-z^Jp>3d-MK;~fznWfu*fhhv6X-!o zj@8EQmi>>*W_6!*HW+NrhMPfiYyei<-iO8D!+fyW8S^!aWOwAMpu;dL43zo)ntSr3 zVBN|Aoob&MBn8APCN{jB_?dT*D&qO454el2=&~B7nr54i|_bF zR)5lB>SZvmqVa)OMW||uo!y|%VPEka6yEd{#Fj*R; z!8J2wg?WSqcXC+=d1RCupkoI zx@5VfQgMsZ1Wu7Ow|oVkF`R=;etHp7a(av9E}DddM0zd*M^maVU{J=UzgP0Tkx)~k z;T4322qgqkbRDO^2_6Rem^_T~0T+y-Q7wo~lFkm38}H_CM1|@h7o~G0=!k9>Yei$| z654Qk8b|_LnrF9vu1)hj0G}M$2a7UVEeIE^7AqS>L=Z0%SgLXKp<{Q_=*p>B;)`4O#L^RL z3ILev5ZpvI7!3J?7J<%V{if7)X)G9g-i2t}fBxb>{P+J|`}J?% zy<4tUC*$$&e*5cx_}$+>fBtN}UjOv!^$$P(czu1dT6GP;o^6C)?Opfw8OFWgL731@ z0Jb|@ZF=i%Z@cRq&biTGJ8pJ8Yu}u;e}B6iwC%x@ZLi;6iyn*wOI~;7jPUAYIH?Od zh}Xt@Aq0u*(xW+vkkv%&|L@^Q6b8p*LS&mj+P%o_L%A#(bV%MUs4R zV^|A*jLGj}2DE9GQ7PSw$LrZMz1wLK8l!^u6nj3akf62f%|D~^kqiYhrPP&h@UNG^ zK&qSbB-Z#P7J`B#y(U5pD|{W?tH4T^Q48#D&!t7-xJI;!%nhb)t$M_y?B1AE)<+I+ zR67Zo%4pxVRD_)ciw^1rQ8G5V_l=Nh=ub3F#Z3daSwv>+ze42H2e>&gomV{M^?_tm zJZ1`V=T4bGh?*Iq6?ImLAR4#FigG<})sCGWHPU(1@Hj?Lvl)cqCdhLs8TydHPP7Ee zwC?zG5Gh=;Hy)peOg-y*08RJs0Tb!FQ=y1XqSIx!+&pZeV7KmCQ+!s)5F??oKNQW4!nnWon;wePbllVBP z-2=kJg+Umy<%O~QEy~-(+E~s$OVM8_WNXpM4K@VKa7*0|4mA|b+dBhGk4z*eR=~|v zf*;+gjOzRhig`^yJ}uFFG1tOK7J0An)MGb_oc@-`jyMdEjf_=HAw9A=inbal)s8)+ zD8%ldL1zbZ43v(pz$Qg8wLZ2q+Dkv!ky({(t?1t|+hi~Q^$_0P@vZFc(f;ke+9z;+ zcmMU<_V1czczSwyettDy+)EHDe)6CH^Z)YYS6{q- z{bsdZUtFC3{Ohm2{NnTW$Ncr1Z~yh*{_yR0Kg{Rzes3@uj(h#4d-ar=Mu)z-?MQZJ zO}{@J4BFp)v+gTx)Aj&c%k;dakJI61*z6YT{%YI1TQqm8!9EG0*;+870d012>L%Ir zqD5Iw{SEt%IpASg#Gc3NjH|G7a~;XC%E@^2>jQ-YX2>Wc`X=-4(^FsFMA zQfA9-xWkAr$jJA=t(D!SROlJZwg_3WRq?DhWFZVY6_#V|-!$Cau&D-=VQ$vhFHv)V zDmghuWERPeyjxk$mo!9`9TCM@^v&^^S#@Q3Cg~Z;$^EH_zz{PfNrcYkP}u{5<|&91 zEpo>-=#^!5w6J)go1~7V5nr|(j|z#&&6SWVAOyV(Jz_~$i;3ctJu|ag2ermVDU~Q0 zmg2m@wiA|&gZo5+1hI6 zOtk%Jylj-5a9S<=oaS5#t375kUkCh=gE%;C;IYRSM*Z^6)^5&#`GvAQH!D2LJ&q0FV@5 zi#JUKLs{W$R+s`q2QE-a7|X36;$!Qas4#OO&tp@yFgo|U6hjBfTceai76!5Hu`(y& z1Qv<~j3G`0bv+Cym-s#^pmqjT+{;hyq9L{C~TNxEa#`(gzT-~u@6H_0f~v0SQ?`W#=9v! zE4;q|8=+EE7fQ==>T1oL_R7(Y#u@Q2F>{HuwR-(;dq2%Rn(k2}{sp_-pWmDA_nQ7* z1+bq0Z1xj?wnIoqzfjxl+ArI;2E)PV^5W^E%V+Q3zkGl5`rGfm`@?^G_Tt5hv(uAj z&z?Pf@}&L$*I$42{{2n+)n})Zv(wYrZ2s48zx&63{Qd8L|Ns2>;}7i{xHx}&e*R=Q z9PjRFNPQh?_Ktt+8H;H|T)ZJme+UaV zDS5LaRtyC{BHEt<4D|)p7do||3KOnOt2@~dH41uu)#Q_bx z3^9^{Kz!<+EL`#K=aT+PD}CsF5cTB)nal`&_T;^33Xr>VCp=6QW>$+K7q44KIvG#* zKV7<#EfJ0VHZ68*C?GrN@;9EQGfxX%U1?4&*Od^h15zk(IpU7k_{cYr5`|zvz{8xJ zNGa-gVkQSz3(n=7rYMKsV%!$sXFmOsa`Q`C5 zA#+pNqjJ3$HeYaxaN!rq_;d_NNg-j&DB8|n0-gRs<2n!zLKm!NT<P43(PLQ1MJO28U7ZzN60#e)xhh!Lp=5t~q`Af11pM*);3!tsex zO-V|KBYWC&P3mq2uS>FGSF|~sH9w}vpa2c*WMr(tC%)kzgmOaUqTaedB7^R)TEmE3 zC6LcdWRPqM%VjzgqLug(b=z>Nd9Y7V?oUN{?)EXCV`RCHatR_NXVk;MX+?Dq zWw-c@R2b$RW0o$68yl!ow*p47RGM%Z#|4MHWD&#|@7<79=}A|LjQoFmz2~4kP;StGOIGrj@akiW@EKL z0=TGKm3eZTh+haL8v=Q!Oe&r0CdkJH^CdBThT2XT>wH+X>mpu+#pEcj%?ZE!9CcX< ztEv#!o5_3m7^fGEWvL9f<2%-*@UD!w_)d3~U2d4^{^!U| z*}^3RY(M41tLD&Rj$I;rSU6)j&n*Xhga^Bhx*TvSQV}w&h4h?JEov;nw(>C+Z(fey zGb{VS6dNtXIW|AJN^?eq9-sQMezz}?rwwLJZw7AL-Fn@2Kl+5&VUp()A%Vxd0yuAMO(@*Z4pHHXL?pxWm+l%)XFTel(4}biR zFTecq_kaA;#rwEII+Litkza%A@d*y z88^(Lyk`TGgd2@$gki8OF`5$pQ3OyRYE{%Jto@2XQ(Pn}a10$lOzeZW3n})O^MrYf zs>cwARBO(K#8_D{JaIBxPQ(k57dZ*of+Qvt$s3;ZD1r;YiManJpon=OX8J^v#O`Y8 zmLFs=-ia{zMdqZ$s6{DRE~GVlhLa#Enmw|N!K6Tj1?-wE?NN+c_z;f_@+=}SaISIY z9%=Adp!Gx2npx9UX-d3&F|rF=bg0Q1oU*KLr6oh7Emac2!>!)0Y7?!?OLNq$qBAi zXo~9~b#+={YyoSpC^E7F_0{$20h_1}9iKPlU6P?tDfl^ABDC~EAul)xb(_>f8(ciJ zqz;zWxLjxNp2;~1-OBNElAotmiikonkUW)!eQc@BTL`*zeNCBs#97-FM-_xGZ0a+$ z+QU8`d!Umq9u;Dlu&`@%eU-AMoq!edJOc^I70CGvDMO8+{0j;J@+F8krz{v+EW}D$ z7>MY820`PKH?UH-$&ig&3Zzs^Cnbx8vGh5}LcrQ!1+N!7eBMGBQ#X)z7$mte+DutF zIi-(g){n!R{23jp;loY28)pH%*>^YoZM%WP-~Ye8^uH_p+jc+q@BcMnlJ!FU@0m8U zd-t9^`{axDX7%m2zrR^rK7amVwOoJu{N-}QFg0acaFZT(p~K_5`iO!hF)>1?}g55GLH zN%ya(j{`g2+&|g8xtVYK@&B~z0XFT#9JOo`V@t>mqvYq(+m(_`uG~i`GyCz(hm7_U z3F`Tlh8$KYT*RboC4afWbTM8tdRTF^X+Joo7=j{&Ocv8nSVUf&NVadD?q-p7WxSW5 zt^g~XrNKx>0ixnQ9ghIBAOf6L6-MJ?V;sGXTYb}%wNN^ML38fegODqb70(=h>e&mT zzzDriCNn9oL5zY%Nv@UgG|7BHs8Ev~Yo6Cy5znN1De#!KK#JsG`NYEsHMON^2W8C| ziyDyhdP|a};%kv2B&o0$bLcOBo&c21RV+{0w^18`=fiYlF&l;ch-L?plAAIDJjDhU zC@zAe>l`qz+$C02P1`LTISzED{u=BIHeS{jNV62QTgifq-hxWREgyMQ&q3<|q(Z@hVTWh%;=sY~6(5Wh4mu0=^RaBm`xnIG zNnJ|f)#QuKkd%FvD(z0tp%_d3%*$4%aRsI3Xz zxzzY@gt6p{yEL9vh^4FuPMM2}VWog`cdZ9x9h zhA8|W=HSfxpjl5$(zC!YW~~W52T`_&3QyMcc5cD_sbRi-HB7>GAe-zzM_B_z;FY52keeK_LjlpDi+nc8A0otQ! z+wA|#c868l9CmMcd*~ZqzCg`bbfAytq_9r}TC66g21ox5;fG1~28CIdym;n&3M<+YOQazz$`>|Qgw?lYM1~nZ z0K^+SEoZoSehdAsEC;wVW+R2RoK+n{2Fe&{lp2}wvBgpt*0<3Usx=zb78eL7fYUfNzwW6{R223*jowAIQ;$*Baqr3RoDyOyVyDLV~v307Ntdp|;$gbzThOEL>N0$w@hsT+3 zwFqeg*;epF&8p;H6FX5RXh7p@!NJeqG%>&0QMik(qF2+z-tn5}0SWh{<8zDcz~WCw znM@Ee;Qa42wqEgIdYp`uP+{J$>ylk&tXXVBxb>VpYB`h9fJ%-5D&h`*4q4sVr}qc# zWUF2gf6fg|2{MgTs0_}^&P3SE?-c(obIS9e3v$WG%0-SXm~@FrTm~tqUG)&nOCtFr zfwzxh;A5pQ4skD)Qg!i7uRnGI%&<(#r5ho?Ro-wuUA13CxT>U8S3`~6I%%U?IP8x{ zi5!?Q#SWUJ6~8PRdHV%Qyp$JuOZ*zQkD;(n6u4zP?MoHcR}ynkW*rtWr71^uo5Gch zTTbA0u2p6wT#JBpp6kQr(rQlOfga<}7^3%He?mqwrpuX@d!4c@AOe{HR@R z+D!{KI_dLtOyBjN?q~gj*q`Y?JIi?kgCAg60^GUtXwnCD9i5%teg5J<-oAag&-l7o zt=F^JW%v8n>-BcKn@x|p-}(5_#~(cY^x=aK@7#TSG(TyYY4T8kL#CiPsm|+`F=fo< zf8%Gl^FDa8_c`Qip!>#Ko9sgAqyYc~UrC%HZmE3wreLr(5N!}dL+}r4`heW>- z$JvDv`fR7uyb?xGg5KQE&hE~jq6oa@;ZbPH(}?8?C&CAxjAo>}Q#6avqUr+0Fc+#9 zupkl7R5=#Z_~L#X0A!n0kdsK*oli+n1HlEueobqcQY~MM$VmjvifDC4CyOI7%Q}qb96b$XW$YlXL78qKFQK`Wk$wXzgXi z_A?mdLwCp9L?gH#BHEABsC_w+rRal$Qy#DEzcV974~(Xy$g<;b>J*_Lg%w0|hK#P? zj9Bz#CG!}$dWooKH8tkp4us?awJU1HZpg!IExMuYpK4*r8xL;MEi{l|_pS;Lr3Hf_ zf;8MG?-@XPagZ$5ISz&EBn6N_u=;VCUp#$_0drEG*oeZ4t|j7U0)cs{T}>6~85p&M zQ9|Wd)e$q?jV6``kE69PF9+AK5qM*zL}_fd2~ld6ZuLKjtQ8h4`BmOXQ(5(YYQ?cSo}} zEvEj2p#!Rj3fK$%$H1n;zolLH4d5PN{}J}Z|DCgk&7_&n`?>#{2Or!lF5B&9w`&h* z(WdEt>G=5U{Otby`%fR3vCsMmWp|W0@~0 zJ#omgB~89r(0kqgWU8vS;Sp=94}|e!JVmry+|eOLtu2V*#S8IKvTY+&j3gp4DGPBn ziKLJ)133UnW;u_t8I;5qv>Q|`j_SmU$-96gmf@(72UyfhR`;-T$;#uBK7kYgrIa1v z$Ek&|$D#!w6R#9;5c3v__SNGtVE%6DapM@Q9HU}jygZd9^pSxzLr{TrB9|cb`22Q| z#a{!-M7xmW%1iFTfr%EtQ*~WXp=5l-3$fVi89!K%SkfkYPyvk~2Wjb8TdI8_@>m6V zmXaWh7`&0Qwtasq+I|7P(C5LNlQFB?Ythm1X$V_3b|7;Ym%D5@YXv9;@U$Ip__?_z z&dq1I1?Z(wYGIM0VeQajRUYoc4PYWAs%Vf?X`4sk9L$P07E%-11Uwo=K~Lr>G+7;U zvRaC|tD2;PyX|0@EtDGO09m$r#kF`W zchY2`}*#S06I+Ps$f*i*^ zEGzmyyV_1JmhaI?5EHN^m+12|l0!vzjbl-uxcbo{%i#K@dwoSEzYs1bW*5o%v)1 zA7dDNK+K?eIkuc*j<6usvpmyZU4~Ku^8#Kbi6%SmN9xEU=#A&A6xvoBPocmr1Rbrr zNQN<1V=-&Qit$##og}efQRF+qx$ZAnNpx3R3{H&Kj?VN2)Tknks&vbMF~{)n>&0boJNV{yP;(cfpA zT*dGoVom7kjvJAxHv<0WL*;`=lJSKAJj2^c30MUoTVZaLq%>+qaSPhZLZbsPg2}6N z8|DRLl}_tpHFxI`fZ83wJJ>>4gFGM}YJLkwKmk|@4@lu_J33I7g2M)^$gHH ztk}=!ZHvWb(wiJCO{eK()*jE=>16m)t)c4>mYWH2AT5TDi|Hi~`pj^fj7_X^`LdIP znxL#nxyOk&r+UD$EGtZk^$eo$q!@k+6#|t=FxNaKivw`8k|}CQT|>Gd@cE(#dV~+p zRIX{{dmg)NzC%Y~4vj}heH%fLu&K1|WHvLc5Ez{6lOuVbsV5sn{Y-}E;Jv&za%2XF zvBF4rVj9!nBs9dPJTPu}Ou(QXNg6!t70LQ)RGKGMDD&Koa$U1zD(Fj`nGkep6a_%a z3^4$Jfhe+vmI-H6OlFu(^d<3QJAaRX`V&Tu#&5qT;aYvuh>^x9&?XiJw9Y zMb;yE0|U{YQ*DNvEN}F|6-h{58qh^HJaNV)h&S?igcoHjg%TIW0cv#Y`IOAEs9k)P z_%7{%Z0w|o7R?uhs&Idc{MMx~!No9P!^?#sk*Q>?#_51giuNKS8?PlG}fF{lUgz4Ln>HO&A==}8b?&;~h zO-{x^EpalRwXX_E+t0I!J)HcEC<{z^wW5gcYNKvlXdf zy1yGgYt^{PQ-W>4alD@5x|8*%CMnZmK&6ZwQ=}Uy0A$+6(m#&Y%miCHd?*1iEG27KFD1oruZX_0MDeXz;u4T@OaWpKL5R)D%2b#YgC~t=km!fVN(X@theWkV8msK8P>Nyr!7z$c=t!4hpbn5wG1CE7_18bBWwhDyhz06J*gAjdHiDRlq6i1$ga+Mys zmT&Lk?NC8~pL=e#T;h-oe)uUVo@us(3l zgMV19YF5jO7plEe zLgi)~Ymw?q*6HYMNo8`BSo$542oRv10>OZa23@*7m{i;m#Ton{GL*5fQwUO}*a_&Q z#!_{eUU!uSQBAlyEJ)NU?SA1Psh@&s6UqiXZaqv!xrh`5qnguXO(D8IDAtFmKLn~7 zx+W{PJIe7us{2J!3Vf}cbnBxVCP2WurDDHt90*1hAR`^3n*Fu*v655nx6GJhXPo1T zB#Lal#w-*~)O3*n#VaV5%w>7LdTcAvcEyv0g#{ooGC3RRlupDQQ)xiWdF1_%*d{GS zmRd@@_Zw!DhhIOM+I+g})3q{5-DMk?Pqs(XO*3(se^=#iyYhXr?S8cFI1L&M0nTH{ z_PYc8r_bg}sry4+xqowW(U<-gKfJ$qeR1*nW^uV%F52C8*X|Bo&2%z7IypT%y?5u% zgFELB@7{fMc6$Hp%LT_|Dt~;eM<3k+Ds?3r|qN7Fl@#rs(RWPw%w0*!k!*! zD3k7u0zn-yXD({NAe%Ds4Ga@(D<#e*$VuH*QHg4W6%|Gz0lAt;6o|N-Q_KNL?X}lY zISM~}Sz7~Bfku|eP#{3@W?5nmLP7-AL+DQ0=8L1)NT!*(d5Kiee`ZD}0JW7JpD<1A1rDT2>f zp&r8sOqi*(l7mIZj9k%_{9}P0QPesW6ic-3>BWhHhCke9puMJpn=12vku^~UT?);# zgx*UyA+%Bw+Pa_?$BRm9J{va-S$65;IZLHDoR5u+E*z)DNTr|#gm52EQG=F3s0{ju zKUPb3r_)SiwIVR8mCE(oji0MQSCBO~1t8ZPx8S$7UlLG$xhgvmEvh6y5Kd;nTt|3l z@eqL(D>`AT*qBrG#6+q_fK!+~)iL>cFO;w2Q8X2`ZF|tX$TnQX- zno4tM$Qps$5=}(bC|@YW7L4cSoulny(-qBK0p1+`!mUm6@p3e6PiNbGR=_ZK-|f2U ze{-{3EtcEuwr!eZqXA%#1(FTGD8%o7-nN^o>$h*-y!ifyuim};{^sWWYO`9eyRWk8 zerY#)2=RZetZG0?8Qhe~=0g>ly&YEWm(;Ci zw>_;Hs1v$WZVX{Qk{44mPqB>q{w#A4DssMqv5jG@%f_$eEAAwgRH4mq*OR4i#kdhl#IAab zO1So$@Y=)NRp8lz>&; zrV&=3ct+#plx+*Ja(7f&rUd>mME6FdFCQ;~mF7W$3)33cig{x+FLlcjJ&-$tx%E*?&DB01#7iJ-MuMT^L8V zm^$wRFxCh9A|$`iRi=uBg)CLzhpf-zoAKZ(+ycnvJ{I?ER46!MA0hHd58 z+W#vJuISm!_Yx7v6X^`45Hi^%7QWyV0S1*G%QB5+xG^n9b0mU*BN+0_l$`Mug)rf& zb%+884AS!|kBJDK-Ie`+S}Y|>6#HPC1AZZ_N1xB-}qlfAt@-)KA0cLkFk$-j5vt`=9< zZ+>|7^~;xk{_gufzkC0x`!BQE?Ck99!Ts~o)05-l&ua@nHKYsGy>CwsgexFc3Ec+gy_W(?a;pK2g?RMDFC(<*5_7tFGELf-I zwGnUCF=n+8r=+Y{BwiYoN=C}x2I-5m)#E@!IP=MA(Q0&e4nQ?ax)NtH)+&-fHtISQ zK@v|}s1+;hTq*Dm zd96Aro~7Hkt#CJ#8A~`yFW>mK)F#?)G@>U}j-l77gjNO|EF#V&&#F#pyQELcv5kyKbf6Np?14;ui0zwsI^FseL#?EAD zqA3u@FBaD4QFu)p@>s!&Na{!dHDR|bf2GgMO$lY^Nn7IbY@!j-rr|`b2ZCqT)KX9Y z7h`4WdIviA;Z_8I_vMW8h**dgiLJ`m){Ki{Q_u)?)=7Fp+NxrD~6(O*biS(V}gXDq^q2bxJk z#(OnWqgmofP!^$^M~Z{Dmlko+`e+hzPhl*|;ZpfHw#3ZQDECEvP};n)S+mQ3?WUiI zH`mMQ>~uT9jx_xHueR&z{I)msdI&(uNdO0jfL~l=3DgzyuirfH>i!qs{pszSm)DDn z?RGt#Huvt`efG&GpFaEK!w;W6e*EyxojYCazE~`~y8qq#iyyxK;p=a{dGYf5i;K(G zZ@%4Zmg~*U#CG#1&rWCaLH*xLV*4MvT-CX|BoRR7IMsaAq z_IIkLY=`tnG)}GvyHp(>(4d{lmT*ji$CPHus4-zjgkidabSxHiX#{SF69>+|;w2*4 zXp`rDnyFGcPz17BU6U(P>A{hzCQoQYZ3{#Oz5UL zOTkA}EO#u#c9#iIT%*fOq5=ToGjFiJhCay(Y7L=Sii|d|X#&0>B`d~Eg6Z%v>S4L& zZW6BnWkN+>l%k!g6jC`c)mPdMrBaWTA{RYG0C8==vBSDfOK<^9O+Q{Pe-*2hn0X|{ za&qQ!#o7>d6&P{08O{O6Lb5r#2-1|EgCePYKo6PC#sQEyrKPHYrq$a(>@W!(1&AJ| z{p!F>L)Hsd?s0JG#rVH*=D>(laWODAJ|M2-Ab=!@d(lRD-f;X5jabaF1;wt3`xpp| zAyr_Ve8MOBNF`=I)v4_vf<#V_JeomH0f}=L{*4xPLn2xV`)moz$|)Rbr7?0e05t^^ zQti8nS=Sj(iGa>8#$fVd@&w1m_HgX`;5L1S&D8v${^+w{?j`Gd7EOp}n@1)78PsCf zIKU&NNM6wGVZa(gQBq9A%L}23p(#fklB`YavKirnNM*Cz>=IhINa#i|KGLC#RPhC7 zhrDYa^6jXzaI42JCatAY7PaQu-t;ClJ|xDh<&<>IBZxSab))du$X4JMfozfnDvpn7NcXR5&)xKC z?!KztjqmJ%%`+H;hvww4eaGbB=v}QBZ{L0Q;^iN{eg216ufJU^uDjCz(WA#7fBfNR z&z^nuqfbBi=)nPwoiF3; zkSJb08C#Y|BTs-e(qoOe8j9(MscO0q(M)yVBm0rOQb63>7X_OHfJV(nB2DBd)*ZY2 zBs0)x0_N!HLK_A5^_h9%mweBFRnYpD=`G{(1u1AqMlwH&?Y$|LDVw<|gd(4T(edlw zs|cJuT3ux0bd%9G(@ZTPx-sEtrk3~T&sXH`~%{ct1l+HsmJwj2@1|b&;ru%pHzu=ZO)yD?NF{z{`bF;qbhi%g4 zq(XWK%I$Lncr^%yEV_S?ZfTPWi&PSM z_2>o-CSfBtxiMF_d>&GxWUl>z=`4w3$&7svMj2+j(_x24?mDOy@W#t1JjE<wO+1T=71&XtXz)1m72rjEZdLvNb(lDPl%Tsr(Tt z4%FnKz+z^MYY3et1Dp|*t)fg=wV0MCE}@h8?s&FqCVn^RU66tLV-7%G3G9Y3U|;$l zFhRDJ zAD^C{9v#h(=CeH~^yJQ+^9K(eJp1I6&p-ds_dopb```cZAO7*5{^hq{zI*rPt8agQ zc6@UG>`wRNK>GwG>!PY!5V{eRs4!vUhuNknK*7+PlY_qv@`)mD0&CucHzI zRmo{*rQM>S*d&KFnN?H(>H<$zXgJtln3ACRGpa(TM-pDoiX?>XZGOTMB9up4W5M8! zJ4DCqtR*7SS|r6og1)q|jZ=cWe9fbCqhNzL6(>CrqHgN&g{MIypJ>YH2yXCuql$Df z$uj#iOkqQ`5hV2uzRfUr>f;h8vL$HgItt{xN( z(z}d#F8SKF6n#{;jDm7G&D(mA$X|gGL!? z28K?CoJ_&*quR(jl+~$6=5xGaTT;jlk}63N^bPLZApwe`1OSIjCTTv4&~Dmj_?S`c zEhARWWNgCAE(F^$^(*dS45KRri-wVYu7l~l3vs0uW5D5WyRYc;qQOwHLt7MXCMT_% zg8KQhqh6F=T7XT22^Ip<*HQ#0`jM`}XMRkMT1f--M~{h1dPJ%niySrm4f6;K&}=P^ZCQ0dN=6h zNIL+R_7Jfogs&sU$?E~`dO=FPjPXS}^`WpxQ(As~=$f;0a?q+N&CX3qAl0{~QM-vb z`JNVqDI-*m6z;XJ_xs`bAi_C>bnPrnn!~ARZQEX5z5VXHKRj0l$A9q` zzxrSQ<8S}=@BaFek3Z__{>^6FkKuQ_&9*(fgxPfe&)w;x#}7aH=;^};4`#F3deeUS z<(HS&Z=Qeu)sy=lJi7bv&hbezZSoN0@crz+PXEttw+kQS;R=~ecE?k1c=o;kLQ4rMjsFpXP{hmbl^>unTR}WzGB@Oq0-1~O ziA2yOr0ALyEUo5H7*e$o_h2z2Au)*R>ikaVH7 zq0v$ki7hQ=-a#2n<*XduxRL@x$}zm^3^k^+3-U#0xsFVl(&slvM&tN&Kmu`%nQ>KZ zU8S3$c5=DGI+yFh3M&XpX>ZzN9qj75ZBT_>~lXvQXBEVq-@wyXDt2Wm6vI)u#(+s$rxp}h&9 z-SstgYh9NxYnoXz8L2x%3)F76H_NN8?*H!l|9t)G+vWPYtN!oaz5BDD{q+C&zyGhl z``f>I`t*a@Y})-~aISi~o3Z z@8SK^v%AN~P1E#f($T2Uy|(Rs1F)kQVXC!vqpzdbI7;MXfdkLtNQj*Syi^WKKa+jE z703oJ$@1r9-V5v5p%^&c^D?8}L~)Tq03O)CsEY+rq0_D8qjlQg6&ZNGob~ad=wm&K zVx)3wer`F!wSd}7wM9)*g*Id0l82Prw8#$7CCzx=n?PRxOA^VWW&wGAuo_z?i>4Yx zQe7BjBOq4>IS+)VN*UZ^Q0O?yC$IC0lf?ZR%Oe@K!6rchT3U)u#VqeIkt)#ixD?RP zg;r(hIrvIM-rScIXgVuW=6S$u^gXbg!S9V+(*PvZK`oeC9}1%`sYsZTi;|w<791_S z!9!eNAhB04tg>n71<{Le*m&&!Jx-WgiF~D-kE?)s?gRUE)Y73&Wah{~+dP!jOukZ$ zjvNAMC5pje6P1+5$!DAS11g0AwVEXZt(eTr)wu-1R^ux2B^zaeyu=MBl4Zv=>6AFt zN9TC4>5GOT>7OItT<;CI{k>2%=Etp3R7{2kxk~kZ3~-B- zx9P5a<4fak2<1T-rJ|KcSx^#rAYvw`$r;?#8ncNKYCNRq1lLX>2a$V*peXqFsR7mD%A9 zC{QLOBdI9Y{B#jt0|)d$-DDXx`f#nywy+NyP_M@e&o7ZFWz7L@cPa3?*CukTy($i;K75R{^aw& z{hPn~tH1p9vrj(4q**SOn4oDUvswQ|CQXBJz0lS0o4)AZc3r^H{OHqXpL9k2&0_KX z-Njemymq^gI4X) zvpb5w_~yCI_)#D_QesYlBvdn|S{-sRsMiU@KBg$N#8&Kle92W|ujz5+Mo>DKOspur zd{hQ7?IMlLK%yxej27d&U;*JD`*Acos!QlX}12Ix5VGe^Mt z7*OjX(DFCMP~Kd`4TNLZ=%TjTgV!esC}S8YI4u_d;}CsVA|ZApoOC87=m_b;`0{ei zi|Wq47U#xVbg|@G#70<x+nr$c?JM$-}J;DelvN%GJp&62Ck79V$!;1?^FCFp-$UA?ob$ zy9EXQTE09{xa)>{+BgYArv0FoNjONWIrS>pAAEYmOl!6Pf;or*V2~=L;W9BDuTetcdX^x~{r}zH{?&(1Kj?3&-R_{_ zKlJ>&!MeL^w{8EwHW-i&W!}Ae_dfsp$3Oq#i~XOqw->Koy?uFgbGh5^Dc}o#r~YJe z5u*3f_%`nwp6#Uje*IUY=8Y~<%9vA>Ec7C0qT!+FMY(0Sd`S3BW|r|u(c>p0a#S*8 zi5nKCkK^(b1kWW&5=M|1Qs_L#5_K%943T`~6<8dT74ewEIg5-0t==9ylk;LQWm_JE z3});NGgvC%HQm))#sras04jn-5}*=QFyK1hO3|PZsZB)KwV4>Wkx6dfHwq&XW{J^^ zc-&b7V*a4NJcZVfhsr!H;jlx8#aDIbT&f!>V14<@+S3%jae#HH26Zz)SW`Y1CEcmH zm<|>CgvV7{GR}DP<-Q3~-Z&e!GAowEN0L=RCcTpKo zO-T_;49PVfj&pTLt-a_Cs+Q!eeD+x&IQ;^1>vc=;ei-+pE8=EqnQ5PU2 zkWQp7xkdH^2luqFF|N2{C+jG(ckDJQuEbixXY`ihKZ0OEF3C+vy> z)jDyE#IqpyM9e2gc%0}OiIz8|5JYwo!_a}}{YD@KE3nv3KX;Nu1PK|)5rsm!Mg-=l zmrYV8gMB-WbC#Jr(dOMV79>>0_Djo%Hlte*lxLX2Y`u6^r^M0(;vv8a!#iyS#;jR@ zQnA!N6Y4&M4Xl{(Q}pjx#fPr4P=DhQr8P95u$(+?A@NKT=<<73F1+FqqP}_L&3*FN zz+lbN5sFzDdzog?xF)U${&dwM`JSUuBi$mKVh21eUnGo=RF7t+v3tCJ4u(rMH(2Pu zOBf9f5}FXKy3pee}|_yY0>5^4+`d z-e0`hZrAhq(dp^wvrj($-DQwuiw6Vzqnbf*X!;} z93LI^KH&TJ@7z6aCeyavcCEts`PtJCo_zS>)4O-i-@d)LxO#VS^?tQlp3IJ^cd{DJ zf~45kh3;~@>%QOSX5G{-5FIbm{ee;RH~3>IzS)9=$mb9d+(9%#_Xd~A967#+tS7gm z1ez=rjRgV}E*Gb6?)KFOHN)^sa6PDB~x`69yrl9fmxS_pIJp5+P_ie*$POcv;qj?4W`Oxi0PTBvOb3;jK$ zQ8VpGJtoNH;j2YK=~+&qC1VXdF={PEb4uWBa!`eWuq|&3Adf8Z;Ukb!_;q>oFN=7A z6vUv$Ci+{h!05uE3JWe{D;hgN@KmPNMv8NH=kQ}{{G@?My-jLh=Yg^5_&6)Xe8xIYnxKKFA5Y;W{BrW5;xH)X%a*}BaJ!H z8sT9w8gjxVg43+-jJ7y#uoNDeVBk73XURMSFq>(tND^~DNA5^NinB+XB8=~tDU2jc zT8&cA;Y*ar4z7jOxNB7F!_8#c;O7YscgK05t^R<@YAZT}A&Vj}?DOmcF6*~iXY4FF z!X)L(lEM@HK}p4F$ixh!a+noO9Z@YQWvMXc#vsn*(YBgPQ>dDg2P5UYGNbQCyxK{X zvli8fg^V?Mf260<{0ydlT?x-mJU-wL?ho-T=Ih|Lt3Vq53~0f%V?s?P_&>b^Y%0 z>dp1_`>q){J3IUM4kNEl5-#vfv^78Vk z`#T>#xc`%%{P;J&{?)I3@$&}{?k6;RAqUR79^k|7YdpTbzPVmpzq@#IaeZ-qd^(%X zChkDs(SMYi6=iSlcwTOs#kOHBx@hdPxji!#d}2ePs(?NPg7I$5Cl4=xvxuVrO z3J5mi+6zg1EJ!5qLTsOfDNtx{n1*4@Pa%QDfQCzAsgg6xf%(a_;3Pb?6*)OMO}#kd?Ewt2)&mRe5<5PM2t$OQab))9rGjAQ#Z-Q)61gAFZ zW<#Q)`TU9)1duZ}?>mz#Z?dzM=RESfv2Y&JCFZWV%D`WxZtMf*bc2c>ga{{*<`T%0 ziclPJt88Qr3P?J;`QAe@+TlMyuP0@2PBIcgWDmzdpa4#D(d$V^x!OqS067kdi)63D zo}K-GJ}xDil#k|2y&lC}-q{AGy=x);H^TV%KI9hX@OVC|Ymo&HlAs4Ld>(-VoI*El zA^j@OSvBD?!N<>}*QFsfq4|@W#6n3N5cJg*9fZq3G1fy)A;n!o z`T;2yDNLH;3?yg@)K@$IBEG61t15;T{I$7vvHGPY?!?+Ph(KUhdmVV&lb;GvKqzaL zyll2;rGUjv6!KhyU!jHV7h>_5HQkjYyn(TpQBAUNfJZBOSSdtArot6KM6cspz-+d$ z;5dA>X_IEn?qt?ZC)QQ}+qN%=*X^MJ=zqfmyLr^$&Su+gx9woJ%`^4w z`et!?bMt<+y6Jx2{r=O_(~m#;=;Mz*?0#~fg*JP+|9ZW7{rc_ie)os}{lEYIAO7*5 zoL!^@a%=?4r_ntgbrb$%kiAEd zp>8ao7AApU*=Q1L#aaeR{I4);P;kG=4rowrO16K;Wdcub$gOmkOXe=iJ0=Fi#IdPv zJAE~8#GypkO)>WX@!=6acMs4Y5fZQIgbGWf$@DsmrqLcaE=iI+Gsx*E1@(;%h+wUkEnJG(cvhj` zByv9riVU}l%1uiw8eEu4qv(wq)9yV8+x-Ape?=4pu<+g_3as97SrT|{XdSHxu(uWRS4x z8XuODZU;?nRe9)J(m0gJzqO!_v?!>fFAMssEbqaqop$D*1^0MmsO@*R`7~M5Bsk!r z`!J4YyL%_wv!m_%#jNWA7VAm(qumYPU4z}h1U%_I0Fyocf6|rX>&<4l+N^sRP`?*A zjDvd*Zo6JAF1sJw?WX61PI^htqel>dl)k|Lu4G`~Us_e*4=m zU%dG4`g(DYz+YTk{OM1BZkow_HtYWNCqMc8;e-2K&oG-#&rVLy&Q7PZ*?O_QT3+2O zuQu&=Fh3~;sQdY3UkOZLv&LqJQC)D33Ru#pnGki%L);xd|5;`o+gt$nYSr z(lNl>fh1YqQNG~2ejUi!FzBBoM=B&uQb^Sb2$j+$-+0DCGp^Nwm@|cDE!`5e2p{Sh zSq{fL)Y!^o9KxJZ3O${O2YHlF=u7@E+_FpqVdbA#DrUNz<}%LOnZ9pfQtWXQ;O&UB zkj#P6Oq%C0k)>K#uW6uhMbBfh(dSmy;-{iH0v19AvS+iB6Xq5?XKNQ~ck?SmNHc0E zF{q&y#?}GN>5Ji}DaK9PV2HrM&wHrPfts3w*FH)nat0m}*zd}FHXw?Wauvv>bpQnk z@`r?hiISXXtY<7S075m1L1^g{hc)|XlG03@~<8m$Ikx1rHBN69_G7KRTJhxVkLMn&Ecp--@+*5ZVdAPx*h@6I#&Foo|2@O8?I~nYT3_}z#0=V@M zMvn+0oiMk7dtP%-D-aq|MAd#Sk9}mo@I(^p`>76@E9IsGkaG-jjwb~njA*#H`rr@^ z+4WhpJLX}d8X@UXV5JL#4mi_*@4N@$AU=q0OqYzkzW?s==FU++THdd^PUqd7OdW48gOlgaQT@9O_{vtBP&>t!FswZB1T^VyyA z{-4>b*(ZqYrqk)9Y2Lnl_bfKd-K?x-Zl0|LJ;(?#HxgcDCDWH=B0T)%<)9?D_^^!#M!m>pz+AeGX%ik|`5s z!m@5l;Ulkj!4&RjAr(7J=Us6k0A#iXj6uYfAKJN#E+*4?Pvsf1R^PejarDUFv4mJA zSvsy}I;s)LD zN`^}0)AA(*^BJuYHg%ii*T_Mib0?<7-<@yl}C+=5{6bu!s}3RNFjve zNXv>s-V%%^HRME*PA0w)OLEG4hz8m1O_*C$Q@}YUP^Nih+n1e^ftand8Xu$OHyD*I zR+cg6NtAbIG1Ab*g&dX;qcB*tgE48(veOjRRV1Qmkplq5g6eQV?;A*lJ=BJphy*RT+ySe#~fjlj|x;1WiL>f0>W-_ zndU6YT34SONCrAb%@eCLgKb=e2;K#x0JJC$j9nvWHl@4J60v1vFXT!wp8q6yybP1} zqz{DBdov934KzrT0sh!|_%S|y>|mHh5snBXIao4(R*>9b>ghL@bJ;l`6W)uBl@Qec z7AI)&^2yX9b7^uG2xo99C{8*V)KAFn38Yld0?myFKfDn_D1(m3AFmdu=*s7FBvc$2 z_T}#+%_h6^qxSrGJ8Sx)xvT!)FQ=>RgIFP2O6zuNz3_7P#-&k9)7p#AR(|E?+Neq_OS*WR4=SqE*V%WZd) zUA?5JoB^a8R9|my@D)!@Qf7KM4;LgP>PcE=*vMG}2qRyw(%tNBw174l&}3Y)F@r>H z{U}QG@=N7xi6pu3tY}8XNoXYocL=D3NsA8FMJTTu^o_)=ZsHjt(auwZ6xqaBJ!Otw zGzvE}rF*h^lg-zRvmq-8AYFD(}P6+i_^K4Ho`d=ZX9NCho6u(`O^Y+W4_yLCZ&OI%U@edO&_w(BEE_ z-n!|Ch0Y+zlm+{-@gy)`v!OTAd`fDx9cie3ICUaPizRLfody75uj2+6Be)C9%A0_? z<+9n$D&iva0ThUywM=B!8CL6IXcRYUaZYWelA02Ja_MG_J)X-T>FMX@r?+>eL(=Xr^&j4XVUGl=|B-F+xjR2JeBxlx@RzW(18rE0C)D|&YBJzP zK2*)}OQ2z}@BGGp5yx48h6A^FMNIgg^p*n|xosAef>zVp4h-FV5mxw5zNkZzC3!|K zTWFP1nNIH0M-2etg^WR-db!L#JVA|;;T+v(@o}312q+bCfOJ&Rw2bFC$bS-6ACAd= z1G7}DBfVjnHtBTUp3k@QrvJsg3aHuchNnx{036S^C$nuc8MXes(x&aI|J8bPi0^HH z28U5=+M9qEtM#JYJAJz^(F1?>|4jCaf_w(G+wEepT&;TYPoD!YnH*}IeL7gL++KHI zWwG3j3Bg_4u{RyGhuy-yBbcxtWH-=6#RJg&j;^tpHp8c~-eS>1PA6^y*t3sr*|%l= zLY`ZXACLLISN(=6L3ghRM!F4xWSkw9`FRH;tW+u_J6@KKPbn?uvkK^NVoMcM)1}DW z$M_WUs%GSifiEx9MmEWWQ4+A^fja3vKw)e%KcpN4{davp4r!`DUQu$UA%KnvYvBCuTTpNgVT3O}3rmR)@y2=A5EopK z+Oh*N$P7G!DCTw!XEDYXaqGT9w#-PyZI*OwgoMhBIu|x3Jrcd8F$ZFN6G5ZL&{ml& z3Culol=6xobx|#n`mE#Y=6Ey!5;-P(Z7{~ z5qNMwQii-dJ5eX#)7^~QL^NTkoa~m76fZc9dA+i83)Wb#lpt#cQ5J!dt!#87O1}<1 zu527)4>;#+2pSAloajXRtwG{v(D%0nZAHn#G40Z*OBBB4*qAst!jR0f#7(x>@gO8= z61b=`(>kF3A(gT~qRR%!ol)0$80riSRhC4K{XiZs)*1K)eTz7R&LO161Z8j`PRa{PpU3xw={QE`a@@9nC(wciaEcVzXX! zzl*kvHnyGzw{HKXX0v(L{1^rQ9DZ_W8|Jh5{>%2Sc)i|S^#5FTKbagFfNZDPCkbr3 zzQkw${>J12qHM-3vj5NvgA>6m_r(@G45G_TEIUV8o$@7&47dswfLJieInABC z@dm*&_TrOx5|y2n#Xnhu3iGxGB-HphD&X4DY(>G1>P}6q98^mFXHh75**>$%AFEk5xI8#Uoz#?&OH`#gmDnl;m20l9?hA zGN;zLXNjM0ME=a*vc9p&N^!Tz?-s#LcI{;{IqmcPd5lUxm1^1i-gOsmCiW$y6l*L~ zb~3C!fGsA#khU-@>=5R-s8`&EVSpW0=qmp#)cuM_i}KlQ`^tm<61T8vsv{M~#~W zp;Ittn1m>nDQ%+$KV+&vWx5hHm`qqGFi-H&P-GZQ^uwo_xGaXwH;k16kdzdch zs6Pxv+7Rd0m?Q`d`zV={cHhT*VrNI~>Aane(wLp4bvxM?_Wd6|o^4OM24Ip4^j+H< z05+@jdbRFH^7|#gq#1SqHpF|4e{)xqFBZ$iVsZGL{gR>W*+F;jod5X8pMCo2Cta0) z-T!m5-v6`Q|I>8^AAb13#~(dCJw5GOfa$c^Znv-By!rlzSKXKDT>*n+X!vaQRHE&s z?L)+9kDU%LJO77upL90KBmc?%H#Ch!rju_G$vZ=Pm;Cr9g`Z1CWcR|4Fg}}t<}#}! zNBN9Z9Lsu!uqQ>`Tb3wEZjj!M%7-$BMNceqv&49Di^N0IzAVLXx#bBOaazJXz=aG9H#ADkzhn?Yk&yj zUd{N}HOnP~^LuHQ;sA()Yz#p0@H*61}0?qJZnZp#i8_tIb+>GIB z0*w;$4A|Tx;|~iu>nTeu6`tWFYgS}tPO*4o_n}I|qNutyBgw2(fIbi-9#m56v8icU zs1`_BE|rq@@1u>(#t|!c8$^meE+Ry}=lTLuTda*MY?Va9GudGRt(JbEIObH7l>#`? z0?#fIi^--(QUJR63xnFoq~j-WZ7X7Q6b-%G;8+F^9}gzr^P|n3-UE=+04CR~>D6j_ z`1g6UJDX<{upMCko9$+`*{*x$&aV3wn!T)le|Js9q83+|#r z5SWSRTV`Ama1_i17JjE-2si@EjA13j`(k1xiD6(&r)C(w@v33;Q&~I=EJCam6R(I8 zBSa&GS~xrtq&PJSy9L7;M|FQbfTE`W9DG&@g^fGZr09dB-7|QBod;hMHwMkf0Bste z&*~%z%e4-b#I4@Xn`Rf3n`R0!%5O^LyCm?OVyL)emmo=+Vi!2F}{NiR$T z=SflVgUY^u%|w~s1yTOM3e*=W_>ZxU0YbnLR6vImKOK$kfDUZqtT6%+Q;Lm#(hN6k z?BN0t8&7>d?Gm#Fv+Dm=zgE<5X-;xh*NzWBa1yfIa|Hi_1~w2`R5y@tMrjJ&Vjfw< z(1DP#p5LU*ezoFW*NGejp!0nO2)rf7Sc1>MQPgPWtp2R(7l%CYEg`STA*!NgK~)NG zo^%2mW7k1oIOZs4F)+^{C@{H7z|D=Ya8i>5MnkFkNl}Tu7m0t7$!y)DS@H!6j}RL1 zb>M)6`~nqw8u+N0x@?5NmXv$WQSy(I4|@YGc;F3M9y)j?Ko!$&R394cA7sN0#gQ97 zoQ5bmxDG3w&30$K1ZX!-=qDGe>BVAtcGPzTO|qk=J({)?^q8Qu*W2tp0PEGdE4PwWDD>nqU-=+$MXlYL&ErN-dTH0_tL|zzpYMGSz*)WWBTL?#D906aae8@Pdo<+VH zYHti*FLbfsFxjMG3dWSQvKJ$@xgzo{gYNm5%b(|zVM$<0u8h~gHp{GafT(RNCM6>( zqak>`Y3J!obN4?{Cc06I7vIy6nnGsf@eKFKCz|IPOx0K5tnhJuH!@lv`VX;sn|VU} z5Q|k?of_vh>&&}?p&rv=%>f>;IA}wRX{GKV2Kg|hVHk);V5=cUD%1o|MmWbrUQofU zjUMh{ADc|@RWb+6S16*^l*3xbX*#a8?^4O_nNezC0#AqB0-kiG%h{X0 z7)dz`qL8r%Z5)r*hjK3m$Z%F1oDF4JnFV1FluXGjUN2T1ZUkC?M(^aNnWnsJgV zhlrP>u1Y5xklpeS@aTA9ujV>AXj;dG?LZ0$L>H|KH0~(wF7)BU++hM4jhm5CKB?mY z3rpXWf({}~Mdg|1^$I9Uh0jaB6hQ8VP;ycCQ?R0Pf9(~5C29{1MNSElr1Vjy`DlhV zK6&9`d43QZO(``o4JIoP3;GZ6#X*l;-iI@s(U1KsMHKd&K9h5(5TA&2nsmS6c(%KH zv^|*(rSoz-d3Q5=yPQ30*Rx4q-|tyEo1^LWdes~(w(HI2db#X=thd`{GMP;#&E&9Y zXr|Mn*>v9jhy7CE`ugVE=g+_X=K00NMOUd$o2G4h7jIX@KYsM+FMspvv$M0G{`B(~ zFTT6Dyy#xg!w2_&^y#zizkK%TvpaY0bYJo9+jsx|`#*m9Z~xx?p2e~UjGi5xK7H`y zgZocT=Etqswr$tj&CPPTSg(4a(f4$qN&o-TE`@*#_8 zk-SzMsD2!~C{cM9Mlt4U&A$ZPTlJEP=N#xWyeuuP}^lyZk$cf>Sqad%5( zUkdtO2-#O7FaXXn3I|4zGoj`{@FaO;IU&I5VcyN!ElXxW{h_{`LFIiLSB>LvFqfN^ zXiIKbz$acRN?ui&QoxvGrmE4UoB@acmOyF0;=M@DOR;lIv^=0Fz!at|5m|vMX05u` z)^fws>+lh<;GLH*ep4{mnOj{_|H)9zS{V65eb(~mxS`trN)-@SX+y|zaWA3Xcy zqo+?lI5|1#zU0Nl?t)Fb zU2WE@%_b)Rbj?Ti?er3$aaP#2SZ^n*7A;B7dk+9-X!{|ltDG!zp>qp7#ImHJbA=Di z5XjHQ!!;ZId!~>b8o#G7#0-InArX`gKdMnpP@ATEQF8snDoJFzDQQHLk?}<4l@zIF z#&jxPZ8%BA)Gjm#d!%c!qI-c@PdzAT_?xC?!S;%5g>b8mCDB~*y98q12MgkeRP*s| zd}3*rWx3i#P*ND{E5{9ED6ok7&Gfkucc37cVa)1{D5$ri8KVHVQ6&@(v!MezXQWS> z2VW+dewXpdA$8M&JVl&bNLsGb9#ewiKpS|Yfe2`!oPw|dV8uwu0JWk`8!OlGpf#3d z3^Ov5@`qk54FU7Y8s!TcO*){Fn}8b105qhSQ;b?i-S}KDOl^H$+NIs31kpNj(!A>NG|9V`L2D-`^ z8~mn$z@(*6vIO&}7YtmjnCOH!<&AT)!g`}rGm^ltwjK;s9tp58dJT|Q-uY+-EwwpK z9OmFl+}8PemW*QPpe`jO()x{~B-f1iwPY~euIbj^>w7^IFeJk~vWutd zxlp_&7$~^;JC${U#iJen@4BsAFhtN z2H@?@{QY9OYKJs{o)k3SoX$7z7qfQ%O}A~kT(1|)oOlfB5kJYPITK z;PLU%*=e5~(0xajmsekZ{msAp%fJ5pKmOD2|M17_tLx+W@q@b$o<8{C;oS!(M<=aW zzX8~_>+N>AUaflxPZK+S?tj*VF6uY#S&*4gfSn3nZsIPt#4seGq`tFb zZ9AVRFzOnH`9AS-C?8yO#29?aQPEZZO@pr<}EnZlwjk>|rko`iu1g$R%b zRmN2=mFPenv+QmJ*$ud}l(OKAHe6m=8-I6DTlV-M8ipz^5krh8i|c84b}VYn`$(*z zE+~ViCE^aFnydf{vOmPMlrK6I<1J8b5WH_Q7WTjj6m){9XJ~qEy6q5sGH9^_LNaA5 zJ-DFr2G6V{0K_bDlXQu30KyPfF#03gC8MhuiK;?lO$rT|4XFYG6?#>+JHtft1ZpmH z3}|johy}JWkI7VVWJZ~B+(NSkOIMCoLK0oV0pY>|+UM#>g)IbrP>z~vcsDvuERD=K zzDq@bPiXN8H>1f|1y*5^=Q$*CdQz?-g|3(BE0RuUyf!N0D8f0U(tXx%N~(eX)ksmr zTWv>GgRYt1BE*%*MpIdxYkJxBMQiTdo%!jW`?GusSmAcDl;Tv6N@s-Oo9i3Fv%0Y2 zh@qcG(nDzeWFl{KX|@Yfqft}9zIXQ-Q}^R>)&iHa3fRFJ7nxpUw_Lcnh+sT3WQ;+h zGL&{Nh9|oD#9?P_-HjRc&d;fIv;B@nD z(StHCmeY4Pv(0vZD9$Fkv!m^u`Sw+_TW$L#z#hBVgMgOnO?$kXPMV|Xbl%n4HfwNt z=j`73+5L;FH>=gn;r@F2_T9gJ`Ma*(2PVJ##TQQ=J)F(w(`j?K^}34x&iQ%MJ9-D= z=k?9at5>hTeg5Ki|Ni@b`sd&N`Ojaye)FdLLQft(`TV0F|K#JJJUG8U_=)$CW7}=J zTCG=`^&UUk|I@ved1LeGZZdwWT?5d)_|<08hN8+`6_OPNiOOW2&(M=E`O_xl;e>YG zsLX_DzrZ!8(Bn*1ux6nL40GV}?^LNSB?zlD-z==M^h9#C9(@Ibp4qrt7!$b>hZR}Q z!Wu(dSQ~|KoY4CK@wfc-k;|nHn}%Nf-7N(mV_qg0bAb9>c;?z z49S!j2J@=nH|!AglsC6C#VVgKGNb}$sGBYm_YMiy5u(x|J3^CjC7(TnI!NMOq38j` z0Fk&~^Dfbh^rVP0oBTj3EkfcY01Td6V)}SA*NgMzrV~J^`+0RQMv16ZH+{nr>&7ik zM-ubyC5YkBqdToLU$JcVFqAC4q!}^Ud?Od9S!og|v~eP;EXBj%jf#cchLrg_y9oFt z?y%i1Sd36TYD>qD7L+`cUyud(W)dqP`!h?TT~2|xR-4Dlrc5hGDbj+qZ)mp^mUKu-KF?SPL-U037MCzD0A0A00(>$XyK)Tvm>Jy72 z$m3|mm;xY+PcG2R$oXT8B>O>l14e@gR&lx4=pp2Js4CKZKLPRBF%`b;Fa&8m>?pq9(AfsfM$B6 zT80DX2dw3!Mk`GWViK=WVVMX6G9r!nS1t?)bF7ek!A^E)0ycdoL5F6637vdwpZ`*$ zw!uo@o!`s6y~ZeEijyd#{?v`1@>zMQ1PFq+(vmGB6-;{Mk!2k|YySAj1n6zRyD9gzi|Z~7 z7;9)vFOZ9OmL)dexHt_5LRrpuUy5Lm2;#Re6o)Z9EesxA-(s?8Qel6koHv5>cu5DP z%!dxy$ah2JVj)t}v{JW2RcU!%F1ro_CUvJVW(cOwPQBYr@L(W?38-zM8HSNoMtt`p ztdlkUu{diOYKjT(l$7GyDb3etPk@vw8qqdx{mmhp{m zH30%w{F-wp3kdGl@)VAB1EOuy#b5qd)IcagP%xKHEpWjH&Nma$N5sBc+Aa8wBAQm- z{bsFXe&#@fWBjTILQ|0IcM`oC&+4cWH-8fl!~m!i0mi9unS|;YIj$DTwj4KwFcU?m z|GWjx_zpg9;0NbyVh>N(PtMj|(fl^e+8w$!V6kr6-T=^th21}0-8){-nzsABX-orH ztyVXy-gkR8KRTVwk7u(@FTqE1=ic^%&o-OoVtKV#-R!K-6zF<@moLBXs`i`fn|JTt z{^&=aJ$(4!^z`)j__&|t_iWM4YQ0&nR&U?E|LW^+{`Jd$`@1I{7;A9MWOU9i*BGl=8$RXoZJe5ly*IJlS6&$R)SkgD z8}`hf7xwPH0SF*M0v4D=90v8B;!sTW49P^|D!EBgm0Gzr<~cNO$)+c-uCjnUo~UBr zLOu|{yAIHpXyOkgU@XQ2Li~#8@z9bnldw_LS7ERo$2jW9ej=#9cQqU zMz@w@3azO!-7o23*i7iLJoiII@V3Kq7G;K5v` zv@^JMY(*o}&`L5y@}!3RY%KdOPdSkaCrI!dLBt+&L_kRCX~3jz?2{vR>-GAFA6{QyFTVZu`Qt|q zpFVx^!3Q67ZNO|cT`X3YmzNh8m+#-dfAi+;n|JTJ|9^RT)%6(N-*tR+{OrlI|MeID z^MCyOZ-4Uf=V!-fo8I`_OaB+E^~KHg<;_jk32?PMZ+7=iHuq1r^B!;7|M#x?zgkbP zR!#N*OuHswzCAzQhz{UZPJ_i7Q8cQYrDcT@7HY~axg8dzx~7|k6lbxC)H4Jq@{0}W z7TQ%g_OR+0M0fz~XN<|j5rihWK2}7UWiGwZ5zVsnSd(fOozGKHf}z9Pz*97v5)4wR zO{7AWXDJMK6B`ROg@-0%Oj*G#MG9*@q%|^a=!AReRU!*C3Iv^Tm20IWz<9iAMixm3 zJrp&pT!uc3X%!_#n!sz41EviSRo-Y*+>EVbRTf39C*Z_^A=< z0}Fp3#bvoMcr+CrTBXxjPNYN%phWswxo(E3u{x+sKb0_sllHDLJiWn9Vhfb1sP^sJt{hY78Kz>*H zyPz)RR8|M1Dt01mq#(r>&UFH#vTo?*bgNX>i;x0YA%R}wFj-HKj%U`@`gc$Ik@;qynEUo- z_I^1#JK9d0bTVzvkG4nCHjlgaKH$~m^5$l>N_V7K_)fUw`%WHxD1&fAYbTLj$l}th(yI`@i?^F5X{UbYJ;! z0nKN#hbLzrJ^uJ_e)gCD^Ot}3i)UY)9-prEv;Xa`-R#=sW__`^zFORLeF(pyv!-X8 z-aFn*C&OpF*-ftcfV0_lH+UPG9u?XiHSMiB6HB+4FQia|MH>+9p~DAl1+B^;pchV- zG8SuDX%~nh8A!ga3oHuPd%*+MQk6ltxnjy_N}2L_eun8Alsp}`G_#LG?(u&$bi2!w>=?OS&;7$R&ki!BEAwVhSo*^Tl=V2!AXsm_7+#5zUCEF|*8%IdN2wev zICrg)p$^&b=bBl}$A!G|N<0?vMPhDqxK1ob)u?4b`HJ~dhweQyCNT+zC|h*BuS1>9 zPkR+Eklf9M5VaE!6WVm3D2FY?1Ul8xBpj=#B`Sy>P2LJ!aYa7pCpqFkoi@@`{X>JH zrJ=P%QAIUHPodEc6I-Ajyc=fZC|LA@(SmS4X_r^S%<@IRE*A#@Ckxo0D*M}lAg0o* zn#qy_MoSPK!)E*?Qwmu$RU>AFEuV?@Ac&8kk8If~P?>B{yOB5%GV6=lf1}ibP+Smc z9KIB$a4WK&S#TgiH+xIcaQcB&k?z z0|-Zf2!I(W-u{MI_0P){h{C)JBKnvk|6=gE8A_!>oH-nD-yuhy$D=8o%-hH3>xXCS z*H`n!ruqJI{=?P$!Rh+WWZO)v>jX~wNZ@v}rvr7rzbn4qU0=Vwy87Vmz4N2v^P{8P zZrA-yv*~n#*~ybny8n3hQVbA7XT@#4G5q?Z7# z*PBhR0P6pD*Y298nKsSc(>p)<@JE01#b5pPPyg~~pZw(P+N>cM-gw9>&^9Y zae2LO0J`48iRNkE*xlpp`FuMW->+?p>(%sn-DC;S1U8-Q=2PQ&VL+e3m+ zWxROU6Jl0W|46}V&l{zjNCSmwg#sZ);850`jCSK-QUsEiXea`j zlSxhl07S?k<{5YjbdCSR-kU~SnqK8$?>^I=Z_QIzb+>ww5JFSLCx zFgOt7FeJ7x1jqT2fDy{DmX(mf3(E;4%fT|kT0smVSu9Wl0SrhMprIwT)U8$z)ivJv z3_IVs=X>{lp8dY3BwKQ5N#DDwtE+CEd+v9>@!5MnLq;nq8uUyW@+s*lEk*mOVgRo! z#V7>18ft4HV29DYcz!4Rx4%+b;khT6s=h4=}1Ti7ag9(q#md;q+6VvTMugKqvT(sKWiqAw17+Yg0DwxLNIzPb? zD%Jv$gV|IQF@?1%(zhIX3LqlaQ3>Unc<@bpd1Cd3+4l+)YU8xTdEK={no!kIO9Rn? zFcmI7oM_mf@!kZU$%+;wAz}?7U*Q2w;bFOYRH_9QuU;L@E}uAf;>60${o(#m|A|{G zO(k%2rnmT(n-8(#Nu>RU;H235=_Td@ydRwdO+iM%^ zqt%t+U~6sjqgNllzI%OlZ})I~IGxO?o-=DLCuxeNS`f+(ej7HM-TWsjx04ml{y$) z)C@pllc#-Axyjjm&>pCTDYAeMpt7|RxiKp7hU<#Rq=9R$=6^1sfr_VH@hD(NHj6nx z1!a^>OCXPznJ{*iDu%96^`KH2w({pi*F?M5V%Aa-X+`D>O0R_}>d@s0*&rb~fagHd8{s#{bk3k)LXT}m-458(aRM>n0i3QB0U2JsCkFqPu82lcH| z6cs7l>2MA}*+2{XOmRoIFqzdbfo0^x(=_JUry!$GY@0-7hhZvVTPA1`q4BCU3o}w= zKT?sH1Oqp6dd7e|R4iW5!M^AWaei0w)U=Yk96-ND7@UaElRR=saoZ0M<|S%-L>1#Ky7W|l#Tm~Kv4*J z36@8mV1-RTVfu+O<19Nrk5Oa5gFHe?v0PQy^9t^_Kq0BNj(SewaK)%U;*tP9^BsD zxw*Hqy|%V89Ig%rM-!PHTk$kCz{+5F`Rt|5wbi@MUi$EpANuf8{&%0!iN zvJ;o4YI^%ZYa|Lc0}r~>f=VrHgRFr(lt2-qaZsp&2_fpmE=^5yCJ|Vlux~R<<3f4{ z3{d@31nX!aLoT}SMd5H9(+0i25bonWU%f%Ns#yagBV=WH<-ORX0mu;)2RtZ)5+^`} z=yFadQrpC~#v`kh0tj=fIof)Llp`mO348p+XgRW%k-$wOwvv*|NN6pT0F0I7O438t z@bc6<&ZAN>cW};+CUOAb6bmVN>&Vo0OLvW02ke+)_t)prqBYnm;7S8W+e<2u=nWSY zIm}%EXPImgnurwxG6CA4z=VOAjzB2$5GNb~X1*BblSnGvoz%lUDchA1Doopjlh2C? z1-$qrJK?GuzGk0J(k4g$sj?sDmqK1kVrhag%d*jVkXZVQn_dZ zk~q{cw)-lMKls ziKH3t z%t5G|c%*{G2F*?dUlLW6+!YL@iUkIGAwp<0cNDRm%7g?G(Ub^XoiSs5Hk7M`%q2Lz z5SXddM#376zZ?ys6)xArWHe1uCkXFxMFbM91xu$&jCNhm4xDYP8*JzHdwObPeC5=^ z#qGoEd&7fq@9Ens*Y-x|w+~1C+1hY&VQc@~=E~KbmHpH+De$e`ou_Z!+*(~fv%Nj& z_h&O5AHVNxHt(#jjz;UNmriY+IeCJW-`iWX z|BcwwR5IJElexcVz6Bml=ji{isapvBPu7OB)xng-yn)Y8>Hv0CvRiG8Y}Eu!Z7qx_ zGoY5r)E^!-Azqpkyizp8w9<1|BK`{&y-Q9Hx@62Xi*vaIL%@TEiTWC7Xr*XJ>Q6f( zyomE=i{}sIFdK;0iWpU0_Sv4+gN)>z>q|c2kh2);axhwg%t$k|5!E;aPesqT;5oGS z0|@eoDA|)`$=?Pgod39BWR)kC#8v8g|Fh6}Iu_82NrzO7fuzm{TFqg10l}4qaa}U{^ zibWV$=SmS(?cvg+L0P2d3f>sd>vrm!{AumoS3ylO#GNxXeWpmt~1bVV-lUWR@DffrJmX zML7H_-4y^gi;@B(QJxi9J%CY>crPaCo0JSYInf;^pzdv_&LmTH#Oo!nD20_|3%@|= z<`hY(#CUTAu_UcmMKSQVc&TGw2`nh;GAHtf*Klj$ix;#mYEfJ29gNtmM&A*i!dg!S z1KnYcVPl9Tacy`HbAl{AbCfy08~{MoP|{Ly1nn5~KmdJ*((FyhsVvxfYIyIW-z8MG zCB@1w10m8S8CAq#A~_+~jN9T{xBPAEKv~N= zs3tTJ2yk*QQbH^Qu{sw zFo+#Cf&&;@qqRN}Z=s;ccm=Tsbo>?36h7@mOTl1@1L%}Wey{S}UY7J7+?GQj2|E~4 zbLmvi^>GVhfxI&~2`$`KK@Km=%q&o{@8J~C;tgkAVP}EcE6C}}PuXvI)ji+#lW%{~ zn@_&=^{-wV4TKuhsGF||{n}F?Bk_Q@JRb|1Qrxo^<_dxxoGPCR<)c^&=e&dwcTvg0 z#u)fW11@xqU1ws4VDcF2GL%F663+S+ry5{Z3x!Ac2*I2l0;c&?R`6StFg*2O+$CbZ z#7d&Xcn_1W5x$H2yMv-TQ2z^ARuw-AWSb?-7!b&0z(GYdTrd^2hlXY)_3!g#nTWnn zc(O<;$zX;h`3dP6!FhbAAXry-e09F9>BX!Y4r2qmYg3Z~n%qL+BoN}Qev4E=9dnp2 z6&CamvyX{FkmPK3{haRho_PC1_%FiWH3XmU+F=A3^ zhG4v=VQhq00n#8USV~3101(T^_PGa>C4qyz0RL>As8DrMI31=(8yA0jb$b8V{k@}k zi0a-^|HIc-Pj8IZN7G|h&GFfd!_AdRPjnHdHJi=$4i28aalJ3S*=%xRePb{f^n3la z(Mq#byhWepFG#p1Jl{`;Bar}SPx*9EE}sEr#4TmudL0IPL98; zsU>y}_vi6plSzZ}cMlJD4-and?Oofw-TcI>H*eg$ed}<1)NHwte~w%G^H8(q*PH@$ zyeG!9{>|M{Q_>tx-2Z1|W$tS^OVAZ>siJx+0hb@T3Qz^OeSP*zNtD?G?Pkjkw0YQIiCtwI_ zAqxII`n*vHI7P0340SOi9MHr<;`D^nO=#;H29tjCx4?r0_rDtUm0-`Hf&R`i41t+# zcqVI9X{IPiGi8-O`oNb`q2(YV|@u`N6gW@Cul@tC9G**A+`M3#6m>1^>nuzcd#gyn4 zVThV88{ukbc1f6jClJ{beQtr10qUCKDl&C@8GR+?*uD}kYiDE(r%I_-pitAJ(nnVA zV9~J?>`L%3icIYzf5D$m%I#F&P05oA@RT4ndxpmDppb%vK^)AuRag5^Q0Usmcw)W| zM8ilru@8)hvf-z9VhteStlyg=BIJiAtmzL$yYl2+lX9jC)KubUF$Ormn9yG2j>yqG6>VHlS>+uowINI5xIJbMDAha^3k7o; zyVtV&okZ6kO+g<389!D7F#UlG7%$5tJd6DPn##~M@lYr!!+ez7my;+5^8zO}1+OgP zsiAXLWLTgBrt6*P#qKKQEN;O`PSiX2kkq)}+zAVFA?gB186=D+Vz$rGxv#?D^w#Lc z?szuSAN|>Pyzy7w`)fb>>gQbCkkA*_(iQ?nv>tXN{OI!BN>2K$aR1}uS|!-M3s zmShSnXndf70LH)VLx)!=oD5#=CU>lggpkCJW zJvdQ`^x~qa?T;h!R976=3612CJT5}a!x&&3$WtO(nelr(lRxK$>Es&t27;qaPEjqW zXj;yyNiU7_roxq7;k4(waa-o*YS+_MK(AhAIvF$yQkaCZ6o7wd!l8+T+c4P4h~Lc@ z;&6h;@{%-|xZvZTCajB=mnj;U>F1RbLITn!tENg!HCQ?^^Ru+J>%9*XNTisxPL4!p3Mt@ z2Kvt}|ECAzdDidE-MyPTw{Ps+-aj}vnv55p2YdgEwK|-gX$pX~$zaf4(1(-$Q@eBN zf=vMCF=4CYd9+!7COvhb2R<{<8ccKJL>LEof|W3d(Qi~B1}JJ!fntyw#%`dI+ze2) zmO74UPc5(&@6w~Qkr?bo5Q!v#iYFN84#6%>qu4wIUZP1K;0EXi0_`?owJZ{p?4I-| zm2rFGd3wSq!i5F^`(Q{|cL{Is_6iIXaH@kK->Hslzgda*^(IKP zMI`F0#OF{vr`yt$yl43F2Phk?6Oa!WC@!l#&A$}%!Z`SFM$x9EWHzLEmoPxv6z=-`2v z%Eu(h=jJ|bD914@TP1KB0fyjc6z>{Tt21R8Q*TkZtt{M19L6GvBWj}oZ>kw@3+Nv( zyI9%BB-rJk{|BkiwLd1Mgj0t{k{4-3`QvI&lQ87Ypb(Nf=Con~5yqiU8G(Of@MjT1|wBvH%S zJ_U7vnAAWWPO21Hy++u89i>1bWrT!^acBsQ7gK&fWT|2!M5IEz;=s1t$`caRQc@!g z*g+t!j2e~Z!r)*i$^GR+3U^RZhbLVs%r8K0s5T@C^5^xY1~`4Nxi;N7Xt4TheK>9K ze_wTLb#m{i{cF3!XLg3q?2dB#biNLcW|IcRA00L@y}|s0UVk*`4+q1Q(P(uvS{aUp z!{K0#*QGyC_!_ORu8amFYX%%nj`j`?4(Cn%xtZwEbnf)Oe{^^-*Z&?10Os{U^ZR&u zTqH;(>?RF*dTQ-8fnuPDNm*S*LWe z+a-mU#?s&2;#&Fv>GGIUpJy&+9HdM)#UzqM?z&~Vhq}`P7wYC^l?78c)D1GZK4FDVHvUpPpg1AbQ zBNg5bW*vrJI2vP7tSg~tzG`PJ%inwqAw^@cvM;pMT#FBUIFkS+oMKyQa8qi%XOioQ z;H{XHf^dt<{BECW)T%Wm$@#;nLgNvz%Sezm>kqU@0dmf*^Wda7i|nbVVWb@!uENtX z1^C2nbZ%83aJ8`~WCG6ptk{mxDCiZ| zgwg5BXcM8lklz9x9}+eAos~vos?fj?;2Ww-1O%oj9>f6Jk(i8yLt@ekiVv-<>y|=9 zAzR^MO>2d$sd^RwY z#heL-g~$|cD~nr^8z-PtT%eVHad@EH^9Zfk;=bEUqrvR__Tk;9_CI`m?dJYqavZAF zliuPk+d%h&>Ez(>aG@XXi8O`4XgFADs(=Rm&-p)dRNj2OsSs9IngYOEv`5F^cr=+b z@1nCbp3dWb50B=*#*?GzY&yS0<|Btn*WXTzGaE;jPaZbEkNUI4p1ip~eEjC>)g9;m z9DlUw>5ZeNR2cMT+$%(5H}xE9C8Z%T(X*XI@a&86q-Lm2rdnvNt0{(yqMw}UJ8SZJ zr^izMpSWB+_y)RFN6{-3JY(9qYzpl?w5YW~XEL@plD)kZns7=fiLIowCQVUZ#@HL( zb+sk%1WXB$RP7z>>7RQISZff!BWCz9`81`%wjKJNNu;EfRJEqT(R0bY3|YcbagPX{ zm=fkFMSWxcKKMQ-Wo;+HUcp)wVo15e11};bc9}rJq=cjaxpF`Glua z3Ocb)juGQauE_;8M*3#%%0x!U~H+)O^CivC1cjdR6Fyl|*gw zrEMmdXK9i$vy-kJyyBHhOB7czPc&iYwA7}fZ=J@339a=m*`-Npu>kyOo!eDJ#8tlu zpdrg8w2%f1dp0BpS|x&MAXL@7nHBhd8oUts6Kl;OCPdpP^Wy5o2;`a{bK+;|(HpD3 z|G^V~_W0Hlw?;GdER%M%e{8q3*OPv)LH^DEd&VfRFWQ#PX0yp`9v0kkm~?g=E7q=g z^&yKl6N*}Zbz*Juf=jnwdgbP4o!?y>&Yg&E9S+|8)b^h~cH+HHZS5WnY)bI_3%9=P zp{E`^cf0vB7sb?1mPFQ_M&QN)W0H7*vQvU9ZsULn^e-`FUE&#;iF~@OfulhlAF>D( z)uE6iR1xF@NLCF*JHA7e8e~#2$Bbb~rFSRwe#Hj?kqJnA5T-sV_cPUV+m9(guIEZH zOY@6}JMQYiCQyKRAJGgd6q-2_r4lq>WZzfHr%IGM=Dpk$Z~ie*jr}&wyhc(CF)DCB zG!%m;npIY&21swTiG)`bWd*6STbc*we$BRnl2LrSvJ5a%}` zk-(hT!o)XVc-D0W4>-Y04K^(=l%~u+f_e35*gvr`yuCk}&Enbd8-MiCt*`%u|Ml;E z(QCi>?zTx?0s~~;fCk-%hlVVdh@u6$ml#JabbeYP30?CA6JC&q9CU#;D&XYF)|THU zUSz`WxKxRWlHUfqxNQBQl$wRZZtySRo}m^-7$O>_-huHaXc`}1q>?JBd<*Lhl2zoN z0>Qdv`<-^B@gS-3k5Vx%6I9(Y1Rvtm+bNiIeD24pPZEm1h0qNF!S z7$fmOnQolOpIZy^Y9`fcF#||aY!N(rD6Y-trtL6FAoWS=lXvq~hr4t;R@YRRolqf! zXC1~-Vji3z;4)F z3*4Uy#9m2V6RYU1BSe%3Gn1G!LE1zzc~*_;08zB$F; zb*=+kty|>bgzlhnZwXDx^9`pO$sXcR)zv8ybmS>QYb=sAyAPCI1Jri8>@g=eN%s86 zPqeb_2@bO%0Xmc9F!53$+F6su=}L50rnwvd-bB*^0B(!te5;%{xtnM$7y&m?N)sEh ztZ27DT;(CbMq7mXwfyKmzD7Rl?(I7?VZY<`zxVPV`pw<_2}Ls|S91$oU|e+=tP-uC zJt@YYNsDPRgs?MQOWvGCNo3rnBpL-o3I&V?36SY{l`lQlF9Gs3OT=8f=DJh0 zLNg444k-m8$}=tXN+!EM3f(fCnH}`j?BR)++DyWbEUi^puL3Jj+{j*dC>0=+1Leh8 zC=z?8!p^OdD9;7ASy-bLBspq6xNN)|$Q$U92@P*7ri1`>WDseX=KAzX_=JY!o8MGM?_S)-*&9cR%<`BGrOLp@|;jLh^D*vVO19(-+un|O+OiP4u{lLYIS zw$N@tu~a(vnkd~i-`Ep)5g8{^D$hHba z^Ra@B#iX!KN{T7Z$a?e}zY?|8b3&07qb#Z}A{Z3qe|X`58Lo1!CiI93!x)cAMQ>Dr zht(pZL6W*bb-ujJ2%EC4%V)wd_7Xv-aaHvPCr4biRZa#8n=8`?&+onT?prUseEZ(h z`*Xs*%(PfYzQV8B3udt5_i2_osD4cJ-xMUzJ-_?n%eNmqyLWPJ9t*HL9(>@Ljdy+Y z#QU#qUf&;0XYJ?NTAAE;X7~QnyIU(`po=6|po_sg1u&A(`T+94)#n1@vOE=yvRBc9 zYOZM1CZ#$)(_>n0H=5R#H$UfCfX0tU8>K~vf(}5I(2Kw`R6sC-!BFDMbyD*LGmmP7_Y zN(VJ+11D_0h?NEccQ_H42}n8Il2Dek7dw*`ytT$Km2y?`*@Hc7bi%-iNBb%je_|_s zUhjU05WgbDELSRr1a<;BkdbwUNbyGbi6tD|l$)$`>jEMzaoR!9d`T3x;dU_hSh$Lg zy?+V>7Um2%x;h%qe*F(WdWZYhQ#TI&>o>pq(&^QgK6H9EGj)c6YGL;Wv1>+h^D26? z#;wf77@{ghiSfZC9Cj=zbfq9mOz2%qYpM=}w76KHhzJ>WF3%LtJreui2~?*7&PZ@p zV2+81NYJDUY_IwrXm4Nn8kG2`mTIqxz%Ujo`OzvCAt7AQFCaTl1H~z(PO6gfr%FKy zBu}5mSM0mH<^uiK{qAG=hLd7?TD1EMh2e;WwER~Yb znebea&>N^2CJCXZ#Mhe;;fr$_!;GMq8$j*T-T`>mLxK$6xPq^ql3|#{;GUv^VDScs zi=-q;3iSP5buYzjG|>fWZGK(pYXhcNhO^b-Y;!c*TAi#6bY(Cb^tE>^Dpz{_F@48> z^%koSzuMK>1kqad+#K|DYjt{Y`>;X(FS_gYXIa;Sqx#&a2T1IQkem^5>YPi`Q0FwFx6mWYh;g|RZHvB zVq~p;Ye7IqIUV4-w-`**d20r`p&AFKQ;N4Oi8Q-#Ay_(+;!#vWGIZ&dXoy01NT1(~>=H!R?T0gzdPQf$6VW&7}_ij-&NfknM5G!Q+yJ&{_oMxI9P6TG7fC!b33>`cQpy#s!4IL$T2qflyd2Csx-`cwz9Ldh% zVE3qZ>tOKYt(9kXhd1^HHxCBa_lCC*`bG`wQmee(2izjs4LC(Ep|FgU`Nr>)*L@{nGaSg8xHjn+jQ^IAIP*8__oh zPkN8K0~(5_(Q8N>(O|tKeEwq?hX!_8CG!KtEKaFb#h1X|~thg;C zE{^vr%CSxq+m(?gP;Dj3ViM6)B~A(=2x;Ezsh@vMCfKGDRF>FMBEvBi;M(c8DBO*3 z1T{o8MQe;3P$y~mHv#MbYADWXEE?pFKP;FLR*o?lCWwHiv9c3QZ1^aHnV%-jVP=~`HjMrC$oS3|m%7|~j|THvOo zvRFtI?NZV6N(@~GuA>xsewTbzJ~x--plD2e1D9sevgB8`ZFHf_uIjRF+qP}nw#_cv zwr$(y^vt|_*ZT>XD^H$?%pH*^P{>W|Y~=Depxd5e35h6~U=5*vYf}*JMw` zP3t4!t-N`k8Gba$ZDCv%2&s`N;lIjH(Pl~Wg)-q1>Xep3MJbvK7?Y%_SW-$!q7(h9 zx~j{riIHnGXUar#VV@)dl42nVSTsTxfv2w3jFp3!FWc9sA6$JzbD%&b1U)x-J zl|XleJ`W#UBQJyNulvt)U8OI%jutYf1hO8|dv}x9W#Kj~dXl zRp_UQiDGC(P)>pO_xej(XGBkuf5vRP4nWvS8e(gK#m+W#AF_V-Lc8@?Gie@W$D?&# zfXd&`0}J7yE=V_>`!^IqY^Auwgmt&^Ep2G^26=F&irsL9xZx|Rc94k zXfFfj_?caUFLcAjPnA&*v#A0qn5#}KlG${g z=Nw&&bQhBncT8=g2Lu8HSVw4l<2cNISCx&g3{VtuHARVg4fTqZ?;*?aN=K#i4Ho&& zs;Hq9|Cxo!Dd%TPn{W<>;oJm*nk=>G0wbqL8%eY_9C`%H)|UtndT9C*zbSiTNdbQ$ zm^$1wt7E&~;<>!ReRPHYfD<8@F?uQUb@ZL>%xjy^tjK$7KXMFSBK)38KFi~Ey&-@z zAzvmVS2?-s+OSMiv^+?>NCaQDt(IXGN&NSU>MqlgptJcd-|}LLpyn#t?sc~FwVUam z9YBZoPx@8^j_5@p!-gk{GRV-@(-v9WglV95W@OOk6YAto+gsFpZGl7}d3;Iz`TV|~ zL$|@+OrAVP6*e~-jdRgCgJiPxV4A|`pxsZ!49h?;l={8`P4JzEa`lvfyh~~fIbe2n zfe%2J<}GOgg1m3#fL$JPNt`Hq$wd>UtQ$zN{1QR*{H65QL!QegHeG_YEkMm9lWLn%*1em^&~ zCZ&M}cfwOoufjQq7)DA$4*5P+oL6IfRj(?)K+2{;-Z$dz%#qa8Y zE9Ihq8jCMuaxSvHine3@9AXqK0fquC`r~@O;BXo;^?s~!rAy@XARFSWa|q%_2YMeq zzc@4~qRE(u(l}|oqL>EDvU-xs-6+~^9f4nXHwDQqs#l&C#gH6eikq#O^G?#9-MC0)}J^ z{J(+$d8jc2eFg>1$ZQq$qTXr#XS;_zv_pr%GZCN*D17M0>Nvg1#XA$m$k`ytZg@)t z48{1f3c@D#MZ3CvdOQ{^63~6C8Q07;gk)NZ2+W8*Z_Xciyg5%XC0$(u+XaBMGcWkr z&xLRK8RB-o9bQcwRGAh%_(q{FomK$ zgz{*JhH$IViCEflh?7FxIBH9qhBWe`|1{H7r9l&(F>`OwKp(g94?5^jQ1-kXT|F(b z!|css43MmL%tC!#Hg#{BEI2mDmq2MF zVLu65PV?4Alss_BA~hsYQjn*R6tVB3@^&Xs1XLa~A8&)l`WMtEfl|(2m{iz9GoY1? zf7~Y`&hIym-9TzuuL9T?=@l3}5VWHG4xaB`Jh_X`YnLq)`4bFnmChEshl4BG1o=+S z(}M0d*bHxGVlm{v=sY~BvpbV{b)FN-6HR+VX|a|)EH8|HpUEsGLt2;u_PB^q{JLBa z%5N||GnP{h^sZ4;Ni@oH?t*SuqOQ8x3EsTD&DjfohHqo3zaTNTm z!L+&DttVwFZ)Q*V(sQ6BsXnDtP-qTT-L(0}>UK^=38uT%{JUZ_wIVP%Afhdx0&%yv z!tB%j^&5tw62JB(^c&?-@`UbW`GG5Jc=&ZqaLKe8mH2Ad^v(`${*(Y%!g#0nf0W=$ z88MEhe=@L7cRt;xN7MU^sn+ve`F(Z3A7glxbmz_J@!*`8(;ECTYVl+|3EKWXk1{!K z{v2K7}XP&b_LPLngv_p5hR<1}k{T(GRgk1MF& z+>e1;BVYTCy(tNIkP*l9h>f>>36N)MTT#?)0MXBMN;o3p7OrrBI%sS_Ph&#YKFlML@$*0!qikv+U*} zZncftZxO{BmoQz-?;~5z3kmNmUWfpJItonLy2*+b54y*Jn6tJ`A>%cWDTvy8_B7Yh zb2+(z@ADqZ%b^axyKwQ{b@FrKdmy}!{_MGLlYd|VY)fPF11JeP=4iY$4Mmnel zx*b{OSOU|}5j2jQO`O2SX|fk#2hcVo++4a73PYnu7nHwZu%gcBfe|Z!JXT@456=nB zGDf82w>Y2sP)4MYevl{&C6ud>re1DR()7-ngK-2^ivQgjY@h;bcSi-k6;}m1V?JL- z7M)NSE%~#%gl!$gkuI@rB{!rz4uDIpZaJj*%@~Z&J?w`ZhygvUM|+);g=kETjm0_J zr<&pcxznYjI0r4^|7(D8N&+iTsZ7E{n`WU^jvE5ny%U+Q-%>$4Trrq5+9S2p&w-f{ z`%OtI<9y4KNTD@ZE}d8swNMB@W|IWM`Zv#b2DbiBS&!qNR_8yR%}idG$Ls&vT25zE zyX&H@&XV8vKZ-_ZQk-3svbeO$Gd4jy`*z9S)phXO-|k$ zF#(>qDZVD~NRJNDb{$=6TOhnwmgNTur2#F6)XE1B6*d)|K?A2mmZ81i7#bD{<;XbR zpc-*8f<=euiUo6?HivO7F^;SDaqaIdwhS!cOp;Lq%ZX%F+`orBmzE%Do6Mx^0L`qR zTYa~RFZeJEFr@|itiqh0y*AoGM9Y9!K-^~R`6U2y&Hxom@^^hR8-#{RE~yerGP}h= z9{P#mfM0MZuEg&ijI3m>=THVq6j_tK%4Q%{YXt}Y$-lfMN*Wk_5}A`(@-aX~9csR9 zu`}_=UA1}pW7RqdNNa<0eS5S0X7c)bxvA@6#LbN2lT&2P&Ibrh>fC0{q{1GO= zXu>aO8WvBb5swK=4AdA}PUPk+X!FaXqYbTy`l8y58EBDjnF%^e!By0+nxq`J2Wcg5 z%3O~K4!klLQH!P#!p_q5Mfph|F=5;~_IJ@9TAGG}^`gQZb?iUpac;xsGC&w>*`7Mo zjf|qY->S1l_m>d~W(MVXldwCL9pw*a*`)07`qnBjc5CBRbhAhWT4!Exjs~iWoZEUl zy3``}GonXbwci{8%EKuO7)9IucEMe71Zj*pXpRIx zIZfX@S`Q3HRY@n0-B!kNK_Tm)eXSpTql+aiwHi&a*ls?*w(||5=wA zX@rwT@}VF#yk9B~88n5m(djw1GfM{}wv+kZSlVnGW$jv(tGDfAJdS1Tjz(8A=Q=w! zuC#~dNKszXoRg)ogT>eBgK+nShla!iVs>)AL5vz z{CxWxmNG;#I9TqmHF5AhmI*NPMiaXpVJOEEHvS~_Ef7066PvSAjT^@2kN<*$As)**HO8nkNuWeonkf@h~ka&0h$^*v;CmUAD87h>-Ncgk+yjDK(?)}M)hemF2Z)!hzY;XjqRt$FW-aI*ch^a zz>lLvf{n4h2%#jS3+PYTQwW@>1CKu5<}WIIgRH3(fN{D-;gP67URlae7(k$!1BIEP1(5pJ6TsffI^K!a%x8o%iJ}8;X5ht@j zI4K7NI{HeNRjcnQ9%KuYQ6N+2uHI=*P-cXBd>{IG8szMoVdf9o+Zt^io*QvV9+=zu z&TkOBj6C2PY-wQSZg6!rdfTmpe@T*O>>$wQuIB!@f9y^k&kdhqeo-wxX4qN^1gb#Y zN^Y^c z3v(wi5vlOlBt5UCHJn*)OjBS|uXaxuW(B468v8?hon+kTMxYloo|w%X>=ow~bcSJ#F1efLq*bAR)x;&zwH<@SE`lFgAZx&1aOssi4H1JTgJ>)`~!G28Q*UhBTi zz2O)tw@<$u{%)m$7cXiCOl(~6>P7k#Fh;%4GZ(&EBUz4(emt1iVOOy$q#h8}@V$&0 zfdZ?z6s>lDlG5L&0#m~7*hRv#Kg-=PIha_$?FW@Dl%5KGl(91k`hZRMc0U*dOa0g= zg&G3Ds6suv=KozIPJ|UMN}CZ8mk&*yiXF<)$pyxouY8T!kMUA7DpKCpNW=W<{Tv&T z3g9pllqE&vrSD;yl_#w@ZEZ3X1pLfZpfU?k+*R!Tvr3zmF3v-f2FF6N*aFBHGUhp| zXtS3nSAhRB|F%#1tm@+Q^Y|{pK0TUn)4Eeypw9`{evD96;@`F(@?mf`AZPT52OlDO z^^l-kMVu&G%0H7F2u@ms4#zU5kU#V*>5U*e3Zalq^Q;o`G8i#wd|ZxuOiaOyQ&}W3 zD$+~xK$6Qw2R_kAC9AY5d@iwyYx{RepjaMVUZW)q`Wzjz11VY9GRw^2n20$)X94@I zP@YBE@cND=hoY{1&@p0)O`9TdHp-8M82X*S&B?Ldzs*S`(LxH6usWBszEpwHuaU!n zSTIwo9Oro%2MH{HDoy~wuAyl51Oxwd{Y#h%3Xrh1U9Xs9&{2wbP9?oolnudmuW?~p zNx3~_s#h`yJbn~^lmLPig4LYUd@^+rnf=zAwLvV_Mo`Rw| zRG_g_ZrYBswO3*-HZLdw>o~A9bXPa3<@!_$n86 zf}gy9`>*XTqN13Y#S9V*59QrO+$Wr8%>taWtPomEXdbJu6XcuHeh?*>;HZOkV0aVr zbTE4pSINqf)*2?$N2nCq*yQzd)Et~R#rF`k^V1uX9&#Mi_Sf!L{k4oHHAR@4Q`Sw& z_^^%eUqW;;iFJog<0riZY_c9uhcwb;jPoLegAx9+Df{ClQi;Ewx)A^VRQ75ww`=U$ zKuT0*E|$ZI*uojwuLg0XR5n->6x?ixFBF14=C~@H%pqKyHUe=^ooI~ZJA?|K)gz6Q zQW8jOh{jXEClRP8W8h*2jZF--iNNzTGgcF!zh(=b0mVQ|S|5MWkhYtL_QUJLXSbku zHEXF0x4s5FImU*7hjs-hCq=D>sy_Tivu-HTJjWniw4m2(8IubW5`C;a^^5eem9vse zkP~xjBNjMFO0z32w7uE*1u|87%`!DsBq$uIlcg0vVGl2_j+fgoRaHAa zLS#(d=PW-6+B{}&FrFS0{G}*4Fc{ePW&G9TbTB~UNFU-dNd6i*(PCkNxp*i>Y;R(v z@?MT@h;|*kzaJj2x1ngN+Fm^h>3HvqcjRFEo(?m2x^ZOQED^XJ{5z59nfumV>uq+U z=Mj3yVx=rm?n3cp1HF)W1bx7HA3N_bE<@-*S|W@Q-%7#^VU1~;jDfB8G98_A?|T2L z-hQRz;AcyNrtS(oYtL$SNOh5?gC zK>+7vyK!6VQMuj7l;maKL`WdIn^_myZ*S!rFM?eLLEK=9p{(XDZ6)J$V7>H6gQrGW zHVNr@pA>`=8@V^y9qb$R%V36maYkdeA9w+lhYUGE3|^ z58-YN6s?PU6Hfz~*d%?#AWh{b_&dCM>WEB_6McggD{vG=*TB%2B9DG6ui9hC&eLv` z$^m>Hne~p?hUaYd8vdaiwG!|G5cE|~w@v}LUZ&;O{K*FsgwOD3=;)a6Je?y)a32H_w>sv_Z+p|z|cDBI76KvL;O$~+CmH2?& z#cgZH@{nwOh9~EJE^+-D|7Y=Q=$ggzpu!S=#@TZ_-Zqtdi~#rOyAn!3AbZ^EJDV80 zo%210k{j+>uDNxt($c@F6e6gedyYCJE7~K6TquPTn{XKEQig9b)s7S)KwjH zok0<_pK?%0oE&NR!Wddl?S$%U(v%=^{3Pz8vWB=hYRupwVtBwu7>lD6qczW9h^`() zp&+s$aj*)GYJ4`~p;qLQA`kCbbT3mL#rM`bFl|B z4j)oqG$pwyE%PUkF)M9G_|_l&`)V}fEA;1bi}!10he@=(i#gNzCEBIyz0H*8XFCzb z{no0)_q}iAy(`#?8&B%7g@FCiMgIQ=X5(C`IWrBM=WZlh;_=efwDWeRn!S!K9~Jb0 zRo~vsC{`5IuQcr5NT%TK(oF5YK9xdZhR=-VqeucwDaeEzYpq2uxBlmXF;QV}cUOab zPad|+B_DdY_<<;?^|NP5oBNLl*iYLZ@Bk#!&;F!|jAxK=SrvuwZ*Euqq7oyyTOoBX zvOHRF%Bogpb`^B-zLGKZ0+;qOgn^-A5y?y0#MZwQ9@vHLf1l8PlLBYCA%v8=C)j`P zt9D(t;5_!Y*@u4Hcio(A@z>%#J$c;s4mB(s;1xYECqAmChynjkd|T(sy4MzbvVq^3 zyj<^(-`+b~ zMN93?6b?Ca4eHvT354uwibFBXKm=$>f+EZ$W4gx?Wrux087k0;MW9bslizdr{^)$Q{z|?it^yAV?b=JnIGiSwr%GdPay@KFpE0(L_MrQYVpn~Qx zaTn*}6_a~I5g4V^?{hp(O!Rqe3_aT{Z2?(p9oEKU-CUq$nfRA-CDD3Z*3}W%pUD6t z&gKI+lS=gz$dDkh`g^FuYRW!3p7F6GH{d**2^k|7x4)=QA)1ALzKsPpEp-z3z`saF z-J-~fYI;L+x;?ZaK6DGa@tF!DPyMYJ`&m#{-&;hnE|`l?5o1NB?3)YZn(KIx+`rdD z?eVxd`e>qv9#6h+5Wc=UYMoz#IadH{#eyXbYu*^s&mP=N8+fu$4|K8DwZ))LmH!QM z#^Y?qhf2+{oUGBu9sK1QLQK+_fr**|4P(Us^i-&#Kn3 z+LRR)7*IAa7D(~sLj66e)GhPUG5DJ6gjRX)_eS$j@q32FaLRdO@>}fXQUgJ7fp57N zqgAEN(cuH~3nOiSgo70Pt5ZbKYj9LgL$rz}s(@V(Qm>q{4JM9Z#GurDJ--R;Z~a1o zJY;bvN1LhGdx}y*{ZU{C0x1gop|~ihr(n|ZpWc~Ib63&U2am&b9fW~D%DJv@{YDI+ zqb$9q*;t*g`*j+VxGh)Q3jSTdeWMqFlgKkO(rFeHYvM8)jrF%__jhYbl^?;u zT%qvH%A9IR&~=x%CZF-HG*2u#doUUtHN5pMI%qwf zXxar6cbmLfRb7uVZWfpyrc89~N}xiBP+m@Q2|}4%`6p%!7&6&z^~{Nz*8V%Sg=c;X zp6A%(^{mTBwoX6`2aYa#Jtq6Fz^gd9Tm#NU9#uLz zD)-wK_NXt;0Hu)gHG3nZwychmhxOb!9x+IKlQO)pSrPWHEzS_%Jkd9Ixj~;4Y9clv z?rJwudv02C{cEm(A0at(N8(>%U|U_L9c^wfElH{0p@(trt$gdjUG;oo;plrQ!gVe( zD1mutsQbB!_jX}Y-GAvEM`LpdG+xi4i>U2HjPKj|q)@6+WhhI;XH}H8@88f$c(P{l z`Q`k1VO=wfWE9_zT6nj&V63WP&y~IrcpBKwUp9Hp(jc8)))c7*{3%6hi%_V#RC!E` zHxmC>s2w~`Oym&FjpwNA6-lQ7@<2lEDiXeCKm;MXcGyj%3TDU!#gZv`282{1l<50v zpB?`^wbghu$gq#J1c^4hR)9KU5!d(4!m{I|`uqIIBJfC1ITeOQ$sj#0_}`5a2s267 zY#1$b7+;39k~QpTch6Cs0Q`wn)AReMzoq-31a7~}hudH5Bn}{;JSr&^lBNJInI_Pb>CNscGrDsK@|SBE-<*R+Xfn)D}EJ(+CIwjz!62 zi5Haazod$(VhqHN8nNW;%0Of;Q9>m_^`Qw~;JKYaLKdjwHcdv$5sjGjxg$b{&#cfO zzKdWhHM`}vpvnSsQNB{X{Lxm(1go!Z25uR4lO%Jr+gHADrI?c~&4xeRi^Kv@a1g4i z$B9xDSY_AB4Wf;iRH(g$L-{f3j|pRaBPlP+!juo%VQ1@dPpIi%=F`rMD*oDluT@Oi zQrt+r!I$d5;cw`EiM(n1xqKJ3#iwoHfA3xD%5ghufBiB0)9G-0=)?+x-aJLs^WL|l zqYCn$=Ja%_$@zgI8IMl79|=w$75&mYGu~sL?kXDK&1qDmiL&}i>$VLG`92DU-4pK#0GbF zCDhwo0{G+Nqc#Cn@2^S2ns%vd6u9>ksc$!{F~>r!hn=(G;??2*1s4Cex^uiG;q0&+ zkOiDXT2);tMT^Tf&RhN{B56}ms$qd+lVC(<)vN#WZcD*~$&BT=mb&!@H{J453aUhe zxKj^@HCKj822h$59lR;9%<6_k3$JVUW9t~+y`W2x{8ZTg^(w_CBNvHvH08_Bx)+@# zM(aJ}Vf80O?}|`mMtn&I>06wjCqt)@5TL_r=a^PPDp*0@Se z#{8`;t`y!ZZbRAVpL>@26W-Jp>vIIcw8Y=@vQ6%Ko7;U1cG7e3P-|85h~fJ+`_#oB z`~aF4PD)M-jw9rh zJmG*?Cn>1woAGP;>?DB?le0%yw3OH?in5mnJ47&11p^00)~ zlaG5w_$bXwOMw6Ovdom12<6@h5X5@V{f-qwvG#FFV2lcl9iAcR{Le(sxlH$-xOC45 zEDZ%Eytwm=T#-!%G35A!q$c2Mb+~1U3S%?BK|6r7@mC_SoXHY!W&Q~YKvG{U>-vIL z;CkTz&W%jWm{)lz7^G4RZjDk1k+1J!9{q6S(e)AIcwp^CF;IR;^teJOWqm+qd zCFH0NdIiQ@M9MI)1SM!62kTuJlnVklQc+ynGgh09o%zE*8gTt}=R|bHoiUxW#$Nw* z7fsjI>dVai-Tu8>d9CSn_fKc-$ug}8@>QF~&0<#AmxsUD4DP1~ZQ>zFneP4xJT8WqB=bBbxQ0)oFCrC{5p>kegB^UxNsA*K|GtF$y) zp-Pr{ScY`>g*}Ro*aq##=PeX7((Z7)uw{VJAlVc_^wI2~C31;QvZyQ3&S<<*zlU9; zG+LYkA0A9Xc(8d_vRiIGTCXInZO`*iDtyeGZ-%G*7*mJg2F6R4M#syd22ZpT$3oA? z(Mzcz(`=4X!V)^^;1|XWYb`3z4?0Rk4fyH*<3={l7wS#0iv0bQeJaV2&`d5MHSI_u z^}=46bVK1lN=lGt42qREB%9LJ=4F()FmzS6TC5f7AC&w3VCK;#%n=#YuZ%!= z)5w#)Rtb6!&{>pbP0a$ZxjUq*70-4%5of4Ekm7r($cU`#HJMx1K|TdA2G%Qfjq-LQ1)ib8}}9 zbVwFRL(qIIBw9) zrOE&U(FIIdXp^eV=D!rdFyG=z3^(J+-E*2HO))cHqWnMeenlI1Ai`smzjMBr4nG}(Yx<1_9O4Ibwq@3EmzK!c2{d2ItI8Gn_pDo1_gM3aD~{;|R38-S{k&qX z99?raLWLDI)8ttLa2#gBQ|MB9nRqbBIy-MHg)3=I#x(A7YDhM5`8tF#WvWqy~NNg ze!(P6RzGBmze5=hSHs91ONd=24qW!e@~u(_XB!t;?vOs3FTR`*PY{L!JH13(a|hpZ z@u64sypoeiN&Um_L1Z zDDg2kc@-J?(IH9H^({_*m-ye?GdgB~!7_du10gOi>6juA(FrnIn3|Z|uzc=zney>m zymW1fc=)W|?zPtb4(OJHu6hQD`9-q6a0xpdg%{gm3z{gAPSf0YL+WSdWqcRhyRvmM zLvU@&i3;oT`}B}+SBy>kY5RY*Arn*1sr$Uy5U9h*k>hr)s$u7!@Q-^VkK7TMGIhB* zc+#JyvSCwo$CSLIAymP7Y+bedrzU!`NXJN8VIb0z#t!wqbCCal06{^3HA3PtC7>2s zW=8W617{F4@=9>;EQ|lPg5R+Tr}(oJ00X#2LU);wiAQyG4<9*DZyF~Up~B9UFY{Dq zq!v!5?tA_{&i&ou@7%-EOvc?Gk=UqL7~K-(s#Ndyqv%O)Jv89|GJl9(Luz&*6NEy!(%CRN zcrK*AAKVYSNbFGwad7c0`|*XtT2}SA`8oC9hWg4~09w z9}Fv9_~w#`n2kdsKLsVHI5NMeFQ!2RlwCPvRBN^ifn z9RfYo?0LM5^;t;9;I3ogGDFGFA1y06{b)2*gtVqE^bpz8WLSjl~DX6r0%7pLp; z?4SU+Xl(n2h0W^Mw#Wf~I_SC@M?w9x%)Z>3ko7{Fnws0&+S`4dj7mMIlUP_-dR2Mq z-ZO7%R{*^C(yHAjO{(*KN+jJ~Kly(y_ekiFg(-`rzQ9IYqhEO1UqW=fE~R^(4xLok zasPu=ye;UQ^{i1b?0;dQ0S!wr*6=iIplwuV@atW3ojmufZwP!R1}wz-_a{AMt<&j+ z&)u^!bG(7b;sOO)R{;P@Lq*il!chuyd&8_UySC*VHO9}2h5?0S5xw2a52oO1`e0$# z^ZF#3Ed;6gQa;5*hG1OQq?*vq{)9~1Q-7+Dwj+v|>`St#AWw1qG#2r-DW3Ug#q+$z zsA=$J8r!XzT#yreBK;yXBtLIR{R^B1;eQuFd_cUu_FauA3tPU&c0bSazIQ{+Tku}V zj)X3}fk!Fo?;N7H*u);rEf=IjtSswf#hsMnMDmMNO1>5H_B6qM^}OCMuk_sR<9?mk zHngZEWkMvbEoB#8E%lk|u5#cbQNzQhQMrE=C9Ri0DHFAMC676I{0uSOcpcvH>n2Sj zKAtY+jHYgP+;b9eP`AACx*ZJ6^wfXf&ur&NDT|9IntEO(Z#F||P1FjculW#2LYW%b zBd_W_Lnq$}x>kQ5tZ?5D{MA^|S>67e@(UyEY*;-yh+4*(0Xbm%UgmNhAe8I*i(D)WaMPr-;r+N`> zh75~WC>?RkT@TT8-UkRg%=j~8oQdGg5>&ck)C%^RYf(j{AID^hJCPVc8FGS=RUKCI zYoG2fdhfx$FL+CE9WRgTmbzZ%Ey6!?M*a4ioEb5u?=*BBmM3P!3M4#C>6FPQ7Oxu& zgZOaZrY&qH4_10B<(Zp;^#yv4B)P8cYFqcJpIxroo7tEYQUN4muD`-iH%l>;;0-!t zhMr({ok+z;Gh3rY%;EQqTy;jjzHED5!gD`9ll4nA{s+5xV=n=u+*4B)fGETjh}kfa z^~liGb|M9^W?9+$gn39IoMc9ZvvfcAN*=Y{`UF8_do@EL0ow^eH9?5CFyF!g8HQ#~ z9$kf}_leeL#TFe{fsvBNcl&7E*f5Z|DAB*G%sew&;#b&@Vcy92sTA#gQI=C{w1P7rXw=9I!WHdC+(DWx^_97vaen8uE1R;3>^ud# z-T>ZZl|H9hEQkQDUJg(D%M!ITr}6~3&UnmLR5*}OHlPnXr44Iarm{3bgG!Ej9pO?Y z9zKOXpN>!1yW_6k{JzBystpOkb;att^2@{uGLhcXo+ydUZ)yJ?@{ILeUM}0+r1rQO z|Ng3h{!dQNYongm%i9^W1M68^Hb;fYHHR_X0@?jt4V8iN#Qoxc<;W7p=Je(67_zGc zl?9^wR1TYlcjaN$n(SzpBaj3xax6*^ZD6K3>e^^@#j~SG&aaaUq&0xKP-&W1fx40~ zhcG43OxVp8s;8hg)sz^;2Um z3QzCTwi-oHK~2>l`v^O?GQ4gQyrL*p?Q7sYelMqPbH053Rb!*NOvrBMxP1A(4Byy! zOpx?6G~7@NsU=f%>y;8EE>Pj(W})i$@pR5Ur%pNb_--$UR>=!kX7a>>yYCz~YdpP#+yKbBNe_M;K z_eI@vLb=HtF#4{J2CAq%No9H>oH0;T;!;FfA+2MZ$A6+n!m9xeq3k;<4L*IhLl{18 zI6f`ZyG}_thRr|ZBMx08Ieks5C<tNK+Nnf5p9) zx-V6Lp+$hxq8pT&a6E;t0CZp8N_FqrN@Ir`4uK(eDl4EY2T{ifF7++z#`{(>BdLU~ zVOJo0PfYQ@|J~v!w^>E#dH)Wyhw0v!q>I%3C$M^;+vG{(vU4%4q`*M$@}Nqu2l<^1 zj*Oc(i85YE)4|3(`tuoH?L*h|cL*4f-}Jkwu)OJUjT& zf?5y-ZZk{{d_;F5#=k#Cwb)iwuMzKO)#TTVw%W&E+wOTNp#K@y<$_0g&X;^T;ovg6 zLk!ia_m2{1jWGkS>6Tt)VX3O88zlf$2Y4IdV?nRy>aPivisX-09x-2 zhMhjfSS)4dml+m?r*9r$KPJ6@ZtX&ja&oI5$4B;574U5M6c-7z?VGY6(kA~nu&96sFAETF%}iOSzJ1xtd#SYTc;ETF zKUk`M?^fL`tPa7?V@oEx%FRUHi@fG+;>~f8$1h&vYN&IBGWZ}dp`v>p`_~<$9}o%A zo_dV7Zrv(sTV0D)Iq`Y#U}C$!@YphDCO~om5K>hsUmXAP2D1@%>SIo>j16&WNGgX1tbf+}<%z$ovfBh}vrTek4shxqV zW#lb-9b3r~mB~#q9NdP2DDHy8WICr)!U-&~hq&xG7{*O1CtN5&XjCwXclbTLi^r&C z#WH6?;m_}~@0{Uj=&%0*Bw!i6x;pdk9F<}9XIgG5*%PN@z(*c=Fc-H_-eZ8#wZ~N?UCtWj)PmcCTOT+Q5B$Vrihj?^ zjks<61}ygFrXB`s*|<4TtD|)W5K4wt*xA`tw0=*lLY$tArRkMXijL!tMX@l^S)}h_ zw#z4q)_p5!SKEDSXs>p#j~v+k!xY4i_=e!}x9?roLoCC&_e5&e1vP)i|KDWhuD9~C zv+3WJB??W`1<+_GrMF+1u$#J?@I6LG{Yk(%EZ~!|nIq=fBYj8$osU4Wq%6IaF#D`P zz(f8DY@m-rryHLGP+%GN8bWF~{-Q)cCP`(#@|QsKYvm?XRF}nb`vO*jo#JL;{}6RZ z(1fWb4LCy;bb^W_WfD`cjE!7R!6=4wqzDRUQ<0E>H$;bI4Ra3GOGa-sIy_gLBifi4 zBm;?G2wd`W{ce(WrpkQ&X}|dBIiA0KE?VD)PtK>dpPhUB+fX7$^Y_nj;MLwk&rfXS z#`iqc23>2TGjqB(Y%r4+g~TC?bh!gAdQU8QgHLTK5fq1VtkedqnU3Si843^RtOi`1 zZ2Yl-+nV-3J^Nf0#!qi9tPH#<^ghb=WC^jy!;hnL>tLm*+I-Pc>g~nAVTHEmLeCQm zMhCg*|DRo6D0=)QkAwn9aZnXmHips6DfE=3>_IDGKbiAE8v?n0kI~*$#JrAq)RB@H zm*HsrvkpM@qx2N#keTf2L<9kVrlPxLLZ?!awqPs4d?6XlKJ$RtHLzYS;Z z>M$9`-rsZ9I*1b%COofF6AXObm>6BQ@QS$@7;MI%sw)@EG{tytoNZ-wrkquSB z!S6yy!14taNOoe*he38(EjH@}l9*P>E&d|jO#Yo3pS$|>5P@OUGNwLJTc?tr&qAQ2 zSFp;iGYVAK%u({4i}@W|>-!R#!$HmV{keQ<$$QezHLeQ!$VsUMmA*b)<`!^VIJr2m zgD{fF`*AA@@x@c&pl<>w-?etHfP3Wa=wd&85hCft$z+}SS>OGL%>43{RRQh$IT!Tg zecs{bTE|v-IqCe9e$Mu@*s)-tJJ$;>6No&u;28u0s*HpmUjq8x8QR?b-yARKHDzm9 z7yIvmn#Dv}x7QZ>PT`pmj3o|G)VVJ84i>h6~};=%y&ntt0mF1t73eL5cbzT|GH8j0ogX=QgCpz|qu8yL9RNuQ*2 z&PHVwqDWXdohBN}rbw?SL;?KIqJ?KEzXv6DE(O$cRBxPa`>-V?z0AEUuA`@MO?N*9 zJ3iF1OFbV^-+2Kg$U37kDg_V=)e-P}Tl4ccf{9Aj|={{LY|CDef|^LGSvI{&!`PL{>!=zn zA)aug6g^ySt-3M62@o7OrLJiq%TKwtvwVef- z6v92Vtmp&kc|Wt`()9LovGcjQZbEPmaUgidE|;Qob@k=eWdG3*Zf;_RYUchlTEU-)eD|BjSi=f&>%DOAO9^cy-ee)sZc zo6#wFsHF}8sQ5`f*&V~##w<cQi6foVt@v#a4Rxf zkDzXKFi-$+0VlwcWGN(+^!oL!e3o$5j^GX#$}>*AE%oE2&ZrUraT(N}~^m^YB$t-E_=epQ0R*)K8hxsv@9hS|sX`N!aDeeQmlls>il-j9xt=~G6D%8Ll{n3l6DPm<^DI@6U` z{uli7z(kG0Q9;wY1u0hqw6c1BN;W(G4Xq~9lNPE2lJZv;iyT(ktkoKHN=~4lRllzg zeng%D(hxha(!CGBXxI|3YlC!8y{W_v1=xfo(3dJnK*trvz0z^JC{V;kHKnYvDfso0 z#4{#m)#r`6X`Fg)Jt$RAgVaM|w&BEzNQ6W~QKFWxM-Zws4K{|ADUdcP6NM4m-WhoH z63OZi2Z&o7S&cr|UiqUTq`w}{u}5thw`l?|W>@JvBY}nZu08RS1JdMZw_WTUUhh}B z1TC6{eZL&($fv$+$}_b&03iywX z%-Pel+3O-A8QVaf1hsccJ*PQhpc!~Qr;$Ggym;9eia8~U*;}|ja|8IHo1bsPiJ>D5 zkm+)!oy-gqTD5E>i{oPW1CaI`js-Ns)X!5*`62#hCjyqZ!estLO5_F$YZn21WQ{tp0-Kykl%EBcyBk{U7* zGtw6?B^Y?Z6b0Wx%o7sl1wGgop;`c1zYyB=SBd1lb_hB04G`b^?F)t8%Yz1>qVft) zP-ji{LJAWIJ60f&$VCWOTbj5cNur`yzGZlog%Kez9eW2jOZtn`2BH=q22_wJO`{}{IlPEdeCn_O^$pJ z>uji%y7@Mtx#r1!XbFP5r8ULM-`mXqy49_2b*o#S`itb1D&7U8$8ro|0@bJ|1Gq>v zqb_I0XG=~7pj>=7&8UEz7#!vtbb$zB_W*^Vg+ejo-F>wf+J)HJ<4mmxaOg$~S>Fn> z098@&D=Jw9R(WWqBssp;K}xAGi8Qz(}m%In$Y5IeYS%+dG4P|Kqwj z9e?MQ?f#=Tr_b!q@SmEB;~ReBFJ^D~lC|~z8~(*d!Z*I|O&|QeZ@znS;|OmyTD$X5 zsYta*O)m88UVrs?z2wC&zH;~7R*+W?isUm=10!n5E5mo;?&SB8UhkQ!SO3!=|M9oH z11%oYZ6YxDT+A86JAU)vy2@#+|hbI(0@4{NQx_c#0p*;n?wvY$76 z0&lpxxbC{^F4=d<&Ye5ESoUy(oc$Ls%7L>C z@8{;`?!W*3&wTbX?|kRGKKaQ{uH7_k*|Mc~g5UOS&wBKu9<_b@c21Ah4Q#{utT~s# zix~$BHeM9PJ@?%6<3I5eU--fohCH2j|Kb(bJZe)x>+60tx7fYs!MRU<_0&6WJ^aOk zv*)s@|LZkZJmZSZ9M)HnW}(CPJv8^xFP*&Q6GwahuCwqTa4=lOlt9>I)A?oyxO0A? zlu0W@2vn=%s2mTGv(S8Fr5f)avIimuhG#ARCxCuO6_(ASbp=yy%y1|I3d)l!=qBJ+O1* zWSW7n>5Ii(aDuj>TzcnLQhc-6>3sg{_w@qzPPe;n$JRaDxAuPb<$DjGo}26C22&I5 z#~;|!%LiO4!d^V87r4`x?tNf(exWgEat)Rx{_*?&ofB#3;&rST2OD=ia+iv`x?|EK1 zcV$2s5n+@%rj;M!uO!XOe$)LGu9I{-old9SZa?#x&-~cOKE}Mf=kRxa=Xbv04R5&S znrq6l$R>mh=av1u;S=}9o#1=k^PZpinV;#!SgSZw&w0*s-t?w7J^9H`mN85-?SW^3 zQ_wuc182PV@AUL^FP>iRiTwP}zv|UL|LVQ__TgD%Pr;3?8)tmeJj>ZJS&|H%pMU4O z-ubeZy?k}AsUP`~ANj4{{LRZQzue7~J6boe4ePVvT$&d%4wTzC_tSt)n>L*}b7tTf z-Syv|a>+$gsajr5XyL1o-1zeTQEC$9Jt@%5ML!SAWYR zHgq}$2*NdcZ#b9+o>$$$*Ok%mx0CH6Y-r7&Sp4sQ_rTA+?as3c-SzP7c-;>_?*IJ3 zEA;WlQAwWNGtmO+qoQK)c1-arF^Z6Ys|r$~@awaOO7~H@2M7=lIEel=qo-D27m2zc zglyFn+;BNt{gOI02R*yE8~KPt0^JxrA$?e*VaVa}xI-b5`wmH^NMv=eNQ;cCcY3~KpjTEmQH^C-_03jfp>?o~SqFEv=DR%yrz$lDl5v)ozq@-X_fPba`P^p+!E4UnBb<_?~U0z%lR0U}1^NALH z{m#?b=>LiPHt*ds71jHdEW&yX0`r%F@GF0U1Y!L+m=EI(;h~eKi$2zWW^!WRj&0qg z7e8+Q?(Lg;_uTI3v-2ls=ZXr>q#KWnaNSYwuP)iSZE@*79S<8^r{zra!-`}dzIE?p zItcIk)Up5h-fx^Z({a&%@AbQXbN|$g7y>YOYgf|#A!=Sl=nqI`#ZxEYMuQ5_x+!E6S1P_a)5;h zJ6Hdw!q;-Rgj(W}^42@V0_}Z3V7Um@9+wiiMP59)(2J5^^}YM}eb@bmA3U(ES=%sG zJmIQsosQ~JS0dC9P*4DM>nICY*bfvW8$T~ZI=h^DA_S=y2pZQ-=`Awi$$KZ*T{yAO zd))u{zui6if$M(dhYzfe2M7^6jl8J*1HqvyCYumUbo)_+vx@%NRNmDnAO(~tq}@MX zC4$l-ymx_u57}af!2~#F86^;jxq|f)h}KZh@C>(480?Pb^w zMiHE@k&zoUX1Cy9>vJ6=8}CM4he$FB%S6!G?}tZXDr}T2J*P7$45&0oGKm4Ukd$b` zar&{V1U3U2FoJJa^zm|x=0Mn92~MS{azqYsk(Mwm;T4O0!K$Co)R|?l z!C*tiwaT_pOm;*2+kWx$0I=8B3!b=pZn5jEBFu&AIRL86kN*U(*>C>7JdEC5@7Sp` zB@z2^`}YU|p!epHdoJ3(c|-4u2Om7PRBn%K1lJ0P`=T!Mrs=7vi8fJ<(kE zFYKpPT{qh1{t1q6?$(;^w*=I58fyJBzMq?$`?hC3bNCba>7V}ThaP(9QIC3*^e(bi z*4EW7xPx@Z;Mx@Kbh3Zt_ErAjcYpVH_x_cd{mNc9+;GFuqesj6!WwzqIA?#AezUn5 z;(K~E^L6>}J7vAOk#wY^nuvqyR$$!}*UfUdsXls(U-!D#-GASG zQ&UstGE+C+c;l_N-nwJQj`Rc@E*x27={Tn+IQ(UWCI5yIo1B~&xT&ijwJ|KR0&_zw zt&#Oog*WzA2M=CS03|thP^kinM7Pu@jebQW_Ss#Rk;h_}7v{RZ`R^|K@*jTdG7{{K zZJzI11-(kao>5N&>WvJ_5EVU6pu`Z=vs5Jss+aOCzz2l>iB_#0oWA${#SYIOU--3u zciFvv^3^NWh@Elj`+Uu`z2Q)~f!O>^a-I1c(G>>hJo{7CaJ0 zS7h4u88%t5)8iv!rp|o@&O}hlw;`=-L3_%5-JQsUa=In|4-J?xbp1C}f+0AoQfC9w{Dd0=c02LU{Lp z$z~J5TVTGjrNwJn%>-N~5>H=8iW1{8qxz#PfYtH=h(!iBg|QYv!742YL`Q;fsha4` zXJV@LXMcO>8%O6mNw@HauGzcL;b5uiP+Oyr)^KWw4@91eixrM?1B;#RnYrF!bg$UI zM;^Tu8@SWY0XA(oIeYfh**R;zg|(rW!uG$gbMvNd`N(vzxBL+<{X5^`YxYenYf|3( z@uP5)p@b-JCd`h5=1Wi$1LH@yC)n{Fz%mbP5# zs^*!?v*wKIeyp1yx1&y{GvtZ%2H}PqUhH}i*<;A=G<&Goc0t_?W{dIoe(%10XV0F! z?6S+2fA^;H1b_U;e;jXa*<-6a&W6v2nTzZm8z$Nt7dE~W_b)rrpo*Xp1UEzdCV?*{ z!E=i6rm8VAb_3uR4kxH_rW_31Z2QY{p?lfxDSqE|8>fop^x@@PjM1Ypx5V9%C7Y5z zeu9#e^P+_wh;5{pb_Zb?D`mD`%(pPx*}Hvm_HVB1r7mNAl0?tdpfp9U`Y4&ozqR}| zAv~6)HN=S`d#rjv$?mJ7FaU;3cR+st;dHQZ&A`zdoewZWR7Jrc#?B@?Wzn)HMb>ye zZ$L|4QV0Wc@OG}giC|k4q)jBYs2zslct%pF(#4EyT?WqpjG&QnOsc@>l6H|5Rg4U# zK_Nlf7)jV5Y=Icxv6&xq2M)rxFdFz2QaDiTMO8_JvyFxxK$ME5B>T{^yNErR77Nqr z0Xj;s47*G%f@l~m6&z%T)hbTKfVkT~v>*NLFQ?`I{oALn*tIDZDcx0Kd;sh#B8(V< zoAgW36pJJ7rMH&{9zNN7`-qEoY@C|JLRa}5>g52B+H-L)2e{|KV4jw#s_0?C`9dLGC z)h&#=2}7Fn*+d27{&)l)O zJ@i9Z?%evY4d%8lrv}eKeJER&Clc8oay zD{fim3lP$B9@d7Ig6CVRWG5jF)@7-TLgQp$Bo$g-5Hki4V|>C`Onu3&Oz+~W8G?dG zSo;X2`V3)%i&0*4KwUDThBavlVD?1-c_Oyad1MxsM^JD`5G*F;q>M=bAw`+JkCJKO z2}wcpc{mubOm(_y62LJjqL9nP>|hwb2d=z?GrgZh%Lvo@LCx5x{YkDyPu@0~ zLxV~XGZ_gdkSl@pyGo)6=)*>?nIB<3&?bnq2}RW*)0=2=%f!#W^=rIzoA7&j(UbSi zE_9kY5t8l-bcM7_srWsD?j;%a1wwHO3J;w)wRgwX-P<;ImOvUIX++Utr}Nl-yLyrR z!r~&fBl8g1S%9TLk7a)lSIoh-fSZT}HlTQlupC+xF#o_^@i*+}mvFwyCY95JJzGTqQJ%AQnn@w=`@o;|zry14)T`&WWG$GiBKK(DZ z3Ywei9=hr2{r5vQ;<;*IS0c~K?8P=Ts;N_ogR!A*@GT7&*phvY$R-($f26U3ErH$> ztux>42;)ntW=deWSDn}|qtQjV8C`@!OIVqbG zyhYIf5jtYfG+E7D$RG;C0jSt8zLh#=oMtk~7ki80U__Y+G3=35V*4z%Lq;Lb)R*ID z^fA`Tum`cQ$x0D>reqcYq2bDe(!^?MqEQ{9h(t@?COU$v1CUs>Ygp*;+`|jM^EdYw z$7GULCpglw+FvNvI#zVUII(>G2>xP*F7j5s0{N$z~`UF4r zlHDg}=fu-l2*rRgix+d3_iWqJ+aNP%oxs?NeTm6LZ)Bdje{%R~x#{l@w_6nZEa?Jb zcn-RC$i^|7d{1v&uRFf@MUz}M7UP>y{-Kc}&`__<7DtzL7W?<_FOfeE!alG9nk~E5 z9d`D&13$^0atX4!{r203OmcSZ+Vz&Vyu~#)LJfgo;1M=_VHj>hU5$B|nfl3}{K;?s z_HQ4^nrJwHbk_|9{xBTuzPXFi`}elnZX0qbzWBv2e*W{HpFOsLd#O9?forn)s;TQ3 zoO`*KT?vCCc$*mS=au>aQ4;S)sThg{Hbga(672l5kgTv8FtFJ>wY%S?Q-bwZEt?k zSPDxN0Jwm8XjH9NwuJEuz?j_S0L)2v5QAo_QnJhwpTIDdWb~L^X32;*GbohcIus;b zmuLeERd))4n~_jFG{}ORV`f{NXHeRwpcLXr31A<1kj+JkU_xY46nA9UW?&LzuB(Wb z=S&_6Xp?NYJ(kLu7;5rtP)d}sM}|N;M(2zy4UmB{K67FnT%{ZA%_TShDL*bvVsKm| z-!71VQ*KCQ;t$5wGH!UELsQsux{A~vTWzDt|5zJVEV%%fZLq9FA{CjL+1$S2KYb+x zFZ|zoeaB-jUMS;c0~Euirh2nk-vRRV##$xnXLJ$9PW z)pcO;I)$Q!3UsIka5ofjg`NEFcfWg>Q0z0G`AitZPiQ`{frs4;wQv4%OQk@3{`%%Q>LqopR_h(_ct=V3ZSGps5fAD* zAz=s|q`I!i@U($tbp{A;dWf`U4bdrJSfqDe@})(5%7nz=#uh-e3<&ye5*3;aFe)3C zUtV13KI>^azU%63LpF3EN@7bmWW|_jWZ-MOlo>P*cna|NAbfygc*W+aCs18V^%Fx_ zSm-|Ws%_U_chTq{D8&*Vfz)9_O<_5f!GTzb6?0AzF#*kfNp)Rh1;_D%Y8*jt#%MR> z3aX4XazL)>go7QJG%p|x45{NSL7t+To=j0$UP2yI`h;~awL`@&Ns!5m-XOt{6XNj| z_;bWN$6f-s8!$--Ol-Pmm4ho~>d)T0%(%OX-j2F@k!t^ga8j!OV@lo>jLj7baXB$E zz#f_MVj1rjk?dQwf++e3!=(nJOkM`a0MZCK;)FS4@W*p*m6a-D?B{>_U(h&>ktSbt z)MDNlO@HH!cYd*Zm5iA+B~k?<(+2r8!EnaT z5=hHi#E{uYX&+K%6}n}1%|iI|{_$_yIK#~5*RVuWk=F83TIldqLo)$R&UQX~`@@hh zQ6lX{Xf9D@g=C&el?||`pp5#}i^g90%2$@bN~wGjYGB`C5O7)KXj~~BH=9;HQEO;G z8=)~ozTt)&cJJQpw&Tty41!z_NnmLB1otHaQXWQKwZ8Y;7rp332M!!SCC&8p+~+>G zbog?6Pv_1}!P2knU!fdLoi0$e=OZW504~qeg6!+|{A$06LOkkouaz^!MCi>{4t3%9{9paj09?a4iN(<())eYO)E6Q#nKiTR@-AVGl3C8NGObAi#cvoK zfON6g6thqeNvW!g5Pp)vzlkzLk=_otD9J{x9L61a!~v;dig14`StCw7#PYECYT!7y zt_xsv@@G0UjBPuP0Rn&6U9~gwj@?Qw)o`na4S*)QfZX;o@P5Q#Zc<(8N@$&ZC&p#xCr2T}l4`&hPw} zx{BlSdseip&$ClpyEX=0RKM}-zn=Ph;l`PH1&6mPUryho4f0?4m0!;Kl*#M&fA9B( zY0l&iqo$VZAR^0}sbQc*HL2Q*Q59#%5gWL>MopWhntF5BD_G7(FG>6U@BjYhLwecE zUfw*IE4$EA9(nWjo8_C*(OUs4Ks*34-8rv-=791U*Yujt&2RG*%B)jn-KHQqncg<} zZ=Sey@JT1#Ekw8nce}4s6&xNwllwB|eg#VINspA5o`#bf+u!$$9pgy>fCLMXDg`SD zPf5f>QubzYP;tnXsxE&lLRZObCMv?CK){jNwbBuY6K7Kr0UY*AzhVSbh!&DQ0${PL z{MOjilEv^akV#2WFP3`*mQ`Amgw$K^iE4flb{BU zwM)kK4WV2QNw1DY$2S^Q8LUW!0wh+gd$K8o=Uup~fvKqig?pqu1QxzOPw56=C~$B(Av|L?nM&+I}c7jK2SPf9}exqwG` zHOk%Djg+2ZGZPbe7tH3;9rLBz+U=wW+oCX)fP?<$yWlGiI<@ydab-}-;; z+`0488lgsJUjN76JII&6{1pvf*tKidBOmps^Wt2ZUqAPAKRez7Siw?vImR!%<~0pp ze&GvWxWBp)|^pC8hr=)V*<~kb0+8A}g9M1>>CI#Za(%UM52Q6N@e+ z>AYfbpA1nOpf9A5Km~9pi=E*c+RqA_6Ob+HGZVYs4MGlqRS?MAk!dkuAO2 zt>Vz}6Zt0`4|zV^^nG5@0I@!yw``owo(|{!1S5k^H#~Wd2Le4}TWkN8OlRO9KXt5@ zZ@0o}9rbJf{{54alPidohoSa{L2{X9Rn^l;hoLII`qi%vz+g^~vsv|W_>--BAL<1q zLydlD`nvk+tLp;CAN;`|42eIN2f(DqmK}5B+#QBFu+V6pwXDS><@4;cE|-m6xBTiD zt_|yIpeCmL`1EHyqbbAr*`FP9sjN6t9Z0$6LGxQTGAqb1dEl>jHE)A_?jG z+Hcvs0uctT`C&D%zl~9K?z>#~Z3+vW?zPu!A4i%UDz_}<77T`Aq<3SkV#%~l9T$LZ+kPnV3%^|Rxt6H>XMUjY7vFNX&Vn12~M>c_g z)3p=C>i2A&6MU*cDspM!e(~3nCIxx3VrH9y z8Yoi*_GWY%3OQtY#y}X!t1O0Vi~O|5ZW?XRr_@eit=UIm8I^fq4F<^#H0O(VghgMa za8jZr<12g66X5`U3_`QbncWf!2)Qp*$Rh$N8iDx4UIHh7Y69KrsL2AALaCrUem@7L#*yp z_)%OG^Tmp;Asl>zyJLeaB)s(4vZu?ao#UV}CRK<^0^4n7+no`CrWnQvF_{9d5(gMj z_eDa(E>Tp4I&8y}J(Sb06SrYg`#-$n>*+4GZ+#+Hs{^N8l{^yI{3yCqcMU^N>WIv%~XGr!1JTW{da zlf{M!nr{1N<9k1OjJjOFGqZ^D6mrWclABQZokII@<8%W4uJ8J{E^u!U{%;WY5A|v% z&F;A=tU0TT()-%f^z=tR_R+c%dGw@9ofd0q5bxA5vdtc%^72`UZp zQ=-%qqCIx+-zPlb3E8b}-n=;sA=4BZ6||gfaD#J2Sg8*{!v@Q#=?o!|`RT8nqKWFc zSM=|pg{g3QMq`0RUL!${H$yIOl7;@N`fZWiBnyimmu(g{u<)wO!1i0(LYp3O@l=;< zE7?W=YlYP=F)HfJ1?;GRNT%>-r^Ax{vC!~_3m|j~>;<1Sz=5r{Q%KApkC^^<9=mU5 zv=0`B7~0tYW&CtCUnU#lQil3a)>u(c&rY$;5E&$@h!N6HCY;a%t_u6a#17gbW&KKv zGL6-EQ&*Xq6a^F%f73w%_rr$xQ)lT$PW=dLyt|e8#(% zZEo(zMF`ZDI(PI2FfMRleP9|q6cb1h_zj?d9he=Ose%A;;_6Te&&YR}biQ!|l9&)` z{-b|2t4%-@9kr@NTub6v$z{|(o^E0*P>y1^w0lqWm< zXcZdb=lHt#qKoQKB@&3Ze?`G59d;+cU#`LIH|0Z&uF5ZGx6|!(dV$v)-}t7w6A??P zberi)#`m}{vr}V0(^sr=6-TzSOz{>&)pdvmBl}Hr9z8dByo#385d$K3uP6xN| zph28;C(S4Fhw(rICr>USQ|^sts^9FNa&M+?x#gDZSzLbkn^4AeCw`L zw_xyiTCLWv{o1b$oRozQf8|RjFWWuc749S84hZ!7*w*aec?FA+v!A=IH|p-O zX$Dk>*e%18c(}W^O^oC50?IvV*DCI48f`_@ZlQt_gq*(iU4@WLtlZovh@)$sBiU1B z;30tHq?<&+xKog_R@3eeMQdSZ2&l`D8812#BVw}7#JWrwt%#xaO)T66iUeV12Xv7m z2JA=txNR>H0yklXx-nTlT{1`Htr~(=*N>nf9RTDAs|la_Ei;NWkuB^Bw-(66a_PjU z8}&>8fK#PRghqC{Y;{pk)YoU*tk9*qI^i5Z#_&cLCR()E z;p#}$1Wh{=a(a_3`h)j=Bi-`ML~-fPnc4Xc>vG^w6FHU3aCUCt?gx)fv|3etD0W9u zBfnr3MJS*Rgzn4MS@P!|fI^bqE2A@miXF)xZQr!vp_8XuX_TE9Q5#*{{x#eim8wHNx;q28M`0r=BfAN=2*b2~kE z_ud!1=tZYbpKd6ShvB#xcwYIL%KnQNU3#>b#xQT)wr#rafDvj?ZRzl)Hgh=Id-v`g z7=pWBNxi?f$SWOKH?ZlOhD)hoiKbUvvX@S$bJtyWUH`*BytueH%njsR54~qBZtj1t3SPtbT)btd)HBy7X7 zKNNT4xd3h@#-&(|*5L}0jg-G>TaGZh3?XHEf^7krSea7oPh*Nk+BDG$J7)Q1suVrA z;5)7gN@xIX)SqB3Ow0+X1xH-DEr|pEBuKhc1*dU4tQ3ZLQIp>Rsi`zJ8!1{p%L*Xe z+Bt{+?$%YF%j8t+=id6Y-}#wGFPvG_+dMfFgK92|nfCwjwmZ{pf8Ue#^rHVV^~M`x zHj6JS4PUD?bb@@H+LIjjtkbrB@;nKM#`3_55GFAHvV zZ_+irXM)1SUZ2eW6-o`VMumQgzq)fMU8fQRA0 z-`S#addhd)amSTcUfC=yI_e-T5kGD2)szA8+uruJpZJNNSffLF{_~&zp7*?`({C}= z9a}EwuRUgmrO9HGu#eH(@4Mdht{ZN+A)KGD7r*%{>*rv;?-@J(=YM>{!fYq@?TIqP zN6n3B&^1S-6>BN{+xNPcRuTTk-#_s0Z@hEuPVhNT+V;WUzG~t0VyRmnK^2wOj;n2o zE=H$2;_S?kH?8y!ewwXQGnhIX1V6sICec5U%DD))0CaQ| zxWi&LCR}FXM0`}qjsdL-xL(@F7Rf18Ub?zmeJwS%*$9}^-~^1;yGW$9(Kn$$kzkxc z0x`#IrONS~zJ$s%ssBRNt3eG3`+A5DfEE}lt(hF9uz4qF582vBn)XMj37yb8MhYlUW?bpfLq%uJRPbdBJRN$o- z&i3B)qW|?Q8(N3G!IN58SU7z6a4-5_J5|kFZn@?4uYY~3)oL(?3(FUituq31zv^93 zCr+F=f205P_WG-j9sTup+&wYfBCJGfv!0>xF)UdRG``j6(a0U@lP1%1RkAg@&8oVo1+21P2poY`q=}S@5D@@v2%87EY$=fKO_Zo4GTj(Y+&I@9AP8~3 zyp;Ms%}f?gfA!~DQ$+xcTvEDsxyET~%f!$AQKlO4?8jcT*kw`|<0jx_?^`El=Z>B_ zvy^YJE~DA`MS|-K*JV_HvQEHyP8qWl%DAV%d8^_qei%0{79PR)&~KWVYVuRdvYz3? z&6CBIyC*pIgL)%&+4hXK$<60?qVc-@J>!1FrJi!aE)p~vKv&Tb0_&B40VJ4KVhP(aBQ&?ob zW3cBqxTAQri}2o09yPo~>>)!yO`v=-j!T+Fa*-g3q9EvfmDv>0dk9xq>x5)^rWlXK z2Q7OpYEZ6$3-J2~Ai*qS)4l?doBjk*CVCMKA4M%iOXn*7Q{@_yDT>8etcx1UGOo!iK^xZpKI?@>gwrG)e)*Z?b|K7|($jtd6lOz+@T)4(}Y<1)2|X z9v~d6s6?!PWGA@tljy^hsw4ncnO_I$MWK6&La-V0a{e`EHucV@u2S6#AYvCH<(VOA-=S_R#C z=tygcEr0_i-Fecb$+j}e_h-+ZUF*FIzrxXT>t?O_wv-ws zAe1icd)#;5dDBljFJ6a^&12!n-f7JvGJu*~!woXt)yqVFiU!reN^!;x z0B0Wdz)v7wiL5{#!q#O=(Pfbw(iw6pI*}Y3P+Y&72(n%Ugcb@e;6wEyPJrdC za&m)(QWY|uqLjoHlFU-=Q4y4!Wt9vTGlP0;V8iC-oDcZ9Hqz=Fm4E&5qyO)1$65uM z0(QigOidJ7`tKFHHg1?~6(Gjf9Mv6u_|!tDW0a8kdDq;+qT1w@+mMH7fQYC3m(1gl zlH@eS*D`xzK**^j2sDUQq_+C=>B)Ax>g=ntM5gec@)lz1-v!!7Ikh`Cc)jTPMZWuM zr;1Xmm07A1(fd#Ug=*dq?+qq(8zTpxIe2YWgj`ouQ4a@^GHQn|^xbRxNSC!kx+b>CssdsiG$TN@w5KtXUd_WoC*aW*+`>49= z+{b5)wP4_oDhQ1usmJ#!Kof{<k6q&S1<3{KP^!qo-hat;_Snn+ zk`lq$j-jMf3|SEcCm|Ilm>~UOlc~_EMRSy<6lQrp`*mL^rdn`ofH9nI{rdlXPrAu} z^Vo~d%y&ZSQbZX>R4-Au=g6@tTL@vp)TC%u^b!C-{E#5!D`XMH?I$Kg{T$QA`{jpr z!N!A=#b^$->bq4tkiCIgP(Od&G(D9@_L&XIHsL< zGj+b@j&ifLlgQ2mX{aIz!Sm5Pm_WyA47a$5Hqnx_M6)zTxw+~AOk}h!K_M1O=LD4A zU;}PqgY^vxLau7*q90W1WZK*gZn9;GUu))MJO7qcTS>bR#{XGucU7kW5fv6J2!m4u zN`pJD0tF|uLn7?RQn*ww5Tew{IUK2AkgZcPc`=ftp8%X%gjmkS2U=py+J85SWeW zh@+fZiZ!pI4?qZD<(7+7zyDWnZ*Oe5M61K$w$B|;N+V(8WNYW9$s!l?lEnj+_p?LC zPK3(8A#9$Wdi;SsJ2r3Z_E9klosP5fqRiItG9!nQcUeu=emRYf384QqRR>iD+c-UG zvTn*TZ^&B$BRihp0nlaMGF7Bgd-FdX36n)gk!cEml0GS^2 zh1Z%?p~+iP+5sOL0_1z~&h&lsH*hxmUkVipod7pM<6T&+i4(`NK12A+Wa_GH3a5-* z296%bj36MAUM>no0=OGoe@jF;gDejLwSXNSTqOIM>jE#T3e>DA1ygVl5t79#gIp~| zOO=`9@B~a)m?7e2G9e0PN=PKAJg!O=AP-h_a2v9+{hG&6w8_Hqciup$?3iFtR`U#2 zUM&X}Yco;%RPb|9^-av%8LvCdu>6(AV(`Y+&IvD05pPn{0}7bl1a-JE)^V2PY{O7e zZ75v@Lzteg;8JH;-2U7aFKM$VzHe$<) zv5fe5(*4$nEDf_M>z>4N&LaaMtnN%@M~0clHB$rUX!rxGlYSc43I6{#3&_O%oQOU& zKYtktk<8wYehhbWUy!uH5fdKd_~L(dfd)mxw z3r8b}?xTRY)n+Re=(*Gwvd|)L;ReANV;oMbPEurb{;SQ9wbA0J^Mkn##FwN3^l_w! zY`eh~G*uY22ri+J-xH^Ci9)O*L0Z%;$&f6SFdTJCjYclz(&dP{X9i8gq-tE@`5`n14(?b?(<0e$t$CtC&L zj}rfR+Z9pz*m&iDY;CewGMMoYpgK^{I;S zrvJ&(zw3UE@T`meD_97Se1fX{BRiff@}~}=G;o6rXgd4h1t8Gc%&EWC`_66{+6N$5 zP9t`+9sP9FXGb8Ahq@j-W?-p^_A5g&MggQC9CH~k*0WJTLs2jQ#wYb>qYzR=0J#i~ z5K4i@TTYTQH}^^CujEgx0>|ha5*zpTKr!`%2it>?tcFWgZUuD?oK-Q8(J^T(Tlh7? zc!-P(SLJ)eQrR8|rJ-2~yorKGRgsklA@bU{=z~ZAyAPsO0F0ujsU$)`A(}N%Bo#2a ztp(&&FrfilCdRBd!xTh>%AFE8>Vb2eevwq!ovdkHY`8 z`$F%(UedEK=LYb>&mQk504n-LjNLyD<`ZxZSRbu`Z(=y|`bk|drpyofa7JKtC#64* z75}?}a(gy(4&x(igd8n65d<76^v_QH1@!-#&!Y+JSm=UMx-%f}VG{|B1@J=xUD1)% zZOEA75ZZsmLmDc>S+o2^8K*2%Mr@<#6ft8xl~^M{ji%UJ(Mozz>S!=$xRNXcJ1E|Q zp%>(WOd^`3zEIf2P$H>fbBw|zRhWGeIeFLhgPmz7BV9O^Q&}kvBG`XVn}n0Z_R-#AMN%?Wql|KmOYX(xCXdNAH|n?B?%nzEnc<3!Rg*XFJ{QqxW9iDh+^R zve*0Yic5C)J~?rEmI}(}-PBl1r$+zVobYbG#81gtB!4wCIbm*-j5LB|Ym_S)8DcSS zXj94qxaGEo!h||8R9CNN5eDQJ0RZsqSWjWu`J($dOjtQaNJ+zitqcN`elwLBJs-hZ z>twzLqQ#bbRH?h!3a0zQ>zwP*$i`ITEv&OiQ3*+5gUiym2w#b5ILTFlri;0sBcz&{|H<|UQ#U4p4jd8TNi6oE28|L7o50W+t1lEm#HuoQ3t|fb2O=z?QIU;; zt-Mq`J0h&2Q-%v0{ZHd%NM7?)luuDwl}g?F3X>#OdnT{KP71poiiLJ_EYlOz@iW8@ zR)aH%xs7radAf0Nq6WAr>;1e$1F*Nr;9&UdIRFg!eFk#B)B(3n`T70Z&m8B;7SQmGjzIOW zKx4F+WwUt6tea4*fw_2I6#LV&8&c$PG=q#3WFJ#5#|I#LI^qKm&fCmwjDhl4d;l)I z)?_M4&52?Gm7>ikl`ZuUh7Q_^OkY8ntppnCJi>))?6I)KjE!c+NRPh!51b{ewUrM? zQp~+7MrLkcKvYHE&Vk^!O^-(i0OE@uYqc@)ajK$(1c!Qg>KNMx43 zwlzM&&PPyTeQ!e~QEU#df%^IkkOrU}7s26|$jX=Ia$NCklQqP?x{9)+65KFk2^^3B zKtWv;*f=KGEW_wNiC-LE$p|lU+Dz4NknYb$>8av*5C!z_0wGV6OOZcZIDaw;n#vmq zMFV^gR_itvQY1?4>jl5^&+X0a&wVk2|Jk!;a?@1XNowYxs8re{9Xoxd_rqnocXl;? zGC;}=QszN%JtBtSq2GvHMVJgbs-H{(-uHLPGQi0LZ3raUJ{tJ zBAnT*hmcuoVP>*jqX(~fwqII}uP%q(8#6S3fB5{v?ZVuF$xoh+LJj6<3^ns45zBi5 zG?duaTls$=hH;5Ntpa2s zN)esV$sU>nOpZqE;q~Jp0RzqkuO;X-)40YSG**Zld4fo!i>&sFjTj)hH9<-Up!>+W zK{6YHh=b5FjYPkOJR*RRxnK;&G~x`#Lrk>eE2MVi0_yi1w@1v0=%^+li?(cX@Cp3Rs$!1UY9mX2z!MWj9Q~Y2 zZ(ZiiQ<*XOhua?x#o%LFhtl6^WJ!&Q!8#8sn?wI`ok-n&&+&N9FQuk|Yj3?R9w!Gh z{Gm2z2dzw2=)!AFD%Hb%XCT9u$8_XWa~R_mmkdb`R63GtW>_yt5EE#p!pI89T7&>6M_+$-dsE0CE$;mOKcqRDaD5X9b>~}^s}Bqa3CrFMPmSJQ zA3C{s=b<_Lu%NJIy1jA9Fqm?zW(=3BF3Hi8ryp_gj!w5rYN3`eqLXJ>_Pky1)Qi`0 zrUsU8`=r~XY)!;oC4CcvUJfv@lrnrRt~})p6aKrmedT0m7tS`Yj;p+`p8SG){w9zu zPkjI!0bum7@&MrShA#}bwriZf*uJVSoDbaqf6R8_)mc)EVC95_zniYBfhs@^?q+qr zy6{@%5_Ikp8@14IDnLY5+K+sld(;;jl#0nqwOhr~FrFqrEN)ZE!WBFVnPRqS14NjQ z#^MwbgH?nDiMuR0mp75f%yFWjZd+MZv0`FG?lDHgY*o&g88I?c5eqn4Q?aTW0rfWo zkR>IfQ5j5@#A=IBiA?6qilU}vaO?*zU=)ntpwn(gr5=fZNhDh{YK<|aW3I<8*f~JH zw+sCNcg~*ZEhcGV228|>ZVIDCSTL>z5(8FvBXMeDSxk)3BPO4gz*b3NTC$ov)<_mY zSS=E8Tt~?3Tkcb%%i+nFZ0U3(ftvC+#fopaD0vlm5^@@zqnjB0?39}HESlbh?qTWDH08gK>*}A zgslc4tVX#T2$K_}G-L0;0T`;k)ia5|FC>&zfIeeK!bkROXqsk}zzkTnNMsGt(8Jle zEs$j>bMBT0-VpYfp#Cg8e6U;qXEdAE0U<(zb#_#4u`mPEFH)UI!yIKJT0{XbcBp(5 z$;-TY7t$(h?aF=6Jxp*RgNR z7KNx21Y$J*0|wF4XCR~LXlYf^(IF&ARaI`)=N`+*z)r^Nw9UAD2>XfHBWz9xu}lRMLK ztu0iDSBVsbd8_uF)s!)jhWdvx7G)LjLn0GN5@v*80o)|z?FhG#XEUcR%4`F5L9J_F zLVFdAGz2{dQk@temAWFhs!%aV@`D#z62X<^>s`R$pI*q|K=3ttQD6JUU94rB2S;V(34pE3?QXVh|D-%j^Bj|6h z3QGwyE6i~W@++&(s3vwSEaZ@O2Wi!AV}5JA+`pe_(dOwkvu53>oD-WZVvQZ<7Z1ruAJCz`ooQp~$J#dv&jk17t!?-Jan8EA5@ET&8u)Ub($|w+KD;W;9r76jtS4Q%VZaCnw)O86y(jZ^J$_Yme6+R=1og@A&iqbYIVgY z8Hub?b@efByy^_#175I-@QDgJFP=1#H{-9B^xih_Ev{#Wih+-|k7+q47$ z0s{dT_nXv~Y_m8)>6DjymwDTCM*isYUq4-4dF(h$_DAi}>VMfLaM~~D;QaH&IpOK z0jr)SVQ_%qKBM+irHM*%)MQ8*A`FO2q8u7p<4h^F+u z*vVxYygpQRo3%%kvX&WT1oR8Nv}NmtnG>^T4?cLbRTL8QPqbSpznj3d4lmCH{1ty> zFy!msMJAIJ;P!)OLZN9b0NW_j^^i6|7_%NobKL(;rY$r26KNj66=WmSxY_kTQ_VsD z*k>x#Ae7E=Q^Wk~!fO>H(5L}QhHHuK+#$0szR8Zk%UYVFLkNQ2Yf&+XcLj6=qFOi{}$T;N=xgJ5p@@|p;O zvlMLNgzL*LPQ(rA{KX^b(L)691Qxe3hqH<#~!!U`|{#sw7B-NGVtV z7bK0*#DZ$d_N!9A&`DrCj$C@NYxCsclk?e4J#NpYg^rP@C&G>xwAFNNkgYXTnEp_k zg~a^Cm_nGEpQ%|UoiE!1163|-|3>IQ$9T9^n_$bv>F{89;P{Dc8#ip6o-FrU5UjLR z$^p8-`RZOx$!}(o=FghR_`+SMLyK71^Z`?Fyq6GoxsS73oBA~8YM=Pq^;`0*=P_qK z92o>C_OtAe*Kn_!$)kWx(R@8%g%aE7Otn54nVar-r^)@cc^$6Vb-?K@i|ag_1Fviy z=6<{Y0b2FhbgG95=M2#)LqLG+ojl~EK4_FMPAu$0otCxgS_N~1PjIcWk}7Th<&ny_ zSC9kIkky9#sjvrCz#hhC0*jw(h`vs&7GJ6fFX0@K;?2nmhy_|tVmM~)-f|;JGH*K1hS@Dq9x<)<&=*c@kLo4FKTE@=BivG)W0LpPFe>KYWmd+6#TgXu1X^2Zo zFQ4>GVVf8F0INQmHMd_|ycdGNz)jWNw4E9{jrataA)?~!`3__`uT=*?R6mkCj;1M4 znf0RaWHLF_FJuIxNZ=cT7N4sbEK~c z91F%og#!|Z&5EIl^o&U)S-Urdom5F71xwx~#lrbT2ck<7-Kq^8t3WGRUJYZ-TY3@1 z&^Tc@MIsuuvABYETByZ}&o6er_ep!w%Jr6+iOF_Bia@Cq&qwqyWxe@~$(}sJ20Z4W zPop|YP94F!^@(UX1S!}s+zCGm}IHvqdXqxiIEQP z3a%;Pex$On7pwqActddVf=t62fkm2Ga3--#G&W;Lj)iX{OrzypFL^kYOS}r})Ku5> zDapl_CM7UdVXO^wL0*;5`)J4|SP8!eNH7|m!JxQ~-ngVWK>uxF(G_S7LAU`@_No#yBhrYi9hMA9 z>^>7a#Z~RnkaRuN4vpDK$`E3MIA%C-jbX;ZI&1`|5v5`UagpLT1oO4DI`Fzk#F*n8{dh_y&MaXzB*pCI z9%@RJG{Tvx8>O0G@raMAZjn3Hgn@}onpcOMx5fns&>GBU?)EF{>lFE1M8o+2kdzPY zot&gbI^ugc2&Cgtp#uOp`=Bv30k{IOlNFIvd^NyA1T-t6{}rsCNPTrph$Le`Le%4I zK!Zv)3}j9XpJmmMa|ssM2uJ|o^{dClx$0^XWU~4IARIc4Px^)K%6BT-m1X7>ou;H`EUQhM3cD1^Ej_flLwJ=N_l?V(*1 zUqAd%yS0P}%D{^-kItFwg}P@$yrfbuu-@ygLuU(9m|>fLRgQzSZuD(X?DwG3Q5-fUPQWqKDqTR! zC=m5_d3K@my-(VWA6~L;da=vHJWHv8hRC@uGlzP?BVs?jc(zSmEWWV! zs|`~V-Po@`eRlrXsWaKrUU9)j(xD9#neEj`FuPWCt-m%x&{*f zT26v};k7DLwoE_Z3$M|}&O@*E>{FO+zA+XEP{R|Qhex!o$t%B}3a;#(mxY5i~E{7@1i5^il&%5K^SuUt< z_JCrtULt@L{7{EgSpQK=7GphTSbP?@m;N}&z>7uLhuPr`P(g?xF-CEXx&l9qV~z%4 zenJ=r-fWnX1UH~ChhCN!UL(2Yk|0Ze9V5Tt8ZjC3F!%iAjL_D|vKUr2GJ{-XRIeG2 z!AwN0gDfWj+C~Kp3CELF>YofcL{xnk)$-XW8b(w?eC`icGO?yj2fE>L`!~4nccUgxZ_sydo_fk7w)Ug{R=XhV zE3q|Rz(@ic00~drWP+BTkZdR9KDY4qGBm&c=);GOogkvDL=oH2$Zs5aOJ-mFfUht6 zQ{{7K=NC_$nN8aTZp+!gtm)~}|986WC;HIQc|*9ihRP0{y?#`H>`zo@U9|pLB>VPiV&qCCSfYmAJQTzJ4P)b?_e9qrPe5yJJ<_LETLVhfI+GM6~MoORY5(s3n<=q z`ZO1cBBtFWP(69JBbVFUV)wfrziX;p?ASQjZjtHaC}Ex{$8M+Qn3pr0;Zzna_c>#a zs5c|C^9yI@=Z~B?)$MjEQEzd#+Tfc}I>G+G{+1@&>Vr{c8U+=#B@~?9p*sNOQM8k- z9zU~KaYW3sfh(6lf12wB4P+GCYT1hGzw(!rds;8TID@g^10zf6loBi2(EBifbQS zTTlwTr)>FdOt<6!u2O0ltFEe+O-q8lfp3#QU@&AQDH&t0kkgchf>Mf^Od*#XnX1~J z82N|{Pt@?FU^$^4Qy*_06jMgECj&!?U>!(Jq#sP$#0fx((T7aqe<E%86JU(&eY*8dTNv!TDECUl-{?$awZ}#rPbJg-Ef)ay#0&a#h&eDHF;C24m04HMY z`BOXZ3!H=IZtZroMvHB|t)hmbUDg3NTtxEY{{8zeMD45iI&kq61W}n{6x2kAyx*%4 zd@Nh5voQO@*T^DkcZQ(%2#&VnQ=1UQ%ZRG6N*ki+ZppeXi&2So(R~l@d98(ON+U~K z+he80!XPrDyf}uoQj%7oQNV^oc7a!!Wx~3&j02}>m;#I(B~@s|%m$2Z5ZCMjQ>ge< z9RN;biI^Oep6uvUtn4z0VIm{92?Hz6yTN55P9-<6Fa{usV*o?AK-*|*u`3-LNu??s zR2^P*MYg#ho1;|q-dy&^IqnRz-LdGpGD-t^hWlz%99dZM%&6zZU4GQwY4_#R^PLdC zSUz2LZ=U+skJ&l5*bQbgDkc?m6Xj!sPWoX4m=!AuIsQ2Z`@eGRBF@OM%T~d%!|_vR z%5i<*_zACY5@lvnptA))muJG_UM50{RR;j{gj2weAjVRP1X}|MP*#Lh+uwK!0h*of zD#sbD&I9fr$0G20=mBK)^3OY+^7JfF(|uggq+pO5Xcc4}vh(q`%E=48A&D~4&aWnQ z&E&mxj3ZvlnUY}Ixlf?t{wOC-_BdPzV6Y5m+5yOJ{6~NEN5chOpw)Xl`>L(dBIm&9 z9La#+t_as;slWUMrRau>Moz#8EFr9;k zUQp$;E77WTddC2oOi)eQYbYwmVn%Xr${i4_d6ZryEYzyUv~NPd38BdtBc7?$-M83# z%q6Vd12Yz%HEbs`8Js%s#7CB0@)GAzHjz z^m`(NElXE_X^IyT5^(tm=-V!tQgyqy0a$df)sUB@td|~595JI}76r3e1zW2NS#_JDtla{lh=#KD(e+YieA#*)dulpXF z2Y|N8n~6>LONVA1Jo+*4k~c0yJ1;r_De-r_CnC{*SUv=ZYc@p*lffqvH4q?o|F*@X zH4plm$EV(NchpO$q3Z?fYA6VhyOh#j_5R(mWy{;&{`NMY7T5aKYPCkTuVSO@Nz`M|(@B_6$>Fk-?=lY3n4FR8dc?aeNruMA zt3{T1IU-ycFh|)S(hgKy9_8GVd;)nS(XddGA+mfjPGr^Gbix&v%)Iv#$MDNt=1jwn zhW;PRNOK{vs$P)~Ts+k!{Q}&F<0FBNpFUGWWuD#_zj5s0y*suB7sTK_%p4B|S1nDN zYfB{HK{xko(!uj`BLFolAka0RnNpU26q zQPgFR4dd4AQYutmDhDeh$IlEdZ+> z90FmK9RS3xh=Ue6Oo=yN8d>olWr}TlUq)h8+$qkgsdfuPSVEM`br2Y&>~$LiEdb+56=DM#bWZE-i{`c0@sN%oqpAe1EYSExLJ^?Hc%#O8&`OWF+>`9oJUw7S7LYJ(TGn( z2DVO+)EpjB$Q9}FT%4{^`o3EC%ee*dpfGN&rzv|z+3npE+}8Em-os z&Hd=Vy7<4m;_{0xo}!5&%yqfLi;G?B^r?ZI%((_@Mr|R)V3AM>($tmuRWLr&0Wl6N zNIMA;2+Np@#y$lG5G+V>t^F}nJ84vFHK^pf44To%)MYxp>J1A%UlFlY(UfA?8SRw- zlwoQhssaSCAIX9w2Cu@9U}>w&7^G^55TZyaQ%;(P04J1GpUDt$V>%!EdZ$E zu_m^|xN8o!)*juSEQl*!|6*xCF{+eoih>2?GuSVvrYl7pZ9?GJjGiso!}{%1lyEgr z&?#cMI9KB)DwJ%FmVbQ?z@zugq+fsN)Ovd6FZwAVrN6~)@*IqxOMuYc0+~H|)s!qDTo&zul?Kf0n zu-0QTq)XsfCX2brp7HG2vuWDU{3a})Z__+z!(1A@*Xv&Qy3?mmzveZsS;a)F+|0ka za?7bdzxG|f^7x%wCKhHpizgQr=DUj>vzMGIAO1*|y$eNA!pPRLCov}=cP}oVq$o`p zA9*dh=g1s2UT);Tp$YhB#CLLL^F(GpRz_c9T-T5~X+@PrDY}yhwouB`sMDCl%`iX} z&Z4nnC1xKoXb{}1rX~zUYl%H8|KUo-Cl>yR(^aZ`E0WV=t!D>?W`SU)+YaH*Ej|s+ zi=ppp=UR)j!LSrU!NHPp!3meC<{1#x>>;3cX$FIiz6b3Xfu-{BRom3~5U@$75&)_; zYlv_m1+|G_C(MJ%FE|E>7!mjrOaH>!4k)Z7&L?|1u$o0kPX%<6AWU98`|YeyB!@lQ zCerVonp-4FRf}-_z^V++>=@$Q0ij1!u&MB7zJKS0{~_nos#WR##ei;W6?FIEqXm4( zcy|p}I7{{pXJi>BVWL%VVnD~k#Xg)He$I6&X~4vB!*(?V<#izjn(}2i0cD*R<$s|8 zr1941Pb3@LHPB2O7A8j{Fqg9pZ+1q@{Cor>gUG06kFXI({I#~qjZ5hG@#77p`i4H0 zLx`mZ<9?eCQM%yWU!|MvcDrwS)0+<7efO#`5X$)Gzy141|HJQHJvmVFQJ2k3bRK+;XY$cwit?C6;lQ3p7PUNA(U%79#D299>ON9*Kctg{PSGUPxOSkO8 z{W0rH5NO03=ncoBDq~5S>thc`y~TQK3FHB*)?meYGX6h+lp>>WQyma1%z$ja#S5mOf`Wq_W@;}s@37xu%L|-KOj+F^4yz}*TCQa##+?09AQQe zGNb4df{!PIOd=by1RDlNRdqLO025RrU_@3t$e@fEOV_TjoV_}#+_TE%Nbd)fxEer? zfVUwA2nwlMsvZA;5V35Ld1Y5nbQLC$1MPpjYujY{_0w}5-#BLTY|z>~RZN^%_@=Za zMRhkF?FmThxmxg~{Ri*wgUjd4+yZ>UMQo2hbn?{P!h$^oc8@pm%oHi=2no144MfvY z8q0@VXwPsRvDOf@X$ZoT!^h6}3hS0OPY3pL(hRAb&3 za&CRiTkG8yQim<} zPkl}l?@=LNwn^IKwiTN4kh*6&cOE+1K!vdGRS>jL;4xi9vE&@A#GLUebMjS(JP%R`&%W$V$*~$5>93S0V=Fa z+e&+f6wHoY3?{ThHUgUL(M1+x!>V`cQYu^7t#Bhns=H(^FXQJbCS7{HxV{NoT^#UB zI%aUJsQ)jfV1N(z67dM*VaaMzn^aHe2?XL%-1JbZ5QG(R7vnkyJi|EALDHbF6eX5S zA|pZKYTXT3nqufh7&;w(kplo_sDxE&uWPFpz$gaTL9K`1t|B_gW8fEbn^8x zOs2~8;mrJepQN?)moO(Te&e05x>p!EWGlP(>_TdW0QFvPed}A()2TbMeJ*48#kX8@<>edaXFH0gw2aQ+diPM2g?n-)sLwuP zvT!z(3p54HRBEyRW4m2^;;W~+^`&w~C?w@nww>H6DCto1YH58RS{h+}ewI5qj68`BEThjwz zrB;Dj-?i1zuN}0>5`xLOwaNgX;C!^6w#^8xD6@ifsWq4S zU1gJ=v*+_in2osdG7h--vdvP@rnyG%@IT z%9OylwqPvufW=OyY!`I9yl4BC$yV#C%Pwj65j&^P&K*5_D3J(rQ3h#o_| z0FlstsiuvYoif2QfzqyLsHCf&YT_my(|l!LW32tBdmo||;J5P^fAJUDZ8mp2>h{v` zd0hjcu8)x(jB}7Ubm-8E2k`&-kB@)cr5ienEDc{uYhk3}3?V|h#6*PX@Mt)`V*8jH z8wKZjtHsEe2v7wJm)e&HP0@zd>u&mb!<3S0rX(_zMv;O?o(;JARTYP#lj^AMa|4b| z<20PtNXO+&FyfOKa@`n4z%q$czR?@^_dW|p4~8oYLA?QSOSgfk#`H7-{5kfSIAj8U ztDqR`=4{ZvQ1z6H0O=~QsQrndB2@Kpp|o|hLWtWj8E@4CiQx*9NzOH4wq*K{GDFQI zr5*=~<`YE=3vbK7mSzCy5i?$I3>`o5daw&hq+wGD082R!xxmnjlF9-p(oi8oycR)R zU2&MXop{imnp+Hw9e~71!DgM6k`^}|3>Ns44nW9t0D|oR^j>odi_}+a*uH6|)NAYg zd*vhc?b^Dz_no^A9kD)a34R(($BZ7R9gj9@U6bkSxGAj5hajFZWmLvkwqzaQ5rxM9$?%$hNtM-x@ciwBzXvoL^3J0}r_O+fJwR?sxy$ zK;MacBk0_Az1-9^Sf_&Cn~!|tBP$q_8~*jh&wcvN#RWB?3Rz|oYX>VW*&t{?rD1XQ zZplgG-`;qcn;NW3qCv;zKlHhW2ag;ldu6rM9MeKHm~>791|I+Q#hl!*2-}nFLu(V$ zvTF7lF%8v+iiP1Q259l?_BhHiGaCX0Sm zQbmz@TZihN;EqdyJ6MrtZF_;fW??faI5ul`TYW|q)tQwk40(%KE!c-nNW4@kT4 zta%O-QauDEO~%yB*!8MX7=W%F7_XoIIdfvn&|;QB0!Lz0H^PNrCy|rHKy9a$JV6dO z&%WPsPQ_Ui^pV<0e@yr9^fZ7IXBUIJ>A5tHO-k5{ZrcUn{niOofpn7r^tcSm`t^Ef zz00C{|KI6W=EOTUZ|qi@q}2QHl8d%K@v=*%Cnvsq?*nB+fRo#rhtz{QDI4Djm*VDv zrZj26+79zXiyl7PC2T5^$sd}+C9)o1cyZ`tzj~^&0NH1V08aC&4k3QeG9cdGnsHRd$>I5bM)wsI0l~N*^?uj(L zabyi}3GAvAi_e)@q8|~(IJpY`h1O_XtpeKnvQa%uGSmteWUxR+i=0)^(?*bG5FM?C zC5H&&FO10bsE403q%G5mim3^~hg2wv8L&4WNRewu|EN2lnpSzV#bNPZWi5`L%6W zvSyAJP2c}{`qSkw=mnOV(eIp3HFvNH(|oM zwJG%LdM2!L#-;&<+_r6D-aIk2RL?*f(^_SFqWILOk2^oj0hu=*a@?Y=eT}fP?kZkd zjfF@x@R&FNQo3jG>Ip$%HtgiYv1V&je3UU9ynj>)9HXc;UwxNz++hxe+RDu60Bos| zEX!D11&QXL;J-M8lv%(c;1W{>w#q~VKCkSvAV%FrlBJeaU9VPy8Xi@@FRb)`NZJAZ zkr3(0U|dF3v79q(7X}|SN03oS|^`=rQZce4zkjQ*L8m`>RW*gP|} zYul!7m+{65=Y=XfkeJ49Nk|eq$}`D*)io;hE=B*JxNqT(2bVf32OsVnIn#A0>rfA; zt_HD8Q;mSBka>W2RGMuNHl;=#lZJ%yC`y`RBxSBFyiP?w>p0CLCIOpR7Duu?l5*J% zb4U?FL2e83D>$_ptlzq*`e#|N?!!?gpV?z*_C+-$pGAW6E8 zLp%&oIOMcMOIP6SJ9b=u{q@5S-49%MQTn8jiel~=;#3zZ*PyDp7vz>0@6Kd@3*{^( zo5q%1a)lC*c@C+HH;1ps7+244{HRB0jwWBh;y4sm7(kQdxYnyN~(BBm0Hi;|p3AflY&D4dy??^1A5 z_JEshmYJX*z`F z6xpLJC@HY=ik&KUK4;blm_o3TxArlFP>mv7l&N2_!iF;F8(^YiyCax+j9wB!VLh1^ z5#l)rCy5g~zEx@SRUD@3ZUcAMEmUF6 z@wb2Uo{f{OAu8JxvLr-!<%;$3$B7pG!(H=-PIcr9AHR2=P-+_3@23(Dz{csKyvJ0wd3pi&oiRvy zka}22S9GM+&QPy@*q}?eKFj}gB+d*oa+4_FRcRc6d!Gon9aoxs4y$q=Pe!~$K{a2_LRBhNo zdUm}u;8m}B)sW-%Gtb)_FR04EGZxu2Rwob0fs%+u7Ode-tdQ8AX+)1O+)=%OCr2)9 zfZH!)J+Qo!Pi<%&__5o1`A(-h#8X%gsd~^6OLZ|;az-oUW;IB?))K))m@H|e9RSq8 z02FP+p+v!Y>zst*uW{bkht6Qh900Y=84Dy6q!U+u8xS+v+NxL=S7uO5AtA)FizC=D zWKBN5C%tCm|)rc)zp@hC+FB_ErU$iQiG zk#-9Z$Sl*yDCVmT9<~v?VCkg|$e@_W0A0d3yvnPB1EXi`qR7_IgLo60us#*YG39=X zauO#8E@h`G`6%sYkm%yms=044ke@sRh{LxW*pU9oy~oeCh=Ofg=?#=ZtDyUjpZ)OH zj^bb4b8Pm_e}8AIu-*Zg7{T06v-zbJd*ht^e)ZZgHEB~Sw$%+Oj2T%2w@uPZgM*#1 z0XL`Ihvl>3Z~x`&$$9@+xb43A$(GjYsM!K6fKQ3#8WD7+>`{-|2cQn2p2pVe{FUYa z4$CpF6LIYbIW}`V!&wD$f(!R^Hv|O8t^D!MR~C*k`i_&Mi-dYvo`KO`r_<@>0GD2R z>DRyh^?pU8iygMQ)FO*v%eIx=F_)Wd&YJLwaHol_fZm5sd)m{69I+Qad)L{s9b3fp zBrs-+!(^z)hTKDd5dq*HAY_<~fX9gju2G*Ih34avv;>MwOcn?J_mAKA(EKvi7L$p+ z2@{GjU~Jgx|FaE`tu{Q3i}WN#LkFFchaeHEzo*_k)FV4bH(%%g;%KL)gOGsrziyYGcv(IHC@3Gmg}(4kFUbGn zkKU6$AWqJ8-uS_PIkV7d7df;vu?q(6!yAmiLX8m^`TN(*)Pz~*SwADZkz}52RVFQW zcFNhYvm=q@W1^)+F}uhg_}biJt|oMJwtLTsP65$lIY<@l0$z{VHxs(h;l!p98~%b6 zY{NPTtUGR(r`x_ zrUOvs07X$;e);7$-+XhcFG^OQP7Jfh(2$eV%H7tiz=l>#*&n#_$}5K%w99u-7iK(& zQ09Xy+=uKfkrH5LLIRGDJiR)Iaf9S*i}E}GP_dX%wx^53#~0{%AHMhhWA9DiC9A4? z;k{1P9lCE%^vKkW&`csCigCah96%9=F9u(X6N$r@1brHXJo8NA6u*ZWB_L0tJjFOf zYTmTf9<_b-MZbEs#|=iA7WAW)Tw>;+2@=! z{D&Rmldov|wRWv0PSkM{FmVt_k%7mvg64Lm31BO$Ebt18?66gT=;`~Lq>?L9OvN?F&;`iJqL~&c zn_8V0A00^mc8^rlBOMCm={JY^OIP2%wy0u}et5;rk8RvD*5ANI0_ll?*DR7)1Sm_~ z62SSaJ{0Q-qJh3rxoJWfZ4xl-kg&fBKTROON+#?Lm`Hn6!yw?s(x60d8>!#?*jS6x zanI&Twa(Tq%|7FUnVdP0sP+wz(Y!$d3AoD~V1w7<`K)8gOtViGHOp=me8L@1OeUx~)|m4b;@ z)e5Oob56W;L9^4s^x681gxG8Z9tw!Xrpk$oySPO-B& z&J^Y%U&pD$hy({ga>kB)zOIyzs)2krAxWPupy5>C&Zb)uHzdnEhK{sZ=hN*z=1MI=<2a z;J}<2T0BlCm;eArMR#cIA-V0+=>Lull`TN|ZSgu#;cffths}`WHB11OE*xk=fQ(%g z9Jq*+*wIn!PTr-6A3^FFr_kG=CrZl*<1W%R?V`A(H-O2GCkK2h*aLZDG6#{rQCF$J zCP2b;Yo$enJB^m>Ih6GH%gxt`C!YAKSH0>5FL=QjXPlA7*)5PiMG&CYCpsz{iYu)M zfD|eMwSGPQxLKyc@SIe_;|zs3gmd-{lsNJ5w=HYP?UkBUOh;z-?#cseM}GGE-8bC5 z=jQwNm_v9ZNBuLI+2^EdoU`j6NVyC| zH5_=u^HT9p?(CPIZE2Xmi$Lkat5gE3`J}T>2nUHgYXEfpq9QC9aPRa60o)GVu-&rNXed3N}v72Af(iC+WCh(UjN`Vp^s%HlSEeEsgxPv5YTnur}Oq#~-y!b>*F zGmKEanY1e|^NhZ}k}fcNL?s6M4ANF)6cC$`OhzpxAEW`eNrtX7|L4llHlzRk61{Bs z;9)ad^xv4$kO7>&j#)ftq&I}RwfSN$mvG!pJF3f7b0h*fMUigNF%R)TOdtu`-2(|J zzb4AuH4}4(eBNgfAGD>SXt5V|H`iX#xtk_~3C7klFuCu4|N9^Q@P}Iqn?^+=Ch?du zUfbSeF_US9;!g1bmI%ilb3}tgRV~=P9rzj zKvKlkpUdi~GOA3HI=d`T2+9)x=U>63-ML9)nh2<(#-J4&dw$(6j3HBCF{i4UR&*37 z!U&C`c<(?erhp^z9+3ibVTE{6*$q0Q<^{so>5DtJKr~39oNY#-Qa*(8PkL#+ypD771{g6PczoBNe{BK=3DgEt!?PZ216b zEFd+clOm`2d`@|KIm^-%KqDN0KNd9aytLoR&e7h-|ghIT&$7 zR-Gl!AVS@EA9_Iq5~~cve~#?Xs0bPotK8RFv7OnWY?#%#QSA^d9+j)}5PaS9UiBOg zfNgH@bh3CLTLe^FV?Kxb)dhT81KOxKCPpXik#=%Ca=F-9_TB#c!)qtEE20Z`5eFL7lS5+ zH_mcGTZikntQl+bEIeg#-x){tS85`&?Jm~PE+Bn9^+`jG`c`vDCGE=j%PmI-HiK-uLYME>t+_;(_=U#gmy~pH_)iRxX7yK}tiO zJ4JN2-HZEM1mfLFAX|)~7twL6R;_Ah@CQBZ;hmR5VZtWna_zRUo|`>9DD?=IzjEjA zGq*4vg2sU3b#kaml-GZAS27O$Pi0W74x(&h zRUjgRZ5Qhs8i{riA@7pdaa@gPU^2<x@4IkZY^b}&>Ih15<{>Afd7+Y(xE{R5o^*ZQj+_Z^3Vk*LZlWmd5M$zrkmYQfuTHCA#&l-T(H>?ce&vw#~aL z*&ml5KKQB^E*jrgG1El%iCRJ0H5ZF;Kf+`KD)^N_(C9&m&)Q0p;+Fn+cz`)9_SCbUn z6y~9G*SRYliHLY?MAFskcqc97&tx`pJH= zR)5*k{RAL=-M4YyvyYuusmU6V0NO?&l^U<#J=#8(?WYfJ+p=%$wN4sH>Ph6XR8|hY zg={kl^Zi`Rmpr8uPthepPR)5WXd9g8)-|p}`Ll7x2TJto^_4Bdwf0ZJ_ThS(0H`*D zz5k~Fxj*OtJpH(#>fY*AKiTyC+jrb}&z?vs^}&+&j5y9R<@D;3Wil7P9rR=~85yE;|FobbjKoXsBV14MCsExI3* z$Z&vKXT=Y)N2c@LG1IHsG07Vw$nAhJDN6+zbfG&(0w&?u8c{oR;+QNtd0o*wgGin6 zSr;}hjMpy`l^y2-#RM@b8ct{0A?)a@Sf36n9uAC-;harY@w>U)h{@NoCJyL=r-iVy z0UeaQpiJ<9M<@gtgRpo_kv5)bF@p|_JdCw$4oYq2`JO3Wa#m|4+xXI#`MCc%ni4AWb6Lt9(P!Q=_fU-*R*Bt*#5EF zq<;AB^?SGO8~@WYmyTBI;e)U}-h+QDXy;klS|`rMckS?O1j6xC(6u{W9{&=!K6|T0iRl>j zDik2;iA*}s$#HH>#yPwKngz^y?F@mIy$ke>_5^T_QASY@Z1piK2Ll6^6mVQb>{)b% z@Ti#prIQ1Dq9+y0^knrcE%3zePk@T2gEX-LCt!(CIk3%UOyW`CG*_D@2B$Cv#D=1T zA|{Dt2gW!R7oUJY3a_L+f<+HD160BF#>(Z_CQHOXm?dv+oG6XWwTSJoQ01TeuqIqg2BGz zm#YqjjJ0vk=s(`Ls;Lw2O7$R`kvCSf+=PS9i&Hsy`MH?ALv%z$ct)i;;BnI7H_%(B zQmYv&_>)HP*RLF%Z1kUvu0g4=D_8z$lCVP#F}J)UUi&{!;gPD_3z_Mnu@lwIF@au9 zZ^bv^`)(abM13VmrbY!QcBCy-fT&=tP7@KTq9f}3g%F)*J(%9BXnQUP-qD3Uz)2K) zNs%r|DUS5!WO+vj{e7oVtyCyFdk>dcX5V^>P(y>LODaHwwR&a{`?Qr+R#Xb;4ItxM z*v!f}R$y4nVhO}ta08{F!!8P*;G57i)5^~;S!EeFLaWNIm91P}IQZXHKL4Y`aoIY21I~ zsT`*6*kYB}-mD(Tg={XlI}x5H;1KkG@y4mFqwOp@?2PlxhnGUIx4e`#6jqetFmra-?8HS#SqWrRd$0XQ`_7H!{zw&bLe)TAMLb(*B1S zj{EQskm6y(9Qk8j^UH-U^B|u&Vld|(vc#}1NqOWFLmj$V__ktPQ~he>PRA- z*Y*h-8BEUK4A&AF6eHDu9-|xvBwNv+8>p|7%AFDmG$Jp*!taTSj@Gev6>f?=I20My zwLR?kkP*;C*%)cA5IP&M@dTArzXvWg^ax-B5GA(YZjKhV6hqWv!>zCqBtO2v8}w*6 zltHp!GY?Vi3;65NsUs?YwhY8nxHfBfb*zq#Vk zN823AmA_cCX6Hyl8pvK+Zk*{ttZM1AfzEU#&!b$dWFg?IZO`-LJoW$;muYRjzZA!SJ*SuMb5O zd^-05rqO}u!5qZExV?@+^jjUZENWwc6>Tsbc-v3-R|_BF_8&_=GfS7|dcFxDk9HCP zN3VF(Ey*1VMPZIH{+YW{ULqmi=0$qoUpgx_37(x@n5g*xc0VsRlloM$RtN6ZzD?Sy zmn;BWQH`A-w%8!N?_G#sPxdKI<&c{xqPkI3rA3Hk!i85o`F7oJOZXL=4Vq z4BQ(9yC)%H1AREm&0MBV-UF0TV^(rBQWj-upakMV?0v(zCG+EUA9Ck38(^(&Uh|v< z;ieC7-LG_2L9nS-o@9cstL(tFF50yWjn8rBZqGo8Rnh zP_3H`>z`%uHkUU$4@@Efc*WD_9ednRwZ`FH5+Q2SDj?_~|F7uar%x~!`Sqy?7Fww2 zOzLhjP0rf%S!^wa@dU^Uie~#z4viv}LZ&A`=V3SarUbJqAj8gL_Kez9g#lEQju950kjiZvCedXlA z&BNXw-ul+JxRm;V2Ojv#zx>N~26z2K+yCR9&9eu~p5ADSie+>K2cuL*VAS#+6E39m zKWeqgZ#PySZA0!eJ>(TDR=o3_?@Zs6%jIRumX%5+Z=2Dor8MQ8!p88UE?`BkuYcgA z`u?hy=-B;DujiwOdFVytk7--ey~CrL+Tc5?{ojj>ZH~FoO^5Ncb1I(zvwet%#=*Uu z1*<&0Qq4n-YF&kzj91+4!kwJJd6ZZgZJ97+>r%F9T%1hGFP8$>B^|Bg(YjQm$wkBj z=%!pJk_f@_k*J5k1y}gJ2Fb?8$vvSp88BWsh*C&f14C;+(h_qQT$Z#6;wNum=*UsH zzEXr{6A|#l;0i+CL`TZ%&uZQY?E-9loyA^PL&#kDn^pJ0&plmKOvW{#EcY9dXzX7Y z&n^x3OL{eH=snA9YHMsJ2oRYaV6TxaIxH0%mHrrLi;ya1kpqaU?1 zz~67&_xW4a4EB|b^2jJE{u_WWg@@|rTyQ>2qy@k}@XcMz|8f7GcH}-!Ipvh}V3#-h z9bc9I#hg#!0c!L%O96M<6*mqk z*f!zAhS21Rsl&mx6hV!2rt`9-#8cf{u#OZcAw4;%cm@oPg9XL1RT7L3#-SDp&X%&9 z{Q}_a)hJwJ%U-K@G++AsI*yR=HlJTtHz5QXMUYX;qzA zSc1;yK+-5rl^jx<=5!>AcG-fB?;zY(A=MaVn-!CeaOaLSHgMFz03 z=EFLr$$(k;KCGZ{IDFFZ99GqMtK7v5*c<{!27{TG&@|Y1j#JishkdD9r#`Pt8Kx=& zAR$&$CIOVU92Aqhp#}7YfCrrZ;jZ<26H*-R?4Pk}{XnJeU2Ug4wRPXvr*2ru03JgT2rBR#B~7cQ;7=6E=_1W2EHX_ z_uha1{RTvbvd_~4_qN|NT2DxhB}vSNy=wpS29r}4_nmd@K=cXl%@$I{>Rt{0A$~K2=>QuaInc4nxAz*&cJ?3JJvVPwm>i@mbLj@tz2nO{nh?WjVL2zp*Ol-e0PYNBJqw!# z#T4OAdiZ)-nX&qqV|J2lH8I9s!7hzEaSCJ4m`}MXe{P{1>dHgxVLY&o)1;B8k|3HS z@xx*xTCK#0{UHKjS65}uZN=D$9VedC7b_;>pJm(oiX7|9FyDWGvRR<36@@&s)?kZsX*c>SgxJ=Q!z;~{) zK%hJg$cJVZs!K$t;0kw0${gg^Y>KcUCGM1tVsG`muRb!|!)>c~4wOr7xn)UErN%9y z3VbTmo39&gxZ$&({cJk}{=zTUtllw_kSeF;FV1ZYoMnJf6j&LdEHSh|u*Zf6>%ILI`s*|&G^Ip>_?36EwEt@L=8EM6R>H?Eu2_$SS(UVQA}OO72l z{fNFf0}YdbYTfJ}t#2ES(1O4Ez7wm%wfr6>ej{+L7!qC3ZUhCb>xGl@vh8|&6Y*<{ zMcCXad(752@<_VXo(yOqR$g?d*-YgLp7KjSI1pyfb zER1IcEO!@ndPL0Ncf?Mt-qqsCwFvwJ6roLn5=x0iUqnrF4G;s!@mFmO%P!RSkK93N z9LSHwE03*~zQsQ|fh;#!6qG%cl&!#eh*9N70Y%u$fIZ;>ym`BmnMcy7r^LDq0{t>Z zk}2jXWRXVBX^cS~!06V}AwMSltJ{RnA2!t*zxC|JVGjQL4g0V*k^NsSD3Cw(sZWgt zEAAn2;e{8rIpA;Kx#?G{cctMt)Pr0$2gH%K;i0VCgYFY13w~rC>M!5CV(0hn+1$ zMGR032kgboT{LEzY)n}MSc<@zI&vUIX!2-iJ8AjFaQlNbbM0b0YNm$@P;2Y!77e9d zd0~8SV*R4v&?a#%xVR47>#w3EIX(2MinFxtYyBEPDtU{<-G3Q9o|rw6eR77h9{5_o zLs^ZP;!5|HPU_k9N(c>o1Jbw>-CCi@1DZ4*Sv+uaM`FnLIuEooyd~vgI+fkKy9!xW3KV;pA=y#0 z3Nv7fz2a{i_ya_~2Lh)Q5tTH8OmRzuofnw5JzE0@X!sZ;3$r*$I8QMW9NE-y06-TD z{eUH5%-cp!J#FsLVB(#veWTS~BNfP@xkP6BeqR?qqyM&ea$I%QRqYQujm7`%u1z!g zN@9~Nz%nNBX7j5B{z_v@ykW@cIJbVz7DpOe&+IRM?KkUxxw2hj<6r*rmo0*=^z+$s z=Crt}qGLst047A6_Sd4KK%e{b<<*f|l3O2#L>A`+(EV~^+)%cc4S>Tuw(G}I2>qWh z+-SRkPS~q@B7|Qwt$q~#)br*TEx6+cGwtMY>lAeOiJ=5aVNIE|+f+x>5=3YyN}MN! z-nBb{9iLU|k+>5lPn3QToXuRsKy~g_IYtCyTQnE&+=L}&BcuDlpt$KyF1TyiM>PBR zBo0I4X3fk=<>+(a2)3YV@2I_q{Fkz_3w|>Os0gZ(Rq27^Vb)`@l;T8%zl0qU5{!?- ztMN>R3DD|H^%?}I=%jSG;vSM>5yseF)w?(5 z++#L-jlRiFJNV&{L}N^b3!Nc*?Ir>>MjN~kBapqLAg4SwaAr6TGs_v#5n`lia3{#a zDRy;thWx&vkR5?UUsp$Fyl%Cr8>u)Mu+sUZ3>^T2)9H0Xf=&@zmb8OMIcZW{ zR#1dS2>{K%0Q+Uu^>SmoJ}P@AMPw2APto?_Xt|UkDw78CKeh&Ac@So#!{9ucA%HhF z(2n2jf%3p?De6vXDd0>WQkHKCGpGWj`f9G;S7&qKOCw^Sk8T?ttJIlgc{TC@{VJEs zq2vX=&#LB=dhHsiTzdVA<}Y)rk{K7>-n^VJsOdH=y7s zckP;Bm~e5I5?ZmNS_#1aeP#OSJB}MaP!oOF{1+K8WY#RO+Z+JI1cxNxC_iM{2KucP zPUMVtkORPalEd-H#_d_;nn0$>kKU$z2f%}*BLlcAkTX0-Mq_?Fb}~^<7PVuCP?$y% z(PVB&3L4;SUdgKXQAoY(rFk>jut`dZEGAr?mU2MQ3u0+I|OCVBXZm3&U%>P-px73B8{}?E*g0DiL=7}+`WEpe>usvt=D zd-B!mbwr4!|Cznd-c0sYV{_ThSMM0E7$epRS?uLE(mhDDJcHq!=>T;5@c8F`zPff$ zkClfWdMGS(xnrzVeF@Q0!#-IORiN@T@~($tc4J?<=mZ|EC358xxVEsg_iUG^>l}b0 zg^(T+1I2XCv=nmbFbBX3!m_I@8wp-n4yxDco?y3cE$1@*I{+<;%{V6=OE}$`a`!+} zKn7s$$)Yv}{qDwY3Y2r1c``R7WOp%3SwSkU+_Q)@qM-1|j(?gX)Y;Qy>`(`Qu|6vR zdK)u{{Zu!WDhI_fDaDe9lAr?nVximGh?~?V0;*hj%Rs z*dar!0HRjc{b7lFT{b`nX;NxEY`B=Bk%P|>`yg>gBpGsqZ>va^ZId|90l?pAGbJql z125Mm|3qQZk$d}kslMOh5V`^>w_u`(PEROKO446rK!+Lha+5rUNomeow=SU-lWyHG zSgiTED&&n$XHsV|M1tWW!zh7@ofVog?X7}p6Dyaq?EfnJDu4gS%fkED%`0};;#OX? zE_xD2`|`VE6>;1kNs<-^@a12vTe*GM{U|x{B)_B|$DDZX1dV=0DS3Fy{{M6Px)wj$ zdPw{C?~fjP_H^IAeY?N6yuC)CXWSGXP0XE}#>4#|yJXpa>wF{{~TO~1MnAVOjATjy;nN`iUsP7N6GnsaTjzPG8~NSvXF@CiNu`8 z&+O#rf+{?~_H_YR%1hx=v5|G|Aj?3}m7p$H8)D`#W+k{i=AoT?`eO8nPEHbCc{arE z8%o>WZ9*PUg#D7X(W<8+aDok;43Ni|h$xV-1QZm|zy*Mkgy%tygo6N+As|7qekZjr zai;=gF%-Xr3hkIER{oU1twGLeRCd4`yeg|SC-DkFym4Ad>iju+gALQ2mt3xM+GLG` zwKDlDfPtUjQ zwr<%{^!ue^OnkafKlGswweAn3KlQ!4H{Z5u=TLuHrF-5exuAxiw;viP-}K0iYwz9C z`mQ!(#*DYW{q046-Lz>_Q9jex*LUp?uJt~jJDAkjU#ICT?m7^c{`}?#Po{kpqGF8( zl*cw9yS@t@07%5*K;fR~02F19k-v6U1*rABW#egcZbK(v@dNHP#&&Bb;7%^Ksd8(c z=2ypr>k#H)QYt`NmKy;NJ^mspD8jKaGwS@x09Xx~z#TRRCv?q#jU1@k{TDeU`#fB- zMOIQ_j1mGObTek=}#M9eAk>OR8akb2-EutaCjvQFXDgL4l*hM=eW?g#~JC9J$DZnpucpEO<(K!5Ggw z)W7yC1F20Cgo9OpwG3xMn3u?J;^J}SRY|8ran^p zE5BOztH*W~Jz26bpMLu3`}gm^;DQT^hLjGw#S{IYu1L^aBv-hgSJ*J=xzrfGzxXE5Hvh(E6Op+b|1pj6%N3JaoA+%Hr%c0 zHNQ5a$qZc_eAMhBs^8k9X+RVjH{rvl&4(0umWB@F7!o;qumd3GS||cXF`8;82PV0{ zL`M#jz=oNo!Q5eB&%`=PG#(;EbYkF9D^aR(k}zjBu=99W9a#BMtBw8vm}<`8}^b z!rMum8*m>v$ zHvKyQttH#L!T|_Vk%{GiCesS&#kfBVsi%(*KlCzJpdUw@LYh= z*0@<|u`sZ=N#$%nPEff(j;pd!vck+W@{%*H9P$O?bB-+AFql;e#O&elNi$$G+*U5< zgjg5j6gh)XL75P^A4^nS9~4(LdE8Hs2sDEn?n%kGfS`#@Gr>YvS>gdBbyAncgtn5`T zm$Pm9`}=q9-1&w#yrFf4Y{&5Ur*Bv}R;{O5fPRj)65dV+U#ZpqzyDmZXSCXSx^BGT zhEIO-lUW?!f(CHXNhf*_#q3S`#XIh}Biw#gzdEEekJ>-Zt9L~jBcHzTNqw0Knk)XJ z6`w_pvhn_z790StcJ6?}J=6ifbsI%4O_L75ghl9s>;Uv&eR>*p0HUxlZ-Mo02cRh3 zI%u6!4gxL~J;te$#OrPcK(v}!Vr)Qlkc0~z46!BD6^<BwVq6^6RFmb_v;pe|TT#+U%eVAB!BJoOA> z9s1wN|M@bB)~qTkQxgGuLPfp`(@#=Yw6mS~)kg-$16EaTm$_U1L7s6r(2&y9and|l z5+o5bkwGvT2>ufV1ZH9mB0tm~vlt%)`7m@Q9IoGn;zlvYeeY%>mt7eJII>HL$uiVo zaf)6o;IkN{f*86)!i-|uZaSWf>@ zrPgxE+`eus-0k6q^}YXsWz{N6U(ibh9e^&R5KX5Lp{J{!tq~4gKaj_10L;CLxSsyP zvQLZ-z=TI}sK*uYZK6r064QCU3LeVcR)k^%F(_$uP$%kMb`NaMyqGEA^ITDeMAF4M zev(*IkGy0}tK`mFum&&>kXgFwiHenk!xKq6y;z&qG;wVd^)*oO?w9t;K zdXpC9krHl(6>wg^h6C8YA}nJepTvNfJ7t z(dyI$&jX2lnzB$OOW(xmJ&=e}<(c_&WPG z2^s7w-M4Y?SAMmw^^@$p^UmA5ckj%hAw*pK_{Tpk`pS!6@{;hwG=bT+ZCjcJd7qy! zzpql4n6ke__iwF?$7m5Z{lio1qcvQiWYAa8-IRUb!|4D4DkRJHG6}e7%-wtrdN38NUF4*o;LO}6e~$2whA8pnrz%8TvsI+N*cVb@I{6VsA^CodlUCCluCj^ zox4%9=O(6ov|M~?m1KmJAXRUz{YfU2-2reAnn;l302#tOx6&Zcw9roLgrhTCqv)j>WG_3@@Ry{TxBAN}Y@Q9B~t zcA#=$w5PQ`y6@e+`G$wL5A~Oe%4|ae<)1vT^#}KDZM~l>ue|aHKlni!zGs*p zhbZ{wH@_L(KFxEm8}43d0u#-1mdr}*gg26_J!5h3&tJUih!c+;s@HuO0|nPVO?8WC zN`eCq=R+;kfqEj)iJF&n@jzNQ0n6tH>jdm4JiX%t+?o|V4V{3As7K4%EpmJB@;``a z*K!&{vkWS-J(vRsXv44nlb`-{uJBJf)FHrH=fWiR^KRS;TuekRHX|d^7YZygM#*FV zh2a4<6a+mFWQje4HkDb3eo7WU38ut&z!8UDN4Xq`)d@?-XbZs(u5O=cJYYx7tG zGM9M=f6Fkb2KSN8F3`Uy(Ke%^GiD;}+f9A5e;ce^XuXqJM z15g(FLm&Dd(I?7BKJpRtHp*VrYW42B@BW|v^KV)o-R-M)edD(qW(|~43pjferP2SF zf3@znYxlI?!-Eez@a}iN+daMN%~$&V+H0>}w=Ui{`1}=DV3Wc5{@io--C@&U!OUc! zEV)fNF~3|B0sjp4)8+3yp*mW3Omzhv)OV#^vl#N^u5$pKF_06|@lL2`9yC2)xLnUcB3xO=`7o=^)~i?Xga^b)y2W9lc}0F40l_|ZYkafdohZQ zPO|5@rl)-}902#Ivh`Sa6q}xOPB!@bx0O*1?O?HjqR!vIwdf%kz;xL6#9e!?xGHEK zqFc)}-vP@Bs%QgW(fuTUiMjY!6+lfGL_1iN7N>FrI0C4LrFmEfJV1xoUt|a}v#?gg zY(SC)mPHPRAq?Qy)$`%J6^X_(NIcna6Z#e6h6U{89PQpr0P?_IJgtl(QjWgg6;wF9D}%yx=ABQo8sAg4gf zK*%(n%n?ULMUB5Z=zc)oO*!;TQOyR=U3gzoHAC!vjSp9^`itYkCP4bS?*6TVeMvc? zXDprT?RR*1_`UCaZ&>|G-+%n$p9pES*(2oKbI*0v_w4ghsg!>Hv5$THmRoLVeSnW{ z8~&%8R+q>mNge|wMCsT*`?Hm6TMGUxU%ouu_{5V=@`S_Fk<@Cn*SzL6(Qke5gCAV5 za3Maz)9thWe8w4PgxjCCq;I^I2Ulq(aQnLP=y|>Cb5F01)G($dDbg^A0JwXFA~tm$ z0_2@>6Emx7M@M|R6*!|}p!qR&+y}s$WE{*S*rj?utla>n|2c}B2wi0ljsH5fc)VW^ zuZUjv0f-)5*dOB-Hl?j|0H|{?=$!YYbF#WYvRBN3dpm1CfKDd?peU*BiY+aVt)oODFzW2Sc|$n%o2GzB~PPcM4ZdqoXZ8w zdB{ul`>tY7&H=$QyhSk@<6B7+BoI!jGY^N!)vO)-h$IQk%?PX!80&tBa7I|7e{y)B z9NL6QcM?XR(cqz9(_+WI5KmF-X|Kv~pB=nFZqeG zz4TH)DXQ1got$~*nPIfCXegQ4M@jRn_LXSm&gzZ>k=klH$tNE-#Nf%A&7G64BDrVy zmGlJ;to0EC=_Ag4xhz90>)K*rC>^3n<{a#6+)%)d;qZE3>|?y#zT@Q8-??6 zK4R8`B14;?sTJ!QF6*KX8BB#mM^zeNtl75?Fb)*~y3#fgBDGW~3Fp}#De`?-+kH4g26o{!Wpr=W z3~k?EjL;l_)H-s?I?T?qn58)+e!xEGNK%8mi{cE#NOWV3|62wK5@J^e*_v~Ba%Ks{ z?3!FtiocEAJK3-IlUHNONdx1fwa)}{K-ueG?pQywuff@U`I8rgBR%icufFG=d$3lN zJ)2KF@x*Jd{g3Dlzy9^FpK!tnn>TL`D|s_!%-FGG$5~B%o}yQ^&X@gs_4eWME&IkV zzxlB`w|aD4d+oKK`OIgGwJ64)yXKl}vgkj2ikHjf_3PJrb=j-~@Wwa3@%Gzq4}a_Q z!~2`%|3*_{?-)O@Eh0j?bm721{^jwN12uwz0Z;hQ^Mnni)3dQxRR7xvF~i7vI>ftS z=->Xpyd1k{VHF%6R9K(x%K7T)GXFswKksjAv)3(D^?tF#1YSa_On0#`On{f|NU#%uHCR< z!%a8cbn(R(FJHd=XFvPdgva`&+t#)g{K-ag{`u#>@P#kD;)*M>U)i{E<1c^t%S$e~ zWX_y98H+c3fE|AL;iIFYCV+{jdwa`U-tw()eJlK-)0Xr#fJqiD@{1VsKYjiF@~2n! zSM~WKGP=#D{!HrrF#WpYp$|YWMmAn$y}Q_$XdW^p9RTjl_;s3h09yR+6CgeCAZKk# zRJBv5(zih;t8JbT85Q}DU`BtXZXBY9Fc1bWdV}sv7UzPJ_7(>sIizT*K25|gilLK^ zANn|Kz8o(Z|KTbXYJlQn?cXtN#qW&*u@3ehY2_%RY6(%4iO7 z%{5`k*0+4+E7y3e(Clzt{NfkieDlrWR~F7pmd!1V*BV<6l;N#9&OQJwif+Q$k?Wlu1LlJ?iDn7>nLZMgl-|y`k-UnUXf&-)SO_d+4Y>WNDudk z1Pz+n8RoDuX&;?$uR=3GJA=Q}nQsi%e2>ca~hLwyvQ6 zj#;Z@>_t@53SyFT%jFzhZsm4>AIyj@LeWshRLLSeas*Kp7P8Za$w$qo=}9DXVYvoK zNUF-uR@IQ1KzfE$#s(-Ia)Omk9-p-zn3X_6;FsH7`d^858Fmx`2RM#<@M=PkAAE*i z^DO_M4*LWa+cgWrO~~W+dmaoR-@g5y|M{OEe)!>on1)kMIpw~PW?X4b4((dfUgWS+aU-$nnw*-+UZqyGNknG62- z6^EzMKVB%*76iyK(x;0iXjmx^H8gu7+zg|%5M$Go0|^;2-g}yi13+}o(#4pfkfhsLVynD3_$j(|AsuD}|P=(Qq(!uzb!@OK6)AD4I(8!3sTQ zB1@lbhlpIShdqQ7?wc*+!1(6EEG-Y$_F%TqG;$50DY%4kv{U4v4A195Cf{WQ$LLUdzxz? z;^M?3@-|>An%TkG6Y8Bcc${>8hRRpH4`G*s<(YHh63~uVKxkAF>v*+ng#6k(oLtqD9CqIL z_#3;S0|Mn94urBe1{}oflqevHTyj=?6IQlzMG^Z!R2_0Oj}@1)^Gy^KjY*#h1e&C8 zD&w`kdGj$>{AlCmT@~+FR%{!&bKRb&9y$AIN6$?kf7d;*EgZ?{=;%vc@{%*oIOFQ8 zuXgrcu6p$5H@_wQCw;SS-MWn%H?CQ;rmwH>*yd~Q+_~Atx8HvICqMbgAN=44Esl5Y zjPixgUAAi5@PFL9CAvZSWV-00_oV+^eDTG9_jiBy;Sc|9`cL|K`itAQZ-3;GN6O{$ zi6@?T{PD*R4GnpZH&@xZ{PN4+^Pcy#nDbX0KbYlmrGz%^t3ABE65Vp$m;azLT*Vs| zF|jHLxfULaksp&SKzJ8$_u2qjbq0uBw=+7u@(LTn>v z!~Mi`b^;C;m^Uq5fjN1@#-WjhOI1uo4( ze^oBR7SCgxlqbfvtb$T8I5S`*5Wtz7Ll+Ti#Zo22AMm#^!Za|VJO*TDFQCP0ydFfE z(UF^Va!wfbV|0wse}4ayj)PIA0RB-pE-H)Pvv(}+%o1T%r||U%)WN*M9tf$13^OER6W$<18LkxT{u^FpZsn5sx?Fp>=G@Mc2Ovebhtkv2wO|fZ-7Z z34gmC0R6#KMSiuN*su?+Hr=@sNHtQ{fVGD* z$~R3%z@(Y0PFCL_4Bn#qSzK`0qh%O{h({e!ZH)jlq+Exdm{@r|`?dBgjTS82!_v# zuPxJuh1;PZ|6-!-yurvTd-~RJq;emFc4v7l+)G#VVpkm?CghLUq|llylf8^>!p4C1 zQ>!=VYq@eRi>)$2D`YA<0LEaGCOiYIixI@KJz=9L-K3n*g1LSF{FleS^NJPWZ(V)+ zx(}Rv+*p;DFC2R3^OpVdt!s-$wQ=Ld4}S3C^q+x&fwRv(``%7H9OzIOF-xe||cWB}fSqEzkS_! zbj!=%b=(;z&mJGG5ozbQocW%Y8M5w?rHiYgqA{@+Ubi~{ZI*0%#RzQT+H&IqmzXfOG`&f`F3LPZw$G$)NaQ_jIdA7@_c}5?PXizV_(vWxp+0u&+44&*IqOpOhc5wHV0HLm8*L5e`)3uc_r0Ff8X>YD^dx+5$;0L?A{ zfx#Gg>%~gVVsxBYSm6C|`Tv=aj z(Eu}A=!#V-rm>4kwf<-4A9?Mscl_`-yCQMz&5ynRtm7)R`n;KaA9}^{U%O-d+Fhe9 zhBr1gc3tyzD98BRV-I`zlNTKruT`2|iG=8(^_y$V^%@;LCpmHPyxZ0u*t~aa!u|ZN z`8w!foU*8I`TSCv|77uPwZ^xtjYt1)JA3h8y#DC%kt(?>6p=mv13~=0ppoS;eeB=I z$QSi>&6}=-M1Yd=Mp8igfpR{A~;b0-L>#s-OMg%IrB zhPmvy6{yTWjNF(E#F>ae5|#&ha#Jz|3}($a14D7H$>hSlI(lMSnPF@mmLn#{0x_7D!<$lbkEw5Pr%@ zH!^D=noBgculj>eo;Gi&lmrjfv1t($1`KNDg!$0dqM2=+(^fb2~>v4@iY zrYQmMC+h5|Al`BhqnwCJdh)w5aZ)B1$$fJ5VhUW;7^5_gO3AvnVI&W?l&8=j)kT$l zr$7g$P*TtP;p`d7N<9`a;pF_E(IDD$C18kD0jCkbZoHzPajt2ePEtHl@a+K_(Q=|v zSPYH@%pXd6W)y3Jke&e{14A<))uhFINF({Y+fx;hqKlc$`ts-3J_={_6s6@t54KAnQ5w`IhKNP<}^&U1tDDdykEn4 z*T!bqHVH5WdD*2B)rPCh^&|TlehSws&8t`DDseT80J(()8f?MKeEe_VU-9cKBg4 z`=)Rh{Uvk85q&Q@W*|+5(i9{cMvLhG#G_{1@V`zQ+g}w8A7^PInV(&pWL)Y7u3yA1 za&RFUkulUwzs%x-Yt#;GnyUasbC}Top~D=279Y0H0f_D}>K1aJUD4a| zp#Md^2v}gf$^nS7flY+;KZye*jLeJ`1|*~V@ICngXb(9AH-m~Z`*qe%jY{b)Y7H9| za(3lTkc**{)Vi>ikKP6D0SPv<8q~p<#)wEH!mgM+zfE^W7$k1ge4&&J5k5pzu{d<~ z!JCC75R?gAg6ozpzUR;}#;`h2qDKf5u?W*Zz?tEnSf*GU7L;0OLQv3GkQ+8pk2n|Q zGZraO6a^az3P`^!ja?95zjaq5_ukp??T%e|2*mvpr>c z3^^G4jTc}hsCEE>G!!;0AkE1^f}+1WmDhXWXY3aSq_yN|4J6iw~=A?!FfBo{~|M2NY43v|x zYTd9riVlod9^0~`Tq;R`-dD~ZNNKJzTCJb3XvXu7F1_HG{v|`nAX1bG#DO-=T!ySO=)D;Dn2fg#6d6@q3sL?H^?k=9F9YHTzVizuZJ^w{((oCj^ z7218-0EJBKf!uQ)0JPgu#bsvV$qAFO| zk}J})v^G%DNar^$5X^z#@qja>2w05zBQSKlj~s4H4Yf?u?CH9*#c0Q zy(3^qe~@R4R>$PjA?s z)t{w4Z`BWT`vTcWGCK^oK{_aM-%&_v7$8gSTdV&wlv0U(twl%yAju)8EozV%A+8BR ztYx=ip&!J~ky`X&$p}N}kS`%dZnzaMg|Rz4TyrHlAQH2JtvLgX#)s8P{ftxRe(|E? z-*I__5WH%~$d$LOdDruoHDYvQ#+yc0FFtX>Stl)Azx%))kL_N!>%hLTTAPQkWOo1T zzS^>RSfT!jd)Wz0>VpgtE`&wl zZlon30D{7D?0Y43<4&MB(Uu`W415rFN;k$6-to{J5nip0Ov%xPa|75=sfI*}o+8O8 zV^|C{TVQ}{Y`K=RG)4@BT?7uiS~VW)-7CCQ<9k&Z64E))97H*lh3k=EVl2%1m)sfWse=G9s<#TWigBVqT1E`O8EM%z zbIGHR%KplqzV7J9wv2t^+v~#L-99{i)i2kkIY6bBpTtJE%=IO+2VVEI!_!Y{bzZl7 zbi=OEt^3E)452CD)A+r?zS2OM<`4BRo;7gHf*C{or8H>1bJeKj{Ydt=*Ui_B4Mi%@}9^e#%L*Y3JT`J9hPzN?AnR90(sam@J%`WP{r` zUf+43zHf}jsytSco=!TF!7|MmNahaG+`$~ZnSMPRiCdxNgw_-*4yDom@ORFvq`44* z9Y}sj*))$yvTtT+c#*Z|AfaqCv+q}EjOj8z&ZEp0!2^0C4TwrNhAQt}99TFc8Bf~- z^FlY=30X6fUa3sCh&VfZrozP&+5+?_1jw`$o+^TtwY3!0v(VBrwMZKGbo+z@Dg%)Y zxqy|}x%ired+ikGn3YcxPwJ*JrHjp=z@z2$T6nhw+OVOBC2f#b*`qT?Vt;t*Do9Zn zuo-aTF}`}Mu_LoloeQ^hMH&?Uom*DsYn*+HK|n|?2+@lK;{n*aG$oY$25_2PBB$x4 z><9;5oZGwr%Pgi4S|W;ERhSrs$=NVETj^QiNK6f|Gt}ZCJbP9H$|9FU=ZJjSRyaxm z69vC>V?1df21c?p3EX9>m@*>PLj=(p8_-&EnzcgcU{=?>Mc9yX1t!-@n5tq2$$nK2 zBp)=XJFpd7ve{Rx#RS?{RNXWF@&9-E=3V1gU%w@q16+CYir1aCc;VbR88kWlN*edq zt((izd4o@yKO@oMlm+diA$lW8NOOZENseAPf7Pb#Ns?zNX(*q@^t*-+oN&aV8U6ha zt>0X2Y$l3E4ZBL_*)tlF{130+vhToX7X8DyZEUv{CELK&V;+ zQ!ZO6Nk8u^(Y;%$Yxh)JNB=}*bp9#LD^Kp2;MC}LBU8}t^1QXXsMywa2VnZ=0RPni zc!G5RI%x^~|IZG^A4Wf9Y?Y zoW70b0J}!bSAVnhtmF19U3l31*+Zl$D~%Ap#-Zg11rP7}vxYVg>>aC0kXNa}HEi1LC|_7#|VL*l^{e_V9Eq8LVCMIC_YkAx2WWz$6%b^vD1ocVZ< zBy*(33U`setW0dn`~-{r9!~=!yey!bIb%kzoT>1v8IVjp;e-<&*I}f~W5I$2Mt6r= zdp7r62jJ9GPc_AK5ieDqtn4p0RB=dyed4UBT9r>fZdMPr0LZ^-Q}9WrGJy19Ie&JU z%LzVoI8yZ{uL$1pY}3Pa6!8eQ7Y*tNoj#BKM zLP%0`8w+i8>krh0n|}3Ls_6g+RyBMC4l#q;3_{sKPS<+!=ApQve0U%g5cjt%*D}_8 zT^J1zHsvEL03$|uS;w6w0%dtpB0MC%$HoF3NFToC`-%rJdmuK__X?wBev}d~8I;^7 zkA+JpwiH+evk|h0;jmPX=taD&RojETVmcT%Fp{ySFy&VlI18GGB1Cd88YfHwg4zZ_60L{Eu1@N(cD@6rKH-_>?aYE^^MBl=c(B@s2_Xyg8SBNDkT6c zAu2V&{Us|%hhI)Vd}QO+1QlIc)MW_EP9~a5;Gejm=0@XnbN#B(qC4Z^^ZGV^<2jYF zTB1{4eFg%9Y|W5@+s+Gzg{m~y?aKQ!Ta{2LS1*DTxtyVvO#TC)TA3Z5;rokPa_SOgZiBEjO z8+jxq;cq}hKwPDj@e$gZ2e zed987pbCXp){i3ueRR#Xf~3;Sj96r`$F-Jf;ytUhV65qytAj(eHYYtAY=n5F_uPF7 z^ei)te#uM#P5}`$xt@d3QwxElAXU)Sm;BBL7F?|&QqUhzbWM}Tl7PiGuJ%Cj=n#s& zU`|kW2BrdGDA1pt`?G zD2J;Gw4jrPm$yM1?eN60=kWo(PU>zh(1IQ4eUb)YmtK15qD70o_{A@FOtvNc(JfoH z?A^O}N^F?ifo$5e=`C-0%Wb#amIjwoKIV74)B%DRL=ht)l4;IR>F{}dbB4-~$9QKBBp-XzF|T^T z!pd+J)3~~!1Ohk_X(ZE?M~f{QGe~r5+>KFdM0JkqtXM4TEEI}m8mzvPh0=|uIlzaWeB9xKfAcSEC?*@cdUti*UUTNMfswJ%2iG6y zFC~lS&YnMeW*XV2ffO5sG$PV5v1v8|mMpk?)jC%d^JJ#IRE)Jrx6#^9>_y#PBh9nf z#`E29KcL&zj_(>Ryyc(w2lIdMsnaUEDt&hp&G1t6Ss%uDT!k?|Lz` z`|CB=T;rje!k>;#LQ&@=>X{H8Y?0|~0!p|=2N<=ajjFh$XX?r;uQV}OOY{_nabdX6 z5H1o<%isR(-+t_4A8WHN9`zu;U?d_x);tNG8mT<*n{btU4+=wsCy zAHA^u=F6ULYV4ujP&Y7~USCxZ>tn3GLgfDy_u!pguA57)TKTE(Z)j11({S<(Q<6PB z3C&_Nd5kx{aPfb8+2KLq$v>!Yz%Bk$-j~RX*V5=eA*G2fv(3(vm@uyEFmltn_2yBO z5g3R;sUBH!wkWI800<6k;y|LaZ;T*zj1un$>ZAj-7%bvSBAv;J9 zs3GKNof@zT*HmscwPI(iS}Se=k(g&FRT*sy64I+Z$|)>|^x(&LkALXxPg;J&;2SS} zxRjW%C{nGPTOJ!bYIf<=C4K2vHtpJz{?k_~EuK4j-mDpe%|cx*>(OVImAXxHDM^l8 zFmL0IJw%>^iPzXr0Wm=@O}7WJJ7oO+vsg#i5;AjtiM9>b@7NGm`p*veZ~kcM$Nyq^ zW#>3iM5i}E{!*+J=PhDk*O0UTaP9c}@9=Be_U+N&wMBpx8VQ8~N;LG324O{6M7W>x z&O7hoi!Z+P(o3TUGCVwt1=kQt-h##u{cAWCW+G*w))8Clx_-9J`t|FJHru^>_q=)Y zOdwF>O(PC9Vt*C{e9_d)8@AhXQEZAkNnaoR=tsZ%-S0l|zysk?tXsD(%qS+rrnk<@ zF!qgV_;@QbTz(U-2b|t)Z)0O)MQ19F{=-x=Tt(5uGMw1(Tolb-cp}_3Nu#04MY(Rl zv`>6PdT{*#)0a?q*9){L@h;ck0q?J3T?cW(?nr`|Qi8cq=-uCyrTZ~E$|$nt7>S9O z{?&>9{_7npH;vXgUWStbpYZ|?;|n{Gsa84tAHqI~7`Uarj*6f>RMKFy?GQwTV)YeI zR!kU_Ce1U|(GGx%vK{W6oKm{4mrbgvf*lpoyZ|XmlPj*10Vx+H7pi`q9m2-3g;}`j z_%W}<*`Do$yEV8I>5&sdn}xjaHvHC z!>|tzyr>=@ow0C*L?<)-_t|Hk9X*Q8n>TywBsx)eP*Hd1HY@ibA)#oAdhM^G73+TG z;fEiN9^f&@91|Xz7wCt-8y#(nj+1x!L@%r8>J3wiusN1}c+WleOnCQbgz~$`!&?Eh z@nqAI}7iH5O*;>oF33g5qIN(pMaSs59cOPAE?iKwx1Bq8hNC?Q zBzlyB65VqYG*xtt+FZsr-+Xg)NQ)LN!l*qQq}QTp=VEF9?6lrVi|Gn8fm?35rJY~j zuyefTX)L;DIFW)#J1~lcaX>W@S(ILV(>>hOt=KbUmwnpck@f8t{|Oqa{KWWg{KFj;Jv;|IYQ z)#WJ?Dpk6jvh0JrGPCc!%TC6U$k!ZV*hokFafuEM@G$`PcQ9b`mX)Uzug2^o zWSCJ5HPDUIc?^xdAG(3uL@##{l>l5lwzV!a=p1#0LO1c+|x$ z0H`pZX4F|H0IYB07#ACVnrx4ABczPkby9AJEYB~T69?u-6 zA@(k!?ruVvl~#-k@j1&QP*5tjoM?P8y$RN8JaV8qe4zHom!A3l*Bsdd&Bw>Kf1Gbz zHFocoN;3s$sDh?JeqX6XO}x#n{RdWV-ge)bjrXo;=^^I1_$DW zeTsj;901?s3K19+W{GSazP67v`V|jus}>dh)7Q&SpS$~CpObEUV7OYXaV;k|=VFSk zHRMtP)|S!+X5MQ`FpH?MoSpELbu|i{*%6B^jV2pID)oRg! zeCIpg2?r3ZTSpHhT9YoCme$8oRPt}p^l)!}{p(*B9pI0C^rNDFOY6dRQJ`Nm?4sFg zQ8EuFumunJy6dis4x>musA%D}ZZs90xS|$8(OC}PXV3X<+qOlI&K-E`>m|D9=u({U zqAOb4t*^({{j0Ij(M_8+wQ~cxYvnM(UD~NIG6u+O!kPzKK)I40T0_B~NOgTVcF$zl z58Dvi+c?|z;p~tI8q3zsunoeO)jA(!89J5A8GuOc>jl_OQftB#XuQb{!-Gu+Mp-Q2uh<4UzYK3*Rk zt)&tF{*l_g;p(3K)jj*FyVHO6R~}n8a>?tD`p>^RrCe%tBNv_70uNWR)Q9dCb$b{weP zuxj*o8!Iht?zOu4)LW1F≪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�&$}lM+@2hG$KbSx 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

18%Y+S!eA`^K%3^paG# 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�EQ7mu81k}ULwE$@=;BzyO39`~DC2NI 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 literal 0 HcmV?d00001 diff --git a/ObjectALDemo/Resources/Default.png b/ObjectALDemo/Resources/Default.png old mode 100755 new mode 100644 index 844a0d0d09a46528783449d3a51768401f99a327..36b6891fe98676a98b9d6e1cdb382f4ef78f10a3 GIT binary patch literal 37342 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

I7sr$oAjI{4sVc|j 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 XSxDaw3`#YC00000NkvXXu0mjfac^cr literal 0 HcmV?d00001 diff --git a/ObjectALDemo/Resources/Icon-Small@2x.png b/ObjectALDemo/Resources/Icon-Small@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cabc87d72ae9f27aaf3f59ca28a9ce545b633096 GIT binary patch literal 5054 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 UplK_YxBvhE07*qoM6N<$f=%<#V*mgE literal 0 HcmV?d00001 diff --git a/ObjectALDemo/Resources/Icon.png b/ObjectALDemo/Resources/Icon.png old mode 100755 new mode 100644 index f0103fa3c1442f27ab51a92fd2a7755da4eeca5d..a88fcfcff09af280bf420bf7efeb6b10044a139b GIT binary patch literal 4903 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 ZU;x9lmAv=_-4_4=002ovPDHLkV1fw7kput$ delta 3640 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 KMNUMnLSTZJ0tFKQ diff --git a/ObjectALDemo/Resources/Icon@2x.png b/ObjectALDemo/Resources/Icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bbfc0f9fb1cbc2da231a6f79b26e382cb1623570 GIT binary patch literal 13076 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 jiKEtN@Lyi^{{ - - -@interface RootViewController : UIViewController { - -} - -@end diff --git a/ObjectALDemo/RootViewController.m b/ObjectALDemo/RootViewController.m deleted file mode 100644 index aebc646..0000000 --- a/ObjectALDemo/RootViewController.m +++ /dev/null @@ -1,152 +0,0 @@ -// -// RootViewController.m -// ObjectAL -// -// Created by Karl Stenerud on 11/23/11. -// - -// -// RootViewController + iAd -// If you want to support iAd, use this class as the controller of your iAd -// - -#import "cocos2d.h" - -#import "RootViewController.h" -#import "GameConfig.h" - -@implementation RootViewController - -/* - // The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad. - - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) { - // Custom initialization - } - return self; - } - */ - -/* - // Implement loadView to create a view hierarchy programmatically, without using a nib. - - (void)loadView { - } - */ - -/* - // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - - (void)viewDidLoad { - [super viewDidLoad]; - } - */ - - -// Override to allow orientations other than the default portrait orientation. -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { - - // - // There are 2 ways to support auto-rotation: - // - The OpenGL / cocos2d way - // - Faster, but doesn't rotate the UIKit objects - // - The ViewController way - // - A bit slower, but the UiKit objects are placed in the right place - // - -#if GAME_AUTOROTATION==kGameAutorotationNone - // - // EAGLView won't be autorotated. - // Since this method should return YES in at least 1 orientation, - // we return YES only in the Portrait orientation - // - return ( interfaceOrientation == UIInterfaceOrientationPortrait ); - -#elif GAME_AUTOROTATION==kGameAutorotationCCDirector - // - // EAGLView will be rotated by cocos2d - // - // Sample: Autorotate only in landscape mode - // - if( interfaceOrientation == UIInterfaceOrientationLandscapeLeft ) { - [[CCDirector sharedDirector] setDeviceOrientation: kCCDeviceOrientationLandscapeRight]; - } else if( interfaceOrientation == UIInterfaceOrientationLandscapeRight) { - [[CCDirector sharedDirector] setDeviceOrientation: kCCDeviceOrientationLandscapeLeft]; - } - - // Since this method should return YES in at least 1 orientation, - // we return YES only in the Portrait orientation - return ( interfaceOrientation == UIInterfaceOrientationPortrait ); - -#elif GAME_AUTOROTATION == kGameAutorotationUIViewController - // - // EAGLView will be rotated by the UIViewController - // - // Sample: Autorotate only in landscpe mode - // - // return YES for the supported orientations - - return ( UIInterfaceOrientationIsLandscape( interfaceOrientation ) ); - -#else -#error Unknown value in GAME_AUTOROTATION - -#endif // GAME_AUTOROTATION - - - // Shold not happen - return NO; -} - -// -// This callback only will be called when GAME_AUTOROTATION == kGameAutorotationUIViewController -// -#if GAME_AUTOROTATION == kGameAutorotationUIViewController --(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration -{ - // - // Assuming that the main window has the size of the screen - // BUG: This won't work if the EAGLView is not fullscreen - /// - CGRect screenRect = [[UIScreen mainScreen] bounds]; - CGRect rect = CGRectZero; - - - if(toInterfaceOrientation == UIInterfaceOrientationPortrait || toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) - rect = screenRect; - - else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) - rect.size = CGSizeMake( screenRect.size.height, screenRect.size.width ); - - CCDirector *director = [CCDirector sharedDirector]; - EAGLView *glView = [director openGLView]; - float contentScaleFactor = [director contentScaleFactor]; - - if( contentScaleFactor != 1 ) { - rect.size.width *= contentScaleFactor; - rect.size.height *= contentScaleFactor; - } - glView.frame = rect; -} -#endif // GAME_AUTOROTATION == kGameAutorotationUIViewController - - -- (void)didReceiveMemoryWarning { - // Releases the view if it doesn't have a superview. - [super didReceiveMemoryWarning]; - - // Release any cached data, images, etc that aren't in use. -} - -- (void)viewDidUnload { - [super viewDidUnload]; - // Release any retained subviews of the main view. - // e.g. self.myOutlet = nil; -} - - -- (void)dealloc { - [super dealloc]; -} - - -@end - diff --git a/ObjectALDemo/Support/Button.m b/ObjectALDemo/Support/Button.m index 58fc92d..b9e2604 100644 --- a/ObjectALDemo/Support/Button.m +++ b/ObjectALDemo/Support/Button.m @@ -33,7 +33,7 @@ - (id) initWithTouchablePortion:(CCNode*) node target:(id) targetIn selector:(SE target = targetIn; selector = selectorIn; - self.isRelativeAnchorPoint = YES; + self.ignoreAnchorPointForPosition = NO; self.anchorPoint = ccp(0.5f, 0.5f); } return self; diff --git a/ObjectALDemo/Support/Slider.m b/ObjectALDemo/Support/Slider.m index 1ae0222..c8210fe 100644 --- a/ObjectALDemo/Support/Slider.m +++ b/ObjectALDemo/Support/Slider.m @@ -63,7 +63,7 @@ - (id) initWithTrack:(CCNode*) trackIn targetedTouches = YES; swallowTouches = YES; isTouchEnabled = YES; - self.isRelativeAnchorPoint = YES; + self.ignoreAnchorPointForPosition = NO; self.anchorPoint = ccp(0.5f, 0.5f); } return self; diff --git a/ObjectALDemo/Support/TouchableNode.m b/ObjectALDemo/Support/TouchableNode.m index b988c24..0cd02d9 100644 --- a/ObjectALDemo/Support/TouchableNode.m +++ b/ObjectALDemo/Support/TouchableNode.m @@ -32,11 +32,11 @@ -(void) registerWithTouchDispatcher if(targetedTouches) { - [[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:touchPriority swallowsTouches:swallowTouches]; + [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:touchPriority swallowsTouches:swallowTouches]; } else { - [[CCTouchDispatcher sharedDispatcher] addStandardDelegate:self priority:touchPriority]; + [[[CCDirector sharedDirector] touchDispatcher] addStandardDelegate:self priority:touchPriority]; } registeredWithDispatcher = YES; } @@ -45,7 +45,7 @@ - (void) unregisterWithTouchDispatcher { if(registeredWithDispatcher) { - [[CCTouchDispatcher sharedDispatcher] removeDelegate:self]; + [[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self]; registeredWithDispatcher = NO; } } diff --git a/ObjectALDemo/main.m b/ObjectALDemo/main.m index 579a806..7b0f3d9 100644 --- a/ObjectALDemo/main.m +++ b/ObjectALDemo/main.m @@ -1,8 +1,9 @@ // // main.m -// ObjectAL +// ObjectALDemo // -// Created by Karl Stenerud on 10-05-29. +// Created by Monkey on 7/09/12. +// Copyright __MyCompanyName__ 2012. All rights reserved. // #import @@ -10,7 +11,7 @@ int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate"); + int retVal = UIApplicationMain(argc, argv, nil, @"AppController"); [pool release]; return retVal; } diff --git a/cocos2d/LICENSE_Kazmath.txt b/cocos2d/LICENSE_Kazmath.txt new file mode 100755 index 0000000..8cc83bc --- /dev/null +++ b/cocos2d/LICENSE_Kazmath.txt @@ -0,0 +1,33 @@ +Kazmath is a 3D math library aimed at game programming. It is released under the modified BSD license. + +Authors + +Luke Benstead +Carsten Haubold + +License + +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + + diff --git a/cocos2d/kazmath/include/kazmath/GL/mat4stack.h b/cocos2d/kazmath/include/kazmath/GL/mat4stack.h new file mode 100755 index 0000000..7de12aa --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/GL/mat4stack.h @@ -0,0 +1,51 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef C_STACK_H_INCLUDED +#define C_STACK_H_INCLUDED + +#include "../mat4.h" + +typedef struct km_mat4_stack { + int capacity; //The total item capacity + int item_count; //The number of items + kmMat4* top; + kmMat4* stack; +} km_mat4_stack; + +#ifdef __cplusplus +extern "C" { +#endif + +void km_mat4_stack_initialize(km_mat4_stack* stack); +void km_mat4_stack_push(km_mat4_stack* stack, const kmMat4* item); +void km_mat4_stack_pop(km_mat4_stack* stack, kmMat4* pOut); +void km_mat4_stack_release(km_mat4_stack* stack); + +#ifdef __cplusplus +} +#endif + +#endif // C_STACK_H_INCLUDED diff --git a/cocos2d/kazmath/include/kazmath/GL/matrix.h b/cocos2d/kazmath/include/kazmath/GL/matrix.h new file mode 100755 index 0000000..1fa0267 --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/GL/matrix.h @@ -0,0 +1,58 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef KM_GL_MATRIX_H_INCLUDED +#define KM_GL_MATRIX_H_INCLUDED + +#define KM_GL_MODELVIEW 0x1700 +#define KM_GL_PROJECTION 0x1701 +#define KM_GL_TEXTURE 0x1702 + +typedef unsigned int kmGLEnum; + +#include "../mat4.h" +#include "../vec3.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void kmGLFreeAll(void); +void kmGLPushMatrix(void); +void kmGLPopMatrix(void); +void kmGLMatrixMode(kmGLEnum mode); +void kmGLLoadIdentity(void); +void kmGLLoadMatrix(const kmMat4* pIn); +void kmGLMultMatrix(const kmMat4* pIn); +void kmGLTranslatef(float x, float y, float z); +void kmGLRotatef(float angle, float x, float y, float z); +void kmGLScalef(float x, float y, float z); +void kmGLGetMatrix(kmGLEnum mode, kmMat4* pOut); + +#ifdef __cplusplus +} +#endif + +#endif // MATRIX_H_INCLUDED diff --git a/cocos2d/kazmath/include/kazmath/aabb.h b/cocos2d/kazmath/include/kazmath/aabb.h new file mode 100755 index 0000000..0e78a04 --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/aabb.h @@ -0,0 +1,53 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef KAZMATH_AABB_H_INCLUDED +#define KAZMATH_AABB_H_INCLUDED + +#include "vec3.h" +#include "utility.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A struture that represents an axis-aligned + * bounding box. + */ +typedef struct kmAABB { + kmVec3 min; /** The max corner of the box */ + kmVec3 max; /** The min corner of the box */ +} kmAABB; + +const int kmAABBContainsPoint(const kmVec3* pPoint, const kmAABB* pBox); +kmAABB* const kmAABBAssign(kmAABB* pOut, const kmAABB* pIn); +kmAABB* const kmAABBScale(kmAABB* pOut, const kmAABB* pIn, kmScalar s); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cocos2d/kazmath/include/kazmath/kazmath.h b/cocos2d/kazmath/include/kazmath/kazmath.h new file mode 100755 index 0000000..ef09870 --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/kazmath.h @@ -0,0 +1,39 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef KAZMATH_H_INCLUDED +#define KAZMATH_H_INCLUDED + +#include "vec2.h" +#include "vec3.h" +#include "mat3.h" +#include "mat4.h" +#include "utility.h" +#include "quaternion.h" +#include "plane.h" +#include "aabb.h" +#include "ray2.h" + +#endif // KAZMATH_H_INCLUDED diff --git a/cocos2d/kazmath/include/kazmath/mat3.h b/cocos2d/kazmath/include/kazmath/mat3.h new file mode 100755 index 0000000..f0de423 --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/mat3.h @@ -0,0 +1,75 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + + +#ifndef MAT3_H_INCLUDED +#define MAT3_H_INCLUDED + +#include "utility.h" + +struct kmVec3; +struct kmQuaternion; + +typedef struct kmMat3{ + kmScalar mat[9]; +} kmMat3; + +#ifdef __cplusplus +extern "C" { +#endif + +kmMat3* const kmMat3Fill(kmMat3* pOut, const kmScalar* pMat); +kmMat3* const kmMat3Adjugate(kmMat3* pOut, const kmMat3* pIn); +kmMat3* const kmMat3Identity(kmMat3* pOut); +kmMat3* const kmMat3Inverse(kmMat3* pOut, const kmScalar pDeterminate, const kmMat3* pM); +const int kmMat3IsIdentity(const kmMat3* pIn); +kmMat3* const kmMat3Transpose(kmMat3* pOut, const kmMat3* pIn); +const kmScalar kmMat3Determinant(const kmMat3* pIn); +kmMat3* const kmMat3Multiply(kmMat3* pOut, const kmMat3* pM1, const kmMat3* pM2); +kmMat3* const kmMat3ScalarMultiply(kmMat3* pOut, const kmMat3* pM, const kmScalar pFactor); + +kmMat3* const kmMat3RotationAxisAngle(kmMat3* pOut, const struct kmVec3* axis, kmScalar radians); +struct kmVec3* const kmMat3RotationToAxisAngle(struct kmVec3* pAxis, kmScalar* radians, const kmMat3* pIn); + +kmMat3* const kmMat3Assign(kmMat3* pOut, const kmMat3* pIn); +const int kmMat3AreEqual(const kmMat3* pM1, const kmMat3* pM2); + +kmMat3* const kmMat3RotationX(kmMat3* pOut, const kmScalar radians); +kmMat3* const kmMat3RotationY(kmMat3* pOut, const kmScalar radians); +kmMat3* const kmMat3RotationZ(kmMat3* pOut, const kmScalar radians); + +kmMat3* const kmMat3Rotation(kmMat3* pOut, const kmScalar radians); +kmMat3* const kmMat3Scaling(kmMat3* pOut, const kmScalar x, const kmScalar y); +kmMat3* const kmMat3Translation(kmMat3* pOut, const kmScalar x, const kmScalar y); + +kmMat3* const kmMat3RotationQuaternion(kmMat3* pOut, const struct kmQuaternion* pIn); +kmMat3* const kmMat3RotationAxisAngle(kmMat3* pOut, const struct kmVec3* axis, kmScalar radians); +struct kmVec3* const kmMat3RotationToAxisAngle(struct kmVec3* pAxis, kmScalar* radians, const kmMat3* pIn); + +#ifdef __cplusplus +} +#endif +#endif // MAT3_H_INCLUDED + diff --git a/cocos2d/kazmath/include/kazmath/mat4.h b/cocos2d/kazmath/include/kazmath/mat4.h new file mode 100755 index 0000000..38008ba --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/mat4.h @@ -0,0 +1,93 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef MAT4_H_INCLUDED +#define MAT4_H_INCLUDED + +#include "utility.h" + +struct kmVec3; +struct kmMat3; +struct kmQuaternion; +struct kmPlane; + +/* +A 4x4 matrix + + | 0 4 8 12 | +mat = | 1 5 9 13 | + | 2 6 10 14 | + | 3 7 11 15 | +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kmMat4 { + kmScalar mat[16]; +} kmMat4; + +kmMat4* const kmMat4Fill(kmMat4* pOut, const kmScalar* pMat); + + +kmMat4* const kmMat4Identity(kmMat4* pOut); + +kmMat4* const kmMat4Inverse(kmMat4* pOut, const kmMat4* pM); + + +const int kmMat4IsIdentity(const kmMat4* pIn); + +kmMat4* const kmMat4Transpose(kmMat4* pOut, const kmMat4* pIn); +kmMat4* const kmMat4Multiply(kmMat4* pOut, const kmMat4* pM1, const kmMat4* pM2); + +kmMat4* const kmMat4Assign(kmMat4* pOut, const kmMat4* pIn); +const int kmMat4AreEqual(const kmMat4* pM1, const kmMat4* pM2); + +kmMat4* const kmMat4RotationX(kmMat4* pOut, const kmScalar radians); +kmMat4* const kmMat4RotationY(kmMat4* pOut, const kmScalar radians); +kmMat4* const kmMat4RotationZ(kmMat4* pOut, const kmScalar radians); +kmMat4* const kmMat4RotationPitchYawRoll(kmMat4* pOut, const kmScalar pitch, const kmScalar yaw, const kmScalar roll); +kmMat4* const kmMat4RotationQuaternion(kmMat4* pOut, const struct kmQuaternion* pQ); +kmMat4* const kmMat4RotationTranslation(kmMat4* pOut, const struct kmMat3* rotation, const struct kmVec3* translation); +kmMat4* const kmMat4Scaling(kmMat4* pOut, const kmScalar x, const kmScalar y, const kmScalar z); +kmMat4* const kmMat4Translation(kmMat4* pOut, const kmScalar x, const kmScalar y, const kmScalar z); + +struct kmVec3* const kmMat4GetUpVec3(struct kmVec3* pOut, const kmMat4* pIn); +struct kmVec3* const kmMat4GetRightVec3(struct kmVec3* pOut, const kmMat4* pIn); +struct kmVec3* const kmMat4GetForwardVec3(struct kmVec3* pOut, const kmMat4* pIn); + +kmMat4* const kmMat4PerspectiveProjection(kmMat4* pOut, kmScalar fovY, kmScalar aspect, kmScalar zNear, kmScalar zFar); +kmMat4* const kmMat4OrthographicProjection(kmMat4* pOut, kmScalar left, kmScalar right, kmScalar bottom, kmScalar top, kmScalar nearVal, kmScalar farVal); +kmMat4* const kmMat4LookAt(kmMat4* pOut, const struct kmVec3* pEye, const struct kmVec3* pCenter, const struct kmVec3* pUp); + +kmMat4* const kmMat4RotationAxisAngle(kmMat4* pOut, const struct kmVec3* axis, kmScalar radians); +struct kmMat3* const kmMat4ExtractRotation(struct kmMat3* pOut, const kmMat4* pIn); +struct kmPlane* const kmMat4ExtractPlane(struct kmPlane* pOut, const kmMat4* pIn, const kmEnum plane); +struct kmVec3* const kmMat4RotationToAxisAngle(struct kmVec3* pAxis, kmScalar* radians, const kmMat4* pIn); +#ifdef __cplusplus +} +#endif +#endif /* MAT4_H_INCLUDED */ diff --git a/cocos2d/kazmath/include/kazmath/neon_matrix_impl.h b/cocos2d/kazmath/include/kazmath/neon_matrix_impl.h new file mode 100755 index 0000000..3fcf3e4 --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/neon_matrix_impl.h @@ -0,0 +1,41 @@ +/* + NEON math library for the iPhone / iPod touch + + Copyright (c) 2009 Justin Saunders + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising + from the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product documentation + would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef __NEON_MATRIX_IMPL_H__ +#define __NEON_MATRIX_IMPL_H__ + +#ifdef __arm__ +#include "arm/arch.h" +#endif + +// Matrixes are assumed to be stored in column major format according to OpenGL +// specification. + +// Multiplies two 4x4 matrices (a,b) outputing a 4x4 matrix (output) +void NEON_Matrix4Mul(const float* a, const float* b, float* output ); + +// Multiplies a 4x4 matrix (m) with a vector 4 (v), outputing a vector 4 +void NEON_Matrix4Vector4Mul(const float* m, const float* v, float* output); + + +#endif // __NEON_MATRIX_IMPL_H__ diff --git a/cocos2d/kazmath/include/kazmath/plane.h b/cocos2d/kazmath/include/kazmath/plane.h new file mode 100755 index 0000000..4270a8a --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/plane.h @@ -0,0 +1,70 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef PLANE_H_INCLUDED +#define PLANE_H_INCLUDED + +#define KM_PLANE_LEFT 0 +#define KM_PLANE_RIGHT 1 +#define KM_PLANE_BOTTOM 2 +#define KM_PLANE_TOP 3 +#define KM_PLANE_NEAR 4 +#define KM_PLANE_FAR 5 + +#include "utility.h" + +struct kmVec3; +struct kmVec4; +struct kmMat4; + +typedef struct kmPlane { + kmScalar a, b, c, d; +} kmPlane; + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum POINT_CLASSIFICATION { + POINT_INFRONT_OF_PLANE = 0, + POINT_BEHIND_PLANE, + POINT_ON_PLANE, +} POINT_CLASSIFICATION; + +const kmScalar kmPlaneDot(const kmPlane* pP, const struct kmVec4* pV); +const kmScalar kmPlaneDotCoord(const kmPlane* pP, const struct kmVec3* pV); +const kmScalar kmPlaneDotNormal(const kmPlane* pP, const struct kmVec3* pV); +kmPlane* const kmPlaneFromPointNormal(kmPlane* pOut, const struct kmVec3* pPoint, const struct kmVec3* pNormal); +kmPlane* const kmPlaneFromPoints(kmPlane* pOut, const struct kmVec3* p1, const struct kmVec3* p2, const struct kmVec3* p3); +kmVec3* const kmPlaneIntersectLine(struct kmVec3* pOut, const kmPlane* pP, const struct kmVec3* pV1, const struct kmVec3* pV2); +kmPlane* const kmPlaneNormalize(kmPlane* pOut, const kmPlane* pP); +kmPlane* const kmPlaneScale(kmPlane* pOut, const kmPlane* pP, kmScalar s); +const POINT_CLASSIFICATION kmPlaneClassifyPoint(const kmPlane* pIn, const kmVec3* pP); /** Classifys a point against a plane */ + +#ifdef __cplusplus +} +#endif + +#endif // PLANE_H_INCLUDED diff --git a/cocos2d/kazmath/include/kazmath/quaternion.h b/cocos2d/kazmath/include/kazmath/quaternion.h new file mode 100755 index 0000000..81ff547 --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/quaternion.h @@ -0,0 +1,113 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef QUATERNION_H_INCLUDED +#define QUATERNION_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include "utility.h" + +struct kmMat4; +struct kmMat3; +struct kmVec3; + +typedef struct kmQuaternion { + kmScalar x; + kmScalar y; + kmScalar z; + kmScalar w; +} kmQuaternion; + +kmQuaternion* const kmQuaternionConjugate(kmQuaternion* pOut, const kmQuaternion* pIn); ///< Returns pOut, sets pOut to the conjugate of pIn + +const kmScalar kmQuaternionDot(const kmQuaternion* q1, const kmQuaternion* q2); ///< Returns the dot product of the 2 quaternions + +kmQuaternion* kmQuaternionExp(kmQuaternion* pOut, const kmQuaternion* pIn); ///< Returns the exponential of the quaternion + +///< Makes the passed quaternion an identity quaternion + +kmQuaternion* kmQuaternionIdentity(kmQuaternion* pOut); + +///< Returns the inverse of the passed Quaternion + +kmQuaternion* kmQuaternionInverse(kmQuaternion* pOut, + const kmQuaternion* pIn); + +///< Returns true if the quaternion is an identity quaternion + +int kmQuaternionIsIdentity(const kmQuaternion* pIn); + +///< Returns the length of the quaternion + +kmScalar kmQuaternionLength(const kmQuaternion* pIn); + +///< Returns the length of the quaternion squared (prevents a sqrt) + +kmScalar kmQuaternionLengthSq(const kmQuaternion* pIn); + +///< Returns the natural logarithm + +kmQuaternion* kmQuaternionLn(kmQuaternion* pOut, const kmQuaternion* pIn); + +///< Multiplies 2 quaternions together + +kmQuaternion* kmQuaternionMultiply(kmQuaternion* pOut, const kmQuaternion* q1, const kmQuaternion* q2); + +///< Normalizes a quaternion + +kmQuaternion* kmQuaternionNormalize(kmQuaternion* pOut, const kmQuaternion* pIn); + +///< Rotates a quaternion around an axis + +kmQuaternion* kmQuaternionRotationAxis(kmQuaternion* pOut, const struct kmVec3* pV, kmScalar angle); + +///< Creates a quaternion from a rotation matrix + +kmQuaternion* kmQuaternionRotationMatrix(kmQuaternion* pOut, const struct kmMat3* pIn); + +///< Create a quaternion from yaw, pitch and roll + +kmQuaternion* kmQuaternionRotationYawPitchRoll(kmQuaternion* pOut, kmScalar yaw, kmScalar pitch, kmScalar roll); +///< Interpolate between 2 quaternions +kmQuaternion* kmQuaternionSlerp(kmQuaternion* pOut, const kmQuaternion* q1, const kmQuaternion* q2, kmScalar t); + +///< Get the axis and angle of rotation from a quaternion +void kmQuaternionToAxisAngle(const kmQuaternion* pIn, struct kmVec3* pVector, kmScalar* pAngle); + +///< Scale a quaternion +kmQuaternion* kmQuaternionScale(kmQuaternion* pOut, const kmQuaternion* pIn, kmScalar s); +kmQuaternion* kmQuaternionAssign(kmQuaternion* pOut, const kmQuaternion* pIn); +kmQuaternion* kmQuaternionAdd(kmQuaternion* pOut, const kmQuaternion* pQ1, const kmQuaternion* pQ2); +kmQuaternion* kmQuaternionRotationBetweenVec3(kmQuaternion* pOut, const struct kmVec3* vec1, const struct kmVec3* vec2, const struct kmVec3* fallback); +struct kmVec3* kmQuaternionMultiplyVec3(struct kmVec3* pOut, const kmQuaternion* q, const struct kmVec3* v); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cocos2d/kazmath/include/kazmath/ray2.h b/cocos2d/kazmath/include/kazmath/ray2.h new file mode 100755 index 0000000..609e6e5 --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/ray2.h @@ -0,0 +1,50 @@ +/* +Copyright (c) 2011, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef RAY_2_H +#define RAY_2_H + +#include "utility.h" +#include "vec2.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kmRay2 { + kmVec2 start; + kmVec2 dir; +} kmRay2; + +void kmRay2Fill(kmRay2* ray, kmScalar px, kmScalar py, kmScalar vx, kmScalar vy); +kmBool kmRay2IntersectLineSegment(const kmRay2* ray, const kmVec2* p1, const kmVec2* p2, kmVec2* intersection); +kmBool kmRay2IntersectTriangle(const kmRay2* ray, const kmVec2* p1, const kmVec2* p2, const kmVec2* p3, kmVec2* intersection, kmVec2* normal_out); +kmBool kmRay2IntersectCircle(const kmRay2* ray, const kmVec2 centre, const kmScalar radius, kmVec2* intersection); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cocos2d/kazmath/include/kazmath/utility.h b/cocos2d/kazmath/include/kazmath/utility.h new file mode 100755 index 0000000..14d66dc --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/utility.h @@ -0,0 +1,74 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef UTILITY_H_INCLUDED +#define UTILITY_H_INCLUDED + +#include + +#ifndef kmScalar +#define kmScalar float +#endif + +#ifndef kmBool +#define kmBool unsigned char +#endif + +#ifndef kmEnum +#define kmEnum unsigned int +#endif + +#ifndef KM_FALSE +#define KM_FALSE 0 +#endif + +#ifndef KM_TRUE +#define KM_TRUE 1 +#endif + +#define kmPI 3.141592f +#define kmPIOver180 0.017453f // PI / 180 +#define kmPIUnder180 57.295779f // 180 / PI +#define kmEpsilon 1.0 / 64.0 + + + +#ifdef __cplusplus +extern "C" { +#endif + +extern kmScalar kmSQR(kmScalar s); +extern kmScalar kmDegreesToRadians(kmScalar degrees); +extern kmScalar kmRadiansToDegrees(kmScalar radians); + +extern kmScalar min(kmScalar lhs, kmScalar rhs); +extern kmScalar max(kmScalar lhs, kmScalar rhs); +extern kmBool kmAlmostEqual(kmScalar lhs, kmScalar rhs); + +#ifdef __cplusplus +} +#endif + +#endif /* UTILITY_H_INCLUDED */ diff --git a/cocos2d/kazmath/include/kazmath/vec2.h b/cocos2d/kazmath/include/kazmath/vec2.h new file mode 100755 index 0000000..3ca56b3 --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/vec2.h @@ -0,0 +1,64 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef VEC2_H_INCLUDED +#define VEC2_H_INCLUDED + +struct kmMat3; + +#ifndef kmScalar +#define kmScalar float +#endif + +#pragma pack(push) /* push current alignment to stack */ +#pragma pack(1) /* set alignment to 1 byte boundary */ +typedef struct kmVec2 { + kmScalar x; + kmScalar y; +} kmVec2; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif +kmVec2* kmVec2Fill(kmVec2* pOut, kmScalar x, kmScalar y); +kmScalar kmVec2Length(const kmVec2* pIn); ///< Returns the length of the vector +kmScalar kmVec2LengthSq(const kmVec2* pIn); ///< Returns the square of the length of the vector +kmVec2* kmVec2Normalize(kmVec2* pOut, const kmVec2* pIn); ///< Returns the vector passed in set to unit length +kmVec2* kmVec2Add(kmVec2* pOut, const kmVec2* pV1, const kmVec2* pV2); ///< Adds 2 vectors and returns the result +kmScalar kmVec2Dot(const kmVec2* pV1, const kmVec2* pV2); /** Returns the Dot product which is the cosine of the angle between the two vectors multiplied by their lengths */ +kmVec2* kmVec2Subtract(kmVec2* pOut, const kmVec2* pV1, const kmVec2* pV2); ///< Subtracts 2 vectors and returns the result +kmVec2* kmVec2Transform(kmVec2* pOut, const kmVec2* pV1, const struct kmMat3* pM); /** Transform the Vector */ +kmVec2* kmVec2TransformCoord(kmVec2* pOut, const kmVec2* pV, const struct kmMat3* pM); /// + +#ifndef kmScalar +#define kmScalar float +#endif + +struct kmMat4; + +typedef struct kmVec3 { + kmScalar x; + kmScalar y; + kmScalar z; +} kmVec3; + +#ifdef __cplusplus +extern "C" { +#endif + +kmVec3* kmVec3Fill(kmVec3* pOut, kmScalar x, kmScalar y, kmScalar z); +kmScalar kmVec3Length(const kmVec3* pIn); /** Returns the length of the vector */ +kmScalar kmVec3LengthSq(const kmVec3* pIn); /** Returns the square of the length of the vector */ +kmVec3* kmVec3Normalize(kmVec3* pOut, const kmVec3* pIn); /** Returns the vector passed in set to unit length */ +kmVec3* kmVec3Cross(kmVec3* pOut, const kmVec3* pV1, const kmVec3* pV2); /** Returns a vector perpendicular to 2 other vectors */ +kmScalar kmVec3Dot(const kmVec3* pV1, const kmVec3* pV2); /** Returns the cosine of the angle between 2 vectors */ +kmVec3* kmVec3Add(kmVec3* pOut, const kmVec3* pV1, const kmVec3* pV2); /** Adds 2 vectors and returns the result */ +kmVec3* kmVec3Subtract(kmVec3* pOut, const kmVec3* pV1, const kmVec3* pV2); /** Subtracts 2 vectors and returns the result */ +kmVec3* kmVec3Transform(kmVec3* pOut, const kmVec3* pV1, const struct kmMat4* pM); /** Transforms a vector (assuming w=1) by a given matrix */ +kmVec3* kmVec3TransformNormal(kmVec3* pOut, const kmVec3* pV, const struct kmMat4* pM);/**Transforms a 3D normal by a given matrix */ +kmVec3* kmVec3TransformCoord(kmVec3* pOut, const kmVec3* pV, const struct kmMat4* pM); /**Transforms a 3D vector by a given matrix, projecting the result back into w = 1. */ +kmVec3* kmVec3Scale(kmVec3* pOut, const kmVec3* pIn, const kmScalar s); /** Scales a vector to length s */ +int kmVec3AreEqual(const kmVec3* p1, const kmVec3* p2); +kmVec3* kmVec3InverseTransform(kmVec3* pOut, const kmVec3* pV, const struct kmMat4* pM); +kmVec3* kmVec3InverseTransformNormal(kmVec3* pOut, const kmVec3* pVect, const struct kmMat4* pM); +kmVec3* kmVec3Assign(kmVec3* pOut, const kmVec3* pIn); +kmVec3* kmVec3Zero(kmVec3* pOut); + +#ifdef __cplusplus +} +#endif +#endif /* VEC3_H_INCLUDED */ diff --git a/cocos2d/kazmath/include/kazmath/vec4.h b/cocos2d/kazmath/include/kazmath/vec4.h new file mode 100755 index 0000000..7507ea3 --- /dev/null +++ b/cocos2d/kazmath/include/kazmath/vec4.h @@ -0,0 +1,68 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#ifndef VEC4_H_INCLUDED +#define VEC4_H_INCLUDED + +#include "utility.h" + +struct kmMat4; + +#pragma pack(push) /* push current alignment to stack */ +#pragma pack(1) /* set alignment to 1 byte boundary */ + +typedef struct kmVec4 { + kmScalar x; + kmScalar y; + kmScalar z; + kmScalar w; +} kmVec4; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +kmVec4* kmVec4Fill(kmVec4* pOut, kmScalar x, kmScalar y, kmScalar z, kmScalar w); +kmVec4* kmVec4Add(kmVec4* pOut, const kmVec4* pV1, const kmVec4* pV2); +kmScalar kmVec4Dot(const kmVec4* pV1, const kmVec4* pV2); +kmScalar kmVec4Length(const kmVec4* pIn); +kmScalar kmVec4LengthSq(const kmVec4* pIn); +kmVec4* kmVec4Lerp(kmVec4* pOut, const kmVec4* pV1, const kmVec4* pV2, kmScalar t); +kmVec4* kmVec4Normalize(kmVec4* pOut, const kmVec4* pIn); +kmVec4* kmVec4Scale(kmVec4* pOut, const kmVec4* pIn, const kmScalar s); ///< Scales a vector to length s +kmVec4* kmVec4Subtract(kmVec4* pOut, const kmVec4* pV1, const kmVec4* pV2); +kmVec4* kmVec4Transform(kmVec4* pOut, const kmVec4* pV, const struct kmMat4* pM); +kmVec4* kmVec4TransformArray(kmVec4* pOut, unsigned int outStride, + const kmVec4* pV, unsigned int vStride, const struct kmMat4* pM, unsigned int count); +int kmVec4AreEqual(const kmVec4* p1, const kmVec4* p2); +kmVec4* kmVec4Assign(kmVec4* pOut, const kmVec4* pIn); + +#ifdef __cplusplus +} +#endif + +#endif // VEC4_H_INCLUDED diff --git a/cocos2d/kazmath/src/CMakeLists.txt b/cocos2d/kazmath/src/CMakeLists.txt new file mode 100755 index 0000000..a389466 --- /dev/null +++ b/cocos2d/kazmath/src/CMakeLists.txt @@ -0,0 +1,14 @@ + +#ADD_LIBRARY(Kazmath STATIC ${KAZMATH_SRCS}) +#INSTALL(TARGETS Kazmath ARCHIVE DESTINATION lib) + +INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include ) + +ADD_LIBRARY(kazmath STATIC ${KAZMATH_SOURCES}) +INSTALL(TARGETS kazmath ARCHIVE DESTINATION lib) + +#ADD_LIBRARY(KazmathGL STATIC ${GL_UTILS_SRCS}) +#INSTALL(TARGETS KazmathGL ARCHIVE DESTINATION lib) + +INSTALL(FILES ${KAZMATH_HEADERS} DESTINATION include/kazmath) +INSTALL(FILES ${GL_UTILS_HEADERS} DESTINATION include/kazmath/GL) diff --git a/cocos2d/kazmath/src/ChangeLog b/cocos2d/kazmath/src/ChangeLog new file mode 100755 index 0000000..f13b823 --- /dev/null +++ b/cocos2d/kazmath/src/ChangeLog @@ -0,0 +1,738 @@ +------------------------------------------------------------ +revno: 111 +committer: Kazade +branch nick: kazmath +timestamp: Thu 2010-08-19 12:07:29 +0100 +message: + Fix #620352. Fix a reference to kmMat4RotationAxisAngle +------------------------------------------------------------ +revno: 110 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Wed 2010-04-21 12:55:39 +0200 +message: + applied the change to the header files as well +------------------------------------------------------------ +revno: 109 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Wed 2010-04-21 12:54:06 +0200 +message: + fixed kmMat4RotationAxis +------------------------------------------------------------ +revno: 108 [merge] +committer: Carsten Haubld +branch nick: kazmath +timestamp: Wed 2010-04-21 12:27:53 +0200 +message: + fixed CMake in kazmathxx due to missing utility.h +------------------------------------------------------------ +revno: 107 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Wed 2010-04-21 12:22:40 +0200 +message: + fixed mat4 rotation axis by normalizing the axis first +------------------------------------------------------------ +revno: 106 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sat 2010-01-09 16:56:04 +0000 +message: + Add cmake module +------------------------------------------------------------ +revno: 105 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sat 2010-01-09 16:23:31 +0000 +message: + Remove kazmodel - it really should belong in its own repo +------------------------------------------------------------ +revno: 104 +committer: Luke Benstead +branch nick: kazmath +timestamp: Fri 2010-01-08 23:03:13 +0000 +message: + Reorganize the headers so that the tests can compile in place +------------------------------------------------------------ +revno: 103 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Mon 2009-08-31 11:21:42 +0200 +message: + Operators now inline, constructors fixed +------------------------------------------------------------ +revno: 102 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Sat 2009-08-29 11:42:59 +0200 +message: + fixed some compilation errors - still how do we define operators in headers correctly?? +------------------------------------------------------------ +revno: 101 +committer: Luke Benstead +branch nick: kazmath +timestamp: Wed 2009-08-26 10:37:52 +0100 +message: + Added the header defines +------------------------------------------------------------ +revno: 100 +committer: Luke Benstead +branch nick: kazmath +timestamp: Wed 2009-08-26 09:38:47 +0100 +message: + Added a V2 for kazmathxx +------------------------------------------------------------ +revno: 99 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-08-02 08:08:26 +0100 +message: + Added missing header file to one of the tests +------------------------------------------------------------ +revno: 98 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Sat 2009-08-01 12:05:25 +0200 +message: + No longer doing self assignment in kmMat4Inverse +------------------------------------------------------------ +revno: 97 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Sat 2009-08-01 11:15:36 +0200 +message: + Fixed kmMat4Inverse +------------------------------------------------------------ +revno: 96 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sat 2009-08-01 09:20:33 +0100 +message: + Fixed some whitespace issues in plane.c +------------------------------------------------------------ +revno: 95 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sat 2009-08-01 09:17:01 +0100 +message: + Rename kmAABBPointInBox to kmAABBContainsPoint +------------------------------------------------------------ +revno: 94 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sat 2009-08-01 09:16:28 +0100 +message: + Implement kmAABBPointInBox +------------------------------------------------------------ +revno: 93 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sat 2009-08-01 09:14:00 +0100 +message: + Implement kmAABBAssign +------------------------------------------------------------ +revno: 92 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sat 2009-08-01 09:10:36 +0100 +message: + Fixed some whitespace and added some comments +------------------------------------------------------------ +revno: 91 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sat 2009-08-01 08:56:13 +0100 +message: + Implemented (untested) kmMat4RotationTranslation to construct a 4x4 matrix from a 3x3 + vec3 +------------------------------------------------------------ +revno: 90 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-07-05 07:55:45 +0100 +message: + Added kmMat3RotationX, kmMat3RotationY and kmMat3RotationZ +------------------------------------------------------------ +revno: 89 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Sat 2009-06-27 09:38:20 +0200 +message: + Fixed a crash? +------------------------------------------------------------ +revno: 88 +committer: Luke Benstead +branch nick: kazmath +timestamp: Tue 2009-04-28 09:52:57 +0100 +message: + Added a test for kmMat4Transpose +------------------------------------------------------------ +revno: 87 +committer: Luke Benstead +branch nick: kazmath +timestamp: Tue 2009-04-28 08:49:59 +0100 +message: + Added a commented test for kmMat4Inverse, however kmMat4Adjugate and kmMat4Determinate need implementing +------------------------------------------------------------ +revno: 86 +committer: Luke Benstead +branch nick: kazmath +timestamp: Tue 2009-04-28 08:46:03 +0100 +message: + Fixed bug in kmQuaternionRotationMatrix +------------------------------------------------------------ +revno: 85 +committer: Luke Benstead +branch nick: kazmath +timestamp: Tue 2009-04-28 08:41:27 +0100 +message: + Added missing include to test_mat3.cpp +------------------------------------------------------------ +revno: 84 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Mon 2009-04-27 22:59:08 +0200 +message: + fixed CMakeLists.txt for the tests +------------------------------------------------------------ +revno: 83 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 21:34:20 +0100 +message: + Added a test for kmMat3Translation +------------------------------------------------------------ +revno: 82 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 21:32:19 +0100 +message: + Fixed bugs in kmMat3Scaling and kmMat3Translation, added test for kmMat3Scaling +------------------------------------------------------------ +revno: 81 [merge] +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 19:04:56 +0100 +message: + Merge from upstream. Fixed mismatching prototype in quaternion.c +------------------------------------------------------------ +revno: 80 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 19:01:35 +0100 +message: + Added a test for kmMat3AreEqual. Fixed a bug in kmMat3AreEqual +------------------------------------------------------------ +revno: 79 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 18:57:28 +0100 +message: + Added a test for kmMat3IsIdentity +------------------------------------------------------------ +revno: 78 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 18:54:48 +0100 +message: + Added a test for kmMat3Identity +------------------------------------------------------------ +revno: 77 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 18:52:14 +0100 +message: + Added a test for kmMat3Fill +------------------------------------------------------------ +revno: 76 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 18:46:30 +0100 +message: + Added some mat3 unit tests. Fixed a bug in kmMat3AreEqual +------------------------------------------------------------ +revno: 75 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 11:08:43 +0100 +message: + Added mat4 test stub +------------------------------------------------------------ +revno: 74 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 11:06:12 +0100 +message: + Enabled unit testing in cmake +------------------------------------------------------------ +revno: 73 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 10:44:37 +0100 +message: + Added cmakelists.txt to the tests subfolder +------------------------------------------------------------ +revno: 72 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 10:37:34 +0100 +message: + Added stub mat3 test file +------------------------------------------------------------ +revno: 71 +committer: Luke Benstead +branch nick: kazmath +timestamp: Mon 2009-04-27 10:35:48 +0100 +message: + Added tests folder for new boost::unit based tests +------------------------------------------------------------ +revno: 70 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Sun 2009-04-26 14:25:48 +0200 +message: + Fixed the new quaternion -> Matrix -> AngleAxis methods and added them to mat4 +------------------------------------------------------------ +revno: 69 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-04-26 12:40:08 +0100 +message: + Added kazmodel to the kazlibs repo +------------------------------------------------------------ +revno: 68 [merge] +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-04-26 12:34:27 +0100 +message: + Merge from upstream +------------------------------------------------------------ +revno: 67 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-04-26 12:33:43 +0100 +message: + Reorganized bzr +------------------------------------------------------------ +revno: 66 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-04-26 10:00:18 +0100 +message: + Renamed kmMat3RotationAxis to kmMat3RotationAxisAngle to be more accurate +------------------------------------------------------------ +revno: 65 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-04-26 09:57:19 +0100 +message: + Fixed some compilation errors +------------------------------------------------------------ +revno: 64 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-04-26 09:54:12 +0100 +message: + Added untested implementation of kmMat3RotationToAxisAngle +------------------------------------------------------------ +revno: 63 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-04-26 09:49:17 +0100 +message: + Added stub for kmMat3RotationToAxisAngle() +------------------------------------------------------------ +revno: 62 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-04-26 09:45:58 +0100 +message: + Corrected a typo +------------------------------------------------------------ +revno: 61 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sun 2009-04-26 09:44:39 +0100 +message: + Fixed broken Quaternion functions + Added (untested) kmMat3RotationAxis() +------------------------------------------------------------ +revno: 60 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sat 2009-04-18 08:55:29 +0100 +message: + Fixed some errors in the quaternion header. + Changed kmQuaternionRotationMatrix to accept a kmMat3 instead of kmMat4 +------------------------------------------------------------ +revno: 59 +committer: Luke Benstead +branch nick: kazmath +timestamp: Sat 2009-04-18 08:42:38 +0100 +message: + Added kmMat3RotationQuaternion +------------------------------------------------------------ +revno: 58 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Sun 2009-04-05 11:58:29 +0200 +message: + Added mat3 +------------------------------------------------------------ +revno: 57 [merge] +committer: Carsten Haubld +branch nick: kazmath +timestamp: Sun 2009-04-05 11:54:15 +0200 +message: + Implemented mat4 and vec4 for kazmathxx +------------------------------------------------------------ +revno: 56 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Sun 2009-04-05 11:53:07 +0200 +message: + Implemented mat4 and vec4 for kazmathxx +------------------------------------------------------------ +revno: 55 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Fri 2009-03-13 16:46:26 +0100 +message: + added km::vec3 to kazmathxx +------------------------------------------------------------ +revno: 54 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Fri 2009-03-13 16:14:51 +0100 +message: + Fixed: kmVec2 is no longer transformed by kmMat4\nAdded km::vec2 +------------------------------------------------------------ +revno: 53 +committer: Carsten Haubld +branch nick: kazmath +timestamp: Fri 2009-03-13 15:38:53 +0100 +message: + Added folder for kazmathxx +------------------------------------------------------------ +revno: 52 [merge] +committer: Luke Benstead +branch nick: trunk +timestamp: Fri 2009-03-13 14:04:32 +0000 +message: + Merge from upstream +------------------------------------------------------------ +revno: 51 +committer: Luke Benstead +branch nick: trunk +timestamp: Fri 2009-03-13 14:00:14 +0000 +message: + Fixed for C89 +------------------------------------------------------------ +revno: 50 +committer: Carsten Haubold +timestamp: Tue 2008-12-30 12:45:23 +0100 +message: + fixed kmGLTranslate +------------------------------------------------------------ +revno: 49 +committer: Carsten Haubold +timestamp: Tue 2008-12-30 12:15:27 +0100 +message: + fixed some stack memory leaks +------------------------------------------------------------ +revno: 48 +committer: Carsten Haubold +timestamp: Tue 2008-12-30 11:01:35 +0100 +message: + The GL matrix stacks now work as expected - matrix multiplication was the wrong way round +------------------------------------------------------------ +revno: 47 +committer: Carsten Haubold +timestamp: Tue 2008-12-30 10:52:28 +0100 +message: + Debug output +------------------------------------------------------------ +revno: 46 +committer: Carsten Haubold +timestamp: Mon 2008-12-29 18:32:13 +0100 +message: + Fixed some compiler errors +------------------------------------------------------------ +revno: 45 +committer: Luke +timestamp: Wed 2008-11-19 08:36:47 +0000 +message: + Added kmGLTranslatef, kmGLScalef and kmGLRotatef +------------------------------------------------------------ +revno: 44 +committer: Luke +timestamp: Tue 2008-11-04 20:57:30 +0000 +message: + Fixed up kazmathxx +------------------------------------------------------------ +revno: 43 +committer: Luke +timestamp: Wed 2008-10-29 09:24:32 +0000 +message: + Started implementing C++ operators in kazmathxx.h +------------------------------------------------------------ +revno: 42 +committer: Luke +timestamp: Tue 2008-10-28 18:21:16 +0000 +message: + Added kazmathxx.h for C++ usage +------------------------------------------------------------ +revno: 41 +committer: Luke +timestamp: Tue 2008-10-28 11:00:41 +0000 +message: + Added Doxygen documentation +------------------------------------------------------------ +revno: 40 +committer: Luke +timestamp: Tue 2008-10-28 08:46:19 +0000 +message: + Began documenting the kmPlane functions. + Changed some assert(0)s to include a not implemented message +------------------------------------------------------------ +revno: 39 +committer: Luke +timestamp: Tue 2008-10-28 08:42:24 +0000 +message: + - Wrote stubs for the AABB functions which raise assertions if used. + - Documented the AABB functions + - Changed the definition of kmAABBPointInBox so that it actually makes sense +------------------------------------------------------------ +revno: 38 +committer: Luke +timestamp: Tue 2008-10-28 08:38:48 +0000 +message: + - Documented utility.c +------------------------------------------------------------ +revno: 37 +committer: Luke +timestamp: Tue 2008-10-28 08:36:30 +0000 +message: + - Documented vec3.c in detail. + - Fixed up a not-implemented assertion. + - Changed existing doc strings to C style - /** */ +------------------------------------------------------------ +revno: 36 +committer: Luke +timestamp: Tue 2008-10-28 08:30:00 +0000 +message: + Removed uneccessary files from git +------------------------------------------------------------ +revno: 35 +committer: Luke +timestamp: Tue 2008-10-28 08:28:49 +0000 +message: + - Documented all the functions in mat4.c + - Fixed up all asserts in mat4.c to include a message + - Tidied up the code. Mat4.c is now done. +------------------------------------------------------------ +revno: 34 +committer: Luke +timestamp: Mon 2008-10-27 21:52:33 +0000 +message: + Added potential 0.1 release binary +------------------------------------------------------------ +revno: 33 +committer: Luke Benstead +timestamp: Mon 2008-10-27 21:46:55 +0000 +message: + Changed the README to include the BSD license +------------------------------------------------------------ +revno: 32 +committer: Luke Benstead +timestamp: Mon 2008-10-27 21:45:51 +0000 +message: + Added the modified BSD license to all source files +------------------------------------------------------------ +revno: 31 +committer: Luke Benstead +timestamp: Mon 2008-10-27 21:11:51 +0000 +message: + Fixed the installation of header files in CMake +------------------------------------------------------------ +revno: 30 +committer: Luke +timestamp: Mon 2008-10-27 21:05:51 +0000 +message: + Added kazmath project files +------------------------------------------------------------ +revno: 29 [merge] +committer: Luke +timestamp: Mon 2008-10-27 21:03:22 +0000 +message: + Merge branch 'master' of git@github.com:Kazade/kazmath +------------------------------------------------------------ +revno: 28 +committer: Luke +timestamp: Mon 2008-10-27 21:02:04 +0000 +message: + Finally got kazmath compiling on VC++, man that compiler sucks! Have MS not heard of C99? +------------------------------------------------------------ +revno: 27 +committer: Luke Benstead +timestamp: Sun 2008-10-26 21:35:24 +0000 +message: + Changed the readme slightly, we need to change the license everywhere +------------------------------------------------------------ +revno: 26 +committer: Luke Benstead +timestamp: Sun 2008-10-26 21:21:47 +0000 +message: + Implemented the stacks test, fixed the undefined references I was getting +------------------------------------------------------------ +revno: 25 +committer: Luke Benstead +timestamp: Sun 2008-10-26 20:59:34 +0000 +message: + Removed the old matrix stack stuff +------------------------------------------------------------ +revno: 24 +committer: Luke Benstead +timestamp: Sun 2008-10-26 20:11:58 +0000 +message: + Started implementing the matrix stack tests +------------------------------------------------------------ +revno: 23 +committer: Luke Benstead +timestamp: Sun 2008-10-26 10:56:51 +0000 +message: + Started new implementation of the GL matrix stack +------------------------------------------------------------ +revno: 22 +committer: Carsten Haubold +timestamp: Thu 2008-08-28 12:37:41 +0200 +message: + Added kmGLRotation +------------------------------------------------------------ +revno: 21 +committer: Luke Benstead +timestamp: Thu 2008-08-28 09:24:00 +0100 +message: + We now have a working matrix stack +------------------------------------------------------------ +revno: 20 +committer: Luke Benstead +timestamp: Wed 2008-08-27 13:34:49 +0100 +message: + Fixed the stack memory constants +------------------------------------------------------------ +revno: 19 +committer: Luke Benstead +timestamp: Wed 2008-08-27 13:33:12 +0100 +message: + Added the initial gl_utils implementation for replacing the matrix functionality deprecated in OpenGL 3.0 +------------------------------------------------------------ +revno: 18 +committer: Carsten Haubold +timestamp: Mon 2008-08-25 12:46:16 +0200 +message: + Fixed a bug in kmMat4LookAt +------------------------------------------------------------ +revno: 17 [merge] +committer: Carsten Haubold +timestamp: Sun 2008-08-24 22:07:49 +0200 +message: + Merge branch 'master' of git@github.com:Kazade/kazmath +------------------------------------------------------------ +revno: 16 +committer: Carsten Haubold +timestamp: Sun 2008-08-24 22:06:45 +0200 +message: + Added kmMat4LookAt +------------------------------------------------------------ +revno: 15 +committer: Carsten Haubold +timestamp: Wed 2008-08-20 11:18:10 +0200 +message: + Added Fill methods for all Vec and Mat structs +------------------------------------------------------------ +revno: 14 +committer: Carsten Haubold +timestamp: Tue 2008-08-19 22:31:55 +0200 +message: + Added UnitTests, changed bool to int and fixed some minor bugs +------------------------------------------------------------ +revno: 13 +committer: Carsten Haubold +timestamp: Sun 2008-08-17 23:19:21 +0200 +message: + removed .svn entries which did not belong here +------------------------------------------------------------ +revno: 12 +committer: Carsten Haubold +timestamp: Sun 2008-08-17 23:17:07 +0200 +message: + some tweaks on matrices and first test-app, PerspectiveProjection is correct ! +------------------------------------------------------------ +revno: 11 +committer: Carsten Haubold +timestamp: Sun 2008-08-17 16:04:09 +0200 +message: + Renamed cotangent to cotangens +------------------------------------------------------------ +revno: 10 +committer: Luke Benstead +timestamp: Sat 2008-08-16 21:51:22 +0100 +message: + Added kmMat4PerspectiveProjection and kmMat4OrthographicProjection +------------------------------------------------------------ +revno: 9 +committer: Luke Benstead +timestamp: Thu 2008-08-14 21:15:45 +0100 +message: + Added the aabb struct +------------------------------------------------------------ +revno: 8 +committer: Luke Benstead +timestamp: Thu 2008-08-14 17:57:43 +0100 +message: + Added the kmAABB structure +------------------------------------------------------------ +revno: 7 +committer: Luke Benstead +timestamp: Thu 2008-08-14 14:32:24 +0100 +message: + Fixed broken kmMat3Transpose +------------------------------------------------------------ +revno: 6 +committer: Luke Benstead +timestamp: Thu 2008-08-14 14:21:04 +0100 +message: + Fixed broken kmMat4Translation, w component was not set +------------------------------------------------------------ +revno: 5 +committer: Luke Benstead +timestamp: Thu 2008-08-14 14:01:47 +0100 +message: + Added mat3.c and mat3.h to the cmake file +------------------------------------------------------------ +revno: 4 +committer: Luke Benstead +timestamp: Thu 2008-08-14 13:56:26 +0100 +message: + Added the authors section to the readme +------------------------------------------------------------ +revno: 3 +committer: Luke Benstead +timestamp: Thu 2008-08-14 13:55:41 +0100 +message: + Updated the readme file +------------------------------------------------------------ +revno: 2 +committer: Luke Benstead +timestamp: Thu 2008-08-14 13:53:26 +0100 +message: + Added kazmath to git +------------------------------------------------------------ +revno: 1 +committer: Luke Benstead +timestamp: Thu 2008-08-14 13:47:51 +0100 +message: + First commit +------------------------------------------------------------ +Use --include-merges or -n0 to see merged revisions. diff --git a/cocos2d/kazmath/src/GL/mat4stack.c b/cocos2d/kazmath/src/GL/mat4stack.c new file mode 100755 index 0000000..f2ffe57 --- /dev/null +++ b/cocos2d/kazmath/src/GL/mat4stack.c @@ -0,0 +1,74 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#include +#include +#include +#include + +#define INITIAL_SIZE 30 +#define INCREMENT 50 + +#include "kazmath/GL/mat4stack.h" + +void km_mat4_stack_initialize(km_mat4_stack* stack) { + stack->stack = (kmMat4*) malloc(sizeof(kmMat4) * INITIAL_SIZE); //allocate the memory + stack->capacity = INITIAL_SIZE; //Set the capacity to 10 + stack->top = NULL; //Set the top to NULL + stack->item_count = 0; +}; + +void km_mat4_stack_push(km_mat4_stack* stack, const kmMat4* item) +{ + stack->top = &stack->stack[stack->item_count]; + kmMat4Assign(stack->top, item); + stack->item_count++; + + if(stack->item_count >= stack->capacity) + { + kmMat4* temp = NULL; + stack->capacity += INCREMENT; + temp = stack->stack; + stack->stack = (kmMat4*) malloc(stack->capacity*sizeof(kmMat4)); + memcpy(stack->stack, temp, sizeof(kmMat4)*(stack->capacity - INCREMENT)); + free(temp); + stack->top = &stack->stack[stack->item_count - 1]; + } +} + +void km_mat4_stack_pop(km_mat4_stack* stack, kmMat4* pOut) +{ + assert(stack->item_count && "Cannot pop an empty stack"); + + stack->item_count--; + stack->top = &stack->stack[stack->item_count - 1]; +} + +void km_mat4_stack_release(km_mat4_stack* stack) { + free(stack->stack); + stack->top = NULL; + stack->item_count = 0; + stack->capacity = 0; +} diff --git a/cocos2d/kazmath/src/GL/matrix.c b/cocos2d/kazmath/src/GL/matrix.c new file mode 100755 index 0000000..f63cf14 --- /dev/null +++ b/cocos2d/kazmath/src/GL/matrix.c @@ -0,0 +1,191 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#include +#include + +#include "kazmath/GL/matrix.h" +#include "kazmath/GL/mat4stack.h" + +km_mat4_stack modelview_matrix_stack; +km_mat4_stack projection_matrix_stack; +km_mat4_stack texture_matrix_stack; + +km_mat4_stack* current_stack = NULL; + +static unsigned char initialized = 0; + +void lazyInitialize() +{ + + if (!initialized) { + kmMat4 identity; //Temporary identity matrix + + //Initialize all 3 stacks + //modelview_matrix_stack = (km_mat4_stack*) malloc(sizeof(km_mat4_stack)); + km_mat4_stack_initialize(&modelview_matrix_stack); + + //projection_matrix_stack = (km_mat4_stack*) malloc(sizeof(km_mat4_stack)); + km_mat4_stack_initialize(&projection_matrix_stack); + + //texture_matrix_stack = (km_mat4_stack*) malloc(sizeof(km_mat4_stack)); + km_mat4_stack_initialize(&texture_matrix_stack); + + current_stack = &modelview_matrix_stack; + initialized = 1; + + kmMat4Identity(&identity); + + //Make sure that each stack has the identity matrix + km_mat4_stack_push(&modelview_matrix_stack, &identity); + km_mat4_stack_push(&projection_matrix_stack, &identity); + km_mat4_stack_push(&texture_matrix_stack, &identity); + } +} + +void kmGLMatrixMode(kmGLEnum mode) +{ + lazyInitialize(); + + switch(mode) + { + case KM_GL_MODELVIEW: + current_stack = &modelview_matrix_stack; + break; + case KM_GL_PROJECTION: + current_stack = &projection_matrix_stack; + break; + case KM_GL_TEXTURE: + current_stack = &texture_matrix_stack; + break; + default: + assert(0 && "Invalid matrix mode specified"); //TODO: Proper error handling + break; + } +} + +void kmGLPushMatrix(void) +{ + kmMat4 top; + + lazyInitialize(); //Initialize the stacks if they haven't been already + + //Duplicate the top of the stack (i.e the current matrix) + kmMat4Assign(&top, current_stack->top); + km_mat4_stack_push(current_stack, &top); +} + +void kmGLPopMatrix(void) +{ + assert(initialized && "Cannot Pop empty matrix stack"); + //No need to lazy initialize, you shouldnt be popping first anyway! + km_mat4_stack_pop(current_stack, NULL); +} + +void kmGLLoadIdentity() +{ + lazyInitialize(); + + kmMat4Identity(current_stack->top); //Replace the top matrix with the identity matrix +} + +void kmGLFreeAll() +{ + //Clear the matrix stacks + km_mat4_stack_release(&modelview_matrix_stack); + km_mat4_stack_release(&projection_matrix_stack); + km_mat4_stack_release(&texture_matrix_stack); + + //Delete the matrices + initialized = 0; //Set to uninitialized + + current_stack = NULL; //Set the current stack to point nowhere +} + +void kmGLMultMatrix(const kmMat4* pIn) +{ + lazyInitialize(); + kmMat4Multiply(current_stack->top, current_stack->top, pIn); +} + +void kmGLLoadMatrix(const kmMat4* pIn) +{ + lazyInitialize(); + kmMat4Assign(current_stack->top, pIn); +} + +void kmGLGetMatrix(kmGLEnum mode, kmMat4* pOut) +{ + lazyInitialize(); + + switch(mode) + { + case KM_GL_MODELVIEW: + kmMat4Assign(pOut, modelview_matrix_stack.top); + break; + case KM_GL_PROJECTION: + kmMat4Assign(pOut, projection_matrix_stack.top); + break; + case KM_GL_TEXTURE: + kmMat4Assign(pOut, texture_matrix_stack.top); + break; + default: + assert(1 && "Invalid matrix mode specified"); //TODO: Proper error handling + break; + } +} + +void kmGLTranslatef(float x, float y, float z) +{ + kmMat4 translation; + + //Create a rotation matrix using the axis and the angle + kmMat4Translation(&translation,x,y,z); + + //Multiply the rotation matrix by the current matrix + kmMat4Multiply(current_stack->top, current_stack->top, &translation); +} + +void kmGLRotatef(float angle, float x, float y, float z) +{ + kmVec3 axis; + kmMat4 rotation; + + //Create an axis vector + kmVec3Fill(&axis, x, y, z); + + //Create a rotation matrix using the axis and the angle + kmMat4RotationAxisAngle(&rotation, &axis, kmDegreesToRadians(angle)); + + //Multiply the rotation matrix by the current matrix + kmMat4Multiply(current_stack->top, current_stack->top, &rotation); +} + +void kmGLScalef(float x, float y, float z) +{ + kmMat4 scaling; + kmMat4Scaling(&scaling, x, y, z); + kmMat4Multiply(current_stack->top, current_stack->top, &scaling); +} diff --git a/cocos2d/kazmath/src/aabb.c b/cocos2d/kazmath/src/aabb.c new file mode 100755 index 0000000..ea334a3 --- /dev/null +++ b/cocos2d/kazmath/src/aabb.c @@ -0,0 +1,63 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#include "kazmath/aabb.h" + +/** + * Returns KM_TRUE if point is in the specified AABB, returns + * KM_FALSE otherwise. + */ +const int kmAABBContainsPoint(const kmVec3* pPoint, const kmAABB* pBox) +{ + if(pPoint->x >= pBox->min.x && pPoint->x <= pBox->max.x && + pPoint->y >= pBox->min.y && pPoint->y <= pBox->max.y && + pPoint->z >= pBox->min.z && pPoint->z <= pBox->max.z) { + return KM_TRUE; + } + + return KM_FALSE; +} + +/** + * Assigns pIn to pOut, returns pOut. + */ +kmAABB* const kmAABBAssign(kmAABB* pOut, const kmAABB* pIn) +{ + kmVec3Assign(&pOut->min, &pIn->min); + kmVec3Assign(&pOut->max, &pIn->max); + return pOut; +} + +/** + * Scales pIn by s, stores the resulting AABB in pOut. Returns pOut + */ +kmAABB* const kmAABBScale(kmAABB* pOut, const kmAABB* pIn, kmScalar s) +{ + assert(0 && "Not implemented"); + return 0; +} + + + diff --git a/cocos2d/kazmath/src/mat3.c b/cocos2d/kazmath/src/mat3.c new file mode 100755 index 0000000..6847caa --- /dev/null +++ b/cocos2d/kazmath/src/mat3.c @@ -0,0 +1,372 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#include +#include +#include + +#include "kazmath/utility.h" +#include "kazmath/vec3.h" +#include "kazmath/mat3.h" +#include "kazmath/quaternion.h" + +kmMat3* const kmMat3Fill(kmMat3* pOut, const kmScalar* pMat) +{ + memcpy(pOut->mat, pMat, sizeof(kmScalar) * 9); + return pOut; +} + +/** Sets pOut to an identity matrix returns pOut*/ +kmMat3* const kmMat3Identity(kmMat3* pOut) +{ + memset(pOut->mat, 0, sizeof(float) * 9); + pOut->mat[0] = pOut->mat[4] = pOut->mat[8] = 1.0f; + return pOut; +} + +const kmScalar kmMat3Determinant(const kmMat3* pIn) +{ + kmScalar output; + /* + calculating the determinant following the rule of sarus, + | 0 3 6 | 0 3 | + m = | 1 4 7 | 1 4 | + | 2 5 8 | 2 5 | + now sum up the products of the diagonals going to the right (i.e. 0,4,8) + and substract the products of the other diagonals (i.e. 2,4,6) + */ + + output = pIn->mat[0] * pIn->mat[4] * pIn->mat[8] + pIn->mat[1] * pIn->mat[5] * pIn->mat[6] + pIn->mat[2] * pIn->mat[3] * pIn->mat[7]; + output -= pIn->mat[2] * pIn->mat[4] * pIn->mat[6] + pIn->mat[0] * pIn->mat[5] * pIn->mat[7] + pIn->mat[1] * pIn->mat[3] * pIn->mat[8]; + + return output; +} + + +kmMat3* const kmMat3Adjugate(kmMat3* pOut, const kmMat3* pIn) +{ + pOut->mat[0] = pIn->mat[4] * pIn->mat[8] - pIn->mat[5] * pIn->mat[7]; + pOut->mat[1] = pIn->mat[2] * pIn->mat[7] - pIn->mat[1] * pIn->mat[8]; + pOut->mat[2] = pIn->mat[1] * pIn->mat[5] - pIn->mat[2] * pIn->mat[4]; + pOut->mat[3] = pIn->mat[5] * pIn->mat[6] - pIn->mat[3] * pIn->mat[8]; + pOut->mat[4] = pIn->mat[0] * pIn->mat[8] - pIn->mat[2] * pIn->mat[6]; + pOut->mat[5] = pIn->mat[2] * pIn->mat[3] - pIn->mat[0] * pIn->mat[5]; + pOut->mat[6] = pIn->mat[3] * pIn->mat[7] - pIn->mat[4] * pIn->mat[6]; + + // XXX: pIn->mat[9] is invalid! +// pOut->mat[7] = pIn->mat[1] * pIn->mat[6] - pIn->mat[9] * pIn->mat[7]; + pOut->mat[8] = pIn->mat[0] * pIn->mat[4] - pIn->mat[1] * pIn->mat[3]; + + return pOut; +} + +kmMat3* const kmMat3Inverse(kmMat3* pOut, const kmScalar pDeterminate, const kmMat3* pM) +{ + kmScalar detInv; + kmMat3 adjugate; + + if(pDeterminate == 0.0) + { + return NULL; + } + + detInv = 1.0 / pDeterminate; + + kmMat3Adjugate(&adjugate, pM); + kmMat3ScalarMultiply(pOut, &adjugate, detInv); + + return pOut; +} + +/** Returns true if pIn is an identity matrix */ +const int kmMat3IsIdentity(const kmMat3* pIn) +{ + static const float identity [] = { 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 1.0f}; + + return (memcmp(identity, pIn->mat, sizeof(float) * 9) == 0); +} + +/** Sets pOut to the transpose of pIn, returns pOut */ +kmMat3* const kmMat3Transpose(kmMat3* pOut, const kmMat3* pIn) +{ + int z, x; + for (z = 0; z < 3; ++z) { + for (x = 0; x < 3; ++x) { + pOut->mat[(z * 3) + x] = pIn->mat[(x * 3) + z]; + } + } + + return pOut; +} + +/* Multiplies pM1 with pM2, stores the result in pOut, returns pOut */ +kmMat3* const kmMat3Multiply(kmMat3* pOut, const kmMat3* pM1, const kmMat3* pM2) +{ + float mat[9]; + + const float *m1 = pM1->mat, *m2 = pM2->mat; + + mat[0] = m1[0] * m2[0] + m1[3] * m2[1] + m1[6] * m2[2]; + mat[1] = m1[1] * m2[0] + m1[4] * m2[1] + m1[7] * m2[2]; + mat[2] = m1[2] * m2[0] + m1[5] * m2[1] + m1[8] * m2[2]; + + mat[3] = m1[0] * m2[3] + m1[3] * m2[4] + m1[6] * m2[5]; + mat[4] = m1[1] * m2[3] + m1[4] * m2[4] + m1[7] * m2[5]; + mat[5] = m1[2] * m2[3] + m1[5] * m2[4] + m1[8] * m2[5]; + + mat[6] = m1[0] * m2[6] + m1[3] * m2[7] + m1[6] * m2[8]; + mat[7] = m1[1] * m2[6] + m1[4] * m2[7] + m1[7] * m2[8]; + mat[8] = m1[2] * m2[6] + m1[5] * m2[7] + m1[8] * m2[8]; + + memcpy(pOut->mat, mat, sizeof(float)*9); + + return pOut; +} + +kmMat3* const kmMat3ScalarMultiply(kmMat3* pOut, const kmMat3* pM, const kmScalar pFactor) +{ + float mat[9]; + int i; + + for(i = 0; i < 9; i++) + { + mat[i] = pM->mat[i] * pFactor; + } + + memcpy(pOut->mat, mat, sizeof(float)*9); + + return pOut; +} + +/** Assigns the value of pIn to pOut */ +kmMat3* const kmMat3Assign(kmMat3* pOut, const kmMat3* pIn) +{ + assert(pOut != pIn); //You have tried to self-assign!! + + memcpy(pOut->mat, pIn->mat, sizeof(float)*9); + + return pOut; +} + +/** Returns true if the 2 matrices are equal (approximately) */ +const int kmMat3AreEqual(const kmMat3* pMat1, const kmMat3* pMat2) +{ + int i; + if (pMat1 == pMat2) { + return KM_TRUE; + } + + for (i = 0; i < 9; ++i) { + if (!(pMat1->mat[i] + kmEpsilon > pMat2->mat[i] && + pMat1->mat[i] - kmEpsilon < pMat2->mat[i])) { + return KM_FALSE; + } + } + + return KM_TRUE; +} + +/* Rotation around the z axis so everything stays planar in XY */ +kmMat3* const kmMat3Rotation(kmMat3* pOut, const float radians) +{ + /* + | cos(A) -sin(A) 0 | + M = | sin(A) cos(A) 0 | + | 0 0 1 | + */ + + pOut->mat[0] = cosf(radians); + pOut->mat[1] = sinf(radians); + pOut->mat[2] = 0.0f; + + pOut->mat[3] = -sinf(radians);; + pOut->mat[4] = cosf(radians); + pOut->mat[5] = 0.0f; + + pOut->mat[6] = 0.0f; + pOut->mat[7] = 0.0f; + pOut->mat[8] = 1.0f; + + return pOut; +} + +/** Builds a scaling matrix */ +kmMat3* const kmMat3Scaling(kmMat3* pOut, const kmScalar x, const kmScalar y) +{ +// memset(pOut->mat, 0, sizeof(float) * 9); + kmMat3Identity(pOut); + pOut->mat[0] = x; + pOut->mat[4] = y; + + return pOut; +} + +kmMat3* const kmMat3Translation(kmMat3* pOut, const kmScalar x, const kmScalar y) +{ +// memset(pOut->mat, 0, sizeof(float) * 9); + kmMat3Identity(pOut); + pOut->mat[6] = x; + pOut->mat[7] = y; +// pOut->mat[8] = 1.0; + + return pOut; +} + + +kmMat3* const kmMat3RotationQuaternion(kmMat3* pOut, const kmQuaternion* pIn) +{ + if (!pIn || !pOut) { + return NULL; + } + + // First row + pOut->mat[0] = 1.0f - 2.0f * (pIn->y * pIn->y + pIn->z * pIn->z); + pOut->mat[1] = 2.0f * (pIn->x * pIn->y - pIn->w * pIn->z); + pOut->mat[2] = 2.0f * (pIn->x * pIn->z + pIn->w * pIn->y); + + // Second row + pOut->mat[3] = 2.0f * (pIn->x * pIn->y + pIn->w * pIn->z); + pOut->mat[4] = 1.0f - 2.0f * (pIn->x * pIn->x + pIn->z * pIn->z); + pOut->mat[5] = 2.0f * (pIn->y * pIn->z - pIn->w * pIn->x); + + // Third row + pOut->mat[6] = 2.0f * (pIn->x * pIn->z - pIn->w * pIn->y); + pOut->mat[7] = 2.0f * (pIn->y * pIn->z + pIn->w * pIn->x); + pOut->mat[8] = 1.0f - 2.0f * (pIn->x * pIn->x + pIn->y * pIn->y); + + return pOut; +} + +kmMat3* const kmMat3RotationAxisAngle(kmMat3* pOut, const struct kmVec3* axis, kmScalar radians) +{ + float rcos = cosf(radians); + float rsin = sinf(radians); + + pOut->mat[0] = rcos + axis->x * axis->x * (1 - rcos); + pOut->mat[1] = axis->z * rsin + axis->y * axis->x * (1 - rcos); + pOut->mat[2] = -axis->y * rsin + axis->z * axis->x * (1 - rcos); + + pOut->mat[3] = -axis->z * rsin + axis->x * axis->y * (1 - rcos); + pOut->mat[4] = rcos + axis->y * axis->y * (1 - rcos); + pOut->mat[5] = axis->x * rsin + axis->z * axis->y * (1 - rcos); + + pOut->mat[6] = axis->y * rsin + axis->x * axis->z * (1 - rcos); + pOut->mat[7] = -axis->x * rsin + axis->y * axis->z * (1 - rcos); + pOut->mat[8] = rcos + axis->z * axis->z * (1 - rcos); + + return pOut; +} + +kmVec3* const kmMat3RotationToAxisAngle(kmVec3* pAxis, kmScalar* radians, const kmMat3* pIn) +{ + /*Surely not this easy?*/ + kmQuaternion temp; + kmQuaternionRotationMatrix(&temp, pIn); + kmQuaternionToAxisAngle(&temp, pAxis, radians); + return pAxis; +} + +/** + * Builds an X-axis rotation matrix and stores it in pOut, returns pOut + */ +kmMat3* const kmMat3RotationX(kmMat3* pOut, const float radians) +{ + /* + | 1 0 0 | + M = | 0 cos(A) -sin(A) | + | 0 sin(A) cos(A) | + + */ + + pOut->mat[0] = 1.0f; + pOut->mat[1] = 0.0f; + pOut->mat[2] = 0.0f; + + pOut->mat[3] = 0.0f; + pOut->mat[4] = cosf(radians); + pOut->mat[5] = sinf(radians); + + pOut->mat[6] = 0.0f; + pOut->mat[7] = -sinf(radians); + pOut->mat[8] = cosf(radians); + + return pOut; +} + +/** + * Builds a rotation matrix using the rotation around the Y-axis + * The result is stored in pOut, pOut is returned. + */ +kmMat3* const kmMat3RotationY(kmMat3* pOut, const float radians) +{ + /* + | cos(A) 0 sin(A) | + M = | 0 1 0 | + | -sin(A) 0 cos(A) | + */ + + pOut->mat[0] = cosf(radians); + pOut->mat[1] = 0.0f; + pOut->mat[2] = -sinf(radians); + + pOut->mat[3] = 0.0f; + pOut->mat[4] = 1.0f; + pOut->mat[5] = 0.0f; + + pOut->mat[6] = sinf(radians); + pOut->mat[7] = 0.0f; + pOut->mat[8] = cosf(radians); + + return pOut; +} + +/** + * Builds a rotation matrix around the Z-axis. The resulting + * matrix is stored in pOut. pOut is returned. + */ +kmMat3* const kmMat3RotationZ(kmMat3* pOut, const float radians) +{ + /* + | cos(A) -sin(A) 0 | + M = | sin(A) cos(A) 0 | + | 0 0 1 | + */ + + pOut->mat[0] = cosf(radians); + pOut->mat[1] =-sinf(radians); + pOut->mat[2] = 0.0f; + + pOut->mat[3] = sinf(radians);; + pOut->mat[4] = cosf(radians); + pOut->mat[5] = 0.0f; + + pOut->mat[6] = 0.0f; + pOut->mat[7] = 0.0f; + pOut->mat[8] = 1.0f; + + return pOut; +} diff --git a/cocos2d/kazmath/src/mat4.c b/cocos2d/kazmath/src/mat4.c new file mode 100755 index 0000000..c55f5fd --- /dev/null +++ b/cocos2d/kazmath/src/mat4.c @@ -0,0 +1,789 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +/** + * @file mat4.c + */ +#include +#include +#include + +#include "kazmath/utility.h" +#include "kazmath/vec3.h" +#include "kazmath/mat4.h" +#include "kazmath/mat3.h" +#include "kazmath/quaternion.h" +#include "kazmath/plane.h" + +#include "kazmath/neon_matrix_impl.h" + +/** + * Fills a kmMat4 structure with the values from a 16 + * element array of floats + * @Params pOut - A pointer to the destination matrix + * pMat - A 16 element array of floats + * @Return Returns pOut so that the call can be nested + */ +kmMat4* const kmMat4Fill(kmMat4* pOut, const kmScalar* pMat) +{ + memcpy(pOut->mat, pMat, sizeof(kmScalar) * 16); + return pOut; +} + +/** + * Sets pOut to an identity matrix returns pOut + * @Params pOut - A pointer to the matrix to set to identity + * @Return Returns pOut so that the call can be nested + */ +kmMat4* const kmMat4Identity(kmMat4* pOut) +{ + memset(pOut->mat, 0, sizeof(float) * 16); + pOut->mat[0] = pOut->mat[5] = pOut->mat[10] = pOut->mat[15] = 1.0f; + return pOut; +} + + +float get(const kmMat4 * pIn, int row, int col) +{ + return pIn->mat[row + 4*col]; +} + +void set(kmMat4 * pIn, int row, int col, float value) +{ + pIn->mat[row + 4*col] = value; +} + +void swap(kmMat4 * pIn, int r1, int c1, int r2, int c2) +{ + float tmp = get(pIn,r1,c1); + set(pIn,r1,c1,get(pIn,r2,c2)); + set(pIn,r2,c2, tmp); +} + +//Returns an upper and a lower triangular matrix which are L and R in the Gauss algorithm +int gaussj(kmMat4 *a, kmMat4 *b) +{ + int i, icol = 0, irow = 0, j, k, l, ll, n = 4, m = 4; + float big, dum, pivinv; + int indxc[n]; + int indxr[n]; + int ipiv[n]; + + for (j = 0; j < n; j++) { + ipiv[j] = 0; + } + + for (i = 0; i < n; i++) { + big = 0.0f; + for (j = 0; j < n; j++) { + if (ipiv[j] != 1) { + for (k = 0; k < n; k++) { + if (ipiv[k] == 0) { + if (abs(get(a,j, k)) >= big) { + big = abs(get(a,j, k)); + irow = j; + icol = k; + } + } + } + } + } + ++(ipiv[icol]); + if (irow != icol) { + for (l = 0; l < n; l++) { + swap(a,irow, l, icol, l); + } + for (l = 0; l < m; l++) { + swap(b,irow, l, icol, l); + } + } + indxr[i] = irow; + indxc[i] = icol; + if (get(a,icol, icol) == 0.0) { + return KM_FALSE; + } + pivinv = 1.0f / get(a,icol, icol); + set(a,icol, icol, 1.0f); + for (l = 0; l < n; l++) { + set(a,icol, l, get(a,icol, l) * pivinv); + } + for (l = 0; l < m; l++) { + set(b,icol, l, get(b,icol, l) * pivinv); + } + + for (ll = 0; ll < n; ll++) { + if (ll != icol) { + dum = get(a,ll, icol); + set(a,ll, icol, 0.0f); + for (l = 0; l < n; l++) { + set(a,ll, l, get(a,ll, l) - get(a,icol, l) * dum); + } + for (l = 0; l < m; l++) { + set(b,ll, l, get(a,ll, l) - get(b,icol, l) * dum); + } + } + } + } +// This is the end of the main loop over columns of the reduction. It only remains to unscram- +// ble the solution in view of the column interchanges. We do this by interchanging pairs of +// columns in the reverse order that the permutation was built up. + for (l = n - 1; l >= 0; l--) { + if (indxr[l] != indxc[l]) { + for (k = 0; k < n; k++) { + swap(a,k, indxr[l], k, indxc[l]); + } + } + } + return KM_TRUE; +} + +/** + * Calculates the inverse of pM and stores the result in + * pOut. + * @Return Returns NULL if there is no inverse, else pOut + */ +kmMat4* const kmMat4Inverse(kmMat4* pOut, const kmMat4* pM) +{ + kmMat4 inv; + kmMat4Assign(&inv, pM); + + kmMat4 tmp; + kmMat4Identity(&tmp); + + if(gaussj(&inv, &tmp) == KM_FALSE) { + return NULL; + } + + kmMat4Assign(pOut, &inv); + return pOut; +} +/** + * Returns KM_TRUE if pIn is an identity matrix + * KM_FALSE otherwise + */ +const int kmMat4IsIdentity(const kmMat4* pIn) +{ + static const float identity [] = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + return (memcmp(identity, pIn->mat, sizeof(float) * 16) == 0); +} + +/** + * Sets pOut to the transpose of pIn, returns pOut + */ +kmMat4* const kmMat4Transpose(kmMat4* pOut, const kmMat4* pIn) +{ + int x, z; + + for (z = 0; z < 4; ++z) { + for (x = 0; x < 4; ++x) { + pOut->mat[(z * 4) + x] = pIn->mat[(x * 4) + z]; + } + } + + return pOut; +} + +/** + * Multiplies pM1 with pM2, stores the result in pOut, returns pOut + */ +kmMat4* const kmMat4Multiply(kmMat4* pOut, const kmMat4* pM1, const kmMat4* pM2) +{ +#if defined(__ARM_NEON__) + + float mat[16]; + + // Invert column-order with row-order + NEON_Matrix4Mul( &pM2->mat[0], &pM1->mat[0], &mat[0] ); + +#else + float mat[16]; + + const float *m1 = pM1->mat, *m2 = pM2->mat; + + mat[0] = m1[0] * m2[0] + m1[4] * m2[1] + m1[8] * m2[2] + m1[12] * m2[3]; + mat[1] = m1[1] * m2[0] + m1[5] * m2[1] + m1[9] * m2[2] + m1[13] * m2[3]; + mat[2] = m1[2] * m2[0] + m1[6] * m2[1] + m1[10] * m2[2] + m1[14] * m2[3]; + mat[3] = m1[3] * m2[0] + m1[7] * m2[1] + m1[11] * m2[2] + m1[15] * m2[3]; + + mat[4] = m1[0] * m2[4] + m1[4] * m2[5] + m1[8] * m2[6] + m1[12] * m2[7]; + mat[5] = m1[1] * m2[4] + m1[5] * m2[5] + m1[9] * m2[6] + m1[13] * m2[7]; + mat[6] = m1[2] * m2[4] + m1[6] * m2[5] + m1[10] * m2[6] + m1[14] * m2[7]; + mat[7] = m1[3] * m2[4] + m1[7] * m2[5] + m1[11] * m2[6] + m1[15] * m2[7]; + + mat[8] = m1[0] * m2[8] + m1[4] * m2[9] + m1[8] * m2[10] + m1[12] * m2[11]; + mat[9] = m1[1] * m2[8] + m1[5] * m2[9] + m1[9] * m2[10] + m1[13] * m2[11]; + mat[10] = m1[2] * m2[8] + m1[6] * m2[9] + m1[10] * m2[10] + m1[14] * m2[11]; + mat[11] = m1[3] * m2[8] + m1[7] * m2[9] + m1[11] * m2[10] + m1[15] * m2[11]; + + mat[12] = m1[0] * m2[12] + m1[4] * m2[13] + m1[8] * m2[14] + m1[12] * m2[15]; + mat[13] = m1[1] * m2[12] + m1[5] * m2[13] + m1[9] * m2[14] + m1[13] * m2[15]; + mat[14] = m1[2] * m2[12] + m1[6] * m2[13] + m1[10] * m2[14] + m1[14] * m2[15]; + mat[15] = m1[3] * m2[12] + m1[7] * m2[13] + m1[11] * m2[14] + m1[15] * m2[15]; + +#endif + + memcpy(pOut->mat, mat, sizeof(float)*16); + + return pOut; +} + +/** + * Assigns the value of pIn to pOut + */ +kmMat4* const kmMat4Assign(kmMat4* pOut, const kmMat4* pIn) +{ + assert(pOut != pIn && "You have tried to self-assign!!"); + + memcpy(pOut->mat, pIn->mat, sizeof(float)*16); + + return pOut; +} + +/** + * Returns KM_TRUE if the 2 matrices are equal (approximately) + */ +const int kmMat4AreEqual(const kmMat4* pMat1, const kmMat4* pMat2) +{ + int i = 0; + + assert(pMat1 != pMat2 && "You are comparing the same thing!"); + + for (i = 0; i < 16; ++i) + { + if (!(pMat1->mat[i] + kmEpsilon > pMat2->mat[i] && + pMat1->mat[i] - kmEpsilon < pMat2->mat[i])) { + return KM_FALSE; + } + } + + return KM_TRUE; +} + +/** + * Build a rotation matrix from an axis and an angle. Result is stored in pOut. + * pOut is returned. + */ +kmMat4* const kmMat4RotationAxisAngle(kmMat4* pOut, const kmVec3* axis, kmScalar radians) +{ + float rcos = cosf(radians); + float rsin = sinf(radians); + + kmVec3 normalizedAxis; + kmVec3Normalize(&normalizedAxis, axis); + + pOut->mat[0] = rcos + normalizedAxis.x * normalizedAxis.x * (1 - rcos); + pOut->mat[1] = normalizedAxis.z * rsin + normalizedAxis.y * normalizedAxis.x * (1 - rcos); + pOut->mat[2] = -normalizedAxis.y * rsin + normalizedAxis.z * normalizedAxis.x * (1 - rcos); + pOut->mat[3] = 0.0f; + + pOut->mat[4] = -normalizedAxis.z * rsin + normalizedAxis.x * normalizedAxis.y * (1 - rcos); + pOut->mat[5] = rcos + normalizedAxis.y * normalizedAxis.y * (1 - rcos); + pOut->mat[6] = normalizedAxis.x * rsin + normalizedAxis.z * normalizedAxis.y * (1 - rcos); + pOut->mat[7] = 0.0f; + + pOut->mat[8] = normalizedAxis.y * rsin + normalizedAxis.x * normalizedAxis.z * (1 - rcos); + pOut->mat[9] = -normalizedAxis.x * rsin + normalizedAxis.y * normalizedAxis.z * (1 - rcos); + pOut->mat[10] = rcos + normalizedAxis.z * normalizedAxis.z * (1 - rcos); + pOut->mat[11] = 0.0f; + + pOut->mat[12] = 0.0f; + pOut->mat[13] = 0.0f; + pOut->mat[14] = 0.0f; + pOut->mat[15] = 1.0f; + + return pOut; +} + +/** + * Builds an X-axis rotation matrix and stores it in pOut, returns pOut + */ +kmMat4* const kmMat4RotationX(kmMat4* pOut, const float radians) +{ + /* + | 1 0 0 0 | + M = | 0 cos(A) -sin(A) 0 | + | 0 sin(A) cos(A) 0 | + | 0 0 0 1 | + + */ + + pOut->mat[0] = 1.0f; + pOut->mat[1] = 0.0f; + pOut->mat[2] = 0.0f; + pOut->mat[3] = 0.0f; + + pOut->mat[4] = 0.0f; + pOut->mat[5] = cosf(radians); + pOut->mat[6] = sinf(radians); + pOut->mat[7] = 0.0f; + + pOut->mat[8] = 0.0f; + pOut->mat[9] = -sinf(radians); + pOut->mat[10] = cosf(radians); + pOut->mat[11] = 0.0f; + + pOut->mat[12] = 0.0f; + pOut->mat[13] = 0.0f; + pOut->mat[14] = 0.0f; + pOut->mat[15] = 1.0f; + + return pOut; +} + +/** + * Builds a rotation matrix using the rotation around the Y-axis + * The result is stored in pOut, pOut is returned. + */ +kmMat4* const kmMat4RotationY(kmMat4* pOut, const float radians) +{ + /* + | cos(A) 0 sin(A) 0 | + M = | 0 1 0 0 | + | -sin(A) 0 cos(A) 0 | + | 0 0 0 1 | + */ + + pOut->mat[0] = cosf(radians); + pOut->mat[1] = 0.0f; + pOut->mat[2] = -sinf(radians); + pOut->mat[3] = 0.0f; + + pOut->mat[4] = 0.0f; + pOut->mat[5] = 1.0f; + pOut->mat[6] = 0.0f; + pOut->mat[7] = 0.0f; + + pOut->mat[8] = sinf(radians); + pOut->mat[9] = 0.0f; + pOut->mat[10] = cosf(radians); + pOut->mat[11] = 0.0f; + + pOut->mat[12] = 0.0f; + pOut->mat[13] = 0.0f; + pOut->mat[14] = 0.0f; + pOut->mat[15] = 1.0f; + + return pOut; +} + +/** + * Builds a rotation matrix around the Z-axis. The resulting + * matrix is stored in pOut. pOut is returned. + */ +kmMat4* const kmMat4RotationZ(kmMat4* pOut, const float radians) +{ + /* + | cos(A) -sin(A) 0 0 | + M = | sin(A) cos(A) 0 0 | + | 0 0 1 0 | + | 0 0 0 1 | + */ + + pOut->mat[0] = cosf(radians); + pOut->mat[1] = sinf(radians); + pOut->mat[2] = 0.0f; + pOut->mat[3] = 0.0f; + + pOut->mat[4] = -sinf(radians);; + pOut->mat[5] = cosf(radians); + pOut->mat[6] = 0.0f; + pOut->mat[7] = 0.0f; + + pOut->mat[8] = 0.0f; + pOut->mat[9] = 0.0f; + pOut->mat[10] = 1.0f; + pOut->mat[11] = 0.0f; + + pOut->mat[12] = 0.0f; + pOut->mat[13] = 0.0f; + pOut->mat[14] = 0.0f; + pOut->mat[15] = 1.0f; + + return pOut; +} + +/** + * Builds a rotation matrix from pitch, yaw and roll. The resulting + * matrix is stored in pOut and pOut is returned + */ +kmMat4* const kmMat4RotationPitchYawRoll(kmMat4* pOut, const kmScalar pitch, const kmScalar yaw, const kmScalar roll) +{ + double cr = cos(pitch); + double sr = sin(pitch); + double cp = cos(yaw); + double sp = sin(yaw); + double cy = cos(roll); + double sy = sin(roll); + double srsp = sr * sp; + double crsp = cr * sp; + + pOut->mat[0] = (kmScalar) cp * cy; + pOut->mat[4] = (kmScalar) cp * sy; + pOut->mat[8] = (kmScalar) - sp; + + pOut->mat[1] = (kmScalar) srsp * cy - cr * sy; + pOut->mat[5] = (kmScalar) srsp * sy + cr * cy; + pOut->mat[9] = (kmScalar) sr * cp; + + pOut->mat[2] = (kmScalar) crsp * cy + sr * sy; + pOut->mat[6] = (kmScalar) crsp * sy - sr * cy; + pOut->mat[10] = (kmScalar) cr * cp; + + pOut->mat[3] = pOut->mat[7] = pOut->mat[11] = 0.0; + pOut->mat[15] = 1.0; + + return pOut; +} + +/** Converts a quaternion to a rotation matrix, + * the result is stored in pOut, returns pOut + */ +kmMat4* const kmMat4RotationQuaternion(kmMat4* pOut, const kmQuaternion* pQ) +{ + pOut->mat[0] = 1.0f - 2.0f * (pQ->y * pQ->y + pQ->z * pQ->z ); + pOut->mat[1] = 2.0f * (pQ->x * pQ->y + pQ->z * pQ->w); + pOut->mat[2] = 2.0f * (pQ->x * pQ->z - pQ->y * pQ->w); + pOut->mat[3] = 0.0f; + + // Second row + pOut->mat[4] = 2.0f * ( pQ->x * pQ->y - pQ->z * pQ->w ); + pOut->mat[5] = 1.0f - 2.0f * ( pQ->x * pQ->x + pQ->z * pQ->z ); + pOut->mat[6] = 2.0f * (pQ->z * pQ->y + pQ->x * pQ->w ); + pOut->mat[7] = 0.0f; + + // Third row + pOut->mat[8] = 2.0f * ( pQ->x * pQ->z + pQ->y * pQ->w ); + pOut->mat[9] = 2.0f * ( pQ->y * pQ->z - pQ->x * pQ->w ); + pOut->mat[10] = 1.0f - 2.0f * ( pQ->x * pQ->x + pQ->y * pQ->y ); + pOut->mat[11] = 0.0f; + + // Fourth row + pOut->mat[12] = 0; + pOut->mat[13] = 0; + pOut->mat[14] = 0; + pOut->mat[15] = 1.0f; + + return pOut; +} + +/** Builds a scaling matrix */ +kmMat4* const kmMat4Scaling(kmMat4* pOut, const kmScalar x, const kmScalar y, + const kmScalar z) +{ + memset(pOut->mat, 0, sizeof(float) * 16); + pOut->mat[0] = x; + pOut->mat[5] = y; + pOut->mat[10] = z; + pOut->mat[15] = 1.0f; + + return pOut; +} + +/** + * Builds a translation matrix. All other elements in the matrix + * will be set to zero except for the diagonal which is set to 1.0 + */ +kmMat4* const kmMat4Translation(kmMat4* pOut, const kmScalar x, + const kmScalar y, const kmScalar z) +{ + //FIXME: Write a test for this + memset(pOut->mat, 0, sizeof(float) * 16); + + pOut->mat[0] = 1.0f; + pOut->mat[5] = 1.0f; + pOut->mat[10] = 1.0f; + + pOut->mat[12] = x; + pOut->mat[13] = y; + pOut->mat[14] = z; + pOut->mat[15] = 1.0f; + + return pOut; +} + +/** + * Get the up vector from a matrix. pIn is the matrix you + * wish to extract the vector from. pOut is a pointer to the + * kmVec3 structure that should hold the resulting vector + */ +kmVec3* const kmMat4GetUpVec3(kmVec3* pOut, const kmMat4* pIn) +{ + pOut->x = pIn->mat[4]; + pOut->y = pIn->mat[5]; + pOut->z = pIn->mat[6]; + + kmVec3Normalize(pOut, pOut); + + return pOut; +} + +/** Extract the right vector from a 4x4 matrix. The result is + * stored in pOut. Returns pOut. + */ +kmVec3* const kmMat4GetRightVec3(kmVec3* pOut, const kmMat4* pIn) +{ + pOut->x = pIn->mat[0]; + pOut->y = pIn->mat[1]; + pOut->z = pIn->mat[2]; + + kmVec3Normalize(pOut, pOut); + + return pOut; +} + +/** + * Extract the forward vector from a 4x4 matrix. The result is + * stored in pOut. Returns pOut. + */ +kmVec3* const kmMat4GetForwardVec3(kmVec3* pOut, const kmMat4* pIn) +{ + pOut->x = pIn->mat[8]; + pOut->y = pIn->mat[9]; + pOut->z = pIn->mat[10]; + + kmVec3Normalize(pOut, pOut); + + return pOut; +} + +/** + * Creates a perspective projection matrix in the + * same way as gluPerspective + */ +kmMat4* const kmMat4PerspectiveProjection(kmMat4* pOut, kmScalar fovY, + kmScalar aspect, kmScalar zNear, + kmScalar zFar) +{ + kmScalar r = kmDegreesToRadians(fovY / 2); + kmScalar deltaZ = zFar - zNear; + kmScalar s = sin(r); + kmScalar cotangent = 0; + + if (deltaZ == 0 || s == 0 || aspect == 0) { + return NULL; + } + + //cos(r) / sin(r) = cot(r) + cotangent = cos(r) / s; + + kmMat4Identity(pOut); + pOut->mat[0] = cotangent / aspect; + pOut->mat[5] = cotangent; + pOut->mat[10] = -(zFar + zNear) / deltaZ; + pOut->mat[11] = -1; + pOut->mat[14] = -2 * zNear * zFar / deltaZ; + pOut->mat[15] = 0; + + return pOut; +} + +/** Creates an orthographic projection matrix like glOrtho */ +kmMat4* const kmMat4OrthographicProjection(kmMat4* pOut, kmScalar left, + kmScalar right, kmScalar bottom, + kmScalar top, kmScalar nearVal, + kmScalar farVal) +{ + kmScalar tx = -((right + left) / (right - left)); + kmScalar ty = -((top + bottom) / (top - bottom)); + kmScalar tz = -((farVal + nearVal) / (farVal - nearVal)); + + kmMat4Identity(pOut); + pOut->mat[0] = 2 / (right - left); + pOut->mat[5] = 2 / (top - bottom); + pOut->mat[10] = -2 / (farVal - nearVal); + pOut->mat[12] = tx; + pOut->mat[13] = ty; + pOut->mat[14] = tz; + + return pOut; +} + +/** + * Builds a translation matrix in the same way as gluLookAt() + * the resulting matrix is stored in pOut. pOut is returned. + */ +kmMat4* const kmMat4LookAt(kmMat4* pOut, const kmVec3* pEye, + const kmVec3* pCenter, const kmVec3* pUp) +{ + kmVec3 f, up, s, u; + kmMat4 translate; + + kmVec3Subtract(&f, pCenter, pEye); + kmVec3Normalize(&f, &f); + + kmVec3Assign(&up, pUp); + kmVec3Normalize(&up, &up); + + kmVec3Cross(&s, &f, &up); + kmVec3Normalize(&s, &s); + + kmVec3Cross(&u, &s, &f); + kmVec3Normalize(&s, &s); + + kmMat4Identity(pOut); + + pOut->mat[0] = s.x; + pOut->mat[4] = s.y; + pOut->mat[8] = s.z; + + pOut->mat[1] = u.x; + pOut->mat[5] = u.y; + pOut->mat[9] = u.z; + + pOut->mat[2] = -f.x; + pOut->mat[6] = -f.y; + pOut->mat[10] = -f.z; + + kmMat4Translation(&translate, -pEye->x, -pEye->y, -pEye->z); + kmMat4Multiply(pOut, pOut, &translate); + + return pOut; +} + +/** + * Extract a 3x3 rotation matrix from the input 4x4 transformation. + * Stores the result in pOut, returns pOut + */ +kmMat3* const kmMat4ExtractRotation(kmMat3* pOut, const kmMat4* pIn) +{ + pOut->mat[0] = pIn->mat[0]; + pOut->mat[1] = pIn->mat[1]; + pOut->mat[2] = pIn->mat[2]; + + pOut->mat[3] = pIn->mat[4]; + pOut->mat[4] = pIn->mat[5]; + pOut->mat[5] = pIn->mat[6]; + + pOut->mat[6] = pIn->mat[8]; + pOut->mat[7] = pIn->mat[9]; + pOut->mat[8] = pIn->mat[10]; + + return pOut; +} + +/** + * Take the rotation from a 4x4 transformation matrix, and return it as an axis and an angle (in radians) + * returns the output axis. + */ +kmVec3* const kmMat4RotationToAxisAngle(kmVec3* pAxis, kmScalar* radians, const kmMat4* pIn) +{ + /*Surely not this easy?*/ + kmQuaternion temp; + kmMat3 rotation; + kmMat4ExtractRotation(&rotation, pIn); + kmQuaternionRotationMatrix(&temp, &rotation); + kmQuaternionToAxisAngle(&temp, pAxis, radians); + return pAxis; +} + +/** Build a 4x4 OpenGL transformation matrix using a 3x3 rotation matrix, + * and a 3d vector representing a translation. Assign the result to pOut, + * pOut is also returned. + */ +kmMat4* const kmMat4RotationTranslation(kmMat4* pOut, const kmMat3* rotation, const kmVec3* translation) +{ + pOut->mat[0] = rotation->mat[0]; + pOut->mat[1] = rotation->mat[1]; + pOut->mat[2] = rotation->mat[2]; + pOut->mat[3] = 0.0f; + + pOut->mat[4] = rotation->mat[3]; + pOut->mat[5] = rotation->mat[4]; + pOut->mat[6] = rotation->mat[5]; + pOut->mat[7] = 0.0f; + + pOut->mat[8] = rotation->mat[6]; + pOut->mat[9] = rotation->mat[7]; + pOut->mat[10] = rotation->mat[8]; + pOut->mat[11] = 0.0f; + + pOut->mat[12] = translation->x; + pOut->mat[13] = translation->y; + pOut->mat[14] = translation->z; + pOut->mat[15] = 1.0f; + + return pOut; +} + +kmPlane* const kmMat4ExtractPlane(kmPlane* pOut, const kmMat4* pIn, const kmEnum plane) +{ + float t = 1.0f; + + switch(plane) { + case KM_PLANE_RIGHT: + pOut->a = pIn->mat[3] - pIn->mat[0]; + pOut->b = pIn->mat[7] - pIn->mat[4]; + pOut->c = pIn->mat[11] - pIn->mat[8]; + pOut->d = pIn->mat[15] - pIn->mat[12]; + break; + case KM_PLANE_LEFT: + pOut->a = pIn->mat[3] + pIn->mat[0]; + pOut->b = pIn->mat[7] + pIn->mat[4]; + pOut->c = pIn->mat[11] + pIn->mat[8]; + pOut->d = pIn->mat[15] + pIn->mat[12]; + break; + case KM_PLANE_BOTTOM: + pOut->a = pIn->mat[3] + pIn->mat[1]; + pOut->b = pIn->mat[7] + pIn->mat[5]; + pOut->c = pIn->mat[11] + pIn->mat[9]; + pOut->d = pIn->mat[15] + pIn->mat[13]; + break; + case KM_PLANE_TOP: + pOut->a = pIn->mat[3] - pIn->mat[1]; + pOut->b = pIn->mat[7] - pIn->mat[5]; + pOut->c = pIn->mat[11] - pIn->mat[9]; + pOut->d = pIn->mat[15] - pIn->mat[13]; + break; + case KM_PLANE_FAR: + pOut->a = pIn->mat[3] - pIn->mat[2]; + pOut->b = pIn->mat[7] - pIn->mat[6]; + pOut->c = pIn->mat[11] - pIn->mat[10]; + pOut->d = pIn->mat[15] - pIn->mat[14]; + break; + case KM_PLANE_NEAR: + pOut->a = pIn->mat[3] + pIn->mat[2]; + pOut->b = pIn->mat[7] + pIn->mat[6]; + pOut->c = pIn->mat[11] + pIn->mat[10]; + pOut->d = pIn->mat[15] + pIn->mat[14]; + break; + default: + assert(0 && "Invalid plane index"); + } + + t = sqrtf(pOut->a * pOut->a + + pOut->b * pOut->b + + pOut->c * pOut->c); + pOut->a /= t; + pOut->b /= t; + pOut->c /= t; + pOut->d /= t; + + return pOut; +} diff --git a/cocos2d/kazmath/src/neon_matrix_impl.c b/cocos2d/kazmath/src/neon_matrix_impl.c new file mode 100755 index 0000000..a00f771 --- /dev/null +++ b/cocos2d/kazmath/src/neon_matrix_impl.c @@ -0,0 +1,97 @@ +/* + NEON math library for the iPhone / iPod touch + + Copyright (c) 2009 Justin Saunders + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising + from the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product documentation + would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "kazmath/neon_matrix_impl.h" + +#if defined(__ARM_NEON__) + +void NEON_Matrix4Mul(const float* a, const float* b, float* output ) +{ + __asm__ volatile + ( + // Store A & B leaving room for q4-q7, which should be preserved + "vldmia %1, { q0-q3 } \n\t" + "vldmia %2, { q8-q11 }\n\t" + + // result = first column of B x first row of A + "vmul.f32 q12, q8, d0[0]\n\t" + "vmul.f32 q13, q8, d2[0]\n\t" + "vmul.f32 q14, q8, d4[0]\n\t" + "vmul.f32 q15, q8, d6[0]\n\t" + + // result += second column of B x second row of A + "vmla.f32 q12, q9, d0[1]\n\t" + "vmla.f32 q13, q9, d2[1]\n\t" + "vmla.f32 q14, q9, d4[1]\n\t" + "vmla.f32 q15, q9, d6[1]\n\t" + + // result += third column of B x third row of A + "vmla.f32 q12, q10, d1[0]\n\t" + "vmla.f32 q13, q10, d3[0]\n\t" + "vmla.f32 q14, q10, d5[0]\n\t" + "vmla.f32 q15, q10, d7[0]\n\t" + + // result += last column of B x last row of A + "vmla.f32 q12, q11, d1[1]\n\t" + "vmla.f32 q13, q11, d3[1]\n\t" + "vmla.f32 q14, q11, d5[1]\n\t" + "vmla.f32 q15, q11, d7[1]\n\t" + + // output = result registers + "vstmia %0, { q12-q15 }" + : // no output + : "r" (output), "r" (a), "r" (b) // input - note *value* of pointer doesn't change + : "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" //clobber + ); +} + +void NEON_Matrix4Vector4Mul(const float* m, const float* v, float* output) +{ + __asm__ volatile + ( + // Store m & v - avoiding q4-q7 which need to be preserved - q0 = result + "vldmia %1, { q8-q11 } \n\t" // q8-q11 = m + "vldmia %2, { q1 } \n\t" // q1 = v + + // result = first column of A x V.x + "vmul.f32 q0, q8, d2[0]\n\t" + + // result += second column of A x V.y + "vmla.f32 q0, q9, d2[1]\n\t" + + // result += third column of A x V.z + "vmla.f32 q0, q10, d3[0]\n\t" + + // result += last column of A x V.w + "vmla.f32 q0, q11, d3[1]\n\t" + + // output = result registers + "vstmia %0, { q0 }" + + : // no output + : "r" (output), "r" (m), "r" (v) // input - note *value* of pointer doesn't change + : "memory", "q0", "q1", "q8", "q9", "q10", "q11" //clobber + ); +} + +#endif diff --git a/cocos2d/kazmath/src/plane.c b/cocos2d/kazmath/src/plane.c new file mode 100755 index 0000000..f8dc02f --- /dev/null +++ b/cocos2d/kazmath/src/plane.c @@ -0,0 +1,175 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#include +#include + +#include "kazmath/vec3.h" +#include "kazmath/vec4.h" +#include "kazmath/plane.h" + +const kmScalar kmPlaneDot(const kmPlane* pP, const kmVec4* pV) +{ + //a*x + b*y + c*z + d*w + + return (pP->a * pV->x + + pP->b * pV->y + + pP->c * pV->z + + pP->d * pV->w); +} + +const kmScalar kmPlaneDotCoord(const kmPlane* pP, const kmVec3* pV) +{ + return (pP->a * pV->x + + pP->b * pV->y + + pP->c * pV->z + pP->d); +} + +const kmScalar kmPlaneDotNormal(const kmPlane* pP, const kmVec3* pV) +{ + return (pP->a * pV->x + + pP->b * pV->y + + pP->c * pV->z); +} + +kmPlane* const kmPlaneFromPointNormal(kmPlane* pOut, const kmVec3* pPoint, const kmVec3* pNormal) +{ + /* + Planea = Nx + Planeb = Ny + Planec = Nz + Planed = −N⋅P + */ + + + pOut->a = pNormal->x; + pOut->b = pNormal->y; + pOut->c = pNormal->z; + pOut->d = -kmVec3Dot(pNormal, pPoint); + + return pOut; +} + +/** + * Creates a plane from 3 points. The result is stored in pOut. + * pOut is returned. + */ +kmPlane* const kmPlaneFromPoints(kmPlane* pOut, const kmVec3* p1, const kmVec3* p2, const kmVec3* p3) +{ + /* + v = (B − A) × (C − A) + n = 1⁄|v| v + Outa = nx + Outb = ny + Outc = nz + Outd = −n⋅A + */ + + kmVec3 n, v1, v2; + kmVec3Subtract(&v1, p2, p1); //Create the vectors for the 2 sides of the triangle + kmVec3Subtract(&v2, p3, p1); + kmVec3Cross(&n, &v1, &v2); //Use the cross product to get the normal + + kmVec3Normalize(&n, &n); //Normalize it and assign to pOut->m_N + + pOut->a = n.x; + pOut->b = n.y; + pOut->c = n.z; + pOut->d = kmVec3Dot(kmVec3Scale(&n, &n, -1.0), p1); + + return pOut; +} + +kmVec3* const kmPlaneIntersectLine(kmVec3* pOut, const kmPlane* pP, const kmVec3* pV1, const kmVec3* pV2) +{ + /* + n = (Planea, Planeb, Planec) + d = V − U + Out = U − d⋅(Pd + n⋅U)⁄(d⋅n) [iff d⋅n ≠ 0] + */ + kmVec3 d; + assert(0 && "Not implemented"); + + + kmVec3Subtract(&d, pV2, pV1); //Get the direction vector + + + //TODO: Continue here! + /*if (fabs(kmVec3Dot(&pP->m_N, &d)) > kmEpsilon) + { + //If we get here then the plane and line are parallel (i.e. no intersection) + pOut = nullptr; //Set to nullptr + + return pOut; + } */ + + return NULL; +} + +kmPlane* const kmPlaneNormalize(kmPlane* pOut, const kmPlane* pP) +{ + kmVec3 n; + kmScalar l = 0; + + n.x = pP->a; + n.y = pP->b; + n.z = pP->c; + + l = 1.0f / kmVec3Length(&n); //Get 1/length + kmVec3Normalize(&n, &n); //Normalize the vector and assign to pOut + + pOut->a = n.x; + pOut->b = n.y; + pOut->c = n.z; + + pOut->d = pP->d * l; //Scale the D value and assign to pOut + + return pOut; +} + +kmPlane* const kmPlaneScale(kmPlane* pOut, const kmPlane* pP, kmScalar s) +{ + assert(0 && "Not implemented"); + return NULL; +} + +/** + * Returns POINT_INFRONT_OF_PLANE if pP is infront of pIn. Returns + * POINT_BEHIND_PLANE if it is behind. Returns POINT_ON_PLANE otherwise + */ +const POINT_CLASSIFICATION kmPlaneClassifyPoint(const kmPlane* pIn, const kmVec3* pP) +{ + // This function will determine if a point is on, in front of, or behind + // the plane. First we store the dot product of the plane and the point. + float distance = pIn->a * pP->x + pIn->b * pP->y + pIn->c * pP->z + pIn->d; + + // Simply put if the dot product is greater than 0 then it is infront of it. + // If it is less than 0 then it is behind it. And if it is 0 then it is on it. + if(distance > 0.001) return POINT_INFRONT_OF_PLANE; + if(distance < -0.001) return POINT_BEHIND_PLANE; + + return POINT_ON_PLANE; +} + diff --git a/cocos2d/kazmath/src/quaternion.c b/cocos2d/kazmath/src/quaternion.c new file mode 100755 index 0000000..c5bf58e --- /dev/null +++ b/cocos2d/kazmath/src/quaternion.c @@ -0,0 +1,582 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + + +#include +#include + +#include "kazmath/utility.h" +#include "kazmath/mat3.h" +#include "kazmath/vec3.h" +#include "kazmath/quaternion.h" + +///< Returns pOut, sets pOut to the conjugate of pIn +kmQuaternion* const kmQuaternionConjugate(kmQuaternion* pOut, const kmQuaternion* pIn) +{ + pOut->x = -pIn->x; + pOut->y = -pIn->y; + pOut->z = -pIn->z; + pOut->w = pIn->w; + + return pOut; +} + +///< Returns the dot product of the 2 quaternions +const kmScalar kmQuaternionDot(const kmQuaternion* q1, const kmQuaternion* q2) +{ + // A dot B = B dot A = AtBt + AxBx + AyBy + AzBz + + return (q1->w * q2->w + + q1->x * q2->x + + q1->y * q2->y + + q1->z * q2->z); +} + +///< Returns the exponential of the quaternion +kmQuaternion* kmQuaternionExp(kmQuaternion* pOut, const kmQuaternion* pIn) +{ + assert(0); + + return pOut; +} + +///< Makes the passed quaternion an identity quaternion +kmQuaternion* kmQuaternionIdentity(kmQuaternion* pOut) +{ + pOut->x = 0.0; + pOut->y = 0.0; + pOut->z = 0.0; + pOut->w = 1.0; + + return pOut; +} + +///< Returns the inverse of the passed Quaternion +kmQuaternion* kmQuaternionInverse(kmQuaternion* pOut, + const kmQuaternion* pIn) +{ + kmScalar l = kmQuaternionLength(pIn); + kmQuaternion tmp; + + if (fabs(l) > kmEpsilon) + { + pOut->x = 0.0; + pOut->y = 0.0; + pOut->z = 0.0; + pOut->w = 0.0; + + return pOut; + } + + + + ///Get the conjugute and divide by the length + kmQuaternionScale(pOut, + kmQuaternionConjugate(&tmp, pIn), 1.0f / l); + + return pOut; +} + +///< Returns true if the quaternion is an identity quaternion +int kmQuaternionIsIdentity(const kmQuaternion* pIn) +{ + return (pIn->x == 0.0 && pIn->y == 0.0 && pIn->z == 0.0 && + pIn->w == 1.0); +} + +///< Returns the length of the quaternion +kmScalar kmQuaternionLength(const kmQuaternion* pIn) +{ + return sqrtf(kmQuaternionLengthSq(pIn)); +} + +///< Returns the length of the quaternion squared (prevents a sqrt) +kmScalar kmQuaternionLengthSq(const kmQuaternion* pIn) +{ + return pIn->x * pIn->x + pIn->y * pIn->y + + pIn->z * pIn->z + pIn->w * pIn->w; +} + +///< Returns the natural logarithm +kmQuaternion* kmQuaternionLn(kmQuaternion* pOut, + const kmQuaternion* pIn) +{ + /* + A unit quaternion, is defined by: + Q == (cos(theta), sin(theta) * v) where |v| = 1 + The natural logarithm of Q is, ln(Q) = (0, theta * v) + */ + + assert(0); + + return pOut; +} + +///< Multiplies 2 quaternions together +extern +kmQuaternion* kmQuaternionMultiply(kmQuaternion* pOut, + const kmQuaternion* q1, + const kmQuaternion* q2) +{ + pOut->w = q1->w * q2->w - q1->x * q2->x - q1->y * q2->y - q1->z * q2->z; + pOut->x = q1->w * q2->x + q1->x * q2->w + q1->y * q2->z - q1->z * q2->y; + pOut->y = q1->w * q2->y + q1->y * q2->w + q1->z * q2->x - q1->x * q2->z; + pOut->z = q1->w * q2->z + q1->z * q2->w + q1->x * q2->y - q1->y * q2->x; + + return pOut; +} + +///< Normalizes a quaternion +kmQuaternion* kmQuaternionNormalize(kmQuaternion* pOut, + const kmQuaternion* pIn) +{ + kmScalar length = kmQuaternionLength(pIn); + assert(fabs(length) > kmEpsilon); + kmQuaternionScale(pOut, pIn, 1.0f / length); + + return pOut; +} + +///< Rotates a quaternion around an axis +kmQuaternion* kmQuaternionRotationAxis(kmQuaternion* pOut, + const kmVec3* pV, + kmScalar angle) +{ + kmScalar rad = angle * 0.5f; + kmScalar scale = sinf(rad); + + pOut->w = cosf(rad); + pOut->x = pV->x * scale; + pOut->y = pV->y * scale; + pOut->z = pV->z * scale; + + return pOut; +} + +///< Creates a quaternion from a rotation matrix +kmQuaternion* kmQuaternionRotationMatrix(kmQuaternion* pOut, + const kmMat3* pIn) +{ +/* +Note: The OpenGL matrices are transposed from the description below +taken from the Matrix and Quaternion FAQ + + if ( mat[0] > mat[5] && mat[0] > mat[10] ) { // Column 0: + S = sqrt( 1.0 + mat[0] - mat[5] - mat[10] ) * 2; + X = 0.25 * S; + Y = (mat[4] + mat[1] ) / S; + Z = (mat[2] + mat[8] ) / S; + W = (mat[9] - mat[6] ) / S; + } else if ( mat[5] > mat[10] ) { // Column 1: + S = sqrt( 1.0 + mat[5] - mat[0] - mat[10] ) * 2; + X = (mat[4] + mat[1] ) / S; + Y = 0.25 * S; + Z = (mat[9] + mat[6] ) / S; + W = (mat[2] - mat[8] ) / S; + } else { // Column 2: + S = sqrt( 1.0 + mat[10] - mat[0] - mat[5] ) * 2; + X = (mat[2] + mat[8] ) / S; + Y = (mat[9] + mat[6] ) / S; + Z = 0.25 * S; + W = (mat[4] - mat[1] ) / S; + } +*/ + + float x, y, z, w; + float *pMatrix = NULL; + float m4x4[16] = {0}; + float scale = 0.0f; + float diagonal = 0.0f; + + if(!pIn) { + return NULL; + } + +/* 0 3 6 + 1 4 7 + 2 5 8 + + 0 1 2 3 + 4 5 6 7 + 8 9 10 11 + 12 13 14 15*/ + + m4x4[0] = pIn->mat[0]; + m4x4[1] = pIn->mat[3]; + m4x4[2] = pIn->mat[6]; + m4x4[4] = pIn->mat[1]; + m4x4[5] = pIn->mat[4]; + m4x4[6] = pIn->mat[7]; + m4x4[8] = pIn->mat[2]; + m4x4[9] = pIn->mat[5]; + m4x4[10] = pIn->mat[8]; + m4x4[15] = 1; + pMatrix = &m4x4[0]; + + diagonal = pMatrix[0] + pMatrix[5] + pMatrix[10] + 1; + + if(diagonal > kmEpsilon) { + // Calculate the scale of the diagonal + scale = (float)sqrt(diagonal ) * 2; + + // Calculate the x, y, x and w of the quaternion through the respective equation + x = ( pMatrix[9] - pMatrix[6] ) / scale; + y = ( pMatrix[2] - pMatrix[8] ) / scale; + z = ( pMatrix[4] - pMatrix[1] ) / scale; + w = 0.25f * scale; + } + else + { + // If the first element of the diagonal is the greatest value + if ( pMatrix[0] > pMatrix[5] && pMatrix[0] > pMatrix[10] ) + { + // Find the scale according to the first element, and double that value + scale = (float)sqrt( 1.0f + pMatrix[0] - pMatrix[5] - pMatrix[10] ) * 2.0f; + + // Calculate the x, y, x and w of the quaternion through the respective equation + x = 0.25f * scale; + y = (pMatrix[4] + pMatrix[1] ) / scale; + z = (pMatrix[2] + pMatrix[8] ) / scale; + w = (pMatrix[9] - pMatrix[6] ) / scale; + } + // Else if the second element of the diagonal is the greatest value + else if (pMatrix[5] > pMatrix[10]) + { + // Find the scale according to the second element, and double that value + scale = (float)sqrt( 1.0f + pMatrix[5] - pMatrix[0] - pMatrix[10] ) * 2.0f; + + // Calculate the x, y, x and w of the quaternion through the respective equation + x = (pMatrix[4] + pMatrix[1] ) / scale; + y = 0.25f * scale; + z = (pMatrix[9] + pMatrix[6] ) / scale; + w = (pMatrix[2] - pMatrix[8] ) / scale; + } + // Else the third element of the diagonal is the greatest value + else + { + // Find the scale according to the third element, and double that value + scale = (float)sqrt( 1.0f + pMatrix[10] - pMatrix[0] - pMatrix[5] ) * 2.0f; + + // Calculate the x, y, x and w of the quaternion through the respective equation + x = (pMatrix[2] + pMatrix[8] ) / scale; + y = (pMatrix[9] + pMatrix[6] ) / scale; + z = 0.25f * scale; + w = (pMatrix[4] - pMatrix[1] ) / scale; + } + } + + pOut->x = x; + pOut->y = y; + pOut->z = z; + pOut->w = w; + + return pOut; + +#if 0 + kmScalar T = pIn->mat[0] + pIn->mat[5] + pIn->mat[10]; + + if (T > kmEpsilon) { + //If the trace is greater than zero we always use this calculation: + /* S = sqrt(T) * 2; + X = ( mat[9] - mat[6] ) / S; + Y = ( mat[2] - mat[8] ) / S; + Z = ( mat[4] - mat[1] ) / S; + W = 0.25 * S;*/ + +/* kmScalar s = sqrtf(T) * 2; + pOut->x = (pIn->mat[9] - pIn->mat[6]) / s; + pOut->y = (pIn->mat[8] - pIn->mat[2]) / s; + pOut->z = (pIn->mat[1] - pIn->mat[4]) / s; + pOut->w = 0.25f * s; + + kmQuaternionNormalize(pOut, pOut); + return pOut; + } + + //Otherwise the calculation depends on which major diagonal element has the greatest value. + + if (pIn->mat[0] > pIn->mat[5] && pIn->mat[0] > pIn->mat[10]) { + kmScalar s = sqrtf(1 + pIn->mat[0] - pIn->mat[5] - pIn->mat[10]) * 2; + pOut->x = 0.25f * s; + pOut->y = (pIn->mat[1] + pIn->mat[4]) / s; + pOut->z = (pIn->mat[8] + pIn->mat[2]) / s; + pOut->w = (pIn->mat[9] - pIn->mat[6]) / s; + } + else if (pIn->mat[5] > pIn->mat[10]) { + kmScalar s = sqrtf(1 + pIn->mat[5] - pIn->mat[0] - pIn->mat[10]) * 2; + pOut->x = (pIn->mat[1] + pIn->mat[4]) / s; + pOut->y = 0.25f * s; + pOut->z = (pIn->mat[9] + pIn->mat[6]) / s; + pOut->w = (pIn->mat[8] - pIn->mat[2]) / s; + } + else { + kmScalar s = sqrt(1.0f + pIn->mat[10] - pIn->mat[0] - pIn->mat[5]) * 2.0f; + pOut->x = (pIn->mat[8] + pIn->mat[2] ) / s; + pOut->y = (pIn->mat[6] + pIn->mat[9] ) / s; + pOut->z = 0.25f * s; + pOut->w = (pIn->mat[1] - pIn->mat[4] ) / s; + } + + kmQuaternionNormalize(pOut, pOut); + return pOut;*/ +#endif // 0 +} + +///< Create a quaternion from yaw, pitch and roll +kmQuaternion* kmQuaternionRotationYawPitchRoll(kmQuaternion* pOut, + kmScalar yaw, + kmScalar pitch, + kmScalar roll) +{ + kmScalar ex, ey, ez; // temp half euler angles + kmScalar cr, cp, cy, sr, sp, sy, cpcy, spsy; // temp vars in roll,pitch yaw + + ex = kmDegreesToRadians(pitch) / 2.0f; // convert to rads and half them + ey = kmDegreesToRadians(yaw) / 2.0f; + ez = kmDegreesToRadians(roll) / 2.0f; + + cr = cosf(ex); + cp = cosf(ey); + cy = cosf(ez); + + sr = sinf(ex); + sp = sinf(ey); + sy = sinf(ez); + + cpcy = cp * cy; + spsy = sp * sy; + + pOut->w = cr * cpcy + sr * spsy; + + pOut->x = sr * cpcy - cr * spsy; + pOut->y = cr * sp * cy + sr * cp * sy; + pOut->z = cr * cp * sy - sr * sp * cy; + + kmQuaternionNormalize(pOut, pOut); + + return pOut; +} + +///< Interpolate between 2 quaternions +kmQuaternion* kmQuaternionSlerp(kmQuaternion* pOut, + const kmQuaternion* q1, + const kmQuaternion* q2, + kmScalar t) +{ + + /*float CosTheta = Q0.DotProd(Q1); + float Theta = acosf(CosTheta); + float SinTheta = sqrtf(1.0f-CosTheta*CosTheta); + + float Sin_T_Theta = sinf(T*Theta)/SinTheta; + float Sin_OneMinusT_Theta = sinf((1.0f-T)*Theta)/SinTheta; + + Quaternion Result = Q0*Sin_OneMinusT_Theta; + Result += (Q1*Sin_T_Theta); + + return Result;*/ + + if (q1->x == q2->x && + q1->y == q2->y && + q1->z == q2->z && + q1->w == q2->w) { + + pOut->x = q1->x; + pOut->y = q1->y; + pOut->z = q1->z; + pOut->w = q1->w; + + return pOut; + } + + kmScalar ct = kmQuaternionDot(q1, q2); + kmScalar theta = acosf(ct); + kmScalar st = sqrtf(1.0 - kmSQR(ct)); + + kmScalar stt = sinf(t * theta) / st; + kmScalar somt = sinf((1.0 - t) * theta) / st; + + kmQuaternion temp, temp2; + kmQuaternionScale(&temp, q1, somt); + kmQuaternionScale(&temp2, q2, stt); + kmQuaternionAdd(pOut, &temp, &temp2); + + return pOut; +} + +///< Get the axis and angle of rotation from a quaternion +void kmQuaternionToAxisAngle(const kmQuaternion* pIn, + kmVec3* pAxis, + kmScalar* pAngle) +{ + kmScalar tempAngle; // temp angle + kmScalar scale; // temp vars + + tempAngle = acosf(pIn->w); + scale = sqrtf(kmSQR(pIn->x) + kmSQR(pIn->y) + kmSQR(pIn->z)); + + if (((scale > -kmEpsilon) && scale < kmEpsilon) + || (scale < 2*kmPI + kmEpsilon && scale > 2*kmPI - kmEpsilon)) // angle is 0 or 360 so just simply set axis to 0,0,1 with angle 0 + { + *pAngle = 0.0f; + + pAxis->x = 0.0f; + pAxis->y = 0.0f; + pAxis->z = 1.0f; + } + else + { + *pAngle = tempAngle * 2.0f; // angle in radians + + pAxis->x = pIn->x / scale; + pAxis->y = pIn->y / scale; + pAxis->z = pIn->z / scale; + kmVec3Normalize(pAxis, pAxis); + } +} + +kmQuaternion* kmQuaternionScale(kmQuaternion* pOut, + const kmQuaternion* pIn, + kmScalar s) +{ + pOut->x = pIn->x * s; + pOut->y = pIn->y * s; + pOut->z = pIn->z * s; + pOut->w = pIn->w * s; + + return pOut; +} + +kmQuaternion* kmQuaternionAssign(kmQuaternion* pOut, const kmQuaternion* pIn) +{ + memcpy(pOut, pIn, sizeof(float) * 4); + + return pOut; +} + +kmQuaternion* kmQuaternionAdd(kmQuaternion* pOut, const kmQuaternion* pQ1, const kmQuaternion* pQ2) +{ + pOut->x = pQ1->x + pQ2->x; + pOut->y = pQ1->y + pQ2->y; + pOut->z = pQ1->z + pQ2->z; + pOut->w = pQ1->w + pQ2->w; + + return pOut; +} + +/** Adapted from the OGRE engine! + + Gets the shortest arc quaternion to rotate this vector to the destination + vector. +@remarks + If you call this with a dest vector that is close to the inverse + of this vector, we will rotate 180 degrees around the 'fallbackAxis' + (if specified, or a generated axis if not) since in this case + ANY axis of rotation is valid. +*/ + +kmQuaternion* kmQuaternionRotationBetweenVec3(kmQuaternion* pOut, const kmVec3* vec1, const kmVec3* vec2, const kmVec3* fallback) { + + kmVec3 v1, v2; + kmScalar a; + + kmVec3Assign(&v1, vec1); + kmVec3Assign(&v2, vec2); + + kmVec3Normalize(&v1, &v1); + kmVec3Normalize(&v2, &v2); + + a = kmVec3Dot(&v1, &v2); + + if (a >= 1.0) { + kmQuaternionIdentity(pOut); + return pOut; + } + + if (a < (1e-6f - 1.0f)) { + if (fabs(kmVec3LengthSq(fallback)) < kmEpsilon) { + kmQuaternionRotationAxis(pOut, fallback, kmPI); + } else { + kmVec3 axis; + kmVec3 X; + X.x = 1.0; + X.y = 0.0; + X.z = 0.0; + + + kmVec3Cross(&axis, &X, vec1); + + //If axis is zero + if (fabs(kmVec3LengthSq(&axis)) < kmEpsilon) { + kmVec3 Y; + Y.x = 0.0; + Y.y = 1.0; + Y.z = 0.0; + + kmVec3Cross(&axis, &Y, vec1); + } + + kmVec3Normalize(&axis, &axis); + + kmQuaternionRotationAxis(pOut, &axis, kmPI); + } + } else { + kmScalar s = sqrtf((1+a) * 2); + kmScalar invs = 1 / s; + + kmVec3 c; + kmVec3Cross(&c, &v1, &v2); + + pOut->x = c.x * invs; + pOut->y = c.y * invs; + pOut->z = c.z * invs; + pOut->w = s * 0.5f; + + kmQuaternionNormalize(pOut, pOut); + } + + return pOut; + +} + +kmVec3* kmQuaternionMultiplyVec3(kmVec3* pOut, const kmQuaternion* q, const kmVec3* v) { + kmVec3 uv, uuv, qvec; + + qvec.x = q->x; + qvec.y = q->y; + qvec.z = q->z; + + kmVec3Cross(&uv, &qvec, v); + kmVec3Cross(&uuv, &qvec, &uv); + + kmVec3Scale(&uv, &uv, (2.0f * q->w)); + kmVec3Scale(&uuv, &uuv, 2.0f); + + kmVec3Add(pOut, v, &uv); + kmVec3Add(pOut, pOut, &uuv); + + return pOut; +} + diff --git a/cocos2d/kazmath/src/ray2.c b/cocos2d/kazmath/src/ray2.c new file mode 100755 index 0000000..514752f --- /dev/null +++ b/cocos2d/kazmath/src/ray2.c @@ -0,0 +1,184 @@ +#include +#include +#include "kazmath/ray2.h" + +void kmRay2Fill(kmRay2* ray, kmScalar px, kmScalar py, kmScalar vx, kmScalar vy) { + ray->start.x = px; + ray->start.y = py; + ray->dir.x = vx; + ray->dir.y = vy; +} + +kmBool kmRay2IntersectLineSegment(const kmRay2* ray, const kmVec2* p1, const kmVec2* p2, kmVec2* intersection) { + + float x1 = ray->start.x; + float y1 = ray->start.y; + float x2 = ray->start.x + ray->dir.x; + float y2 = ray->start.y + ray->dir.y; + float x3 = p1->x; + float y3 = p1->y; + float x4 = p2->x; + float y4 = p2->y; + + float denom = (y4 -y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); + + //If denom is zero, the lines are parallel + if(denom > -kmEpsilon && denom < kmEpsilon) { + return KM_FALSE; + } + + float ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom; +// float ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom; + + float x = x1 + ua * (x2 - x1); + float y = y1 + ua * (y2 - y1); + + if(x < min(p1->x, p2->x) - kmEpsilon || + x > max(p1->x, p2->x) + kmEpsilon || + y < min(p1->y, p2->y) - kmEpsilon || + y > max(p1->y, p2->y) + kmEpsilon) { + //Outside of line + //printf("Outside of line, %f %f (%f %f)(%f, %f)\n", x, y, p1->x, p1->y, p2->x, p2->y); + return KM_FALSE; + } + + if(x < min(x1, x2) - kmEpsilon || + x > max(x1, x2) + kmEpsilon || + y < min(y1, y2) - kmEpsilon || + y > max(y1, y2) + kmEpsilon) { + //printf("Outside of ray, %f %f (%f %f)(%f, %f)\n", x, y, x1, y1, x2, y2); + return KM_FALSE; + } + + intersection->x = x; + intersection->y = y; + + return KM_TRUE; + + +/* + kmScalar A1, B1, C1; + kmScalar A2, B2, C2; + + A1 = ray->dir.y; + B1 = ray->dir.x; + C1 = A1 * ray->start.x + B1 * ray->start.y; + + A2 = p2->y - p1->y; + B2 = p2->x - p1->x; + C2 = A2 * p1->x + B2 * p1->y; + + double det = (A1 * B2) - (A2 * B1); + if(det == 0) { + printf("Parallel\n"); + return KM_FALSE; + } + + double x = (B2*C1 - B1*C2) / det; + double y = (A1*C2 - A2*C1) / det; + + if(x < min(p1->x, p2->x) - kmEpsilon || + x > max(p1->x, p2->x) + kmEpsilon || + y < min(p1->y, p2->y) - kmEpsilon || + y > max(p1->y, p2->y) + kmEpsilon) { + //Outside of line + printf("Outside of line, %f %f (%f %f)(%f, %f)\n", x, y, p1->x, p1->y, p2->x, p2->y); + return KM_FALSE; + } + + kmScalar x1 = ray->start.x; + kmScalar x2 = ray->start.x + ray->dir.x; + + kmScalar y1 = ray->start.y; + kmScalar y2 = ray->start.y + ray->dir.y; + + if(x < min(x1, x2) - kmEpsilon || + x > max(x1, x2) + kmEpsilon || + y < min(y1, y2) - kmEpsilon || + y > max(y1, y2) + kmEpsilon) { + printf("Outside of ray, %f %f (%f %f)(%f, %f)\n", x, y, x1, y1, x2, y2); + return KM_FALSE; + } + + intersection->x = x; + intersection->y = y; + + return KM_TRUE;*/ +} + +void calculate_line_normal(kmVec2 p1, kmVec2 p2, kmVec2* normal_out) { + kmVec2 tmp; + kmVec2Subtract(&tmp, &p2, &p1); //Get direction vector + + normal_out->x = -tmp.y; + normal_out->y = tmp.x; + kmVec2Normalize(normal_out, normal_out); + + //TODO: should check that the normal is pointing out of the triangle +} + +kmBool kmRay2IntersectTriangle(const kmRay2* ray, const kmVec2* p1, const kmVec2* p2, const kmVec2* p3, kmVec2* intersection, kmVec2* normal_out) { + kmVec2 intersect; + kmVec2 final_intersect; + kmVec2 normal; + kmScalar distance = 10000.0f; + kmBool intersected = KM_FALSE; + + if(kmRay2IntersectLineSegment(ray, p1, p2, &intersect)) { + intersected = KM_TRUE; + + kmVec2 tmp; + kmScalar this_distance = kmVec2Length(kmVec2Subtract(&tmp, &intersect, &ray->start)); + if(this_distance < distance) { + final_intersect.x = intersect.x; + final_intersect.y = intersect.y; + distance = this_distance; + + calculate_line_normal(*p1, *p2, &normal); + } + } + + if(kmRay2IntersectLineSegment(ray, p2, p3, &intersect)) { + intersected = KM_TRUE; + + kmVec2 tmp; + kmScalar this_distance = kmVec2Length(kmVec2Subtract(&tmp, &intersect, &ray->start)); + if(this_distance < distance) { + final_intersect.x = intersect.x; + final_intersect.y = intersect.y; + distance = this_distance; + + calculate_line_normal(*p2, *p3, &normal); + } + } + + if(kmRay2IntersectLineSegment(ray, p3, p1, &intersect)) { + intersected = KM_TRUE; + + kmVec2 tmp; + kmScalar this_distance = kmVec2Length(kmVec2Subtract(&tmp, &intersect, &ray->start)); + if(this_distance < distance) { + final_intersect.x = intersect.x; + final_intersect.y = intersect.y; + distance = this_distance; + + calculate_line_normal(*p3, *p1, &normal); + } + } + + if(intersected) { + intersection->x = final_intersect.x; + intersection->y = final_intersect.y; + if(normal_out) { + normal_out->x = normal.x; + normal_out->y = normal.y; + } + } + + return intersected; +} + +kmBool kmRay2IntersectCircle(const kmRay2* ray, const kmVec2 centre, const kmScalar radius, kmVec2* intersection) { + assert(0 && "Not implemented"); + return 0; +} diff --git a/cocos2d/kazmath/src/utility.c b/cocos2d/kazmath/src/utility.c new file mode 100755 index 0000000..1d89aca --- /dev/null +++ b/cocos2d/kazmath/src/utility.c @@ -0,0 +1,59 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#include "kazmath/utility.h" + +/** + * Returns the square of s (e.g. s*s) + */ +kmScalar kmSQR(kmScalar s) { + return s*s; +} + +/** + * Returns degrees as radians. + */ +kmScalar kmDegreesToRadians(kmScalar degrees) { + return degrees * kmPIOver180; +} + +/** + * Returns radians as degrees + */ +kmScalar kmRadiansToDegrees(kmScalar radians) { + return radians * kmPIUnder180; +} + +kmScalar min(kmScalar lhs, kmScalar rhs) { + return (lhs < rhs)? lhs : rhs; +} + +kmScalar max(kmScalar lhs, kmScalar rhs) { + return (lhs > rhs)? lhs : rhs; +} + +kmBool kmAlmostEqual(kmScalar lhs, kmScalar rhs) { + return (lhs + kmEpsilon > rhs && lhs - kmEpsilon < rhs); +} diff --git a/cocos2d/kazmath/src/vec2.c b/cocos2d/kazmath/src/vec2.c new file mode 100755 index 0000000..1a9511e --- /dev/null +++ b/cocos2d/kazmath/src/vec2.c @@ -0,0 +1,118 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +#include +#include + +#include "kazmath/mat3.h" +#include "kazmath/vec2.h" +#include "kazmath/utility.h" + +kmVec2* kmVec2Fill(kmVec2* pOut, kmScalar x, kmScalar y) +{ + pOut->x = x; + pOut->y = y; + return pOut; +} + +kmScalar kmVec2Length(const kmVec2* pIn) +{ + return sqrtf(kmSQR(pIn->x) + kmSQR(pIn->y)); +} + +kmScalar kmVec2LengthSq(const kmVec2* pIn) +{ + return kmSQR(pIn->x) + kmSQR(pIn->y); +} + +kmVec2* kmVec2Normalize(kmVec2* pOut, const kmVec2* pIn) +{ + kmScalar l = 1.0f / kmVec2Length(pIn); + + kmVec2 v; + v.x = pIn->x * l; + v.y = pIn->y * l; + + pOut->x = v.x; + pOut->y = v.y; + + return pOut; +} + +kmVec2* kmVec2Add(kmVec2* pOut, const kmVec2* pV1, const kmVec2* pV2) +{ + pOut->x = pV1->x + pV2->x; + pOut->y = pV1->y + pV2->y; + + return pOut; +} + +kmScalar kmVec2Dot(const kmVec2* pV1, const kmVec2* pV2) +{ + return pV1->x * pV2->x + pV1->y * pV2->y; +} + +kmVec2* kmVec2Subtract(kmVec2* pOut, const kmVec2* pV1, const kmVec2* pV2) +{ + pOut->x = pV1->x - pV2->x; + pOut->y = pV1->y - pV2->y; + + return pOut; +} + +kmVec2* kmVec2Transform(kmVec2* pOut, const kmVec2* pV, const kmMat3* pM) +{ + kmVec2 v; + + v.x = pV->x * pM->mat[0] + pV->y * pM->mat[3] + pM->mat[6]; + v.y = pV->x * pM->mat[1] + pV->y * pM->mat[4] + pM->mat[7]; + + pOut->x = v.x; + pOut->y = v.y; + + return pOut; +} + +kmVec2* kmVec2TransformCoord(kmVec2* pOut, const kmVec2* pV, const kmMat3* pM) +{ + assert(0); + return NULL; +} + +kmVec2* kmVec2Scale(kmVec2* pOut, const kmVec2* pIn, const kmScalar s) +{ + pOut->x = pIn->x * s; + pOut->y = pIn->y * s; + + return pOut; +} + +int kmVec2AreEqual(const kmVec2* p1, const kmVec2* p2) +{ + return ( + (p1->x < p2->x + kmEpsilon && p1->x > p2->x - kmEpsilon) && + (p1->y < p2->y + kmEpsilon && p1->y > p2->y - kmEpsilon) + ); +} diff --git a/cocos2d/kazmath/src/vec3.c b/cocos2d/kazmath/src/vec3.c new file mode 100755 index 0000000..c520900 --- /dev/null +++ b/cocos2d/kazmath/src/vec3.c @@ -0,0 +1,310 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + +/** + * @file vec3.c + */ + +#include +#include + +#include "kazmath/utility.h" +#include "kazmath/vec4.h" +#include "kazmath/mat4.h" +#include "kazmath/vec3.h" + +/** + * Fill a kmVec3 structure using 3 floating point values + * The result is store in pOut, returns pOut + */ +kmVec3* kmVec3Fill(kmVec3* pOut, kmScalar x, kmScalar y, kmScalar z) +{ + pOut->x = x; + pOut->y = y; + pOut->z = z; + return pOut; +} + + +/** + * Returns the length of the vector + */ +kmScalar kmVec3Length(const kmVec3* pIn) +{ + return sqrtf(kmSQR(pIn->x) + kmSQR(pIn->y) + kmSQR(pIn->z)); +} + +/** + * Returns the square of the length of the vector + */ +kmScalar kmVec3LengthSq(const kmVec3* pIn) +{ + return kmSQR(pIn->x) + kmSQR(pIn->y) + kmSQR(pIn->z); +} + + /** + * Returns the vector passed in set to unit length + * the result is stored in pOut. + */ +kmVec3* kmVec3Normalize(kmVec3* pOut, const kmVec3* pIn) +{ + kmScalar l = 1.0f / kmVec3Length(pIn); + + kmVec3 v; + v.x = pIn->x * l; + v.y = pIn->y * l; + v.z = pIn->z * l; + + pOut->x = v.x; + pOut->y = v.y; + pOut->z = v.z; + + return pOut; +} + +/** + * Returns a vector perpendicular to 2 other vectors. + * The result is stored in pOut. + */ +kmVec3* kmVec3Cross(kmVec3* pOut, const kmVec3* pV1, const kmVec3* pV2) +{ + + kmVec3 v; + + v.x = (pV1->y * pV2->z) - (pV1->z * pV2->y); + v.y = (pV1->z * pV2->x) - (pV1->x * pV2->z); + v.z = (pV1->x * pV2->y) - (pV1->y * pV2->x); + + pOut->x = v.x; + pOut->y = v.y; + pOut->z = v.z; + + return pOut; +} + +/** + * Returns the cosine of the angle between 2 vectors + */ +kmScalar kmVec3Dot(const kmVec3* pV1, const kmVec3* pV2) +{ + return ( pV1->x * pV2->x + + pV1->y * pV2->y + + pV1->z * pV2->z ); +} + +/** + * Adds 2 vectors and returns the result. The resulting + * vector is stored in pOut. + */ +kmVec3* kmVec3Add(kmVec3* pOut, const kmVec3* pV1, const kmVec3* pV2) +{ + kmVec3 v; + + v.x = pV1->x + pV2->x; + v.y = pV1->y + pV2->y; + v.z = pV1->z + pV2->z; + + pOut->x = v.x; + pOut->y = v.y; + pOut->z = v.z; + + return pOut; +} + + /** + * Subtracts 2 vectors and returns the result. The result is stored in + * pOut. + */ +kmVec3* kmVec3Subtract(kmVec3* pOut, const kmVec3* pV1, const kmVec3* pV2) +{ + kmVec3 v; + + v.x = pV1->x - pV2->x; + v.y = pV1->y - pV2->y; + v.z = pV1->z - pV2->z; + + pOut->x = v.x; + pOut->y = v.y; + pOut->z = v.z; + + return pOut; +} + + /** + * Transforms vector (x, y, z, 1) by a given matrix. The result + * is stored in pOut. pOut is returned. + */ +kmVec3* kmVec3Transform(kmVec3* pOut, const kmVec3* pV, const kmMat4* pM) +{ + /* + a = (Vx, Vy, Vz, 1) + b = (a×M)T + Out = (bx, by, bz) + */ + + kmVec3 v; + + v.x = pV->x * pM->mat[0] + pV->y * pM->mat[4] + pV->z * pM->mat[8] + pM->mat[12]; + v.y = pV->x * pM->mat[1] + pV->y * pM->mat[5] + pV->z * pM->mat[9] + pM->mat[13]; + v.z = pV->x * pM->mat[2] + pV->y * pM->mat[6] + pV->z * pM->mat[10] + pM->mat[14]; + + pOut->x = v.x; + pOut->y = v.y; + pOut->z = v.z; + + return pOut; +} + +kmVec3* kmVec3InverseTransform(kmVec3* pOut, const kmVec3* pVect, const kmMat4* pM) +{ + kmVec3 v1, v2; + + v1.x = pVect->x - pM->mat[12]; + v1.y = pVect->y - pM->mat[13]; + v1.z = pVect->z - pM->mat[14]; + + v2.x = v1.x * pM->mat[0] + v1.y * pM->mat[1] + v1.z * pM->mat[2]; + v2.y = v1.x * pM->mat[4] + v1.y * pM->mat[5] + v1.z * pM->mat[6]; + v2.z = v1.x * pM->mat[8] + v1.y * pM->mat[9] + v1.z * pM->mat[10]; + + pOut->x = v2.x; + pOut->y = v2.y; + pOut->z = v2.z; + + return pOut; +} + +kmVec3* kmVec3InverseTransformNormal(kmVec3* pOut, const kmVec3* pVect, const kmMat4* pM) +{ + kmVec3 v; + + v.x = pVect->x * pM->mat[0] + pVect->y * pM->mat[1] + pVect->z * pM->mat[2]; + v.y = pVect->x * pM->mat[4] + pVect->y * pM->mat[5] + pVect->z * pM->mat[6]; + v.z = pVect->x * pM->mat[8] + pVect->y * pM->mat[9] + pVect->z * pM->mat[10]; + + pOut->x = v.x; + pOut->y = v.y; + pOut->z = v.z; + + return pOut; +} + + +kmVec3* kmVec3TransformCoord(kmVec3* pOut, const kmVec3* pV, const kmMat4* pM) +{ + /* + a = (Vx, Vy, Vz, 1) + b = (a×M)T + Out = 1⁄bw(bx, by, bz) + */ + + kmVec4 v; + kmVec4 inV; + kmVec4Fill(&inV, pV->x, pV->y, pV->z, 1.0); + + kmVec4Transform(&v, &inV,pM); + + pOut->x = v.x / v.w; + pOut->y = v.y / v.w; + pOut->z = v.z / v.w; + + return pOut; +} + +kmVec3* kmVec3TransformNormal(kmVec3* pOut, const kmVec3* pV, const kmMat4* pM) +{ +/* + a = (Vx, Vy, Vz, 0) + b = (a×M)T + Out = (bx, by, bz) +*/ + //Omits the translation, only scaling + rotating + kmVec3 v; + + v.x = pV->x * pM->mat[0] + pV->y * pM->mat[4] + pV->z * pM->mat[8]; + v.y = pV->x * pM->mat[1] + pV->y * pM->mat[5] + pV->z * pM->mat[9]; + v.z = pV->x * pM->mat[2] + pV->y * pM->mat[6] + pV->z * pM->mat[10]; + + pOut->x = v.x; + pOut->y = v.y; + pOut->z = v.z; + + return pOut; + +} + +/** + * Scales a vector to length s. Does not normalize first, + * you should do that! + */ +kmVec3* kmVec3Scale(kmVec3* pOut, const kmVec3* pIn, const kmScalar s) +{ + pOut->x = pIn->x * s; + pOut->y = pIn->y * s; + pOut->z = pIn->z * s; + + return pOut; +} + +/** + * Returns KM_TRUE if the 2 vectors are approximately equal + */ +int kmVec3AreEqual(const kmVec3* p1, const kmVec3* p2) +{ + if ((p1->x < (p2->x + kmEpsilon) && p1->x > (p2->x - kmEpsilon)) && + (p1->y < (p2->y + kmEpsilon) && p1->y > (p2->y - kmEpsilon)) && + (p1->z < (p2->z + kmEpsilon) && p1->z > (p2->z - kmEpsilon))) { + return 1; + } + + return 0; +} + +/** + * Assigns pIn to pOut. Returns pOut. If pIn and pOut are the same + * then nothing happens but pOut is still returned + */ +kmVec3* kmVec3Assign(kmVec3* pOut, const kmVec3* pIn) { + if (pOut == pIn) { + return pOut; + } + + pOut->x = pIn->x; + pOut->y = pIn->y; + pOut->z = pIn->z; + + return pOut; +} + +/** + * Sets all the elements of pOut to zero. Returns pOut. + */ +kmVec3* kmVec3Zero(kmVec3* pOut) { + pOut->x = 0.0f; + pOut->y = 0.0f; + pOut->z = 0.0f; + + return pOut; +} diff --git a/cocos2d/kazmath/src/vec4.c b/cocos2d/kazmath/src/vec4.c new file mode 100755 index 0000000..4842e46 --- /dev/null +++ b/cocos2d/kazmath/src/vec4.c @@ -0,0 +1,154 @@ +/* +Copyright (c) 2008, Luke Benstead. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. +*/ + + +#include +#include + +#include "kazmath/utility.h" +#include "kazmath/vec4.h" +#include "kazmath/mat4.h" + + +kmVec4* kmVec4Fill(kmVec4* pOut, kmScalar x, kmScalar y, kmScalar z, kmScalar w) +{ + pOut->x = x; + pOut->y = y; + pOut->z = z; + pOut->w = w; + return pOut; +} + + +/// Adds 2 4D vectors together. The result is store in pOut, the function returns +/// pOut so that it can be nested in another function. +kmVec4* kmVec4Add(kmVec4* pOut, const kmVec4* pV1, const kmVec4* pV2) { + pOut->x = pV1->x + pV2->x; + pOut->y = pV1->y + pV2->y; + pOut->z = pV1->z + pV2->z; + pOut->w = pV1->w + pV2->w; + + return pOut; +} + +/// Returns the dot product of 2 4D vectors +kmScalar kmVec4Dot(const kmVec4* pV1, const kmVec4* pV2) { + return ( pV1->x * pV2->x + + pV1->y * pV2->y + + pV1->z * pV2->z + + pV1->w * pV2->w ); +} + +/// Returns the length of a 4D vector, this uses a sqrt so if the squared length will do use +/// kmVec4LengthSq +kmScalar kmVec4Length(const kmVec4* pIn) { + return sqrtf(kmSQR(pIn->x) + kmSQR(pIn->y) + kmSQR(pIn->z) + kmSQR(pIn->w)); +} + +/// Returns the length of the 4D vector squared. +kmScalar kmVec4LengthSq(const kmVec4* pIn) { + return kmSQR(pIn->x) + kmSQR(pIn->y) + kmSQR(pIn->z) + kmSQR(pIn->w); +} + +/// Returns the interpolation of 2 4D vectors based on t. Currently not implemented! +kmVec4* kmVec4Lerp(kmVec4* pOut, const kmVec4* pV1, const kmVec4* pV2, kmScalar t) { + assert(0); + return pOut; +} + +/// Normalizes a 4D vector. The result is stored in pOut. pOut is returned +kmVec4* kmVec4Normalize(kmVec4* pOut, const kmVec4* pIn) { + kmScalar l = 1.0f / kmVec4Length(pIn); + + pOut->x *= l; + pOut->y *= l; + pOut->z *= l; + pOut->w *= l; + + return pOut; +} + +/// Scales a vector to the required length. This performs a Normalize before multiplying by S. +kmVec4* kmVec4Scale(kmVec4* pOut, const kmVec4* pIn, const kmScalar s) { + kmVec4Normalize(pOut, pIn); + + pOut->x *= s; + pOut->y *= s; + pOut->z *= s; + pOut->w *= s; + return pOut; +} + +/// Subtracts one 4D pV2 from pV1. The result is stored in pOut. pOut is returned +kmVec4* kmVec4Subtract(kmVec4* pOut, const kmVec4* pV1, const kmVec4* pV2) { + pOut->x = pV1->x - pV2->x; + pOut->y = pV1->y - pV2->y; + pOut->z = pV1->z - pV2->z; + pOut->w = pV1->w - pV2->w; + + return pOut; +} + +/// Transforms a 4D vector by a matrix, the result is stored in pOut, and pOut is returned. +kmVec4* kmVec4Transform(kmVec4* pOut, const kmVec4* pV, const kmMat4* pM) { + pOut->x = pV->x * pM->mat[0] + pV->y * pM->mat[4] + pV->z * pM->mat[8] + pV->w * pM->mat[12]; + pOut->y = pV->x * pM->mat[1] + pV->y * pM->mat[5] + pV->z * pM->mat[9] + pV->w * pM->mat[13]; + pOut->z = pV->x * pM->mat[2] + pV->y * pM->mat[6] + pV->z * pM->mat[10] + pV->w * pM->mat[14]; + pOut->w = pV->x * pM->mat[3] + pV->y * pM->mat[7] + pV->z * pM->mat[11] + pV->w * pM->mat[15]; + return pOut; +} + +/// Loops through an input array transforming each vec4 by the matrix. +kmVec4* kmVec4TransformArray(kmVec4* pOut, unsigned int outStride, + const kmVec4* pV, unsigned int vStride, const kmMat4* pM, unsigned int count) { + unsigned int i = 0; + //Go through all of the vectors + while (i < count) { + const kmVec4* in = pV + (i * vStride); //Get a pointer to the current input + kmVec4* out = pOut + (i * outStride); //and the current output + kmVec4Transform(out, in, pM); //Perform transform on it + ++i; + } + + return pOut; +} + +int kmVec4AreEqual(const kmVec4* p1, const kmVec4* p2) { + return ( + (p1->x < p2->x + kmEpsilon && p1->x > p2->x - kmEpsilon) && + (p1->y < p2->y + kmEpsilon && p1->y > p2->y - kmEpsilon) && + (p1->z < p2->z + kmEpsilon && p1->z > p2->z - kmEpsilon) && + (p1->w < p2->w + kmEpsilon && p1->w > p2->w - kmEpsilon) + ); +} + +kmVec4* kmVec4Assign(kmVec4* pOut, const kmVec4* pIn) { + assert(pOut != pIn); + + memcpy(pOut, pIn, sizeof(float) * 4); + + return pOut; +} + From ea2ff9516bf858d6129d5d87ea28892a1240dfaf Mon Sep 17 00:00:00 2001 From: Greg Harding Date: Sat, 8 Sep 2012 10:42:05 +1200 Subject: [PATCH 3/3] added ObjectAL audio initialisation to the app delegate --- ObjectALDemo/AppDelegate.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ObjectALDemo/AppDelegate.m b/ObjectALDemo/AppDelegate.m index c504527..92c5f8a 100644 --- a/ObjectALDemo/AppDelegate.m +++ b/ObjectALDemo/AppDelegate.m @@ -9,6 +9,7 @@ #import "AppDelegate.h" #import "IntroLayer.h" +#import "ObjectAL.h" @implementation AppController @@ -70,6 +71,10 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // Assume that PVR images have premultiplied alpha [CCTexture2D PVRImagesHavePremultipliedAlpha:YES]; + // Kick off audio initialization before our main scene begins. + // Note: This default init will cause OALSimpleAudio to take all 32 sources. + [OALSimpleAudio sharedInstance]; + // and add the scene to the stack. The director will run it when it automatically when the view is displayed. [director_ pushScene: [IntroLayer scene]];

w)qcD9=e;@%!pOBSfc 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 CFBundlePackageType APPL + CFBundleShortVersionString + 1.0 CFBundleSignature ???? CFBundleVersion @@ -35,14 +37,24 @@ UIPrerenderedIcon - UIStatusBarHidden - UIRequiredDeviceCapabilities accelerometer - opengles-1 + opengles-2 + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + diff --git a/ObjectALDemo/Resources/AssetSources.txt b/ObjectALDemo/Resources/ObjectALDemo/AssetSources.txt similarity index 100% rename from ObjectALDemo/Resources/AssetSources.txt rename to ObjectALDemo/Resources/ObjectALDemo/AssetSources.txt diff --git a/ObjectALDemo/Resources/Back.png b/ObjectALDemo/Resources/ObjectALDemo/Back.png similarity index 100% rename from ObjectALDemo/Resources/Back.png rename to ObjectALDemo/Resources/ObjectALDemo/Back.png diff --git a/ObjectALDemo/Resources/ColdFunk.caf b/ObjectALDemo/Resources/ObjectALDemo/ColdFunk.caf similarity index 100% rename from ObjectALDemo/Resources/ColdFunk.caf rename to ObjectALDemo/Resources/ObjectALDemo/ColdFunk.caf diff --git a/ObjectALDemo/Resources/Exit.png b/ObjectALDemo/Resources/ObjectALDemo/Exit.png similarity index 100% rename from ObjectALDemo/Resources/Exit.png rename to ObjectALDemo/Resources/ObjectALDemo/Exit.png diff --git a/ObjectALDemo/Resources/Ganymede.png b/ObjectALDemo/Resources/ObjectALDemo/Ganymede.png similarity index 100% rename from ObjectALDemo/Resources/Ganymede.png rename to ObjectALDemo/Resources/ObjectALDemo/Ganymede.png diff --git a/ObjectALDemo/Resources/HappyAlley.caf b/ObjectALDemo/Resources/ObjectALDemo/HappyAlley.caf similarity index 100% rename from ObjectALDemo/Resources/HappyAlley.caf rename to ObjectALDemo/Resources/ObjectALDemo/HappyAlley.caf diff --git a/ObjectALDemo/Resources/Jupiter.png b/ObjectALDemo/Resources/ObjectALDemo/Jupiter.png similarity index 100% rename from ObjectALDemo/Resources/Jupiter.png rename to ObjectALDemo/Resources/ObjectALDemo/Jupiter.png diff --git a/ObjectALDemo/Resources/Next.png b/ObjectALDemo/Resources/ObjectALDemo/Next.png similarity index 100% rename from ObjectALDemo/Resources/Next.png rename to ObjectALDemo/Resources/ObjectALDemo/Next.png diff --git a/ObjectALDemo/Resources/Pew.caf b/ObjectALDemo/Resources/ObjectALDemo/Pew.caf similarity index 100% rename from ObjectALDemo/Resources/Pew.caf rename to ObjectALDemo/Resources/ObjectALDemo/Pew.caf diff --git a/ObjectALDemo/Resources/PlanetKiller.mp3 b/ObjectALDemo/Resources/ObjectALDemo/PlanetKiller.mp3 similarity index 100% rename from ObjectALDemo/Resources/PlanetKiller.mp3 rename to ObjectALDemo/Resources/ObjectALDemo/PlanetKiller.mp3 diff --git a/ObjectALDemo/Resources/Pow.caf b/ObjectALDemo/Resources/ObjectALDemo/Pow.caf similarity index 100% rename from ObjectALDemo/Resources/Pow.caf rename to ObjectALDemo/Resources/ObjectALDemo/Pow.caf diff --git a/ObjectALDemo/Resources/RocketShip.png b/ObjectALDemo/Resources/ObjectALDemo/RocketShip.png similarity index 100% rename from ObjectALDemo/Resources/RocketShip.png rename to ObjectALDemo/Resources/ObjectALDemo/RocketShip.png diff --git a/ObjectALDemo/Resources/panel-bg.png b/ObjectALDemo/Resources/ObjectALDemo/panel-bg.png similarity index 100% rename from ObjectALDemo/Resources/panel-bg.png rename to ObjectALDemo/Resources/ObjectALDemo/panel-bg.png diff --git a/ObjectALDemo/Resources/panel-lamp-off.png b/ObjectALDemo/Resources/ObjectALDemo/panel-lamp-off.png similarity index 100% rename from ObjectALDemo/Resources/panel-lamp-off.png rename to ObjectALDemo/Resources/ObjectALDemo/panel-lamp-off.png diff --git a/ObjectALDemo/Resources/panel-lamp-on.png b/ObjectALDemo/Resources/ObjectALDemo/panel-lamp-on.png similarity index 100% rename from ObjectALDemo/Resources/panel-lamp-on.png rename to ObjectALDemo/Resources/ObjectALDemo/panel-lamp-on.png diff --git a/ObjectALDemo/Resources/panel-slider-knob.png b/ObjectALDemo/Resources/ObjectALDemo/panel-slider-knob.png similarity index 100% rename from ObjectALDemo/Resources/panel-slider-knob.png rename to ObjectALDemo/Resources/ObjectALDemo/panel-slider-knob.png diff --git a/ObjectALDemo/Resources/panel-slider-track-long.png b/ObjectALDemo/Resources/ObjectALDemo/panel-slider-track-long.png similarity index 100% rename from ObjectALDemo/Resources/panel-slider-track-long.png rename to ObjectALDemo/Resources/ObjectALDemo/panel-slider-track-long.png diff --git a/ObjectALDemo/Resources/panel-slider-track.png b/ObjectALDemo/Resources/ObjectALDemo/panel-slider-track.png similarity index 100% rename from ObjectALDemo/Resources/panel-slider-track.png rename to ObjectALDemo/Resources/ObjectALDemo/panel-slider-track.png diff --git a/ObjectALDemo/Resources/panel-trim-horiz.png b/ObjectALDemo/Resources/ObjectALDemo/panel-trim-horiz.png similarity index 100% rename from ObjectALDemo/Resources/panel-trim-horiz.png rename to ObjectALDemo/Resources/ObjectALDemo/panel-trim-horiz.png diff --git a/ObjectALDemo/Resources/panel-trim-vert.png b/ObjectALDemo/Resources/ObjectALDemo/panel-trim-vert.png similarity index 100% rename from ObjectALDemo/Resources/panel-trim-vert.png rename to ObjectALDemo/Resources/ObjectALDemo/panel-trim-vert.png diff --git a/ObjectALDemo/Resources/vu-face.png b/ObjectALDemo/Resources/ObjectALDemo/vu-face.png similarity index 100% rename from ObjectALDemo/Resources/vu-face.png rename to ObjectALDemo/Resources/ObjectALDemo/vu-face.png diff --git a/ObjectALDemo/Resources/vu-needle.png b/ObjectALDemo/Resources/ObjectALDemo/vu-needle.png similarity index 100% rename from ObjectALDemo/Resources/vu-needle.png rename to ObjectALDemo/Resources/ObjectALDemo/vu-needle.png diff --git a/ObjectALDemo/Resources/vu-shell.png b/ObjectALDemo/Resources/ObjectALDemo/vu-shell.png similarity index 100% rename from ObjectALDemo/Resources/vu-shell.png rename to ObjectALDemo/Resources/ObjectALDemo/vu-shell.png diff --git a/ObjectALDemo/Resources/fps_images-hd.png b/ObjectALDemo/Resources/fps_images-hd.png new file mode 100644 index 0000000000000000000000000000000000000000..54a299ed918917d64a4cab5bce0fba2c1035354c GIT binary patch literal 23443 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 jgPfNf7LgX|mhO`728ly=+{OF-SD&MJ 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 jgPfN_W5~ETd6b6P81`q)$X^;UVhLDhM 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 literal 6203 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 diff --git a/ObjectALDemo/Resources/iTunesArtwork b/ObjectALDemo/Resources/iTunesArtwork new file mode 100644 index 0000000000000000000000000000000000000000..fc580216daa33659e7e993ab9634f3545e4bd1a5 GIT binary patch literal 71770 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