155bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui/*
255bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * Copyright (C) 2013 The Android Open Source Project
355bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui *
455bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * Licensed under the Apache License, Version 2.0 (the "License");
555bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * you may not use this file except in compliance with the License.
655bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * You may obtain a copy of the License at
755bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui *
855bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui *      http://www.apache.org/licenses/LICENSE-2.0
955bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui *
1055bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * Unless required by applicable law or agreed to in writing, software
1155bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * distributed under the License is distributed on an "AS IS" BASIS,
1255bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1355bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * See the License for the specific language governing permissions and
1455bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * limitations under the License.
1555bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui */
1655bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui
1755bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui#include <math.h>
1855bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui#include <utils/Log.h>
1987f9df880e4a5bfba65d2ed413b3ead649595129Chris Craik#include <utils/Trace.h>
201240752c44c21632adaf64f6768669ec6d0ebc8cLazar Trsic#include <utils/MathUtils.h>
2155bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui
2255bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui#include "AmbientShadow.h"
232507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik#include "Properties.h"
2455bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui#include "ShadowTessellator.h"
257b4516e7ea552ad08d6e7277d311ef11bd8f12e8ztenghui#include "SpotShadow.h"
262507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik#include "Vector.h"
2755bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui
2855bfb4e728fe1db619af5d2c287f4abe711b3343ztenghuinamespace android {
2955bfb4e728fe1db619af5d2c287f4abe711b3343ztenghuinamespace uirenderer {
3055bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui
3105f3d6e5111fd08df5cd9aae2c3d28399dc0e7f5Chris Craikvoid ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
3250ecf849cb7ccc3482517b74d2214b347927791eztenghui        const Vector3* casterPolygon, int casterVertexCount,
33af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui        const Vector3& centroid3d, const Rect& casterBounds,
34af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui        const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) {
3587f9df880e4a5bfba65d2ed413b3ead649595129Chris Craik    ATRACE_CALL();
3687f9df880e4a5bfba65d2ed413b3ead649595129Chris Craik
3755bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui    // A bunch of parameters to tweak the shadow.
3855bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui    // TODO: Allow some of these changable by debug settings or APIs.
398def74de33b197c0c5ec8774576b1d71c7ec4f1bztenghui    float heightFactor = 1.0f / 128;
407b4516e7ea552ad08d6e7277d311ef11bd8f12e8ztenghui    const float geomFactor = 64;
4155bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui
422507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik    if (CC_UNLIKELY(Properties::overrideAmbientRatio > 0.0f)) {
432507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik        heightFactor *= Properties::overrideAmbientRatio;
44f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik    }
45f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik
46af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui    Rect ambientShadowBounds(casterBounds);
47af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui    ambientShadowBounds.outset(maxZ * geomFactor * heightFactor);
48af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui
49af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui    if (!localClip.intersects(ambientShadowBounds)) {
50af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui#if DEBUG_SHADOW
51af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui        ALOGD("Ambient shadow is out of clip rect!");
52af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui#endif
5305f3d6e5111fd08df5cd9aae2c3d28399dc0e7f5Chris Craik        return;
54af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui    }
55af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui
5605f3d6e5111fd08df5cd9aae2c3d28399dc0e7f5Chris Craik    AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon,
5750ecf849cb7ccc3482517b74d2214b347927791eztenghui            casterVertexCount, centroid3d, heightFactor, geomFactor,
5850ecf849cb7ccc3482517b74d2214b347927791eztenghui            shadowVertexBuffer);
5955bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui}
6055bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui
6105f3d6e5111fd08df5cd9aae2c3d28399dc0e7f5Chris Craikvoid ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
62c50a03d78aaedd0003377e98710e7038bda330e9ztenghui        const Vector3* casterPolygon, int casterVertexCount, const Vector3& casterCentroid,
63797b95b26bbb7557678af78b9a2a61830158920fChris Craik        const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius,
64797b95b26bbb7557678af78b9a2a61830158920fChris Craik        const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer) {
6587f9df880e4a5bfba65d2ed413b3ead649595129Chris Craik    ATRACE_CALL();
6687f9df880e4a5bfba65d2ed413b3ead649595129Chris Craik
67797b95b26bbb7557678af78b9a2a61830158920fChris Craik    Vector3 adjustedLightCenter(lightCenter);
682507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik    if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) {
692507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik        adjustedLightCenter.y = - Properties::overrideLightPosY; // negated since this shifts up
70f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik    }
712507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik    if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) {
722507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik        adjustedLightCenter.z = Properties::overrideLightPosZ;
73f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik    }
74f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik
757b4516e7ea552ad08d6e7277d311ef11bd8f12e8ztenghui#if DEBUG_SHADOW
768d0ec389531d071529fb0a800f10733b057205d9Teng-Hui Zhu    ALOGD("light center %f %f %f %d",
778d0ec389531d071529fb0a800f10733b057205d9Teng-Hui Zhu            adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius);
787b4516e7ea552ad08d6e7277d311ef11bd8f12e8ztenghui#endif
79cf22d184a37d4dd551b045857e5725601f89236cTeng-Hui Zhu    if (isnan(adjustedLightCenter.x)
80cf22d184a37d4dd551b045857e5725601f89236cTeng-Hui Zhu            || isnan(adjustedLightCenter.y)
81cf22d184a37d4dd551b045857e5725601f89236cTeng-Hui Zhu            || isnan(adjustedLightCenter.z)) {
82cf22d184a37d4dd551b045857e5725601f89236cTeng-Hui Zhu        return;
83cf22d184a37d4dd551b045857e5725601f89236cTeng-Hui Zhu    }
843197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik
853197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik    // light position (because it's in local space) needs to compensate for receiver transform
863197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik    // TODO: should apply to light orientation, not just position
873197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik    Matrix4 reverseReceiverTransform;
883197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik    reverseReceiverTransform.loadInverse(receiverTransform);
89797b95b26bbb7557678af78b9a2a61830158920fChris Craik    reverseReceiverTransform.mapPoint3d(adjustedLightCenter);
903197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik
912507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik    if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) {
922507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik        lightRadius = Properties::overrideLightRadius;
93f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik    }
94f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik
95af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui    // Now light and caster are both in local space, we will check whether
96af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui    // the shadow is within the clip area.
97797b95b26bbb7557678af78b9a2a61830158920fChris Craik    Rect lightRect = Rect(adjustedLightCenter.x - lightRadius, adjustedLightCenter.y - lightRadius,
98797b95b26bbb7557678af78b9a2a61830158920fChris Craik            adjustedLightCenter.x + lightRadius, adjustedLightCenter.y + lightRadius);
99af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui    lightRect.unionWith(localClip);
100af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui    if (!lightRect.intersects(casterBounds)) {
101af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui#if DEBUG_SHADOW
102af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui        ALOGD("Spot shadow is out of clip rect!");
103af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui#endif
10405f3d6e5111fd08df5cd9aae2c3d28399dc0e7f5Chris Craik        return;
105af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui    }
106af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui
107c50a03d78aaedd0003377e98710e7038bda330e9ztenghui    SpotShadow::createSpotShadow(isCasterOpaque, adjustedLightCenter, lightRadius,
108c50a03d78aaedd0003377e98710e7038bda330e9ztenghui            casterPolygon, casterVertexCount, casterCentroid, shadowVertexBuffer);
109c50a03d78aaedd0003377e98710e7038bda330e9ztenghui
11050ecf849cb7ccc3482517b74d2214b347927791eztenghui#if DEBUG_SHADOW
11150ecf849cb7ccc3482517b74d2214b347927791eztenghui     if(shadowVertexBuffer.getVertexCount() <= 0) {
11250ecf849cb7ccc3482517b74d2214b347927791eztenghui        ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount());
11350ecf849cb7ccc3482517b74d2214b347927791eztenghui     }
11450ecf849cb7ccc3482517b74d2214b347927791eztenghui#endif
1157b4516e7ea552ad08d6e7277d311ef11bd8f12e8ztenghui}
11663d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui
11763d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui/**
11863d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui * Calculate the centroid of a 2d polygon.
11963d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui *
12063d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui * @param poly The polygon, which is represented in a Vector2 array.
12163d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui * @param polyLength The length of the polygon in terms of number of vertices.
12263d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui * @return the centroid of the polygon.
12363d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui */
12463d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghuiVector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) {
12563d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    double sumx = 0;
12663d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    double sumy = 0;
12763d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    int p1 = polyLength - 1;
12863d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    double area = 0;
12963d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    for (int p2 = 0; p2 < polyLength; p2++) {
13063d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui        double x1 = poly[p1].x;
13163d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui        double y1 = poly[p1].y;
13263d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui        double x2 = poly[p2].x;
13363d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui        double y2 = poly[p2].y;
13463d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui        double a = (x1 * y2 - x2 * y1);
13563d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui        sumx += (x1 + x2) * a;
13663d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui        sumy += (y1 + y2) * a;
13763d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui        area += a;
13863d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui        p1 = p2;
13963d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    }
14063d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui
14163d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    Vector2 centroid = poly[0];
14263d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    if (area != 0) {
1431aa5d2d7068147ff781cfe911a93f01593a68c79John Reck        centroid = (Vector2){static_cast<float>(sumx / (3 * area)),
1441aa5d2d7068147ff781cfe911a93f01593a68c79John Reck            static_cast<float>(sumy / (3 * area))};
14563d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    } else {
14650ecf849cb7ccc3482517b74d2214b347927791eztenghui        ALOGW("Area is 0 while computing centroid!");
14763d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    }
14863d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui    return centroid;
14963d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui}
15063d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui
151c50a03d78aaedd0003377e98710e7038bda330e9ztenghui// Make sure p1 -> p2 is going CW around the poly.
152c50a03d78aaedd0003377e98710e7038bda330e9ztenghuiVector2 ShadowTessellator::calculateNormal(const Vector2& p1, const Vector2& p2) {
153c50a03d78aaedd0003377e98710e7038bda330e9ztenghui    Vector2 result = p2 - p1;
154c50a03d78aaedd0003377e98710e7038bda330e9ztenghui    if (result.x != 0 || result.y != 0) {
155c50a03d78aaedd0003377e98710e7038bda330e9ztenghui        result.normalize();
156c50a03d78aaedd0003377e98710e7038bda330e9ztenghui        // Calculate the normal , which is CCW 90 rotate to the delta.
157c50a03d78aaedd0003377e98710e7038bda330e9ztenghui        float tempy = result.y;
158c50a03d78aaedd0003377e98710e7038bda330e9ztenghui        result.y = result.x;
159c50a03d78aaedd0003377e98710e7038bda330e9ztenghui        result.x = -tempy;
160c50a03d78aaedd0003377e98710e7038bda330e9ztenghui    }
161c50a03d78aaedd0003377e98710e7038bda330e9ztenghui    return result;
162c50a03d78aaedd0003377e98710e7038bda330e9ztenghui}
1632e023f3827dfc0dfc1ed7c3dd54d02b4a993f0b4ztenghui
164512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghuiint ShadowTessellator::getExtraVertexNumber(const Vector2& vector1,
165512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui        const Vector2& vector2, float divisor) {
166512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    // When there is no distance difference, there is no need for extra vertices.
167512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    if (vector1.lengthSquared() == 0 || vector2.lengthSquared() == 0) {
168512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui        return 0;
169512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    }
170512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    // The formula is :
171512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    // extraNumber = floor(acos(dot(n1, n2)) / (M_PI / EXTRA_VERTEX_PER_PI))
172512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    // The value ranges for each step are:
173512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    // dot( ) --- [-1, 1]
174512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    // acos( )     --- [0, M_PI]
175512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    // floor(...)  --- [0, EXTRA_VERTEX_PER_PI]
176512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    float dotProduct = vector1.dot(vector2);
1771240752c44c21632adaf64f6768669ec6d0ebc8cLazar Trsic    // make sure that dotProduct value is in acsof input range [-1, 1]
1781240752c44c21632adaf64f6768669ec6d0ebc8cLazar Trsic    dotProduct = MathUtils::clamp(dotProduct, -1.0f, 1.0f);
179512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    // TODO: Use look up table for the dotProduct to extraVerticesNumber
180512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    // computation, if needed.
181512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    float angle = acosf(dotProduct);
182512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    return (int) floor(angle / divisor);
183512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui}
184512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui
185512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghuivoid ShadowTessellator::checkOverflow(int used, int total, const char* bufferName) {
186512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui    LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d",
187512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui            bufferName, used, total);
188512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui}
189512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui
19055bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui}; // namespace uirenderer
19155bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui}; // namespace android
192