Commit 8900b4c8 authored by Viktor Lidholt's avatar Viktor Lidholt

Adds TexturedLine and animated EffectLine to sprites

parent 6d78b050
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
import 'dart:sky' as sky;
import 'package:sky/services.dart';
import 'package:sky/rendering.dart';
import 'package:sky/theme/colors.dart' as colors;
import 'package:sky/widgets.dart';
import 'package:skysprites/skysprites.dart';
AssetBundle _initBundle() {
if (rootBundle != null)
return rootBundle;
return new NetworkAssetBundle(Uri.base);
}
final AssetBundle _bundle = _initBundle();
ImageMap _images;
SpriteSheet _spriteSheet;
TestApp _app;
main() async {
_images = new ImageMap(_bundle);
await _images.load([
'assets/checker.png',
'assets/line_effects.png'
]);
assert(_images["assets/checker.png"] != null);
_app = new TestApp();
runApp(_app);
}
class TestApp extends App {
TestApp() {
_testBed = new TestBed(_labelTexts[_selectedLine]);
}
TestBed _testBed;
int _selectedLine = 0;
List<String> _labelTexts = [
"Colored",
"Smoke",
"Electric"
];
Widget build() {
ThemeData theme = new ThemeData(
brightness: ThemeBrightness.light,
primarySwatch: colors.Purple
);
return new Theme(
data: theme,
child: new Title(
title: 'Test drawAtlas',
child: _buildColumn()
)
);
}
Column _buildColumn() {
return new Column([
new Flexible(child: _buildSpriteWidget()),
_buildTabBar()
]);
}
TabBar _buildTabBar() {
return new TabBar(
labels: _buildTabLabels(),
selectedIndex: _selectedLine,
onChanged: (int selectedLine) {
setState(() {
_selectedLine = selectedLine;
});
}
);
}
List<TabLabel> _buildTabLabels() {
List<TabLabel> labels = [];
for(String text in _labelTexts) {
labels.add(new TabLabel(text: text));
}
return labels;
}
SpriteWidget _buildSpriteWidget() {
_testBed.setupLine(_labelTexts[_selectedLine]);
return new SpriteWidget(
_testBed,
SpriteBoxTransformMode.letterbox
);
}
}
class TestBed extends NodeWithSize {
EffectLine _line;
TestBed(String lineType) : super(new Size(1024.0, 1024.0)) {
userInteractionEnabled = true;
setupLine(lineType);
}
void setupLine(String lineType) {
if (_line != null) {
_line.removeFromParent();
}
if (lineType == "Colored") {
// Create a line with no texture and a color sequence
_line = new EffectLine(
texture: null,
colorSequence: new ColorSequence.fromStartAndEndColor(new Color(0xffff0000), new Color(0xff0000ff)),
widthMode: EffectLineWidthMode.barrel,
minWidth: 20.0,
maxWidth: 50.0,
animationMode: EffectLineAnimationMode.scroll,
fadeAfterDelay: 1.0,
fadeDuration: 1.0
);
} else if (lineType == "Smoke") {
Texture baseTexture = new Texture(_images['assets/line_effects.png']);
Texture smokyLineTexture = baseTexture.textureFromRect(new Rect.fromLTRB(0.0, 0.0, 1024.0, 128.0));
_line = new EffectLine(
texture: smokyLineTexture,
textureLoopLength: 300.0,
colorSequence: new ColorSequence.fromStartAndEndColor(new Color(0xffffffff), new Color(0x00ffffff)),
widthMode: EffectLineWidthMode.barrel,
minWidth: 20.0,
maxWidth: 80.0,
animationMode: EffectLineAnimationMode.scroll
);
} else if (lineType == "Electric") {
Texture baseTexture = new Texture(_images['assets/line_effects.png']);
Texture electricLineTexture = baseTexture.textureFromRect(new Rect.fromLTRB(0.0, 384.0, 1024.0, 512.0));
_line = new EffectLine(
texture: electricLineTexture,
textureLoopLength: 300.0,
widthMode: EffectLineWidthMode.barrel,
minWidth: 20.0,
maxWidth: 100.0,
animationMode: EffectLineAnimationMode.random
);
}
addChild(_line);
}
bool handleEvent(SpriteBoxEvent event) {
if (event.type == "pointerdown") _line.points = [];
if (event.type == "pointerdown" || event.type == "pointermove") {
Point pos = convertPointToNodeSpace(event.boxPosition);
_line.addPoint(pos);
}
return true;
}
}
......@@ -37,6 +37,8 @@ class ColorSequence {
Color colorAtPosition(double pos) {
assert(pos >= 0.0 && pos <= 1.0);
if (pos == 0.0) return colors[0];
double lastStop = colorStops[0];
Color lastColor = colors[0];
......
part of skysprites;
enum EffectLineWidthMode {
linear,
barrel,
}
enum EffectLineAnimationMode {
none,
scroll,
random,
}
class EffectLine extends Node {
EffectLine({
this.texture: null,
List<Point> points,
this.widthMode : EffectLineWidthMode.linear,
this.minWidth: 10.0,
this.maxWidth: 10.0,
this.animationMode: EffectLineAnimationMode.none,
this.scrollSpeed: 0.1,
this.fadeDuration: null,
this.fadeAfterDelay: null,
this.textureLoopLength: null,
this.simplify: true,
ColorSequence colorSequence
}) {
if (points == null) this.points = [];
else this.points = points;
_colorSequence = colorSequence;
if (_colorSequence == null)
_colorSequence = new ColorSequence.fromStartAndEndColor(
new Color(0xffffffff),
new Color(0xffffffff));
_painter = new TexturedLinePainter(points, _colors, _widths, texture);
_painter.textureLoopLength = textureLoopLength;
}
final Texture texture;
final EffectLineWidthMode widthMode;
final double minWidth;
final double maxWidth;
final EffectLineAnimationMode animationMode;
final double scrollSpeed;
ColorSequence _colorSequence;
ColorSequence get colorSequence => _colorSequence;
List<Point> _points;
List<Point> get points => _points;
set points(List<Point> points) {
_points = points;
_pointAges = [];
for (int i = 0; i < _points.length; i++) {
_pointAges.add(0.0);
}
}
List<double> _pointAges;
List<Color> _colors;
List<double> _widths;
final double fadeDuration;
final double fadeAfterDelay;
final double textureLoopLength;
final bool simplify;
TexturedLinePainter _painter;
double _offset = 0.0;
void update(double dt) {
// Update scrolling position
if (animationMode == EffectLineAnimationMode.scroll) {
_offset += dt * scrollSpeed;
_offset %= 1.0;
} else if (animationMode == EffectLineAnimationMode.random) {
_offset = randomDouble();
}
// Update age of line points, and remove if neccessary
if (fadeDuration != null && fadeAfterDelay != null) {
for (int i = _points.length - 1; i >= 0; i--) {
_pointAges[i] += dt;
if (_pointAges[i] > (fadeDuration + fadeAfterDelay)) {
_pointAges.removeAt(i);
_points.removeAt(i);
}
}
}
}
void paint(PaintingCanvas canvas) {
if (points.length < 2) return;
//_painter.textureLoopLength = textureLoopLength;
_painter.points = points;
// Calculate colors
List<double> stops = _painter.calculatedTextureStops;
List<Color> colors = [];
for (int i = 0; i < stops.length; i++) {
double stop = stops[i];
Color color = _colorSequence.colorAtPosition(stop);
if (fadeDuration != null && fadeAfterDelay != null) {
double age = _pointAges[i];
if (age > fadeAfterDelay) {
double fade = 1.0 - (age - fadeAfterDelay) / fadeDuration;
int alpha = (color.alpha * fade).toInt().clamp(0, 255);
color = new Color.fromARGB(alpha, color.red, color.green, color.blue);
}
}
colors.add(color);
}
_painter.colors = colors;
// Calculate widths
List<double> widths = [];
for (double stop in stops) {
if (widthMode == EffectLineWidthMode.linear) {
double width = minWidth + (maxWidth - minWidth) * stop;
widths.add(width);
} else if (widthMode == EffectLineWidthMode.barrel) {
double width = minWidth + math.sin(stop * math.PI) * (maxWidth - minWidth);
widths.add(width);
}
}
_painter.widths = widths;
_painter.textureStopOffset = _offset;
_painter.paint(canvas);
}
void addPoint(Point point) {
// Skip duplicate points
if (points.length > 0 && point.x == points[points.length - 1].x && point.y == points[points.length - 1].y)
return;
if (simplify) {
}
// Add point and point's age
_points.add(point);
_pointAges.add(0.0);
}
}
......@@ -21,9 +21,10 @@ import 'package:sky_services/media/media.mojom.dart';
import 'package:vector_math/vector_math.dart';
part 'action.dart';
part 'constraint.dart';
part 'action_spline.dart';
part 'color_secuence.dart';
part 'constraint.dart';
part 'effect_line.dart';
part 'image_map.dart';
part 'label.dart';
part 'layer.dart';
......@@ -38,5 +39,6 @@ part 'spritesheet.dart';
part 'sprite_box.dart';
part 'sprite_widget.dart';
part 'texture.dart';
part 'textured_line.dart';
part 'util.dart';
part 'virtual_joystick.dart';
part of skysprites;
class TexturedLine extends Node {
TexturedLine(List<Point> points, List<Color> colors, List<double> widths, [Texture texture, List<double> textureStops]) {
painter = new TexturedLinePainter(points, colors, widths, texture, textureStops);
}
TexturedLinePainter painter;
void paint(PaintingCanvas canvas) {
painter.paint(canvas);
}
}
class TexturedLinePainter {
TexturedLinePainter(this._points, this.colors, this.widths, [Texture texture, this.textureStops]) {
this.texture = texture;
}
List<Point> _points;
List<Point> get points => _points;
set points(List<Point> points) {
_points = points;
_calculatedTextureStops = null;
}
List<Color> colors;
List<double> widths;
Texture _texture;
Texture get texture => _texture;
set texture(Texture texture) {
_texture = texture;
if (texture == null) {
_cachedPaint = new Paint();
} else {
Matrix4 matrix = new Matrix4.identity();
ImageShader shader = new ImageShader(texture.image,
TileMode.repeated, TileMode.repeated, matrix.storage);
_cachedPaint = new Paint();
_cachedPaint.setShader(shader);
}
}
List<double> textureStops;
List<double> _calculatedTextureStops;
List<double> get calculatedTextureStops {
if (_calculatedTextureStops == null)
_calculateTextureStops();
return _calculatedTextureStops;
}
double _length;
double get length {
if (_calculatedTextureStops == null)
_calculateTextureStops();
return _length;
}
double textureStopOffset = 0.0;
double _textureLoopLength;
get textureLoopLength => textureLoopLength;
set textureLoopLength(double textureLoopLength) {
_textureLoopLength = textureLoopLength;
_calculatedTextureStops = null;
}
Paint _cachedPaint = new Paint();
void paint(PaintingCanvas canvas) {
// Check input values
assert(_points != null);
if (_points.length < 2) return;
assert(_points.length == colors.length);
assert(_points.length == widths.length);
// Calculate normals
List<Vector2> vectors = [];
for (Point pt in _points) {
vectors.add(new Vector2(pt.x, pt.y));
}
List<Vector2> miters = _computeMiterList(vectors, false);
List<Point> vertices = [];
List<int> indicies = [];
List<Color> verticeColors = [];
List<Point> textureCoordinates;
double textureTop;
double textureBottom;
List<double> stops;
// Add first point
Point lastPoint = _points[0];
Vector2 lastMiter = miters[0];
// Add vertices and colors
_addVerticesForPoint(vertices, lastPoint, lastMiter, widths[0]);
verticeColors.add(colors[0]);
verticeColors.add(colors[0]);
if (texture != null) {
assert(texture.rotated == false);
// Setup for calculating texture coordinates
textureTop = texture.frame.top;
textureBottom = texture.frame.bottom;
textureCoordinates = [];
// Use correct stops
if (textureStops != null) {
assert(_points.length == textureStops.length);
stops = textureStops;
} else {
if (_calculatedTextureStops == null) _calculateTextureStops();
stops = _calculatedTextureStops;
}
// Texture coordinate points
double xPos = _xPosForStop(stops[0]);
textureCoordinates.add(new Point(xPos, textureTop));
textureCoordinates.add(new Point(xPos, textureBottom));
}
// Add the rest of the points
for (int i = 1; i < _points.length; i++) {
// Add vertices
Point currentPoint = _points[i];
Vector2 currentMiter = miters[i];
_addVerticesForPoint(vertices, currentPoint, currentMiter, widths[i]);
// Add references to the triangles
int lastIndex0 = (i - 1) * 2;
int lastIndex1 = (i - 1) * 2 + 1;
int currentIndex0 = i * 2;
int currentIndex1 = i * 2 + 1;
indicies.addAll([lastIndex0, lastIndex1, currentIndex0]);
indicies.addAll([lastIndex1, currentIndex1, currentIndex0]);
// Add colors
verticeColors.add(colors[i]);
verticeColors.add(colors[i]);
if (texture != null) {
// Texture coordinate points
double xPos = _xPosForStop(stops[i]);
textureCoordinates.add(new Point(xPos, textureTop));
textureCoordinates.add(new Point(xPos, textureBottom));
}
// Update last values
lastPoint = currentPoint;
lastMiter = currentMiter;
}
canvas.drawVertices(VertexMode.triangles, vertices, textureCoordinates, verticeColors, TransferMode.modulate, indicies, _cachedPaint);
}
double _xPosForStop(double stop) {
if (_textureLoopLength == null) {
return texture.frame.left + texture.frame.width * (stop - textureStopOffset);
} else {
return texture.frame.left + texture.frame.width * (stop - textureStopOffset * (_textureLoopLength / length)) * (length / _textureLoopLength);
}
}
void _addVerticesForPoint(List<Point> vertices, Point point, Vector2 miter, double width) {
double halfWidth = width / 2.0;
Offset offset0 = new Offset(miter[0] * halfWidth, miter[1] * halfWidth);
Offset offset1 = new Offset(-miter[0] * halfWidth, -miter[1] * halfWidth);
vertices.add(point + offset0);
vertices.add(point + offset1);
}
void _calculateTextureStops() {
List<double> stops = [];
double length = 0.0;
// Add first stop
stops.add(0.0);
// Calculate distance to each point from the first point along the line
for (int i = 1; i < _points.length; i++) {
Point lastPoint = _points[i - 1];
Point currentPoint = _points[i];
double dist = GameMath.pointQuickDist(lastPoint, currentPoint);
length += dist;
stops.add(length);
}
// Normalize the values in the range [0.0, 1.0]
for (int i = 1; i < points.length; i++) {
stops[i] = stops[i] / length;
new Point(512.0, 512.0);
}
_calculatedTextureStops = stops;
_length = length;
}
}
Vector2 _computeMiter(Vector2 lineA, Vector2 lineB) {
Vector2 miter = new Vector2(- (lineA[1] + lineB[1]), lineA[0] + lineB[0]);
miter.normalize();
double miterLength = 1.0 / dot2(miter, new Vector2(-lineA[1], lineA[0]));
miter = miter.scale(miterLength);
return miter;
}
Vector2 _vectorNormal(Vector2 v) {
return new Vector2(-v[1], v[0]);
}
Vector2 _vectorDirection(Vector2 a, Vector2 b) {
Vector2 result = a - b;
return result.normalize();
}
List<Vector2> _computeMiterList(List<Vector2> points, bool closed) {
List<Vector2> out = [];
Vector2 curNormal = null;
if (closed) {
points = new List<Vector2>.from(points);
points.add(points[0]);
}
int total = points.length;
for (int i = 1; i < total; i++) {
Vector2 last = points[i - 1];
Vector2 cur = points[i];
Vector2 next = (i < total - 1) ? points[i + 1] : null;
Vector2 lineA = _vectorDirection(cur, last);
if (curNormal == null) {
curNormal = _vectorNormal(lineA);
}
if (i == 1) {
out.add(curNormal);
}
if (next == null) {
curNormal = _vectorNormal(lineA);
out.add(curNormal);
} else {
Vector2 lineB = _vectorDirection(next, cur);
Vector2 miter = _computeMiter(lineA, lineB);
out.add(miter);
}
}
return out;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment