public void PrepareForShadowVolume()
{
/* NOTE
Sinbad would dearly, dearly love to just use a 4D position buffer in order to
store the extra 'w' value I need to differentiate between extruded and
non-extruded sections of the buffer, so that vertex programs could use that.
Hey, it works fine for GL. However, D3D9 in it's infinite stupidity, does not
support 4d position vertices in the fixed-function pipeline. If you use them,
you just see nothing. Since we can't know whether the application is going to use
fixed function or vertex programs, we have to stick to 3d position vertices and
store the 'w' in a separate 1D texture coordinate buffer, which is only used
when rendering the shadow.
*/
// Upfront, lets check whether we have vertex program capability
RenderSystem renderSystem = Root.Instance.RenderSystem;
bool useVertexPrograms = false;
if ( renderSystem != null && renderSystem.Capabilities.HasCapability( Capabilities.VertexPrograms ) )
{
useVertexPrograms = true;
}
// Look for a position element
VertexElement posElem = vertexDeclaration.FindElementBySemantic( VertexElementSemantic.Position );
if ( posElem != null )
{
short posOldSource = posElem.Source;
HardwareVertexBuffer vbuf = vertexBufferBinding.GetBuffer( posOldSource );
bool wasSharedBuffer = false;
// Are there other elements in the buffer except for the position?
if ( vbuf.VertexSize > posElem.Size )
{
// We need to create another buffer to contain the remaining elements
// Most drivers don't like gaps in the declaration, and in any case it's waste
wasSharedBuffer = true;
}
HardwareVertexBuffer newPosBuffer = null, newRemainderBuffer = null;
VertexDeclaration newRemainderDeclaration = (VertexDeclaration)vertexDeclaration.Clone();
if ( wasSharedBuffer )
{
bool found = false;
int index = 0;
do
{
if (newRemainderDeclaration.GetElement(index).Semantic == VertexElementSemantic.Position)
{
newRemainderDeclaration.RemoveElement(index);
found = true;
}
index++;
} while ( !found );
newRemainderBuffer = HardwareBufferManager.Instance.CreateVertexBuffer( newRemainderDeclaration, vbuf.VertexCount, vbuf.Usage, vbuf.HasShadowBuffer );
}
// Allocate new position buffer, will be FLOAT3 and 2x the size
int oldVertexCount = vbuf.VertexCount;
int newVertexCount = oldVertexCount * 2;
VertexDeclaration newPosDecl = HardwareBufferManager.Instance.CreateVertexDeclaration();
newPosDecl.AddElement( 0, 0, VertexElementType.Float3, VertexElementSemantic.Position );
newPosBuffer = HardwareBufferManager.Instance.CreateVertexBuffer( newPosDecl, newVertexCount, vbuf.Usage, vbuf.HasShadowBuffer );
// Iterate over the old buffer, copying the appropriate elements and initializing the rest
IntPtr baseSrcPtr = vbuf.Lock( BufferLocking.ReadOnly );
// Point first destination pointer at the start of the new position buffer,
// the other one half way along
IntPtr destPtr = newPosBuffer.Lock( BufferLocking.Discard );
// oldVertexCount * 3 * 4, since we are dealing with byte offsets here
IntPtr dest2Ptr = new IntPtr( destPtr.ToInt64() + ( oldVertexCount * 12 ) );
int prePosVertexSize = 0;
int postPosVertexSize = 0;
int postPosVertexOffset = 0;
if ( wasSharedBuffer )
{
// Precalculate any dimensions of vertex areas outside the position
prePosVertexSize = posElem.Offset;
postPosVertexOffset = prePosVertexSize + posElem.Size;
postPosVertexSize = vbuf.VertexSize - postPosVertexOffset;
// the 2 separate bits together should be the same size as the remainder buffer vertex
Debug.Assert( newRemainderBuffer.VertexSize == ( prePosVertexSize + postPosVertexSize ) );
IntPtr baseDestRemPtr = newRemainderBuffer.Lock( BufferLocking.Discard );
int baseSrcOffset = 0;
int baseDestRemOffset = 0;
unsafe
{
float* pDest = (float*)destPtr.ToPointer();
float* pDest2 = (float*)dest2Ptr.ToPointer();
int destCount = 0, dest2Count = 0;
// Iterate over the vertices
for ( int v = 0; v < oldVertexCount; v++ )
{
float* pSrc = (float*)( (byte*)baseSrcPtr.ToPointer() + posElem.Offset + baseSrcOffset );
// Copy position, into both buffers
pDest[ destCount++ ] = pDest2[ dest2Count++ ] = pSrc[ 0 ];
pDest[ destCount++ ] = pDest2[ dest2Count++ ] = pSrc[ 1 ];
pDest[ destCount++ ] = pDest2[ dest2Count++ ] = pSrc[ 2 ];
// now deal with any other elements
// Basically we just memcpy the vertex excluding the position
if ( prePosVertexSize > 0 )
{
Memory.Copy( baseSrcPtr, baseDestRemPtr, baseSrcOffset, baseDestRemOffset, prePosVertexSize );
}
if ( postPosVertexSize > 0 )
{
Memory.Copy( baseSrcPtr, baseDestRemPtr, baseSrcOffset + postPosVertexOffset, baseDestRemOffset + prePosVertexSize, postPosVertexSize );
}
// increment the pointer offsets
baseDestRemOffset += newRemainderBuffer.VertexSize;
baseSrcOffset += vbuf.VertexSize;
} // next vertex
} // unsafe
}
else
{
// copy the data directly
Memory.Copy( baseSrcPtr, destPtr, vbuf.Size );
Memory.Copy( baseSrcPtr, dest2Ptr, vbuf.Size );
}
vbuf.Unlock();
newPosBuffer.Unlock();
if ( wasSharedBuffer )
{
newRemainderBuffer.Unlock();
}
// At this stage, he original vertex buffer is going to be destroyed
// So we should force the deallocation of any temporary copies
HardwareBufferManager.Instance.ForceReleaseBufferCopies( vbuf );
if ( useVertexPrograms )
{
unsafe
{
VertexDeclaration decl = HardwareBufferManager.Instance.CreateVertexDeclaration();
decl.AddElement(0, 0, VertexElementType.Float1, VertexElementSemantic.Position);
// Now it's time to set up the w buffer
hardwareShadowVolWBuffer = HardwareBufferManager.Instance.CreateVertexBuffer( decl, newVertexCount, BufferUsage.StaticWriteOnly, false );
// Fill the first half with 1.0, second half with 0.0
IntPtr wPtr = hardwareShadowVolWBuffer.Lock( BufferLocking.Discard );
float* pDest = (float*)wPtr.ToPointer();
int destCount = 0;
for ( int v = 0; v < oldVertexCount; v++ )
{
pDest[ destCount++ ] = 1.0f;
}
for ( int v = 0; v < oldVertexCount; v++ )
{
pDest[ destCount++ ] = 0.0f;
}
} // unsafe
hardwareShadowVolWBuffer.Unlock();
} // if vertexPrograms
short newPosBufferSource = 0;
if ( wasSharedBuffer )
{
// Get the a new buffer binding index
newPosBufferSource = vertexBufferBinding.NextIndex;
// Re-bind the old index to the remainder buffer
vertexBufferBinding.SetBinding( posOldSource, newRemainderBuffer );
}
else
{
// We can just re-use the same source idex for the new position buffer
newPosBufferSource = posOldSource;
}
// Bind the new position buffer
vertexBufferBinding.SetBinding( newPosBufferSource, newPosBuffer );
// Now, alter the vertex declaration to change the position source
// and the offsets of elements using the same buffer
for ( int i = 0; i < vertexDeclaration.ElementCount; i++ )
{
VertexElement element = vertexDeclaration.GetElement( i );
if ( element.Semantic == VertexElementSemantic.Position )
{
// Modify position to point at new position buffer
vertexDeclaration.ModifyElement( i, newPosBufferSource /* new source buffer */, 0 /* no offset now */, VertexElementType.Float3, VertexElementSemantic.Position );
}
else if ( wasSharedBuffer &&
element.Source == posOldSource &&
element.Offset > prePosVertexSize )
{
// This element came after position, remove the position's size
vertexDeclaration.ModifyElement( i, posOldSource /* same old source */, element.Offset - posElem.Size /* less offset now */, element.Type, element.Semantic, element.Index );
}
}
} // if posElem != null
}