public EntityShadowRenderable( Entity parent, HardwareIndexBuffer indexBuffer, VertexData vertexData, bool createSeparateLightCap, SubEntity subEntity, bool isLightCap ) { this.parent = parent; // Save link to vertex data this.currentVertexData = vertexData; // Initialize render op renderOperation.indexData = new IndexData(); renderOperation.indexData.indexBuffer = indexBuffer; renderOperation.indexData.indexStart = 0; // index start and count are sorted out later // Create vertex data which just references position component (and 2 component) renderOperation.vertexData = new VertexData(); renderOperation.vertexData.vertexDeclaration = HardwareBufferManager.Instance.CreateVertexDeclaration(); renderOperation.vertexData.vertexBufferBinding = HardwareBufferManager.Instance.CreateVertexBufferBinding(); // Map in position data renderOperation.vertexData.vertexDeclaration.AddElement( 0, 0, VertexElementType.Float3, VertexElementSemantic.Position ); this.originalPosBufferBinding = vertexData.vertexDeclaration.FindElementBySemantic( VertexElementSemantic.Position ).Source; this.positionBuffer = vertexData.vertexBufferBinding.GetBuffer( this.originalPosBufferBinding ); renderOperation.vertexData.vertexBufferBinding.SetBinding( 0, this.positionBuffer ); // Map in w-coord buffer (if present) if ( vertexData.hardwareShadowVolWBuffer != null ) { renderOperation.vertexData.vertexDeclaration.AddElement( 1, 0, VertexElementType.Float1, VertexElementSemantic.TexCoords, 0 ); this.wBuffer = vertexData.hardwareShadowVolWBuffer; renderOperation.vertexData.vertexBufferBinding.SetBinding( 1, this.wBuffer ); } // Use same vertex start as input renderOperation.vertexData.vertexStart = vertexData.vertexStart; if ( isLightCap ) { // Use original vertex count, no extrusion renderOperation.vertexData.vertexCount = vertexData.vertexCount; } else { // Vertex count must take into account the doubling of the buffer, // because second half of the buffer is the extruded copy renderOperation.vertexData.vertexCount = vertexData.vertexCount*2; if ( createSeparateLightCap ) { // Create child light cap lightCap = new EntityShadowRenderable( parent, indexBuffer, vertexData, false, subEntity, true ); } } }
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?" ); var lodEnt = this.lodEntityList[ this.meshLodIndex - 1 ]; // index - 1 as we skip index 0 (original LOD) if ( HasSkeleton && lodEnt.HasSkeleton ) { // Copy the animation state set to lod entity, we assume the lod // entity only has a subset animation states 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 PrepareTempBlendedBuffers(); } // Update any animation UpdateAnimation(); // Calculate the object space light details var 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 var isAnimated = HasSkeleton || this.mesh.HasVertexAnimation; if ( !isAnimated ) { var world2Obj = parentNode.FullTransform.Inverse(); lightPos = world2Obj*lightPos; } // We need to search the edge list for silhouette edges var edgeList = GetEdgeList(); // Init shadow renderable list if required var init = ( this.shadowRenderables.Count == 0 ); if ( init ) { this.shadowRenderables.Capacity = edgeList.edgeGroups.Count; } var 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 ( var i = 0; i < this.shadowRenderables.Capacity; i++ ) { egi = (EdgeData.EdgeGroup)edgeList.edgeGroups[ i ]; var data = ( isAnimated ? FindBlendedVertexData( egi.vertexData ) : egi.vertexData ); if ( init ) { // Try to find corresponding SubEntity; this allows the // linkage of visibility between ShadowRenderable and SubEntity var subEntity = 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 ( 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 ) { var srcPtr = esr.PositionBuffer.Lock( BufferLocking.Normal ); var destPtr = srcPtr + ( 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 UpdateEdgeListLightFacing( edgeList, lightPos ); // Generate indexes and update renderables GenerateShadowVolume( edgeList, indexBuffer, light, this.shadowRenderables, flags ); return this.shadowRenderables.GetEnumerator(); }