public (int VertexOffset, int VertexCount) AddVertices(ReadOnlySpan <Byte> vertexData, VertexDeclaration vertexFormat) { var count = vertexData.Length / vertexFormat.VertexStride; if (count == 0) { return(_VertexCount, 0); } if (_VertexCount == 0) { _VertexElements = vertexFormat.GetVertexElements(); _VertexStride = vertexFormat.VertexStride; } else { var thisVD = new VertexDeclaration(_VertexStride, _VertexElements); if (!thisVD.Equals(vertexFormat)) { throw new ArgumentException(nameof(vertexFormat)); } } vertexData = vertexData.Slice(0, count * _VertexStride); int offset = _VertexCount; Array.Resize(ref _VertexData, (offset + count) * _VertexStride); vertexData.CopyTo(_VertexData.AsSpan().Slice(offset * _VertexStride)); _VertexCount += count; return(offset, count); }
public static Byte[] ToXnaVertices(this IMeshPrimitiveDecoder src, VertexDeclaration decl) { var dst = new Byte[src.VertexCount * decl.VertexStride]; var elements = decl.GetVertexElements(); for (int i = 0; i < src.VertexCount; ++i) { var vrt = new VertexEncoder(dst, decl.VertexStride, i); var jw = src.GetSkinWeights(i); foreach (var e in elements) { switch (e.VertexElementUsage) { case VertexElementUsage.Position: vrt.Encode(e, src.GetPosition(i)); break; case VertexElementUsage.Normal: vrt.Encode(e, src.GetNormal(i)); break; case VertexElementUsage.Tangent: vrt.Encode(e, src.GetTangent(i)); break; case VertexElementUsage.Color: vrt.Encode(e, src.GetColor(i, e.UsageIndex)); break; case VertexElementUsage.TextureCoordinate: vrt.Encode(e, src.GetTextureCoord(i, e.UsageIndex)); break; case VertexElementUsage.BlendIndices: vrt.Encode(e, jw.Indices); break; case VertexElementUsage.BlendWeight: vrt.Encode(e, jw.Weights); break; } } } return(dst); }
private static BoundingBox?GetBoundingBox(ModelMeshPart meshPart, Matrix transform) { if (meshPart.VertexBuffer == null) { return(null); } VertexDeclaration vd = meshPart.VertexBuffer.VertexDeclaration; VertexElement[] elements = vd.GetVertexElements(); VertexElementUsage usage = VertexElementUsage.Position; Func <VertexElement, bool> elementPredicate = ve => ve.VertexElementUsage == usage && ve.VertexElementFormat == VertexElementFormat.Vector3; if (!elements.Any(elementPredicate)) { return(null); } VertexElement element = elements.First(elementPredicate); Vector3[] positions = new Vector3[meshPart.NumVertices]; meshPart.VertexBuffer.GetData((meshPart.VertexOffset * vd.VertexStride) + element.Offset, positions, 0, positions.Length, vd.VertexStride); if (positions == null) { return(null); } Vector3[] transformedPositions = new Vector3[positions.Length]; Vector3.Transform(positions, ref transform, transformedPositions); return(BoundingBox.CreateFromPoints(transformedPositions)); }
private List <VertexPositionNormalColor> _GetVerticesFromPart(ModelMeshPart part, Func <VertexPositionNormalColor[], short[], List <VertexPositionNormalColor> > prepareTriangles) { VertexDeclaration declaration = part.VertexBuffer.VertexDeclaration; VertexElement[] vertexElements = declaration.GetVertexElements(); VertexElement vertexPosition = new VertexElement(); VertexPositionNormalColor[] vertices = new VertexPositionNormalColor[part.NumVertices]; part.VertexBuffer.GetData <VertexPositionNormalColor>( part.VertexOffset * declaration.VertexStride + vertexPosition.Offset, vertices, 0, part.NumVertices, declaration.VertexStride); short[] indices = new short[part.PrimitiveCount * 3]; part.IndexBuffer.GetData <short>( part.StartIndex * 2, indices, 0, part.PrimitiveCount * 3); return(prepareTriangles(vertices, indices)); }
internal static bool AreEqual(VertexDeclaration vertexDeclarationA, VertexDeclaration vertexDeclarationB) { // Compare vertex strides. if (vertexDeclarationA.VertexStride != vertexDeclarationB.VertexStride) { return(false); } // Compare vertex element count. var vertexElementsA = vertexDeclarationA.GetVertexElements(); var vertexElementsB = vertexDeclarationB.GetVertexElements(); if (vertexElementsA.Length != vertexElementsB.Length) { return(false); } // Compare each vertex element structure. for (int j = 0; j < vertexElementsA.Length; j++) { if (vertexElementsA[j] != vertexElementsB[j]) { return(false); } } return(true); }
public static void WriteVertexDeclaration(TWXmlNode node, VertexDeclaration decl) { if (decl == null) { node.Value = "NULL"; return; } //TWXmlNode node = parentNode.CreateChildNode( name ); VertexElement[] elements = decl.GetVertexElements(); TWXmlNode elementsNode = node.CreateChildNode("Elements"); elementsNode.AddAttribute("count", elements.Length.ToString()); for (int i = 0; i < elements.Length; i++) { TWXmlNode elementNode = elementsNode.CreateChildNode("VertexElement"); elementNode.AddChildNode("Offset", elements[i].Offset.ToString()); elementNode.AddChildNode("Stream", elements[i].Stream.ToString()); elementNode.AddChildNode("UsageIndex", elements[i].UsageIndex.ToString()); elementNode.AddChildNode("VertexElementFormat", elements[i].VertexElementFormat.ToString()); elementNode.AddChildNode("VertexElementMethod", elements[i].VertexElementMethod.ToString()); elementNode.AddChildNode("VertexElementUsage", elements[i].VertexElementUsage.ToString()); } }
/// <summary> /// Extracts the model radius and center. /// </summary> /// <param name="bi">The bi.</param> /// <param name="radius">The radius.</param> /// <param name="center">The center.</param> public static void ExtractModelRadiusAndCenter(BatchInformation bi, out float radius, out Vector3 center) { // Read the format of the vertex buffer VertexDeclaration declaration = bi.VertexDeclaration; VertexElement[] vertexElements = declaration.GetVertexElements(); // Find the element that holds the position VertexElement vertexPosition = new VertexElement(); foreach (VertexElement vert in vertexElements) { if (vert.VertexElementUsage == VertexElementUsage.Position && vert.VertexElementFormat == VertexElementFormat.Vector3) { vertexPosition = vert; // There should only be one break; } } // Check the position element found is valid if (vertexPosition == null || vertexPosition.VertexElementUsage != VertexElementUsage.Position || vertexPosition.VertexElementFormat != VertexElementFormat.Vector3) { throw new Exception("Model uses unsupported vertex format!"); } // This where we store the vertices until transformed Vector3[] allVertex = new Vector3[bi.NumVertices]; // Read the vertices from the buffer in to the array bi.VertexBuffer.GetData <Vector3>( bi.BaseVertex * declaration.VertexStride + vertexPosition.Offset, allVertex, 0, bi.NumVertices, declaration.VertexStride); // Transform them based on the relative bone location and the world if provided for (int i = 0; i != allVertex.Length; ++i) { Vector3.Transform(ref allVertex[i], ref bi.ModelLocalTransformation, out allVertex[i]); } BoundingSphere bs = BoundingSphere.CreateFromPoints(allVertex); radius = bs.Radius; center = bs.Center; }
public static Vector3[] GetVertexElement(ModelMeshPart meshPart, VertexElementUsage usage) { VertexDeclaration vd = meshPart.VertexBuffer.VertexDeclaration; VertexElement[] elements = vd.GetVertexElements(); Func <VertexElement, bool> elementPredicate = ve => ve.VertexElementUsage == usage && ve.VertexElementFormat == VertexElementFormat.Vector3; if (!elements.Any(elementPredicate)) { return(null); } VertexElement element = elements.First(elementPredicate); Vector3[] vertexData = new Vector3[meshPart.NumVertices]; meshPart.VertexBuffer.GetData((meshPart.VertexOffset * vd.VertexStride) + element.Offset, vertexData, 0, vertexData.Length, vd.VertexStride); return(vertexData); }
public static void ExtractData(List <JVector> vertices, List <TriangleVertexIndices> indices, Model model) { Matrix[] bones_ = new Matrix[model.Bones.Count]; model.CopyAbsoluteBoneTransformsTo(bones_); foreach (ModelMesh modelmesh in model.Meshes) { JMatrix xform = Helper.ToJitterMatrix(bones_[modelmesh.ParentBone.Index]); foreach (ModelMeshPart meshPart in modelmesh.MeshParts) { // Before we add any more where are we starting from int offset = vertices.Count; // Read the format of the vertex buffer VertexDeclaration declaration = meshPart.VertexBuffer.VertexDeclaration; VertexElement[] vertexElements = declaration.GetVertexElements(); // Find the element that holds the position VertexElement vertexPosition = new VertexElement(); foreach (VertexElement vert in vertexElements) { if (vert.VertexElementUsage == VertexElementUsage.Position && vert.VertexElementFormat == VertexElementFormat.Vector3) { vertexPosition = vert; // There should only be one break; } } // Check the position element found is valid if (vertexPosition == null || vertexPosition.VertexElementUsage != VertexElementUsage.Position || vertexPosition.VertexElementFormat != VertexElementFormat.Vector3) { throw new Exception("Model uses unsupported vertex format!"); } // This where we store the vertices until transformed JVector[] allVertex = new JVector[meshPart.NumVertices]; // Read the vertices from the buffer in to the array meshPart.VertexBuffer.GetData <JVector>( meshPart.VertexOffset * declaration.VertexStride + vertexPosition.Offset, allVertex, 0, meshPart.NumVertices, declaration.VertexStride); // Transform them based on the relative bone location and the world if provided for (int i = 0; i != allVertex.Length; ++i) { JVector.Transform(ref allVertex[i], ref xform, out allVertex[i]); } // Store the transformed vertices with those from all the other meshes in this model vertices.AddRange(allVertex); // Find out which vertices make up which triangles if (meshPart.IndexBuffer.IndexElementSize != IndexElementSize.SixteenBits) { // This could probably be handled by using int in place of short but is unnecessary throw new Exception("Model uses 32-bit indices, which are not supported."); } // Each primitive is a triangle short[] indexElements = new short[meshPart.PrimitiveCount * 3]; meshPart.IndexBuffer.GetData <short>( meshPart.StartIndex * 2, indexElements, 0, meshPart.PrimitiveCount * 3); // Each TriangleVertexIndices holds the three indexes to each vertex that makes up a triangle TriangleVertexIndices[] tvi = new TriangleVertexIndices[meshPart.PrimitiveCount]; for (int i = 0; i != tvi.Length; ++i) { // The offset is because we are storing them all in the one array and the // vertices were added to the end of the array. tvi[i].I0 = indexElements[i * 3 + 0] + offset; tvi[i].I1 = indexElements[i * 3 + 1] + offset; tvi[i].I2 = indexElements[i * 3 + 2] + offset; } // Store our triangles indices.AddRange(tvi); } } }
/// <summary> /// Get all the triangles from each mesh part (Changed for XNA 4) /// </summary> public void ExtractModelMeshPartData(ModelMeshPart meshPart, ref Matrix transform, StreamWriter writer) { List <Vector3> vertices = new List <Vector3>(); List <int> indices = new List <int>(); // Before we add any more where are we starting from int offset = 0; // == Vertices (Changed for XNA 4.0) // Read the format of the vertex buffer VertexDeclaration declaration = meshPart.VertexBuffer.VertexDeclaration; VertexElement[] vertexElements = declaration.GetVertexElements(); // Find the element that holds the position VertexElement vertexPosition = new VertexElement(); foreach (VertexElement vert in vertexElements) { if (vert.VertexElementUsage == VertexElementUsage.Position && vert.VertexElementFormat == VertexElementFormat.Vector3) { vertexPosition = vert; // There should only be one break; } } // Check the position element found is valid if (vertexPosition == null || vertexPosition.VertexElementUsage != VertexElementUsage.Position || vertexPosition.VertexElementFormat != VertexElementFormat.Vector3) { throw new Exception("Model uses unsupported vertex format!"); } // This where we store the vertices until transformed Vector3[] allVertex = new Vector3[meshPart.NumVertices]; // Read the vertices from the buffer in to the array meshPart.VertexBuffer.GetData <Vector3>( meshPart.VertexOffset * declaration.VertexStride + vertexPosition.Offset, allVertex, 0, meshPart.NumVertices, declaration.VertexStride); // Transform them based on the relative bone location and the world if provided for (int i = 0; i != allVertex.Length; ++i) { Vector3.Transform(ref allVertex[i], ref transform, out allVertex[i]); } // Store the transformed vertices with those from all the other meshes in this model vertices.AddRange(allVertex); // == Indices (Changed for XNA 4) // Find out which vertices make up which triangles if (meshPart.IndexBuffer.IndexElementSize != IndexElementSize.SixteenBits) { // This could probably be handled by using int in place of short but is unnecessary throw new Exception("Model uses 32-bit indices, which are not supported."); } // Each primitive is a triangle short[] indexElements = new short[meshPart.PrimitiveCount * 3]; meshPart.IndexBuffer.GetData <short>( meshPart.StartIndex * 2, indexElements, 0, meshPart.PrimitiveCount * 3); // Each TriangleVertexIndices holds the three indexes to each vertex that makes up a triangle //TriangleVertexIndices[] tvi = new TriangleVertexIndices[meshPart.PrimitiveCount]; for (int i = 0; i != meshPart.PrimitiveCount; ++i) { // The offset is because we are storing them all in the one array and the // vertices were added to the end of the array. indices.Add(indexElements[i * 3 + 0] + offset); //tvi[i].B = indexElements[i * 3 + 1] + offset; //tvi[i].C = indexElements[i * 3 + 2] + offset; } // Store our triangles //indices.AddRange(tvi); for (int i = 0; i < indices.Count - 3; i += 3) { Matrix correctionMatrix = Matrix.CreateRotationX(MathHelper.PiOver2); Vector3 Pt1 = vertices[i]; Vector3 Pt2 = vertices[i + 1]; Vector3 Pt3 = vertices[i + 2]; Vector3 Normal = Vector3.Cross(Pt2, Pt1); Normal.Normalize(); //Normal.Normalize(); writer.WriteLine(string.Format("facet normal {0} {1} {2}", Normal.X, Normal.Y, Normal.Z)); writer.WriteLine("outer loop"); writer.WriteLine(string.Format("vertex {0} {1} {2}", Pt1.X, Pt1.Y, Pt1.Z)); writer.WriteLine(string.Format("vertex {0} {1} {2}", Pt2.X, Pt2.Y, Pt2.Z)); writer.WriteLine(string.Format("vertex {0} {1} {2}", Pt3.X, Pt3.Y, Pt3.Z)); writer.WriteLine("endloop"); writer.WriteLine("endfacet"); } }
/// <summary> /// Calculates the minimum bounding box that fits this model. /// </summary> protected virtual void CalculateMinimumBoundingBox() { bool needTransform = false; int baseVertex = 0; foreach (ModelMesh modelMesh in mesh) { if (transforms != null) { needTransform = !transforms[modelMesh.ParentBone.Index].Equals(Matrix.Identity); } foreach (ModelMeshPart part in modelMesh.MeshParts) { baseVertex = vertices.Count; Vector3[] data = new Vector3[part.NumVertices]; // Read the format of the vertex buffer VertexDeclaration declaration = part.VertexBuffer.VertexDeclaration; VertexElement[] vertexElements = declaration.GetVertexElements(); // Find the element that holds the position VertexElement vertexPosition = new VertexElement(); foreach (VertexElement vert in vertexElements) { if (vert.VertexElementUsage == VertexElementUsage.Position && vert.VertexElementFormat == VertexElementFormat.Vector3) { vertexPosition = vert; // There should only be one break; } } // Check the position element found is valid if (vertexPosition == null || vertexPosition.VertexElementUsage != VertexElementUsage.Position || vertexPosition.VertexElementFormat != VertexElementFormat.Vector3) { throw new Exception("Model uses unsupported vertex format!"); } part.VertexBuffer.GetData <Vector3>(part.VertexOffset * declaration.VertexStride + vertexPosition.Offset, data, 0, part.NumVertices, declaration.VertexStride); if (needTransform) { Matrix transform = transforms[modelMesh.ParentBone.Index]; for (int ndx = 0; ndx < data.Length; ndx++) { Vector3.Transform(ref data[ndx], ref transform, out data[ndx]); } } vertices.AddRange(data); int[] tmpIndices = new int[part.PrimitiveCount * 3]; if (part.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) { ushort[] tmp = new ushort[part.PrimitiveCount * 3]; part.IndexBuffer.GetData <ushort>(part.StartIndex * 2, tmp, 0, tmp.Length); Array.Copy(tmp, 0, tmpIndices, 0, tmpIndices.Length); } else { part.IndexBuffer.GetData <int>(part.StartIndex * 2, tmpIndices, 0, tmpIndices.Length); } if (baseVertex != 0) { for (int i = 0; i < tmpIndices.Length; i++) { tmpIndices[i] += baseVertex; } } indices.AddRange(tmpIndices); } } if (vertices.Count == 0) { throw new GoblinException("Corrupted model vertices. Failed to calculate MBB."); } else { boundingBox = BoundingBox.CreateFromPoints(vertices); boundingSphere = BoundingSphere.CreateFromPoints(vertices); if (offsetTransform.Equals(Matrix.Identity)) { offsetTransform.Translation = (boundingBox.Min + boundingBox.Max) / 2; } boundingBoxCalculated = true; } }
private static void TransformVertices(byte[] buffer, int startVertex, int vertexCount, VertexDeclaration vertexDeclaration, Vector3 scale, Pose pose) { // If the transform does not have a scale/rotation/translation, we can abort. bool hasScale = Vector3.AreNumericallyEqual(scale, Vector3.One); if (!pose.HasRotation && !pose.HasTranslation && !hasScale) return; Matrix world = pose; world.M11 *= scale.X; world.M12 *= scale.X; world.M13 *= scale.X; world.M21 *= scale.Y; world.M22 *= scale.Y; world.M23 *= scale.Y; world.M31 *= scale.Z; world.M32 *= scale.Z; world.M33 *= scale.Z; Matrix worldInverseTranspose; bool scaleIsUniform = (Numeric.AreEqual(scale.X, scale.Y) && Numeric.AreEqual(scale.Y, scale.Z)); if (scaleIsUniform) { // With a uniform scale we can use the normal world matrix to transform normals. worldInverseTranspose = world; } else { // We have to transform normals using the inverse transposed matrix. Matrix.Invert(ref world, out worldInverseTranspose); Matrix.Transpose(ref worldInverseTranspose, out worldInverseTranspose); } int vertexStride = vertexDeclaration.VertexStride; unsafe { fixed (byte* pBuffer = buffer) { foreach (var element in vertexDeclaration.GetVertexElements()) { if (element.UsageIndex > 0) continue; var usage = element.VertexElementUsage; if (usage != VertexElementUsage.Position && usage != VertexElementUsage.Normal && usage != VertexElementUsage.Tangent && usage != VertexElementUsage.Binormal) { continue; } if (element.VertexElementFormat != VertexElementFormat.Vector3) throw new NotSupportedException( "Cannot merge meshes. Vertex elements with the semantic Position, Normal, Tangent or Binormal must use format Vector3."); int offset = element.Offset; if (usage == VertexElementUsage.Position) { for (int i = 0; i < vertexCount; i++) { Vector3* pVector3 = (Vector3*)(pBuffer + (startVertex + i) * vertexStride + offset); Vector3.Transform(ref *pVector3, ref world, out *pVector3); } } else { for (int i = 0; i < vertexCount; i++) { Vector3* pVector3 = (Vector3*)(pBuffer + (startVertex + i) * vertexStride + offset); Vector3.TransformNormal(ref *pVector3, ref worldInverseTranspose, out *pVector3); (*pVector3).Normalize(); } } } } } #else using (var stream = new MemoryStream(buffer)) using (var reader = new BinaryReader(stream)) using (var writer = new BinaryWriter(stream)) { foreach (var element in vertexDeclaration.GetVertexElements()) { if (element.UsageIndex > 0) continue; var usage = element.VertexElementUsage; if (usage != VertexElementUsage.Position && usage != VertexElementUsage.Normal && usage != VertexElementUsage.Tangent && usage != VertexElementUsage.Binormal) { continue; } if (element.VertexElementFormat != VertexElementFormat.Vector3) throw new NotSupportedException( "Cannot merge meshes. Vertex elements with the semantic Position, Normal, Tangent or Binormal must use format Vector3."); int offset = element.Offset; if (usage == VertexElementUsage.Position) { for (int i = 0; i < vertexCount; i++) { int startIndex = (startVertex + i) * vertexStride + offset; Vector3 vector3 = ReadVector3(reader, startIndex); Vector3.Transform(ref vector3, ref world, out vector3); WriteVector3(writer, vector3, startIndex); } } else { for (int i = 0; i < vertexCount; i++) { int startIndex = (startVertex + i) * vertexStride + offset; Vector3 vector3 = ReadVector3(reader, startIndex); Vector3.TransformNormal(ref vector3, ref worldInverseTranspose, out vector3); vector3.Normalize(); WriteVector3(writer, vector3, startIndex); } } } } }
/// <summary> /// Calculates the bounding box that contains the sum of the bounding boxes /// of the model node's mesh parts /// </summary> public BoundingBox CreateBoundingBox2(Model mModel) { // no model, no bounds if (mModel == null) { return(new BoundingBox()); } // NOTE: we could use ModelMesh's built in BoundingSphere property // to create a bounding box with BoundingBox.CreateFromSphere, // but the source spheres are already overestimates and // box from sphere is an additional overestimate, resulting // in an unnecessarily huge bounding box // assume the worst case ;) Vector3 min = Vector3.One * float.MaxValue; Vector3 max = Vector3.One * float.MinValue; foreach (ModelMesh mesh in mModel.Meshes) { // grab the raw vertex data from the mesh's vertex buffer byte[] data = new byte[mesh.VertexBuffer.SizeInBytes]; mesh.VertexBuffer.GetData(data); // iterate over each part, comparing all vertex positions with // the current min and max, updating as necessary foreach (ModelMeshPart part in mesh.MeshParts) { VertexDeclaration decl = part.VertexDeclaration; VertexElement[] elem = decl.GetVertexElements(); int stride = decl.GetVertexStrideSize(0); // find the vertex stream offset of the vertex data's position element short pos_at = -1; for (int i = 0; i < elem.Length; ++i) { // not interested... if (elem[i].VertexElementUsage != VertexElementUsage.Position) { continue; } // save the offset pos_at = elem[i].Offset; break; } // didn't find the position element... not good if (pos_at == -1) { throw new Exception("No position data?!?!"); } // decode the position of each vertex in the stream and // compare its value to the min/max of the bounding box for (int i = 0; i < data.Length; i += stride) { int ind = i + pos_at; int fs = sizeof(float); float x = BitConverter.ToSingle(data, ind); float y = BitConverter.ToSingle(data, ind + fs); float z = BitConverter.ToSingle(data, ind + (fs * 2)); // if position is outside bounding box, then update the // bounding box min/max to fit it if (x < min.X) { min.X = x; } if (x > max.X) { max.X = x; } if (y < min.Y) { min.Y = y; } if (y > max.Y) { max.Y = y; } if (z < min.Z) { min.Z = z; } if (z > max.Z) { max.Z = z; } } } } // update bounds return(new BoundingBox(min, max)); }
/// <summary> /// Helper to get the vertex and index List from the model. /// </summary> /// <param name="vert">The vert.</param> /// <param name="ind">The ind.</param> /// <param name="model">The model.</param> private void ExtractData(ref Vector3[] vert, ref int[] ind, IModelo model) { List <Vector3> vertices = new List <Vector3>(); List <int> indices = new List <int>(); for (int i = 0; i < model.MeshNumber; i++) { BatchInformation[] bi = model.GetBatchInformation(i); for (int j = 0; j < bi.Length; j++) { BatchInformation info = bi[j]; int offset = vertices.Count; Vector3[] a = new Vector3[info.NumVertices]; // Read the format of the vertex buffer VertexDeclaration declaration = bi[j].VertexBuffer.VertexDeclaration; VertexElement[] vertexElements = declaration.GetVertexElements(); // Find the element that holds the position VertexElement vertexPosition = new VertexElement(); foreach (VertexElement elem in vertexElements) { if (elem.VertexElementUsage == VertexElementUsage.Position && elem.VertexElementFormat == VertexElementFormat.Vector3) { vertexPosition = elem; // There should only be one break; } } // Check the position element found is valid if (vertexPosition == null || vertexPosition.VertexElementUsage != VertexElementUsage.Position || vertexPosition.VertexElementFormat != VertexElementFormat.Vector3) { throw new Exception("Model uses unsupported vertex format!"); } // This where we store the vertices until transformed // Read the vertices from the buffer in to the array bi[j].VertexBuffer.GetData <Vector3>( bi[j].BaseVertex * declaration.VertexStride + vertexPosition.Offset, a, 0, bi[j].NumVertices, declaration.VertexStride); for (int k = 0; k != a.Length; ++k) { Vector3.Transform(ref a[k], ref info.ModelLocalTransformation, out a[k]); } vertices.AddRange(a); if (info.IndexBuffer.IndexElementSize != IndexElementSize.SixteenBits) { int[] s = new int[info.PrimitiveCount * 3]; info.IndexBuffer.GetData <int>(info.StartIndex * 2, s, 0, info.PrimitiveCount * 3); for (int k = 0; k != info.PrimitiveCount; ++k) { indices.Add(s[k * 3 + 2] + offset); indices.Add(s[k * 3 + 1] + offset); indices.Add(s[k * 3 + 0] + offset); } } else { short[] s = new short[info.PrimitiveCount * 3]; info.IndexBuffer.GetData <short>(info.StartIndex * 2, s, 0, info.PrimitiveCount * 3); for (int k = 0; k != info.PrimitiveCount; ++k) { indices.Add(s[k * 3 + 2] + offset); indices.Add(s[k * 3 + 1] + offset); indices.Add(s[k * 3 + 0] + offset); } } } } ind = indices.ToArray(); vert = vertices.ToArray(); }
public void ExtractModelMeshPartData(ModelMeshPart meshPart, ref Matrix transform, List <Vector3> vertices, List <Display3D.TriangleVertexIndices> indices) { int offset = vertices.Count; /* Vertices */ VertexDeclaration declaration = meshPart.VertexBuffer.VertexDeclaration; VertexElement[] vertexElements = declaration.GetVertexElements(); VertexElement vertexPosition = new VertexElement(); foreach (VertexElement vert in vertexElements) { if (vert.VertexElementUsage == VertexElementUsage.Position && vert.VertexElementFormat == VertexElementFormat.Vector3) { vertexPosition = vert; break; } } if (vertexPosition == null || vertexPosition.VertexElementUsage != VertexElementUsage.Position || vertexPosition.VertexElementFormat != VertexElementFormat.Vector3) { throw new Exception("Model uses unsupported vertex format!"); } Vector3[] allVertex = new Vector3[meshPart.NumVertices]; meshPart.VertexBuffer.GetData <Vector3>( meshPart.VertexOffset * declaration.VertexStride + vertexPosition.Offset, allVertex, 0, meshPart.NumVertices, declaration.VertexStride); for (int i = 0; i != allVertex.Length; ++i) { Vector3.Transform(ref allVertex[i], ref transform, out allVertex[i]); } vertices.AddRange(allVertex); /* Indices */ if (meshPart.IndexBuffer.IndexElementSize != IndexElementSize.SixteenBits) { throw new Exception("Model uses 32-bit indices, which are not supported."); } short[] indexElements = new short[meshPart.PrimitiveCount * 3]; meshPart.IndexBuffer.GetData <short>( meshPart.StartIndex * 2, indexElements, 0, meshPart.PrimitiveCount * 3); Display3D.TriangleVertexIndices[] tvi = new Display3D.TriangleVertexIndices[meshPart.PrimitiveCount]; for (int i = 0; i != tvi.Length; ++i) { tvi[i].A = indexElements[i * 3 + 0] + offset; tvi[i].B = indexElements[i * 3 + 1] + offset; tvi[i].C = indexElements[i * 3 + 2] + offset; } indices.AddRange(tvi); }
public void ValidateVertexDeclarationForShader(VertexDeclaration declaration, IShader shader, Type verticesType) { VertexUsage[] usage; if (vertexDeclarationUsage == null) { vertexDeclarationUsage = new Dictionary <VertexDeclaration, VertexUsage[]>(); } if (!vertexDeclarationUsage.TryGetValue(declaration, out usage)) { //build usage VertexElement[] elements = declaration.GetVertexElements(); SortedList <VertexElementUsage, List <int> > usageIndices = new SortedList <VertexElementUsage, List <int> >(); foreach (VertexElement ve in elements) { List <int> inds; if (!usageIndices.TryGetValue(ve.VertexElementUsage, out inds)) { inds = new List <int>(); usageIndices.Add(ve.VertexElementUsage, inds); } inds.Add(ve.UsageIndex); } List <VertexUsage> usages = new List <VertexUsage>(); foreach (KeyValuePair <VertexElementUsage, List <int> > kvp in usageIndices) { VertexUsage vuse = new VertexUsage(); kvp.Value.Sort(); vuse.usage = kvp.Key; foreach (int i in kvp.Value) { vuse.index = i; usages.Add(vuse); } } usage = usages.ToArray(); vertexDeclarationUsage.Add(declaration, usage); } int shaderCount = shader.GetVertexInputCount(); if (shaderCount == 0) { return; } VertexElementUsage use; int index; int sv = 0; for (int dv = 0; dv < usage.Length && sv < shaderCount;) { shader.GetVertexInput(sv, out use, out index); if (usage[dv].usage == use) { if (usage[dv].index == index) { dv++; //all happy, elements match. sv++; continue; } if (usage[dv].index > index) { //bugger, missing element break; } dv++; continue; } if ((int)use > (int)usage[dv].usage) { dv++; continue; } break; //bugger. } if (sv < shaderCount) { //problems.. shader.GetVertexInput(sv, out use, out index); //generate an error describing the problem, //fill it in with details about the state string vertexType = "VerticesGroup"; string vertexDecl = "vertex structure"; string shaderType = string.Format("type {0}", shader.GetType()); string errorFormat = @"Error: The current vertex shader is attempting to read data that is not present in the vertices being drawn.{5}The shader currently in use ({0}) has a vertex shader that reads '{1}{2}' from each vertex.{5}However, the {3} being drawn does not contain a '{1}{2}' value in it's {4}."; if (verticesType != null) { vertexType = string.Format("Vertices<{0}> object", verticesType.FullName); } //add some helpers in some common situations... if (shader.GetType().IsPublic == false && shader.GetType().Namespace == "Xen.Ex.Material") { shaderType = "MaterialShader"; if (use == VertexElementUsage.Tangent || use == VertexElementUsage.Binormal) { errorFormat += Environment.NewLine; errorFormat += "NOTE: MaterialShader properties may change the vertex data it tries to access. Using a Normal Map requires the vertices have Tangents and Binormals."; } if (use == VertexElementUsage.Color) { errorFormat += Environment.NewLine; errorFormat += "NOTE: MaterialShader properties may change the vertex data it tries to access. Setting 'UseVertexColour' to true requires the vertices have Color0 data."; } if (use == VertexElementUsage.Normal) { errorFormat += Environment.NewLine; errorFormat += "NOTE: MaterialShader properties may change the vertex data it tries to access. Enabling lighting requires the vertices have Normals."; } } if (verticesType == typeof(byte)) { vertexType = "XNA vertex data"; vertexDecl = "vertex declaration"; if (use == VertexElementUsage.Tangent || use == VertexElementUsage.Binormal) { errorFormat += Environment.NewLine; errorFormat += "NOTE: If you are drawing a ModelInstance, the Xen Model Importer can generate Tangent/Binormal data by setting the 'Generate Tangent Frames' Content Processor property for the file to true."; } } string error = string.Format(errorFormat, shaderType, use, index, vertexType, vertexDecl, Environment.NewLine); throw new InvalidOperationException(error); } }
/// <summary> /// Extracts the model mesh part data. /// </summary> /// <param name="meshPart">The mesh part.</param> /// <param name="transform">The transform.</param> /// <param name="vertices">The vertices.</param> /// <param name="indices">The indices.</param> public static void ExtractModelMeshPartData(ModelMeshPart meshPart, ref Matrix transform, List <Vector3> vertices, List <int> indices) { // Before we add any more where are we starting from int offset = vertices.Count; // == Vertices (Changed for XNA 4.0) // Read the format of the vertex buffer VertexDeclaration declaration = meshPart.VertexBuffer.VertexDeclaration; VertexElement[] vertexElements = declaration.GetVertexElements(); // Find the element that holds the position VertexElement vertexPosition = new VertexElement(); foreach (VertexElement vert in vertexElements) { if (vert.VertexElementUsage == VertexElementUsage.Position && vert.VertexElementFormat == VertexElementFormat.Vector3) { vertexPosition = vert; // There should only be one break; } } // Check the position element found is valid if (vertexPosition == null || vertexPosition.VertexElementUsage != VertexElementUsage.Position || vertexPosition.VertexElementFormat != VertexElementFormat.Vector3) { throw new Exception("Model uses unsupported vertex format!"); } // This where we store the vertices until transformed Vector3[] allVertex = new Vector3[meshPart.NumVertices]; // Read the vertices from the buffer in to the array meshPart.VertexBuffer.GetData <Vector3>( meshPart.VertexOffset * declaration.VertexStride + vertexPosition.Offset, allVertex, 0, meshPart.NumVertices, declaration.VertexStride); // Transform them based on the relative bone location and the world if provided for (int i = 0; i != allVertex.Length; ++i) { Vector3.Transform(ref allVertex[i], ref transform, out allVertex[i]); } // Store the transformed vertices with those from all the other meshes in this model vertices.AddRange(allVertex); // == Indices (Changed for XNA 4) // Find out which vertices make up which triangles if (meshPart.IndexBuffer.IndexElementSize != IndexElementSize.SixteenBits) { // This could probably be handled by using int in place of short but is unnecessary throw new Exception("Model uses 32-bit indices, which are not supported."); } // Each primitive is a triangle short[] indexElements = new short[meshPart.PrimitiveCount * 3]; meshPart.IndexBuffer.GetData <short>( meshPart.StartIndex * 2, indexElements, 0, meshPart.PrimitiveCount * 3); // Each TriangleVertexIndices holds the three indexes to each vertex that makes up a triangle if (meshPart.IndexBuffer.IndexElementSize != IndexElementSize.SixteenBits) { int[] s = new int[meshPart.PrimitiveCount * 3]; meshPart.IndexBuffer.GetData <int>(meshPart.StartIndex * 2, s, 0, meshPart.PrimitiveCount * 3); for (int k = 0; k != meshPart.PrimitiveCount; ++k) { indices.Add(s[k * 3 + 2] + offset); indices.Add(s[k * 3 + 1] + offset); indices.Add(s[k * 3 + 0] + offset); } } else { short[] s = new short[meshPart.PrimitiveCount * 3]; meshPart.IndexBuffer.GetData <short>(meshPart.StartIndex * 2, s, 0, meshPart.PrimitiveCount * 3); for (int k = 0; k != meshPart.PrimitiveCount; ++k) { indices.Add(s[k * 3 + 2] + offset); indices.Add(s[k * 3 + 1] + offset); indices.Add(s[k * 3 + 0] + offset); } } }
private static VertexBuffer LoadVertexBuffer( ref VertexDeclaration declaration, int elementsPerRow, JArray data, out List <Vector3> positions) { var rowsCount = data.Count / elementsPerRow; var elements = declaration.GetVertexElements(); var blendWeightOffset = 0; var blendWeightCount = (from e in elements where e.VertexElementUsage == VertexElementUsage.BlendWeight select e).Count(); var hasBlendWeight = blendWeightCount > 0; if (blendWeightCount > 4) { throw new Exception("4 is maximum amount of weights per bone"); } if (hasBlendWeight) { blendWeightOffset = (from e in elements where e.VertexElementUsage == VertexElementUsage.BlendWeight select e).First().Offset; var newElements = new List <VertexElement>(); newElements.AddRange(from e in elements where e.VertexElementUsage != VertexElementUsage.BlendWeight select e); newElements.Add(new VertexElement(blendWeightOffset, VertexElementFormat.Byte4, VertexElementUsage.BlendIndices, 0)); newElements.Add(new VertexElement(blendWeightOffset + 4, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 0)); declaration = new VertexDeclaration(newElements.ToArray()); } positions = new List <Vector3>(); var byteData = new byte[rowsCount * declaration.VertexStride]; for (var i = 0; i < rowsCount; ++i) { var destIdx = i * declaration.VertexStride; var srcIdx = i * elementsPerRow; var weightsCount = 0; for (var j = 0; j < elements.Length; ++j) { var element = elements[j]; if (element.VertexElementUsage == VertexElementUsage.BlendWeight) { // Convert from libgdx multiple vector2 blendweight // to single int4 blendindices/vector4 blendweight if (element.VertexElementFormat != VertexElementFormat.Vector2) { throw new Exception("Only Vector2 format for BlendWeight supported."); } var offset = i * declaration.VertexStride + blendWeightOffset + weightsCount; LoadByte(byteData, ref offset, (int)(float)data[srcIdx++]); offset = i * declaration.VertexStride + blendWeightOffset + 4 + weightsCount * 4; LoadFloat(byteData, ref offset, (float)data[srcIdx++]); ++weightsCount; continue; } switch (element.VertexElementFormat) { case VertexElementFormat.Vector2: LoadFloat(byteData, ref destIdx, (float)data[srcIdx++]); LoadFloat(byteData, ref destIdx, (float)data[srcIdx++]); break; case VertexElementFormat.Vector3: var v = new Vector3((float)data[srcIdx++], (float)data[srcIdx++], (float)data[srcIdx++]); if (element.VertexElementUsage == VertexElementUsage.Position) { positions.Add(v); } LoadFloat(byteData, ref destIdx, v.X); LoadFloat(byteData, ref destIdx, v.Y); LoadFloat(byteData, ref destIdx, v.Z); break; case VertexElementFormat.Vector4: LoadFloat(byteData, ref destIdx, (float)data[srcIdx++]); LoadFloat(byteData, ref destIdx, (float)data[srcIdx++]); LoadFloat(byteData, ref destIdx, (float)data[srcIdx++]); LoadFloat(byteData, ref destIdx, (float)data[srcIdx++]); break; case VertexElementFormat.Byte4: LoadByte(byteData, ref destIdx, (int)data[srcIdx++]); LoadByte(byteData, ref destIdx, (int)data[srcIdx++]); LoadByte(byteData, ref destIdx, (int)data[srcIdx++]); LoadByte(byteData, ref destIdx, (int)data[srcIdx++]); break; case VertexElementFormat.Color: LoadFloat(byteData, ref destIdx, (float)data[srcIdx++]); break; default: throw new Exception(string.Format("{0} not supported", element.VertexElementFormat)); } } } var result = new VertexBuffer(Nrs.GraphicsDevice, declaration, rowsCount, BufferUsage.None); result.SetData(byteData); return(result); }
public static VertexBuffer ConvertVB(VertexBuffer vb, VertexDeclaration fromDecl, int fromStreamIndex, VertexDeclaration toDecl, int toStreamIndex) { byte[] fromData = new byte[vb.SizeInBytes]; vb.GetData <byte>(fromData); int fromNumVertices = vb.SizeInBytes / fromDecl.GetVertexStrideSize(0); List <int> vertMap = new List <int>(); //find mappings for (int x = 0; x < fromDecl.GetVertexElements().Length; x++) { VertexElement thisElem = fromDecl.GetVertexElements()[x]; bool bFound = false; int i = 0; for (i = 0; i < toDecl.GetVertexElements().Length; i++) { VertexElement elem = toDecl.GetVertexElements()[i]; if (elem.Stream == toStreamIndex) { if (thisElem.VertexElementUsage == elem.VertexElementUsage && thisElem.UsageIndex == elem.UsageIndex && thisElem.VertexElementFormat == elem.VertexElementFormat) { bFound = true; break; } } } if (bFound) { vertMap.Add(i); } else { vertMap.Add(-1); } } int newBufferSize = fromNumVertices * toDecl.GetVertexStrideSize(toStreamIndex); byte[] toData = new byte[newBufferSize]; int toDeclVertexStride = toDecl.GetVertexStrideSize(toStreamIndex); int fromDeclVertexStride = fromDecl.GetVertexStrideSize(fromStreamIndex); for (int x = 0; x < vertMap.Count; x++) { int i = vertMap[x]; if (i != -1) { VertexElement fromElem = fromDecl.GetVertexElements()[x]; VertexElement toElem = toDecl.GetVertexElements()[i]; for (int k = 0; k < fromNumVertices; k++) { for (int j = 0; j < Utility.GetVertexElementSize(fromDecl, x); j++) { toData[k * toDeclVertexStride + toElem.Offset + j] = fromData[k * fromDeclVertexStride + fromElem.Offset + j]; } } } } VertexBuffer newVB = new VertexBuffer( Utility.GraphicsDevice, fromNumVertices * toDecl.GetVertexStrideSize(toStreamIndex), BufferUsage.None); // in xna 1.0 use vb.ResourceUsage instead newVB.SetData <byte>(toData); return(newVB); }