Skip to content

Commit

Permalink
Scissor rect support for DisplayObjectContainer
Browse files Browse the repository at this point in the history
  • Loading branch information
tconkling committed Feb 5, 2013
1 parent d7c1ddb commit 0cad6ae
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 23 deletions.
59 changes: 43 additions & 16 deletions starling/src/starling/core/RenderSupport.as
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ package starling.core
import starling.textures.Texture;
import starling.utils.Color;
import starling.utils.MatrixUtil;
import starling.utils.RectangleUtil;

/** A class that contains helper methods simplifying Stage3D rendering.
*
Expand All @@ -51,7 +52,8 @@ package starling.core
private var mRenderTarget:Texture;
private var mBackBufferWidth:int;
private var mBackBufferHeight:int;
private var mScissorRectangle:Rectangle;
private var mScissorRectStack:Vector.<Rectangle>;
private var mScissorRectStackSize:int;

private var mQuadBatches:Vector.<QuadBatch>;
private var mCurrentQuadBatchID:int;
Expand All @@ -75,7 +77,7 @@ package starling.core
mDrawCount = 0;
mRenderTarget = null;
mBlendMode = BlendMode.NORMAL;
mScissorRectangle = new Rectangle();
mScissorRectStack = new <Rectangle>[];

mCurrentQuadBatchID = 0;
mQuadBatches = new <QuadBatch>[new QuadBatch()];
Expand Down Expand Up @@ -247,36 +249,61 @@ package starling.core

/** The scissor rectangle can be used to limit rendering in the current render target to
* a certain area. This method expects the rectangle in stage coordinates
* (different to the context3D method with the same name, which expects pixels).
* Pass <code>null</code> to turn off scissoring.
* CAUTION: not a copy -- use with care! */
public function get scissorRectangle():Rectangle
{
return mScissorRectangle.isEmpty() ? null : mScissorRectangle;
* (different to the context3D method with the same name, which expects pixels). */
public function pushScissorRect(rect:Rectangle):Rectangle
{
if (mScissorRectStack.length < mScissorRectStackSize + 1)
mScissorRectStack.push(new Rectangle());

mScissorRectStack[mScissorRectStackSize].copyFrom(rect);
rect = mScissorRectStack[mScissorRectStackSize];

// intersect with the last pushed scissor rect
if (mScissorRectStackSize > 0)
rect = RectangleUtil.intersect(rect, mScissorRectStack[mScissorRectStackSize-1], rect);

++mScissorRectStackSize;

updateClipping();

// return the intersected scissor rect so callers can skip draw calls if it's empty
return rect;
}

public function set scissorRectangle(value:Rectangle):void
public function popScissorRect():void
{
if (value)
if (mScissorRectStackSize > 0)
{
mScissorRectangle.setTo(value.x, value.y, value.width, value.height);

--mScissorRectStackSize;
updateClipping();
}
}

protected function updateClipping():void
{
finishQuadBatch();

if (mScissorRectStackSize > 0)
{
var rect:Rectangle = mScissorRectStack[mScissorRectStackSize-1];
sRectangle.setTo(rect.x, rect.y, rect.width, rect.height);

var width:int = mRenderTarget ? mRenderTarget.root.nativeWidth : mBackBufferWidth;
var height:int = mRenderTarget ? mRenderTarget.root.nativeHeight : mBackBufferHeight;

MatrixUtil.transformCoords(mProjectionMatrix, value.x, value.y, sPoint);
// convert to pixel coordinates
MatrixUtil.transformCoords(mProjectionMatrix, rect.x, rect.y, sPoint);
sRectangle.x = Math.max(0, ( sPoint.x + 1) / 2) * width;
sRectangle.y = Math.max(0, (-sPoint.y + 1) / 2) * height;

MatrixUtil.transformCoords(mProjectionMatrix, value.right, value.bottom, sPoint);
MatrixUtil.transformCoords(mProjectionMatrix, rect.right, rect.bottom, sPoint);
sRectangle.right = Math.min(1, ( sPoint.x + 1) / 2) * width;
sRectangle.bottom = Math.min(1, (-sPoint.y + 1) / 2) * height;

Starling.context.setScissorRectangle(sRectangle);
}
else
else
{
mScissorRectangle.setEmpty();
Starling.context.setScissorRectangle(null);
}
}
Expand Down
75 changes: 70 additions & 5 deletions starling/src/starling/display/DisplayObjectContainer.as
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package starling.display
import starling.events.Event;
import starling.filters.FragmentFilter;
import starling.utils.MatrixUtil;
import starling.utils.RectangleUtil;

use namespace starling_internal;

Expand Down Expand Up @@ -65,12 +66,13 @@ package starling.display
public class DisplayObjectContainer extends DisplayObject
{
// members

private var mScissorRect:Rectangle;
private var mChildren:Vector.<DisplayObject>;

/** Helper objects. */
private static var sHelperMatrix:Matrix = new Matrix();
private static var sHelperPoint:Point = new Point();
private static var sHelperRect:Rectangle = new Rectangle();
private static var sBroadcastListeners:Vector.<DisplayObject> = new <DisplayObject>[];

// construction
Expand All @@ -96,6 +98,48 @@ package starling.display
super.dispose();
}

/** The object's scissor rect.
* <strong>Note</strong>: clip rects are axis aligned with the screen, so they
* will not be rotated or skewed if the DisplayObjectContainer is. */
public function get scissorRect():Rectangle { return mScissorRect; }
public function set scissorRect(val:Rectangle) :void { mScissorRect = val; }

/** Returns the bounds of the container's scissorRect in the given coordinate space, or
* null if the container doens't have a scissorRect. */
public function getScissorBounds(targetSpace:DisplayObject, resultRect:Rectangle=null):Rectangle
{
if (mScissorRect == null) return null;

if (resultRect == null) resultRect = new Rectangle();

var minX:Number = Number.MAX_VALUE;
var maxX:Number = -Number.MAX_VALUE;
var minY:Number = Number.MAX_VALUE;
var maxY :Number = -Number.MAX_VALUE;

var transformMatrix:Matrix = getTransformationMatrix(targetSpace, sHelperMatrix);
var x :Number = 0;
var y :Number = 0;
for (var i:int=0; i<4; ++i)
{
switch(i)
{
case 0: x = mScissorRect.left; y = mScissorRect.top; break;
case 1: x = mScissorRect.left; y = mScissorRect.bottom; break;
case 2: x = mScissorRect.right; y = mScissorRect.top; break;
case 3: x = mScissorRect.right; y = mScissorRect.bottom; break;
}
var transformedPoint :Point = MatrixUtil.transformCoords(transformMatrix, x, y, sHelperPoint);
minX = Math.min(minX, transformedPoint.x);
maxX = Math.max(maxX, transformedPoint.x);
minY = Math.min(minY, transformedPoint.y);
maxY = Math.max(maxY, transformedPoint.y);
}

resultRect.setTo(minX, minY, maxX-minX, maxY-minY);
return resultRect;
}

// child management

/** Adds a child to the container. It will be at the frontmost position. */
Expand Down Expand Up @@ -267,11 +311,10 @@ package starling.display
getTransformationMatrix(targetSpace, sHelperMatrix);
MatrixUtil.transformCoords(sHelperMatrix, 0.0, 0.0, sHelperPoint);
resultRect.setTo(sHelperPoint.x, sHelperPoint.y, 0, 0);
return resultRect;
}
else if (numChildren == 1)
{
return mChildren[0].getBounds(targetSpace, resultRect);
resultRect = mChildren[0].getBounds(targetSpace, resultRect);
}
else
{
Expand All @@ -288,8 +331,13 @@ package starling.display
}

resultRect.setTo(minX, minY, maxX - minX, maxY - minY);
return resultRect;
}

// if we have a scissor rect, intersect it with our bounds
if (mScissorRect != null)
resultRect = RectangleUtil.intersect(resultRect, getScissorBounds(targetSpace, resultRect));

return resultRect;
}

/** @inheritDoc */
Expand All @@ -298,6 +346,9 @@ package starling.display
if (forTouch && (!visible || !touchable))
return null;

if (mScissorRect != null && !mScissorRect.containsPoint(localPoint))
return null;

var localX:Number = localPoint.x;
var localY:Number = localPoint.y;

Expand All @@ -318,7 +369,18 @@ package starling.display

/** @inheritDoc */
public override function render(support:RenderSupport, parentAlpha:Number):void
{
{
if (mScissorRect != null)
{
var scissor:Rectangle = support.pushScissorRect(getScissorBounds(this.stage, sHelperRect));
// don't bother rendering our children if our scissored bounds are empty
if (scissor.isEmpty())
{
support.popScissorRect();
return;
}
}

var alpha:Number = parentAlpha * this.alpha;
var numChildren:int = mChildren.length;
var blendMode:String = support.blendMode;
Expand All @@ -342,6 +404,9 @@ package starling.display
support.popMatrix();
}
}

if (mScissorRect != null)
support.popScissorRect();
}

/** Dispatches an event on all children (recursively). The event must not bubble. */
Expand Down
4 changes: 2 additions & 2 deletions starling/src/starling/textures/RenderTexture.as
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ package starling.textures
// limit drawing to relevant area
sScissorRect.setTo(0, 0, mActiveTexture.nativeWidth, mActiveTexture.nativeHeight);

mSupport.scissorRectangle = sScissorRect;
mSupport.pushScissorRect(sScissorRect);
mSupport.renderTarget = mActiveTexture;
mSupport.clear();

Expand All @@ -187,7 +187,7 @@ package starling.textures
mSupport.finishQuadBatch();
mSupport.nextFrame();
mSupport.renderTarget = null;
mSupport.scissorRectangle = null;
mSupport.popScissorRect();
}
}

Expand Down

1 comment on commit 0cad6ae

@gsynuh
Copy link

@gsynuh gsynuh commented on 0cad6ae Mar 31, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @tconkling , this is a much appreciated commit :)

Please sign in to comment.