Problem creating a CCSprite out of a CCDrawNode

I'm trying to create a mask sprite out of a CCDrawNode via a CCRenderTexture but I just can't get it to work. I've created a very simple example which demonstrates the problem (my real use case involves multiple complex polygons that I want to use to reveal an image in various stages of completion).

In the attached image (a gray background with small white circe in lower left corner) you can see the result of the code below which just shows that the CCDrawNode is being correct constructed:

// create a dark gray layer
CCLayer layer = new CCLayer();
layer.AddChild(new CCLayerColor(CCColor4B.Gray));
AddChild(layer);

// construct a white circle
CCDrawNode circle = new CCDrawNode();
circle.DrawSolidCircle(new CCPoint(100.0f, 100.0f), 75.0f, new CCColor4B(255, 255, 255, 255));
layer.AddChild(circle);

Now, in the following code, nothing happens, I just get a gray background. There is not very much information on this - the API documentation is lacking lots of detail and even reviewing the CocosSharp unit tests I couldn't find anything that helped either - apologies if it's there but I failed to find it!

// create a dark gray layer
CCLayer layer = new CCLayer();
layer.AddChild(new CCLayerColor(CCColor4B.Gray));
AddChild(layer);

// construct a white circle
CCDrawNode circle = new CCDrawNode();
circle.DrawSolidCircle(new CCPoint(100.0f, 100.0f), 75.0f, new CCColor4B(255, 255, 255, 255));

// create sprite from draw node
CCRenderTexture rtm = new CCRenderTexture(new CCSize(200.0f, 200.0f), new CCSize(200.0f, 200.0f) , CCSurfaceFormat.Color, CCDepthFormat.Depth24Stencil8);
rtm.Begin();
circle.Visit();
rtm.End();
CCSprite circleSprite = new CCSprite(rtm.Sprite.Texture);
circleSprite.Position = new CCPoint(100.0f, 100.0f);
layer.AddChild(circleSprite);

I'm hoping that I'm doing something mind-bogglingly stupid!

As you can see from the code above I'm creating the circle CCDrawNode but NOT adding it to the layer - if I do add it to the layer this generates a run-time exception in the circle.Visit() line:

System.NullReferenceException: Object reference not set to an instance of an object
  at CocosSharp.CCDrawManager.DrawPrimitives[VertexPositionColor] (PrimitiveType type, Microsoft.Xna.Framework.Graphics.VertexPositionColor[] vertices, Int32 offset, Int32 count) [0x00000] in <filename unknown>:0
  at CocosSharp.CCDrawNode.FlushTriangles () [0x00000] in <filename unknown>:0
  at CocosSharp.CCDrawNode.Draw () [0x00000] in <filename unknown>:0
  at CocosSharp.CCNode.Visit () [0x00000] in <filename unknown>:0
  at Blueprint.Shared.TestScene.TestDrawNodeToSprite () [0x0008d] in /Users/user/Documents/F-Drive/AppsMobilis/Blueprint Game/Blueprint.Shared/TestScene.cs:46
  at Blueprint.Shared.TestScene..ctor (CocosSharp.CCWindow gameWindow, Blueprint.Shared.GameAppDelegate gameApp) [0x00009] in /Users/user/Documents/F-Drive/AppsMobilis/Blueprint Game/Blueprint.Shared/TestScene.cs:17
  at Blueprint.Shared.GameAppDelegate.ApplicationDidFinishLaunching (CocosSharp.CCApplication application, CocosSharp.CCWindow mainWindow) [0x0001d] in /Users/user/Documents/F-Drive/AppsMobilis/Blueprint Game/Blueprint.Shared/GameAppDelegate.cs:202
  at CocosSharp.CCApplication.LoadContent () [0x00000] in <filename unknown>:0
  at Microsoft.Xna.Framework.DrawableGameComponent.Initialize () [0x00000] in <filename unknown>:0
  at CocosSharp.CCApplication.Initialize () [0x00000] in <filename unknown>:0
  at Microsoft.Xna.Framework.Game.InitializeExistingComponents () [0x00000] in <filename unknown>:0
  at Microsoft.Xna.Framework.Game.Initialize () [0x00000] in <filename unknown>:0
  at CocosSharp.CCGame.Initialize () [0x00000] in <filename unknown>:0
  at Microsoft.Xna.Framework.Game.DoInitialize () [0x00000] in <filename unknown>:0
  at Microsoft.Xna.Framework.Game.Run (GameRunBehavior runBehavior) [0x00000] in <filename unknown>:0
  at Microsoft.Xna.Framework.Game.Run () [0x00000] in <filename unknown>:0
  at CocosSharp.CCApplication.StartGame () [0x00000] in <filename unknown>:0
  at Blueprint.iOS.AppDelegate.FinishedLaunching (UIKit.UIApplication app, Foundation.NSDictionary options) [0x00033] in /Users/user/Documents/F-Drive/AppsMobilis/Blueprint Game/Blueprint.iOS/AppDelegate.cs:29
  at at (wrapper managed-to-native) UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at UIKit.UIApplication.Main (System.String[] args, IntPtr principal, IntPtr delegate) [0x00005] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:62
  at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00038] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:46
  at Blueprint.iOS.Application.Main (System.String[] args) [0x00008] in /Users/user/Documents/F-Drive/AppsMobilis/Blueprint Game/Blueprint.iOS/Main.cs:17

Any help greatly appreciated, Adrian

Posts

  • RamiTabbaraRamiTabbara AUMember, Xamarin Team Xamurai

    Hi Adrian,

    Just a few small suggestions. Firstly, adding a CCLayerColor as a child of another layer is generally unnecessary as you could simply have

    
    CCLayer layer = new CCLayerColor(CCColor4B.Gray);
    
    

    and then add that to your scene. If you want to customise your layer by adding nodes then the recommended pattern is to subclass your CCLayer and make use of the constructor as well as the overridden method void AddedToScene to handle any code dependent on visible bounds (such as laying out your nodes). i.e.

    
            public GameLayer()
            {
                circleNode = new CCDrawNode();
                circleNode.DrawSolidCircle(new CCPoint(100.0f, 100.0f), 75.0f, CCColor4B.White);
                AddChild(circleNode);
            }
    
            protected override void AddedToScene()
            {
                base.AddedToScene();
    
                // Use the bounds to layout the positioning of our drawable assets
                CCRect bounds = VisibleBoundsWorldspace;
            }
    
    

    Finally, with respect to the CCRenderTexture I've opened up a new issue here and we'll take a look at it asap.

  • Thanks for the comments. I was only using a gray layer in order to give some contrast to ensure that the sprite texture being created wasn't a black one (due to blend functions, strange clipping or something) rather than a white one as I'd expected.

    Look forward to hearing back from you on the render texture issue.

    I was then going to use a further render texture to combine the drawn mask with a background image (as I can't use the stencil capability of a sprite as it's not supported by MonoGame on iOS or Mac) to reveal parts of an image in stages as the game progresses. Is there another way to do this - possibly by having a layer just for the reveal image with a clipping node in it - would that work?

    (Apologies I'm very new to Cocos and CocosSharp so probably asking lots of silly noob questions!)

    Thanks again, Adrian

  • kjpou1kjpou1 LUMember, Xamarin Team Xamurai

    Adrian

    For the error you are having can you do something like the following:

    // construct a white circle
    CCDrawNode circle = new CCDrawNode();
    
    // create sprite from draw node
    CCRenderTexture rtm = new CCRenderTexture(new CCSize(200.0f, 200.0f), new CCSize(200.0f, 200.0f) , CCSurfaceFormat.Color, CCDepthFormat.Depth24Stencil8);
    
    rtm.AddChild (circle);  // Add the circle as a child of the rendertarget
    
    rtm.Begin();  // Make the rendertarget the current drawing context
    
    // construct a white circle
    circle.DrawSolidCircle(new CCPoint(100.0f, 100.0f), 75.0f, new CCColor4B(255, 255, 255, 255));
    
    rtm.End();  // End the current drawing context
    
    CCSprite circleSprite = new CCSprite(rtm.Sprite.Texture);
    circleSprite.Position = new CCPoint(100.0f, 100.0f);
    layer.AddChild(circleSprite);
    

    Let us know how you get on with that.

  • Hmmm, that didn't work, I tried the following and I don't see a sprite:

    // create render texture containing draw node
    CCRenderTexture rtm = new CCRenderTexture(new CCSize(200.0f, 200.0f), new CCSize(200.0f, 200.0f), CCSurfaceFormat.Color, CCDepthFormat.Depth24Stencil8);
    
    CCDrawNode circle = new CCDrawNode();
    rtm.AddChild(circle);
    
    rtm.Begin();
    circle.DrawSolidCircle(new CCPoint(100.0f, 100.0f), 75.0f, new CCColor4B(255, 255, 255, 255));
    rtm.End();
    
    // create sprite from render texture
    CCSprite circleSprite = new CCSprite(rtm.Sprite.Texture);
    circleSprite.Position = new CCPoint(100.0f, 100.0f);
    
    layer.AddChild(circleSprite);
    

    I then also tried adding circle.visit(); before rtm.End(); and that didn't work either.

    Are there any working examples of this - I tested this on both iOS and Android with the same result.

    Thanks, Adrian

  • I changed my test to the following in order to confirm that the render texture is being created correctly with the draw node inside it and I do get the circle appearing.

    CCLayer layer = new CCLayer();
    AddChild(layer);
    
    // create render texture containing draw node
    CCRenderTexture rtm = new CCRenderTexture(new CCSize(200.0f, 200.0f), new CCSize(200.0f, 200.0f), CCSurfaceFormat.Color, CCDepthFormat.Depth24Stencil8);
    
    CCDrawNode circle = new CCDrawNode();
    rtm.AddChild(circle);
    
    rtm.Begin();
    circle.DrawSolidCircle(new CCPoint(100.0f, 100.0f), 75.0f, new CCColor4B(255, 255, 255, 255));
    rtm.End();
    
    layer.AddChild(rtm);
    

    So the problem looks like something to do with creating a sprite from the render texture using CCSprite circleSprite = new CCSprite(rtm.Sprite.Texture);

    Of course, it could be something to do with the surface or depth formats of the render texture I suppose that prevents it from being converted into a sprite.

    Any further ideas that I can try?

    Thanks, Adrian

  • ShermanUitzetterShermanUitzetter USMember ✭✭
    edited January 2015

    When I read the previous few posts, I immediately thought of what I would try next. To me it looks like your second test might have worked because the circle CCDrawNode is still in the node hierarchy, so it drew in the layer. Maybe, as a further test, you could replace this bit (the stuff you removed):

    // create sprite from render texture
    CCSprite circleSprite = new CCSprite(rtm.Sprite.Texture);
    circleSprite.Position = new CCPoint(100.0f, 100.0f);
    

    With this:

    // create sprite from render texture
    CCSprite circleSprite = new CCSprite(new CCTexture2D(200, 200));
    Color data = new Color[200 * 200];
    rtm.Sprite.Texture.XNATexture.GetData<Color>(data)
    circleSprite.Texture.XNATexture.SetData<Color>(data);
    circleSprite.Position = new CCPoint(100.0f, 100.0f);
    

    Hopefully (untested code here) this creates a new sprite, with a new texture, and then copies the raw data from the render texture to the sprite's texture. You could also try to look at the data that GetData() gets to see if there's a circle drawn in there.

  • kjpou1kjpou1 LUMember, Xamarin Team Xamurai
    edited January 2015

    Hey guys

    Silly me. I was so focused on CCDrawNode that it really did not hit me. The CCDrawNode is actually a CCNode that needs to be added as a child to the Scene Graph thus the Node in the name.

    To actually draw to the RenderTarget that is created you will need to use the CCDrawingPrimitives which draws to the GraphicsDevice which the RenderTarget is attached to.

                // create render texture containing draw node
                CCRenderTexture rtm = new CCRenderTexture(new CCSize(200.0f, 200.0f), new CCSize(200.0f, 200.0f), CCSurfaceFormat.Color, CCDepthFormat.Depth24Stencil8);
    
                rtm.Begin();
    
                CCDrawingPrimitives.Begin();
    
                CCDrawingPrimitives.DrawColor = new CCColor4B(255, 255, 255, 255);
                CCDrawingPrimitives.DrawSolidCircle(new CCPoint(100.0f, 100.0f), 75.0f, 0, 20);
    
                CCDrawingPrimitives.End();
    
                rtm.End();
    
                // create sprite from render texture
                CCSprite circleSprite = new CCSprite(rtm.Texture);
                circleSprite.Position = new CCPoint(200.0f, 200.0f);
    
                AddChild(circleSprite);
    
  • Hi, Thanks for both of the suggestions above. Unfortunately neither of these worked. Hmmm, makes me think that something is not quite right and I can't find any CocosSharp examples to help me either. I'll do some experimenting and Googling to see if I can figure this out over the weekend.

  • kjpou1kjpou1 LUMember, Xamarin Team Xamurai

    Adrian

    Not sure what is going on. Rami updated the example this morning that is working for us here.

    https://github.com/mono/CocosSharp/blob/master/tests/tests/classes/tests/DrawPrimitivesTest/DrawPrimitivesTest.cs#L65

  • I've just raised a new thread about potential CCRenderTexture issues - see forums.xamarin.com/discussion/31994/ccrendertexture-issues#latest - this may be relevant.

    I notice that in the example it is using a fullscreen render texture - I think this could be masking a problem - see my post just in case it is relevant.

    Thanks, Adrian

  • AdrianNelson.0022AdrianNelson.0022 GBMember ✭✭
    edited February 2015

    This may have been fixed by the change that has now been performed in the CCRenderTexture post forums.xamarin.com/discussion/31994/ccrendertexture-issues#latest - but just in case the code I have below draws an orange render texture with a small white circle in the top right corner (completely within the render texture).

    But when I grab the texture data all I get is the orange render texture and there is no white circle bit at all.

    CCDrawNode circle = new CCDrawNode();
    circle.DrawSolidCircle(new CCPoint(50.0f, 50.0f), 25.0f, new CCColor4B(255, 255, 255, 255));
    
    // create render texture containing draw node
    CCRenderTexture rtm = new CCRenderTexture(new CCSize(200.0f, 200.0f), new CCSize(200.0f, 200.0f), CCSurfaceFormat.Color, CCDepthFormat.Depth24Stencil8);
    rtm.AddChild(circle);
    rtm.BeginWithClear(CCColor4B.Orange);
    rtm.End();
    
    CCTexture2D tex = new CCTexture2D(200, 200, CCSurfaceFormat.Color, true, false);
    Color [] data = new Color[200 * 200];
    rtm.Sprite.Texture.XNATexture.GetData<Color>(data);
    tex.XNATexture.SetData<Color>(data);
    
    CCRect rr = new CCRect(0, 0, 200, 200);
    CCSprite xx = new CCSprite(tex, rr, false);
    xx.AnchorPoint = CCPoint.AnchorLowerLeft;
    xx.Position = new CCPoint(200.0f, 200.0f);
    AddChild(xx);
    

    The following doesn't work either and it should produce the same results (I think?) but using the drawing primitives:

    CCRenderTexture rtm = new CCRenderTexture(new CCSize(200.0f, 200.0f), new CCSize(200.0f, 200.0f));
    rtm.BeginWithClear(CCColor4B.Orange);
    CCDrawingPrimitives.Begin();
    CCDrawingPrimitives.DrawColor = CCColor4B.White;
    CCDrawingPrimitives.DrawSolidCircle(new CCPoint(50.0f, 50.0f), 25.0f, 0, 20);
    CCDrawingPrimitives.End();
    rtm.End();
    
    CCSprite xx = new CCSprite(rtm.Sprite.Texture);
    xx.AnchorPoint = CCPoint.AnchorLowerLeft;
    xx.Position = new CCPoint(200.0f, 200.0f);
    AddChild(xx);
    

    Thanks, Adrian

  • kjpou1kjpou1 LUMember, Xamarin Team Xamurai

    Adrian this should be fixed in the current code base.

  • Excellent, I look forward to testing all these changes in the next release.

    Many thanks as always, Adrian

  • AdrianNelson.0022AdrianNelson.0022 GBMember ✭✭

    I've just tested the new CocosSharp 1.3.2 update and I can confirm that render textures now correctly capture and also clip graphic primitives as in the example below which now works and creates an orange square with a white circle clipped at the top right. Many thanks!

    CCLayer layer = new CCLayer();
    AddChild(layer);
    
    CCDrawNode circle = new CCDrawNode();
    circle.DrawSolidCircle(new CCPoint(175.0f, 175.0f), 75.0f, new CCColor4B(255, 255, 255, 255));
    
    CCRenderTexture rtm = new CCRenderTexture(new CCSize(200.0f, 200.0f), new CCSize(200.0f, 200.0f), CCSurfaceFormat.Color, CCDepthFormat.Depth24Stencil8);
    rtm.BeginWithClear(CCColor4B.Orange);
    circle.Visit();
    rtm.End();
    
    rtm.Position = new CCPoint(200.0f, 200.0f);
    
    layer.AddChild(rtm);
    
Sign In or Register to comment.