
Sprite nodes use Point instead of Vector2 Updates sprite test app Refactors accounting for pivot points in sprites Adds abstract NodeWithSize class in Sprites. Refactors SpriteNode to Sprite Refactors TransformNode to Node (may need to find another name as it conflicts with Sky's Node). Sprite system now uses and caches transformation matrices. R=abarth@chromium.org Review URL: https://codereview.chromium.org/1180703002.
268 lines
5.7 KiB
Dart
268 lines
5.7 KiB
Dart
part of sprites;
|
|
|
|
double degrees2radians(double degrees) => degrees * Math.PI/180.8;
|
|
|
|
double radians2degrees(double radians) => radians * 180.0/Math.PI;
|
|
|
|
class Node {
|
|
|
|
// Member variables
|
|
|
|
SpriteBox _spriteBox;
|
|
Node _parent;
|
|
|
|
Point _position;
|
|
double _rotation;
|
|
|
|
bool _isMatrixDirty;
|
|
Matrix4 _transformMatrix;
|
|
Matrix4 _transformMatrixFromWorld;
|
|
|
|
double _scaleX;
|
|
double _scaleY;
|
|
|
|
bool visible;
|
|
|
|
double _zPosition;
|
|
int _addedOrder;
|
|
int _childrenLastAddedOrder;
|
|
bool _childrenNeedSorting;
|
|
|
|
List<Node>_children;
|
|
|
|
// Constructors
|
|
|
|
Node() {
|
|
_rotation = 0.0;
|
|
_position = new Point(0.0, 0.0);
|
|
_scaleX = _scaleY = 1.0;
|
|
_isMatrixDirty = false;
|
|
_transformMatrix = new Matrix4.identity();
|
|
_children = [];
|
|
_childrenNeedSorting = false;
|
|
_childrenLastAddedOrder = 0;
|
|
visible = true;
|
|
}
|
|
|
|
// Property setters and getters
|
|
|
|
SpriteBox get spriteBox => _spriteBox;
|
|
|
|
Node get parent => _parent;
|
|
|
|
double get rotation => _rotation;
|
|
|
|
void set rotation(double rotation) {
|
|
_rotation = rotation;
|
|
_isMatrixDirty = true;
|
|
}
|
|
|
|
Point get position => _position;
|
|
|
|
void set position(Point position) {
|
|
_position = position;
|
|
_isMatrixDirty = true;
|
|
}
|
|
|
|
double get zPosition => _zPosition;
|
|
|
|
void set zPosition(double zPosition) {
|
|
_zPosition = zPosition;
|
|
if (_parent != null) {
|
|
_parent._childrenNeedSorting = true;
|
|
}
|
|
}
|
|
|
|
double get scale {
|
|
assert(_scaleX == _scaleY);
|
|
return _scaleX;
|
|
}
|
|
|
|
void set scale(double scale) {
|
|
_scaleX = _scaleY = scale;
|
|
_isMatrixDirty = true;
|
|
}
|
|
|
|
List<Node> get children => _children;
|
|
|
|
// Adding and removing children
|
|
|
|
void addChild(Node child) {
|
|
assert(child._parent == null);
|
|
|
|
_childrenNeedSorting = true;
|
|
_children.add(child);
|
|
child._parent = this;
|
|
child._spriteBox = this._spriteBox;
|
|
_childrenLastAddedOrder += 1;
|
|
child._addedOrder = _childrenLastAddedOrder;
|
|
}
|
|
|
|
void removeChild(Node child) {
|
|
if (_children.remove(child)) {
|
|
child._parent = null;
|
|
child._spriteBox = null;
|
|
}
|
|
}
|
|
|
|
void removeFromParent() {
|
|
assert(_parent != null);
|
|
_parent.removeFromParent();
|
|
}
|
|
|
|
void removeAllChildren() {
|
|
for (Node child in _children) {
|
|
child._parent = null;
|
|
child._spriteBox = null;
|
|
}
|
|
_children = [];
|
|
_childrenNeedSorting = false;
|
|
}
|
|
|
|
// Calculating the transformation matrix
|
|
|
|
Matrix4 get transformMatrix {
|
|
if (!_isMatrixDirty) {
|
|
return _transformMatrix;
|
|
}
|
|
|
|
double cx, sx, cy, sy;
|
|
|
|
if (_rotation == 0.0) {
|
|
cx = 1.0;
|
|
sx = 0.0;
|
|
cy = 1.0;
|
|
sy = 0.0;
|
|
}
|
|
else {
|
|
double radiansX = degrees2radians(_rotation);
|
|
double radiansY = degrees2radians(_rotation);
|
|
|
|
cx = Math.cos(radiansX);
|
|
sx = Math.sin(radiansX);
|
|
cy = Math.cos(radiansY);
|
|
sy = Math.sin(radiansY);
|
|
}
|
|
|
|
// Create transformation matrix for scale, position and rotation
|
|
_transformMatrix.setValues(cy * _scaleX, sy * _scaleX, 0.0, 0.0,
|
|
-sx * _scaleY, cx * _scaleY, 0.0, 0.0,
|
|
0.0, 0.0, 1.0, 0.0,
|
|
_position.x, _position.y, 0.0, 1.0
|
|
);
|
|
|
|
return _transformMatrix;
|
|
}
|
|
|
|
// Transforms to other nodes
|
|
|
|
Matrix4 _nodeToBoxMatrix() {
|
|
Matrix4 t = transformMatrix;
|
|
|
|
Node p = this.parent;
|
|
while (p != null) {
|
|
t = new Matrix4.copy(p.transformMatrix).multiply(t);
|
|
p = p.parent;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
Matrix4 _boxToNodeMatrix() {
|
|
Matrix4 t = _nodeToBoxMatrix();
|
|
t.invert();
|
|
return t;
|
|
}
|
|
|
|
Point convertPointToNodeSpace(Point boxPoint) {
|
|
assert(boxPoint != null);
|
|
assert(_spriteBox != null);
|
|
|
|
Vector4 v =_boxToNodeMatrix().transform(new Vector4(boxPoint.x, boxPoint.y, 0.0, 1.0));
|
|
return new Point(v[0], v[1]);
|
|
}
|
|
|
|
Point convertPointToBoxSpace(Point nodePoint) {
|
|
assert(nodePoint != null);
|
|
assert(_spriteBox != null);
|
|
|
|
Vector4 v =_nodeToBoxMatrix().transform(new Vector4(nodePoint.x, nodePoint.y, 0.0, 1.0));
|
|
return new Point(v[0], v[1]);
|
|
}
|
|
|
|
Point convertPointFromNode(Point point, Node node) {
|
|
assert(node != null);
|
|
assert(point != null);
|
|
assert(_spriteBox != null);
|
|
assert(_spriteBox == node._spriteBox);
|
|
|
|
Point boxPoint = node.convertPointToBoxSpace(point);
|
|
Point localPoint = convertPointToNodeSpace(boxPoint);
|
|
|
|
return localPoint;
|
|
}
|
|
|
|
// Hit test
|
|
|
|
bool hitTest(Point nodePoint) {
|
|
assert(nodePoint != null);
|
|
|
|
return false;
|
|
}
|
|
|
|
// Rendering
|
|
|
|
void visit(PictureRecorder canvas) {
|
|
if (!visible) return;
|
|
|
|
prePaint(canvas);
|
|
paint(canvas);
|
|
visitChildren(canvas);
|
|
postPaint(canvas);
|
|
}
|
|
|
|
void prePaint(PictureRecorder canvas) {
|
|
canvas.save();
|
|
|
|
// TODO: Can this be done more efficiently?
|
|
// Get the transformation matrix and apply transform
|
|
List<double> matrix = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
|
|
this.transformMatrix.copyIntoArray(matrix);
|
|
Float32List list32 = new Float32List.fromList(matrix);
|
|
canvas.concat(list32);
|
|
}
|
|
|
|
void paint(PictureRecorder canvas) {
|
|
|
|
}
|
|
|
|
void visitChildren(PictureRecorder canvas) {
|
|
// Sort children primarily by zPosition, secondarily by added order
|
|
if (_childrenNeedSorting) {
|
|
_children.sort((Node a, Node b) {
|
|
if (a._zPosition == b._zPosition) {
|
|
return b._addedOrder - a._addedOrder;
|
|
}
|
|
else if (a._zPosition > b._zPosition) {
|
|
return 1;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
});
|
|
_childrenNeedSorting = false;
|
|
}
|
|
|
|
// Visit each child
|
|
_children.forEach((child) => child.visit(canvas));
|
|
}
|
|
|
|
void postPaint(PictureRecorder canvas) {
|
|
canvas.restore();
|
|
}
|
|
|
|
// Receiving update calls
|
|
|
|
void update(double dt) {
|
|
|
|
}
|
|
} |