| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.view.shadow; |
| |
| import android.graphics.Rect; |
| import android.view.math.Math3DHelper; |
| |
| /** |
| * Generates the vertices required for spot shadow and all other shadow-related rendering. |
| */ |
| class SpotShadowVertexCalculator { |
| |
| private SpotShadowVertexCalculator() { } |
| |
| /** |
| * Create evenly distributed circular light source points from x and y (on flat z plane). |
| * This is useful for ray tracing the shadow points later. Format : (x1,y1,z1,x2,y2,z2 ...) |
| * |
| * @param radius - radius of the light source |
| * @param x - center X of the light source |
| * @param y - center Y of the light source |
| * @param height - how high (z depth) the light should be |
| * @return float points (x,y,z) of light source points. |
| */ |
| public static float[] calculateLight(float radius, float x, float y, float height) { |
| float[] ret = new float[4 * 3]; |
| // bottom |
| ret[0] = x; |
| ret[1] = y + radius; |
| ret[2] = height; |
| // left |
| ret[3] = x - radius; |
| ret[4] = y; |
| ret[5] = height; |
| // top |
| ret[6] = x; |
| ret[7] = y - radius; |
| ret[8] = height; |
| // right |
| ret[9] = x + radius; |
| ret[10] = y; |
| ret[11] = height; |
| |
| return ret; |
| } |
| |
| /** |
| * @param polyLength - length of the outline polygon |
| * @return size required for shadow vertices mData array based on # of vertices in the |
| * outline polygon |
| */ |
| public static int[] getStripSizes(int polyLength){ |
| return new int[] { ((polyLength + 4) / 8) * 16 + 2, 4}; |
| } |
| |
| /** |
| * Generate shadow vertices based on params. Format : (x1,y1,z1,x2,y2,z2 ...) |
| * Precondition : Light poly must be evenly distributed on a flat surface |
| * Precondition : Poly vertices must be a convex |
| * Precondition : Light height must be higher than any poly vertices |
| * |
| * @param lightPoly - Vertices of a light source. |
| * @param poly - Vertices of opaque object casting shadow |
| * @param polyLength - Size of the vertices |
| * @param strength - Strength of the shadow overall [0-1] |
| * @param retstrips - Arrays of triplets, each triplet represents a point, thus every array to |
| * be filled in format : {x1, y1, z1, x2, y2, z2, ...}, |
| * every 3 consecutive triplets constitute a triangle to fill, namely [t1, t2, t3], [t2, t3, |
| * t4], ... If at some point [t(n-1), tn, t(n+1)] is no longer a desired a triangle and |
| * there are more triangles to draw one can start a new array, hence retstrips is a 2D array. |
| */ |
| public static void calculateShadow( |
| float[] lightPoly, |
| float[] poly, |
| int polyLength, |
| float strength, |
| float[][] retstrips) { |
| float[] outerStrip = retstrips[0]; |
| |
| // We would like to unify the cases where we have roundrects and rectangles |
| int roundedEdgeSegments = ((polyLength == 4) ? 0 : ShadowConstants.SPLICE_ROUNDED_EDGE); |
| int sideLength = (roundedEdgeSegments / 2 + 1) * 2; |
| float[] umbra = new float[4 * 2 * sideLength]; |
| int idx = (roundedEdgeSegments + 1) / 2; |
| int uShift = 0; |
| // If we have even number of segments in rounded corner (0 included), the central point of |
| // rounded corner contributes to the hull twice, from 2 different light sources, thus |
| // rollBack in that case, otherwise every point contributes only once |
| int rollBack = (((polyLength % 8) == 0) ? 0 : 1); |
| // Calculate umbra - a hull of all projections |
| for (int s = 0; s < 4; ++s) { // 4 sides |
| float lx = lightPoly[s * 3 + 0]; |
| float ly = lightPoly[s * 3 + 1]; |
| float lz = lightPoly[s * 3 + 2]; |
| for (int i = 0; i < sideLength; ++i, uShift += 2, ++idx) { |
| int shift = (idx % polyLength) * 3; |
| |
| float t = lz / (lz - poly[shift + 2]); |
| |
| umbra[uShift + 0] = lx - t * (lx - poly[shift + 0]); |
| umbra[uShift + 1] = ly - t * (ly - poly[shift + 1]); |
| } |
| |
| idx -= rollBack; |
| } |
| |
| idx = roundedEdgeSegments; |
| // An array that wil contain top, right, bottom, left coordinate of penumbra |
| float[] penumbraRect = new float[4]; |
| // Calculate penumbra |
| for (int s = 0; s < 4; ++s, idx += (roundedEdgeSegments + 1)) { // 4 sides |
| int sp = (s + 2) % 4; |
| |
| float lz = lightPoly[sp * 3 + 2]; |
| |
| int shift = (idx % polyLength) * 3; |
| |
| float t = lz / (lz - poly[shift + 2]); |
| |
| // We are interested in just one coordinate: x or y, depending on the light source |
| int c = (s + 1) % 2; |
| penumbraRect[s] = |
| lightPoly[sp * 3 + c] - t * (lightPoly[sp * 3 + c] - poly[shift + c]); |
| } |
| if (penumbraRect[0] > penumbraRect[2]) { |
| float tmp = (penumbraRect[0] + penumbraRect[2]) / 2.0f; |
| penumbraRect[0] = penumbraRect[2] = tmp; |
| } |
| if (penumbraRect[3] > penumbraRect[1]) { |
| float tmp = (penumbraRect[1] + penumbraRect[3]) / 2.0f; |
| penumbraRect[1] = penumbraRect[3] = tmp; |
| } |
| |
| // Now just connect umbra points (at least 8 of them) with the closest points from |
| // penumbra (only 4 of them) to form triangles to fill the entire space between umbra and |
| // penumbra |
| idx = sideLength * 4 - sideLength / 2; |
| int rsShift = 0; |
| for (int s = 0; s < 4; ++s) { |
| int xidx = (((s + 3) % 4) / 2) * 2 + 1; |
| int yidx = (s / 2) * 2; |
| float penumbraX = penumbraRect[xidx]; |
| float penumbraY = penumbraRect[yidx]; |
| for (int i = 0; i < sideLength; ++i, rsShift += 6, ++idx) { |
| int shift = (idx % (sideLength * 4)) * 2; |
| |
| outerStrip[rsShift + 0] = umbra[shift + 0]; |
| outerStrip[rsShift + 1] = umbra[shift + 1]; |
| outerStrip[rsShift + 3] = penumbraX; |
| outerStrip[rsShift + 4] = penumbraY; |
| outerStrip[rsShift + 5] = strength; |
| } |
| } |
| // Connect with the beginning |
| outerStrip[rsShift + 0] = outerStrip[0]; |
| outerStrip[rsShift + 1] = outerStrip[1]; |
| // outerStrip[rsShift + 2] = 0; |
| outerStrip[rsShift + 3] = outerStrip[3]; |
| outerStrip[rsShift + 4] = outerStrip[4]; |
| outerStrip[rsShift + 5] = strength; |
| |
| float[] innerStrip = retstrips[1]; |
| // Covering penumbra rectangle |
| // left, top |
| innerStrip[0] = penumbraRect[3]; |
| innerStrip[1] = penumbraRect[0]; |
| innerStrip[2] = strength; |
| // right, top |
| innerStrip[3] = penumbraRect[1]; |
| innerStrip[4] = penumbraRect[0]; |
| innerStrip[5] = strength; |
| // left, bottom |
| innerStrip[6] = penumbraRect[3]; |
| innerStrip[7] = penumbraRect[2]; |
| innerStrip[8] = strength; |
| // right, bottom |
| innerStrip[9] = penumbraRect[1]; |
| innerStrip[10] = penumbraRect[2]; |
| innerStrip[11] = strength; |
| } |
| } |