public void Build(bool stencilShadows, bool logDetails) { // Ok, here's where we transfer the vertices and indexes to the shared buffers VertexDeclaration dcl = vertexData.vertexDeclaration; VertexBufferBinding binds = vertexData.vertexBufferBinding; // create index buffer, and lock if (logDetails) { log.InfoFormat("GeometryBucket.Build: Creating index buffer indexType {0} indexData.indexCount {1}", indexType, indexData.indexCount); } indexData.indexBuffer = HardwareBufferManager.Instance.CreateIndexBuffer(indexType, indexData.indexCount, BufferUsage.StaticWriteOnly); IntPtr indexBufferIntPtr = indexData.indexBuffer.Lock(BufferLocking.Discard); // create all vertex buffers, and lock ushort b; ushort posBufferIdx = dcl.FindElementBySemantic(VertexElementSemantic.Position).Source; List <List <VertexElement> > bufferElements = new List <List <VertexElement> >(); unsafe { byte *[] destBufferPtrs = new byte *[binds.BindingCount]; for (b = 0; b < binds.BindingCount; ++b) { int vertexCount = vertexData.vertexCount; if (logDetails) { log.InfoFormat("GeometryBucket.Build b {0}, binds.BindingCount {1}, vertexCount {2}, dcl.GetVertexSize(b) {3}", b, binds.BindingCount, vertexCount, dcl.GetVertexSize(b)); } // Need to double the vertex count for the position buffer // if we're doing stencil shadows if (stencilShadows && b == posBufferIdx) { vertexCount = vertexCount * 2; if (vertexCount > maxVertexIndex) { throw new Exception("Index range exceeded when using stencil shadows, consider " + "reducing your region size or reducing poly count"); } } HardwareVertexBuffer vbuf = HardwareBufferManager.Instance.CreateVertexBuffer(dcl.GetVertexSize(b), vertexCount, BufferUsage.StaticWriteOnly); binds.SetBinding(b, vbuf); IntPtr pLock = vbuf.Lock(BufferLocking.Discard); destBufferPtrs[b] = (byte *)pLock.ToPointer(); // Pre-cache vertex elements per buffer bufferElements.Add(dcl.FindElementBySource(b)); } // iterate over the geometry items int indexOffset = 0; IEnumerator iter = queuedGeometry.GetEnumerator(); Vector3 regionCenter = parent.Parent.Parent.Center; int * pDestInt = (int *)indexBufferIntPtr.ToPointer(); ushort * pDestUShort = (ushort *)indexBufferIntPtr.ToPointer(); foreach (QueuedGeometry geom in queuedGeometry) { // copy indexes across with offset IndexData srcIdxData = geom.geometry.indexData; IntPtr srcIntPtr = srcIdxData.indexBuffer.Lock(BufferLocking.ReadOnly); if (indexType == IndexType.Size32) { int *pSrcInt = (int *)srcIntPtr.ToPointer(); for (int i = 0; i < srcIdxData.indexCount; i++) { *pDestInt++ = (*pSrcInt++) + indexOffset; } } else { ushort *pSrcUShort = (ushort *)(srcIntPtr.ToPointer()); for (int i = 0; i < srcIdxData.indexCount; i++) { *pDestUShort++ = (ushort)((*pSrcUShort++) + indexOffset); } } srcIdxData.indexBuffer.Unlock(); // Now deal with vertex buffers // we can rely on buffer counts / formats being the same VertexData srcVData = geom.geometry.vertexData; VertexBufferBinding srcBinds = srcVData.vertexBufferBinding; for (b = 0; b < binds.BindingCount; ++b) { // Iterate over vertices destBufferPtrs[b] = CopyVertices(srcBinds.GetBuffer(b), destBufferPtrs[b], bufferElements[b], geom, regionCenter); } indexOffset += geom.geometry.vertexData.vertexCount; } } // unlock everything indexData.indexBuffer.Unlock(); for (b = 0; b < binds.BindingCount; ++b) { binds.GetBuffer(b).Unlock(); } // If we're dealing with stencil shadows, copy the position data from // the early half of the buffer to the latter part if (stencilShadows) { unsafe { HardwareVertexBuffer buf = binds.GetBuffer(posBufferIdx); IntPtr src = buf.Lock(BufferLocking.Normal); byte * pSrc = (byte *)src.ToPointer(); // Point dest at second half (remember vertexcount is original count) byte *pDst = pSrc + buf.VertexSize * vertexData.vertexCount; int count = buf.VertexSize * buf.VertexCount; while (count-- > 0) { *pDst++ = *pSrc++; } buf.Unlock(); // Also set up hardware W buffer if appropriate RenderSystem rend = Root.Instance.RenderSystem; if (null != rend && rend.Caps.CheckCap(Capabilities.VertexPrograms)) { buf = HardwareBufferManager.Instance.CreateVertexBuffer(sizeof(float), vertexData.vertexCount * 2, BufferUsage.StaticWriteOnly, false); // Fill the first half with 1.0, second half with 0.0 float *pW = (float *)buf.Lock(BufferLocking.Discard).ToPointer(); for (int v = 0; v < vertexData.vertexCount; ++v) { *pW++ = 1.0f; } for (int v = 0; v < vertexData.vertexCount; ++v) { *pW++ = 0.0f; } buf.Unlock(); vertexData.hardwareShadowVolWBuffer = buf; } } } }
/// <summary> /// /// </summary> /// <param name="lockedBuffer"></param> protected unsafe void DistributeControlPoints(IntPtr lockedBuffer) { // Insert original control points into expanded mesh int uStep = 1 << uLevel; int vStep = 1 << vLevel; void * pSrc = Marshal.UnsafeAddrOfPinnedArrayElement(controlPointBuffer, 0).ToPointer(); void * pDest; int vertexSize = declaration.GetVertexSize(0); float *pSrcReal, pDestReal; int * pSrcRGBA, pDestRGBA; VertexElement elemPos = declaration.FindElementBySemantic(VertexElementSemantic.Position); VertexElement elemNorm = declaration.FindElementBySemantic(VertexElementSemantic.Normal); VertexElement elemTex0 = declaration.FindElementBySemantic(VertexElementSemantic.TexCoords, 0); VertexElement elemTex1 = declaration.FindElementBySemantic(VertexElementSemantic.TexCoords, 1); VertexElement elemDiffuse = declaration.FindElementBySemantic(VertexElementSemantic.Diffuse); for (int v = 0; v < meshHeight; v += vStep) { // set dest by v from base pDest = (void *)((byte *)(lockedBuffer.ToPointer()) + (vertexSize * meshWidth * v)); for (int u = 0; u < meshWidth; u += uStep) { // Copy Position pSrcReal = (float *)((byte *)pSrc + elemPos.Offset); pDestReal = (float *)((byte *)pDest + elemPos.Offset); *pDestReal++ = *pSrcReal++; *pDestReal++ = *pSrcReal++; *pDestReal++ = *pSrcReal++; // Copy Normals if (elemNorm != null) { pSrcReal = (float *)((byte *)pSrc + elemNorm.Offset); pDestReal = (float *)((byte *)pDest + elemNorm.Offset); *pDestReal++ = *pSrcReal++; *pDestReal++ = *pSrcReal++; *pDestReal++ = *pSrcReal++; } // Copy Diffuse if (elemDiffuse != null) { pSrcRGBA = (int *)((byte *)pSrc + elemDiffuse.Offset); pDestRGBA = (int *)((byte *)pDest + elemDiffuse.Offset); *pDestRGBA++ = *pSrcRGBA++; } // Copy texture coords if (elemTex0 != null) { pSrcReal = (float *)((byte *)pSrc + elemTex0.Offset); pDestReal = (float *)((byte *)pDest + elemTex0.Offset); for (int dim = 0; dim < VertexElement.GetTypeCount(elemTex0.Type); dim++) { *pDestReal++ = *pSrcReal++; } } if (elemTex1 != null) { pSrcReal = (float *)((byte *)pSrc + elemTex1.Offset); pDestReal = (float *)((byte *)pDest + elemTex1.Offset); for (int dim = 0; dim < VertexElement.GetTypeCount(elemTex1.Type); dim++) { *pDestReal++ = *pSrcReal++; } } // Increment source by one vertex pSrc = (void *)((byte *)(pSrc) + vertexSize); // Increment dest by 1 vertex * uStep pDest = (void *)((byte *)(pDest) + (vertexSize * uStep)); } // u } // v }
/// <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); }