Behavior of CCRenderTexture after update

Hi there,

I am developing an app where the user can draw lines (drawings). The lines get then converted to sprites with CCRenderTexture. The problem is that after the update 1.5.0.0 the lines aren't converted anymore (or not visible).

I read the new render pipeline, especially the part about visiting in a render texture but I could not find out the problem in my code.

Following code worked fine before the update:

CCDrawNode line;
.
.
CCSize cSize = bounding.Size + FrameSize;
rtm = new CCRenderTexture(cSize, cSize);

rtm.BeginWithClear(new CCColor4B(217/255f, 217/255f, 217/255f, 1.0f));
line.AdditionalTransform = CCAffineTransform.Translate(CCAffineTransform.Identity, -bounding.Origin.X + FrameSize * 0.5f, -bounding.Origin.Y + FrameSize * 0.5f);
line.Visit();
rtm.End();
line.AdditionalTransform = CCAffineTransform.Identity;

rtm.Texture.IsAntialiased = true;

modelSprite = new CCSprite(rtm.Texture);
modelSprite.AnchorPoint = CCPoint.AnchorMiddle;
this.AddChild(modelSprite, 2)

I have attached a screenshot with before and after the update.

Anyone an idea what could be going wrong in my code?

Best regard

Posts

  • RamiTabbaraRamiTabbara AUMember, Xamarin Team Xamurai

    Hi Muhamed,

    As discussed in the article on the new renderer pipeline here, you now have the ability to specify the parent transform when visiting a node. This means you don't need to attach an additional transform to achieve the behaviour you're after. Instead you can do something like the following

    
    ...
    CCSize cSize = bounding.Size + FrameSize;
    
    var transform = CCAffineTransform.Translate(CCAffineTransform.Identity, -bounding.Origin.X + FrameSize * 0.5f, -bounding.Origin.Y + FrameSize * 0.5f);
    
    rtm = new CCRenderTexture(cSize, cSize);
    rtm.BeginWithClear(new CCColor4B(217/255f, 217/255f, 217/255f, 1.0f));
    // If the CCDrawNode 
    line.Visit(ref transform);
    rtm.End();
    
    

    Although to be honest, without knowing where you're get bounding and FrameSize, the transformation does seem a little strange.

    I think the best thing is to just clarify what you're trying to achieve. If you just want to render a drawnode into a render texture then all you should need to do is:

    
    
    CCDrawNode line;
    // Create draw node
    
    CCSize size = line.ContentSize;
    
    CCRenderTexture renderTexture = new CCRenderTexture(size, size);
    renderTexture.BeingWithClear(new CCColor4B(217/255f, 217/255f, 217/255f, 1.0f));
    
    // If no transform is passed to visit, then the parent transform is assumed to be the Identity
    // This means we're drawing relative to the origin of the render texture
    line.Visit();
    renderTexture.End();
    
    
  • Hello @RamiTabbara

    Many thanks for your reply!
    To clarify what I'm trying to do:
    In this post I asked how to correctly save a line drawing as a Sprite and got the answer:

    When the CCDrawNode is used via a Visit it is offset from the bottom left corner of the screen. At this time we do not support passing in a custom viewport for the render texture so you will need to offset the position by the amount from the bottom left.

    So that's why the transformation (proposed by @kjpou1 ). Bounding is just the boundingRect of the drawnode and framesize is just 10.0.
    Now all I want to do is to save the drawnode into a texture and I tried both your examples (with and without transformation) but still get an empty sprite without the drawing inside and just the background.

    I am saving the rendertexture into a sprite with:
    modelSprite = new CCSprite(rtm.Texture);

    Is this the correct way?

  • MuhamedAhmetovicMuhamedAhmetovic CHMember
    edited July 2015

    Hello @kjpou1 , @RamiTabbara

    Thank you for the links. As I said it worked perfectly before the update. As soon as I installed the update it broke. I tried every possibility in your links but still the same problem.

    I tried following (which as I said worked perfectly before the update):

    rtm.BeginWithClear(new CCColor4B(217/255f, 217/255f, 217/255f, 0.8f));
    line.Position -= line.BoundingRect.Origin;
    line.Visit();
    rtm.End();
    line.Position = CCPoint.Zero;
    

    I have done a minimal application that does exactly the thing I want and the problem is also there. Could you please have a quick look at it?

    Many thanks!

  • kjpou1kjpou1 LUMember, Xamarin Team Xamurai

    Hey Muhamed

    Thanks for the code. Instead of posting back your project I will try to explain what is going on from your example. That way if anyone wants to take a look at your project and then the mods to get it to work the way you want.

    In your ModelNode constructor replaced code which is a direct drop from Render Target example :

        public ModelNode(CCLayerGradient layer ,CCDrawNode canvasNode) : base()
        {
            isSelected = false;
    
            var renderSize = canvasNode.BoundingRect.Size;
            var renderOrigin = canvasNode.BoundingRect.Origin;
    
            // here we calculate the translation of the rendering.  It should be the offset of BoundingRect Origin
            // this will then be passed to the Visit(ref CCAffineTransform) method.
            var translate = CCAffineTransform.Translate(canvasNode.AffineWorldTransform, -renderOrigin.X, -renderOrigin.Y);
    
            var rtm = new CCRenderTexture(renderSize, renderSize);
    
            rtm.BeginWithClear(CCColor4B.Green);
            canvasNode.Visit(ref translate);
            rtm.End();
    
            rtm.Texture.IsAntialiased = true;
            rtm.Sprite.AnchorPoint = CCPoint.AnchorMiddle;
            rtm.Sprite.Opacity = 127;
            AddChild(rtm.Sprite, 50, 100);
            Position = canvasNode.BoundingRect.Center;
    
        }
    

    In the section of code above you are drawing to a canvasNode DrawNode that is basically the size of the screen. You then want to take only that part of the image that was drawn and create a smaller image version containing only that part of the image that was drawn to the DrawNode.

    We added in the last release a convenience property BoundingRect to allow you to get this bounding region. Hmm, maybe BoundingRegion would have been a better name instead of BoundingRect. Well that is neither here nor there and anyway this allows you to obtain that region.

            var renderSize = canvasNode.BoundingRect.Size;
            var renderOrigin = canvasNode.BoundingRect.Origin;
    

    You then need to create a render target the size of this bounding area which BoundingRect does for you with BoundingRect.Size. This is then used to create the render target.

    Because the canvas DrawNode obtains the screen points they can be anything from 0,0 to the screens width and height. You only need that section of the canvas that has been drawn to so that is obtained from the BoundingRect.Origin which is the lower left corner of all the primitives that were drawn to the canvas.

            // here we calculate the translation of the rendering.  It should be the offset of BoundingRect Origin
            // this will then be passed to the Visit(ref CCAffineTransform) method.
            var translate = CCAffineTransform.Translate(canvasNode.AffineWorldTransform, -renderOrigin.X, -renderOrigin.Y);
    

    We will create a translation transform that will offset our drawing when we do the canvasNode.Visit() so that we will only draw that section of the node to our render target.


    In your GameLayer in OnTouchesEnded you are clearing the lines before the rendering has had a chance to render to the render target. Basically direct drop from Render Target example.

        void OnTouchesBegan (List<CCTouch> touches, CCEvent touchEvent)
        {
                        line.Clear ();  <---  Moved here for now.  
            var touch = touches [0];
            firstPoint = touch.Location;
            lastPoint = touch.Location;
        }
    
        void OnTouchesMoved (List<CCTouch> touches, CCEvent touchEvent)
        {
            var touch = touches [0];
    
            line.DrawSegment (lastPoint, touch.Location, 1, new CCColor4F (CCColor4B.Black));
    
            lastPoint = touch.Location;
        }
    
        void OnTouchesEnded (List<CCTouch> touches, CCEvent touchEvent)
        {
            mNode = new ModelNode (this, line);
            this.AddChild (mNode, 100);
                        //line.Clear ();  <----- Can not do this here because you have not given the renderer the time render the lines yet.
        }
    

    The Renderer Pipeline is not an immediate render process anymore as outlined in the Renderer Pipeline overview that Rami posted. Basically it takes all these draw calls and batches them up to be processed at the end of rendering cycle. So what you were doing here is clearing the lines that you had already drawn to the line DrawNode before there was a chance to process them. Thus the reason the line.Clear() being moved to the OnTouchesBegan. You may need to move this someplace else in your overall application.

    Hope this helps.

  • Hej @kjpou1

    Many thanks for your help and detailed explanation again!
    I see now that because of my early line.Clear() call I never saw anything not even the "wrong thing" :smile:

    Is there a possibility to wait for the rendering cycle to end and then call the line.Clear() method? Is this maybe in a separate thread and possible to wait for this thread to end?

    Another thing is that following is not working (It used to work before the update):
    rtm.BeginWithClear(new CCColor4B(217/255f, 217/255f, 217/255f, 0.8f));
    While following works without problems:
    rtm.BeginWithClear(CCColor4B.Gray);

    Why is that?

  • kjpou1kjpou1 LUMember, Xamarin Team Xamurai

    Muhamed

    There are multiple ways to achieve this:

        void OnTouchesEnded (List<CCTouch> touches, CCEvent touchEvent)
        {
            mNode = new ModelNode (this, line);
            this.AddChild (mNode, 100);
    
            // use a custom render command
            var clearLinesCommand = new CCCustomCommand(() =>
                {
                    line.Clear();
                }
            );
            Renderer.AddCommand(clearLinesCommand);
    
           // ----  OR -----
    
           // use a schedule one 
            ScheduleOnce(
                (dt) =>
                {
                    line.Clear();
                }
                ,0
    
            );
        }
    

    Do not use both here but instead pick which one you want.

    For your color:

    new CCColor4B(217/255f, 217/255f, 217/255f, 0.8f)

    CCColor4B takes values from [0 .. 255] inclusive. What you have there is what would be used for a CCColor4F which takes float values [0 .. 1] inclusive.

  • @kjpou1

    Thanks for your reply!
    It doesn't seem to work somehow:
    Neither
    rtm.BeginWithClear(new CCColor4B(217, 217, 217, 204))
    nor
    rtm.BeginWithClear (217, 217, 217, 204);
    works for me... very strange...

  • kjpou1kjpou1 LUMember, Xamarin Team Xamurai
    edited July 2015

    You may be having a problem with your BlendFunc as reported here https://github.com/mono/CocosSharp/issues/252.

    There is a test here for this as well as explanation in the code.

  • Yes it was the BlendFunc. That solved the problem many thanks!!

  • kjpou1kjpou1 LUMember, Xamarin Team Xamurai

    Great Muhamed!!

Sign In or Register to comment.