public override IEnumerator GetShadowVolumeRenderableEnumerator( ShadowTechnique technique,
Light light,
HardwareIndexBuffer indexBuffer,
bool extrudeVertices,
float extrusionDistance,
int flags )
{
Debug.Assert( indexBuffer != null, "Only external index buffers are supported right now" );
Debug.Assert( indexBuffer.Type == IndexType.Size16, "Only 16-bit indexes supported for now" );
// Potentially delegate to LOD entity
if ( this.meshLodIndex > 0 && this.mesh.IsLodManual )
{
Debug.Assert( this.meshLodIndex - 1 < this.lodEntityList.Count,
"No LOD EntityList - did you build the manual LODs after creating the entity?" );
Entity lodEnt = lodEntityList[ meshLodIndex - 1 ];
// index - 1 as we skip index 0 (original LOD)
if ( this.HasSkeleton && lodEnt.HasSkeleton )
{
// Copy the animation state set to lod entity, we assume the lod
// entity only has a subset animation states
this.CopyAnimationStateSubset( lodEnt.animationState, this.animationState );
}
return lodEnt.GetShadowVolumeRenderableEnumerator( technique,
light,
indexBuffer,
extrudeVertices,
extrusionDistance,
flags );
}
// Prep mesh if required
// NB This seems to result in memory corruptions, having problems
// tracking them down. For now, ensure that shadows are enabled
// before any entities are created
if ( !this.mesh.IsPreparedForShadowVolumes )
{
this.mesh.PrepareForShadowVolume();
// reset frame last updated to force update of buffers
this.frameAnimationLastUpdated = 0;
// re-prepare buffers
this.PrepareTempBlendedBuffers();
}
// Update any animation
this.UpdateAnimation();
// Calculate the object space light details
Vector4 lightPos = light.GetAs4DVector();
// Only use object-space light if we're not doing transforms
// Since when animating the positions are already transformed into
// world space so we need world space light position
bool isAnimated = this.HasSkeleton || this.mesh.HasVertexAnimation;
if ( !isAnimated )
{
Matrix4 world2Obj = this.parentNode.FullTransform.Inverse();
lightPos = world2Obj * lightPos;
}
// We need to search the edge list for silhouette edges
EdgeData edgeList = this.GetEdgeList();
// Init shadow renderable list if required
bool init = ( this.shadowRenderables.Count == 0 );
if ( init )
{
this.shadowRenderables.Capacity = edgeList.edgeGroups.Count;
}
bool updatedSharedGeomNormals = false;
EntityShadowRenderable esr = null;
EdgeData.EdgeGroup egi;
// note: using capacity for the loop since no items are in the list yet.
// capacity is set to how large the collection will be in the end
for ( int i = 0; i < this.shadowRenderables.Capacity; i++ )
{
egi = (EdgeData.EdgeGroup)edgeList.edgeGroups[ i ];
VertexData data = ( isAnimated
? this.FindBlendedVertexData( egi.vertexData )
:
egi.vertexData );
if ( init )
{
// Try to find corresponding SubEntity; this allows the
// linkage of visibility between ShadowRenderable and SubEntity
SubEntity subEntity = this.FindSubEntityForVertexData( egi.vertexData );
// Create a new renderable, create a separate light cap if
// we're using hardware skinning since otherwise we get
// depth-fighting on the light cap
esr = new EntityShadowRenderable( this,
indexBuffer,
data,
subEntity.VertexProgramInUse || !extrudeVertices,
subEntity );
this.shadowRenderables.Add( esr );
}
else
{
esr = (EntityShadowRenderable)this.shadowRenderables[ i ];
if ( this.HasSkeleton )
{
// If we have a skeleton, we have no guarantee that the position
// buffer we used last frame is the same one we used last frame
// since a temporary buffer is requested each frame
// therefore, we need to update the EntityShadowRenderable
// with the current position buffer
esr.RebindPositionBuffer( data, isAnimated );
}
}
// For animated entities we need to recalculate the face normals
if ( isAnimated )
{
if ( egi.vertexData != this.mesh.SharedVertexData || !updatedSharedGeomNormals )
{
// recalculate face normals
edgeList.UpdateFaceNormals( egi.vertexSet, esr.PositionBuffer );
// If we're not extruding in software we still need to update
// the latter part of the buffer (the hardware extruded part)
// with the latest animated positions
if ( !extrudeVertices )
{
IntPtr srcPtr = esr.PositionBuffer.Lock( BufferLocking.Normal );
IntPtr destPtr = new IntPtr( srcPtr.ToInt64() + ( egi.vertexData.vertexCount * 12 ) );
// 12 = sizeof(float) * 3
Memory.Copy( srcPtr, destPtr, 12 * egi.vertexData.vertexCount );
esr.PositionBuffer.Unlock();
}
if ( egi.vertexData == this.mesh.SharedVertexData )
{
updatedSharedGeomNormals = true;
}
}
}
// Extrude vertices in software if required
if ( extrudeVertices )
{
ExtrudeVertices( esr.PositionBuffer, egi.vertexData.vertexCount, lightPos, extrusionDistance );
}
// Stop suppressing hardware update now, if we were
esr.PositionBuffer.SuppressHardwareUpdate( false );
}
// Calc triangle light facing
this.UpdateEdgeListLightFacing( edgeList, lightPos );
// Generate indexes and update renderables
this.GenerateShadowVolume( edgeList, indexBuffer, light, this.shadowRenderables, flags );
return this.shadowRenderables.GetEnumerator();
}