public static void CopyBoneAssignments(SubMesh dst, SubMesh src, Dictionary<uint, uint> vertexIdMap) { foreach (KeyValuePair<uint, uint> vertexMapping in vertexIdMap) { if (!src.BoneAssignmentList.ContainsKey((int)vertexMapping.Key)) continue; List<VertexBoneAssignment> srcVbaList = src.BoneAssignmentList[(int)vertexMapping.Key]; foreach (VertexBoneAssignment srcVba in srcVbaList) { Debug.Assert(srcVba.vertexIndex == (int)vertexMapping.Key); VertexBoneAssignment dstVba = new VertexBoneAssignment(); dstVba.boneIndex = srcVba.boneIndex; dstVba.vertexIndex = (int)vertexMapping.Value; dstVba.weight = srcVba.weight; dst.AddBoneAssignment(dstVba); } } }
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> /// Used to generate a face list based on vertices. /// </summary> private void _tesselate2DMesh( SubMesh subMesh, int width, int height, bool doubleSided, BufferUsage indexBufferUsage, bool indexShadowBuffer ) { int vInc, uInc, v, u, iterations; int vCount, uCount; vInc = 1; v = 0; iterations = doubleSided ? 2 : 1; // setup index count subMesh.indexData.indexCount = ( width - 1 ) * ( height - 1 ) * 2 * iterations * 3; // create the index buffer using the current API subMesh.indexData.indexBuffer = HardwareBufferManager.Instance.CreateIndexBuffer( IndexType.Size16, subMesh.indexData.indexCount, indexBufferUsage, indexShadowBuffer ); short v1, v2, v3; // grab a reference for easy access HardwareIndexBuffer idxBuffer = subMesh.indexData.indexBuffer; // lock the whole index buffer IntPtr data = idxBuffer.Lock( BufferLocking.Discard ); unsafe { short* pIndex = (short*)data.ToPointer(); while ( 0 < iterations-- ) { // make tris in a zigzag pattern (strip compatible) u = 0; uInc = 1; vCount = height - 1; while ( 0 < vCount-- ) { uCount = width - 1; while ( 0 < uCount-- ) { // First Tri in cell // ----------------- v1 = (short)( ( ( v + vInc ) * width ) + u ); v2 = (short)( ( v * width ) + u ); v3 = (short)( ( ( v + vInc ) * width ) + ( u + uInc ) ); // Output indexes *pIndex++ = v1; *pIndex++ = v2; *pIndex++ = v3; // Second Tri in cell // ------------------ v1 = (short)( ( ( v + vInc ) * width ) + ( u + uInc ) ); v2 = (short)( ( v * width ) + u ); v3 = (short)( ( v * width ) + ( u + uInc ) ); // Output indexes *pIndex++ = v1; *pIndex++ = v2; *pIndex++ = v3; // Next column u += uInc; } // while uCount v += vInc; u = 0; } // while vCount v = height - 1; vInc = -vInc; } // while iterations }// unsafe // unlock the buffer idxBuffer.Unlock(); }
public void AddSubmesh(SubMesh subMesh) { subMeshes.Add(subMesh); AddSubmeshIndexData(subMesh.IndexData); foreach (IndexData indexData in subMesh.LodFaceList) AddSubmeshIndexData(indexData); }
public static void CopySubMesh(SubMesh dstSubMesh, SubMesh srcSubMesh, SubMeshData subMeshData) { dstSubMesh.OperationType = srcSubMesh.OperationType; dstSubMesh.useSharedVertices = srcSubMesh.useSharedVertices; dstSubMesh.MaterialName = srcSubMesh.MaterialName; CopyIndexData(dstSubMesh.IndexData, srcSubMesh.IndexData, subMeshData.VertexIdMap); if (!srcSubMesh.useSharedVertices) { dstSubMesh.useSharedVertices = false; dstSubMesh.vertexData = new VertexData(); CopyVertexData(dstSubMesh.VertexData, srcSubMesh.VertexData, subMeshData.VertexIdMap); CopyBoneAssignments(dstSubMesh, srcSubMesh, subMeshData.VertexIdMap); } Debug.Assert(srcSubMesh.VertexAnimationType == VertexAnimationType.None); }
/// <summary> /// Look up or calculate the geometry data to use for this SubMesh /// </summary> /// <param name="sm"> </param> /// <returns> </returns> public List<SubMeshLodGeometryLink> DetermineGeometry( SubMesh sm ) { // First, determine if we've already seen this submesh before if ( mSubMeshGeometryLookup.ContainsKey( sm ) ) { return mSubMeshGeometryLookup[ sm ]; } // Otherwise, we have to create a new one var lodList = new List<SubMeshLodGeometryLink>(); mSubMeshGeometryLookup[ sm ] = lodList; int numLods = sm.Parent.IsLodManual ? 1 : sm.Parent.LodLevelCount; lodList.Capacity = numLods; for ( int lod = 0; lod < numLods; ++lod ) { SubMeshLodGeometryLink geomLink = lodList[ lod ]; IndexData lodIndexData; if ( lod == 0 ) { lodIndexData = sm.IndexData; } else { lodIndexData = sm.LodFaceList[ lod - 1 ]; } // Can use the original mesh geometry? if ( sm.useSharedVertices ) { if ( sm.Parent.SubMeshCount == 1 ) { // Ok, this is actually our own anyway geomLink.vertexData = sm.Parent.SharedVertexData; geomLink.indexData = lodIndexData; } else { // We have to split it SplitGeometry( sm.Parent.SharedVertexData, lodIndexData, ref geomLink ); } } else { if ( lod == 0 ) { // Ok, we can use the existing geometry; should be in full // use by just this SubMesh geomLink.vertexData = sm.vertexData; geomLink.indexData = sm.indexData; } else { // We have to split it SplitGeometry( sm.vertexData, lodIndexData, ref geomLink ); } } Debug.Assert( geomLink.vertexData.vertexStart == 0, "Cannot use vertexStart > 0 on indexed geometry due to rendersystem incompatibilities - see the docs!" ); } return lodList; }
protected void WriteSubMeshTextureAliases(BinaryWriter writer, SubMesh subMesh) { LogManager.Instance.Write("Exporting submesh texture aliases..."); foreach (KeyValuePair<string, string> pair in subMesh.TextureAliases) { long start_offset = writer.Seek(0, SeekOrigin.Current); WriteChunk(writer, MeshChunkID.SubMeshTextureAlias, 0); WriteString(writer, pair.Key); WriteString(writer, pair.Value); long end_offset = writer.Seek(0, SeekOrigin.Current); writer.Seek((int)start_offset, SeekOrigin.Begin); WriteChunk(writer, MeshChunkID.SubMeshTextureAlias, (int)(end_offset - start_offset)); writer.Seek((int)end_offset, SeekOrigin.Begin); } }
/// <summary> /// This is only used if the SceneManager chooses to render the node. This option can be set /// for SceneNodes at SceneManager.DisplaySceneNodes, and for entities based on skeletal /// models using Entity.DisplaySkeleton = true. /// </summary> public void GetRenderOperation(RenderOperation op) { if(nodeSubMesh == null) { Mesh nodeMesh = MeshManager.Instance.Load("axes.mesh"); nodeSubMesh = nodeMesh.GetSubMesh(0); } // return the render operation of the submesh itself nodeSubMesh.GetRenderOperation(op); }
protected void ReadBoneAssignments(XmlNode node, SubMesh subMesh) { foreach (XmlNode childNode in node.ChildNodes) { switch (childNode.Name) { case "vertexboneassignment": ReadVertexBoneAssigment(childNode, subMesh); break; default: DebugMessage(childNode); break; } } }
/// <summary> /// Look up or calculate the geometry data to use for this SubMesh /// </summary> /// <param name="sm"> </param> /// <returns> </returns> public List <SubMeshLodGeometryLink> DetermineGeometry(SubMesh sm) { // First, determine if we've already seen this submesh before if (mSubMeshGeometryLookup.ContainsKey(sm)) { return(mSubMeshGeometryLookup[sm]); } // Otherwise, we have to create a new one var lodList = new List <SubMeshLodGeometryLink>(); mSubMeshGeometryLookup[sm] = lodList; int numLods = sm.Parent.IsLodManual ? 1 : sm.Parent.LodLevelCount; lodList.Capacity = numLods; for (int lod = 0; lod < numLods; ++lod) { SubMeshLodGeometryLink geomLink = lodList[lod]; IndexData lodIndexData; if (lod == 0) { lodIndexData = sm.IndexData; } else { lodIndexData = sm.LodFaceList[lod - 1]; } // Can use the original mesh geometry? if (sm.useSharedVertices) { if (sm.Parent.SubMeshCount == 1) { // Ok, this is actually our own anyway geomLink.vertexData = sm.Parent.SharedVertexData; geomLink.indexData = lodIndexData; } else { // We have to split it SplitGeometry(sm.Parent.SharedVertexData, lodIndexData, ref geomLink); } } else { if (lod == 0) { // Ok, we can use the existing geometry; should be in full // use by just this SubMesh geomLink.vertexData = sm.vertexData; geomLink.indexData = sm.indexData; } else { // We have to split it SplitGeometry(sm.vertexData, lodIndexData, ref geomLink); } } Debug.Assert(geomLink.vertexData.vertexStart == 0, "Cannot use vertexStart > 0 on indexed geometry due to rendersystem incompatibilities - see the docs!"); } return(lodList); }
/// <summary> /// Used to generate a face list based on vertices. /// </summary> /// <param name="subMesh"></param> /// <param name="xSegments"></param> /// <param name="ySegments"></param> /// <param name="doubleSided"></param> private void Tesselate2DMesh(SubMesh subMesh, int width, int height, bool doubleSided, BufferUsage indexBufferUsage, bool indexShadowBuffer) { int vInc, uInc, v, u, iterations; int vCount, uCount; vInc = 1; v = 0; iterations = doubleSided ? 2 : 1; // setup index count subMesh.indexData.indexCount = (width - 1) * (height - 1) * 2 * iterations * 3; // create the index buffer using the current API subMesh.indexData.indexBuffer = HardwareBufferManager.Instance.CreateIndexBuffer(IndexType.Size16, subMesh.indexData.indexCount, indexBufferUsage, indexShadowBuffer); short v1, v2, v3; // grab a reference for easy access HardwareIndexBuffer idxBuffer = subMesh.indexData.indexBuffer; // lock the whole index buffer IntPtr data = idxBuffer.Lock(BufferLocking.Discard); unsafe { short *pIndex = (short *)data.ToPointer(); while (0 < iterations--) { // make tris in a zigzag pattern (strip compatible) u = 0; uInc = 1; vCount = height - 1; while (0 < vCount--) { uCount = width - 1; while (0 < uCount--) { // First Tri in cell // ----------------- v1 = (short)(((v + vInc) * width) + u); v2 = (short)((v * width) + u); v3 = (short)(((v + vInc) * width) + (u + uInc)); // Output indexes *pIndex++ = v1; *pIndex++ = v2; *pIndex++ = v3; // Second Tri in cell // ------------------ v1 = (short)(((v + vInc) * width) + (u + uInc)); v2 = (short)((v * width) + u); v3 = (short)((v * width) + (u + uInc)); // Output indexes *pIndex++ = v1; *pIndex++ = v2; *pIndex++ = v3; // Next column u += uInc; } // while uCount v += vInc; u = 0; } // while vCount v = height - 1; vInc = -vInc; } // while iterations } // unsafe // unlock the buffer idxBuffer.Unlock(); }
/// <summary> /// /// </summary> /// <param name="name"></param> /// <param name="plane"></param> /// <param name="width"></param> /// <param name="height"></param> /// <param name="curvature"></param> /// <param name="xSegments"></param> /// <param name="ySegments"></param> /// <param name="normals"></param> /// <param name="numberOfTexCoordSets"></param> /// <param name="uTiles"></param> /// <param name="vTiles"></param> /// <param name="upVector"></param> /// <param name="orientation"></param> /// <param name="vertexBufferUsage"></param> /// <param name="indexBufferUsage"></param> /// <param name="vertexShadowBuffer"></param> /// <param name="indexShadowBuffer"></param> /// <returns></returns> public Mesh CreateCurvedIllusionPlane(string name, Plane plane, float width, float height, float curvature, int xSegments, int ySegments, bool normals, int numberOfTexCoordSets, float uTiles, float vTiles, Vector3 upVector, Quaternion orientation, BufferUsage vertexBufferUsage, BufferUsage indexBufferUsage, bool vertexShadowBuffer, bool indexShadowBuffer) { Mesh mesh = CreateManual(name); SubMesh subMesh = mesh.CreateSubMesh(name + "SubMesh"); // set up vertex data, use a single shared buffer mesh.SharedVertexData = new VertexData(); VertexData vertexData = mesh.SharedVertexData; // 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); // optional normals if (normals) { vertexDeclaration.AddElement(0, currentOffset, VertexElementType.Float3, VertexElementSemantic.Normal); currentOffset += VertexElement.GetTypeSize(VertexElementType.Float3); } for (ushort i = 0; i < numberOfTexCoordSets; i++) { // assumes 2d texture coordinates vertexDeclaration.AddElement(0, currentOffset, VertexElementType.Float2, VertexElementSemantic.TexCoords, i); currentOffset += VertexElement.GetTypeSize(VertexElementType.Float2); } vertexData.vertexCount = (xSegments + 1) * (ySegments + 1); // allocate vertex buffer HardwareVertexBuffer vertexBuffer = HardwareBufferManager.Instance.CreateVertexBuffer(vertexDeclaration.GetVertexSize(0), vertexData.vertexCount, vertexBufferUsage, vertexShadowBuffer); // set up the binding, one source only VertexBufferBinding binding = vertexData.vertexBufferBinding; binding.SetBinding(0, vertexBuffer); // work out the transform required, default orientation of plane is normal along +z, distance 0 Matrix4 xlate, xform, rot; Matrix3 rot3 = Matrix3.Identity; xlate = rot = Matrix4.Identity; // determine axes Vector3 zAxis, yAxis, xAxis; zAxis = plane.Normal; zAxis.Normalize(); yAxis = upVector; yAxis.Normalize(); xAxis = yAxis.Cross(zAxis); if (xAxis.Length == 0) { throw new AxiomException("The up vector for a plane cannot be parallel to the planes normal."); } rot3.FromAxes(xAxis, yAxis, zAxis); rot = rot3; // set up standard xform from origin xlate.Translation = plane.Normal * -plane.D; // concatenate xform = xlate * rot; // generate vertex data, imagine a large sphere with the camera located near the top, // the lower the curvature, the larger the sphere. use the angle from the viewer to the // points on the plane float cameraPosition; // camera position relative to the sphere center // derive sphere radius (unused) //float sphereDistance; // distance from the camera to the sphere along box vertex vector float sphereRadius; // actual values irrelevant, it's the relation between the sphere's radius and the camera's position which is important float SPHERE_RADIUS = 100; float CAMERA_DISTANCE = 5; sphereRadius = SPHERE_RADIUS - curvature; cameraPosition = sphereRadius - CAMERA_DISTANCE; // lock the whole buffer float xSpace = width / xSegments; float ySpace = height / ySegments; float halfWidth = width / 2; float halfHeight = height / 2; Vector3 vec = Vector3.Zero; Vector3 norm = Vector3.Zero; Vector3 min = Vector3.Zero; Vector3 max = Vector3.Zero; float maxSquaredLength = 0; bool firstTime = true; // generate vertex data GenerateCurvedIllusionPlaneVertexData(vertexBuffer, ySegments, xSegments, xSpace, halfWidth, ySpace, halfHeight, xform, firstTime, normals, orientation, cameraPosition, sphereRadius, uTiles, vTiles, numberOfTexCoordSets, ref min, ref max, ref maxSquaredLength); // generate face list subMesh.useSharedVertices = true; Tesselate2DMesh(subMesh, xSegments + 1, ySegments + 1, false, indexBufferUsage, indexShadowBuffer); // generate bounds for the mesh mesh.BoundingBox = new AxisAlignedBox(min, max); mesh.BoundingSphereRadius = MathUtil.Sqrt(maxSquaredLength); mesh.Load(); mesh.Touch(); return(mesh); }
public Mesh CreateBoneMesh(string name) { Mesh mesh = CreateManual(name); mesh.SkeletonName = name + ".skeleton"; SubMesh subMesh = mesh.CreateSubMesh("BoneSubMesh"); subMesh.useSharedVertices = true; subMesh.MaterialName = "BaseWhite"; // short[] faces = { 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 2, 1, 2, 5, 1, 5, 4, 1, 4, 3, 1, 3, 2 }; // short[] faces = { 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 2, 5, 1, 5, 2, 1, 4, 5, 1, 3, 4, 1, 2, 3 }; short[] faces = { 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 2, 1, 2, 5, 1, 5, 4, 1, 4, 3, 1, 3, 2, 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 2, 5, 1, 5, 2, 1, 4, 5, 1, 3, 4, 1, 2, 3 }; int faceCount = faces.Length / 3; // faces per bone int vertexCount = 6; // vertices per bone // set up vertex data, use a single shared buffer mesh.SharedVertexData = new VertexData(); VertexData vertexData = mesh.SharedVertexData; // 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); vertexDeclaration.AddElement(0, currentOffset, VertexElementType.Float3, VertexElementSemantic.Normal); currentOffset += VertexElement.GetTypeSize(VertexElementType.Float3); int boneCount = mesh.Skeleton.BoneCount; // I want 6 vertices per bone - exclude the root bone vertexData.vertexCount = boneCount * vertexCount; // 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); Vector3[] vertices = new Vector3[vertexData.vertexCount]; GetVertices(ref vertices, mesh.Skeleton.RootBone); // Generate vertex data unsafe { // lock the vertex buffer IntPtr data = vertexBuffer.Lock(BufferLocking.Discard); float *pData = (float *)data.ToPointer(); foreach (Vector3 vec in vertices) { // assign to geometry *pData++ = vec.x; *pData++ = vec.y; *pData++ = vec.z; // fake normals *pData++ = 0; *pData++ = 1; *pData++ = 0; } // unlock the buffer vertexBuffer.Unlock(); } // unsafe // Generate index data HardwareIndexBuffer indexBuffer = HardwareBufferManager.Instance.CreateIndexBuffer(IndexType.Size16, faces.Length * boneCount, BufferUsage.StaticWriteOnly); subMesh.indexData.indexBuffer = indexBuffer; subMesh.indexData.indexCount = faces.Length * boneCount; subMesh.indexData.indexStart = 0; for (ushort boneIndex = 0; boneIndex < mesh.Skeleton.BoneCount; ++boneIndex) { Axiom.Animating.Bone bone = mesh.Skeleton.GetBone(boneIndex); short[] tmpFaces = new short[faces.Length]; for (int tmp = 0; tmp < faces.Length; ++tmp) { tmpFaces[tmp] = (short)(faces[tmp] + vertexCount * bone.Handle); } indexBuffer.WriteData(faces.Length * bone.Handle * sizeof(short), tmpFaces.Length * sizeof(short), tmpFaces, true); } for (ushort boneIndex = 0; boneIndex < mesh.Skeleton.BoneCount; ++boneIndex) { Axiom.Animating.Bone bone = mesh.Skeleton.GetBone(boneIndex); Axiom.Animating.Bone parentBone = bone; if (bone.Parent != null) { parentBone = (Axiom.Animating.Bone)bone.Parent; } for (int vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) { Axiom.Animating.VertexBoneAssignment vba = new Axiom.Animating.VertexBoneAssignment(); // associate the base of the joint display with the bone's parent, // and the rest of the points with the bone. vba.boneIndex = parentBone.Handle; vba.weight = 1.0f; vba.vertexIndex = vertexCount * bone.Handle + vertexIndex; mesh.AddBoneAssignment(vba); } } mesh.Load(); mesh.Touch(); return(mesh); }
private static 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 * 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 = MathUtil.Max(maxSquaredLength, vec.LengthSquared); } if (normals) { vec = Vector3.UnitZ; vec = rotation * 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> /// /// </summary> /// <param name="name">Name of the plane mesh.</param> /// <param name="plane">Plane to use for distance and orientation of the mesh.</param> /// <param name="width">Width in world coordinates.</param> /// <param name="height">Height in world coordinates.</param> /// <param name="xSegments">Number of x segments for tesselation.</param> /// <param name="ySegments">Number of y segments for tesselation.</param> /// <param name="normals">If true, plane normals are created.</param> /// <param name="numTexCoordSets">Number of 2d texture coord sets to use.</param> /// <param name="uTile">Number of times the texture should be repeated in the u direction.</param> /// <param name="vTile">Number of times the texture should be repeated in the v direction.</param> /// <param name="upVec">The up direction of the plane.</param> /// <returns></returns> public Mesh CreatePlane(string name, Plane plane, float width, float height, int xSegments, int ySegments, bool normals, int numTexCoordSets, float uTile, float vTile, Vector3 upVec, BufferUsage vertexBufferUsage, BufferUsage indexBufferUsage, bool vertexShadowBuffer, bool indexShadowBuffer) { Mesh mesh = CreateManual(name); SubMesh subMesh = mesh.CreateSubMesh(name + "SubMesh"); mesh.SharedVertexData = new VertexData(); VertexData vertexData = mesh.SharedVertexData; VertexDeclaration decl = vertexData.vertexDeclaration; int currOffset = 0; // add position data decl.AddElement(0, currOffset, VertexElementType.Float3, VertexElementSemantic.Position); currOffset += VertexElement.GetTypeSize(VertexElementType.Float3); // normals are optional if (normals) { decl.AddElement(0, currOffset, VertexElementType.Float3, VertexElementSemantic.Normal); currOffset += VertexElement.GetTypeSize(VertexElementType.Float3); } // add texture coords for (ushort i = 0; i < numTexCoordSets; i++) { decl.AddElement(0, currOffset, VertexElementType.Float2, VertexElementSemantic.TexCoords, i); currOffset += VertexElement.GetTypeSize(VertexElementType.Float2); } vertexData.vertexCount = (xSegments + 1) * (ySegments + 1); // create a new vertex buffer (based on current API) HardwareVertexBuffer vbuf = HardwareBufferManager.Instance.CreateVertexBuffer(decl.GetVertexSize(0), vertexData.vertexCount, vertexBufferUsage, vertexShadowBuffer); // get a reference to the vertex buffer binding VertexBufferBinding binding = vertexData.vertexBufferBinding; // bind the first vertex buffer binding.SetBinding(0, vbuf); // transform the plane based on its plane def Matrix4 translate = Matrix4.Identity; Matrix4 transform = Matrix4.Zero; Matrix4 rotation = Matrix4.Identity; Matrix3 rot3x3 = Matrix3.Zero; Vector3 xAxis, yAxis, zAxis; zAxis = plane.Normal; zAxis.Normalize(); yAxis = upVec; yAxis.Normalize(); xAxis = yAxis.Cross(zAxis); if (xAxis.Length == 0) { throw new AxiomException("The up vector for a plane cannot be parallel to the planes normal."); } rot3x3.FromAxes(xAxis, yAxis, zAxis); rotation = rot3x3; // set up transform from origin translate.Translation = plane.Normal * -plane.D; transform = translate * rotation; float xSpace = width / xSegments; float ySpace = height / ySegments; float halfWidth = width / 2; float halfHeight = height / 2; float xTexCoord = (1.0f * uTile) / xSegments; float yTexCoord = (1.0f * vTile) / ySegments; Vector3 vec = Vector3.Zero; Vector3 min = Vector3.Zero; Vector3 max = Vector3.Zero; float maxSquaredLength = 0; bool firstTime = true; // generate vertex data GeneratePlaneVertexData(vbuf, ySegments, xSegments, xSpace, halfWidth, ySpace, halfHeight, transform, firstTime, normals, rotation, numTexCoordSets, xTexCoord, yTexCoord, subMesh, ref min, ref max, ref maxSquaredLength); // generate face list Tesselate2DMesh(subMesh, xSegments + 1, ySegments + 1, false, indexBufferUsage, indexShadowBuffer); // generate bounds for the mesh mesh.BoundingBox = new AxisAlignedBox(min, max); mesh.BoundingSphereRadius = MathUtil.Sqrt(maxSquaredLength); mesh.Load(); mesh.Touch(); return(mesh); }
protected virtual void ReadSubMeshBoneAssignment( BinaryReader reader, SubMesh sub ) { var assignment = new VertexBoneAssignment(); // read the data from the file assignment.vertexIndex = ReadInt( reader ); assignment.boneIndex = ReadUShort( reader ); assignment.weight = ReadFloat( reader ); // add the assignment to the mesh sub.AddBoneAssignment( assignment ); }
protected void WriteSubMeshOperation( BinaryWriter writer, SubMesh subMesh ) { var start_offset = writer.Seek( 0, SeekOrigin.Current ); WriteChunk( writer, MeshChunkID.SubMeshOperation, 0 ); WriteUShort( writer, (ushort)subMesh.operationType ); var end_offset = writer.Seek( 0, SeekOrigin.Current ); writer.Seek( (int)start_offset, SeekOrigin.Begin ); WriteChunk( writer, MeshChunkID.SubMeshOperation, (int)( end_offset - start_offset ) ); writer.Seek( (int)end_offset, SeekOrigin.Begin ); }
protected void ReadFaces(XmlNode node, SubMesh subMesh, IndexType indexType) { uint faceCount = uint.Parse(node.Attributes["count"].Value); int faceIndex = 0; int[,] data = new int[faceCount, 3]; foreach (XmlNode childNode in node.ChildNodes) { switch (childNode.Name) { case "face": ReadFace(childNode, data, faceIndex++); break; default: DebugMessage(childNode); break; } } int count = data.GetLength(1); subMesh.indexData.indexStart = 0; subMesh.indexData.indexCount = data.GetLength(0) * data.GetLength(1); HardwareIndexBuffer idxBuffer = null; // create the index buffer idxBuffer = HardwareBufferManager.Instance. CreateIndexBuffer( indexType, subMesh.indexData.indexCount, mesh.IndexBufferUsage, mesh.UseIndexShadowBuffer); IntPtr indices = idxBuffer.Lock(BufferLocking.Discard); if (indexType == IndexType.Size32) { // read the ints into the buffer data unsafe { int* pInts = (int*)indices.ToPointer(); for (int i = 0; i < faceCount; ++i) for (int j = 0; j < count; ++j) { Debug.Assert(i * count + j < subMesh.indexData.indexCount, "Wrote off end of index buffer"); pInts[i * count + j] = data[i, j]; } } } else { // read the shorts into the buffer data unsafe { short* pShorts = (short*)indices.ToPointer(); for (int i = 0; i < faceCount; ++i) for (int j = 0; j < count; ++j) { Debug.Assert(i * count + j < subMesh.indexData.indexCount, "Wrote off end of index buffer"); pShorts[i * count + j] = (short)data[i, j]; } } } // unlock the buffer to commit idxBuffer.Unlock(); // save the index buffer subMesh.indexData.indexBuffer = idxBuffer; }
protected void WriteSubMesh(BinaryWriter writer, SubMesh subMesh) { long start_offset = writer.Seek(0, SeekOrigin.Current); WriteChunk(writer, MeshChunkID.SubMesh, 0); WriteString(writer, subMesh.MaterialName); WriteBool(writer, subMesh.useSharedVertices); WriteUInt(writer, (uint)subMesh.indexData.indexCount); bool indexes32bit = (subMesh.indexData.indexBuffer.Type == IndexType.Size32); WriteBool(writer, indexes32bit); IntPtr buf = subMesh.indexData.indexBuffer.Lock(BufferLocking.Discard); try { if (indexes32bit) WriteInts(writer, subMesh.indexData.indexCount, buf); else WriteShorts(writer, subMesh.indexData.indexCount, buf); } finally { subMesh.indexData.indexBuffer.Unlock(); } if (!subMesh.useSharedVertices) WriteGeometry(writer, subMesh.vertexData); WriteSubMeshTextureAliases(writer, subMesh); WriteSubMeshOperation(writer, subMesh); Dictionary<int, List<VertexBoneAssignment>> weights = subMesh.BoneAssignmentList; foreach (int v in weights.Keys) { List<VertexBoneAssignment> vbaList = weights[v]; foreach (VertexBoneAssignment vba in vbaList) WriteSubMeshBoneAssignment(writer, vba); } // Write the texture alias (not currently supported) long end_offset = writer.Seek(0, SeekOrigin.Current); writer.Seek((int)start_offset, SeekOrigin.Begin); WriteChunk(writer, MeshChunkID.SubMesh, (int)(end_offset - start_offset)); writer.Seek((int)end_offset, SeekOrigin.Begin); }
protected void ReadGeometry(XmlNode node, SubMesh subMesh) { if (subMesh.useSharedVertices) throw new Exception("I don't support shared vertices"); VertexData vertexData = new VertexData(); subMesh.vertexData = vertexData; vertexData.vertexStart = 0; vertexData.vertexCount = int.Parse(node.Attributes["vertexcount"].Value); XmlVertexData xmlVertexData = new XmlVertexData(vertexData.vertexCount); // Read in the various vertex buffers for this geometry, and // consolidate them into one vertex buffer. foreach (XmlNode childNode in node.ChildNodes) { switch (childNode.Name) { case "vertexbuffer": ReadVertexBuffer(childNode, subMesh.vertexData, xmlVertexData); break; default: DebugMessage(childNode); break; } } xmlVertexDataDict[subMesh] = xmlVertexData; }
public WaterMesh( String meshName, float planeSize, int cmplx ) { // najak R-F // Assign Fields to the Initializer values this.meshName = meshName; this.size = planeSize; this.cmplx = cmplx; // Number of Rows/Columns in the Water Grid representation cmplxAdj = (float)System.Math.Pow( ( cmplx / 64f ), 1.4f ) * 2; numFaces = 2 * (int)System.Math.Pow( cmplx, 2 ); // Each square is split into 2 triangles. numVertices = (int)System.Math.Pow( ( cmplx + 1 ), 2 ); // Vertex grid is (Complexity+1) squared // Allocate and initialize space for calculated Normals vNorms = new Vector3[ cmplx + 1, cmplx + 1 ]; // vertex Normals for each grid point fNorms = new Vector3[ cmplx, cmplx, 2 ]; // face Normals for each triangle // Create mesh and submesh to represent the Water mesh = (Mesh)MeshManager.Instance.CreateManual( meshName, ResourceGroupManager.DefaultResourceGroupName, null ); subMesh = mesh.CreateSubMesh(); subMesh.useSharedVertices = false; // Construct metadata to describe the buffers associated with the water submesh subMesh.vertexData = new VertexData(); subMesh.vertexData.vertexStart = 0; subMesh.vertexData.vertexCount = numVertices; // Define local variables to point to the VertexData Properties VertexDeclaration vdecl = subMesh.vertexData.vertexDeclaration; // najak: seems like metadata VertexBufferBinding vbind = subMesh.vertexData.vertexBufferBinding; // najak: pointer to actual buffer //najak: Set metadata to describe the three vertex buffers that will be accessed. vdecl.AddElement( 0, 0, VertexElementType.Float3, VertexElementSemantic.Position ); vdecl.AddElement( 1, 0, VertexElementType.Float3, VertexElementSemantic.Normal ); vdecl.AddElement( 2, 0, VertexElementType.Float2, VertexElementSemantic.TexCoords ); // Prepare buffer for positions - todo: first attempt, slow // Create the Position Vertex Buffer and Bind it index 0 - Write Only posVBuf = HwBufMgr.CreateVertexBuffer( vdecl.Clone(0), numVertices, BufferUsage.DynamicWriteOnly ); vbind.SetBinding( 0, posVBuf ); // Prepare buffer for normals - write only // Create the Normals Buffer and Bind it to index 1 - Write only normVBuf = HwBufMgr.CreateVertexBuffer( vdecl.Clone(1), numVertices, BufferUsage.DynamicWriteOnly ); vbind.SetBinding( 1, normVBuf ); // Prepare Texture Coordinates buffer (static, written only once) // Creates a 2D buffer of 2D coordinates: (Complexity X Complexity), pairs. // Each pair indicates the normalized coordinates of the texture to map to. // (0,1.00), (0.02, 1.00), (0.04, 1.00), ... (1.00,1.00) // (0,0.98), (0.02, 0.98), (0.04, 1.00), ... (1.00,0.98) // ... // (0,0.00), (0.02, 0.00), (0.04, 0.00), ... (1.00,0.00) // This construct is simple and is used to calculate the Texture map. // Todo: Write directly to the buffer, when Axiom supports this in safe manner float[ , , ] tcBufDat = new float[ cmplx + 1, cmplx + 1, 2 ]; for ( int i = 0; i <= cmplx; i++ ) { // 2D column iterator for texture map for ( int j = 0; j <= cmplx; j++ ) { // 2D row iterator for texture map // Define the normalized(0..1) X/Y-coordinates for this element of the 2D grid tcBufDat[ i, j, 0 ] = (float)i / cmplx; tcBufDat[ i, j, 1 ] = 1.0f - ( (float)j / ( cmplx ) ); } } // Now Create the actual hardware buffer to contain the Texture Coordinate 2d map. // and Bind it to buffer index 2 tcVBuf = HwBufMgr.CreateVertexBuffer( vdecl.Clone(2), numVertices, BufferUsage.StaticWriteOnly ); tcVBuf.WriteData( 0, tcVBuf.Size, tcBufDat, true ); vbind.SetBinding( 2, tcVBuf ); // Create a Graphics Buffer on non-shared vertex indices (3 points for each triangle). // Since the water grid consist of [Complexity x Complexity] squares, each square is // split into 2 right triangles 45-90-45. That is how the water mesh is constructed. // Therefore the number of faces = 2 * Complexity * Complexity ushort[ , , ] idxBuf = new ushort[ cmplx, cmplx, 6 ]; for ( int i = 0; i < cmplx; i++ ) { // iterate the rows for ( int j = 0; j < cmplx; j++ ) { // iterate the columns // Define 4 corners of each grid ushort p0 = (ushort)( i * ( cmplx + 1 ) + j ); // top left point on square ushort p1 = (ushort)( i * ( cmplx + 1 ) + j + 1 ); // top right ushort p2 = (ushort)( ( i + 1 ) * ( cmplx + 1 ) + j ); // bottom left ushort p3 = (ushort)( ( i + 1 ) * ( cmplx + 1 ) + j + 1 ); // bottom right // Split Square Grid element into 2 adjacent triangles. idxBuf[ i, j, 0 ] = p2; idxBuf[ i, j, 1 ] = p1; idxBuf[ i, j, 2 ] = p0; // top-left triangle idxBuf[ i, j, 3 ] = p2; idxBuf[ i, j, 4 ] = p3; idxBuf[ i, j, 5 ] = p1; // bottom-right triangle } } // Copy Index Buffer to the Hardware Index Buffer HardwareIndexBuffer hdwrIdxBuf = HwBufMgr.CreateIndexBuffer( IndexType.Size16, 3 * numFaces, BufferUsage.StaticWriteOnly, true ); hdwrIdxBuf.WriteData( 0, numFaces * 3 * 2, idxBuf, true ); // Set index buffer for this submesh subMesh.indexData.indexBuffer = hdwrIdxBuf; subMesh.indexData.indexStart = 0; subMesh.indexData.indexCount = 3 * numFaces; //Prepare Vertex Position Buffers (Note: make 3, since each frame is function of previous two) vBufs = new Vector3[ 3 ][ , ]; for ( int b = 0; b < 3; b++ ) { vBufs[ b ] = new Vector3[ cmplx + 1, cmplx + 1 ]; for ( int y = 0; y <= cmplx; y++ ) { for ( int x = 0; x <= cmplx; x++ ) { vBufs[ b ][ y, x ].x = (float)( x ) / (float)( cmplx ) * (float)size; vBufs[ b ][ y, x ].y = 0; vBufs[ b ][ y, x ].z = (float)( y ) / (float)( cmplx ) * (float)size; } } } curBufNum = 0; vBuf = vBufs[ curBufNum ]; posVBuf.WriteData( 0, posVBuf.Size, vBufs[ 0 ], true ); AxisAlignedBox meshBounds = new AxisAlignedBox( new Vector3( 0, 0, 0 ), new Vector3( size, 0, size ) ); mesh.BoundingBox = meshBounds; // mesh->_setBounds(meshBounds); // najak: can't find _setBounds() mesh.Load(); mesh.Touch(); } // end WaterMesh Constructor
protected void ReadVertexBoneAssigment(XmlNode node, SubMesh subMesh) { VertexBoneAssignment assignment = new VertexBoneAssignment(); // read the data from the file assignment.vertexIndex = int.Parse(node.Attributes["vertexindex"].Value); assignment.boneIndex = ushort.Parse(node.Attributes["boneindex"].Value); ; assignment.weight = float.Parse(node.Attributes["weight"].Value); ; // add the assignment to the mesh subMesh.AddBoneAssignment(assignment); }
/// <summary> /// Creates a new <see cref="SubMesh"/> and gives it a name. /// </summary> /// <param name="name">Name of the new <see cref="SubMesh"/>.</param> /// <returns>A new <see cref="SubMesh"/> with this Mesh as its parent.</returns> public SubMesh CreateSubMesh( string name ) { var subMesh = new SubMesh(); subMesh.Name = name; // set the parent of the subMesh to us subMesh.Parent = this; // add to the list of child meshes this._subMeshList.Add( subMesh ); return subMesh; }
protected XmlElement WriteBoneAssignments(SubMesh subMesh) { return WriteBoneAssignments(subMesh.BoneAssignmentList); }
public void AddSubmeshData(SubMesh subMesh) { SubMeshData smd = null; if (!subMesh.useSharedVertices) smd = new SubMeshData(subMesh.VertexData); else { if (sharedSubMeshData == null) sharedSubMeshData = new SubMeshData(subMesh.VertexData); smd = sharedSubMeshData; } smd.AddSubmesh(subMesh); subMeshDataMap[subMesh.Name] = smd; }
protected XmlElement WriteFaces(SubMesh subMesh, IndexType indexType, bool isTriList) { XmlElement node = document.CreateElement("faces"); // Extract the hardware vertex buffer data into this array int[,] data = new int[subMesh.NumFaces, 3]; HardwareIndexBuffer idxBuffer = subMesh.indexData.indexBuffer; IntPtr indices = idxBuffer.Lock(BufferLocking.ReadOnly); if (isTriList) GetTriangleListIndices(ref data, indices, indexType, subMesh.indexData.indexCount); else GetTriangleStripOrFanIndices(ref data, indices, indexType, subMesh.indexData.indexCount); // unlock the buffer idxBuffer.Unlock(); int faceCount = data.GetLength(0); XmlAttribute attr; attr = document.CreateAttribute("count"); attr.Value = faceCount.ToString(); node.Attributes.Append(attr); if (isTriList) { for (int i = 0; i < faceCount; ++i) { XmlElement childNode = WriteFace(ref data, i); node.AppendChild(childNode); } } else { // triangle strip or fan if (faceCount != 0) { XmlElement childNode = WriteFace(ref data, 0); node.AppendChild(childNode); } for (int i = 1; i < faceCount; ++i) { XmlElement childNode = WriteNextFace(ref data, i); node.AppendChild(childNode); } } return node; }
protected XmlElement WriteGeometry(SubMesh subMesh) { XmlElement node = document.CreateElement("geometry"); XmlAttribute attr; attr = document.CreateAttribute("vertexcount"); attr.Value = subMesh.vertexData.vertexCount.ToString(); node.Attributes.Append(attr); return WriteGeometry(xmlVertexDataDict[subMesh], node); }
protected XmlElement WriteSubmesh(SubMesh subMesh) { XmlElement node = document.CreateElement("submesh"); XmlAttribute attr; attr = document.CreateAttribute("material"); attr.Value = subMesh.MaterialName; node.Attributes.Append(attr); attr = document.CreateAttribute("usesharedvertices"); attr.Value = (subMesh.useSharedVertices) ? "true" : "false"; node.Attributes.Append(attr); VertexData vertexData = (subMesh.useSharedVertices) ? mesh.SharedVertexData : subMesh.vertexData; IndexType indexType = IndexType.Size16; if (vertexData.vertexCount > short.MaxValue) indexType = IndexType.Size32; attr = document.CreateAttribute("use32bitindexes"); attr.Value = (indexType == IndexType.Size32) ? "true" : "false"; node.Attributes.Append(attr); bool isTriList = true; // TODO: Support things other than triangle lists attr = document.CreateAttribute("operationtype"); RenderOperation op = new RenderOperation(); subMesh.GetRenderOperation(op); switch (op.operationType) { case OperationType.TriangleList: attr.Value = "triangle_list"; break; case OperationType.TriangleStrip: attr.Value = "triangle_strip"; isTriList = false; break; case OperationType.TriangleFan: attr.Value = "triangle_fan"; isTriList = false; break; default: throw new AxiomException("Export of non triangle lists is not supported"); } node.Attributes.Append(attr); XmlElement childNode; childNode = WriteFaces(subMesh, indexType, isTriList); node.AppendChild(childNode); if (!subMesh.useSharedVertices) { childNode = WriteGeometry(subMesh); node.AppendChild(childNode); } if (subMesh.BoneAssignmentList.Count > 0) { childNode = WriteBoneAssignments(subMesh); node.AppendChild(childNode); } return node; }
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 }
protected XmlElement WriteSubmeshName(SubMesh subMesh, int index) { XmlElement node = document.CreateElement("submeshname"); XmlAttribute attr; attr = document.CreateAttribute("index"); attr.Value = index.ToString(); node.Attributes.Append(attr); attr = document.CreateAttribute("name"); attr.Value = subMesh.Name; node.Attributes.Append(attr); return node; }
protected virtual void ReadSubMeshOperation( BinaryReader reader, SubMesh sub ) { sub.operationType = (OperationType)ReadShort( reader ); }
protected List<SubMeshLodGeometryLink> DetermineGeometry( SubMesh sm ) { // First, determine if we've already seen this submesh before List<SubMeshLodGeometryLink> lodList; if ( this.subMeshGeometryLookup.TryGetValue( sm, out lodList ) ) { return lodList; } // Otherwise, we have to create a new one lodList = new List<SubMeshLodGeometryLink>(); this.subMeshGeometryLookup[ sm ] = lodList; var numLods = sm.Parent.IsLodManual ? 1 : sm.Parent.LodLevelCount; for ( var lod = 0; lod < numLods; ++lod ) { var geomLink = new SubMeshLodGeometryLink(); lodList.Add( geomLink ); var lodIndexData = lod == 0 ? sm.indexData : sm.LodFaceList[ lod - 1 ]; // Can use the original mesh geometry? if ( sm.useSharedVertices ) { if ( sm.Parent.SubMeshCount == 1 ) { // Ok, this is actually our own anyway geomLink.vertexData = sm.Parent.SharedVertexData; geomLink.indexData = lodIndexData; } else { // We have to split it SplitGeometry( sm.Parent.SharedVertexData, lodIndexData, geomLink ); } } else { if ( lod == 0 ) { // Ok, we can use the existing geometry; should be in full // use by just this SubMesh geomLink.vertexData = sm.VertexData; geomLink.indexData = sm.IndexData; } else { // We have to split it SplitGeometry( sm.VertexData, lodIndexData, geomLink ); } } Debug.Assert( geomLink.vertexData.vertexStart == 0, "Cannot use vertexStart > 0 on indexed geometry due to " + "rendersystem incompatibilities - see the docs!" ); } return lodList; }
protected void WriteSubMesh( BinaryWriter writer, SubMesh subMesh ) { // cache header location var start_offset = writer.Seek( 0, SeekOrigin.Current ); // Header WriteChunk( writer, MeshChunkID.SubMesh, 0 ); // Name WriteString( writer, subMesh.MaterialName ); // useSharedVertices WriteBool( writer, subMesh.useSharedVertices ); // indexCount WriteUInt( writer, (uint)subMesh.indexData.indexCount ); // indexes32bit var indexes32bit = ( subMesh.indexData.indexBuffer.Type == IndexType.Size32 ); WriteBool( writer, indexes32bit ); var buf = subMesh.indexData.indexBuffer.Lock( BufferLocking.Discard ); try { if ( indexes32bit ) { WriteInts( writer, subMesh.indexData.indexCount, buf ); } else { WriteShorts( writer, subMesh.indexData.indexCount, buf ); } } finally { subMesh.indexData.indexBuffer.Unlock(); } if ( !subMesh.useSharedVertices ) { WriteGeometry( writer, subMesh.vertexData ); } WriteSubMeshOperation( writer, subMesh ); var weights = subMesh.BoneAssignmentList; foreach ( var v in weights.Keys ) { var vbaList = weights[ v ]; foreach ( var vba in vbaList ) { WriteSubMeshBoneAssignment( writer, vba ); } } // Write the texture alias (not currently supported) var end_offset = writer.Seek( 0, SeekOrigin.Current ); writer.Seek( (int)start_offset, SeekOrigin.Begin ); WriteChunk( writer, MeshChunkID.SubMesh, (int)( end_offset - start_offset ) ); writer.Seek( (int)end_offset, SeekOrigin.Begin ); }
/// <summary> /// Iterate over the the vertices, building up a bounding /// box for the submesh. /// </summary> protected void CreateSubMeshBoundingBox(SubMesh subMesh) { AxisAlignedBox box = new AxisAlignedBox(); IndexData indexData = subMesh.indexData; int count = indexData.indexCount; IndexType type = indexData.indexBuffer.Type; VertexData vertexData = null; if (mesh.SharedVertexData != null) vertexData = mesh.SharedVertexData; else vertexData = subMesh.vertexData; VertexElement posElem = vertexData.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.Position); int offset = posElem.Offset; Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue); HardwareVertexBuffer posBuffer = vertexData.vertexBufferBinding.GetBuffer(posElem.Source); int vertexSize = posBuffer.VertexSize; try { IntPtr indexPtr = indexData.indexBuffer.Lock(BufferLocking.ReadOnly); try { IntPtr posPtr = posBuffer.Lock(BufferLocking.ReadOnly); unsafe { byte* pBaseVertex = (byte*)posPtr.ToPointer(); short* p16Idx = null; int* p32Idx = null; if (type == IndexType.Size16) p16Idx = (short*)indexPtr.ToPointer(); else p32Idx = (int*)indexPtr.ToPointer(); for (int i=0; i<count; i++) { int index = (type == IndexType.Size16 ? p16Idx[i] : p32Idx[i]); byte* pVertex = pBaseVertex + (index * vertexSize); float* pReal = (float*)(pVertex + offset); float x = *pReal++; if (x < min.x) min.x = x; if (x > max.x) max.x = x; float y = *pReal++; if (y < min.y) min.y = y; if (y > max.y) max.y = y; float z = *pReal++; if (z < min.z) min.z = z; if (z > max.z) max.z = z; } } } finally { posBuffer.Unlock(); } } finally { indexData.indexBuffer.Unlock(); } subMesh.BoundingBox = new AxisAlignedBox(min, max); }
protected void WriteMeshLodGenerated( BinaryWriter writer, SubMesh subMesh, int usageIndex ) { var start_offset = writer.Seek( 0, SeekOrigin.Current ); WriteChunk( writer, MeshChunkID.MeshLODGenerated, 0 ); var indexData = subMesh.lodFaceList[ usageIndex - 1 ]; var indexes32bit = ( indexData.indexBuffer.Type == IndexType.Size32 ); WriteInt( writer, indexData.indexCount ); WriteBool( writer, indexes32bit ); // lock the buffer var data = indexData.indexBuffer.Lock( BufferLocking.ReadOnly ); if ( indexes32bit ) { WriteInts( writer, indexData.indexCount, data ); } else { WriteShorts( writer, indexData.indexCount, data ); } indexData.indexBuffer.Unlock(); var end_offset = writer.Seek( 0, SeekOrigin.Current ); writer.Seek( (int)start_offset, SeekOrigin.Begin ); WriteChunk( writer, MeshChunkID.MeshLODGenerated, (int)( end_offset - start_offset ) ); writer.Seek( (int)end_offset, SeekOrigin.Begin ); }
private static MeshTriangle[] ExtractSubmeshTriangles( SubMesh subMesh ) { int[] vertIdx = new int[ 3 ]; Vector3[] vertPos = new Vector3[ 3 ]; VertexElement posElem = subMesh.vertexData.vertexDeclaration.FindElementBySemantic( VertexElementSemantic.Position ); HardwareVertexBuffer posBuffer = posBuffer = subMesh.vertexData.vertexBufferBinding.GetBuffer( posElem.Source ); IntPtr indexPtr = subMesh.indexData.indexBuffer.Lock( BufferLocking.ReadOnly ); IntPtr posPtr = posBuffer.Lock( BufferLocking.ReadOnly ); int posOffset = posElem.Offset / sizeof( float ); int posStride = posBuffer.VertexSize / sizeof( float ); int numFaces = subMesh.indexData.indexCount / 3; MeshTriangle[] triangles = new MeshTriangle[ numFaces ]; unsafe { int* pIdxInt32 = null; short* pIdxShort = null; float* pVPos = (float*) posPtr.ToPointer(); if( subMesh.indexData.indexBuffer.Type == IndexType.Size32 ) pIdxInt32 = (int*) indexPtr.ToPointer(); else pIdxShort = (short*) indexPtr.ToPointer(); // loop through all faces to calculate the tangents for( int n = 0; n < numFaces; n++ ) { for( int i = 0; i < 3; i++ ) { // get indices of vertices that form a polygon in the position buffer if( subMesh.indexData.indexBuffer.Type == IndexType.Size32 ) vertIdx[ i ] = pIdxInt32[ 3 * n + i ]; else vertIdx[ i ] = pIdxShort[ 3 * n + i ]; vertPos[ i ].x = pVPos[ vertIdx[ i ] * posStride + posOffset ]; vertPos[ i ].y = pVPos[ vertIdx[ i ] * posStride + posOffset + 1 ]; vertPos[ i ].z = pVPos[ vertIdx[ i ] * posStride + posOffset + 2 ]; } triangles[ n ] = new MeshTriangle( vertPos[ 0 ], vertPos[ 1 ], vertPos[ 2 ] ); } } posBuffer.Unlock(); subMesh.indexData.indexBuffer.Unlock(); if( DoLog ) { int count = triangles.Length; Log( string.Format( " extracted {0} triangles", count ) ); for( int i = 0; i < count; i++ ) Log( string.Format( " {0}", triangles[ i ].ToString() ) ); } return triangles; }