Flutter Engine
The Flutter Engine
CubeActivity.java
Go to the documentation of this file.
1/*
2 * Copyright 2021 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8package org.skia.jetskidemo;
9
10import android.app.Activity;
11import android.content.res.Resources;
12import android.os.Bundle;
13import android.view.GestureDetector;
14import android.view.MotionEvent;
15import android.view.ScaleGestureDetector;
16import android.view.SurfaceView;
17
18import org.skia.jetski.Canvas;
19import org.skia.jetski.Matrix;
20import org.skia.jetski.Surface;
21import org.skia.jetski.util.SurfaceRenderer;
22
23import org.skia.jetskidemo.samples.ImageShaderSample;
24import org.skia.jetskidemo.samples.RuntimeSample;
25import org.skia.jetskidemo.samples.Sample;
26import org.skia.jetskidemo.samples.SkottieSample;
27
28import static java.lang.Math.tan;
29
30class Face {
31 private float rotX;
32 private float rotY;
33 public Sample sample;
34
35 Face(float rotX, float rotY, Sample sample) {
36 this.rotX = rotX;
37 this.rotY = rotY;
38 this.sample = sample;
39 }
40
42 return new Matrix().rotateY(rotY).rotateX(rotX).translate(0, 0, scale);
43 }
44}
45
46// TODO: make this public?
47class Vec3 {
48 public float x, y, z;
49
50 public Vec3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
51
52 public float length() { return (float)Math.sqrt(x*x + y*y + z*z); }
53
54 public Vec3 normalize() {
55 mul(1/length());
56 return this;
57 }
58
59 public Vec3 add(float v) {
60 x += v; y += v; z += v;
61 return this;
62 }
63
64 public Vec3 mul(float v) {
65 x *= v; y *= v; z *= v;
66 return this;
67 }
68
69 public float dot(Vec3 v) {
70 return x*v.x + y*v.y + z*v.z;
71 }
72
73 public Vec3 cross(Vec3 v) {
74 float xx = y*v.z - z*v.y,
75 yy = z*v.x - x*v.z,
76 zz = x*v.y - y*v.x;
77 x = xx; y = yy; z = zz;
78 return this;
79 }
80};
81
83 private Matrix mRotMatrix = new Matrix();
84 private Vec3 mRotAxis = new Vec3(0, 1, 0),
85 mCurrentDrag;
86 private float mRotSpeed = (float)Math.PI,
87 mCenterX,
88 mCenterY,
89 mRadius;
90
91 public VSphereAnimator(float x, float y, float r) {
92 mCenterX = x;
93 mCenterY = y;
94 mRadius = r;
95 }
96
97 public void animate(float dt) {
98 final float kDecay = 0.99f;
99
100 rotate(mRotAxis, mRotSpeed * dt);
101
102 mRotSpeed *= kDecay;
103 }
104
105 public Matrix getMatrix() {
106 return mRotMatrix;
107 }
108
109 public void fling(float dx, float dy) {
110 Vec3 u = normalVec(mCenterX, mCenterY),
111 v = normalVec(mCenterX + dx, mCenterY + dy);
112 mRotAxis = u.cross(v).normalize();
113
114 double flingSpeed = Math.sqrt(dx*dx + dy*dy)/mRadius;
115 mRotSpeed = (float)(flingSpeed*Math.PI);
116 }
117
118 public void startDrag(MotionEvent e) {
119 mCurrentDrag = normalVec(e.getX(), e.getY());
120 mRotSpeed = 0;
121 }
122
123 public void drag(MotionEvent e) {
124 Vec3 u = mCurrentDrag, // previous drag position
125 v = normalVec(e.getX(), e.getY()); // new drag position
126
127 float angle = (float)Math.acos(Math.max(-1, Math.min(1, u.dot(v)/u.length()/v.length())));
128 Vec3 axis = u.cross(v).normalize();
129
130 rotate(axis, angle);
131
132 mCurrentDrag = v;
133 }
134
135 private Vec3 normalVec(float x, float y) {
136 x = (x - mCenterX)/mRadius;
137 y = -(y - mCenterY)/mRadius;
138 float len2 = x*x + y*y;
139
140 if (len2 > 1) {
141 // normalize
142 float len = (float)Math.sqrt(len2);
143 x /= len;
144 y /= len;
145 len2 = 1;
146 }
147
148 return new Vec3(x, y, (float)Math.sqrt(1 - len2));
149 }
150
151 private void rotate(Vec3 axis, float angle) {
152 mRotMatrix = new Matrix().rotate(axis.x, axis.y, axis.z, angle)
153 .preConcat(mRotMatrix);
154 }
155};
156
157class CubeRenderer extends SurfaceRenderer implements GestureDetector.OnGestureListener,
158 ScaleGestureDetector.OnScaleGestureListener {
159 private VSphereAnimator mVSphere;
160 private Matrix mViewMatrix;
161 private float mCubeSideLength;
162 private long mPrevMS;
163 private Face[] mFaces;
164 private float mZoom = 1;
165
166 public CubeRenderer(Resources res) {
167 final float rot = (float) Math.PI;
168 mFaces = new Face[] {
169 new Face(0, -rot/2, new ImageShaderSample(res, R.raw.brickwork_texture)),
170 new Face(0, 0 , new SkottieSample(res, R.raw.im_thirsty)),
171 new Face(0, rot , new RuntimeSample(res, R.raw.runtime_shader1)),
172 new Face(rot/2, 0 , new SkottieSample(res, R.raw.permission)),
173 new Face(0, rot/2 , new RuntimeSample(res, R.raw.runtime_shader2)),
174 };
175 }
176
177 @Override
179 float cx = surface.getWidth() / 2,
180 cy = surface.getHeight() / 2,
181 r = Math.min(cx, cy);
182
183 mVSphere = new VSphereAnimator(cx, cy, r);
184
185 // square viewport size fitting the given surface
186 float vsz = r * 2;
187
188 mCubeSideLength = vsz * 0.5f;
189
190 float viewAngle = (float)Math.PI / 4f,
191 viewDistance = (float)(r / tan(viewAngle/2));
192
193 mViewMatrix = new Matrix()
194 // centered viewport
195 .translate(cx, cy)
196 // perspective
197 .scale(vsz/2, vsz/2, 1)
198 .preConcat(Matrix.makePerspective(0.05f, viewDistance, viewAngle))
199 // camera
200 .preConcat(Matrix.makeLookAt(0, 0, -viewDistance, 0, 0, 0, 0, 1, 0));
201 }
202
203 @Override
204 protected void onRenderFrame(Canvas canvas, long ms) {
205 if (mPrevMS == 0) {
206 mPrevMS = ms;
207 }
208
209 mVSphere.animate((ms - mPrevMS) / 1000.f);
210 mPrevMS = ms;
211
212
213 // clear canvas
214 canvas.drawColor(0xffffffff);
215
216 canvas.save();
217 canvas.concat(mViewMatrix);
218 canvas.scale(mZoom, mZoom);
219 canvas.concat(mVSphere.getMatrix());
220
221 drawFaces(canvas, ms, false);
222 drawFaces(canvas, ms, true);
223
224 canvas.restore();
225 }
226
227 private void drawFaces(Canvas canvas, long ms, boolean renderFront) {
228 for (Face f : mFaces) {
229 canvas.save();
230 canvas.concat(f.asMatrix(mCubeSideLength/2));
231
232 if (front(canvas.getLocalToDevice()) == renderFront) {
233 f.sample.render(canvas, ms,
234 -mCubeSideLength/2,
235 -mCubeSideLength/2,
236 mCubeSideLength/2,
237 mCubeSideLength/2);
238 }
239 canvas.restore();
240 }
241 }
242
243 private void zoom(float z) {
244 final float kMinZoom = 0.5f,
245 kMaxZoom = 3.0f;
246 mZoom = Math.max(kMinZoom, Math.min(kMaxZoom, mZoom * z));
247 }
248
249 @Override
250 public boolean onFling(MotionEvent e1, MotionEvent e2, float dx, float dy) {
251 mVSphere.fling(dx, dy);
252 return true;
253 }
254
255 @Override
256 public boolean onDown(MotionEvent e) {
257 mVSphere.startDrag(e);
258 return true;
259 }
260
261 @Override
262 public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx, float dy) {
263 mVSphere.drag(e2);
264 return true;
265 }
266
267 // GestureDetector stubs
268 @Override
269 public boolean onSingleTapUp(MotionEvent e) { return false; }
270
271 @Override
272 public void onLongPress(MotionEvent e) {}
273
274 @Override
275 public void onShowPress(MotionEvent e) {}
276
277 private boolean front(Matrix m) {
278 Matrix m2;
279 try {
280 m2 = Matrix.makeInverse(m);
281 } catch (RuntimeException e) {
282 m2 = new Matrix();
283 }
284 return m2.getAtRowCol(2, 2) > 0;
285 }
286
287 // OnScaleGestureListener
288 @Override
289 public boolean onScale(ScaleGestureDetector detector) {
290 zoom(detector.getScaleFactor());
291 return true;
292 }
293
294 @Override
295 public boolean onScaleBegin(ScaleGestureDetector detector) {
296 zoom(detector.getScaleFactor());
297 return true;
298 }
299
300 @Override
301 public void onScaleEnd(ScaleGestureDetector detector) {
302 zoom(detector.getScaleFactor());
303 }
304}
305
306public class CubeActivity extends Activity {
307 static {
308 System.loadLibrary("jetski");
309 }
310
311 private GestureDetector mGestureDetector;
312 private ScaleGestureDetector mScaleDetector;
313
314 @Override
315 protected void onCreate(Bundle savedInstanceState) {
316 super.onCreate(savedInstanceState);
317 setContentView(R.layout.activity_cube);
318
319 SurfaceView sv = findViewById(R.id.surfaceView);
320
321 CubeRenderer renderer = new CubeRenderer(getResources());
322 sv.getHolder().addCallback(renderer);
323
324 mGestureDetector = new GestureDetector(this, renderer);
325 mScaleDetector = new ScaleGestureDetector(this, renderer);
326 }
327
328 @Override
329 public boolean onTouchEvent(MotionEvent e) {
330 // always dispatch to both detectors
331 mGestureDetector.onTouchEvent(e);
332 mScaleDetector.onTouchEvent(e);
333
334 return true;
335 }
336}
float e1
void scale(float sx, float sy, float sz)
Definition: Canvas.java:69
void concat(Matrix m)
Definition: Canvas.java:49
Matrix getLocalToDevice()
Definition: Canvas.java:44
void drawColor(Color c)
Definition: Canvas.java:99
static Matrix makePerspective(float near, float far, float angle)
Definition: Matrix.java:51
static Matrix makeLookAt(float eyeX, float eyeY, float eyeZ, float coaX, float coaY, float coaZ, float upX, float upY, float upZ)
Definition: Matrix.java:43
static Matrix makeInverse(Matrix m)
Definition: Matrix.java:55
float getAtRowCol(int r, int c)
Definition: Matrix.java:85
boolean onTouchEvent(MotionEvent e)
void onCreate(Bundle savedInstanceState)
boolean onFling(MotionEvent e1, MotionEvent e2, float dx, float dy)
boolean onSingleTapUp(MotionEvent e)
boolean onScaleBegin(ScaleGestureDetector detector)
boolean onScroll(MotionEvent e1, MotionEvent e2, float dx, float dy)
boolean onScale(ScaleGestureDetector detector)
boolean onDown(MotionEvent e)
void onSurfaceInitialized(Surface surface)
void onRenderFrame(Canvas canvas, long ms)
Face(float rotX, float rotY, Sample sample)
Matrix asMatrix(float scale)
void fling(float dx, float dy)
Vec3(float x, float y, float z)
VkSurfaceKHR surface
Definition: main.cc:49
#define R(r)
double y
double x
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
SK_API sk_sp< PrecompileColorFilter > Matrix()
const Scalar scale