Home | History | Annotate | Download | only in utils
      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