diff options
Diffstat (limited to 'libs/hwui/ShadowTessellator.cpp')
-rw-r--r-- | libs/hwui/ShadowTessellator.cpp | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp new file mode 100644 index 000000000000..30c6f5dd1984 --- /dev/null +++ b/libs/hwui/ShadowTessellator.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "OpenGLRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW + +#include <math.h> +#include <utils/Log.h> +#include <utils/Trace.h> + +#include "AmbientShadow.h" +#include "Caches.h" +#include "ShadowTessellator.h" +#include "SpotShadow.h" + +namespace android { +namespace uirenderer { + +template<typename T> +static inline T max(T a, T b) { + return a > b ? a : b; +} + +void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, + const Vector3* casterPolygon, int casterVertexCount, + const Vector3& centroid3d, const Rect& casterBounds, + const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) { + ATRACE_CALL(); + + // A bunch of parameters to tweak the shadow. + // TODO: Allow some of these changable by debug settings or APIs. + float heightFactor = 1.0f / 128; + const float geomFactor = 64; + + Caches& caches = Caches::getInstance(); + if (CC_UNLIKELY(caches.propertyAmbientRatio > 0.0f)) { + heightFactor *= caches.propertyAmbientRatio; + } + + Rect ambientShadowBounds(casterBounds); + ambientShadowBounds.outset(maxZ * geomFactor * heightFactor); + + if (!localClip.intersects(ambientShadowBounds)) { +#if DEBUG_SHADOW + ALOGD("Ambient shadow is out of clip rect!"); +#endif + return; + } + + AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon, + casterVertexCount, centroid3d, heightFactor, geomFactor, + shadowVertexBuffer); +} + +void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, + const Vector3* casterPolygon, int casterVertexCount, + const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius, + const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer) { + ATRACE_CALL(); + + Caches& caches = Caches::getInstance(); + + Vector3 adjustedLightCenter(lightCenter); + if (CC_UNLIKELY(caches.propertyLightPosY > 0)) { + adjustedLightCenter.y = - caches.propertyLightPosY; // negated since this shifts up + } + if (CC_UNLIKELY(caches.propertyLightPosZ > 0)) { + adjustedLightCenter.z = caches.propertyLightPosZ; + } + +#if DEBUG_SHADOW + ALOGD("light center %f %f %f", + adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z); +#endif + + // light position (because it's in local space) needs to compensate for receiver transform + // TODO: should apply to light orientation, not just position + Matrix4 reverseReceiverTransform; + reverseReceiverTransform.loadInverse(receiverTransform); + reverseReceiverTransform.mapPoint3d(adjustedLightCenter); + + const int lightVertexCount = 8; + if (CC_UNLIKELY(caches.propertyLightDiameter > 0)) { + lightRadius = caches.propertyLightDiameter; + } + + // Now light and caster are both in local space, we will check whether + // the shadow is within the clip area. + Rect lightRect = Rect(adjustedLightCenter.x - lightRadius, adjustedLightCenter.y - lightRadius, + adjustedLightCenter.x + lightRadius, adjustedLightCenter.y + lightRadius); + lightRect.unionWith(localClip); + if (!lightRect.intersects(casterBounds)) { +#if DEBUG_SHADOW + ALOGD("Spot shadow is out of clip rect!"); +#endif + return; + } + + SpotShadow::createSpotShadow(isCasterOpaque, + casterPolygon, casterVertexCount, adjustedLightCenter, lightRadius, + lightVertexCount, shadowVertexBuffer); +#if DEBUG_SHADOW + if(shadowVertexBuffer.getVertexCount() <= 0) { + ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount()); + } +#endif +} + +void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) { + int currentIndex = 0; + const int rays = SHADOW_RAY_COUNT; + // For the penumbra area. + for (int layer = 0; layer < 2; layer ++) { + int baseIndex = layer * rays; + for (int i = 0; i < rays; i++) { + shadowIndices[currentIndex++] = i + baseIndex; + shadowIndices[currentIndex++] = rays + i + baseIndex; + } + // To close the loop, back to the ray 0. + shadowIndices[currentIndex++] = 0 + baseIndex; + // Note this is the same as the first index of next layer loop. + shadowIndices[currentIndex++] = rays + baseIndex; + } + +#if DEBUG_SHADOW + if (currentIndex != MAX_SHADOW_INDEX_COUNT) { + ALOGW("vertex index count is wrong. current %d, expected %d", + currentIndex, MAX_SHADOW_INDEX_COUNT); + } + for (int i = 0; i < MAX_SHADOW_INDEX_COUNT; i++) { + ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]); + } +#endif +} + +/** + * Calculate the centroid of a 2d polygon. + * + * @param poly The polygon, which is represented in a Vector2 array. + * @param polyLength The length of the polygon in terms of number of vertices. + * @return the centroid of the polygon. + */ +Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) { + double sumx = 0; + double sumy = 0; + int p1 = polyLength - 1; + double area = 0; + for (int p2 = 0; p2 < polyLength; p2++) { + double x1 = poly[p1].x; + double y1 = poly[p1].y; + double x2 = poly[p2].x; + double y2 = poly[p2].y; + double a = (x1 * y2 - x2 * y1); + sumx += (x1 + x2) * a; + sumy += (y1 + y2) * a; + area += a; + p1 = p2; + } + + Vector2 centroid = poly[0]; + if (area != 0) { + centroid = Vector2(sumx / (3 * area), sumy / (3 * area)); + } else { + ALOGW("Area is 0 while computing centroid!"); + } + return centroid; +} + +/** + * Test whether the polygon is order in clockwise. + * + * @param polygon the polygon as a Vector2 array + * @param len the number of points of the polygon + */ +bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) { + if (len < 2 || polygon == NULL) { + ALOGW("Invalid polygon %p, length is %d @ isClockwise()", polygon, len); + return true; + } + double sum = 0; + double p1x = polygon[len - 1].x; + double p1y = polygon[len - 1].y; + for (int i = 0; i < len; i++) { + + double p2x = polygon[i].x; + double p2y = polygon[i].y; + sum += p1x * p2y - p2x * p1y; + p1x = p2x; + p1y = p2y; + } + return sum < 0; +} + +bool ShadowTessellator::isClockwisePath(const SkPath& path) { + SkPath::Iter iter(path, false); + SkPoint pts[4]; + SkPath::Verb v; + + Vector<Vector2> arrayForDirection; + while (SkPath::kDone_Verb != (v = iter.next(pts))) { + switch (v) { + case SkPath::kMove_Verb: + arrayForDirection.add(Vector2(pts[0].x(), pts[0].y())); + break; + case SkPath::kLine_Verb: + arrayForDirection.add(Vector2(pts[1].x(), pts[1].y())); + break; + case SkPath::kQuad_Verb: + arrayForDirection.add(Vector2(pts[1].x(), pts[1].y())); + arrayForDirection.add(Vector2(pts[2].x(), pts[2].y())); + break; + case SkPath::kCubic_Verb: + arrayForDirection.add(Vector2(pts[1].x(), pts[1].y())); + arrayForDirection.add(Vector2(pts[2].x(), pts[2].y())); + arrayForDirection.add(Vector2(pts[3].x(), pts[3].y())); + break; + default: + break; + } + } + + return isClockwise(arrayForDirection.array(), arrayForDirection.size()); +} + +void ShadowTessellator::reverseVertexArray(Vertex* polygon, int len) { + int n = len / 2; + for (int i = 0; i < n; i++) { + Vertex tmp = polygon[i]; + int k = len - 1 - i; + polygon[i] = polygon[k]; + polygon[k] = tmp; + } +} + +}; // namespace uirenderer +}; // namespace android |