| /* |
| * 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.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 points - how many light source points to generate |
| * @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, int points, float x, float y, float height) { |
| float[] ret = new float[points * 3]; |
| for (int i = 0; i < points; i++) { |
| double angle = 2 * i * Math.PI / points; |
| ret[i * 3] = (float) Math.sin(angle) * radius + x; |
| ret[i * 3 + 1] = (float) Math.cos(angle) * radius + y; |
| ret[i * 3 + 2] = (height); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * @param rays - Number of rays to use for tracing |
| * @param layers - Number of layers for shadow rendering. |
| * @return size required for shadow vertices mData array based on # of rays and layers |
| */ |
| public static int getStripSize(int rays, int layers){ |
| return (2 + rays + ((layers) * 2 * (rays + 1))); |
| } |
| |
| /** |
| * 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 lightPolyLength - Size of the vertices (usually lightPoly.length/3 unless w is |
| * included) |
| * @param poly - Vertices of opaque object casting shadow |
| * @param polyLength - Size of the vertices |
| * @param rays - Number of rays to use for tracing. It determines accuracy of the outline |
| * (bounds) of the shadow |
| * @param layers - Number of layers for shadow. It determines intensity of pen-umbra |
| * @param strength - Strength of the shadow overall [0-1] |
| * @param retstrips - Array mData to be filled in format : {x1, y1, z1, x2, y2, z2} |
| * @return 1 if successful, error code otherwise. |
| */ |
| public static int calculateShadow( |
| float[] lightPoly, |
| int lightPolyLength, |
| float[] poly, |
| int polyLength, |
| int rays, |
| int layers, |
| float strength, |
| float[] retstrips) { |
| float[] shadowRegion = new float[lightPolyLength * polyLength * 2]; |
| float[] outline = new float[polyLength * 2]; |
| float[] umbra = new float[polyLength * lightPolyLength * 2]; |
| int umbraLength = 0; |
| |
| int k = 0; |
| for (int j = 0; j < lightPolyLength; j++) { |
| int m = 0; |
| for (int i = 0; i < polyLength; i++) { |
| float t = lightPoly[j * 3 + 2] - poly[i * 3 + 2]; |
| if (t == 0) { |
| return 0; |
| } |
| t = lightPoly[j * 3 + 2] / t; |
| float x = lightPoly[j * 3] - t * (lightPoly[j * 3] - poly[i * 3]); |
| float y = lightPoly[j * 3 + 1] - t * (lightPoly[j * 3 + 1] - poly[i * 3 + 1]); |
| |
| shadowRegion[k * 2] = x; |
| shadowRegion[k * 2 + 1] = y; |
| outline[m * 2] = x; |
| outline[m * 2 + 1] = y; |
| |
| k++; |
| m++; |
| } |
| |
| if (umbraLength == 0) { |
| for (int i = 0; i < polyLength * 2; i++) { |
| umbra[i] = outline[i]; |
| } |
| umbraLength = polyLength; |
| } else { |
| umbraLength = Math3DHelper.intersection(outline, polyLength, umbra, umbraLength); |
| if (umbraLength == 0) { |
| break; |
| } |
| |
| } |
| } |
| int shadowRegionLength = k; |
| |
| float[] penumbra = new float[k * 2]; |
| int penumbraLength = Math3DHelper.hull(shadowRegion, shadowRegionLength, penumbra); |
| if (umbraLength < 3) {// no real umbra make a fake one |
| float[] p = new float[3]; |
| Math3DHelper.centroid3d(lightPoly, lightPolyLength, p); |
| float[] centShadow = new float[polyLength * 2]; |
| for (int i = 0; i < polyLength; i++) { |
| float t = p[2] - poly[i * 3 + 2]; |
| if (t == 0) { |
| return 0; |
| } |
| t = p[2] / t; |
| float x = p[0] - t * (p[0] - poly[i * 3]); |
| float y = p[1] - t * (p[1] - poly[i * 3 + 1]); |
| |
| centShadow[i * 2] = x; |
| centShadow[i * 2 + 1] = y; |
| } |
| float[] c = new float[2]; |
| Math3DHelper.centroid2d(centShadow, polyLength, c); |
| for (int i = 0; i < polyLength; i++) { |
| centShadow[i * 2] = (c[0] * 9 + centShadow[i * 2]) / 10; |
| centShadow[i * 2 + 1] = (c[1] * 9 + centShadow[i * 2 + 1]) / 10; |
| } |
| umbra = centShadow; // fake umbra |
| umbraLength = polyLength; // same size as the original polygon |
| } |
| |
| Math3DHelper.donutPie2(penumbra, penumbraLength, umbra, umbraLength, rays, |
| layers, strength, retstrips); |
| return 1; |
| } |
| } |