Home | History | Annotate | Download | only in shadow
      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 
     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      */
     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      */
     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      */
     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 }