ShadowTessellator.cpp revision 87f9df880e4a5bfba65d2ed413b3ead649595129
1/*
2 * Copyright (C) 2013 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#define LOG_TAG "OpenGLRenderer"
18#define ATRACE_TAG ATRACE_TAG_VIEW
19
20#include <math.h>
21#include <utils/Log.h>
22#include <utils/Trace.h>
23
24#include "AmbientShadow.h"
25#include "ShadowTessellator.h"
26#include "SpotShadow.h"
27
28namespace android {
29namespace uirenderer {
30
31template<typename T>
32static inline T max(T a, T b) {
33    return a > b ? a : b;
34}
35
36void ShadowTessellator::tessellateAmbientShadow(const Vector3* casterPolygon,
37        int casterVertexCount, const Vector3& centroid3d,
38        VertexBuffer& shadowVertexBuffer) {
39    ATRACE_CALL();
40
41    // A bunch of parameters to tweak the shadow.
42    // TODO: Allow some of these changable by debug settings or APIs.
43    const float heightFactor = 128;
44    const float geomFactor = 64;
45
46    AmbientShadow::createAmbientShadow(casterPolygon, casterVertexCount,
47            centroid3d, heightFactor, geomFactor, shadowVertexBuffer);
48
49}
50
51void ShadowTessellator::tessellateSpotShadow(const Vector3* casterPolygon, int casterVertexCount,
52        const Vector3& lightPosScale, const mat4& receiverTransform,
53        int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer) {
54    ATRACE_CALL();
55
56    // A bunch of parameters to tweak the shadow.
57    // TODO: Allow some of these changable by debug settings or APIs.
58    int maximal = max(screenWidth, screenHeight);
59    Vector3 lightCenter(screenWidth * lightPosScale.x, screenHeight * lightPosScale.y,
60            maximal * lightPosScale.z);
61#if DEBUG_SHADOW
62    ALOGD("light center %f %f %f", lightCenter.x, lightCenter.y, lightCenter.z);
63#endif
64
65    // light position (because it's in local space) needs to compensate for receiver transform
66    // TODO: should apply to light orientation, not just position
67    Matrix4 reverseReceiverTransform;
68    reverseReceiverTransform.loadInverse(receiverTransform);
69    reverseReceiverTransform.mapPoint3d(lightCenter);
70
71    const float lightSize = maximal / 4;
72    const int lightVertexCount = 16;
73
74    SpotShadow::createSpotShadow(casterPolygon, casterVertexCount, lightCenter,
75            lightSize, lightVertexCount, shadowVertexBuffer);
76
77}
78
79void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) {
80    int currentIndex = 0;
81    const int layers = SHADOW_LAYER_COUNT;
82    const int rays = SHADOW_RAY_COUNT;
83    // For the penumbra area.
84    for (int i = 0; i < layers; i++) {
85        for (int j = 0; j < rays; j++) {
86            shadowIndices[currentIndex++] = i * rays + j;
87            shadowIndices[currentIndex++] = (i + 1) * rays + j;
88        }
89        // To close the loop, back to the ray 0.
90        shadowIndices[currentIndex++] = i * rays;
91        shadowIndices[currentIndex++] = (i + 1) * rays;
92    }
93    uint16_t base = layers * rays;
94    uint16_t centroidIndex = (layers + 1) * rays;
95    // For the umbra area, using strips to simulate the fans.
96    for (int k = 0; k < rays; k++) {
97        shadowIndices[currentIndex++] = base + k;
98        shadowIndices[currentIndex++] = centroidIndex;
99    }
100    shadowIndices[currentIndex++] = base;
101
102#if DEBUG_SHADOW
103    if (currentIndex != SHADOW_INDEX_COUNT) {
104        ALOGE("vertex index count is wrong. current %d, expected %d",
105                currentIndex, SHADOW_INDEX_COUNT);
106    }
107    for (int i = 0; i < SHADOW_INDEX_COUNT; i++) {
108        ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]);
109    }
110#endif
111}
112
113/**
114 * Calculate the centroid of a 2d polygon.
115 *
116 * @param poly The polygon, which is represented in a Vector2 array.
117 * @param polyLength The length of the polygon in terms of number of vertices.
118 * @return the centroid of the polygon.
119 */
120Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) {
121    double sumx = 0;
122    double sumy = 0;
123    int p1 = polyLength - 1;
124    double area = 0;
125    for (int p2 = 0; p2 < polyLength; p2++) {
126        double x1 = poly[p1].x;
127        double y1 = poly[p1].y;
128        double x2 = poly[p2].x;
129        double y2 = poly[p2].y;
130        double a = (x1 * y2 - x2 * y1);
131        sumx += (x1 + x2) * a;
132        sumy += (y1 + y2) * a;
133        area += a;
134        p1 = p2;
135    }
136
137    Vector2 centroid = poly[0];
138    if (area != 0) {
139        centroid = Vector2(sumx / (3 * area), sumy / (3 * area));
140    } else {
141        ALOGE("Area is 0 while computing centroid!");
142    }
143    return centroid;
144}
145
146}; // namespace uirenderer
147}; // namespace android
148