package no.agens.depth; import android.animation.ValueAnimator; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; import no.agens.depth.headers.Renderable; public class Smoke extends Renderable { public static final float WIND_SENSITIVITY = 7f; float height, width; int numberOfTurns; float density; private final float[] drawingVerts = new float[TOTAL_SLICES_COUNT * 2]; private final float[] staticVerts = new float[TOTAL_SLICES_COUNT * 2]; private static final int HORIZONTAL_SLICES = 1; private static final int VERTICAL_SLICES = 80; private static final int TOTAL_SLICES_COUNT = (HORIZONTAL_SLICES + 1) * (VERTICAL_SLICES + 1); private void createVerts() { float xDimesion = (float) bitmap.getWidth(); float yDimesion = (float) bitmap.getHeight(); int index = 0; for (int y = 0; y <= VERTICAL_SLICES; y++) { float fy = yDimesion * y / VERTICAL_SLICES; for (int x = 0; x <= HORIZONTAL_SLICES; x++) { float fx = xDimesion * x / HORIZONTAL_SLICES; setXY(drawingVerts, index, fx, fy); setXY(staticVerts, index, fx, fy); index += 1; } } } public void setXY(float[] array, int index, float x, float y) { array[index * 2 + 0] = x; array[index * 2 + 1] = y; } public Smoke(Bitmap bitmap, float x, float y, float height, float width, int numberOfTurns, float density) { super(bitmap, x, y); this.height = height; this.width = width; this.numberOfTurns = numberOfTurns; paint.setStyle(Paint.Style.STROKE); this.density = density; createVerts(); createPath(); pathPointOffsetAnim = ValueAnimator.ofFloat(0, ((bitmap.getHeight() / (float) numberOfTurns) * 2f) / bitmap.getHeight()).setDuration(1500); pathPointOffsetAnim.setRepeatCount(ValueAnimator.INFINITE); pathPointOffsetAnim.setRepeatMode(ValueAnimator.RESTART); pathPointOffsetAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { pathPointOffset = (float) animation.getAnimatedValue(); } }); pathPointOffsetAnim.setInterpolator(new LinearInterpolator()); pathPointOffsetAnim.start(); createPath(); } ValueAnimator pathPointOffsetAnim; public void destroy() { super.destroy(); pathPointOffsetAnim.cancel(); } @Override public void pause() { super.pause(); pathPointOffsetAnim.pause(); } @Override public void resume() { super.resume(); pathPointOffsetAnim.resume(); } Path smokePath = new Path(); Paint paint = new Paint(); float pathPointOffset = 1; @Override public void draw(Canvas canvas) { // alphaCanvas.drawPath(smokePath, paint); canvas.drawBitmapMesh(bitmap, HORIZONTAL_SLICES, VERTICAL_SLICES, drawingVerts, 0, null, 0, paint); } public void setY(float y) { this.y = y; createPath(); } @Override public void update(float deltaTime, float wind) { matchVertsToPath(wind); } private void createPath() { smokePath.reset(); smokePath.moveTo(x, y); int step = (int) (height / numberOfTurns); boolean goLeft = true; for (int i = 0; i < numberOfTurns; i++) { if (goLeft) smokePath.cubicTo(x, y - step * i, x + width, y - step * i - step / 2, x, y - step * i - step); else smokePath.cubicTo(x, y - step * i, x - width, y - step * i - step / 2, x, y - step * i - step); goLeft = !goLeft; } } float[] coords = new float[2]; float[] coords2 = new float[2]; private void matchVertsToPath(float wind) { PathMeasure pm = new PathMeasure(smokePath, false); for (int i = 0; i < staticVerts.length / 2; i++) { float yIndexValue = staticVerts[i * 2 + 1]; float xIndexValue = staticVerts[i * 2]; float percentOffsetY = (0.000001f + yIndexValue) / bitmap.getHeight(); float percentOffsetY2 = (0.000001f + yIndexValue) / (bitmap.getHeight() + ((bitmap.getHeight() / numberOfTurns) * 4f)); percentOffsetY2 += pathPointOffset; pm.getPosTan(pm.getLength() * (1f - percentOffsetY), coords, null); pm.getPosTan(pm.getLength() * (1f - percentOffsetY2), coords2, null); if (xIndexValue == 0) { float desiredXCoord = coords2[0] - (bitmap.getWidth()) / 2; desiredXCoord -= (desiredXCoord - x) * percentOffsetY; desiredXCoord += (wind / 3f) * density + ((wind * WIND_SENSITIVITY) * (1f - smokeExponentionWindStuff.getInterpolation(percentOffsetY))); setXY(drawingVerts, i, desiredXCoord, coords[1]); } else { float desiredXCoord = coords2[0] + (bitmap.getWidth()) / 2; desiredXCoord -= (desiredXCoord - x) * percentOffsetY; desiredXCoord += (wind / 3f) * density + ((wind * WIND_SENSITIVITY) * (1f - smokeExponentionWindStuff.getInterpolation(percentOffsetY))); setXY(drawingVerts, i, desiredXCoord, coords[1]); } } } DecelerateInterpolator smokeExponentionWindStuff = new DecelerateInterpolator(1f); }