[657] | 1 | /*
|
---|
| 2 | -----------------------------------------------------------------------------
|
---|
| 3 | This source file is part of OGRE
|
---|
| 4 | (Object-oriented Graphics Rendering Engine)
|
---|
| 5 | For the latest info, see http://www.ogre3d.org/
|
---|
| 6 |
|
---|
| 7 | Copyright (c) 2000-2005 The OGRE Team
|
---|
| 8 | Also see acknowledgements in Readme.html
|
---|
| 9 |
|
---|
| 10 | This program is free software; you can redistribute it and/or modify it under
|
---|
| 11 | the terms of the GNU Lesser General Public License as published by the Free Software
|
---|
| 12 | Foundation; either version 2 of the License, or (at your option) any later
|
---|
| 13 | version.
|
---|
| 14 |
|
---|
| 15 | This program is distributed in the hope that it will be useful, but WITHOUT
|
---|
| 16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
---|
| 17 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
---|
| 18 |
|
---|
| 19 | You should have received a copy of the GNU Lesser General Public License along with
|
---|
| 20 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
---|
| 21 | Place - Suite 330, Boston, MA 02111-1307, USA, or go to
|
---|
| 22 | http://www.gnu.org/copyleft/lesser.txt.
|
---|
| 23 | -----------------------------------------------------------------------------
|
---|
| 24 | */
|
---|
| 25 | #include "OgreStableHeaders.h"
|
---|
| 26 | #include "OgreLight.h"
|
---|
| 27 | #include "OgreEdgeListBuilder.h"
|
---|
| 28 |
|
---|
| 29 | namespace Ogre {
|
---|
| 30 | const LightList& ShadowRenderable::getLights(void) const
|
---|
| 31 | {
|
---|
| 32 | // return empty
|
---|
| 33 | static LightList ll;
|
---|
| 34 | return ll;
|
---|
| 35 | }
|
---|
| 36 | // ------------------------------------------------------------------------
|
---|
| 37 | void ShadowCaster::updateEdgeListLightFacing(EdgeData* edgeData,
|
---|
| 38 | const Vector4& lightPos)
|
---|
| 39 | {
|
---|
| 40 | edgeData->updateTriangleLightFacing(lightPos);
|
---|
| 41 | }
|
---|
| 42 | // ------------------------------------------------------------------------
|
---|
| 43 | void ShadowCaster::generateShadowVolume(EdgeData* edgeData,
|
---|
| 44 | HardwareIndexBufferSharedPtr indexBuffer, const Light* light,
|
---|
| 45 | ShadowRenderableList& shadowRenderables, unsigned long flags)
|
---|
| 46 | {
|
---|
| 47 | // Edge groups should be 1:1 with shadow renderables
|
---|
| 48 | assert(edgeData->edgeGroups.size() == shadowRenderables.size());
|
---|
| 49 |
|
---|
| 50 | EdgeData::EdgeGroupList::iterator egi, egiend;
|
---|
| 51 | ShadowRenderableList::iterator si;
|
---|
| 52 |
|
---|
| 53 | Light::LightTypes lightType = light->getType();
|
---|
| 54 |
|
---|
| 55 | // Lock index buffer for writing
|
---|
| 56 | unsigned short* pIdx = static_cast<unsigned short*>(
|
---|
| 57 | indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
|
---|
| 58 | size_t indexStart = 0;
|
---|
| 59 |
|
---|
| 60 | // Iterate over the groups and form renderables for each based on their
|
---|
| 61 | // lightFacing
|
---|
| 62 | si = shadowRenderables.begin();
|
---|
| 63 | egiend = edgeData->edgeGroups.end();
|
---|
| 64 | for (egi = edgeData->edgeGroups.begin(); egi != egiend; ++egi, ++si)
|
---|
| 65 | {
|
---|
| 66 | EdgeData::EdgeGroup& eg = *egi;
|
---|
| 67 | RenderOperation* lightShadOp = 0;
|
---|
| 68 | // Initialise the index bounds for this shadow renderable
|
---|
| 69 | RenderOperation* shadOp = (*si)->getRenderOperationForUpdate();
|
---|
| 70 | shadOp->indexData->indexCount = 0;
|
---|
| 71 | shadOp->indexData->indexStart = indexStart;
|
---|
| 72 | // original number of verts (without extruded copy)
|
---|
| 73 | size_t originalVertexCount = eg.vertexData->vertexCount;
|
---|
| 74 | bool firstDarkCapTri = true;
|
---|
| 75 | unsigned short darkCapStart;
|
---|
| 76 |
|
---|
| 77 | EdgeData::EdgeList::iterator i, iend;
|
---|
| 78 | iend = eg.edges.end();
|
---|
| 79 | for (i = eg.edges.begin(); i != iend; ++i)
|
---|
| 80 | {
|
---|
| 81 | EdgeData::Edge& edge = *i;
|
---|
| 82 |
|
---|
| 83 | // Silhouette edge, when two tris has opposite light facing, or
|
---|
| 84 | // degenerate edge where only tri 1 is valid and the tri light facing
|
---|
| 85 | EdgeData::Triangle &t1 = edgeData->triangles[edge.triIndex[0]];
|
---|
| 86 | if ((edge.degenerate && t1.lightFacing) ||
|
---|
| 87 | (!edge.degenerate && (t1.lightFacing ^ edgeData->triangles[edge.triIndex[1]].lightFacing)))
|
---|
| 88 | {
|
---|
| 89 | size_t v0 = edge.vertIndex[0];
|
---|
| 90 | size_t v1 = edge.vertIndex[1];
|
---|
| 91 | if (!t1.lightFacing)
|
---|
| 92 | {
|
---|
| 93 | // Inverse edge indexes when t1 is light away
|
---|
| 94 | std::swap(v0, v1);
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | /* Note edge(v0, v1) run anticlockwise along the edge from
|
---|
| 98 | the light facing tri so to point shadow volume tris outward,
|
---|
| 99 | light cap indexes have to be backwards
|
---|
| 100 |
|
---|
| 101 | We emit 2 tris if light is a point light, 1 if light
|
---|
| 102 | is directional, because directional lights cause all
|
---|
| 103 | points to converge to a single point at infinity.
|
---|
| 104 |
|
---|
| 105 | First side tri = near1, near0, far0
|
---|
| 106 | Second tri = far0, far1, near1
|
---|
| 107 |
|
---|
| 108 | 'far' indexes are 'near' index + originalVertexCount
|
---|
| 109 | because 'far' verts are in the second half of the
|
---|
| 110 | buffer
|
---|
| 111 | */
|
---|
| 112 | *pIdx++ = v1;
|
---|
| 113 | *pIdx++ = v0;
|
---|
| 114 | *pIdx++ = v0 + originalVertexCount;
|
---|
| 115 | shadOp->indexData->indexCount += 3;
|
---|
| 116 |
|
---|
| 117 | // Are we extruding to infinity?
|
---|
| 118 | if (!(lightType == Light::LT_DIRECTIONAL &&
|
---|
| 119 | flags & SRF_EXTRUDE_TO_INFINITY))
|
---|
| 120 | {
|
---|
| 121 | // additional tri to make quad
|
---|
| 122 | *pIdx++ = v0 + originalVertexCount;
|
---|
| 123 | *pIdx++ = v1 + originalVertexCount;
|
---|
| 124 | *pIdx++ = v1;
|
---|
| 125 | shadOp->indexData->indexCount += 3;
|
---|
| 126 | }
|
---|
| 127 | // Do dark cap tri
|
---|
| 128 | // Use McGuire et al method, a triangle fan covering all silhouette
|
---|
| 129 | // edges and one point (taken from the initial tri)
|
---|
| 130 | if (flags & SRF_INCLUDE_DARK_CAP)
|
---|
| 131 | {
|
---|
| 132 | if (firstDarkCapTri)
|
---|
| 133 | {
|
---|
| 134 | darkCapStart = v0 + originalVertexCount;
|
---|
| 135 | firstDarkCapTri = false;
|
---|
| 136 | }
|
---|
| 137 | else
|
---|
| 138 | {
|
---|
| 139 | *pIdx++ = darkCapStart;
|
---|
| 140 | *pIdx++ = v1 + originalVertexCount;
|
---|
| 141 | *pIdx++ = v0 + originalVertexCount;
|
---|
| 142 | shadOp->indexData->indexCount += 3;
|
---|
| 143 | }
|
---|
| 144 |
|
---|
| 145 | }
|
---|
| 146 | }
|
---|
| 147 |
|
---|
| 148 | }
|
---|
| 149 |
|
---|
| 150 | // Do light cap
|
---|
| 151 | if (flags & SRF_INCLUDE_LIGHT_CAP)
|
---|
| 152 | {
|
---|
| 153 | ShadowRenderable* lightCapRend = 0;
|
---|
| 154 |
|
---|
| 155 | if ((*si)->isLightCapSeparate())
|
---|
| 156 | {
|
---|
| 157 | // separate light cap
|
---|
| 158 | lightCapRend = (*si)->getLightCapRenderable();
|
---|
| 159 | lightShadOp = lightCapRend->getRenderOperationForUpdate();
|
---|
| 160 | lightShadOp->indexData->indexCount = 0;
|
---|
| 161 | // start indexes after the current total
|
---|
| 162 | // NB we don't update the total here since that's done below
|
---|
| 163 | lightShadOp->indexData->indexStart =
|
---|
| 164 | indexStart + shadOp->indexData->indexCount;
|
---|
| 165 | }
|
---|
| 166 |
|
---|
| 167 | EdgeData::TriangleList::iterator ti, tiend;
|
---|
| 168 | tiend = edgeData->triangles.end();
|
---|
| 169 | for (ti = edgeData->triangles.begin(); ti != tiend; ++ti)
|
---|
| 170 | {
|
---|
| 171 | EdgeData::Triangle& t = *ti;
|
---|
| 172 | // Light facing, and vertex set matches
|
---|
| 173 | if (t.lightFacing && t.vertexSet == eg.vertexSet)
|
---|
| 174 | {
|
---|
| 175 | *pIdx++ = t.vertIndex[0];
|
---|
| 176 | *pIdx++ = t.vertIndex[1];
|
---|
| 177 | *pIdx++ = t.vertIndex[2];
|
---|
| 178 | if (lightShadOp)
|
---|
| 179 | {
|
---|
| 180 | lightShadOp->indexData->indexCount += 3;
|
---|
| 181 | }
|
---|
| 182 | else
|
---|
| 183 | {
|
---|
| 184 | shadOp->indexData->indexCount += 3;
|
---|
| 185 | }
|
---|
| 186 | }
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | }
|
---|
| 190 | // update next indexStart (all renderables sharing the buffer)
|
---|
| 191 | indexStart += shadOp->indexData->indexCount;
|
---|
| 192 | // Add on the light cap too
|
---|
| 193 | if (lightShadOp)
|
---|
| 194 | indexStart += lightShadOp->indexData->indexCount;
|
---|
| 195 |
|
---|
| 196 |
|
---|
| 197 | }
|
---|
| 198 |
|
---|
| 199 |
|
---|
| 200 | // Unlock index buffer
|
---|
| 201 | indexBuffer->unlock();
|
---|
| 202 |
|
---|
| 203 | // In debug mode, check we didn't overrun the index buffer
|
---|
| 204 | assert(indexStart <= indexBuffer->getNumIndexes() &&
|
---|
| 205 | "Index buffer overrun while generating shadow volume!! "
|
---|
| 206 | "You must increase the size of the shadow index buffer.");
|
---|
| 207 |
|
---|
| 208 | }
|
---|
| 209 | // ------------------------------------------------------------------------
|
---|
| 210 | void ShadowCaster::extrudeVertices(
|
---|
| 211 | HardwareVertexBufferSharedPtr vertexBuffer,
|
---|
| 212 | size_t originalVertexCount, const Vector4& light, Real extrudeDist)
|
---|
| 213 | {
|
---|
| 214 | assert (vertexBuffer->getVertexSize() == sizeof(float) * 3
|
---|
| 215 | && "Position buffer should contain only positions!");
|
---|
| 216 |
|
---|
| 217 | // Extrude the first area of the buffer into the second area
|
---|
| 218 | // Lock the entire buffer for writing, even though we'll only be
|
---|
| 219 | // updating the latter because you can't have 2 locks on the same
|
---|
| 220 | // buffer
|
---|
| 221 | float* pSrc = static_cast<float*>(
|
---|
| 222 | vertexBuffer->lock(HardwareBuffer::HBL_NORMAL));
|
---|
| 223 |
|
---|
| 224 | float* pDest = pSrc + originalVertexCount * 3;
|
---|
| 225 | // Assume directional light, extrusion is along light direction
|
---|
| 226 | Vector3 extrusionDir(-light.x, -light.y, -light.z);
|
---|
| 227 | extrusionDir.normalise();
|
---|
| 228 | extrusionDir *= extrudeDist;
|
---|
| 229 | for (size_t vert = 0; vert < originalVertexCount; ++vert)
|
---|
| 230 | {
|
---|
| 231 | if (light.w != 0.0f)
|
---|
| 232 | {
|
---|
| 233 | // Point light, adjust extrusionDir
|
---|
| 234 | extrusionDir.x = pSrc[0] - light.x;
|
---|
| 235 | extrusionDir.y = pSrc[1] - light.y;
|
---|
| 236 | extrusionDir.z = pSrc[2] - light.z;
|
---|
| 237 | extrusionDir.normalise();
|
---|
| 238 | extrusionDir *= extrudeDist;
|
---|
| 239 | }
|
---|
| 240 | *pDest++ = *pSrc++ + extrusionDir.x;
|
---|
| 241 | *pDest++ = *pSrc++ + extrusionDir.y;
|
---|
| 242 | *pDest++ = *pSrc++ + extrusionDir.z;
|
---|
| 243 |
|
---|
| 244 | }
|
---|
| 245 | vertexBuffer->unlock();
|
---|
| 246 |
|
---|
| 247 | }
|
---|
| 248 | // ------------------------------------------------------------------------
|
---|
| 249 | void ShadowCaster::extrudeBounds(AxisAlignedBox& box, const Vector4& light, Real extrudeDist) const
|
---|
| 250 | {
|
---|
| 251 | Vector3 extrusionDir;
|
---|
| 252 |
|
---|
| 253 | if (light.w == 0)
|
---|
| 254 | {
|
---|
| 255 | // Parallel projection guarantees min/max relationship remains the same
|
---|
| 256 | extrusionDir.x = -light.x;
|
---|
| 257 | extrusionDir.y = -light.y;
|
---|
| 258 | extrusionDir.z = -light.z;
|
---|
| 259 | extrusionDir.normalise();
|
---|
| 260 | extrusionDir *= extrudeDist;
|
---|
| 261 | box.setExtents(box.getMinimum() + extrusionDir,
|
---|
| 262 | box.getMaximum() + extrusionDir);
|
---|
| 263 | }
|
---|
| 264 | else
|
---|
| 265 | {
|
---|
| 266 | const Vector3* corners = box.getAllCorners();
|
---|
| 267 | Vector3 vmin, vmax;
|
---|
| 268 |
|
---|
| 269 | for (unsigned short i = 0; i < 8; ++i)
|
---|
| 270 | {
|
---|
| 271 | extrusionDir.x = corners[i].x - light.x;
|
---|
| 272 | extrusionDir.y = corners[i].y - light.y;
|
---|
| 273 | extrusionDir.z = corners[i].z - light.z;
|
---|
| 274 | extrusionDir.normalise();
|
---|
| 275 | extrusionDir *= extrudeDist;
|
---|
| 276 | Vector3 res = corners[i] + extrusionDir;
|
---|
| 277 | if (i == 0)
|
---|
| 278 | {
|
---|
| 279 | vmin = res;
|
---|
| 280 | vmax = res;
|
---|
| 281 | }
|
---|
| 282 | else
|
---|
| 283 | {
|
---|
| 284 | vmin.makeFloor(res);
|
---|
| 285 | vmax.makeCeil(res);
|
---|
| 286 | }
|
---|
| 287 | }
|
---|
| 288 |
|
---|
| 289 | box.setExtents(vmin, vmax);
|
---|
| 290 |
|
---|
| 291 | }
|
---|
| 292 |
|
---|
| 293 | }
|
---|
| 294 | // ------------------------------------------------------------------------
|
---|
| 295 | Real ShadowCaster::getExtrusionDistance(const Vector3& objectPos, const Light* light) const
|
---|
| 296 | {
|
---|
| 297 | Vector3 diff = objectPos - light->getDerivedPosition();
|
---|
| 298 | return light->getAttenuationRange() - diff.length();
|
---|
| 299 | }
|
---|
| 300 |
|
---|
| 301 | }
|
---|