155bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui/* 255bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * Copyright (C) 2013 The Android Open Source Project 355bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * 455bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * Licensed under the Apache License, Version 2.0 (the "License"); 555bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * you may not use this file except in compliance with the License. 655bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * You may obtain a copy of the License at 755bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * 855bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * http://www.apache.org/licenses/LICENSE-2.0 955bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * 1055bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * Unless required by applicable law or agreed to in writing, software 1155bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * distributed under the License is distributed on an "AS IS" BASIS, 1255bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1355bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * See the License for the specific language governing permissions and 1455bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui * limitations under the License. 1555bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui */ 1655bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui 1755bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui#include <math.h> 1855bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui#include <utils/Log.h> 1987f9df880e4a5bfba65d2ed413b3ead649595129Chris Craik#include <utils/Trace.h> 201240752c44c21632adaf64f6768669ec6d0ebc8cLazar Trsic#include <utils/MathUtils.h> 2155bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui 2255bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui#include "AmbientShadow.h" 232507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik#include "Properties.h" 2455bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui#include "ShadowTessellator.h" 257b4516e7ea552ad08d6e7277d311ef11bd8f12e8ztenghui#include "SpotShadow.h" 262507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik#include "Vector.h" 2755bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui 2855bfb4e728fe1db619af5d2c287f4abe711b3343ztenghuinamespace android { 2955bfb4e728fe1db619af5d2c287f4abe711b3343ztenghuinamespace uirenderer { 3055bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui 3105f3d6e5111fd08df5cd9aae2c3d28399dc0e7f5Chris Craikvoid ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, 3250ecf849cb7ccc3482517b74d2214b347927791eztenghui const Vector3* casterPolygon, int casterVertexCount, 33af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui const Vector3& centroid3d, const Rect& casterBounds, 34af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) { 3587f9df880e4a5bfba65d2ed413b3ead649595129Chris Craik ATRACE_CALL(); 3687f9df880e4a5bfba65d2ed413b3ead649595129Chris Craik 3755bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui // A bunch of parameters to tweak the shadow. 3855bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui // TODO: Allow some of these changable by debug settings or APIs. 398def74de33b197c0c5ec8774576b1d71c7ec4f1bztenghui float heightFactor = 1.0f / 128; 407b4516e7ea552ad08d6e7277d311ef11bd8f12e8ztenghui const float geomFactor = 64; 4155bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui 422507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik if (CC_UNLIKELY(Properties::overrideAmbientRatio > 0.0f)) { 432507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik heightFactor *= Properties::overrideAmbientRatio; 44f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik } 45f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik 46af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui Rect ambientShadowBounds(casterBounds); 47af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui ambientShadowBounds.outset(maxZ * geomFactor * heightFactor); 48af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui 49af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui if (!localClip.intersects(ambientShadowBounds)) { 50af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui#if DEBUG_SHADOW 51af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui ALOGD("Ambient shadow is out of clip rect!"); 52af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui#endif 5305f3d6e5111fd08df5cd9aae2c3d28399dc0e7f5Chris Craik return; 54af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui } 55af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui 5605f3d6e5111fd08df5cd9aae2c3d28399dc0e7f5Chris Craik AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon, 5750ecf849cb7ccc3482517b74d2214b347927791eztenghui casterVertexCount, centroid3d, heightFactor, geomFactor, 5850ecf849cb7ccc3482517b74d2214b347927791eztenghui shadowVertexBuffer); 5955bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui} 6055bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui 6105f3d6e5111fd08df5cd9aae2c3d28399dc0e7f5Chris Craikvoid ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, 62c50a03d78aaedd0003377e98710e7038bda330e9ztenghui const Vector3* casterPolygon, int casterVertexCount, const Vector3& casterCentroid, 63797b95b26bbb7557678af78b9a2a61830158920fChris Craik const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius, 64797b95b26bbb7557678af78b9a2a61830158920fChris Craik const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer) { 6587f9df880e4a5bfba65d2ed413b3ead649595129Chris Craik ATRACE_CALL(); 6687f9df880e4a5bfba65d2ed413b3ead649595129Chris Craik 67797b95b26bbb7557678af78b9a2a61830158920fChris Craik Vector3 adjustedLightCenter(lightCenter); 682507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) { 692507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik adjustedLightCenter.y = - Properties::overrideLightPosY; // negated since this shifts up 70f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik } 712507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) { 722507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik adjustedLightCenter.z = Properties::overrideLightPosZ; 73f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik } 74f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik 757b4516e7ea552ad08d6e7277d311ef11bd8f12e8ztenghui#if DEBUG_SHADOW 768d0ec389531d071529fb0a800f10733b057205d9Teng-Hui Zhu ALOGD("light center %f %f %f %d", 778d0ec389531d071529fb0a800f10733b057205d9Teng-Hui Zhu adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius); 787b4516e7ea552ad08d6e7277d311ef11bd8f12e8ztenghui#endif 79cf22d184a37d4dd551b045857e5725601f89236cTeng-Hui Zhu if (isnan(adjustedLightCenter.x) 80cf22d184a37d4dd551b045857e5725601f89236cTeng-Hui Zhu || isnan(adjustedLightCenter.y) 81cf22d184a37d4dd551b045857e5725601f89236cTeng-Hui Zhu || isnan(adjustedLightCenter.z)) { 82cf22d184a37d4dd551b045857e5725601f89236cTeng-Hui Zhu return; 83cf22d184a37d4dd551b045857e5725601f89236cTeng-Hui Zhu } 843197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik 853197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik // light position (because it's in local space) needs to compensate for receiver transform 863197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik // TODO: should apply to light orientation, not just position 873197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik Matrix4 reverseReceiverTransform; 883197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik reverseReceiverTransform.loadInverse(receiverTransform); 89797b95b26bbb7557678af78b9a2a61830158920fChris Craik reverseReceiverTransform.mapPoint3d(adjustedLightCenter); 903197cded4e265bc99dc82d695bbb7163fe134ed4Chris Craik 912507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) { 922507c34d91bb0d722b6012e85cb47387b2aa6873Chris Craik lightRadius = Properties::overrideLightRadius; 93f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik } 94f5be3ca5cc5b3a10747b577f60059a99862bb9a8Chris Craik 95af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui // Now light and caster are both in local space, we will check whether 96af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui // the shadow is within the clip area. 97797b95b26bbb7557678af78b9a2a61830158920fChris Craik Rect lightRect = Rect(adjustedLightCenter.x - lightRadius, adjustedLightCenter.y - lightRadius, 98797b95b26bbb7557678af78b9a2a61830158920fChris Craik adjustedLightCenter.x + lightRadius, adjustedLightCenter.y + lightRadius); 99af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui lightRect.unionWith(localClip); 100af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui if (!lightRect.intersects(casterBounds)) { 101af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui#if DEBUG_SHADOW 102af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui ALOGD("Spot shadow is out of clip rect!"); 103af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui#endif 10405f3d6e5111fd08df5cd9aae2c3d28399dc0e7f5Chris Craik return; 105af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui } 106af6f7ed8dd4288a41d0a07a1f0f0be7d6d035b33ztenghui 107c50a03d78aaedd0003377e98710e7038bda330e9ztenghui SpotShadow::createSpotShadow(isCasterOpaque, adjustedLightCenter, lightRadius, 108c50a03d78aaedd0003377e98710e7038bda330e9ztenghui casterPolygon, casterVertexCount, casterCentroid, shadowVertexBuffer); 109c50a03d78aaedd0003377e98710e7038bda330e9ztenghui 11050ecf849cb7ccc3482517b74d2214b347927791eztenghui#if DEBUG_SHADOW 11150ecf849cb7ccc3482517b74d2214b347927791eztenghui if(shadowVertexBuffer.getVertexCount() <= 0) { 11250ecf849cb7ccc3482517b74d2214b347927791eztenghui ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount()); 11350ecf849cb7ccc3482517b74d2214b347927791eztenghui } 11450ecf849cb7ccc3482517b74d2214b347927791eztenghui#endif 1157b4516e7ea552ad08d6e7277d311ef11bd8f12e8ztenghui} 11663d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui 11763d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui/** 11863d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui * Calculate the centroid of a 2d polygon. 11963d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui * 12063d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui * @param poly The polygon, which is represented in a Vector2 array. 12163d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui * @param polyLength The length of the polygon in terms of number of vertices. 12263d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui * @return the centroid of the polygon. 12363d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui */ 12463d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghuiVector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) { 12563d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui double sumx = 0; 12663d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui double sumy = 0; 12763d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui int p1 = polyLength - 1; 12863d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui double area = 0; 12963d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui for (int p2 = 0; p2 < polyLength; p2++) { 13063d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui double x1 = poly[p1].x; 13163d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui double y1 = poly[p1].y; 13263d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui double x2 = poly[p2].x; 13363d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui double y2 = poly[p2].y; 13463d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui double a = (x1 * y2 - x2 * y1); 13563d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui sumx += (x1 + x2) * a; 13663d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui sumy += (y1 + y2) * a; 13763d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui area += a; 13863d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui p1 = p2; 13963d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui } 14063d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui 14163d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui Vector2 centroid = poly[0]; 14263d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui if (area != 0) { 1431aa5d2d7068147ff781cfe911a93f01593a68c79John Reck centroid = (Vector2){static_cast<float>(sumx / (3 * area)), 1441aa5d2d7068147ff781cfe911a93f01593a68c79John Reck static_cast<float>(sumy / (3 * area))}; 14563d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui } else { 14650ecf849cb7ccc3482517b74d2214b347927791eztenghui ALOGW("Area is 0 while computing centroid!"); 14763d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui } 14863d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui return centroid; 14963d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui} 15063d41abb40b3ce40d8b9bccb1cf186e8158a3687ztenghui 151c50a03d78aaedd0003377e98710e7038bda330e9ztenghui// Make sure p1 -> p2 is going CW around the poly. 152c50a03d78aaedd0003377e98710e7038bda330e9ztenghuiVector2 ShadowTessellator::calculateNormal(const Vector2& p1, const Vector2& p2) { 153c50a03d78aaedd0003377e98710e7038bda330e9ztenghui Vector2 result = p2 - p1; 154c50a03d78aaedd0003377e98710e7038bda330e9ztenghui if (result.x != 0 || result.y != 0) { 155c50a03d78aaedd0003377e98710e7038bda330e9ztenghui result.normalize(); 156c50a03d78aaedd0003377e98710e7038bda330e9ztenghui // Calculate the normal , which is CCW 90 rotate to the delta. 157c50a03d78aaedd0003377e98710e7038bda330e9ztenghui float tempy = result.y; 158c50a03d78aaedd0003377e98710e7038bda330e9ztenghui result.y = result.x; 159c50a03d78aaedd0003377e98710e7038bda330e9ztenghui result.x = -tempy; 160c50a03d78aaedd0003377e98710e7038bda330e9ztenghui } 161c50a03d78aaedd0003377e98710e7038bda330e9ztenghui return result; 162c50a03d78aaedd0003377e98710e7038bda330e9ztenghui} 1632e023f3827dfc0dfc1ed7c3dd54d02b4a993f0b4ztenghui 164512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghuiint ShadowTessellator::getExtraVertexNumber(const Vector2& vector1, 165512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui const Vector2& vector2, float divisor) { 166512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui // When there is no distance difference, there is no need for extra vertices. 167512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui if (vector1.lengthSquared() == 0 || vector2.lengthSquared() == 0) { 168512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui return 0; 169512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui } 170512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui // The formula is : 171512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui // extraNumber = floor(acos(dot(n1, n2)) / (M_PI / EXTRA_VERTEX_PER_PI)) 172512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui // The value ranges for each step are: 173512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui // dot( ) --- [-1, 1] 174512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui // acos( ) --- [0, M_PI] 175512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui // floor(...) --- [0, EXTRA_VERTEX_PER_PI] 176512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui float dotProduct = vector1.dot(vector2); 1771240752c44c21632adaf64f6768669ec6d0ebc8cLazar Trsic // make sure that dotProduct value is in acsof input range [-1, 1] 1781240752c44c21632adaf64f6768669ec6d0ebc8cLazar Trsic dotProduct = MathUtils::clamp(dotProduct, -1.0f, 1.0f); 179512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui // TODO: Use look up table for the dotProduct to extraVerticesNumber 180512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui // computation, if needed. 181512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui float angle = acosf(dotProduct); 182512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui return (int) floor(angle / divisor); 183512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui} 184512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui 185512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghuivoid ShadowTessellator::checkOverflow(int used, int total, const char* bufferName) { 186512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d", 187512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui bufferName, used, total); 188512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui} 189512e643ce83b1d48ad9630a3622276f795cf4fb2ztenghui 19055bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui}; // namespace uirenderer 19155bfb4e728fe1db619af5d2c287f4abe711b3343ztenghui}; // namespace android 192