1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2006 The Android Open Source Project
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/utils/SkCamera.h"
9*c8dee2aaSAndroid Build Coastguard Worker
SkScalarDotDiv(int count,const SkScalar a[],int step_a,const SkScalar b[],int step_b,SkScalar denom)10*c8dee2aaSAndroid Build Coastguard Worker static SkScalar SkScalarDotDiv(int count, const SkScalar a[], int step_a,
11*c8dee2aaSAndroid Build Coastguard Worker const SkScalar b[], int step_b,
12*c8dee2aaSAndroid Build Coastguard Worker SkScalar denom) {
13*c8dee2aaSAndroid Build Coastguard Worker SkScalar prod = 0;
14*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < count; i++) {
15*c8dee2aaSAndroid Build Coastguard Worker prod += a[0] * b[0];
16*c8dee2aaSAndroid Build Coastguard Worker a += step_a;
17*c8dee2aaSAndroid Build Coastguard Worker b += step_b;
18*c8dee2aaSAndroid Build Coastguard Worker }
19*c8dee2aaSAndroid Build Coastguard Worker return prod / denom;
20*c8dee2aaSAndroid Build Coastguard Worker }
21*c8dee2aaSAndroid Build Coastguard Worker
22*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
23*c8dee2aaSAndroid Build Coastguard Worker
SkPatch3D()24*c8dee2aaSAndroid Build Coastguard Worker SkPatch3D::SkPatch3D() {
25*c8dee2aaSAndroid Build Coastguard Worker this->reset();
26*c8dee2aaSAndroid Build Coastguard Worker }
27*c8dee2aaSAndroid Build Coastguard Worker
reset()28*c8dee2aaSAndroid Build Coastguard Worker void SkPatch3D::reset() {
29*c8dee2aaSAndroid Build Coastguard Worker fOrigin = {0, 0, 0};
30*c8dee2aaSAndroid Build Coastguard Worker fU = {SK_Scalar1, 0, 0};
31*c8dee2aaSAndroid Build Coastguard Worker fV = {0, -SK_Scalar1, 0};
32*c8dee2aaSAndroid Build Coastguard Worker }
33*c8dee2aaSAndroid Build Coastguard Worker
transform(const SkM44 & m,SkPatch3D * dst) const34*c8dee2aaSAndroid Build Coastguard Worker void SkPatch3D::transform(const SkM44& m, SkPatch3D* dst) const {
35*c8dee2aaSAndroid Build Coastguard Worker if (dst == nullptr) {
36*c8dee2aaSAndroid Build Coastguard Worker dst = const_cast<SkPatch3D*>(this);
37*c8dee2aaSAndroid Build Coastguard Worker }
38*c8dee2aaSAndroid Build Coastguard Worker dst->fU = m * fU;
39*c8dee2aaSAndroid Build Coastguard Worker dst->fV = m * fV;
40*c8dee2aaSAndroid Build Coastguard Worker auto [x,y,z,_] = m.map(fOrigin.x, fOrigin.y, fOrigin.z, 1);
41*c8dee2aaSAndroid Build Coastguard Worker dst->fOrigin = {x, y, z};
42*c8dee2aaSAndroid Build Coastguard Worker }
43*c8dee2aaSAndroid Build Coastguard Worker
dotWith(SkScalar dx,SkScalar dy,SkScalar dz) const44*c8dee2aaSAndroid Build Coastguard Worker SkScalar SkPatch3D::dotWith(SkScalar dx, SkScalar dy, SkScalar dz) const {
45*c8dee2aaSAndroid Build Coastguard Worker SkScalar cx = fU.y * fV.z - fU.z * fV.y;
46*c8dee2aaSAndroid Build Coastguard Worker SkScalar cy = fU.z * fV.x - fU.x * fV.y;
47*c8dee2aaSAndroid Build Coastguard Worker SkScalar cz = fU.x * fV.y - fU.y * fV.x;
48*c8dee2aaSAndroid Build Coastguard Worker
49*c8dee2aaSAndroid Build Coastguard Worker return cx * dx + cy * dy + cz * dz;
50*c8dee2aaSAndroid Build Coastguard Worker }
51*c8dee2aaSAndroid Build Coastguard Worker
52*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
53*c8dee2aaSAndroid Build Coastguard Worker
SkCamera3D()54*c8dee2aaSAndroid Build Coastguard Worker SkCamera3D::SkCamera3D() {
55*c8dee2aaSAndroid Build Coastguard Worker this->reset();
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker
reset()58*c8dee2aaSAndroid Build Coastguard Worker void SkCamera3D::reset() {
59*c8dee2aaSAndroid Build Coastguard Worker fLocation = {0, 0, -SkIntToScalar(576)}; // 8 inches backward
60*c8dee2aaSAndroid Build Coastguard Worker fAxis = {0, 0, SK_Scalar1}; // forward
61*c8dee2aaSAndroid Build Coastguard Worker fZenith = {0, -SK_Scalar1, 0}; // up
62*c8dee2aaSAndroid Build Coastguard Worker
63*c8dee2aaSAndroid Build Coastguard Worker fObserver = {0, 0, fLocation.z};
64*c8dee2aaSAndroid Build Coastguard Worker
65*c8dee2aaSAndroid Build Coastguard Worker fNeedToUpdate = true;
66*c8dee2aaSAndroid Build Coastguard Worker }
67*c8dee2aaSAndroid Build Coastguard Worker
update()68*c8dee2aaSAndroid Build Coastguard Worker void SkCamera3D::update() {
69*c8dee2aaSAndroid Build Coastguard Worker fNeedToUpdate = true;
70*c8dee2aaSAndroid Build Coastguard Worker }
71*c8dee2aaSAndroid Build Coastguard Worker
doUpdate() const72*c8dee2aaSAndroid Build Coastguard Worker void SkCamera3D::doUpdate() const {
73*c8dee2aaSAndroid Build Coastguard Worker SkV3 axis, zenith, cross;
74*c8dee2aaSAndroid Build Coastguard Worker
75*c8dee2aaSAndroid Build Coastguard Worker // construct a orthonormal basis of cross (x), zenith (y), and axis (z)
76*c8dee2aaSAndroid Build Coastguard Worker axis = fAxis.normalize();
77*c8dee2aaSAndroid Build Coastguard Worker
78*c8dee2aaSAndroid Build Coastguard Worker zenith = fZenith - (axis * fZenith) * axis;
79*c8dee2aaSAndroid Build Coastguard Worker zenith = zenith.normalize();
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker cross = axis.cross(zenith);
82*c8dee2aaSAndroid Build Coastguard Worker
83*c8dee2aaSAndroid Build Coastguard Worker {
84*c8dee2aaSAndroid Build Coastguard Worker SkMatrix* orien = &fOrientation;
85*c8dee2aaSAndroid Build Coastguard Worker auto [x, y, z] = fObserver;
86*c8dee2aaSAndroid Build Coastguard Worker
87*c8dee2aaSAndroid Build Coastguard Worker // Looking along the view axis we have:
88*c8dee2aaSAndroid Build Coastguard Worker //
89*c8dee2aaSAndroid Build Coastguard Worker // /|\ zenith
90*c8dee2aaSAndroid Build Coastguard Worker // |
91*c8dee2aaSAndroid Build Coastguard Worker // |
92*c8dee2aaSAndroid Build Coastguard Worker // | * observer (projected on XY plane)
93*c8dee2aaSAndroid Build Coastguard Worker // |
94*c8dee2aaSAndroid Build Coastguard Worker // |____________\ cross
95*c8dee2aaSAndroid Build Coastguard Worker // /
96*c8dee2aaSAndroid Build Coastguard Worker //
97*c8dee2aaSAndroid Build Coastguard Worker // So this does a z-shear along the view axis based on the observer's x and y values,
98*c8dee2aaSAndroid Build Coastguard Worker // and scales in x and y relative to the negative of the observer's z value
99*c8dee2aaSAndroid Build Coastguard Worker // (the observer is in the negative z direction).
100*c8dee2aaSAndroid Build Coastguard Worker
101*c8dee2aaSAndroid Build Coastguard Worker orien->set(SkMatrix::kMScaleX, x * axis.x - z * cross.x);
102*c8dee2aaSAndroid Build Coastguard Worker orien->set(SkMatrix::kMSkewX, x * axis.y - z * cross.y);
103*c8dee2aaSAndroid Build Coastguard Worker orien->set(SkMatrix::kMTransX, x * axis.z - z * cross.z);
104*c8dee2aaSAndroid Build Coastguard Worker orien->set(SkMatrix::kMSkewY, y * axis.x - z * zenith.x);
105*c8dee2aaSAndroid Build Coastguard Worker orien->set(SkMatrix::kMScaleY, y * axis.y - z * zenith.y);
106*c8dee2aaSAndroid Build Coastguard Worker orien->set(SkMatrix::kMTransY, y * axis.z - z * zenith.z);
107*c8dee2aaSAndroid Build Coastguard Worker orien->set(SkMatrix::kMPersp0, axis.x);
108*c8dee2aaSAndroid Build Coastguard Worker orien->set(SkMatrix::kMPersp1, axis.y);
109*c8dee2aaSAndroid Build Coastguard Worker orien->set(SkMatrix::kMPersp2, axis.z);
110*c8dee2aaSAndroid Build Coastguard Worker }
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker
patchToMatrix(const SkPatch3D & quilt,SkMatrix * matrix) const113*c8dee2aaSAndroid Build Coastguard Worker void SkCamera3D::patchToMatrix(const SkPatch3D& quilt, SkMatrix* matrix) const {
114*c8dee2aaSAndroid Build Coastguard Worker if (fNeedToUpdate) {
115*c8dee2aaSAndroid Build Coastguard Worker this->doUpdate();
116*c8dee2aaSAndroid Build Coastguard Worker fNeedToUpdate = false;
117*c8dee2aaSAndroid Build Coastguard Worker }
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker const SkScalar* mapPtr = (const SkScalar*)(const void*)&fOrientation;
120*c8dee2aaSAndroid Build Coastguard Worker const SkScalar* patchPtr;
121*c8dee2aaSAndroid Build Coastguard Worker
122*c8dee2aaSAndroid Build Coastguard Worker SkV3 diff = quilt.fOrigin - fLocation;
123*c8dee2aaSAndroid Build Coastguard Worker SkScalar dot = diff.dot({mapPtr[6], mapPtr[7], mapPtr[8]});
124*c8dee2aaSAndroid Build Coastguard Worker
125*c8dee2aaSAndroid Build Coastguard Worker // This multiplies fOrientation by the matrix [quilt.fU quilt.fV diff] -- U, V, and diff are
126*c8dee2aaSAndroid Build Coastguard Worker // column vectors in the matrix -- then divides by the length of the projection of diff onto
127*c8dee2aaSAndroid Build Coastguard Worker // the view axis (which is 'dot'). This transforms the patch (which transforms from local path
128*c8dee2aaSAndroid Build Coastguard Worker // space to world space) into view space (since fOrientation transforms from world space to
129*c8dee2aaSAndroid Build Coastguard Worker // view space).
130*c8dee2aaSAndroid Build Coastguard Worker //
131*c8dee2aaSAndroid Build Coastguard Worker // The divide by 'dot' isn't strictly necessary as the homogeneous divide would do much the
132*c8dee2aaSAndroid Build Coastguard Worker // same thing (it's just scaling the entire matrix by 1/dot). It looks like it's normalizing
133*c8dee2aaSAndroid Build Coastguard Worker // the matrix into some canonical space.
134*c8dee2aaSAndroid Build Coastguard Worker patchPtr = (const SkScalar*)&quilt;
135*c8dee2aaSAndroid Build Coastguard Worker matrix->set(SkMatrix::kMScaleX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
136*c8dee2aaSAndroid Build Coastguard Worker matrix->set(SkMatrix::kMSkewY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
137*c8dee2aaSAndroid Build Coastguard Worker matrix->set(SkMatrix::kMPersp0, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot));
138*c8dee2aaSAndroid Build Coastguard Worker
139*c8dee2aaSAndroid Build Coastguard Worker patchPtr += 3;
140*c8dee2aaSAndroid Build Coastguard Worker matrix->set(SkMatrix::kMSkewX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
141*c8dee2aaSAndroid Build Coastguard Worker matrix->set(SkMatrix::kMScaleY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
142*c8dee2aaSAndroid Build Coastguard Worker matrix->set(SkMatrix::kMPersp1, SkScalarDotDiv(3, patchPtr, 1, mapPtr+6, 1, dot));
143*c8dee2aaSAndroid Build Coastguard Worker
144*c8dee2aaSAndroid Build Coastguard Worker patchPtr = (const SkScalar*)(const void*)&diff;
145*c8dee2aaSAndroid Build Coastguard Worker matrix->set(SkMatrix::kMTransX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
146*c8dee2aaSAndroid Build Coastguard Worker matrix->set(SkMatrix::kMTransY, SkScalarDotDiv(3, patchPtr, 1, mapPtr+3, 1, dot));
147*c8dee2aaSAndroid Build Coastguard Worker matrix->set(SkMatrix::kMPersp2, SK_Scalar1);
148*c8dee2aaSAndroid Build Coastguard Worker }
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
151*c8dee2aaSAndroid Build Coastguard Worker
Sk3DView()152*c8dee2aaSAndroid Build Coastguard Worker Sk3DView::Sk3DView() {
153*c8dee2aaSAndroid Build Coastguard Worker fRec = &fInitialRec;
154*c8dee2aaSAndroid Build Coastguard Worker }
155*c8dee2aaSAndroid Build Coastguard Worker
~Sk3DView()156*c8dee2aaSAndroid Build Coastguard Worker Sk3DView::~Sk3DView() {
157*c8dee2aaSAndroid Build Coastguard Worker Rec* rec = fRec;
158*c8dee2aaSAndroid Build Coastguard Worker while (rec != &fInitialRec) {
159*c8dee2aaSAndroid Build Coastguard Worker Rec* next = rec->fNext;
160*c8dee2aaSAndroid Build Coastguard Worker delete rec;
161*c8dee2aaSAndroid Build Coastguard Worker rec = next;
162*c8dee2aaSAndroid Build Coastguard Worker }
163*c8dee2aaSAndroid Build Coastguard Worker }
164*c8dee2aaSAndroid Build Coastguard Worker
save()165*c8dee2aaSAndroid Build Coastguard Worker void Sk3DView::save() {
166*c8dee2aaSAndroid Build Coastguard Worker Rec* rec = new Rec;
167*c8dee2aaSAndroid Build Coastguard Worker rec->fNext = fRec;
168*c8dee2aaSAndroid Build Coastguard Worker rec->fMatrix = fRec->fMatrix;
169*c8dee2aaSAndroid Build Coastguard Worker fRec = rec;
170*c8dee2aaSAndroid Build Coastguard Worker }
171*c8dee2aaSAndroid Build Coastguard Worker
restore()172*c8dee2aaSAndroid Build Coastguard Worker void Sk3DView::restore() {
173*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fRec != &fInitialRec);
174*c8dee2aaSAndroid Build Coastguard Worker Rec* next = fRec->fNext;
175*c8dee2aaSAndroid Build Coastguard Worker delete fRec;
176*c8dee2aaSAndroid Build Coastguard Worker fRec = next;
177*c8dee2aaSAndroid Build Coastguard Worker }
178*c8dee2aaSAndroid Build Coastguard Worker
179*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
setCameraLocation(SkScalar x,SkScalar y,SkScalar z)180*c8dee2aaSAndroid Build Coastguard Worker void Sk3DView::setCameraLocation(SkScalar x, SkScalar y, SkScalar z) {
181*c8dee2aaSAndroid Build Coastguard Worker // the camera location is passed in inches, set in pt
182*c8dee2aaSAndroid Build Coastguard Worker SkScalar lz = z * 72.0f;
183*c8dee2aaSAndroid Build Coastguard Worker fCamera.fLocation = {x * 72.0f, y * 72.0f, lz};
184*c8dee2aaSAndroid Build Coastguard Worker fCamera.fObserver = {0, 0, lz};
185*c8dee2aaSAndroid Build Coastguard Worker fCamera.update();
186*c8dee2aaSAndroid Build Coastguard Worker
187*c8dee2aaSAndroid Build Coastguard Worker }
188*c8dee2aaSAndroid Build Coastguard Worker
getCameraLocationX() const189*c8dee2aaSAndroid Build Coastguard Worker SkScalar Sk3DView::getCameraLocationX() const {
190*c8dee2aaSAndroid Build Coastguard Worker return fCamera.fLocation.x / 72.0f;
191*c8dee2aaSAndroid Build Coastguard Worker }
192*c8dee2aaSAndroid Build Coastguard Worker
getCameraLocationY() const193*c8dee2aaSAndroid Build Coastguard Worker SkScalar Sk3DView::getCameraLocationY() const {
194*c8dee2aaSAndroid Build Coastguard Worker return fCamera.fLocation.y / 72.0f;
195*c8dee2aaSAndroid Build Coastguard Worker }
196*c8dee2aaSAndroid Build Coastguard Worker
getCameraLocationZ() const197*c8dee2aaSAndroid Build Coastguard Worker SkScalar Sk3DView::getCameraLocationZ() const {
198*c8dee2aaSAndroid Build Coastguard Worker return fCamera.fLocation.z / 72.0f;
199*c8dee2aaSAndroid Build Coastguard Worker }
200*c8dee2aaSAndroid Build Coastguard Worker #endif
201*c8dee2aaSAndroid Build Coastguard Worker
translate(SkScalar x,SkScalar y,SkScalar z)202*c8dee2aaSAndroid Build Coastguard Worker void Sk3DView::translate(SkScalar x, SkScalar y, SkScalar z) {
203*c8dee2aaSAndroid Build Coastguard Worker fRec->fMatrix.preTranslate(x, y, z);
204*c8dee2aaSAndroid Build Coastguard Worker }
205*c8dee2aaSAndroid Build Coastguard Worker
rotateX(SkScalar deg)206*c8dee2aaSAndroid Build Coastguard Worker void Sk3DView::rotateX(SkScalar deg) {
207*c8dee2aaSAndroid Build Coastguard Worker fRec->fMatrix.preConcat(SkM44::Rotate({1, 0, 0}, deg * SK_ScalarPI / 180));
208*c8dee2aaSAndroid Build Coastguard Worker }
209*c8dee2aaSAndroid Build Coastguard Worker
rotateY(SkScalar deg)210*c8dee2aaSAndroid Build Coastguard Worker void Sk3DView::rotateY(SkScalar deg) {
211*c8dee2aaSAndroid Build Coastguard Worker fRec->fMatrix.preConcat(SkM44::Rotate({0,-1, 0}, deg * SK_ScalarPI / 180));
212*c8dee2aaSAndroid Build Coastguard Worker }
213*c8dee2aaSAndroid Build Coastguard Worker
rotateZ(SkScalar deg)214*c8dee2aaSAndroid Build Coastguard Worker void Sk3DView::rotateZ(SkScalar deg) {
215*c8dee2aaSAndroid Build Coastguard Worker fRec->fMatrix.preConcat(SkM44::Rotate({0, 0, 1}, deg * SK_ScalarPI / 180));
216*c8dee2aaSAndroid Build Coastguard Worker }
217*c8dee2aaSAndroid Build Coastguard Worker
dotWithNormal(SkScalar x,SkScalar y,SkScalar z) const218*c8dee2aaSAndroid Build Coastguard Worker SkScalar Sk3DView::dotWithNormal(SkScalar x, SkScalar y, SkScalar z) const {
219*c8dee2aaSAndroid Build Coastguard Worker SkPatch3D patch;
220*c8dee2aaSAndroid Build Coastguard Worker patch.transform(fRec->fMatrix);
221*c8dee2aaSAndroid Build Coastguard Worker return patch.dotWith(x, y, z);
222*c8dee2aaSAndroid Build Coastguard Worker }
223*c8dee2aaSAndroid Build Coastguard Worker
getMatrix(SkMatrix * matrix) const224*c8dee2aaSAndroid Build Coastguard Worker void Sk3DView::getMatrix(SkMatrix* matrix) const {
225*c8dee2aaSAndroid Build Coastguard Worker if (matrix != nullptr) {
226*c8dee2aaSAndroid Build Coastguard Worker SkPatch3D patch;
227*c8dee2aaSAndroid Build Coastguard Worker patch.transform(fRec->fMatrix);
228*c8dee2aaSAndroid Build Coastguard Worker fCamera.patchToMatrix(patch, matrix);
229*c8dee2aaSAndroid Build Coastguard Worker }
230*c8dee2aaSAndroid Build Coastguard Worker }
231*c8dee2aaSAndroid Build Coastguard Worker
232*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
233*c8dee2aaSAndroid Build Coastguard Worker
applyToCanvas(SkCanvas * canvas) const234*c8dee2aaSAndroid Build Coastguard Worker void Sk3DView::applyToCanvas(SkCanvas* canvas) const {
235*c8dee2aaSAndroid Build Coastguard Worker SkMatrix matrix;
236*c8dee2aaSAndroid Build Coastguard Worker
237*c8dee2aaSAndroid Build Coastguard Worker this->getMatrix(&matrix);
238*c8dee2aaSAndroid Build Coastguard Worker canvas->concat(matrix);
239*c8dee2aaSAndroid Build Coastguard Worker }
240