1 /* 2 * Copyright 2006 The Android Open Source Project 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 "SkCamera.h" 9 10 static SkScalar SkScalarDotDiv(int count, const SkScalar a[], int step_a, 11 const SkScalar b[], int step_b, 12 SkScalar denom) { 13 SkScalar prod = 0; 14 for (int i = 0; i < count; i++) { 15 prod += a[0] * b[0]; 16 a += step_a; 17 b += step_b; 18 } 19 return prod / denom; 20 } 21 22 static SkScalar SkScalarDot(int count, const SkScalar a[], int step_a, 23 const SkScalar b[], int step_b) { 24 SkScalar prod = 0; 25 for (int i = 0; i < count; i++) { 26 prod += a[0] * b[0]; 27 a += step_a; 28 b += step_b; 29 } 30 return prod; 31 } 32 33 /////////////////////////////////////////////////////////////////////////////// 34 35 SkScalar SkPoint3D::normalize(SkUnit3D* unit) const { 36 SkScalar mag = SkScalarSqrt(fX*fX + fY*fY + fZ*fZ); 37 if (mag) { 38 SkScalar scale = SkScalarInvert(mag); 39 unit->fX = fX * scale; 40 unit->fY = fY * scale; 41 unit->fZ = fZ * scale; 42 } else { 43 unit->fX = unit->fY = unit->fZ = 0; 44 } 45 return mag; 46 } 47 48 SkScalar SkUnit3D::Dot(const SkUnit3D& a, const SkUnit3D& b) { 49 return a.fX * b.fX + a.fY * b.fY + a.fZ * b.fZ; 50 } 51 52 void SkUnit3D::Cross(const SkUnit3D& a, const SkUnit3D& b, SkUnit3D* cross) { 53 SkASSERT(cross); 54 55 // use x,y,z, in case &a == cross or &b == cross 56 57 SkScalar x = a.fY * b.fZ - a.fZ * b.fY; 58 SkScalar y = a.fZ * b.fX - a.fX * b.fY; 59 SkScalar z = a.fX * b.fY - a.fY * b.fX; 60 61 cross->set(x, y, z); 62 } 63 64 /////////////////////////////////////////////////////////////////////////////// 65 66 SkPatch3D::SkPatch3D() { 67 this->reset(); 68 } 69 70 void SkPatch3D::reset() { 71 fOrigin.set(0, 0, 0); 72 fU.set(SK_Scalar1, 0, 0); 73 fV.set(0, -SK_Scalar1, 0); 74 } 75 76 void SkPatch3D::transform(const SkMatrix3D& m, SkPatch3D* dst) const { 77 if (dst == nullptr) { 78 dst = (SkPatch3D*)this; 79 } 80 m.mapVector(fU, &dst->fU); 81 m.mapVector(fV, &dst->fV); 82 m.mapPoint(fOrigin, &dst->fOrigin); 83 } 84 85 SkScalar SkPatch3D::dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const { 86 SkScalar cx = fU.fY * fV.fZ - fU.fZ * fV.fY; 87 SkScalar cy = fU.fZ * fV.fX - fU.fX * fV.fY; 88 SkScalar cz = fU.fX * fV.fY - fU.fY * fV.fX; 89 90 return cx * dx + cy * dy + cz * dz; 91 } 92 93 /////////////////////////////////////////////////////////////////////////////// 94 95 void SkMatrix3D::reset() { 96 memset(fMat, 0, sizeof(fMat)); 97 fMat[0][0] = fMat[1][1] = fMat[2][2] = SK_Scalar1; 98 } 99 100 void SkMatrix3D::setTranslate(SkScalar x, SkScalar y, SkScalar z) { 101 memset(fMat, 0, sizeof(fMat)); 102 fMat[0][0] = x; 103 fMat[1][1] = y; 104 fMat[2][2] = z; 105 } 106 107 void SkMatrix3D::setRotateX(SkScalar degX) { 108 SkScalar s, c; 109 110 s = SkScalarSinCos(SkDegreesToRadians(degX), &c); 111 this->setRow(0, SK_Scalar1, 0, 0); 112 this->setRow(1, 0, c, -s); 113 this->setRow(2, 0, s, c); 114 } 115 116 void SkMatrix3D::setRotateY(SkScalar degY) { 117 SkScalar s, c; 118 119 s = SkScalarSinCos(SkDegreesToRadians(degY), &c); 120 this->setRow(0, c, 0, -s); 121 this->setRow(1, 0, SK_Scalar1, 0); 122 this->setRow(2, s, 0, c); 123 } 124 125 void SkMatrix3D::setRotateZ(SkScalar degZ) { 126 SkScalar s, c; 127 128 s = SkScalarSinCos(SkDegreesToRadians(degZ), &c); 129 this->setRow(0, c, -s, 0); 130 this->setRow(1, s, c, 0); 131 this->setRow(2, 0, 0, SK_Scalar1); 132 } 133 134 void SkMatrix3D::preTranslate(SkScalar x, SkScalar y, SkScalar z) { 135 SkScalar col[3] = { x, y, z}; 136 137 for (int i = 0; i < 3; i++) { 138 fMat[i][3] += SkScalarDot(3, &fMat[i][0], 1, col, 1); 139 } 140 } 141 142 void SkMatrix3D::preRotateX(SkScalar degX) { 143 SkMatrix3D m; 144 m.setRotateX(degX); 145 this->setConcat(*this, m); 146 } 147 148 void SkMatrix3D::preRotateY(SkScalar degY) { 149 SkMatrix3D m; 150 m.setRotateY(degY); 151 this->setConcat(*this, m); 152 } 153 154 void SkMatrix3D::preRotateZ(SkScalar degZ) { 155 SkMatrix3D m; 156 m.setRotateZ(degZ); 157 this->setConcat(*this, m); 158 } 159 160 void SkMatrix3D::setConcat(const SkMatrix3D& a, const SkMatrix3D& b) { 161 SkMatrix3D tmp; 162 SkMatrix3D* c = this; 163 164 if (this == &a || this == &b) { 165 c = &tmp; 166 } 167 for (int i = 0; i < 3; i++) { 168 for (int j = 0; j < 3; j++) { 169 c->fMat[i][j] = SkScalarDot(3, &a.fMat[i][0], 1, &b.fMat[0][j], 4); 170 } 171 c->fMat[i][3] = SkScalarDot(3, &a.fMat[i][0], 1, 172 &b.fMat[0][3], 4) + a.fMat[i][3]; 173 } 174 175 if (c == &tmp) { 176 *this = tmp; 177 } 178 } 179 180 void SkMatrix3D::mapPoint(const SkPoint3D& src, SkPoint3D* dst) const { 181 SkScalar x = SkScalarDot(3, &fMat[0][0], 1, &src.fX, 1) + fMat[0][3]; 182 SkScalar y = SkScalarDot(3, &fMat[1][0], 1, &src.fX, 1) + fMat[1][3]; 183 SkScalar z = SkScalarDot(3, &fMat[2][0], 1, &src.fX, 1) + fMat[2][3]; 184 dst->set(x, y, z); 185 } 186 187 void SkMatrix3D::mapVector(const SkVector3D& src, SkVector3D* dst) const { 188 SkScalar x = SkScalarDot(3, &fMat[0][0], 1, &src.fX, 1); 189 SkScalar y = SkScalarDot(3, &fMat[1][0], 1, &src.fX, 1); 190 SkScalar z = SkScalarDot(3, &fMat[2][0], 1, &src.fX, 1); 191 dst->set(x, y, z); 192 } 193 194 /////////////////////////////////////////////////////////////////////////////// 195 196 SkCamera3D::SkCamera3D() { 197 this->reset(); 198 } 199 200 void SkCamera3D::reset() { 201 fLocation.set(0, 0, -SkIntToScalar(576)); // 8 inches backward 202 fAxis.set(0, 0, SK_Scalar1); // forward 203 fZenith.set(0, -SK_Scalar1, 0); // up 204 205 fObserver.set(0, 0, fLocation.fZ); 206 207 fNeedToUpdate = true; 208 } 209 210 void SkCamera3D::update() { 211 fNeedToUpdate = true; 212 } 213 214 void SkCamera3D::doUpdate() const { 215 SkUnit3D axis, zenith, cross; 216 217 // construct a orthonormal basis of cross (x), zenith (y), and axis (z) 218 fAxis.normalize(&axis); 219 220 { 221 SkScalar dot = SkUnit3D::Dot(*SkTCast<const SkUnit3D*>(&fZenith), axis); 222 223 zenith.fX = fZenith.fX - dot * axis.fX; 224 zenith.fY = fZenith.fY - dot * axis.fY; 225 zenith.fZ = fZenith.fZ - dot * axis.fZ; 226 227 SkTCast<SkPoint3D*>(&zenith)->normalize(&zenith); 228 } 229 230 SkUnit3D::Cross(axis, zenith, &cross); 231 232 { 233 SkMatrix* orien = &fOrientation; 234 SkScalar x = fObserver.fX; 235 SkScalar y = fObserver.fY; 236 SkScalar z = fObserver.fZ; 237 238 // Looking along the view axis we have: 239 // 240 // /|\ zenith 241 // | 242 // | 243 // | * observer (projected on XY plane) 244 // | 245 // |____________\ cross 246 // / 247 // 248 // So this does a z-shear along the view axis based on the observer's x and y values, 249 // and scales in x and y relative to the negative of the observer's z value 250 // (the observer is in the negative z direction). 251 252 orien->set(SkMatrix::kMScaleX, x * axis.fX - z * cross.fX); 253 orien->set(SkMatrix::kMSkewX, x * axis.fY - z * cross.fY); 254 orien->set(SkMatrix::kMTransX, x * axis.fZ - z * cross.fZ); 255 orien->set(SkMatrix::kMSkewY, y * axis.fX - z * zenith.fX); 256 orien->set(SkMatrix::kMScaleY, y * axis.fY - z * zenith.fY); 257 orien->set(SkMatrix::kMTransY, y * axis.fZ - z * zenith.fZ); 258 orien->set(SkMatrix::kMPersp0, axis.fX); 259 orien->set(SkMatrix::kMPersp1, axis.fY); 260 orien->set(SkMatrix::kMPersp2, axis.fZ); 261 } 262 } 263 264 void SkCamera3D::patchToMatrix(const SkPatch3D& quilt, SkMatrix* matrix) const { 265 if (fNeedToUpdate) { 266 this->doUpdate(); 267 fNeedToUpdate = false; 268 } 269 270 const SkScalar* mapPtr = (const SkScalar*)(const void*)&fOrientation; 271 const SkScalar* patchPtr; 272 SkPoint3D diff; 273 SkScalar dot; 274 275 diff.fX = quilt.fOrigin.fX - fLocation.fX; 276 diff.fY = quilt.fOrigin.fY - fLocation.fY; 277 diff.fZ = quilt.fOrigin.fZ - fLocation.fZ; 278 279 dot = SkUnit3D::Dot(*SkTCast<const SkUnit3D*>(&diff), 280 *SkTCast<const SkUnit3D*>(SkTCast<const SkScalar*>(&fOrientation) + 6)); 281 282 // This multiplies fOrientation by the matrix [quilt.fU quilt.fV diff] -- U, V, and diff are 283 // column vectors in the matrix -- then divides by the length of the projection of diff onto 284 // the view axis (which is 'dot'). This transforms the patch (which transforms from local path 285 // space to world space) into view space (since fOrientation transforms from world space to 286 // view space). 287 // 288 // The divide by 'dot' isn't strictly necessary as the homogeneous divide would do much the 289 // same thing (it's just scaling the entire matrix by 1/dot). It looks like it's normalizing 290 // the matrix into some canonical space. 291 patchPtr = (const SkScalar*)&quilt; 292 matrix->set(SkMatrix::kMScaleX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot)); 293 matrix->set(SkMatrix::kMSkewY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot)); 294 matrix->set(SkMatrix::kMPersp0, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot)); 295 296 patchPtr += 3; 297 matrix->set(SkMatrix::kMSkewX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot)); 298 matrix->set(SkMatrix::kMScaleY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot)); 299 matrix->set(SkMatrix::kMPersp1, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot)); 300 301 patchPtr = (const SkScalar*)(const void*)&diff; 302 matrix->set(SkMatrix::kMTransX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot)); 303 matrix->set(SkMatrix::kMTransY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot)); 304 matrix->set(SkMatrix::kMPersp2, SK_Scalar1); 305 } 306 307 /////////////////////////////////////////////////////////////////////////////// 308 309 Sk3DView::Sk3DView() { 310 fInitialRec.fMatrix.reset(); 311 fRec = &fInitialRec; 312 } 313 314 Sk3DView::~Sk3DView() { 315 Rec* rec = fRec; 316 while (rec != &fInitialRec) { 317 Rec* next = rec->fNext; 318 delete rec; 319 rec = next; 320 } 321 } 322 323 void Sk3DView::save() { 324 Rec* rec = new Rec; 325 rec->fNext = fRec; 326 rec->fMatrix = fRec->fMatrix; 327 fRec = rec; 328 } 329 330 void Sk3DView::restore() { 331 SkASSERT(fRec != &fInitialRec); 332 Rec* next = fRec->fNext; 333 delete fRec; 334 fRec = next; 335 } 336 337 #ifdef SK_BUILD_FOR_ANDROID 338 void Sk3DView::setCameraLocation(SkScalar x, SkScalar y, SkScalar z) { 339 // the camera location is passed in inches, set in pt 340 SkScalar lz = z * 72.0f; 341 fCamera.fLocation.set(x * 72.0f, y * 72.0f, lz); 342 fCamera.fObserver.set(0, 0, lz); 343 fCamera.update(); 344 345 } 346 347 SkScalar Sk3DView::getCameraLocationX() { 348 return fCamera.fLocation.fX / 72.0f; 349 } 350 351 SkScalar Sk3DView::getCameraLocationY() { 352 return fCamera.fLocation.fY / 72.0f; 353 } 354 355 SkScalar Sk3DView::getCameraLocationZ() { 356 return fCamera.fLocation.fZ / 72.0f; 357 } 358 #endif 359 360 void Sk3DView::translate(SkScalar x, SkScalar y, SkScalar z) { 361 fRec->fMatrix.preTranslate(x, y, z); 362 } 363 364 void Sk3DView::rotateX(SkScalar deg) { 365 fRec->fMatrix.preRotateX(deg); 366 } 367 368 void Sk3DView::rotateY(SkScalar deg) { 369 fRec->fMatrix.preRotateY(deg); 370 } 371 372 void Sk3DView::rotateZ(SkScalar deg) { 373 fRec->fMatrix.preRotateZ(deg); 374 } 375 376 SkScalar Sk3DView::dotWithNormal(SkScalar x, SkScalar y, SkScalar z) const { 377 SkPatch3D patch; 378 patch.transform(fRec->fMatrix); 379 return patch.dotWith(x, y, z); 380 } 381 382 void Sk3DView::getMatrix(SkMatrix* matrix) const { 383 if (matrix != nullptr) { 384 SkPatch3D patch; 385 patch.transform(fRec->fMatrix); 386 fCamera.patchToMatrix(patch, matrix); 387 } 388 } 389 390 #include "SkCanvas.h" 391 392 void Sk3DView::applyToCanvas(SkCanvas* canvas) const { 393 SkMatrix matrix; 394 395 this->getMatrix(&matrix); 396 canvas->concat(matrix); 397 } 398