| 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 | #ifndef SkM44_DEFINED | 
|---|
| 9 | #define SkM44_DEFINED | 
|---|
| 10 |  | 
|---|
| 11 | #include "include/core/SkMatrix.h" | 
|---|
| 12 | #include "include/core/SkScalar.h" | 
|---|
| 13 |  | 
|---|
| 14 | struct SkV2 { | 
|---|
| 15 | float x, y; | 
|---|
| 16 |  | 
|---|
| 17 | bool operator==(const SkV2 v) const { return x == v.x && y == v.y; } | 
|---|
| 18 | bool operator!=(const SkV2 v) const { return !(*this == v); } | 
|---|
| 19 |  | 
|---|
| 20 | static SkScalar   Dot(SkV2 a, SkV2 b) { return a.x * b.x + a.y * b.y; } | 
|---|
| 21 | static SkScalar Cross(SkV2 a, SkV2 b) { return a.x * b.y - a.y * b.x; } | 
|---|
| 22 | static SkV2 Normalize(SkV2 v) { return v * (1.0f / v.length()); } | 
|---|
| 23 |  | 
|---|
| 24 | SkV2 operator-() const { return {-x, -y}; } | 
|---|
| 25 | SkV2 operator+(SkV2 v) const { return {x+v.x, y+v.y}; } | 
|---|
| 26 | SkV2 operator-(SkV2 v) const { return {x-v.x, y-v.y}; } | 
|---|
| 27 |  | 
|---|
| 28 | SkV2 operator*(SkV2 v) const { return {x*v.x, y*v.y}; } | 
|---|
| 29 | friend SkV2 operator*(SkV2 v, SkScalar s) { return {v.x*s, v.y*s}; } | 
|---|
| 30 | friend SkV2 operator*(SkScalar s, SkV2 v) { return {v.x*s, v.y*s}; } | 
|---|
| 31 |  | 
|---|
| 32 | void operator+=(SkV2 v) { *this = *this + v; } | 
|---|
| 33 | void operator-=(SkV2 v) { *this = *this - v; } | 
|---|
| 34 | void operator*=(SkV2 v) { *this = *this * v; } | 
|---|
| 35 | void operator*=(SkScalar s) { *this = *this * s; } | 
|---|
| 36 |  | 
|---|
| 37 | SkScalar lengthSquared() const { return Dot(*this, *this); } | 
|---|
| 38 | SkScalar length() const { return SkScalarSqrt(this->lengthSquared()); } | 
|---|
| 39 |  | 
|---|
| 40 | SkScalar   dot(SkV2 v) const { return Dot(*this, v); } | 
|---|
| 41 | SkScalar cross(SkV2 v) const { return Cross(*this, v); } | 
|---|
| 42 | SkV2 normalize()       const { return Normalize(*this); } | 
|---|
| 43 |  | 
|---|
| 44 | const float* ptr() const { return &x; } | 
|---|
| 45 | float* ptr() { return &x; } | 
|---|
| 46 | }; | 
|---|
| 47 |  | 
|---|
| 48 | struct SkV3 { | 
|---|
| 49 | float x, y, z; | 
|---|
| 50 |  | 
|---|
| 51 | bool operator==(const SkV3& v) const { | 
|---|
| 52 | return x == v.x && y == v.y && z == v.z; | 
|---|
| 53 | } | 
|---|
| 54 | bool operator!=(const SkV3& v) const { return !(*this == v); } | 
|---|
| 55 |  | 
|---|
| 56 | static SkScalar Dot(const SkV3& a, const SkV3& b) { return a.x*b.x + a.y*b.y + a.z*b.z; } | 
|---|
| 57 | static SkV3   Cross(const SkV3& a, const SkV3& b) { | 
|---|
| 58 | 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 }; | 
|---|
| 59 | } | 
|---|
| 60 | static SkV3 Normalize(const SkV3& v) { return v * (1.0f / v.length()); } | 
|---|
| 61 |  | 
|---|
| 62 | SkV3 operator-() const { return {-x, -y, -z}; } | 
|---|
| 63 | SkV3 operator+(const SkV3& v) const { return { x + v.x, y + v.y, z + v.z }; } | 
|---|
| 64 | SkV3 operator-(const SkV3& v) const { return { x - v.x, y - v.y, z - v.z }; } | 
|---|
| 65 |  | 
|---|
| 66 | SkV3 operator*(const SkV3& v) const { | 
|---|
| 67 | return { x*v.x, y*v.y, z*v.z }; | 
|---|
| 68 | } | 
|---|
| 69 | friend SkV3 operator*(const SkV3& v, SkScalar s) { | 
|---|
| 70 | return { v.x*s, v.y*s, v.z*s }; | 
|---|
| 71 | } | 
|---|
| 72 | friend SkV3 operator*(SkScalar s, const SkV3& v) { return v*s; } | 
|---|
| 73 |  | 
|---|
| 74 | void operator+=(SkV3 v) { *this = *this + v; } | 
|---|
| 75 | void operator-=(SkV3 v) { *this = *this - v; } | 
|---|
| 76 | void operator*=(SkV3 v) { *this = *this * v; } | 
|---|
| 77 | void operator*=(SkScalar s) { *this = *this * s; } | 
|---|
| 78 |  | 
|---|
| 79 | SkScalar lengthSquared() const { return Dot(*this, *this); } | 
|---|
| 80 | SkScalar length() const { return SkScalarSqrt(Dot(*this, *this)); } | 
|---|
| 81 |  | 
|---|
| 82 | SkScalar dot(const SkV3& v) const { return Dot(*this, v); } | 
|---|
| 83 | SkV3   cross(const SkV3& v) const { return Cross(*this, v); } | 
|---|
| 84 | SkV3 normalize()            const { return Normalize(*this); } | 
|---|
| 85 |  | 
|---|
| 86 | const float* ptr() const { return &x; } | 
|---|
| 87 | float* ptr() { return &x; } | 
|---|
| 88 | }; | 
|---|
| 89 |  | 
|---|
| 90 | struct SkV4 { | 
|---|
| 91 | float x, y, z, w; | 
|---|
| 92 |  | 
|---|
| 93 | bool operator==(const SkV4& v) const { | 
|---|
| 94 | return x == v.x && y == v.y && z == v.z && w == v.w; | 
|---|
| 95 | } | 
|---|
| 96 | bool operator!=(const SkV4& v) const { return !(*this == v); } | 
|---|
| 97 |  | 
|---|
| 98 | SkV4 operator-() const { return {-x, -y, -z, -w}; } | 
|---|
| 99 | SkV4 operator+(const SkV4& v) const { return { x + v.x, y + v.y, z + v.z, w + v.w }; } | 
|---|
| 100 | SkV4 operator-(const SkV4& v) const { return { x - v.x, y - v.y, z - v.z, w - v.w }; } | 
|---|
| 101 |  | 
|---|
| 102 | SkV4 operator*(const SkV4& v) const { | 
|---|
| 103 | return { x*v.x, y*v.y, z*v.z, w*v.w }; | 
|---|
| 104 | } | 
|---|
| 105 | friend SkV4 operator*(const SkV4& v, SkScalar s) { | 
|---|
| 106 | return { v.x*s, v.y*s, v.z*s, v.w*s }; | 
|---|
| 107 | } | 
|---|
| 108 | friend SkV4 operator*(SkScalar s, const SkV4& v) { return v*s; } | 
|---|
| 109 |  | 
|---|
| 110 | const float* ptr() const { return &x; } | 
|---|
| 111 | float* ptr() { return &x; } | 
|---|
| 112 | }; | 
|---|
| 113 |  | 
|---|
| 114 | /** | 
|---|
| 115 | *  4x4 matrix used by SkCanvas and other parts of Skia. | 
|---|
| 116 | * | 
|---|
| 117 | *  Skia assumes a right-handed coordinate system: | 
|---|
| 118 | *      +X goes to the right | 
|---|
| 119 | *      +Y goes down | 
|---|
| 120 | *      +Z goes into the screen (away from the viewer) | 
|---|
| 121 | */ | 
|---|
| 122 | class SkM44 { | 
|---|
| 123 | public: | 
|---|
| 124 | SkM44(const SkM44& src) = default; | 
|---|
| 125 | SkM44& operator=(const SkM44& src) = default; | 
|---|
| 126 |  | 
|---|
| 127 | constexpr SkM44() | 
|---|
| 128 | : fMat{1, 0, 0, 0, | 
|---|
| 129 | 0, 1, 0, 0, | 
|---|
| 130 | 0, 0, 1, 0, | 
|---|
| 131 | 0, 0, 0, 1} | 
|---|
| 132 | {} | 
|---|
| 133 |  | 
|---|
| 134 | SkM44(const SkM44& a, const SkM44& b) { | 
|---|
| 135 | this->setConcat(a, b); | 
|---|
| 136 | } | 
|---|
| 137 |  | 
|---|
| 138 | enum Uninitialized_Constructor { | 
|---|
| 139 | kUninitialized_Constructor | 
|---|
| 140 | }; | 
|---|
| 141 | SkM44(Uninitialized_Constructor) {} | 
|---|
| 142 |  | 
|---|
| 143 | enum NaN_Constructor { | 
|---|
| 144 | kNaN_Constructor | 
|---|
| 145 | }; | 
|---|
| 146 | SkM44(NaN_Constructor) | 
|---|
| 147 | : fMat{SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, | 
|---|
| 148 | SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, | 
|---|
| 149 | SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, | 
|---|
| 150 | SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN} | 
|---|
| 151 | {} | 
|---|
| 152 |  | 
|---|
| 153 | /** | 
|---|
| 154 | *  Parameters are treated as row-major. | 
|---|
| 155 | */ | 
|---|
| 156 | SkM44(SkScalar m0, SkScalar m4, SkScalar m8,  SkScalar m12, | 
|---|
| 157 | SkScalar m1, SkScalar m5, SkScalar m9,  SkScalar m13, | 
|---|
| 158 | SkScalar m2, SkScalar m6, SkScalar m10, SkScalar m14, | 
|---|
| 159 | SkScalar m3, SkScalar m7, SkScalar m11, SkScalar m15) | 
|---|
| 160 | { | 
|---|
| 161 | fMat[0] = m0; fMat[4] = m4; fMat[8]  = m8;  fMat[12] = m12; | 
|---|
| 162 | fMat[1] = m1; fMat[5] = m5; fMat[9]  = m9;  fMat[13] = m13; | 
|---|
| 163 | fMat[2] = m2; fMat[6] = m6; fMat[10] = m10; fMat[14] = m14; | 
|---|
| 164 | fMat[3] = m3; fMat[7] = m7; fMat[11] = m11; fMat[15] = m15; | 
|---|
| 165 | } | 
|---|
| 166 |  | 
|---|
| 167 | static SkM44 Rows(const SkV4& r0, const SkV4& r1, const SkV4& r2, const SkV4& r3) { | 
|---|
| 168 | SkM44 m(kUninitialized_Constructor); | 
|---|
| 169 | m.setRow(0, r0); | 
|---|
| 170 | m.setRow(1, r1); | 
|---|
| 171 | m.setRow(2, r2); | 
|---|
| 172 | m.setRow(3, r3); | 
|---|
| 173 | return m; | 
|---|
| 174 | } | 
|---|
| 175 | static SkM44 Cols(const SkV4& c0, const SkV4& c1, const SkV4& c2, const SkV4& c3) { | 
|---|
| 176 | SkM44 m(kUninitialized_Constructor); | 
|---|
| 177 | m.setCol(0, c0); | 
|---|
| 178 | m.setCol(1, c1); | 
|---|
| 179 | m.setCol(2, c2); | 
|---|
| 180 | m.setCol(3, c3); | 
|---|
| 181 | return m; | 
|---|
| 182 | } | 
|---|
| 183 |  | 
|---|
| 184 | static SkM44 RowMajor(const SkScalar r[16]) { | 
|---|
| 185 | return SkM44(r[ 0], r[ 1], r[ 2], r[ 3], | 
|---|
| 186 | r[ 4], r[ 5], r[ 6], r[ 7], | 
|---|
| 187 | r[ 8], r[ 9], r[10], r[11], | 
|---|
| 188 | r[12], r[13], r[14], r[15]); | 
|---|
| 189 | } | 
|---|
| 190 | static SkM44 ColMajor(const SkScalar c[16]) { | 
|---|
| 191 | return SkM44(c[0], c[4], c[ 8], c[12], | 
|---|
| 192 | c[1], c[5], c[ 9], c[13], | 
|---|
| 193 | c[2], c[6], c[10], c[14], | 
|---|
| 194 | c[3], c[7], c[11], c[15]); | 
|---|
| 195 | } | 
|---|
| 196 |  | 
|---|
| 197 | static SkM44 Translate(SkScalar x, SkScalar y, SkScalar z = 0) { | 
|---|
| 198 | return SkM44(1, 0, 0, x, | 
|---|
| 199 | 0, 1, 0, y, | 
|---|
| 200 | 0, 0, 1, z, | 
|---|
| 201 | 0, 0, 0, 1); | 
|---|
| 202 | } | 
|---|
| 203 |  | 
|---|
| 204 | static SkM44 Scale(SkScalar x, SkScalar y, SkScalar z = 1) { | 
|---|
| 205 | return SkM44(x, 0, 0, 0, | 
|---|
| 206 | 0, y, 0, 0, | 
|---|
| 207 | 0, 0, z, 0, | 
|---|
| 208 | 0, 0, 0, 1); | 
|---|
| 209 | } | 
|---|
| 210 |  | 
|---|
| 211 | static SkM44 Rotate(SkV3 axis, SkScalar radians) { | 
|---|
| 212 | SkM44 m(kUninitialized_Constructor); | 
|---|
| 213 | m.setRotate(axis, radians); | 
|---|
| 214 | return m; | 
|---|
| 215 | } | 
|---|
| 216 |  | 
|---|
| 217 | bool operator==(const SkM44& other) const; | 
|---|
| 218 | bool operator!=(const SkM44& other) const { | 
|---|
| 219 | return !(other == *this); | 
|---|
| 220 | } | 
|---|
| 221 |  | 
|---|
| 222 | void getColMajor(SkScalar v[]) const { | 
|---|
| 223 | memcpy(v, fMat, sizeof(fMat)); | 
|---|
| 224 | } | 
|---|
| 225 | void getRowMajor(SkScalar v[]) const; | 
|---|
| 226 |  | 
|---|
| 227 | SkScalar rc(int r, int c) const { | 
|---|
| 228 | SkASSERT(r >= 0 && r <= 3); | 
|---|
| 229 | SkASSERT(c >= 0 && c <= 3); | 
|---|
| 230 | return fMat[c*4 + r]; | 
|---|
| 231 | } | 
|---|
| 232 | void setRC(int r, int c, SkScalar value) { | 
|---|
| 233 | SkASSERT(r >= 0 && r <= 3); | 
|---|
| 234 | SkASSERT(c >= 0 && c <= 3); | 
|---|
| 235 | fMat[c*4 + r] = value; | 
|---|
| 236 | } | 
|---|
| 237 |  | 
|---|
| 238 | SkV4 row(int i) const { | 
|---|
| 239 | SkASSERT(i >= 0 && i <= 3); | 
|---|
| 240 | return {fMat[i + 0], fMat[i + 4], fMat[i + 8], fMat[i + 12]}; | 
|---|
| 241 | } | 
|---|
| 242 | SkV4 col(int i) const { | 
|---|
| 243 | SkASSERT(i >= 0 && i <= 3); | 
|---|
| 244 | return {fMat[i*4 + 0], fMat[i*4 + 1], fMat[i*4 + 2], fMat[i*4 + 3]}; | 
|---|
| 245 | } | 
|---|
| 246 |  | 
|---|
| 247 | void setRow(int i, const SkV4& v) { | 
|---|
| 248 | SkASSERT(i >= 0 && i <= 3); | 
|---|
| 249 | fMat[i + 0]  = v.x; | 
|---|
| 250 | fMat[i + 4]  = v.y; | 
|---|
| 251 | fMat[i + 8]  = v.z; | 
|---|
| 252 | fMat[i + 12] = v.w; | 
|---|
| 253 | } | 
|---|
| 254 | void setCol(int i, const SkV4& v) { | 
|---|
| 255 | SkASSERT(i >= 0 && i <= 3); | 
|---|
| 256 | memcpy(&fMat[i*4], v.ptr(), sizeof(v)); | 
|---|
| 257 | } | 
|---|
| 258 |  | 
|---|
| 259 | SkM44& setIdentity() { | 
|---|
| 260 | *this = { 1, 0, 0, 0, | 
|---|
| 261 | 0, 1, 0, 0, | 
|---|
| 262 | 0, 0, 1, 0, | 
|---|
| 263 | 0, 0, 0, 1 }; | 
|---|
| 264 | return *this; | 
|---|
| 265 | } | 
|---|
| 266 |  | 
|---|
| 267 | SkM44& setTranslate(SkScalar x, SkScalar y, SkScalar z = 0) { | 
|---|
| 268 | *this = { 1, 0, 0, x, | 
|---|
| 269 | 0, 1, 0, y, | 
|---|
| 270 | 0, 0, 1, z, | 
|---|
| 271 | 0, 0, 0, 1 }; | 
|---|
| 272 | return *this; | 
|---|
| 273 | } | 
|---|
| 274 |  | 
|---|
| 275 | SkM44& setScale(SkScalar x, SkScalar y, SkScalar z = 1) { | 
|---|
| 276 | *this = { x, 0, 0, 0, | 
|---|
| 277 | 0, y, 0, 0, | 
|---|
| 278 | 0, 0, z, 0, | 
|---|
| 279 | 0, 0, 0, 1 }; | 
|---|
| 280 | return *this; | 
|---|
| 281 | } | 
|---|
| 282 |  | 
|---|
| 283 | /** | 
|---|
| 284 | *  Set this matrix to rotate about the specified unit-length axis vector, | 
|---|
| 285 | *  by an angle specified by its sin() and cos(). | 
|---|
| 286 | * | 
|---|
| 287 | *  This does not attempt to verify that axis.length() == 1 or that the sin,cos values | 
|---|
| 288 | *  are correct. | 
|---|
| 289 | */ | 
|---|
| 290 | SkM44& setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle); | 
|---|
| 291 |  | 
|---|
| 292 | /** | 
|---|
| 293 | *  Set this matrix to rotate about the specified unit-length axis vector, | 
|---|
| 294 | *  by an angle specified in radians. | 
|---|
| 295 | * | 
|---|
| 296 | *  This does not attempt to verify that axis.length() == 1. | 
|---|
| 297 | */ | 
|---|
| 298 | SkM44& setRotateUnit(SkV3 axis, SkScalar radians) { | 
|---|
| 299 | return this->setRotateUnitSinCos(axis, SkScalarSin(radians), SkScalarCos(radians)); | 
|---|
| 300 | } | 
|---|
| 301 |  | 
|---|
| 302 | /** | 
|---|
| 303 | *  Set this matrix to rotate about the specified axis vector, | 
|---|
| 304 | *  by an angle specified in radians. | 
|---|
| 305 | * | 
|---|
| 306 | *  Note: axis is not assumed to be unit-length, so it will be normalized internally. | 
|---|
| 307 | *        If axis is already unit-length, call setRotateAboutUnitRadians() instead. | 
|---|
| 308 | */ | 
|---|
| 309 | SkM44& setRotate(SkV3 axis, SkScalar radians); | 
|---|
| 310 |  | 
|---|
| 311 | SkM44& setConcat(const SkM44& a, const SkM44& b); | 
|---|
| 312 |  | 
|---|
| 313 | friend SkM44 operator*(const SkM44& a, const SkM44& b) { | 
|---|
| 314 | return SkM44(a, b); | 
|---|
| 315 | } | 
|---|
| 316 |  | 
|---|
| 317 | SkM44& preConcat(const SkM44& m) { | 
|---|
| 318 | return this->setConcat(*this, m); | 
|---|
| 319 | } | 
|---|
| 320 |  | 
|---|
| 321 | /** If this is invertible, return that in inverse and return true. If it is | 
|---|
| 322 | *  not invertible, return false and leave the inverse parameter unchanged. | 
|---|
| 323 | */ | 
|---|
| 324 | bool SK_WARN_UNUSED_RESULT invert(SkM44* inverse) const; | 
|---|
| 325 |  | 
|---|
| 326 | SkM44 transpose() const; | 
|---|
| 327 |  | 
|---|
| 328 | void dump() const; | 
|---|
| 329 |  | 
|---|
| 330 | //////////// | 
|---|
| 331 |  | 
|---|
| 332 | SkV4 map(float x, float y, float z, float w) const; | 
|---|
| 333 | SkV4 operator*(const SkV4& v) const { | 
|---|
| 334 | return this->map(v.x, v.y, v.z, v.w); | 
|---|
| 335 | } | 
|---|
| 336 | SkV3 operator*(SkV3 v) const { | 
|---|
| 337 | auto v4 = this->map(v.x, v.y, v.z, 0); | 
|---|
| 338 | return {v4.x, v4.y, v4.z}; | 
|---|
| 339 | } | 
|---|
| 340 |  | 
|---|
| 341 | ////////////////////// Converting to/from SkMatrix | 
|---|
| 342 |  | 
|---|
| 343 | /* When converting from SkM44 to SkMatrix, the third row and | 
|---|
| 344 | * column is dropped.  When converting from SkMatrix to SkM44 | 
|---|
| 345 | * the third row and column remain as identity: | 
|---|
| 346 | * [ a b c ]      [ a b 0 c ] | 
|---|
| 347 | * [ d e f ]  ->  [ d e 0 f ] | 
|---|
| 348 | * [ g h i ]      [ 0 0 1 0 ] | 
|---|
| 349 | *                [ g h 0 i ] | 
|---|
| 350 | */ | 
|---|
| 351 | SkMatrix asM33() const { | 
|---|
| 352 | return SkMatrix::MakeAll(fMat[0], fMat[4], fMat[12], | 
|---|
| 353 | fMat[1], fMat[5], fMat[13], | 
|---|
| 354 | fMat[3], fMat[7], fMat[15]); | 
|---|
| 355 | } | 
|---|
| 356 |  | 
|---|
| 357 | SkM44(const SkMatrix& src) | 
|---|
| 358 | : SkM44(src[SkMatrix::kMScaleX], src[SkMatrix::kMSkewX],  0, src[SkMatrix::kMTransX], | 
|---|
| 359 | src[SkMatrix::kMSkewY],  src[SkMatrix::kMScaleY], 0, src[SkMatrix::kMTransY], | 
|---|
| 360 | 0,                       0,                       1, 0, | 
|---|
| 361 | src[SkMatrix::kMPersp0], src[SkMatrix::kMPersp1], 0, src[SkMatrix::kMPersp2]) | 
|---|
| 362 | {} | 
|---|
| 363 |  | 
|---|
| 364 | SkM44& operator=(const SkMatrix& src) { | 
|---|
| 365 | *this = SkM44(src); | 
|---|
| 366 | return *this; | 
|---|
| 367 | } | 
|---|
| 368 |  | 
|---|
| 369 | SkM44& preTranslate(SkScalar x, SkScalar y); | 
|---|
| 370 | SkM44& preScale(SkScalar x, SkScalar y); | 
|---|
| 371 | SkM44& preConcat(const SkMatrix&); | 
|---|
| 372 |  | 
|---|
| 373 | private: | 
|---|
| 374 | /* Stored in column-major. | 
|---|
| 375 | *  Indices | 
|---|
| 376 | *  0  4  8  12        1 0 0 trans_x | 
|---|
| 377 | *  1  5  9  13  e.g.  0 1 0 trans_y | 
|---|
| 378 | *  2  6 10  14        0 0 1 trans_z | 
|---|
| 379 | *  3  7 11  15        0 0 0 1 | 
|---|
| 380 | */ | 
|---|
| 381 | SkScalar fMat[16]; | 
|---|
| 382 |  | 
|---|
| 383 | double determinant() const; | 
|---|
| 384 |  | 
|---|
| 385 | friend class SkMatrixPriv; | 
|---|
| 386 | }; | 
|---|
| 387 |  | 
|---|
| 388 | SkM44 Sk3LookAt(const SkV3& eye, const SkV3& center, const SkV3& up); | 
|---|
| 389 | SkM44 Sk3Perspective(float near, float far, float angle); | 
|---|
| 390 |  | 
|---|
| 391 | #endif | 
|---|
| 392 |  | 
|---|