/// <summary> /// Updates the face normals for this edge list based on (changed) /// position information, useful for animated objects. /// </summary> /// <param name="vertexSet">The vertex set we are updating.</param> /// <param name="positionBuffer">The updated position buffer, must contain ONLY xyz.</param> public void UpdateFaceNormals(int vertexSet, HardwareVertexBuffer positionBuffer) { unsafe { Debug.Assert(positionBuffer.VertexSize == sizeof(float) * 3, "Position buffer should contain only positions!"); // Lock buffer for reading IntPtr posPtr = positionBuffer.Lock(BufferLocking.ReadOnly); float *pVert = (float *)posPtr.ToPointer(); // Iterate over the triangles for (int i = 0; i < triangles.Count; i++) { Triangle t = (Triangle)triangles[i]; // Only update tris which are using this vertex set if (t.vertexSet == vertexSet) { int offset = t.vertIndex[0] * 3; Vector3 v1 = new Vector3(pVert[offset], pVert[offset + 1], pVert[offset + 2]); offset = t.vertIndex[1] * 3; Vector3 v2 = new Vector3(pVert[offset], pVert[offset + 1], pVert[offset + 2]); offset = t.vertIndex[2] * 3; Vector3 v3 = new Vector3(pVert[offset], pVert[offset + 1], pVert[offset + 2]); t.normal = MathUtil.CalculateFaceNormal(v1, v2, v3); } } } // unlock the buffer positionBuffer.Unlock(); }
/// <summary> /// Utility method to pull a bunch of floats out of a vertex buffer. /// </summary> /// <param name="data"></param> /// <param name="vBuffer"></param> /// <param name="elem"></param> public static void ReadBuffer( float[ , ] data, HardwareVertexBuffer vBuffer, VertexElement elem ) { int count = data.GetLength( 1 ); IntPtr bufData = vBuffer.Lock( BufferLocking.ReadOnly ); Debug.Assert( vBuffer.VertexSize % sizeof( float ) == 0 ); Debug.Assert( elem.Offset % sizeof( float ) == 0 ); int vertexCount = vBuffer.VertexCount; int vertexSpan = vBuffer.VertexSize / sizeof( float ); int offset = elem.Offset / sizeof( float ); unsafe { float* pFloats = (float*) bufData.ToPointer(); for( int i = 0; i < vertexCount; ++i ) { for( int j = 0; j < count; ++j ) { Debug.Assert( ((offset + i * vertexSpan + j) * sizeof( float )) < (vertexCount * vBuffer.VertexSize), "Read off end of vertex buffer" ); data[ i, j ] = pFloats[ offset + i * vertexSpan + j ]; } } } // unlock the buffer vBuffer.Unlock(); }
/// <summary> /// Utility method for extruding vertices based on a light. /// </summary> /// <remarks> /// Unfortunately, because D3D cannot handle homogenous (4D) position /// coordinates in the fixed-function pipeline (GL can, but we have to /// be cross-API), when we extrude in software we cannot extrude to /// infinity the way we do in the vertex program (by setting w to /// 0.0f). Therefore we extrude by a fixed distance, which may cause /// some problems with larger scenes. Luckily better hardware (ie /// vertex programs) can fix this. /// </remarks> /// <param name="vertexBuffer">The vertex buffer containing ONLY xyz position /// values, which must be originalVertexCount * 2 * 3 floats long.</param> /// <param name="originalVertexCount">The count of the original number of /// vertices, ie the number in the mesh, not counting the doubling /// which has already been done (by <see cref="VertexData.PrepareForShadowVolume"/>) /// to provide the extruded area of the buffer.</param> /// <param name="lightPosition"> 4D light position in object space, when w=0.0f this /// represents a directional light</param> /// <param name="extrudeDistance">The distance to extrude.</param> public static void ExtrudeVertices(HardwareVertexBuffer vertexBuffer, int originalVertexCount, Vector4 lightPosition, float extrudeDistance) { #if !AXIOM_SAFE_ONLY unsafe #endif { Debug.Assert(vertexBuffer.VertexSize == sizeof(float) * 3, "Position buffer should contain only positions!"); // Extrude the first area of the buffer into the second area // Lock the entire buffer for writing, even though we'll only be // updating the latter because you can't have 2 locks on the same // buffer var srcPtr = vertexBuffer.Lock(BufferLocking.Normal); var destPtr = srcPtr + (originalVertexCount * 3 * 4); var pSrc = srcPtr.ToFloatPointer(); var pDest = destPtr.ToFloatPointer(); int destCount = 0, srcCount = 0; // Assume directional light, extrusion is along light direction var extrusionDir = new Vector3(-lightPosition.x, -lightPosition.y, -lightPosition.z); extrusionDir.Normalize(); extrusionDir *= extrudeDistance; for (var vert = 0; vert < originalVertexCount; vert++) { if (lightPosition.w != 0.0f) { // Point light, adjust extrusionDir extrusionDir.x = pSrc[srcCount + 0] - lightPosition.x; extrusionDir.y = pSrc[srcCount + 1] - lightPosition.y; extrusionDir.z = pSrc[srcCount + 2] - lightPosition.z; extrusionDir.Normalize(); extrusionDir *= extrudeDistance; } pDest[destCount++] = pSrc[srcCount++] + extrusionDir.x; pDest[destCount++] = pSrc[srcCount++] + extrusionDir.y; pDest[destCount++] = pSrc[srcCount++] + extrusionDir.z; } } vertexBuffer.Unlock(); }
private static void ReadBuffer(HardwareVertexBuffer vBuffer, int vertexCount, int vertexSize, ref Vector3[] data) { IntPtr bufData = vBuffer.Lock(BufferLocking.ReadOnly); unsafe { float* pFloats = (float*)bufData.ToPointer(); for (int i = 0; i < vertexCount; ++i) for (int j = 0; j < 3; ++j) { Debug.Assert(sizeof(float) * (i * 3 + j) < vertexCount * vertexSize, "Read off end of vertex buffer"); data[i][j] = pFloats[i * 3 + j]; } } // unlock the buffer vBuffer.Unlock(); }
/// <summary> /// Updates the face normals for this edge list based on (changed) /// position information, useful for animated objects. /// </summary> /// <param name="vertexSet">The vertex set we are updating.</param> /// <param name="positionBuffer">The updated position buffer, must contain ONLY xyz.</param> public void UpdateFaceNormals(int vertexSet, HardwareVertexBuffer positionBuffer) { #if !AXIOM_SAFE_ONLY unsafe #endif { Debug.Assert(positionBuffer.VertexSize == sizeof(float) * 3, "Position buffer should contain only positions!"); // Lock buffer for reading var posPtr = positionBuffer.Lock(BufferLocking.ReadOnly); var pVert = posPtr.ToFloatPointer(); // Iterate over the triangles for (var i = 0; i < this.triangles.Count; i++) { var t = (Triangle)this.triangles[i]; // Only update tris which are using this vertex set if (t.vertexSet == vertexSet) { var offset = t.vertIndex[0] * 3; var v1 = new Vector3(pVert[offset], pVert[offset + 1], pVert[offset + 2]); offset = t.vertIndex[1] * 3; var v2 = new Vector3(pVert[offset], pVert[offset + 1], pVert[offset + 2]); offset = t.vertIndex[2] * 3; var v3 = new Vector3(pVert[offset], pVert[offset + 1], pVert[offset + 2]); t.normal = CalculateFaceNormal(v1, v2, v3); } } } // unlock the buffer positionBuffer.Unlock(); }
protected void UpdateVertexBuffer( HardwareVertexBuffer posBuf, HardwareVertexBuffer deltaBuf, Rectangle rect ) { Debug.Assert( rect.Left >= this.mOffsetX && rect.Right <= this.mBoundaryX && rect.Top >= this.mOffsetY && rect.Bottom <= this.mBoundaryY ); // potentially reset our bounds depending on coverage of the update ResetBounds( rect ); //main data var inc = (ushort)( ( this.mTerrain.Size - 1 )/( this.mVertexDataRecord.Resolution - 1 ) ); long destOffsetX = rect.Left <= this.mOffsetX ? 0 : ( rect.Left - this.mOffsetX )/inc; long destOffsetY = rect.Top <= this.mOffsetY ? 0 : ( rect.Top - this.mOffsetY )/inc; // Fill the buffers BufferLocking lockmode; if ( destOffsetX != 0 || destOffsetY != 0 || rect.Width < this.mSize || rect.Height < this.mSize ) { lockmode = BufferLocking.Normal; } else { lockmode = BufferLocking.Discard; } Real uvScale = 1.0f/( this.mTerrain.Size - 1 ); var pBaseHeight = this.mTerrain.GetHeightData( rect.Left, rect.Top ); var pBaseDelta = this.mTerrain.GetDeltaData( rect.Left, rect.Top ); var rowskip = (ushort)( this.mTerrain.Size*inc ); ushort destPosRowSkip = 0, destDeltaRowSkip = 0; BufferBase pRootPosBuf = null; BufferBase pRootDeltaBuf = null; BufferBase pRowPosBuf = null; BufferBase pRowDeltaBuf = null; if ( posBuf != null ) { destPosRowSkip = (ushort)( this.mVertexDataRecord.Size*posBuf.VertexSize ); pRootPosBuf = posBuf.Lock( lockmode ); pRowPosBuf = pRootPosBuf; // skip dest buffer in by left/top pRowPosBuf += destOffsetY*destPosRowSkip + destOffsetX*posBuf.VertexSize; } if ( deltaBuf != null ) { destDeltaRowSkip = (ushort)( this.mVertexDataRecord.Size*deltaBuf.VertexSize ); pRootDeltaBuf = deltaBuf.Lock( lockmode ); pRowDeltaBuf = pRootDeltaBuf; // skip dest buffer in by left/top pRowDeltaBuf += destOffsetY*destDeltaRowSkip + destOffsetX*deltaBuf.VertexSize; } Vector3 pos = Vector3.Zero; for ( var y = (ushort)rect.Top; y < rect.Bottom; y += inc ) { #if !AXIOM_SAFE_ONLY unsafe #endif { var pHeight = pBaseHeight.ToFloatPointer(); var pHIdx = 0; var pDelta = pBaseDelta.ToFloatPointer(); var pDeltaIdx = 0; var pPosBuf = pRowPosBuf != null ? pRowPosBuf.ToFloatPointer() : null; var pPosBufIdx = 0; var pDeltaBuf = pRowDeltaBuf != null ? pRowDeltaBuf.ToFloatPointer() : null; var pDeltaBufIdx = 0; for ( var x = (ushort)rect.Left; x < rect.Right; x += inc ) { if ( pPosBuf != null ) { this.mTerrain.GetPoint( x, y, pHeight[ pHIdx ], ref pos ); // Update bounds *before* making relative MergeIntoBounds( x, y, pos ); // relative to local centre pos -= this.mLocalCentre; pHIdx += inc; pPosBuf[ pPosBufIdx++ ] = pos.x; pPosBuf[ pPosBufIdx++ ] = pos.y; pPosBuf[ pPosBufIdx++ ] = pos.z; // UVs - base UVs vary from 0 to 1, all other values // will be derived using scalings pPosBuf[ pPosBufIdx++ ] = x*uvScale; pPosBuf[ pPosBufIdx++ ] = 1.0f - ( y*uvScale ); } if ( pDeltaBuf != null ) { //delta pDeltaBuf[ pDeltaBufIdx++ ] = pDelta[ pDeltaIdx ]; pDeltaIdx += inc; // delta LOD threshold // we want delta to apply to LODs no higher than this value // at runtime this will be combined with a per-renderable parameter // to ensure we only apply morph to the correct LOD pDeltaBuf[ pDeltaBufIdx++ ] = (float)this.mTerrain.GetLODLevelWhenVertexEliminated( x, y ) - 1.0f; } } // end unsafe } //end for pBaseHeight += rowskip; pBaseDelta += rowskip; if ( pRowPosBuf != null ) { pRowPosBuf += destPosRowSkip; } if ( pRowDeltaBuf != null ) { pRowDeltaBuf += destDeltaRowSkip; } } //end for // Skirts now // skirt spacing based on top-level resolution (* inc to cope with resolution which is not the max) var skirtSpacing = (ushort)( this.mVertexDataRecord.SkirtRowColSkip*inc ); var skirtOffset = Vector3.Zero; this.mTerrain.GetVector( 0, 0, -this.mTerrain.SkirtSize, ref skirtOffset ); // skirt rows // clamp rows to skirt spacing (round up) var skirtStartX = rect.Left; var skirtStartY = rect.Top; // for rows, clamp Y to skirt frequency, X to inc (LOD resolution vs top) if ( skirtStartY%skirtSpacing != 0 ) { skirtStartY += skirtSpacing - ( skirtStartY%skirtSpacing ); } if ( skirtStartX%inc != 0 ) { skirtStartX += inc - ( skirtStartX%inc ); } skirtStartY = System.Math.Max( skirtStartY, (long)this.mOffsetY ); pBaseHeight = this.mTerrain.GetHeightData( skirtStartX, skirtStartY ); if ( posBuf != null ) { // position dest buffer just after the main vertex data pRowPosBuf = pRootPosBuf + posBuf.VertexSize*this.mVertexDataRecord.Size*this.mVertexDataRecord.Size; // move it onwards to skip the skirts we don't need to update pRowPosBuf += destPosRowSkip*( ( skirtStartY - this.mOffsetY )/skirtSpacing ); pRowPosBuf += posBuf.VertexSize*( skirtStartX - this.mOffsetX )/inc; } if ( deltaBuf != null ) { // position dest buffer just after the main vertex data pRowDeltaBuf = pRootDeltaBuf + deltaBuf.VertexSize*this.mVertexDataRecord.Size*this.mVertexDataRecord.Size; // move it onwards to skip the skirts we don't need to update pRowDeltaBuf += destDeltaRowSkip*( skirtStartY - this.mOffsetY )/skirtSpacing; pRowDeltaBuf += deltaBuf.VertexSize*( skirtStartX - this.mOffsetX )/inc; } for ( var y = (ushort)skirtStartY; y < (ushort)rect.Bottom; y += skirtSpacing ) { #if !AXIOM_SAFE_ONLY unsafe #endif { var pHeight = pBaseHeight.ToFloatPointer(); var pHIdx = 0; var pPosBuf = pRowPosBuf != null ? pRowPosBuf.ToFloatPointer() : null; var pPosIdx = 0; var pDeltaBuf = pRowDeltaBuf != null ? pRowDeltaBuf.ToFloatPointer() : null; var pDeltaIdx = 0; for ( var x = (ushort)skirtStartX; x < (ushort)rect.Right; x += inc ) { if ( pPosBuf != null ) { this.mTerrain.GetPoint( x, y, pHeight[ pHIdx ], ref pos ); // relative to local centre pos -= this.mLocalCentre; pHIdx += inc; pos += skirtOffset; pPosBuf[ pPosIdx++ ] = pos.x; pPosBuf[ pPosIdx++ ] = pos.y; pPosBuf[ pPosIdx++ ] = pos.z; // UVs - same as base pPosBuf[ pPosIdx++ ] = x*uvScale; pPosBuf[ pPosIdx++ ] = 1.0f - ( y*uvScale ); } if ( pDeltaBuf != null ) { // delta (none) pDeltaBuf[ pDeltaIdx++ ] = 0; // delta threshold (irrelevant) pDeltaBuf[ pDeltaIdx++ ] = 99; } } //end for pBaseHeight += this.mTerrain.Size*skirtSpacing; if ( pRowPosBuf != null ) { pRowPosBuf += destPosRowSkip; } if ( pRowDeltaBuf != null ) { pRowDeltaBuf += destDeltaRowSkip; } } // end unsafe } //end for // skirt cols // clamp cols to skirt spacing (round up) skirtStartX = rect.Left; if ( skirtStartX%skirtSpacing != 0 ) { skirtStartX += skirtSpacing - ( skirtStartX%skirtSpacing ); } // clamp Y to inc (LOD resolution vs top) skirtStartY = rect.Top; if ( skirtStartY%inc != 0 ) { skirtStartY += inc - ( skirtStartY%inc ); } skirtStartX = System.Math.Max( skirtStartX, (long)this.mOffsetX ); if ( posBuf != null ) { // position dest buffer just after the main vertex data and skirt rows pRowPosBuf = pRootPosBuf + posBuf.VertexSize*this.mVertexDataRecord.Size*this.mVertexDataRecord.Size; // skip the row skirts pRowPosBuf += this.mVertexDataRecord.NumSkirtRowsCols*this.mVertexDataRecord.Size*posBuf.VertexSize; // move it onwards to skip the skirts we don't need to update pRowPosBuf += destPosRowSkip*( skirtStartX - this.mOffsetX )/skirtSpacing; pRowPosBuf += posBuf.VertexSize*( skirtStartY - this.mOffsetY )/inc; } if ( deltaBuf != null ) { // Deltaition dest buffer just after the main vertex data and skirt rows pRowDeltaBuf = pRootDeltaBuf + deltaBuf.VertexSize*this.mVertexDataRecord.Size*this.mVertexDataRecord.Size; // skip the row skirts pRowDeltaBuf += this.mVertexDataRecord.NumSkirtRowsCols*this.mVertexDataRecord.Size*deltaBuf.VertexSize; // move it onwards to skip the skirts we don't need to update pRowDeltaBuf += destDeltaRowSkip*( skirtStartX - this.mOffsetX )/skirtSpacing; pRowDeltaBuf += deltaBuf.VertexSize*( skirtStartY - this.mOffsetY )/inc; } for ( var x = (ushort)skirtStartX; x < (ushort)rect.Right; x += skirtSpacing ) { #if !AXIOM_SAFE_ONLY unsafe #endif { var pPosBuf = pRowPosBuf != null ? pRowPosBuf.ToFloatPointer() : null; var pPosIdx = 0; var pDeltaBuf = pRowDeltaBuf != null ? pRowDeltaBuf.ToFloatPointer() : null; var pDeltaIdx = 0; for ( var y = (ushort)skirtStartY; y < (ushort)rect.Bottom; y += inc ) { if ( pPosBuf != null ) { this.mTerrain.GetPoint( x, y, this.mTerrain.GetHeightAtPoint( x, y ), ref pos ); // relative to local centre pos -= this.mLocalCentre; pos += skirtOffset; pPosBuf[ pPosIdx++ ] = pos.x; pPosBuf[ pPosIdx++ ] = pos.y; pPosBuf[ pPosIdx++ ] = pos.z; // UVs - same as base pPosBuf[ pPosIdx++ ] = x*uvScale; pPosBuf[ pPosIdx++ ] = 1.0f - ( y*uvScale ); } if ( pDeltaBuf != null ) { // delta (none) pDeltaBuf[ pDeltaIdx++ ] = 0; // delta threshold (irrelevant) pDeltaBuf[ pDeltaIdx++ ] = 99; } } //end for if ( pRowPosBuf != null ) { pRowPosBuf += destPosRowSkip; } if ( pRowDeltaBuf != null ) { pRowDeltaBuf += destDeltaRowSkip; } } // end unsafe } //end for if ( posBuf != null ) { posBuf.Unlock(); } if ( deltaBuf != null ) { deltaBuf.Unlock(); } }
public unsafe void Initialize( int startx, int startz, Real[] pageHeightData ) { if ( mOptions.maxGeoMipMapLevel != 0 ) { int i = (int)1 << ( mOptions.maxGeoMipMapLevel - 1 ); if ( ( i + 1 ) > mOptions.tileSize ) { LogManager.Instance.Write( "Invalid maximum mipmap specifed, must be n, such that 2^(n-1)+1 < tileSize \n" ); return; } } DeleteGeometry(); //calculate min and max heights; Real min = 256000, max = 0; mTerrain = new VertexData(); mTerrain.vertexStart = 0; mTerrain.vertexCount = mOptions.tileSize * mOptions.tileSize; renderOperation.useIndices = true; renderOperation.operationType = mOptions.useTriStrips ? OperationType.TriangleStrip : OperationType.TriangleList; renderOperation.vertexData = mTerrain; renderOperation.indexData = GetIndexData(); VertexDeclaration decl = mTerrain.vertexDeclaration; VertexBufferBinding bind = mTerrain.vertexBufferBinding; // positions int offset = 0; decl.AddElement( MAIN_BINDING, offset, VertexElementType.Float3, VertexElementSemantic.Position ); offset += VertexElement.GetTypeSize( VertexElementType.Float3 ); if ( mOptions.lit ) { decl.AddElement( MAIN_BINDING, offset, VertexElementType.Float3, VertexElementSemantic.Position ); offset += VertexElement.GetTypeSize( VertexElementType.Float3 ); } // texture coord sets decl.AddElement( MAIN_BINDING, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 0 ); offset += VertexElement.GetTypeSize( VertexElementType.Float2 ); decl.AddElement( MAIN_BINDING, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 1 ); offset += VertexElement.GetTypeSize( VertexElementType.Float2 ); if ( mOptions.coloured ) { decl.AddElement( MAIN_BINDING, offset, VertexElementType.Color, VertexElementSemantic.Diffuse ); offset += VertexElement.GetTypeSize( VertexElementType.Color ); } // Create shared vertex buffer mMainBuffer = HardwareBufferManager.Instance.CreateVertexBuffer( decl.Clone( MAIN_BINDING ), mTerrain.vertexCount, BufferUsage.StaticWriteOnly ); // Create system memory copy with just positions in it, for use in simple reads //mPositionBuffer = OGRE_ALLOC_T(float, mTerrain.vertexCount * 3, MEMCATEGORY_GEOMETRY); mPositionBuffer = new float[ mTerrain.vertexCount * 3 ]; bind.SetBinding( MAIN_BINDING, mMainBuffer ); if ( mOptions.lodMorph ) { // Create additional element for delta decl.AddElement( DELTA_BINDING, 0, VertexElementType.Float1, VertexElementSemantic.BlendWeights ); // NB binding is not set here, it is set when deriving the LOD } mInit = true; mRenderLevel = 0; mMinLevelDistSqr = new Real[ mOptions.maxGeoMipMapLevel ]; int endx = startx + mOptions.tileSize; int endz = startz + mOptions.tileSize; Vector3 left, down, here; VertexElement poselem = decl.FindElementBySemantic( VertexElementSemantic.Position ); VertexElement texelem0 = decl.FindElementBySemantic( VertexElementSemantic.TexCoords, 0 ); VertexElement texelem1 = decl.FindElementBySemantic( VertexElementSemantic.TexCoords, 1 ); //fixed ( float* pSysPos = mPositionBuffer ) { int pos = 0; byte* pBase = (byte*)mMainBuffer.Lock( BufferLocking.Discard ); for ( int j = startz; j < endz; j++ ) { for ( int i = startx; i < endx; i++ ) { float* pPos = (float*)( pBase + poselem.Offset ); float* pTex0 = (float*)( pBase + texelem0.Offset ); float* pTex1 = (float*)( pBase + texelem1.Offset ); //poselem.baseVertexPointerToElement(pBase, &pPos); //texelem0.baseVertexPointerToElement(pBase, &pTex0); //texelem1.baseVertexPointerToElement(pBase, &pTex1); Real height = pageHeightData[ j * mOptions.pageSize + i ]; height = height * mOptions.scale.y; // scale height //*pSysPos++ = *pPos++ = (float) i*mOptions.scale.x; //x //*pSysPos++ = *pPos++ = height; // y //*pSysPos++ = *pPos++ = (float) j*mOptions.scale.z; //z mPositionBuffer[ pos++ ] = *pPos++ = (float)i * mOptions.scale.x; //x mPositionBuffer[ pos++ ] = *pPos++ = height; // y mPositionBuffer[ pos++ ] = *pPos++ = (float)j * mOptions.scale.z; //z *pTex0++ = (float)i / (float)( mOptions.pageSize - 1 ); *pTex0++ = (float)j / (float)( mOptions.pageSize - 1 ); *pTex1++ = ( (float)i / (float)( mOptions.tileSize - 1 ) ) * mOptions.detailTile; *pTex1++ = ( (float)j / (float)( mOptions.tileSize - 1 ) ) * mOptions.detailTile; if ( height < min ) min = (Real)height; if ( height > max ) max = (Real)height; pBase += mMainBuffer.VertexSize; } } mMainBuffer.Unlock(); mBounds = new AxisAlignedBox(); mBounds.SetExtents( new Vector3( (Real)startx * mOptions.scale.x, min, (Real)startz * mOptions.scale.z ), new Vector3( (Real)( endx - 1 ) * mOptions.scale.x, max, (Real)( endz - 1 ) * mOptions.scale.z ) ); mCenter = new Vector3( ( startx * mOptions.scale.x + ( endx - 1 ) * mOptions.scale.x ) / 2, ( min + max ) / 2, ( startz * mOptions.scale.z + ( endz - 1 ) * mOptions.scale.z ) / 2 ); boundingRadius = Math.Sqrt( Utility.Sqr( max - min ) + Utility.Sqr( ( endx - 1 - startx ) * mOptions.scale.x ) + Utility.Sqr( ( endz - 1 - startz ) * mOptions.scale.z ) ) / 2; // Create delta buffer list if required to morph if ( mOptions.lodMorph ) { // Create delta buffer for all except the lowest mip mDeltaBuffers = new AxiomSortedCollection<int, HardwareVertexBuffer>( mOptions.maxGeoMipMapLevel - 1 ); } Real C = CalculateCFactor(); CalculateMinLevelDist2( C ); } }
/// <summary> /// Fill a vertex buffer with the contents of a one dimensional /// integer array /// </summary> /// <param name="vBuffer">HardwareVertexBuffer to populate</param> /// <param name="vertexCount">the number of vertices</param> /// <param name="vertexSize">the size of each vertex</param> /// <param name="data">the array of data to put in the buffer</param> internal static void FillBuffer( HardwareVertexBuffer vBuffer, int vertexCount, int vertexSize, int[] data ) { IntPtr bufData = vBuffer.Lock( BufferLocking.Discard ); unsafe { int* pInts = (int*) bufData.ToPointer(); for( int i = 0; i < vertexCount; ++i ) { Debug.Assert( sizeof( int ) * i < vertexCount * vertexSize, "Wrote off end of vertex buffer" ); pInts[ i ] = data[ i ]; } } // unlock the buffer vBuffer.Unlock(); }
/// <summary> /// Hacked old version to add a transform /// </summary> /// <param name="vBuffer"></param> /// <param name="vertexOffset"></param> /// <param name="vertexStride"></param> /// <param name="vertexCount"></param> /// <param name="vertexSize"></param> /// <param name="data"></param> /// <param name="transform"></param> private void ReadBuffer(HardwareVertexBuffer vBuffer, int vertexOffset, int vertexStride, int vertexCount, int vertexSize, float[,] data, Matrix4 transform) { int count = data.GetLength(1); IntPtr bufData = vBuffer.Lock(BufferLocking.ReadOnly); Debug.Assert(count == 3); int floatOffset = vertexOffset / sizeof(float); int floatStride = vertexStride / sizeof(float); Debug.Assert(vertexOffset % sizeof(float) == 0); unsafe { float* pFloats = (float*)bufData.ToPointer(); for (int i = 0; i < vertexCount; ++i) { Vector3 tmpVec = Vector3.Zero; for (int j = 0; j < count; ++j) { int k = i * floatStride + floatOffset + j; Debug.Assert(sizeof(float) * k < vertexCount * vertexStride, "Read off end of vertex buffer"); tmpVec[j] = pFloats[k]; } tmpVec = transform * tmpVec; for (int j = 0; j < count; ++j) data[i, j] = tmpVec[j]; } } // unlock the buffer vBuffer.Unlock(); }
/// <summary> /// Tells the system to build the mesh relating to the surface into externally created buffers. /// </summary> /// <remarks> /// The VertexDeclaration of the vertex buffer must be identical to the one passed into /// <see cref="DefineSurface"/>. In addition, there must be enough space in the buffer to /// accommodate the patch at full detail level; you should check <see cref="RequiredVertexCount"/> /// and <see cref="RequiredIndexCount"/> to determine this. This method does not create an internal /// mesh for this patch and so GetMesh will return null if you call it after building the /// patch this way. /// </remarks> /// <param name="destVertexBuffer">The destination vertex buffer in which to build the patch.</param> /// <param name="vertexStart">The offset at which to start writing vertices for this patch.</param> /// <param name="destIndexBuffer">The destination index buffer in which to build the patch.</param> /// <param name="indexStart">The offset at which to start writing indexes for this patch.</param> public void Build(HardwareVertexBuffer destVertexBuffer, int vertexStart, HardwareIndexBuffer destIndexBuffer, int indexStart) { if(controlPoints.Count == 0) { return; } vertexBuffer = destVertexBuffer; vertexOffset = vertexStart; indexBuffer = destIndexBuffer; indexOffset = indexStart; // lock just the region we are interested in IntPtr lockedBuffer = vertexBuffer.Lock( vertexOffset * declaration.GetVertexSize(0), requiredVertexCount * declaration.GetVertexSize(0), BufferLocking.NoOverwrite); DistributeControlPoints(lockedBuffer); // subdivide the curves to the max // Do u direction first, so need to step over v levels not done yet int vStep = 1 << maxVLevel; int uStep = 1 << maxULevel; // subdivide this row in u for(int v = 0; v < meshHeight; v += vStep) { SubdivideCurve(lockedBuffer, v * meshWidth, uStep, meshWidth / uStep, uLevel); } // Now subdivide in v direction, this time all the u direction points are there so no step for(int u = 0; u < meshWidth; u++) { SubdivideCurve(lockedBuffer, u, vStep * meshWidth, meshHeight / vStep, vLevel); } // don't forget to unlock! vertexBuffer.Unlock(); // Make triangles from mesh at this current level of detail MakeTriangles(); }
private void _generatePlaneVertexData( HardwareVertexBuffer vbuf, int ySegments, int xSegments, float xSpace, float halfWidth, float ySpace, float halfHeight, Matrix4 transform, bool firstTime, bool normals, Matrix4 rotation, int numTexCoordSets, float xTexCoord, float yTexCoord, SubMesh subMesh, ref Vector3 min, ref Vector3 max, ref float maxSquaredLength ) { Vector3 vec; unsafe { // lock the vertex buffer IntPtr data = vbuf.Lock( BufferLocking.Discard ); float* pData = (float*)data.ToPointer(); for ( int y = 0; y <= ySegments; y++ ) { for ( int x = 0; x <= xSegments; x++ ) { // centered on origin vec.x = ( x * xSpace ) - halfWidth; vec.y = ( y * ySpace ) - halfHeight; vec.z = 0.0f; vec = transform.TransformAffine( vec ); *pData++ = vec.x; *pData++ = vec.y; *pData++ = vec.z; // Build bounds as we go if ( firstTime ) { min = vec; max = vec; maxSquaredLength = vec.LengthSquared; firstTime = false; } else { min.Floor( vec ); max.Ceil( vec ); maxSquaredLength = Utility.Max( maxSquaredLength, vec.LengthSquared ); } if ( normals ) { vec = Vector3.UnitZ; vec = rotation.TransformAffine( vec ); *pData++ = vec.x; *pData++ = vec.y; *pData++ = vec.z; } for ( int i = 0; i < numTexCoordSets; i++ ) { *pData++ = x * xTexCoord; *pData++ = 1 - ( y * yTexCoord ); } // for texCoords } // for x } // for y // unlock the buffer vbuf.Unlock(); subMesh.useSharedVertices = true; } // unsafe }
/// <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> 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 var renderSystem = Root.Instance.RenderSystem; var useVertexPrograms = false; if (renderSystem != null && renderSystem.Capabilities.HasCapability(Capabilities.VertexPrograms)) { useVertexPrograms = true; } // Look for a position element var posElem = this.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.Position); if (posElem != null) { var posOldSource = posElem.Source; var vbuf = this.vertexBufferBinding.GetBuffer(posOldSource); var 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; var newRemainderDeclaration = (VertexDeclaration)this.vertexDeclaration.Clone(); if (wasSharedBuffer) { var found = false; var 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 var oldVertexCount = vbuf.VertexCount; var newVertexCount = oldVertexCount * 2; var 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 var baseSrcPtr = vbuf.Lock(BufferLocking.ReadOnly); // Point first destination pointer at the start of the new position buffer, // the other one half way along var destPtr = newPosBuffer.Lock(BufferLocking.Discard); // oldVertexCount * 3 * 4, since we are dealing with byte offsets here var dest2Ptr = destPtr + (oldVertexCount * 12); var prePosVertexSize = 0; var postPosVertexSize = 0; var 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)); var baseDestRemPtr = newRemainderBuffer.Lock(BufferLocking.Discard); var baseSrcOffset = 0; var baseDestRemOffset = 0; #if !AXIOM_SAFE_ONLY unsafe #endif { var pDest = destPtr.ToFloatPointer(); var pDest2 = dest2Ptr.ToFloatPointer(); int destCount = 0, dest2Count = 0; // Iterate over the vertices for (var v = 0; v < oldVertexCount; v++) { var pSrc = (baseSrcPtr + (posElem.Offset + baseSrcOffset)).ToFloatPointer(); // 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) { #if !AXIOM_SAFE_ONLY unsafe #endif { var decl = HardwareBufferManager.Instance.CreateVertexDeclaration(); decl.AddElement(0, 0, VertexElementType.Float1, VertexElementSemantic.Position); // Now it's time to set up the w buffer this.hardwareShadowVolWBuffer = HardwareBufferManager.Instance.CreateVertexBuffer(decl, newVertexCount, BufferUsage.StaticWriteOnly, false); // Fill the first half with 1.0, second half with 0.0 var wPtr = this.hardwareShadowVolWBuffer.Lock(BufferLocking.Discard); var pDest = wPtr.ToFloatPointer(); var destCount = 0; for (var v = 0; v < oldVertexCount; v++) { pDest[destCount++] = 1.0f; } for (var v = 0; v < oldVertexCount; v++) { pDest[destCount++] = 0.0f; } } // unsafe this.hardwareShadowVolWBuffer.Unlock(); } // if vertexPrograms short newPosBufferSource = 0; if (wasSharedBuffer) { // Get the a new buffer binding index newPosBufferSource = this.vertexBufferBinding.NextIndex; // Re-bind the old index to the remainder buffer this.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 this.vertexBufferBinding.SetBinding(newPosBufferSource, newPosBuffer); // Now, alter the vertex declaration to change the position source // and the offsets of elements using the same buffer for (var i = 0; i < this.vertexDeclaration.ElementCount; i++) { var element = this.vertexDeclaration.GetElement(i); if (element.Semantic == VertexElementSemantic.Position) { // Modify position to point at new position buffer this.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 this.vertexDeclaration.ModifyElement(i, posOldSource /* same old source */, element.Offset - posElem.Size /* less offset now */, element.Type, element.Semantic, element.Index); } } } // if posElem != null }
/// <summary> /// Utility method for extruding vertices based on a light. /// </summary> /// <remarks> /// Unfortunately, because D3D cannot handle homogenous (4D) position /// coordinates in the fixed-function pipeline (GL can, but we have to /// be cross-API), when we extrude in software we cannot extrude to /// infinity the way we do in the vertex program (by setting w to /// 0.0f). Therefore we extrude by a fixed distance, which may cause /// some problems with larger scenes. Luckily better hardware (ie /// vertex programs) can fix this. /// </remarks> /// <param name="vertexBuffer">The vertex buffer containing ONLY xyz position /// values, which must be originalVertexCount * 2 * 3 floats long.</param> /// <param name="originalVertexCount">The count of the original number of /// vertices, ie the number in the mesh, not counting the doubling /// which has already been done (by <see cref="VertexData.PrepareForShadowVolume"/>) /// to provide the extruded area of the buffer.</param> /// <param name="lightPosition"> 4D light position in object space, when w=0.0f this /// represents a directional light</param> /// <param name="extrudeDistance">The distance to extrude.</param> public static void ExtrudeVertices( HardwareVertexBuffer vertexBuffer, int originalVertexCount, Vector4 lightPosition, float extrudeDistance ) { unsafe { Debug.Assert( vertexBuffer.VertexSize == sizeof( float ) * 3, "Position buffer should contain only positions!" ); // Extrude the first area of the buffer into the second area // Lock the entire buffer for writing, even though we'll only be // updating the latter because you can't have 2 locks on the same // buffer IntPtr srcPtr = vertexBuffer.Lock( BufferLocking.Normal ); IntPtr destPtr = new IntPtr( srcPtr.ToInt64() + ( originalVertexCount * 3 * 4 ) ); float* pSrc = (float*)srcPtr.ToPointer(); float* pDest = (float*)destPtr.ToPointer(); int destCount = 0, srcCount = 0; // Assume directional light, extrusion is along light direction Vector3 extrusionDir = new Vector3( -lightPosition.x, -lightPosition.y, -lightPosition.z ); extrusionDir.Normalize(); extrusionDir *= extrudeDistance; for ( int vert = 0; vert < originalVertexCount; vert++ ) { if ( lightPosition.w != 0.0f ) { // Point light, adjust extrusionDir extrusionDir.x = pSrc[ srcCount + 0 ] - lightPosition.x; extrusionDir.y = pSrc[ srcCount + 1 ] - lightPosition.y; extrusionDir.z = pSrc[ srcCount + 2 ] - lightPosition.z; extrusionDir.Normalize(); extrusionDir *= extrudeDistance; } pDest[ destCount++ ] = pSrc[ srcCount++ ] + extrusionDir.x; pDest[ destCount++ ] = pSrc[ srcCount++ ] + extrusionDir.y; pDest[ destCount++ ] = pSrc[ srcCount++ ] + extrusionDir.z; } } vertexBuffer.Unlock(); }
public unsafe void DebugLog(Log log) { log.Write("EdgeListBuilder Log"); log.Write("-------------------"); log.Write("Number of vertex sets: {0}", vertexDataList.Count); log.Write("Number of index sets: {0}", indexDataList.Count); int i, j; // Log original vertex data for (i = 0; i < vertexDataList.Count; i++) { VertexData vData = (VertexData)vertexDataList[i]; log.Write("."); log.Write("Original vertex set {0} - vertex count {1}", i, vData.vertexCount); VertexElement posElem = vData.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.Position); HardwareVertexBuffer vbuf = vData.vertexBufferBinding.GetBuffer(posElem.Source); // lock the buffer for reading IntPtr basePtr = vbuf.Lock(BufferLocking.ReadOnly); byte *pBaseVertex = (byte *)basePtr.ToPointer(); float *pReal; for (j = 0; j < vData.vertexCount; j++) { pReal = (float *)(pBaseVertex + posElem.Offset); log.Write("Vertex {0}: ({1}, {2}, {3})", j, pReal[0], pReal[1], pReal[2]); pBaseVertex += vbuf.VertexSize; } vbuf.Unlock(); } // Log original index data for (i = 0; i < indexDataList.Count; i += 3) { IndexData iData = (IndexData)indexDataList[i]; log.Write("."); log.Write("Original triangle set {0} - index count {1} - vertex set {2})", i, iData.indexCount, indexDataVertexDataSetList[i]); // Get the indexes ready for reading short *p16Idx = null; int * p32Idx = null; IntPtr idxPtr = iData.indexBuffer.Lock(BufferLocking.ReadOnly); if (iData.indexBuffer.Type == IndexType.Size32) { p32Idx = (int *)idxPtr.ToPointer(); } else { p16Idx = (short *)idxPtr.ToPointer(); } for (j = 0; j < iData.indexCount / 3; j++) { if (iData.indexBuffer.Type == IndexType.Size32) { log.Write("Triangle {0}: ({1}, {2}, {3})", j, *p32Idx++, *p32Idx++, *p32Idx++); } else { log.Write("Triangle {0}: ({1}, {2}, {3})", j, *p16Idx++, *p16Idx++, *p16Idx++); } } iData.indexBuffer.Unlock(); // Log common vertex list log.Write("."); log.Write("Common vertex list - vertex count {0}", vertices.Count); for (i = 0; i < vertices.Count; i++) { CommonVertex c = (CommonVertex)vertices[i]; log.Write("Common vertex {0}: (vertexSet={1}, originalIndex={2}, position={3}", i, c.vertexSet, c.index, c.position); } } }
protected int CopyVertices( HardwareVertexBuffer srcBuf, BufferBase pDst, List<VertexElement> elems, QueuedGeometry geom, Vector3 regionCenter ) { #if !AXIOM_SAFE_ONLY unsafe #endif { // lock source var src = srcBuf.Lock( BufferLocking.ReadOnly ); var bufInc = srcBuf.VertexSize; var temp = Vector3.Zero; // Calculate elem sizes outside the loop var elemSizes = new int[elems.Count]; for ( var i = 0; i < elems.Count; i++ ) { elemSizes[ i ] = VertexElement.GetTypeSize( elems[ i ].Type ); } // Move the position offset calculation outside the loop var positionDelta = geom.position - regionCenter; for ( var v = 0; v < geom.geometry.vertexData.vertexCount; ++v ) { // iterate over vertex elements for ( var i = 0; i < elems.Count; i++ ) { var elem = elems[ i ]; var pSrcReal = ( src + elem.Offset ).ToFloatPointer(); var pDstReal = ( pDst + elem.Offset ).ToFloatPointer(); switch ( elem.Semantic ) { case VertexElementSemantic.Position: temp.x = pSrcReal[ 0 ]; temp.y = pSrcReal[ 1 ]; temp.z = pSrcReal[ 2 ]; // transform temp = ( geom.orientation*( temp*geom.scale ) ); pDstReal[ 0 ] = temp.x + positionDelta.x; pDstReal[ 1 ] = temp.y + positionDelta.y; pDstReal[ 2 ] = temp.z + positionDelta.z; break; case VertexElementSemantic.Normal: case VertexElementSemantic.Tangent: case VertexElementSemantic.Binormal: temp.x = pSrcReal[ 0 ]; temp.y = pSrcReal[ 1 ]; temp.z = pSrcReal[ 2 ]; // rotation only temp = geom.orientation*temp; pDstReal[ 0 ] = temp.x; pDstReal[ 1 ] = temp.y; pDstReal[ 2 ] = temp.z; break; default: // just raw copy var size = elemSizes[ i ]; // Optimize the loop for the case that // these things are in units of 4 if ( ( size & 0x3 ) == 0x3 ) { var cnt = size/4; while ( cnt-- > 0 ) { pDstReal[ cnt ] = pSrcReal[ cnt ]; } } else { // Fall back to the byte-by-byte copy var pbSrc = ( src + elem.Offset ).ToBytePointer(); var pbDst = ( pDst + elem.Offset ).ToBytePointer(); while ( size-- > 0 ) { pbDst[ size ] = pbSrc[ size ]; } } break; } } // Increment both pointers pDst.Ptr += bufInc; src.Ptr += bufInc; } srcBuf.Unlock(); return pDst.Ptr; } }
/// <summary> /// /// </summary> /// <param name="indexSet"></param> /// <param name="vertexSet"></param> protected void BuildTrianglesEdges(int indexSet, int vertexSet) { IndexData indexData = (IndexData)indexDataList[indexSet]; OperationType opType = operationTypes[indexSet]; int iterations = 0; switch (opType) { case OperationType.TriangleList: iterations = indexData.indexCount / 3; break; case OperationType.TriangleFan: case OperationType.TriangleStrip: iterations = indexData.indexCount - 2; break; } // locate postion element & the buffer to go with it VertexData vertexData = (VertexData)vertexDataList[vertexSet]; VertexElement posElem = vertexData.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.Position); HardwareVertexBuffer posBuffer = vertexData.vertexBufferBinding.GetBuffer(posElem.Source); IntPtr posPtr = posBuffer.Lock(BufferLocking.ReadOnly); IntPtr idxPtr = indexData.indexBuffer.Lock(BufferLocking.ReadOnly); unsafe { byte *pBaseVertex = (byte *)posPtr.ToPointer(); short *p16Idx = null; int * p32Idx = null; // counters used for pointer indexing int count16 = 0; int count32 = 0; if (indexData.indexBuffer.Type == IndexType.Size16) { p16Idx = (short *)idxPtr.ToPointer(); } else { p32Idx = (int *)idxPtr.ToPointer(); } float *pReal = null; int triStart = edgeData.triangles.Count; // iterate over all the groups of 3 indices edgeData.triangles.Capacity = triStart + iterations; for (int t = 0; t < iterations; t++) { EdgeData.Triangle tri = new EdgeData.Triangle(); tri.indexSet = indexSet; tri.vertexSet = vertexSet; int[] index = new int[3]; Vector3[] v = new Vector3[3]; for (int i = 0; i < 3; i++) { // Standard 3-index read for tri list or first tri in strip / fan if (opType == OperationType.TriangleList || t == 0) { if (indexData.indexBuffer.Type == IndexType.Size32) { index[i] = p32Idx[count32++]; } else { index[i] = p16Idx[count16++]; } } else { // Strips and fans are formed from last 2 indexes plus the // current one for triangles after the first if (indexData.indexBuffer.Type == IndexType.Size32) { index[i] = p32Idx[i - 2]; } else { index[i] = p16Idx[i - 2]; } // Perform single-index increment at the last tri index if (i == 2) { if (indexData.indexBuffer.Type == IndexType.Size32) { count32++; } else { count16++; } } } // populate tri original vertex index tri.vertIndex[i] = index[i]; // Retrieve the vertex position byte *pVertex = pBaseVertex + (index[i] * posBuffer.VertexSize); pReal = (float *)(pVertex + posElem.Offset); v[i].x = *pReal++; v[i].y = *pReal++; v[i].z = *pReal++; // find this vertex in the existing vertex map, or create it tri.sharedVertIndex[i] = FindOrCreateCommonVertex(v[i], vertexSet, indexSet, index[i]); } // Calculate triangle normal (NB will require recalculation for // skeletally animated meshes) tri.normal = MathUtil.CalculateFaceNormal(v[0], v[1], v[2]); // Add triangle to list edgeData.triangles.Add(tri); try { // create edges from common list if (tri.sharedVertIndex[0] < tri.sharedVertIndex[1]) { CreateEdge(vertexSet, triStart + t, tri.vertIndex[0], tri.vertIndex[1], tri.sharedVertIndex[0], tri.sharedVertIndex[1]); } if (tri.sharedVertIndex[1] < tri.sharedVertIndex[2]) { CreateEdge(vertexSet, triStart + t, tri.vertIndex[1], tri.vertIndex[2], tri.sharedVertIndex[1], tri.sharedVertIndex[2]); } if (tri.sharedVertIndex[2] < tri.sharedVertIndex[0]) { CreateEdge(vertexSet, triStart + t, tri.vertIndex[2], tri.vertIndex[0], tri.sharedVertIndex[2], tri.sharedVertIndex[0]); } } catch (Exception ex) { //Debug.WriteLine(ex.ToString()); //Debug.WriteLine(ex.StackTrace); // unlock those buffers! indexData.indexBuffer.Unlock(); posBuffer.Unlock(); throw ex; } } // for iterations } // unsafe // unlock those buffers! indexData.indexBuffer.Unlock(); posBuffer.Unlock(); }
unsafe void UpdateVertexBufferr(HardwareVertexBuffer posbuf, HardwareVertexBuffer deltabuf, Rectangle rect) { // potentially reset our bounds depending on coverage of the update ResetBounds(rect); // Main data ushort inc = (ushort)((mTerrain.Size-1) / (mVertexDataRecord.Resolution-1)); long destOffsetX = rect.Left <= mOffsetX ? 0 : (rect.Left - mOffsetX) / inc; long destOffsetY = rect.Top <= mOffsetY ? 0 : (rect.Top - mOffsetY) / inc; // Fill the buffers BufferLocking lockMode; if (destOffsetX != 0 || destOffsetY != 0|| rect.Right - rect.Left < mSize || rect.Bottom - rect.Top < mSize) { lockMode = BufferLocking.Normal; } else { lockMode = BufferLocking.Discard; } Real uvScale = 1.0 / (mTerrain.Size - 1); float* pBaseHeight = (float*)mTerrain.GetHeightData(rect.Left, rect.Top); float* pBaseDelta = (float*)mTerrain.GetDeltaData(rect.Left, rect.Top); ushort rowskip = (ushort)(mTerrain.Size * inc); ushort destPosRowSkip = 0, destDeltaRowSkip = 0; byte* pRootPosBuf = (byte*)IntPtr.Zero; byte* pRootDeltaBuf = (byte*)IntPtr.Zero; byte* pRowPosBuf = (byte*)IntPtr.Zero; byte* pRowDeltaBuf = (byte*)IntPtr.Zero; if (posbuf != null) { destPosRowSkip = (ushort)(mVertexDataRecord.Size * posbuf.VertexSize); pRootPosBuf = (byte*)(posbuf.Lock(lockMode)); pRowPosBuf = pRootPosBuf; // skip dest buffer in by left/top pRowPosBuf += destOffsetY * destPosRowSkip + destOffsetX * posbuf.VertexSize; } if (deltabuf != null) { destDeltaRowSkip = (ushort)(mVertexDataRecord.Size * deltabuf.VertexSize); pRootDeltaBuf = (byte*)(deltabuf.Lock(lockMode)); pRowDeltaBuf = pRootDeltaBuf; // skip dest buffer in by left/top pRowDeltaBuf += destOffsetY * destDeltaRowSkip + destOffsetX * deltabuf.VertexSize; } Vector3 pos = Vector3.Zero; int posIndex = 0; for (ushort y = (ushort)rect.Top; y < rect.Bottom; y += inc) { float* pHeight = pBaseHeight; float* pDelta = pBaseDelta; float* pPosBuf = (float*)(pRowPosBuf); float* pDeltaBuf = (float*)(pRowDeltaBuf); for (ushort x = (ushort)rect.Left; x < rect.Right; x += inc) { if (pPosBuf != (float*)IntPtr.Zero) { mTerrain.GetPoint(x, y, pBaseHeight[posIndex], ref pos); // Update bounds *before* making relative MergeIntoBounds(x, y, pos); // relative to local centre pos -= mLocalCentre; //pHeight += inc; posIndex += inc; *pPosBuf++ = pos.x; *pPosBuf++ = pos.y; *pPosBuf++ = pos.z; // UVs - base UVs vary from 0 to 1, all other values // will be derived using scalings *pPosBuf++ = x * uvScale; *pPosBuf++ = 1.0 - (y * uvScale); } if (pDeltaBuf != (float*)IntPtr.Zero) { // delta *pDeltaBuf++ = *pDelta; pDelta += inc; // delta LOD threshold // we want delta to apply to LODs no higher than this value // at runtime this will be combined with a per-renderable parameter // to ensure we only apply morph to the correct LOD *pDeltaBuf++ = (float)mTerrain.GetLODLevelWhenVertexEliminated(x, y) - 1.0f; } } //pBaseHeight += rowskip; posIndex += rowskip; pBaseDelta += rowskip; if (pRowPosBuf != (float*)IntPtr.Zero) pRowPosBuf += destPosRowSkip; if (pRowDeltaBuf!= (float*)IntPtr.Zero) pRowDeltaBuf += destDeltaRowSkip; } // Skirts now // skirt spacing based on top-level resolution (* inc to cope with resolution which is not the max) ushort skirtSpacing = (ushort)(mVertexDataRecord.SkirtRowColSkip * inc); Vector3 skirtOffset = Vector3.Zero; mTerrain.GetVector(0, 0, -mTerrain.SkirtSize, ref skirtOffset); // skirt rows // clamp rows to skirt spacing (round up) long skirtStartX = rect.Left; long skirtStartY = rect.Top; if (skirtStartY % skirtSpacing != 0) skirtStartY += skirtSpacing - (skirtStartY % skirtSpacing); skirtStartY = System.Math.Max(skirtStartY, (long)mOffsetY); pBaseHeight = (float*)mTerrain.GetHeightData(skirtStartX, skirtStartY); if (posbuf != null) { // position dest buffer just after the main vertex data pRowPosBuf = pRootPosBuf + posbuf.VertexSize * mVertexDataRecord.Size * mVertexDataRecord.Size; // move it onwards to skip the skirts we don't need to update pRowPosBuf += destPosRowSkip * (skirtStartY - mOffsetY) / skirtSpacing; pRowPosBuf += posbuf.VertexSize * (skirtStartX - mOffsetX); } if (deltabuf != null) { // position dest buffer just after the main vertex data pRowDeltaBuf = pRootDeltaBuf + deltabuf.VertexSize * mVertexDataRecord.Size * mVertexDataRecord.Size; // move it onwards to skip the skirts we don't need to update pRowDeltaBuf += destDeltaRowSkip * (skirtStartY - mOffsetY) / skirtSpacing; pRowDeltaBuf += deltabuf.VertexSize * (skirtStartX - mOffsetX); } for (ushort y = (ushort)skirtStartY; y < rect.Bottom; y += skirtSpacing) { float* pHeight = pBaseHeight; float* pPosBuf = (float*)(pRowPosBuf); float* pDeltaBuf = (float*)(pRowDeltaBuf); for (ushort x = (ushort)skirtStartX; x < rect.Right; x += inc) { if (pPosBuf != (float*)IntPtr.Zero) { mTerrain.GetPoint(x, y, *pHeight, ref pos); // relative to local centre pos -= mLocalCentre; pHeight += inc; pos += skirtOffset; *pPosBuf++ = pos.x; *pPosBuf++ = pos.y; *pPosBuf++ = pos.z; // UVs - same as base *pPosBuf++ = x * uvScale; *pPosBuf++ = 1.0 - (y * uvScale); } if (pDeltaBuf != (float*)IntPtr.Zero) { // delta (none) *pDeltaBuf++ = 0; // delta threshold (irrelevant) *pDeltaBuf++ = 99; } } pBaseHeight += mTerrain.Size * skirtSpacing; if (pRowPosBuf != (byte*)IntPtr.Zero) pRowPosBuf += destPosRowSkip; if (pRowDeltaBuf != (byte*)IntPtr.Zero) pRowDeltaBuf += destDeltaRowSkip; } // skirt cols // clamp cols to skirt spacing (round up) skirtStartX = rect.Left; if (skirtStartX % skirtSpacing != 0) skirtStartX += skirtSpacing - (skirtStartX % skirtSpacing); skirtStartY = rect.Top; skirtStartX = System.Math.Max(skirtStartX, (long)mOffsetX); if (posbuf != null) { // position dest buffer just after the main vertex data and skirt rows pRowPosBuf = pRootPosBuf + posbuf.VertexSize * mVertexDataRecord.Size * mVertexDataRecord.Size; // skip the row skirts pRowPosBuf += mVertexDataRecord.NumSkirtRowsCols * mVertexDataRecord.Size * posbuf.VertexSize; // move it onwards to skip the skirts we don't need to update pRowPosBuf += destPosRowSkip * (skirtStartX - mOffsetX) / skirtSpacing; pRowPosBuf += posbuf.VertexSize * (skirtStartY - mOffsetY); } if (deltabuf != null) { // Deltaition dest buffer just after the main vertex data and skirt rows pRowDeltaBuf = pRootDeltaBuf + deltabuf.VertexSize * mVertexDataRecord.Size * mVertexDataRecord.Size; // skip the row skirts pRowDeltaBuf += mVertexDataRecord.NumSkirtRowsCols * mVertexDataRecord.Size * deltabuf.VertexSize; // move it onwards to skip the skirts we don't need to update pRowDeltaBuf += destDeltaRowSkip * (skirtStartX - mOffsetX) / skirtSpacing; pRowDeltaBuf += deltabuf.VertexSize * (skirtStartY - mOffsetY); } for (ushort x = (ushort)skirtStartX; x < rect.Right; x += skirtSpacing) { float* pPosBuf = (float*)(pRowPosBuf); float* pDeltaBuf = (float*)(pRowDeltaBuf); for (ushort y = (ushort)skirtStartY; y < rect.Bottom; y += inc) { if (pPosBuf != (float*)IntPtr.Zero) { mTerrain.GetPoint(x, y, mTerrain.GetHeightAtPoint(x, y), ref pos); // relative to local centre pos -= mLocalCentre; pos += skirtOffset; *pPosBuf++ = pos.x; *pPosBuf++ = pos.y; *pPosBuf++ = pos.z; // UVs - same as base *pPosBuf++ = x * uvScale; *pPosBuf++ = 1.0 - (y * uvScale); } if (pDeltaBuf != (float*)IntPtr.Zero) { // delta (none) *pDeltaBuf++ = 0; // delta threshold (irrelevant) *pDeltaBuf++ = 99; } } if (pRowPosBuf != (byte*)IntPtr.Zero) pRowPosBuf += destPosRowSkip; if (pRowDeltaBuf != (byte*)IntPtr.Zero) pRowDeltaBuf += destDeltaRowSkip; } if (posbuf != null) posbuf.Unlock(); if (deltabuf != null) deltabuf.Unlock(); }
/// <summary> /// Fetch the data from the vBuffer into the data array /// </summary> /// <remarks>the data field that will be populated by this call should /// already have the correct dimensions</remarks> /// <param name="vBuffer">the vertex buffer with the data</param> /// <param name="vertexCount">the number of vertices</param> /// <param name="vertexSize">the size of each vertex</param> /// <param name="data">the array to fill</param> internal void GetBuffer(HardwareVertexBuffer vBuffer, int vertexCount, int vertexSize, float[,] data) { int count = data.GetLength(1); IntPtr bufData = vBuffer.Lock(BufferLocking.Discard); unsafe { float* pFloats = (float*)bufData.ToPointer(); for (int i = 0; i < vertexCount; ++i) for (int j = 0; j < count; ++j) { Debug.Assert(sizeof(float) * (i * count + j) < vertexCount * vertexSize, "Read off end of index buffer"); data[i, j] = pFloats[i * count + j]; } } // unlock the buffer vBuffer.Unlock(); }
/// <summary>Get a hardware vertex buffer version of the vertex offsets.</summary> public HardwareVertexBuffer GetHardwareVertexBuffer( int numVertices ) { if ( vertexBuffer == null ) { // Create buffer VertexDeclaration decl = HardwareBufferManager.Instance.CreateVertexDeclaration(); decl.AddElement(0, 0, VertexElementType.Float3, VertexElementSemantic.Position); vertexBuffer = HardwareBufferManager.Instance.CreateVertexBuffer( decl, numVertices, BufferUsage.StaticWriteOnly, false ); // lock the vertex buffer IntPtr ipBuf = vertexBuffer.Lock( BufferLocking.Discard ); unsafe { float* buffer = (float*)ipBuf.ToPointer(); for ( int i = 0; i < numVertices * 3; i++ ) buffer[ i ] = 0f; // Set each vertex foreach ( KeyValuePair<int, Vector3> pair in vertexOffsetMap ) { int offset = 3 * pair.Key; Vector3 v = pair.Value; buffer[ offset++ ] = v.x; buffer[ offset++ ] = v.y; buffer[ offset ] = v.z; } vertexBuffer.Unlock(); } } return vertexBuffer; }
/// <summary> /// Updates the face normals for this edge list based on (changed) /// position information, useful for animated objects. /// </summary> /// <param name="vertexSet">The vertex set we are updating.</param> /// <param name="positionBuffer">The updated position buffer, must contain ONLY xyz.</param> public void UpdateFaceNormals(int vertexSet, HardwareVertexBuffer positionBuffer) { unsafe { Debug.Assert(positionBuffer.VertexSize == sizeof(float) * 3, "Position buffer should contain only positions!"); // Lock buffer for reading IntPtr posPtr = positionBuffer.Lock(BufferLocking.ReadOnly); float* pVert = (float*)posPtr.ToPointer(); // Iterate over the triangles for (int i = 0; i < triangles.Count; i++) { Triangle t = (Triangle)triangles[i]; // Only update tris which are using this vertex set if (t.vertexSet == vertexSet) { int offset = t.vertIndex[0] * 3; Vector3 v1 = new Vector3(pVert[offset], pVert[offset + 1], pVert[offset + 2]); offset = t.vertIndex[1] * 3; Vector3 v2 = new Vector3(pVert[offset], pVert[offset + 1], pVert[offset + 2]); offset = t.vertIndex[2] * 3; Vector3 v3 = new Vector3(pVert[offset], pVert[offset + 1], pVert[offset + 2]); t.normal = MathUtil.CalculateFaceNormal(v1, v2, v3); } } } // unlock the buffer positionBuffer.Unlock(); }
private static void GenerateCurvedIllusionPlaneVertexData(HardwareVertexBuffer vertexBuffer, int ySegments, int xSegments, float xSpace, float halfWidth, float ySpace, float halfHeight, Matrix4 xform, bool firstTime, bool normals, Quaternion orientation, float cameraPosition, float sphereRadius, float uTiles, float vTiles, int numberOfTexCoordSets, ref Vector3 min, ref Vector3 max, ref float maxSquaredLength) { Vector3 vec; Vector3 norm; float sphereDistance; unsafe { // lock the vertex buffer IntPtr data = vertexBuffer.Lock(BufferLocking.Discard); float* pData = (float*)data.ToPointer(); for (int y = 0; y < ySegments + 1; ++y) { for (int x = 0; x < xSegments + 1; ++x) { // centered on origin vec.x = (x * xSpace) - halfWidth; vec.y = (y * ySpace) - halfHeight; vec.z = 0.0f; // transform by orientation and distance vec = xform * vec; // assign to geometry *pData++ = vec.x; *pData++ = vec.y; *pData++ = vec.z; // build bounds as we go if (firstTime) { min = vec; max = vec; maxSquaredLength = vec.LengthSquared; firstTime = false; } else { min.Floor(vec); max.Ceil(vec); maxSquaredLength = MathUtil.Max(maxSquaredLength, vec.LengthSquared); } if (normals) { norm = Vector3.UnitZ; norm = orientation * norm; *pData++ = vec.x; *pData++ = vec.y; *pData++ = vec.z; } // generate texture coordinates, normalize position, modify by orientation to return +y up vec = orientation.Inverse() * vec; vec.Normalize(); // find distance to sphere sphereDistance = MathUtil.Sqrt(cameraPosition * cameraPosition * (vec.y * vec.y - 1.0f) + sphereRadius * sphereRadius) - cameraPosition * vec.y; vec.x *= sphereDistance; vec.z *= sphereDistance; // use x and y on sphere as texture coordinates, tiled float s = vec.x * (0.01f * uTiles); float t = vec.z * (0.01f * vTiles); for (int i = 0; i < numberOfTexCoordSets; i++) { *pData++ = s; *pData++ = (1 - t); } } // x } // y // unlock the buffer vertexBuffer.Unlock(); } // unsafe }
private void _generateCurvedIllusionPlaneVertexData( HardwareVertexBuffer vertexBuffer, int ySegments, int xSegments, float xSpace, float halfWidth, float ySpace, float halfHeight, Matrix4 xform, bool firstTime, bool normals, Quaternion orientation, float curvature, float uTiles, float vTiles, int numberOfTexCoordSets, ref Vector3 min, ref Vector3 max, ref float maxSquaredLength ) { // Imagine a large sphere with the camera located near the top // The lower the curvature, the larger the sphere // Use the angle from viewer to the points on the plane // Credit to Aftershock for the general approach Real cameraPosition; // Camera position relative to sphere center // Derive sphere radius //Vector3 vertPos; // position relative to camera //Real sphDist; // Distance from camera to sphere along box vertex vector // Vector3 camToSph; // camera position to sphere Real sphereRadius;// Sphere radius // Actual values irrelevant, it's the relation between sphere radius and camera position that's important Real sphRadius = 100.0f; Real camDistance = 5.0f; sphereRadius = sphRadius - curvature; cameraPosition = sphereRadius - camDistance; Vector3 vec; Vector3 norm; float sphereDistance; unsafe { // lock the vertex buffer IntPtr data = vertexBuffer.Lock( BufferLocking.Discard ); float* pData = (float*)data.ToPointer(); for ( int y = 0; y < ySegments + 1; ++y ) { for ( int x = 0; x < xSegments + 1; ++x ) { // centered on origin vec.x = ( x * xSpace ) - halfWidth; vec.y = ( y * ySpace ) - halfHeight; vec.z = 0.0f; // transform by orientation and distance vec = xform * vec; // assign to geometry *pData++ = vec.x; *pData++ = vec.y; *pData++ = vec.z; // build bounds as we go if ( firstTime ) { min = vec; max = vec; maxSquaredLength = vec.LengthSquared; firstTime = false; } else { min.Floor( vec ); max.Ceil( vec ); maxSquaredLength = Utility.Max( maxSquaredLength, vec.LengthSquared ); } if ( normals ) { norm = Vector3.UnitZ; norm = orientation * norm; *pData++ = vec.x; *pData++ = vec.y; *pData++ = vec.z; } // generate texture coordinates, normalize position, modify by orientation to return +y up vec = orientation.Inverse() * vec; vec.Normalize(); // find distance to sphere sphereDistance = Utility.Sqrt( cameraPosition * cameraPosition * ( vec.y * vec.y - 1.0f ) + sphereRadius * sphereRadius ) - cameraPosition * vec.y; vec.x *= sphereDistance; vec.z *= sphereDistance; // use x and y on sphere as texture coordinates, tiled float s = vec.x * ( 0.01f * uTiles ); float t = vec.z * ( 0.01f * vTiles ); for ( int i = 0; i < numberOfTexCoordSets; i++ ) { *pData++ = s; *pData++ = ( 1 - t ); } } // x } // y // unlock the buffer vertexBuffer.Unlock(); } // unsafe }
/// <summary> /// Updates the face normals for this edge list based on (changed) /// position information, useful for animated objects. /// </summary> /// <param name="vertexSet">The vertex set we are updating.</param> /// <param name="positionBuffer">The updated position buffer, must contain ONLY xyz.</param> public void UpdateFaceNormals( int vertexSet, HardwareVertexBuffer positionBuffer ) { #if !AXIOM_SAFE_ONLY unsafe #endif { Debug.Assert( positionBuffer.VertexSize == sizeof ( float )*3, "Position buffer should contain only positions!" ); // Lock buffer for reading var posPtr = positionBuffer.Lock( BufferLocking.ReadOnly ); var pVert = posPtr.ToFloatPointer(); // Iterate over the triangles for ( var i = 0; i < this.triangles.Count; i++ ) { var t = (Triangle)this.triangles[ i ]; // Only update tris which are using this vertex set if ( t.vertexSet == vertexSet ) { var offset = t.vertIndex[ 0 ]*3; var v1 = new Vector3( pVert[ offset ], pVert[ offset + 1 ], pVert[ offset + 2 ] ); offset = t.vertIndex[ 1 ]*3; var v2 = new Vector3( pVert[ offset ], pVert[ offset + 1 ], pVert[ offset + 2 ] ); offset = t.vertIndex[ 2 ]*3; var v3 = new Vector3( pVert[ offset ], pVert[ offset + 1 ], pVert[ offset + 2 ] ); t.normal = Utility.CalculateFaceNormal( v1, v2, v3 ); } } } // unlock the buffer positionBuffer.Unlock(); }
protected void WriteGeometryVertexBuffer( BinaryWriter writer, short bindIndex, HardwareVertexBuffer vertexBuffer ) { var start_offset = writer.Seek( 0, SeekOrigin.Current ); WriteChunk( writer, MeshChunkID.GeometryVertexBuffer, 0 ); WriteShort( writer, bindIndex ); WriteShort( writer, (short)vertexBuffer.VertexSize ); var buf = vertexBuffer.Lock( BufferLocking.Discard ); try { WriteGeometryVertexBufferData( writer, vertexBuffer.Size, buf ); } finally { vertexBuffer.Unlock(); } var end_offset = writer.Seek( 0, SeekOrigin.Current ); writer.Seek( (int)start_offset, SeekOrigin.Begin ); WriteChunk( writer, MeshChunkID.GeometryVertexBuffer, (int)( end_offset - start_offset ) ); writer.Seek( (int)end_offset, SeekOrigin.Begin ); }
/// <summary> /// Performs a software vertex morph, of the kind used for /// morph animation although it can be used for other purposes. /// </summary> /// <remarks> /// This function will linearly interpolate positions between two /// source buffers, into a third buffer. /// </remarks> /// <param name="t">Parametric distance between the start and end buffer positions</param> /// <param name="b1">Vertex buffer containing VertexElementType.Float3 entries for the start positions</param> /// <param name="b2">Vertex buffer containing VertexElementType.Float3 entries for the end positions</param> /// <param name="targetVertexData"> VertexData destination; assumed to have a separate position /// buffer already bound, and the number of vertices must agree with the /// number in start and end /// </param> public static void SoftwareVertexMorph( float t, HardwareVertexBuffer b1, HardwareVertexBuffer b2, VertexData targetVertexData ) { #if !AXIOM_SAFE_ONLY unsafe #endif { var bpb1 = b1.Lock( BufferLocking.ReadOnly ).ToFloatPointer(); var pb1 = 0; var bpb2 = b2.Lock( BufferLocking.ReadOnly ).ToFloatPointer(); var pb2 = 0; var posElem = targetVertexData.vertexDeclaration.FindElementBySemantic( VertexElementSemantic.Position ); Debug.Assert( posElem != null ); var destBuf = targetVertexData.vertexBufferBinding.GetBuffer( posElem.Source ); Debug.Assert( posElem.Size == destBuf.VertexSize, "Positions must be in a buffer on their own for morphing" ); var bpdst = destBuf.Lock( BufferLocking.Discard ).ToFloatPointer(); var pdst = 0; for ( var i = 0; i < targetVertexData.vertexCount; ++i ) { // x bpdst[ pdst++ ] = bpb1[ pb1 ] + t*( bpb2[ pb2 ] - bpb1[ pb1 ] ); ++pb1; ++pb2; // y bpdst[ pdst++ ] = bpb1[ pb1 ] + t*( bpb2[ pb2 ] - bpb1[ pb1 ] ); ++pb1; ++pb2; // z bpdst[ pdst++ ] = bpb1[ pb1 ] + t*( bpb2[ pb2 ] - bpb1[ pb1 ] ); ++pb1; ++pb2; } destBuf.Unlock(); b1.Unlock(); b2.Unlock(); } }
private void ReadBuffer(HardwareVertexBuffer vBuffer, int vertexOffset, int vertexStride, int vertexCount, int vertexSize, uint[] data) { IntPtr bufData = vBuffer.Lock(BufferLocking.ReadOnly); int intOffset = vertexOffset / sizeof(int); int intStride = vertexStride / sizeof(int); Debug.Assert(vertexOffset % sizeof(int) == 0); unsafe { uint* pInts = (uint*)bufData.ToPointer(); for (int i = 0; i < vertexCount; ++i) { Debug.Assert(sizeof(int) * i < vertexCount * vertexStride, "Read off end of vertex buffer"); data[i] = pInts[i * intStride + intOffset]; } } // unlock the buffer vBuffer.Unlock(); }
private void _generateCurvedPlaneVertexData( HardwareVertexBuffer vbuf, int ySegments, int xSegments, float xSpace, float halfWidth, float ySpace, float halfHeight, Matrix4 transform, bool firstTime, bool normals, Matrix4 rotation, float curvature, int numTexCoordSets, float xTexCoord, float yTexCoord, SubMesh subMesh, ref Vector3 min, ref Vector3 max, ref float maxSquaredLength ) { Vector3 vec; unsafe { // lock the vertex buffer IntPtr data = vbuf.Lock( BufferLocking.Discard ); float* pData = (float*)data.ToPointer(); for ( int y = 0; y <= ySegments; y++ ) { for ( int x = 0; x <= xSegments; x++ ) { // centered on origin vec.x = ( x * xSpace ) - halfWidth; vec.y = ( y * ySpace ) - halfHeight; // Here's where curved plane is different from standard plane. Amazing, I know. Real diff_x = ( x - ( (Real)xSegments / 2 ) ) / (Real)xSegments; Real diff_y = ( y - ( (Real)ySegments / 2 ) ) / (Real)ySegments; Real dist = Utility.Sqrt( diff_x * diff_x + diff_y * diff_y ); vec.z = ( -Utility.Sin( ( 1 - dist ) * ( Utility.PI / 2 ) ) * curvature ) + curvature; // Transform by orientation and distance Vector3 pos = transform.TransformAffine( vec ); *pData++ = pos.x; *pData++ = pos.y; *pData++ = pos.z; // Build bounds as we go if ( firstTime ) { min = vec; max = vec; maxSquaredLength = vec.LengthSquared; firstTime = false; } else { min.Floor( vec ); max.Ceil( vec ); maxSquaredLength = Utility.Max( maxSquaredLength, vec.LengthSquared ); } if ( normals ) { // This part is kinda 'wrong' for curved planes... but curved planes are // very valuable outside sky planes, which don't typically need normals // so I'm not going to mess with it for now. // Default normal is along unit Z //vec = Vector3::UNIT_Z; // Rotate vec = rotation.TransformAffine( vec ); *pData++ = vec.x; *pData++ = vec.y; *pData++ = vec.z; } for ( int i = 0; i < numTexCoordSets; i++ ) { *pData++ = x * xTexCoord; *pData++ = 1 - ( y * yTexCoord ); } // for texCoords } // for x } // for y // unlock the buffer vbuf.Unlock(); subMesh.useSharedVertices = true; } // unsafe }
/// <summary> /// Fill a vertex buffer with the contents of a two dimensional /// float array /// </summary> /// <param name="vBuffer">HardwareVertexBuffer to populate</param> /// <param name="vertexCount">the number of vertices</param> /// <param name="vertexSize">the size of each vertex</param> /// <param name="vertexOffset">the offset (in bytes) of this element in the vertex buffer</param> /// <param name="vertexStride">the stride (in bytes) between vertices</param> /// <param name="data">the array of data to put in the buffer</param> internal static void FillBuffer( HardwareVertexBuffer vBuffer, int vertexCount, int vertexSize, int vertexOffset, int vertexStride, uint[ , ] data ) { int count = data.GetLength( 1 ); IntPtr bufData = vBuffer.Lock( BufferLocking.Discard ); int uintStride = vertexStride / sizeof( uint ); int uintOffset = vertexOffset / sizeof( uint ); unsafe { uint* pUints = (uint*) bufData.ToPointer(); for( int i = 0; i < vertexCount; ++i ) for( int j = 0; j < count; ++j ) { Debug.Assert( sizeof( uint ) * (i * uintStride + uintOffset + j) < vertexCount * vertexSize, "Wrote off end of vertex buffer" ); pUints[ i * uintStride + uintOffset + j ] = data[ i, j ]; } } // unlock the buffer vBuffer.Unlock(); }
public List <TriangleVertices> Build() { List <TriangleVertices> triangles = new List <TriangleVertices>(); try { for (int ind = 0, indexSet = 0; ind < indexDataList.Count; ind++, indexSet++) { int vertexSet = (int)indexDataVertexDataSetList[ind]; IndexData indexData = (IndexData)indexDataList[indexSet]; OperationType opType = operationTypes[indexSet]; int iterations = 0; switch (opType) { case OperationType.TriangleList: iterations = indexData.indexCount / 3; break; case OperationType.TriangleFan: case OperationType.TriangleStrip: iterations = indexData.indexCount - 2; break; } // locate position element & the buffer to go with it VertexData vertexData = (VertexData)vertexDataList[vertexSet]; VertexElement posElem = vertexData.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.Position); HardwareVertexBuffer posBuffer = vertexData.vertexBufferBinding.GetBuffer(posElem.Source); try { IntPtr posPtr = posBuffer.Lock(BufferLocking.ReadOnly); IntPtr idxPtr = indexData.indexBuffer.Lock(BufferLocking.ReadOnly); unsafe { byte *pBaseVertex = (byte *)posPtr.ToPointer(); ushort *p16Idx = null; uint * p32Idx = null; // counters used for pointer indexing int count16 = 0; int count32 = 0; if (indexData.indexBuffer.Type == IndexType.Size16) { p16Idx = (ushort *)idxPtr.ToPointer(); } else { p32Idx = (uint *)idxPtr.ToPointer(); } float *pReal = null; int triStart = triangles.Count; // iterate over all the groups of 3 indices triangles.Capacity = triStart + iterations; uint[] index = new uint[3]; for (int t = 0; t < iterations; t++) { Vector3[] v = new Vector3[3]; TriangleVertices tri = new TriangleVertices(v); bool broken = false; for (int i = 0; i < 3; i++) { // Standard 3-index read for tri list or first tri in strip / fan if (opType == OperationType.TriangleList || t == 0) { if (indexData.indexBuffer.Type == IndexType.Size32) { index[i] = p32Idx[count32++]; } else { index[i] = p16Idx[count16++]; } } else { // Strips and fans are formed from last 2 indexes plus the // current one for triangles after the first if (indexData.indexBuffer.Type == IndexType.Size32) { index[i] = p32Idx[i - 2]; } else { index[i] = p16Idx[i - 2]; } // Perform single-index increment at the last tri index if (i == 2) { if (indexData.indexBuffer.Type == IndexType.Size32) { count32++; } else { count16++; } } } // Retrieve the vertex position if (index[i] >= posBuffer.VertexCount) { log.InfoFormat("TriangleListBuilder.Build: Error: index[i] {0} is not less than posBuffer.VertexCount {1}, iteration t {2}", index[i], posBuffer.VertexCount, t); broken = true; break; } Debug.Assert(index[i] < posBuffer.VertexCount); byte *pVertex = pBaseVertex + (index[i] * posBuffer.VertexSize); pReal = (float *)(pVertex + posElem.Offset); v[i].x = *pReal++; v[i].y = *pReal++; v[i].z = *pReal++; } if (!broken) { // Put the points in in counter-clockwise order if (((v[0].x - v[2].x) * (v[1].y - v[2].y) - (v[1].x - v[2].x) * (v[0].y - v[2].y)) < 0) { // Clockwise, so reverse points 1 and 2 Vector3 tmp = v[1]; v[1] = v[2]; v[2] = tmp; } // Debug.Assert(((v[0].x - v[2].x) * (v[1].y - v[2].y) - (v[1].x - v[2].x) * (v[0].y - v[2].y)) >= 0); // Add to the list of triangles triangles.Add(new TriangleVertices(v)); } } // for iterations } // unsafe } finally { // unlock those buffers! indexData.indexBuffer.Unlock(); posBuffer.Unlock(); } } return(triangles); } catch (Exception e) { log.ErrorFormat("TriangleListBuilder.Build: Exception raised during triangle building: {0}", e.Message); triangles.Clear(); return(triangles); } }
protected unsafe byte *CopyVertices(HardwareVertexBuffer srcBuf, byte *pDst, List<VertexElement> elems, QueuedGeometry geom, Vector3 regionCenter) { // lock source IntPtr src = srcBuf.Lock(BufferLocking.ReadOnly); int bufInc = srcBuf.VertexSize; byte * pSrc = (byte *)src.ToPointer(); float * pSrcReal; float * pDstReal; Vector3 temp = Vector3.Zero; // Calculate elem sizes outside the loop int [] elemSizes = new int[elems.Count]; for (int i=0; i<elems.Count; i++) elemSizes[i] = VertexElement.GetTypeSize(elems[i].Type); // Move the position offset calculation outside the loop Vector3 positionDelta = geom.position - regionCenter; for(int v = 0; v < geom.geometry.vertexData.vertexCount; ++v) { // iterate over vertex elements for (int i=0; i<elems.Count; i++) { VertexElement elem = elems[i]; pSrcReal = (float *)(pSrc + elem.Offset); pDstReal = (float *)(pDst + elem.Offset); switch(elem.Semantic) { case VertexElementSemantic.Position: temp.x = *pSrcReal++; temp.y = *pSrcReal++; temp.z = *pSrcReal++; // transform temp = (geom.orientation * (temp * geom.scale)); *pDstReal++ = temp.x + positionDelta.x; *pDstReal++ = temp.y + positionDelta.y; *pDstReal++ = temp.z + positionDelta.z; break; case VertexElementSemantic.Normal: case VertexElementSemantic.Tangent: case VertexElementSemantic.Binormal: temp.x = *pSrcReal++; temp.y = *pSrcReal++; temp.z = *pSrcReal++; // rotation only temp = geom.orientation * temp; *pDstReal++ = temp.x; *pDstReal++ = temp.y; *pDstReal++ = temp.z; break; default: // just raw copy int size = elemSizes[i]; // Optimize the loop for the case that // these things are in units of 4 if ((size & 0x3) == 0x0) { int cnt = size / 4; while (cnt-- > 0) *pDstReal++ = *pSrcReal++; } else { // Fall back to the byte-by-byte copy byte * pbSrc = (byte*)pSrcReal; byte * pbDst = (byte*)pDstReal; while(size-- > 0) *pbDst++ = *pbSrc++; } break; } } // Increment both pointers pDst += bufInc; pSrc += bufInc; } srcBuf.Unlock(); return pDst; }
/// <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> 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 }
protected void SetPointsImpl(List <Vector3> points, List <ColorEx> colors, List <List <VertexBoneAssignment> > boneAssignments) { if (colors != null && points.Count != colors.Count) { throw new Exception("Invalid parameters to SetPoints. Point list length does not match colors list length"); } Vector3 min = Vector3.Zero; Vector3 max = Vector3.Zero; // set up vertex data vertexData = new VertexData(); // set up vertex declaration VertexDeclaration vertexDeclaration = vertexData.vertexDeclaration; int currentOffset = 0; // always need positions vertexDeclaration.AddElement(0, currentOffset, VertexElementType.Float3, VertexElementSemantic.Position); currentOffset += VertexElement.GetTypeSize(VertexElementType.Float3); int colorOffset = currentOffset / sizeof(float); if (colors != null) { vertexDeclaration.AddElement(0, currentOffset, VertexElementType.Color, VertexElementSemantic.Diffuse); currentOffset += VertexElement.GetTypeSize(VertexElementType.Color); } int boneIndexOffset = currentOffset / sizeof(float); if (boneAssignments != null) { vertexDeclaration.AddElement(0, currentOffset, VertexElementType.UByte4, VertexElementSemantic.BlendIndices); currentOffset += VertexElement.GetTypeSize(VertexElementType.UByte4); } int boneWeightOffset = currentOffset / sizeof(float); if (boneAssignments != null) { vertexDeclaration.AddElement(0, currentOffset, VertexElementType.Float4, VertexElementSemantic.BlendWeights); currentOffset += VertexElement.GetTypeSize(VertexElementType.Float4); } int stride = currentOffset / sizeof(float); vertexData.vertexCount = points.Count; // allocate vertex buffer HardwareVertexBuffer vertexBuffer = HardwareBufferManager.Instance.CreateVertexBuffer(vertexDeclaration.GetVertexSize(0), vertexData.vertexCount, BufferUsage.StaticWriteOnly); // set up the binding, one source only VertexBufferBinding binding = vertexData.vertexBufferBinding; binding.SetBinding(0, vertexBuffer); // Generate vertex data unsafe { // lock the vertex buffer IntPtr data = vertexBuffer.Lock(BufferLocking.Discard); byte * pData = (byte *)data.ToPointer(); float *pFloat = (float *)pData; uint * pInt = (uint *)pData; for (int i = 0; i < points.Count; ++i) { Vector3 vec = points[i]; // assign to geometry pFloat[stride * i] = vec.x; pFloat[stride * i + 1] = vec.y; pFloat[stride * i + 2] = vec.z; if (colors != null) { // assign to diffuse pInt[stride * i + colorOffset] = Root.Instance.RenderSystem.ConvertColor(colors[i]); } if (boneAssignments != null) { for (int j = 0; j < 4; ++j) { pData[4 * (stride * i + boneIndexOffset) + j] = (byte)(boneAssignments[i][j].boneIndex); pFloat[stride * i + boneWeightOffset + j] = boneAssignments[i][j].weight; } } } // unlock the buffer vertexBuffer.Unlock(); } // unsafe for (int i = 0; i < points.Count; ++i) { Vector3 vec = points[i]; // Also update the bounding sphere radius float len = vec.Length; if (len > boundingSphereRadius) { boundingSphereRadius = len; } // Also update the bounding box if (vec.x < min.x) { min.x = vec.x; } if (vec.y < min.y) { min.y = vec.y; } if (vec.z < min.z) { min.z = vec.z; } if (vec.x > max.x) { max.x = vec.x; } if (vec.y > max.y) { max.y = vec.y; } if (vec.z > max.z) { max.z = vec.z; } } // Set the SimpleRenderable bounding box box = new AxisAlignedBox(min, max); }