Gamua / Starling-Framework

The Cross Platform Game Engine
http://www.starling-framework.org
Other
2.84k stars 819 forks source link

Calling render() manually omits masks #912

Closed joshtynjala closed 7 years ago

joshtynjala commented 7 years ago

Feathers has a class called RenderDelegate with a render() overide that simply calls render() on another display object. It basically allows the target display object to be rendered with different transformations. I use it for animated transitions so that, once the animation is completed, I don't need to worry about resetting any changes that I made. Here is a simplified version of the RenderDelegate class:

class RenderDelegate extends DisplayObject
{
    public function RenderDelegate(target:DisplayObject)
    {
        this.target = target;
    }

    public var target:DisplayObject;

    override public function getBounds(targetSpace:DisplayObject, resultRect:Rectangle = null):Rectangle
    {
        if(!resultRect) resultRect = new Rectangle();
        return resultRect;
    }

    override public function render(painter:Painter):void
    {
        target.setRequiresRedraw();
        target.render(painter);
    }
}

After commit 70f98e86bf73dc1ff6b690ddfb7ab48d52c8d11b, if the target (or one of its children) has a mask, that mask is not included when the RenderDelegate renders. See the following code to reproduce:

var quad:Quad = new Quad(100, 100, 0xff0000);
quad.mask = new Quad(50, 50);
quad.x = 40;
quad.y = 40;
this.addChild(quad);

var delegate:RenderDelegate = new RenderDelegate(quad);
delegate.x = 40;
delegate.y = 150;
this.addChild(delegate);
PrimaryFeather commented 7 years ago

Mhm, code that influences Starling's rendering like that has become a little more difficult because of the render cache. Sorry for that!

In my tests, this didn't work before the mentioned commit, either. But since that commit, the recommended way to implement such a delegate has changed, anyway, so it's good that you bring it up!

Please implement the render method like this:

override public function render(painter:Painter):void
{
    var wasCacheEnabled:Boolean = painter.cacheEnabled;
    var filter:FragmentFilter = target.filter;
    var mask:DisplayObject = target.mask;

    painter.cacheEnabled = false;

    if (mask)   painter.drawMask(mask);

    if (filter) filter.render(painter);
    else        target.render(painter);

    if (mask)   painter.eraseMask(mask);

    painter.cacheEnabled = wasCacheEnabled;
    painter.excludeFromCache(this);
}

Some notes:

Please try it out — that should do the trick!

joshtynjala commented 7 years ago

I actually oversimplified my example, I think. This should be what worked correctly before that commit and failed after:

var sprite:Sprite = new Sprite();
var quad:Quad = new Quad(100, 100, 0xff0000);
quad.mask = new Quad(50, 50);
sprite.addChild(quad);
sprite.x = 40;
sprite.y = 40;
this.addChild(sprite);

var delegate:RenderDelegate = new RenderDelegate(sprite);
delegate.x = 40;
delegate.y = 150;
this.addChild(delegate);

Your changes seem to fix the issue with this new code too! I spent at least an hour on Friday trying various combinations of cacheEnabled and excludeFromCache(). I don't know why I didn't get it to work, but I guess I was calling things in the wrong order, or maybe I passed the wrong display object to excludeFromCache(). Regardless, it works now! Thanks, Daniel.

PrimaryFeather commented 7 years ago

I actually oversimplified my example, I think. This should be what worked correctly before that commit and failed after [...]

Yes, that's what I thought! I used exactly that code for my first test. :smile:

I spent at least an hour on Friday trying various combinations of cacheEnabled and excludeFromCache(). I don't know why I didn't get it to work, but I guess I was calling things in the wrong order, or maybe I passed the wrong display object to excludeFromCache().

Yeah, I can imagine ... only the order of cacheEnabled is relevant, excludeFromCache can be called anywhere in the method — but you have to pass the right objects, and I can totally see that this is tricky. If it wasn't so damn useful, I would have thrown that cache away already, I assure you! :wink: