/// <summary>
        ///		Utility method, extract info from the given VertexData
        /// </summary>
        public void ExtractFrom(VertexData sourceData)
        {
            // Release old buffer copies first
            HardwareBufferManager mgr = HardwareBufferManager.Instance;

            if (destPositionBuffer != null)
            {
                mgr.ReleaseVertexBufferCopy(destPositionBuffer);
                Debug.Assert(destPositionBuffer == null);
            }
            if (destNormalBuffer != null)
            {
                mgr.ReleaseVertexBufferCopy(destNormalBuffer);
                Debug.Assert(destNormalBuffer == null);
            }

            VertexDeclaration   decl       = sourceData.vertexDeclaration;
            VertexBufferBinding bind       = sourceData.vertexBufferBinding;
            VertexElement       posElem    = decl.FindElementBySemantic(VertexElementSemantic.Position);
            VertexElement       normElem   = decl.FindElementBySemantic(VertexElementSemantic.Normal);
            VertexElement       tanElem    = decl.FindElementBySemantic(VertexElementSemantic.Tangent);
            VertexElement       binormElem = decl.FindElementBySemantic(VertexElementSemantic.Binormal);

            Debug.Assert(posElem != null, "Positions are required");

            posBindIndex      = posElem.Source;
            srcPositionBuffer = bind.GetBuffer(posBindIndex);

            if (normElem == null)
            {
                posNormalShareBuffer = false;
                srcNormalBuffer      = null;
            }
            else
            {
                normBindIndex = normElem.Source;
                if (normBindIndex == posBindIndex)
                {
                    posNormalShareBuffer = true;
                    srcNormalBuffer      = null;
                }
                else
                {
                    posNormalShareBuffer = false;
                    srcNormalBuffer      = bind.GetBuffer(normBindIndex);
                }
            }
            if (tanElem == null)
            {
                srcTangentBuffer = null;
            }
            else
            {
                tanBindIndex     = tanElem.Source;
                srcTangentBuffer = bind.GetBuffer(tanBindIndex);
            }

            if (binormElem == null)
            {
                srcBinormalBuffer = null;
            }
            else
            {
                binormBindIndex   = binormElem.Source;
                srcBinormalBuffer = bind.GetBuffer(binormBindIndex);
            }
        }
예제 #2
0
		/// <summary>
		///     Sets up the surface by defining it's control points, type and initial subdivision level.
		/// </summary>
		/// <remarks>
		///     This method initialises the surface by passing it a set of control points. The type of curves to be used
		///     are also defined here, although the only supported option currently is a bezier patch. You can also
		///     specify a global subdivision level here if you like, although it is recommended that the parameter
		///     is left as AUTO_LEVEL, which means the system decides how much subdivision is required (based on the
		///     curvature of the surface).
		/// </remarks>
		/// <param name="controlPointArray">
		///     An array containing the vertex data which define control points of the curves
		///     rather than actual vertices. Note that you are expected to provide not
		///     just position information, but potentially normals and texture coordinates too.
		///     The array is internally treated as a contiguous memory buffer without any gaps between the elements.
		///     The format of the buffer is defined in the VertexDeclaration parameter.
		/// </param>
		/// <param name="declaration">
		///     VertexDeclaration describing the contents of the buffer.
		///     Note this declaration must _only_ draw on buffer source 0!
		/// </param>
		/// <param name="width">Specifies the width of the patch in control points.</param>
		/// <param name="height">Specifies the height of the patch in control points.</param>
		/// <param name="type">The type of surface.</param>
		/// <param name="uMaxSubdivisionLevel">
		///     If you want to manually set the top level of subdivision,
		///     do it here, otherwise let the system decide.
		/// </param>
		/// <param name="vMaxSubdivisionLevel">
		///     If you want to manually set the top level of subdivision,
		///     do it here, otherwise let the system decide.
		/// </param>
		/// <param name="visibleSide">Determines which side of the patch (or both) triangles are generated for.</param>
		public void DefineSurface( Array controlPointArray, VertexDeclaration declaration, int width, int height,
		                           PatchSurfaceType type, int uMaxSubdivisionLevel, int vMaxSubdivisionLevel,
		                           VisibleSide visibleSide )
		{
			if ( height == 0 || width == 0 )
			{
				return; // Do nothing - garbage
			}

			//pin the input to have a pointer
			Memory.UnpinObject( controlPointArray );
			this.controlPointBuffer = Memory.PinObject( controlPointArray );

			this.type = type;
			this.controlWidth = width;
			this.controlHeight = height;
			this.controlCount = width*height;
			this.declaration = declaration;

			// Copy positions into Vector3 vector
			this.controlPoints.Clear();
			var elem = declaration.FindElementBySemantic( VertexElementSemantic.Position );
			var vertSize = declaration.GetVertexSize( 0 );

#if !AXIOM_SAFE_ONLY
			unsafe
#endif
			{
				var pVert = this.controlPointBuffer;
				for ( var i = 0; i < this.controlCount; i++ )
				{
					var pReal = ( pVert + ( ( i*vertSize ) + elem.Offset ) ).ToFloatPointer();
					this.controlPoints.Add( new Vector3( pReal[ 0 ], pReal[ 1 ], pReal[ 2 ] ) );
				}
			}
			this.side = visibleSide;

			// Determine max level
			// Initialize to 100% detail
			this.subdivisionFactor = 1.0f;

			if ( uMaxSubdivisionLevel == AUTO_LEVEL )
			{
				this.uLevel = this.maxULevel = GetAutoULevel();
			}
			else
			{
				this.uLevel = this.maxULevel = uMaxSubdivisionLevel;
			}

			if ( vMaxSubdivisionLevel == AUTO_LEVEL )
			{
				this.vLevel = this.maxVLevel = GetAutoVLevel();
			}
			else
			{
				this.vLevel = this.maxVLevel = vMaxSubdivisionLevel;
			}

			// Derive mesh width / height
			this.meshWidth = ( LevelWidth( this.maxULevel ) - 1 )*( ( this.controlWidth - 1 )/2 ) + 1;
			this.meshHeight = ( LevelWidth( this.maxVLevel ) - 1 )*( ( this.controlHeight - 1 )/2 ) + 1;

			// Calculate number of required vertices / indexes at max resolution
			this.requiredVertexCount = this.meshWidth*this.meshHeight;
			var iterations = ( this.side == VisibleSide.Both ) ? 2 : 1;
			this.requiredIndexCount = ( this.meshWidth - 1 )*( this.meshHeight - 1 )*2*iterations*3;

			// Calculate bounds based on control points
			var min = Vector3.Zero;
			var max = Vector3.Zero;
			var maxSqRadius = 0.0f;
			var first = true;

			for ( var i = 0; i < this.controlPoints.Count; i++ )
			{
				var vec = this.controlPoints[ i ];
				if ( first )
				{
					min = max = vec;
					maxSqRadius = vec.LengthSquared;
					first = false;
				}
				else
				{
					min.Floor( vec );
					max.Ceil( vec );
					maxSqRadius = Utility.Max( vec.LengthSquared, maxSqRadius );
				}
			}

			// set the bounds of the patch
			this.aabb.SetExtents( min, max );
			this.boundingSphereRadius = Utility.Sqrt( maxSqRadius );
		}
        /// <summary>
        ///     Sets up the surface by defining it's control points, type and initial subdivision level.
        /// </summary>
        /// <remarks>
        ///     This method initialises the surface by passing it a set of control points. The type of curves to be used
        ///     are also defined here, although the only supported option currently is a bezier patch. You can also
        ///     specify a global subdivision level here if you like, although it is recommended that the parameter
        ///     is left as AUTO_LEVEL, which means the system decides how much subdivision is required (based on the
        ///     curvature of the surface).
        /// </remarks>
        /// <param name="controlPoints">
        ///     A pointer to a buffer containing the vertex data which defines control points 
        ///     of the curves rather than actual vertices. Note that you are expected to provide not
        ///     just position information, but potentially normals and texture coordinates too. The
        ///     format of the buffer is defined in the VertexDeclaration parameter.
        /// </param>
        /// <param name="decl">
        ///     VertexDeclaration describing the contents of the buffer. 
        ///     Note this declaration must _only_ draw on buffer source 0!
        /// </param>
        /// <param name="width">Specifies the width of the patch in control points.</param>
        /// <param name="height">Specifies the height of the patch in control points.</param>
        /// <param name="type">The type of surface.</param>
        /// <param name="uMaxSubdivisionLevel">
        ///     If you want to manually set the top level of subdivision, 
        ///     do it here, otherwise let the system decide.
        /// </param>
        /// <param name="vMaxSubdivisionLevel">
        ///     If you want to manually set the top level of subdivision, 
        ///     do it here, otherwise let the system decide.
        /// </param>
        /// <param name="side">Determines which side of the patch (or both) triangles are generated for.</param>
        public unsafe void DefineSurface(System.Array controlPointBuffer, VertexDeclaration declaration, int width, int height, 
            PatchSurfaceType type, int uMaxSubdivisionLevel, int vMaxSubdivisionLevel, VisibleSide visibleSide)
        {
            if (height == 0 || width == 0) {
                return; // Do nothing - garbage
            }

            this.type = type;
            this.controlWidth = width;
            this.controlHeight = height;
            this.controlCount = width * height;
            this.controlPointBuffer = controlPointBuffer;
            this.declaration = declaration;

            // Copy positions into Vector3 vector
            controlPoints.Clear();
            VertexElement elem = declaration.FindElementBySemantic(VertexElementSemantic.Position);
            int vertSize = declaration.GetVertexSize(0);
            byte *pVert = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement(controlPointBuffer, 0);
            float* pReal = null;

            for (int i = 0; i < controlCount; i++) {
                pReal = (float*)(pVert + elem.Offset);
                controlPoints.Add(new Vector3(pReal[0], pReal[1], pReal[2]));
                pVert += vertSize;
            }

            this.side = visibleSide;

            // Determine max level
            // Initialise to 100% detail
            subdivisionFactor = 1.0f;

            if (uMaxSubdivisionLevel == AUTO_LEVEL) {
                uLevel = maxULevel = GetAutoULevel();
            }
            else {
                uLevel = maxULevel = uMaxSubdivisionLevel;
            }

            if (vMaxSubdivisionLevel == AUTO_LEVEL) {
                vLevel = maxVLevel = GetAutoVLevel();
            }
            else {
                vLevel = maxVLevel = vMaxSubdivisionLevel;
            }

            // Derive mesh width / height
            meshWidth  = (LevelWidth(maxULevel) - 1) * ((controlWidth-1) / 2) + 1;
            meshHeight = (LevelWidth(maxVLevel) - 1) * ((controlHeight-1) / 2) + 1;

            // Calculate number of required vertices / indexes at max resolution
            requiredVertexCount = meshWidth * meshHeight;
            int iterations = (side == VisibleSide.Both)? 2 : 1;
            requiredIndexCount = (meshWidth-1) * (meshHeight - 1) * 2 * iterations * 3;

            // Calculate bounds based on control points
            Vector3 min = Vector3.Zero;
            Vector3 max = Vector3.Zero;
            float maxSqRadius = 0.0f;
            bool first = true;

            for(int i = 0; i < controlPoints.Count; i++) {
                Vector3 vec = controlPoints[i];
                if (first) {
                    min = max = vec;
                    maxSqRadius = vec.LengthSquared;
                    first = false;
                }
                else {
                    min.Floor(vec);
                    max.Ceil(vec);
                    maxSqRadius = MathUtil.Max(vec.LengthSquared, maxSqRadius);
                }
            }

            // set the bounds of the patch
            aabb.SetExtents(min, max);
            boundingSphereRadius = MathUtil.Sqrt(maxSqRadius);
        }
        /// <summary>
        ///		Modifies the vertex data to be suitable for use for rendering shadow geometry.
        /// </summary>
        /// <remarks>
        ///		<para>
        ///			Preparing vertex data to generate a shadow volume involves firstly ensuring that the
        ///			vertex buffer containing the positions is a standalone vertex buffer,
        ///			with no other components in it. This method will therefore break apart any existing
        ///			vertex buffers if position is sharing a vertex buffer.
        ///			Secondly, it will double the size of this vertex buffer so that there are 2 copies of
        ///			the position data for the mesh. The first half is used for the original, and the second
        ///			half is used for the 'extruded' version. The vertex count used to render will remain
        ///			the same though, so as not to add any overhead to regular rendering of the object.
        ///			Both copies of the position are required in one buffer because shadow volumes stretch
        ///			from the original mesh to the extruded version.
        ///		</para>
        ///		<para>
        ///			It's important to appreciate that this method can fundamentally change the structure of your
        ///			vertex buffers, although in reality they will be new buffers. As it happens, if other
        ///			objects are using the original buffers then they will be unaffected because the reference
        ///			counting will keep them intact. However, if you have made any assumptions about the
        ///			structure of the vertex data in the buffers of this object, you may have to rethink them.
        ///		</para>
        /// </remarks>
        // TODO: Step through and test
        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.Caps.CheckCap(Capabilities.VertexPrograms))
            {
                useVertexPrograms = true;
            }

            // Look for a position element
            VertexElement posElem =
                vertexDeclaration.FindElementBySemantic(VertexElementSemantic.Position);

            if (posElem != null)
            {
                ushort 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;

                if (wasSharedBuffer)
                {
                    newRemainderBuffer = HardwareBufferManager.Instance.CreateVertexBuffer(
                        vbuf.VertexSize - posElem.Size, 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;

                newPosBuffer = HardwareBufferManager.Instance.CreateVertexBuffer(
                    VertexElement.GetTypeSize(VertexElementType.Float3),
                    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.ToInt32() + (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 {
                        // Now it's time to set up the w buffer
                        hardwareShadowVolWBuffer =
                            HardwareBufferManager.Instance.CreateVertexBuffer(
                                sizeof(float),
                                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

                ushort 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
        }