diff --git a/cocos2d-ios.xcodeproj/project.pbxproj b/cocos2d-ios.xcodeproj/project.pbxproj index 3edf13e30a7..2c25bd2eb41 100644 --- a/cocos2d-ios.xcodeproj/project.pbxproj +++ b/cocos2d-ios.xcodeproj/project.pbxproj @@ -1176,6 +1176,8 @@ 50FD7B3C0FFAA8AC004073B5 /* grossini_dance_atlas.png in Resources */ = {isa = PBXBuildFile; fileRef = 509A7A970F61A2400032F449 /* grossini_dance_atlas.png */; }; 50FD7B3F0FFAA8BC004073B5 /* background.png in Resources */ = {isa = PBXBuildFile; fileRef = 50C1959C0EE4B90800829067 /* background.png */; }; 50FD7B450FFAA8C6004073B5 /* atlastest.png in Resources */ = {isa = PBXBuildFile; fileRef = 506882680E957C4A00F943E5 /* atlastest.png */; }; + 6D0E1183C5BE3CB5DA10D03D /* CCRenderTargetNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D0E11D552E8A1F6205D918E /* CCRenderTargetNode.h */; }; + 6D0E1D88B980A0D8C05324E6 /* CCRenderTargetNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D0E193AA35301C580F6B274 /* CCRenderTargetNode.m */; }; 6F305B5F0FB9D2790052E700 /* TransformUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F305B5D0FB9D2790052E700 /* TransformUtils.h */; }; 6F305B600FB9D2790052E700 /* TransformUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F305B5E0FB9D2790052E700 /* TransformUtils.m */; }; A00942C915180C730029A5C6 /* b1.png in Resources */ = {isa = PBXBuildFile; fileRef = 5001659D0E72DB470085673F /* b1.png */; }; @@ -4299,6 +4301,8 @@ 50F9EA8F0E1AE87E000E7616 /* fire.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = fire.png; sourceTree = ""; }; 50FBB2D8117613F500150761 /* CCActionTween.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCActionTween.h; sourceTree = ""; }; 50FBB2D9117613F500150761 /* CCActionTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCActionTween.m; sourceTree = ""; }; + 6D0E11D552E8A1F6205D918E /* CCRenderTargetNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCRenderTargetNode.h; sourceTree = ""; }; + 6D0E193AA35301C580F6B274 /* CCRenderTargetNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCRenderTargetNode.m; sourceTree = ""; }; 6F305B5D0FB9D2790052E700 /* TransformUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TransformUtils.h; sourceTree = ""; }; 6F305B5E0FB9D2790052E700 /* TransformUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TransformUtils.m; sourceTree = ""; }; A009431115180C730029A5C6 /* MultithreadTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MultithreadTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -6770,6 +6774,7 @@ 50150434113300F900A9CA65 /* CCProgressTimer.m */, 50316AA410291280003ACFE7 /* CCRenderTexture.h */, 50316AA510291280003ACFE7 /* CCRenderTexture.m */, + 6D0E1133D830D8F22BF7452D /* RenderTargetNode */, ); name = "Misc Nodes"; sourceTree = ""; @@ -6799,6 +6804,15 @@ path = "Resources/cocos2d-artwork"; sourceTree = ""; }; + 6D0E1133D830D8F22BF7452D /* RenderTargetNode */ = { + isa = PBXGroup; + children = ( + 6D0E11D552E8A1F6205D918E /* CCRenderTargetNode.h */, + 6D0E193AA35301C580F6B274 /* CCRenderTargetNode.m */, + ); + name = RenderTargetNode; + sourceTree = ""; + }; A015BFAB13B826D800C73E76 /* kazmath */ = { isa = PBXGroup; children = ( @@ -7493,6 +7507,7 @@ A09CEEA3150EB30D001E49B8 /* ccShader_PositionTextureColorAlphaTest_frag.h in Headers */, A0A7A53C1514F27D00C8BD16 /* CCActionCatmullRom.h in Headers */, A039EBFF155C686B0061EE37 /* CCNode+Debug.h in Headers */, + 6D0E1183C5BE3CB5DA10D03D /* CCRenderTargetNode.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -11330,6 +11345,7 @@ A0A7A53D1514F27D00C8BD16 /* CCActionCatmullRom.m in Sources */, A039EC00155C686B0061EE37 /* CCNode+Debug.m in Sources */, A04FA967156303C600EC18D4 /* ccCArray.m in Sources */, + 6D0E1D88B980A0D8C05324E6 /* CCRenderTargetNode.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/cocos2d-mac.xcodeproj/project.pbxproj b/cocos2d-mac.xcodeproj/project.pbxproj index dc6301c626f..c0c0bf55363 100644 --- a/cocos2d-mac.xcodeproj/project.pbxproj +++ b/cocos2d-mac.xcodeproj/project.pbxproj @@ -5193,6 +5193,8 @@ E0DA433D12A03A3C00210ACE /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E0F9256D1224207200EF2362 /* libz.dylib */; }; E0DA433E12A03A3C00210ACE /* liblibpng.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E076EDB91225FEAA00DE0DA2 /* liblibpng.a */; }; E0DA434912A03AE200210ACE /* FullScreenTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E0DA434812A03AE100210ACE /* FullScreenTest.m */; }; + FBEFDA6215E226E000CD6DF0 /* CCRenderTargetNode.h in Headers */ = {isa = PBXBuildFile; fileRef = FBEFDA6015E226E000CD6DF0 /* CCRenderTargetNode.h */; }; + FBEFDA6315E226E000CD6DF0 /* CCRenderTargetNode.m in Sources */ = {isa = PBXBuildFile; fileRef = FBEFDA6115E226E000CD6DF0 /* CCRenderTargetNode.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -6497,6 +6499,8 @@ E0DA434812A03AE100210ACE /* FullScreenTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FullScreenTest.m; path = tests/FullScreenTest.m; sourceTree = ""; }; E0DA434A12A03B0C00210ACE /* FullScreenTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FullScreenTest.h; path = tests/FullScreenTest.h; sourceTree = ""; }; E0F9256D1224207200EF2362 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; + FBEFDA6015E226E000CD6DF0 /* CCRenderTargetNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCRenderTargetNode.h; sourceTree = ""; }; + FBEFDA6115E226E000CD6DF0 /* CCRenderTargetNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCRenderTargetNode.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -7790,6 +7794,7 @@ E076E7791225ECF700DE0DA2 /* Misc Nodes */ = { isa = PBXGroup; children = ( + FBEFDA6615E226F100CD6DF0 /* RenderTargetNode */, E076E6611225EC7400DE0DA2 /* CCMotionStreak.h */, E076E6621225EC7400DE0DA2 /* CCMotionStreak.m */, E076E66F1225EC7400DE0DA2 /* CCProgressTimer.h */, @@ -8159,6 +8164,15 @@ path = iOS; sourceTree = ""; }; + FBEFDA6615E226F100CD6DF0 /* RenderTargetNode */ = { + isa = PBXGroup; + children = ( + FBEFDA6015E226E000CD6DF0 /* CCRenderTargetNode.h */, + FBEFDA6115E226E000CD6DF0 /* CCRenderTargetNode.m */, + ); + name = RenderTargetNode; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -8378,6 +8392,7 @@ A009FB90150EC87500897349 /* ccShaders.h in Headers */, A0A7A507151416A000C8BD16 /* CCActionCatmullRom.h in Headers */, A03714D7154F1D9300081CBA /* CCNode+Debug.h in Headers */, + FBEFDA6215E226E000CD6DF0 /* CCRenderTargetNode.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -14100,6 +14115,7 @@ A0A7A506151416A000C8BD16 /* CCActionCatmullRom.m in Sources */, A03714D8154F1D9300081CBA /* CCNode+Debug.m in Sources */, A0C1EDC91562F979000709DA /* ccCArray.m in Sources */, + FBEFDA6315E226E000CD6DF0 /* CCRenderTargetNode.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/cocos2d/CCRenderTargetNode.h b/cocos2d/CCRenderTargetNode.h new file mode 100644 index 00000000000..32d568660f8 --- /dev/null +++ b/cocos2d/CCRenderTargetNode.h @@ -0,0 +1,39 @@ +// +// Created by krzysztof.zablocki on 8/19/12. +// +// +// + + +#import +#import "ccMacros.h" +#import "CCNode.h" +#import "CCSprite.h" +#import "Support/OpenGL_Internal.h" +#import "kazmath/mat4.h" +@class CCRenderTexture; + +#define CC_GL_CLEAR_COLOR GL_COLOR_BUFFER_BIT +#define CC_GL_CLEAR_DEPTH GL_DEPTH_BUFFER_BIT +#define CC_GL_CLEAR_STENCIL CC_GL_CLEAR_STENCIL + +/* + CCRenderTargetNode is a real rendering target node, it render all its children into CCRenderTexture. + Each CCRenderTexture has lazy created CCRenderTargetNode, so you can add any nodes that you want to render inside render texture into renderTexture.renderTargetNode. + You can also use it to render to texture that isn't in tree, you just need to make sure the render texture isn't released before this node, as it doesn't retain renderTexture. + If you specify clearFlags for render target node it will clear render texture content each frame before before rendering. + */ +@interface CCRenderTargetNode : CCNode +//- (void)beginWithClear:(float)r g:(float)g b:(float)b a:(float)a depth:(float)depthValue stencil:(int)stencilValue; + +@property (nonatomic, assign) GLbitfield clearFlags; // default to none, use CC_GL_CLEAR_X + +//! values used for clearing when clearFlags are set to corresponding bits +@property (nonatomic, assign) ccColor4F clearColor; +@property (nonatomic, assign) GLfloat clearDepth; +@property (nonatomic, assign) GLint clearStencil; + +//! doesn't retain render texture, it's your responsibility to make sure texture is not released before this node +- (id)initWithRenderTexture:(CCRenderTexture *)texture; + +@end diff --git a/cocos2d/CCRenderTargetNode.m b/cocos2d/CCRenderTargetNode.m new file mode 100644 index 00000000000..c4ede2882ab --- /dev/null +++ b/cocos2d/CCRenderTargetNode.m @@ -0,0 +1,101 @@ +// +// Created by krzysztof.zablocki on 8/19/12. +// +// +// + +#import "CCRenderTargetNode.h" +#import "CCRenderTexture.h" +#import "CCGrid.h" +#import "kazmath/mat4.h" +#import "kazmath/GL/matrix.h" + +@implementation CCRenderTargetNode { + CCRenderTexture *renderTexture_; + GLbitfield clearFlags_; + ccColor4F clearColor_; + GLfloat clearDepth_; + GLint clearStencil_; +} +@synthesize clearFlags = clearFlags_; +@synthesize clearColor = clearColor_; +@synthesize clearDepth = clearDepth_; +@synthesize clearStencil = clearStencil_; + + +- (id)initWithRenderTexture:(CCRenderTexture *)renderTexture +{ + self = [super init]; + if (self) { + renderTexture_ = renderTexture; + } + return self; +} + +- (void)visit +{ +// override visit. +// Don't call visit on its children + + if (!visible_) { + return; + } + + kmGLPushMatrix(); + + if (grid_ && grid_.active) { + [grid_ beforeDraw]; + [self transformAncestors]; + } + + [self sortAllChildren]; + [self transform]; + [self draw]; + + if (grid_ && grid_.active) { + [grid_ afterDraw:self]; + } + + kmGLPopMatrix(); + + orderOfArrival_ = 0; + +} + +- (void)draw +{ + NSAssert(renderTexture_ != nil, @"RenderTexture is needed by CCRenderTargetNode"); + + BOOL requireClearing = self.clearFlags != 0; + + [renderTexture_ begin]; + + if (requireClearing) { + // save previous 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(self.clearColor.r, self.clearColor.g, self.clearColor.b, self.clearColor.a); + glClearDepth(self.clearDepth); + glClearStencil(self.clearStencil); + glClear(self.clearFlags); + + // restore clear colors + glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + glClearDepth(depthClearValue); + glClearStencil(stencilClearValue); + } + + //! make sure all children are drawn + CCNode *child; + CCARRAY_FOREACH(children_, child) { + [child visit]; +} + + [renderTexture_ end]; +} +@end diff --git a/cocos2d/CCRenderTexture.h b/cocos2d/CCRenderTexture.h index 5d468759d43..7eff42b76c3 100644 --- a/cocos2d/CCRenderTexture.h +++ b/cocos2d/CCRenderTexture.h @@ -30,6 +30,7 @@ #import "CCSprite.h" #import "Support/OpenGL_Internal.h" #import "kazmath/mat4.h" +#import "CCRenderTargetNode.h" #ifdef __CC_PLATFORM_IOS #import @@ -59,7 +60,7 @@ typedef enum GLint oldFBO_; CCTexture2D* texture_; CCSprite* sprite_; - + CCRenderTargetNode *renderTargetNode_; GLenum pixelFormat_; } @@ -70,6 +71,11 @@ typedef enum */ @property (nonatomic,readwrite, assign) CCSprite* sprite; +/* + lazy created render target node that allows you to simplify rendering into render texture. Any children added to it will be rendered into render texture. + */ +@property (nonatomic, readonly) CCRenderTargetNode *renderTargetNode; + /** 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; diff --git a/cocos2d/CCRenderTexture.m b/cocos2d/CCRenderTexture.m index 905d97dbedb..e506bf1d6de 100644 --- a/cocos2d/CCRenderTexture.m +++ b/cocos2d/CCRenderTexture.m @@ -38,6 +38,7 @@ // extern #import "kazmath/GL/matrix.h" +#import "CCRenderTargetNode.h" @implementation CCRenderTexture @@ -157,6 +158,16 @@ -(void)dealloc [super dealloc]; } +- (CCRenderTargetNode *)renderTargetNode +{ + if (!renderTargetNode_) { + renderTargetNode_ = [[CCRenderTargetNode alloc] initWithRenderTexture:self]; + [self addChild:renderTargetNode_]; + [renderTargetNode_ release]; + } + return renderTargetNode_; +} + -(void)begin { CCDirector *director = [CCDirector sharedDirector]; diff --git a/tests/RenderTextureTest.h b/tests/RenderTextureTest.h index 4a68d1558f4..2652502e777 100644 --- a/tests/RenderTextureTest.h +++ b/tests/RenderTextureTest.h @@ -46,4 +46,9 @@ @interface RenderTextureTestDepthStencil : RenderTextureTest @end +@interface RenderTextureTargetNode : RenderTextureTest { + CCSprite *sprite1, *sprite2; +} +@end + diff --git a/tests/RenderTextureTest.m b/tests/RenderTextureTest.m index 8d9785f8d72..c7c165cdbbf 100644 --- a/tests/RenderTextureTest.m +++ b/tests/RenderTextureTest.m @@ -13,7 +13,8 @@ @"RenderTextureSave", @"RenderTextureIssue937", @"RenderTextureZbuffer", - @"RenderTextureTestDepthStencil" + @"RenderTextureTestDepthStencil", + @"RenderTextureTargetNode" }; Class nextAction(void); @@ -689,6 +690,73 @@ -(NSString*) subtitle return @"Circle should be missing 1/4 of its region"; } @end +#pragma mark - +#pragma mark RenderTextureTargetNode +@implementation RenderTextureTargetNode + +-(id) init +{ + /* + * 1 2 + * A: A1 A2 + * + * B: B1 B2 + * + * A1: premulti sprite + * A2: premulti render + * + * B1: non-premulti sprite + * B2: non-premulti render + */ + if( (self=[super init]) ) { + + CCLayerColor *background = [CCLayerColor layerWithColor:ccc4(40,40,40,255)]; + [self addChild:background]; + + // A1 + sprite1 = [CCSprite spriteWithFile:@"fire.png"]; + + // B1 + sprite2 = [CCSprite spriteWithFile:@"fire_rgba8888.pvr"]; + + CGSize s = [CCDirector sharedDirector].winSize; + /* A2 & B2 setup */ + CCRenderTexture *rend = [CCRenderTexture renderTextureWithWidth:s.width height:s.height pixelFormat:kCCTexture2DPixelFormat_RGBA4444]; + [rend setPosition:ccp(s.width/2, s.height/2)]; + + [rend.renderTargetNode addChild:sprite1]; + [rend.renderTargetNode addChild:sprite2]; + rend.renderTargetNode.clearColor = ccc4f(0, 0, 0, 0); + rend.renderTargetNode.clearFlags = CC_GL_CLEAR_COLOR; + + + [self addChild:rend]; + [self scheduleUpdate]; + } + + return self; +} + +- (void)update:(float)dt +{ + static float time = 0; + float r = 80; + sprite1.position = ccp(cosf(time * 2) * r, sinf(time * 2) * r); + sprite2.position = ccp(sinf(time * 2) * r, cosf(time * 2) * r); + + time += dt; +} + +-(NSString*) title +{ + return @"Testing Render Target Node"; +} + +-(NSString*) subtitle +{ + return @"Sprites should be equal and move with each frame"; +} +@end #pragma mark - #pragma mark AppDelegate (iOS) @@ -756,3 +824,6 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification } @end #endif + + +