1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view.shadow; 18 19 import android.view.math.Math3DHelper; 20 21 /** 22 * Generates the vertices required for spot shadow and all other shadow-related rendering. 23 */ 24 class SpotShadowVertexCalculator { 25 SpotShadowVertexCalculator()26 private SpotShadowVertexCalculator() { } 27 28 /** 29 * Create evenly distributed circular light source points from x and y (on flat z plane). 30 * This is useful for ray tracing the shadow points later. Format : (x1,y1,z1,x2,y2,z2 ...) 31 * 32 * @param radius - radius of the light source 33 * @param points - how many light source points to generate 34 * @param x - center X of the light source 35 * @param y - center Y of the light source 36 * @param height - how high (z depth) the light should be 37 * @return float points (x,y,z) of light source points. 38 */ calculateLight(float radius, int points, float x, float y, float height)39 public static float[] calculateLight(float radius, int points, float x, float y, float height) { 40 float[] ret = new float[points * 3]; 41 for (int i = 0; i < points; i++) { 42 double angle = 2 * i * Math.PI / points; 43 ret[i * 3] = (float) Math.sin(angle) * radius + x; 44 ret[i * 3 + 1] = (float) Math.cos(angle) * radius + y; 45 ret[i * 3 + 2] = (height); 46 } 47 48 return ret; 49 } 50 51 /** 52 * @param rays - Number of rays to use for tracing 53 * @param layers - Number of layers for shadow rendering. 54 * @return size required for shadow vertices mData array based on # of rays and layers 55 */ getStripSize(int rays, int layers)56 public static int getStripSize(int rays, int layers){ 57 return (2 + rays + ((layers) * 2 * (rays + 1))); 58 } 59 60 /** 61 * Generate shadow vertices based on params. Format : (x1,y1,z1,x2,y2,z2 ...) 62 * Precondition : Light poly must be evenly distributed on a flat surface 63 * Precondition : Poly vertices must be a convex 64 * Precondition : Light height must be higher than any poly vertices 65 * 66 * @param lightPoly - Vertices of a light source. 67 * @param lightPolyLength - Size of the vertices (usually lightPoly.length/3 unless w is 68 * included) 69 * @param poly - Vertices of opaque object casting shadow 70 * @param polyLength - Size of the vertices 71 * @param rays - Number of rays to use for tracing. It determines accuracy of the outline 72 * (bounds) of the shadow 73 * @param layers - Number of layers for shadow. It determines intensity of pen-umbra 74 * @param strength - Strength of the shadow overall [0-1] 75 * @param retstrips - Array mData to be filled in format : {x1, y1, z1, x2, y2, z2} 76 * @return 1 if successful, error code otherwise. 77 */ calculateShadow( float[] lightPoly, int lightPolyLength, float[] poly, int polyLength, int rays, int layers, float strength, float[] retstrips)78 public static int calculateShadow( 79 float[] lightPoly, 80 int lightPolyLength, 81 float[] poly, 82 int polyLength, 83 int rays, 84 int layers, 85 float strength, 86 float[] retstrips) { 87 float[] shadowRegion = new float[lightPolyLength * polyLength * 2]; 88 float[] outline = new float[polyLength * 2]; 89 float[] umbra = new float[polyLength * lightPolyLength * 2]; 90 int umbraLength = 0; 91 92 int k = 0; 93 for (int j = 0; j < lightPolyLength; j++) { 94 int m = 0; 95 for (int i = 0; i < polyLength; i++) { 96 float t = lightPoly[j * 3 + 2] - poly[i * 3 + 2]; 97 if (t == 0) { 98 return 0; 99 } 100 t = lightPoly[j * 3 + 2] / t; 101 float x = lightPoly[j * 3] - t * (lightPoly[j * 3] - poly[i * 3]); 102 float y = lightPoly[j * 3 + 1] - t * (lightPoly[j * 3 + 1] - poly[i * 3 + 1]); 103 104 shadowRegion[k * 2] = x; 105 shadowRegion[k * 2 + 1] = y; 106 outline[m * 2] = x; 107 outline[m * 2 + 1] = y; 108 109 k++; 110 m++; 111 } 112 113 if (umbraLength == 0) { 114 for (int i = 0; i < polyLength * 2; i++) { 115 umbra[i] = outline[i]; 116 } 117 umbraLength = polyLength; 118 } else { 119 umbraLength = Math3DHelper.intersection(outline, polyLength, umbra, umbraLength); 120 if (umbraLength == 0) { 121 break; 122 } 123 124 } 125 } 126 int shadowRegionLength = k; 127 128 float[] penumbra = new float[k * 2]; 129 int penumbraLength = Math3DHelper.hull(shadowRegion, shadowRegionLength, penumbra); 130 if (umbraLength < 3) {// no real umbra make a fake one 131 float[] p = new float[3]; 132 Math3DHelper.centroid3d(lightPoly, lightPolyLength, p); 133 float[] centShadow = new float[polyLength * 2]; 134 for (int i = 0; i < polyLength; i++) { 135 float t = p[2] - poly[i * 3 + 2]; 136 if (t == 0) { 137 return 0; 138 } 139 t = p[2] / t; 140 float x = p[0] - t * (p[0] - poly[i * 3]); 141 float y = p[1] - t * (p[1] - poly[i * 3 + 1]); 142 143 centShadow[i * 2] = x; 144 centShadow[i * 2 + 1] = y; 145 } 146 float[] c = new float[2]; 147 Math3DHelper.centroid2d(centShadow, polyLength, c); 148 for (int i = 0; i < polyLength; i++) { 149 centShadow[i * 2] = (c[0] * 9 + centShadow[i * 2]) / 10; 150 centShadow[i * 2 + 1] = (c[1] * 9 + centShadow[i * 2 + 1]) / 10; 151 } 152 umbra = centShadow; // fake umbra 153 umbraLength = polyLength; // same size as the original polygon 154 } 155 156 Math3DHelper.donutPie2(penumbra, penumbraLength, umbra, umbraLength, rays, 157 layers, strength, retstrips); 158 return 1; 159 } 160 }