Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2020 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 | |
| 8 | #include "include/core/SkCanvas.h" |
| 9 | #include "include/core/SkMatrix44.h" |
| 10 | #include "include/core/SkPaint.h" |
| 11 | #include "include/core/SkRRect.h" |
Mike Reed | 7543587 | 2020-01-13 21:15:06 -0500 | [diff] [blame] | 12 | #include "include/private/SkM44.h" |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 13 | #include "include/utils/Sk3D.h" |
| 14 | #include "include/utils/SkRandom.h" |
| 15 | #include "samplecode/Sample.h" |
| 16 | #include "tools/Resources.h" |
| 17 | |
| 18 | static SkMatrix44 inv(const SkMatrix44& m) { |
| 19 | SkMatrix44 inverse; |
| 20 | SkAssertResult(m.invert(&inverse)); |
| 21 | return inverse; |
| 22 | } |
| 23 | |
| 24 | class Sample3DView : public Sample { |
| 25 | protected: |
| 26 | float fNear = 0.05f; |
| 27 | float fFar = 4; |
| 28 | float fAngle = SK_ScalarPI / 12; |
| 29 | |
| 30 | SkPoint3 fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 }; |
| 31 | SkPoint3 fCOA { 0, 0, 0 }; |
| 32 | SkPoint3 fUp { 0, 1, 0 }; |
| 33 | |
| 34 | SkMatrix44 fRot; |
| 35 | SkPoint3 fTrans; |
| 36 | |
| 37 | void rotate(float x, float y, float z) { |
| 38 | SkMatrix44 r; |
| 39 | if (x) { |
| 40 | r.setRotateAboutUnit(1, 0, 0, x); |
| 41 | } else if (y) { |
| 42 | r.setRotateAboutUnit(0, 1, 0, y); |
| 43 | } else { |
| 44 | r.setRotateAboutUnit(0, 0, 1, z); |
| 45 | } |
| 46 | fRot.postConcat(r); |
| 47 | } |
| 48 | |
| 49 | public: |
Mike Reed | ee0a03a | 2020-01-14 16:44:47 -0500 | [diff] [blame^] | 50 | void saveCamera(SkCanvas* canvas, const SkRect& area, SkScalar zscale) { |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 51 | SkMatrix44 camera, |
| 52 | perspective, |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 53 | viewport; |
| 54 | |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 55 | Sk3Perspective(&perspective, fNear, fFar, fAngle); |
| 56 | Sk3LookAt(&camera, fEye, fCOA, fUp); |
Mike Reed | 7543587 | 2020-01-13 21:15:06 -0500 | [diff] [blame] | 57 | viewport.setScale(area.width()*0.5f, area.height()*0.5f, zscale) |
| 58 | .postTranslate(area.centerX(), area.centerY(), 0); |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 59 | |
Mike Reed | ee0a03a | 2020-01-14 16:44:47 -0500 | [diff] [blame^] | 60 | canvas->saveCamera(viewport * perspective, camera * inv(viewport)); |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | bool onChar(SkUnichar uni) override { |
| 64 | float delta = SK_ScalarPI / 30; |
| 65 | switch (uni) { |
| 66 | case '8': this->rotate( delta, 0, 0); return true; |
| 67 | case '2': this->rotate(-delta, 0, 0); return true; |
| 68 | case '4': this->rotate(0, delta, 0); return true; |
| 69 | case '6': this->rotate(0, -delta, 0); return true; |
| 70 | case '-': this->rotate(0, 0, delta); return true; |
| 71 | case '+': this->rotate(0, 0, -delta); return true; |
| 72 | |
| 73 | case 'i': fTrans.fZ += 0.1f; SkDebugf("z %g\n", fTrans.fZ); return true; |
| 74 | case 'k': fTrans.fZ -= 0.1f; SkDebugf("z %g\n", fTrans.fZ); return true; |
| 75 | |
| 76 | case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true; |
| 77 | case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true; |
| 78 | case 'f': fFar += 0.1f; SkDebugf("far %g\n", fFar); return true; |
| 79 | case 'F': fFar -= 0.1f; SkDebugf("far %g\n", fFar); return true; |
| 80 | default: break; |
| 81 | } |
| 82 | return false; |
| 83 | } |
| 84 | }; |
| 85 | |
| 86 | struct SkV3 { |
| 87 | float x, y, z; |
| 88 | |
| 89 | static SkScalar Dot(const SkV3& a, const SkV3& b) { return a.x*b.x + a.y*b.y + a.z*b.z; } |
| 90 | static SkV3 Cross(const SkV3& a, const SkV3& b) { |
| 91 | return { a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x }; |
| 92 | } |
| 93 | |
| 94 | SkV3 operator+(const SkV3& v) const { return { x + v.x, y + v.y, z + v.z }; } |
| 95 | SkV3 operator-(const SkV3& v) const { return { x - v.x, y - v.y, z - v.z }; } |
| 96 | |
| 97 | friend SkV3 operator*(const SkV3& v, SkScalar s) { |
| 98 | return { v.x*s, v.y*s, v.z*s }; |
| 99 | } |
| 100 | friend SkV3 operator*(SkScalar s, const SkV3& v) { return v*s; } |
| 101 | |
| 102 | SkScalar operator*(const SkV3& v) const { return Dot(*this, v); } |
| 103 | SkV3 operator%(const SkV3& v) const { return Cross(*this, v); } |
| 104 | |
| 105 | SkScalar lengthSquared() const { return Dot(*this, *this); } |
| 106 | SkScalar length() const { return SkScalarSqrt(Dot(*this, *this)); } |
| 107 | }; |
| 108 | |
| 109 | typedef SkV3 SkP3; |
| 110 | |
| 111 | static SkMatrix44 RX(SkScalar rad) { |
| 112 | SkScalar c = SkScalarCos(rad), s = SkScalarSin(rad); |
| 113 | SkMatrix44 m; |
| 114 | m.set3x3(1, 0, 0, |
| 115 | 0, c, s, |
| 116 | 0,-s, c); |
| 117 | return m; |
| 118 | } |
| 119 | |
| 120 | static SkMatrix44 RY(SkScalar rad) { |
| 121 | SkScalar c = SkScalarCos(rad), s = SkScalarSin(rad); |
| 122 | SkMatrix44 m; |
| 123 | m.set3x3( c, 0,-s, |
| 124 | 0, 1, 0, |
| 125 | s, 0, c); |
| 126 | return m; |
| 127 | } |
| 128 | |
| 129 | struct Face { |
| 130 | SkScalar fRx, fRy; |
| 131 | |
| 132 | static SkMatrix44 T(SkScalar x, SkScalar y, SkScalar z) { |
| 133 | SkMatrix44 m; |
| 134 | m.setTranslate(x, y, z); |
| 135 | return m; |
| 136 | } |
| 137 | |
| 138 | static SkMatrix44 R(SkScalar x, SkScalar y, SkScalar z, SkScalar rad) { |
| 139 | SkMatrix44 m; |
| 140 | m.setRotateAboutUnit(x, y, z, rad); |
| 141 | return m; |
| 142 | } |
| 143 | |
| 144 | SkMatrix44 asM44(SkScalar scale) const { |
| 145 | return RY(fRy) * RX(fRx) * T(0, 0, scale); |
| 146 | } |
| 147 | }; |
| 148 | |
Mike Reed | 7543587 | 2020-01-13 21:15:06 -0500 | [diff] [blame] | 149 | static bool front(const SkM44& m) { |
| 150 | SkM44 m2; |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 151 | m.invert(&m2); |
Mike Reed | 7543587 | 2020-01-13 21:15:06 -0500 | [diff] [blame] | 152 | /* |
| 153 | * Classically we want to dot the transpose(inverse(ctm)) with our surface normal. |
| 154 | * In this case, the normal is known to be {0, 0, 1}, so we only actually need to look |
| 155 | * at the z-scale of the inverse (the transpose doesn't change the main diagonal, so |
| 156 | * no need to actually transpose). |
| 157 | */ |
| 158 | return m2.atColMajor(10) > 0; |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | const Face faces[] = { |
| 162 | { 0, 0 }, // front |
| 163 | { 0, SK_ScalarPI }, // back |
| 164 | |
| 165 | { SK_ScalarPI/2, 0 }, // top |
| 166 | {-SK_ScalarPI/2, 0 }, // bottom |
| 167 | |
| 168 | { 0, SK_ScalarPI/2 }, // left |
| 169 | { 0,-SK_ScalarPI/2 }, // right |
| 170 | }; |
| 171 | |
| 172 | |
| 173 | class SampleRR3D : public Sample3DView { |
| 174 | SkRRect fRR; |
| 175 | sk_sp<SkShader> fShader; |
| 176 | |
| 177 | SkString name() override { return SkString("rrect3d"); } |
| 178 | |
| 179 | void onOnceBeforeDraw() override { |
| 180 | fRR = SkRRect::MakeRectXY({20, 20, 380, 380}, 50, 50); |
| 181 | fShader = GetResourceAsImage("images/mandrill_128.png") |
| 182 | ->makeShader(SkMatrix::MakeScale(3, 3)); |
| 183 | } |
| 184 | |
| 185 | bool onChar(SkUnichar uni) override { |
| 186 | return this->Sample3DView::onChar(uni); |
| 187 | } |
| 188 | |
| 189 | void drawContent(SkCanvas* canvas, const SkMatrix44& m) { |
Mike Reed | 7543587 | 2020-01-13 21:15:06 -0500 | [diff] [blame] | 190 | SkMatrix44 trans; |
| 191 | trans.setTranslate(200, 200, 0); // center of the rotation |
| 192 | |
| 193 | canvas->concat(trans * fRot * m * inv(trans)); |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 194 | |
| 195 | SkPaint paint; |
Mike Reed | 7543587 | 2020-01-13 21:15:06 -0500 | [diff] [blame] | 196 | paint.setAlphaf(front(canvas->getTotalM44()) ? 1 : 0.25f); |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 197 | paint.setShader(fShader); |
| 198 | canvas->drawRRect(fRR, paint); |
| 199 | } |
| 200 | |
| 201 | void onDrawContent(SkCanvas* canvas) override { |
| 202 | canvas->translate(400, 300); |
| 203 | |
Mike Reed | ee0a03a | 2020-01-14 16:44:47 -0500 | [diff] [blame^] | 204 | this->saveCamera(canvas, {0, 0, 400, 400}, 200); |
Mike Reed | 7543587 | 2020-01-13 21:15:06 -0500 | [diff] [blame] | 205 | |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 206 | for (auto f : faces) { |
| 207 | SkAutoCanvasRestore acr(canvas, true); |
Mike Reed | 7543587 | 2020-01-13 21:15:06 -0500 | [diff] [blame] | 208 | this->drawContent(canvas, f.asM44(200)); |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 209 | } |
Mike Reed | ee0a03a | 2020-01-14 16:44:47 -0500 | [diff] [blame^] | 210 | |
| 211 | canvas->restore(); |
Mike Reed | 69ace2a | 2020-01-11 15:57:14 -0500 | [diff] [blame] | 212 | } |
| 213 | }; |
| 214 | DEF_SAMPLE( return new SampleRR3D(); ) |