00001 /* 00002 ----------------------------------------------------------------------------- 00003 This source file is part of OGRE 00004 (Object-oriented Graphics Rendering Engine) 00005 For the latest info, see http://www.ogre3d.org/ 00006 00007 Copyright © 2000-2004 The OGRE Team 00008 Also see acknowledgements in Readme.html 00009 00010 This program is free software; you can redistribute it and/or modify it under 00011 the terms of the GNU Lesser General Public License as published by the Free Software 00012 Foundation; either version 2 of the License, or (at your option) any later 00013 version. 00014 00015 This program is distributed in the hope that it will be useful, but WITHOUT 00016 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 00017 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 00018 00019 You should have received a copy of the GNU Lesser General Public License along with 00020 this program; if not, write to the Free Software Foundation, Inc., 59 Temple 00021 Place - Suite 330, Boston, MA 02111-1307, USA, or go to 00022 http://www.gnu.org/copyleft/lesser.txt. 00023 ----------------------------------------------------------------------------- 00024 */ 00025 #include "OgreStableHeaders.h" 00026 #include "OgreLight.h" 00027 #include "OgreEdgeListBuilder.h" 00028 00029 namespace Ogre { 00030 // ------------------------------------------------------------------------ 00031 void ShadowCaster::updateEdgeListLightFacing(EdgeData* edgeData, 00032 const Vector4& lightPos) 00033 { 00034 edgeData->updateTriangleLightFacing(lightPos); 00035 } 00036 // ------------------------------------------------------------------------ 00037 void ShadowCaster::generateShadowVolume(EdgeData* edgeData, 00038 HardwareIndexBufferSharedPtr indexBuffer, const Light* light, 00039 ShadowRenderableList& shadowRenderables, unsigned long flags) 00040 { 00041 // Edge groups should be 1:1 with shadow renderables 00042 assert(edgeData->edgeGroups.size() == shadowRenderables.size()); 00043 00044 EdgeData::EdgeGroupList::iterator egi, egiend; 00045 ShadowRenderableList::iterator si; 00046 00047 Light::LightTypes lightType = light->getType(); 00048 00049 // Lock index buffer for writing 00050 unsigned short* pIdx = static_cast<unsigned short*>( 00051 indexBuffer->lock(HardwareBuffer::HBL_DISCARD)); 00052 size_t indexStart = 0; 00053 00054 // Iterate over the groups and form renderables for each based on their 00055 // lightFacing 00056 si = shadowRenderables.begin(); 00057 egiend = edgeData->edgeGroups.end(); 00058 for (egi = edgeData->edgeGroups.begin(); egi != egiend; ++egi, ++si) 00059 { 00060 EdgeData::EdgeGroup& eg = *egi; 00061 RenderOperation* lightShadOp = 0; 00062 // Initialise the index bounds for this shadow renderable 00063 RenderOperation* shadOp = (*si)->getRenderOperationForUpdate(); 00064 shadOp->indexData->indexCount = 0; 00065 shadOp->indexData->indexStart = indexStart; 00066 // original number of verts (without extruded copy) 00067 size_t originalVertexCount = eg.vertexData->vertexCount; 00068 bool firstDarkCapTri = true; 00069 unsigned short darkCapStart; 00070 00071 EdgeData::EdgeList::iterator i, iend; 00072 iend = eg.edges.end(); 00073 for (i = eg.edges.begin(); i != iend; ++i) 00074 { 00075 EdgeData::Edge& edge = *i; 00076 00077 EdgeData::Triangle &t1 = edgeData->triangles[edge.triIndex[0]]; 00078 EdgeData::Triangle &t2 = 00079 edge.degenerate? edgeData->triangles[edge.triIndex[0]] : edgeData->triangles[edge.triIndex[1]]; 00080 if (t1.lightFacing && (edge.degenerate || !t2.lightFacing)) 00081 { 00082 /* Silhouette edge, first tri facing the light 00083 Also covers degenerate tris where only tri 1 is valid 00084 Remember verts run anticlockwise along the edge from 00085 tri 0 so to point shadow volume tris outward, light cap 00086 indexes have to be backwards 00087 00088 We emit 2 tris if light is a point light, 1 if light 00089 is directional, because directional lights cause all 00090 points to converge to a single point at infinity. 00091 00092 First side tri = near1, near0, far0 00093 Second tri = far0, far1, near1 00094 00095 'far' indexes are 'near' index + originalVertexCount 00096 because 'far' verts are in the second half of the 00097 buffer 00098 */ 00099 *pIdx++ = edge.vertIndex[1]; 00100 *pIdx++ = edge.vertIndex[0]; 00101 *pIdx++ = edge.vertIndex[0] + originalVertexCount; 00102 shadOp->indexData->indexCount += 3; 00103 00104 // Are we extruding to infinity? 00105 if (!(lightType == Light::LT_DIRECTIONAL && 00106 flags & SRF_EXTRUDE_TO_INFINITY)) 00107 { 00108 // additional tri to make quad 00109 *pIdx++ = edge.vertIndex[0] + originalVertexCount; 00110 *pIdx++ = edge.vertIndex[1] + originalVertexCount; 00111 *pIdx++ = edge.vertIndex[1]; 00112 shadOp->indexData->indexCount += 3; 00113 } 00114 // Do dark cap tri 00115 // Use McGuire et al method, a triangle fan covering all silhouette 00116 // edges and one point (taken from the initial tri) 00117 if (flags & SRF_INCLUDE_DARK_CAP) 00118 { 00119 if (firstDarkCapTri) 00120 { 00121 darkCapStart = edge.vertIndex[0] + originalVertexCount; 00122 firstDarkCapTri = false; 00123 } 00124 else 00125 { 00126 *pIdx++ = darkCapStart; 00127 *pIdx++ = edge.vertIndex[1] + originalVertexCount; 00128 *pIdx++ = edge.vertIndex[0] + originalVertexCount; 00129 shadOp->indexData->indexCount += 3; 00130 } 00131 00132 } 00133 } 00134 else if (!t1.lightFacing && (edge.degenerate || t2.lightFacing)) 00135 { 00136 // Silhouette edge, second tri facing the light 00137 // Note edge indexes inverse of when t1 is light facing 00138 *pIdx++ = edge.vertIndex[0]; 00139 *pIdx++ = edge.vertIndex[1]; 00140 *pIdx++ = edge.vertIndex[1] + originalVertexCount; 00141 shadOp->indexData->indexCount += 3; 00142 00143 // Are we extruding to infinity? 00144 if (!(lightType == Light::LT_DIRECTIONAL && 00145 flags & SRF_EXTRUDE_TO_INFINITY)) 00146 { 00147 // additional tri to make quad 00148 *pIdx++ = edge.vertIndex[1] + originalVertexCount; 00149 *pIdx++ = edge.vertIndex[0] + originalVertexCount; 00150 *pIdx++ = edge.vertIndex[0]; 00151 shadOp->indexData->indexCount += 3; 00152 } 00153 // Do dark cap tri 00154 // Use McGuire et al method, a triangle fan covering all silhouette 00155 // edges and one point (taken from the initial tri) 00156 if (flags & SRF_INCLUDE_DARK_CAP) 00157 { 00158 if (firstDarkCapTri) 00159 { 00160 darkCapStart = edge.vertIndex[1] + originalVertexCount; 00161 firstDarkCapTri = false; 00162 } 00163 else 00164 { 00165 *pIdx++ = darkCapStart; 00166 *pIdx++ = edge.vertIndex[0] + originalVertexCount; 00167 *pIdx++ = edge.vertIndex[1] + originalVertexCount; 00168 shadOp->indexData->indexCount += 3; 00169 } 00170 00171 } 00172 } 00173 00174 } 00175 00176 // Do light cap 00177 if (flags & SRF_INCLUDE_LIGHT_CAP) 00178 { 00179 ShadowRenderable* lightCapRend = 0; 00180 00181 if ((*si)->isLightCapSeparate()) 00182 { 00183 // separate light cap 00184 lightCapRend = (*si)->getLightCapRenderable(); 00185 lightShadOp = lightCapRend->getRenderOperationForUpdate(); 00186 lightShadOp->indexData->indexCount = 0; 00187 // start indexes after the current total 00188 // NB we don't update the total here since that's done below 00189 lightShadOp->indexData->indexStart = 00190 indexStart + shadOp->indexData->indexCount; 00191 } 00192 00193 EdgeData::TriangleList::iterator ti, tiend; 00194 tiend = edgeData->triangles.end(); 00195 for (ti = edgeData->triangles.begin(); ti != tiend; ++ti) 00196 { 00197 EdgeData::Triangle& t = *ti; 00198 // Light facing, and vertex set matches 00199 if (t.lightFacing && t.vertexSet == eg.vertexSet) 00200 { 00201 *pIdx++ = t.vertIndex[0]; 00202 *pIdx++ = t.vertIndex[1]; 00203 *pIdx++ = t.vertIndex[2]; 00204 if (lightShadOp) 00205 { 00206 lightShadOp->indexData->indexCount += 3; 00207 } 00208 else 00209 { 00210 shadOp->indexData->indexCount += 3; 00211 } 00212 } 00213 } 00214 00215 } 00216 // update next indexStart (all renderables sharing the buffer) 00217 indexStart += shadOp->indexData->indexCount; 00218 // Add on the light cap too 00219 if (lightShadOp) 00220 indexStart += lightShadOp->indexData->indexCount; 00221 00222 00223 } 00224 00225 00226 // Unlock index buffer 00227 indexBuffer->unlock(); 00228 00229 // In debug mode, check we didn't overrun the index buffer 00230 assert(indexStart <= indexBuffer->getNumIndexes() && 00231 "Index buffer overrun while generating shadow volume!! " 00232 "You must increase the size of the shadow index buffer."); 00233 00234 } 00235 // ------------------------------------------------------------------------ 00236 void ShadowCaster::extrudeVertices( 00237 HardwareVertexBufferSharedPtr vertexBuffer, 00238 size_t originalVertexCount, const Vector4& light, Real extrudeDist) 00239 { 00240 assert (vertexBuffer->getVertexSize() == sizeof(Real) * 3 00241 && "Position buffer should contain only positions!"); 00242 00243 // Extrude the first area of the buffer into the second area 00244 // Lock the entire buffer for writing, even though we'll only be 00245 // updating the latter because you can't have 2 locks on the same 00246 // buffer 00247 Real* pSrc = static_cast<Real*>( 00248 vertexBuffer->lock(HardwareBuffer::HBL_NORMAL)); 00249 00250 Real* pDest = pSrc + originalVertexCount * 3; 00251 // Assume directional light, extrusion is along light direction 00252 Vector3 extrusionDir(-light.x, -light.y, -light.z); 00253 extrusionDir.normalise(); 00254 extrusionDir *= extrudeDist; 00255 for (size_t vert = 0; vert < originalVertexCount; ++vert) 00256 { 00257 if (light.w != 0.0f) 00258 { 00259 // Point light, adjust extrusionDir 00260 extrusionDir.x = pSrc[0] - light.x; 00261 extrusionDir.y = pSrc[1] - light.y; 00262 extrusionDir.z = pSrc[2] - light.z; 00263 extrusionDir.normalise(); 00264 extrusionDir *= extrudeDist; 00265 } 00266 *pDest++ = *pSrc++ + extrusionDir.x; 00267 *pDest++ = *pSrc++ + extrusionDir.y; 00268 *pDest++ = *pSrc++ + extrusionDir.z; 00269 00270 } 00271 vertexBuffer->unlock(); 00272 00273 } 00274 // ------------------------------------------------------------------------ 00275 void ShadowCaster::extrudeBounds(AxisAlignedBox& box, const Vector4& light, Real extrudeDist) const 00276 { 00277 Vector3 extrusionDir; 00278 00279 if (light.w == 0) 00280 { 00281 // Parallel projection guarantees min/max relationship remains the same 00282 extrusionDir.x = -light.x; 00283 extrusionDir.y = -light.y; 00284 extrusionDir.z = -light.z; 00285 extrusionDir.normalise(); 00286 extrusionDir *= extrudeDist; 00287 box.setExtents(box.getMinimum() + extrusionDir, 00288 box.getMaximum() + extrusionDir); 00289 } 00290 else 00291 { 00292 const Vector3* corners = box.getAllCorners(); 00293 Vector3 vmin, vmax; 00294 00295 for (unsigned short i = 0; i < 8; ++i) 00296 { 00297 extrusionDir.x = corners[i].x - light.x; 00298 extrusionDir.y = corners[i].y - light.y; 00299 extrusionDir.z = corners[i].z - light.z; 00300 extrusionDir.normalise(); 00301 extrusionDir *= extrudeDist; 00302 Vector3 res = corners[i] + extrusionDir; 00303 if (i == 0) 00304 { 00305 vmin = res; 00306 vmax = res; 00307 } 00308 else 00309 { 00310 vmin.makeFloor(res); 00311 vmax.makeCeil(res); 00312 } 00313 } 00314 00315 box.setExtents(vmin, vmax); 00316 00317 } 00318 00319 } 00320 // ------------------------------------------------------------------------ 00321 Real ShadowCaster::getExtrusionDistance(const Vector3& objectPos, const Light* light) const 00322 { 00323 Vector3 diff = objectPos - light->getDerivedPosition(); 00324 return light->getAttenuationRange() - diff.length(); 00325 } 00326 00327 }
Copyright © 2002-2003 by The OGRE Team
Last modified Sun Nov 28 19:48:46 2004