| /* |
| * 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" |
| |
| /** |
| * Extra vertices for the corner for smoother corner. |
| * Only for outer vertices. |
| * Note that we use such extra memory to avoid an extra loop. |
| */ |
| // For half circle, we could add EXTRA_VERTEX_PER_PI vertices. |
| // Set to 1 if we don't want to have any. |
| #define EXTRA_CORNER_VERTEX_PER_PI 12 |
| |
| // For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI, |
| // therefore, the maximum number of extra vertices will be twice bigger. |
| #define MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * EXTRA_CORNER_VERTEX_PER_PI) |
| |
| // For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals. |
| #define CORNER_RADIANS_DIVISOR (M_PI / EXTRA_CORNER_VERTEX_PER_PI) |
| |
| /** |
| * Extra vertices for the Edge for interpolation artifacts. |
| * Same value for both inner and outer vertices. |
| */ |
| #define EXTRA_EDGE_VERTEX_PER_PI 50 |
| |
| #define MAX_EXTRA_EDGE_VERTEX_NUMBER (2 * EXTRA_EDGE_VERTEX_PER_PI) |
| |
| #define EDGE_RADIANS_DIVISOR (M_PI / EXTRA_EDGE_VERTEX_PER_PI) |
| |
| /** |
| * Other constants: |
| */ |
| // For the edge of the penumbra, the opacity is 0. |
| #define OUTER_OPACITY (0.0f) |
| |
| // Once the alpha difference is greater than this threshold, we will allocate extra |
| // edge vertices. |
| // If this is set to negative value, then all the edge will be tessellated. |
| #define ALPHA_THRESHOLD (0.1f / 255.0f) |
| |
| #include <math.h> |
| #include <utils/Log.h> |
| #include <utils/Vector.h> |
| |
| #include "AmbientShadow.h" |
| #include "ShadowTessellator.h" |
| #include "Vertex.h" |
| #include "utils/MathUtils.h" |
| |
| namespace android { |
| namespace uirenderer { |
| |
| /** |
| * Local utility functions. |
| */ |
| inline Vector2 getNormalFromVertices(const Vector3* vertices, int current, int next) { |
| // Convert from Vector3 to Vector2 first. |
| Vector2 currentVertex = { vertices[current].x, vertices[current].y }; |
| Vector2 nextVertex = { vertices[next].x, vertices[next].y }; |
| |
| return ShadowTessellator::calculateNormal(currentVertex, nextVertex); |
| } |
| |
| // The input z value will be converted to be non-negative inside. |
| // The output must be ranged from 0 to 1. |
| inline float getAlphaFromFactoredZ(float factoredZ) { |
| return 1.0 / (1 + MathUtils::max(factoredZ, 0.0f)); |
| } |
| |
| inline float getTransformedAlphaFromAlpha(float alpha) { |
| return acosf(1.0f - 2.0f * alpha); |
| } |
| |
| // The output is ranged from 0 to M_PI. |
| inline float getTransformedAlphaFromFactoredZ(float factoredZ) { |
| return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ)); |
| } |
| |
| inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike, |
| const Vector3& secondVertex, const Vector3& centroid) { |
| Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y}; |
| secondSpike.normalize(); |
| |
| int result = ShadowTessellator::getExtraVertexNumber(secondSpike, *currentSpike, |
| EDGE_RADIANS_DIVISOR); |
| *currentSpike = secondSpike; |
| return result; |
| } |
| |
| // Given the caster's vertex count, compute all the buffers size depending on |
| // whether or not the caster is opaque. |
| inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount, |
| int* totalUmbraCount, int casterVertexCount, bool isCasterOpaque) { |
| // Compute the size of the vertex buffer. |
| int outerVertexCount = casterVertexCount * 2 + MAX_EXTRA_CORNER_VERTEX_NUMBER + |
| MAX_EXTRA_EDGE_VERTEX_NUMBER; |
| int innerVertexCount = casterVertexCount + MAX_EXTRA_EDGE_VERTEX_NUMBER; |
| *totalVertexCount = outerVertexCount + innerVertexCount; |
| |
| // Compute the size of the index buffer. |
| *totalIndexCount = 2 * outerVertexCount + 2; |
| |
| // Compute the size of the umber buffer. |
| // For translucent object, keep track of the umbra(inner) vertex in order to draw |
| // inside. We only need to store the index information. |
| *totalUmbraCount = 0; |
| if (!isCasterOpaque) { |
| // Add the centroid if occluder is translucent. |
| (*totalVertexCount)++; |
| *totalIndexCount += 2 * innerVertexCount + 1; |
| *totalUmbraCount = innerVertexCount; |
| } |
| } |
| |
| inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) { |
| return fabsf(firstAlpha - secondAlpha) > ALPHA_THRESHOLD; |
| } |
| |
| /** |
| * Calculate the shadows as a triangle strips while alpha value as the |
| * shadow values. |
| * |
| * @param isCasterOpaque Whether the caster is opaque. |
| * @param vertices The shadow caster's polygon, which is represented in a Vector3 |
| * array. |
| * @param vertexCount The length of caster's polygon in terms of number of |
| * vertices. |
| * @param centroid3d The centroid of the shadow caster. |
| * @param heightFactor The factor showing the higher the object, the lighter the |
| * shadow. |
| * @param geomFactor The factor scaling the geometry expansion along the normal. |
| * |
| * @param shadowVertexBuffer Return an floating point array of (x, y, a) |
| * triangle strips mode. |
| * |
| * An simple illustration: |
| * For now let's mark the outer vertex as Pi, the inner as Vi, the centroid as C. |
| * |
| * First project the occluder to the Z=0 surface. |
| * Then we got all the inner vertices. And we compute the normal for each edge. |
| * According to the normal, we generate outer vertices. E.g: We generate P1 / P4 |
| * as extra corner vertices to make the corner looks round and smoother. |
| * |
| * Due to the fact that the alpha is not linear interpolated along the inner |
| * edge, when the alpha is different, we may add extra vertices such as P2.1, P2.2, |
| * V0.1, V0.2 to avoid the visual artifacts. |
| * |
| * (P3) |
| * (P2) (P2.1) (P2.2) | ' (P4) |
| * (P1)' | | | | ' |
| * ' | | | | ' |
| * (P0) ------------------------------------------------(P5) |
| * | (V0) (V0.1) (V0.2) |(V1) |
| * | | |
| * | | |
| * | (C) | |
| * | | |
| * | | |
| * | | |
| * | | |
| * (V3)-----------------------------------(V2) |
| */ |
| void AmbientShadow::createAmbientShadow(bool isCasterOpaque, |
| const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d, |
| float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) { |
| shadowVertexBuffer.setMode(VertexBuffer::kIndices); |
| |
| // In order to computer the outer vertices in one loop, we need pre-compute |
| // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value |
| // for vertex 0. |
| Vector2 previousNormal = getNormalFromVertices(casterVertices, |
| casterVertexCount - 1 , 0); |
| Vector2 currentSpike = {casterVertices[0].x - centroid3d.x, |
| casterVertices[0].y - centroid3d.y}; |
| currentSpike.normalize(); |
| float currentAlpha = getAlphaFromFactoredZ(casterVertices[0].z * heightFactor); |
| |
| // Preparing all the output data. |
| int totalVertexCount, totalIndexCount, totalUmbraCount; |
| computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount, |
| casterVertexCount, isCasterOpaque); |
| AlphaVertex* shadowVertices = |
| shadowVertexBuffer.alloc<AlphaVertex>(totalVertexCount); |
| int vertexBufferIndex = 0; |
| uint16_t* indexBuffer = shadowVertexBuffer.allocIndices<uint16_t>(totalIndexCount); |
| int indexBufferIndex = 0; |
| uint16_t umbraVertices[totalUmbraCount]; |
| int umbraIndex = 0; |
| |
| for (int i = 0; i < casterVertexCount; i++) { |
| // Corner: first figure out the extra vertices we need for the corner. |
| const Vector3& innerVertex = casterVertices[i]; |
| Vector2 currentNormal = getNormalFromVertices(casterVertices, i, |
| (i + 1) % casterVertexCount); |
| |
| int extraVerticesNumber = ShadowTessellator::getExtraVertexNumber(currentNormal, |
| previousNormal, CORNER_RADIANS_DIVISOR); |
| |
| float expansionDist = innerVertex.z * heightFactor * geomFactor; |
| const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1. |
| #if DEBUG_SHADOW |
| ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber); |
| #endif |
| |
| // Corner: fill the corner Vertex Buffer(VB) and Index Buffer(IB). |
| // We fill the inner vertex first, such that we can fill the index buffer |
| // inside the loop. |
| int currentInnerVertexIndex = vertexBufferIndex; |
| if (!isCasterOpaque) { |
| umbraVertices[umbraIndex++] = vertexBufferIndex; |
| } |
| AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x, |
| casterVertices[i].y, |
| getTransformedAlphaFromAlpha(currentAlpha)); |
| |
| const Vector3& innerStart = casterVertices[i]; |
| |
| // outerStart is the first outer vertex for this inner vertex. |
| // outerLast is the last outer vertex for this inner vertex. |
| Vector2 outerStart = {0, 0}; |
| Vector2 outerLast = {0, 0}; |
| // This will create vertices from [0, cornerSlicesNumber] inclusively, |
| // which means minimally 2 vertices even without the extra ones. |
| for (int j = 0; j <= cornerSlicesNumber; j++) { |
| Vector2 averageNormal = |
| previousNormal * (cornerSlicesNumber - j) + currentNormal * j; |
| averageNormal /= cornerSlicesNumber; |
| averageNormal.normalize(); |
| Vector2 outerVertex; |
| outerVertex.x = innerVertex.x + averageNormal.x * expansionDist; |
| outerVertex.y = innerVertex.y + averageNormal.y * expansionDist; |
| |
| indexBuffer[indexBufferIndex++] = vertexBufferIndex; |
| indexBuffer[indexBufferIndex++] = currentInnerVertexIndex; |
| AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x, |
| outerVertex.y, OUTER_OPACITY); |
| |
| if (j == 0) { |
| outerStart = outerVertex; |
| } else if (j == cornerSlicesNumber) { |
| outerLast = outerVertex; |
| } |
| } |
| previousNormal = currentNormal; |
| |
| // Edge: first figure out the extra vertices needed for the edge. |
| const Vector3& innerNext = casterVertices[(i + 1) % casterVertexCount]; |
| float nextAlpha = getAlphaFromFactoredZ(innerNext.z * heightFactor); |
| if (needsExtraForEdge(currentAlpha, nextAlpha)) { |
| // TODO: See if we can / should cache this outer vertex across the loop. |
| Vector2 outerNext; |
| float expansionDist = innerNext.z * heightFactor * geomFactor; |
| outerNext.x = innerNext.x + currentNormal.x * expansionDist; |
| outerNext.y = innerNext.y + currentNormal.y * expansionDist; |
| |
| // Compute the angle and see how many extra points we need. |
| int extraVerticesNumber = getEdgeExtraAndUpdateSpike(¤tSpike, |
| innerNext, centroid3d); |
| #if DEBUG_SHADOW |
| ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i); |
| #endif |
| // Edge: fill the edge's VB and IB. |
| // This will create vertices pair from [1, extraVerticesNumber - 1]. |
| // If there is no extra vertices created here, the edge will be drawn |
| // as just 2 triangles. |
| for (int k = 1; k < extraVerticesNumber; k++) { |
| int startWeight = extraVerticesNumber - k; |
| Vector2 currentOuter = |
| (outerLast * startWeight + outerNext * k) / extraVerticesNumber; |
| indexBuffer[indexBufferIndex++] = vertexBufferIndex; |
| AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x, |
| currentOuter.y, OUTER_OPACITY); |
| |
| if (!isCasterOpaque) { |
| umbraVertices[umbraIndex++] = vertexBufferIndex; |
| } |
| Vector3 currentInner = |
| (innerStart * startWeight + innerNext * k) / extraVerticesNumber; |
| indexBuffer[indexBufferIndex++] = vertexBufferIndex; |
| AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x, |
| currentInner.y, |
| getTransformedAlphaFromFactoredZ(currentInner.z * heightFactor)); |
| } |
| } |
| currentAlpha = nextAlpha; |
| } |
| |
| indexBuffer[indexBufferIndex++] = 1; |
| indexBuffer[indexBufferIndex++] = 0; |
| |
| if (!isCasterOpaque) { |
| // Add the centroid as the last one in the vertex buffer. |
| float centroidOpacity = |
| getTransformedAlphaFromFactoredZ(centroid3d.z * heightFactor); |
| int centroidIndex = vertexBufferIndex; |
| AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x, |
| centroid3d.y, centroidOpacity); |
| |
| for (int i = 0; i < umbraIndex; i++) { |
| // Note that umbraVertices[0] is always 0. |
| // So the start and the end of the umbra are using the "0". |
| // And penumbra ended with 0, so a degenerated triangle is formed b/t |
| // the umbra and penumbra. |
| indexBuffer[indexBufferIndex++] = umbraVertices[i]; |
| indexBuffer[indexBufferIndex++] = centroidIndex; |
| } |
| indexBuffer[indexBufferIndex++] = 0; |
| } |
| |
| // At the end, update the real index and vertex buffer size. |
| shadowVertexBuffer.updateVertexCount(vertexBufferIndex); |
| shadowVertexBuffer.updateIndexCount(indexBufferIndex); |
| shadowVertexBuffer.computeBounds<AlphaVertex>(); |
| |
| ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Ambient Vertex Buffer"); |
| ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Ambient Index Buffer"); |
| ShadowTessellator::checkOverflow(umbraIndex, totalUmbraCount, "Ambient Umbra Buffer"); |
| |
| #if DEBUG_SHADOW |
| for (int i = 0; i < vertexBufferIndex; i++) { |
| ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y, |
| shadowVertices[i].alpha); |
| } |
| for (int i = 0; i < indexBufferIndex; i++) { |
| ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]); |
| } |
| #endif |
| } |
| |
| }; // namespace uirenderer |
| }; // namespace android |