/// <summary> /// As an optimization, ProcessVertexChannel is overriden to remove data which /// is not used by the vertex shader. /// </summary> /// <param name="geometry">the geometry object which contains the /// vertex channel</param> /// <param name="vertexChannelIndex">the index of the vertex channel /// to operate on</param> /// <param name="context">the context that the processor is operating /// under. in most cases, this parameter isn't necessary; but could /// be used to log a warning that a channel had been removed.</param> protected override void ProcessVertexChannel(GeometryContent geometry, int vertexChannelIndex, ContentProcessorContext context) { String vertexChannelName = geometry.Vertices.Channels[vertexChannelIndex].Name; // if this vertex channel has an acceptable names, process it as normal. if (acceptableVertexChannelNames.Contains(vertexChannelName)) { base.ProcessVertexChannel(geometry, vertexChannelIndex, context); } // otherwise, remove it from the vertex channels; it's just extra data // we don't need. else { geometry.Vertices.Channels.Remove(vertexChannelName); } // Remove texture channel for untextured meshes if (!geometry.Vertices.Channels.Contains(VertexChannelNames.TextureCoordinate(0))) { geometry.Vertices.Channels.Remove(VertexChannelNames.TextureCoordinate(0)); } // Remove vertex weights for unskinned meshes if (!geometry.Vertices.Channels.Contains(VertexChannelNames.Weights())) { geometry.Vertices.Channels.Remove(VertexChannelNames.Weights()); } }
private bool ValidateGeometry(GeometryContent geometry, ContentProcessorContext context) { // Check if the geometry has material if (geometry.Material == null) { throw new InvalidContentException(string.Format( "Mesh {0} has a geometry that does not have a material.", geometry.Parent.Name)); } // Check if the geometry has skinning information if (!geometry.Vertices.Channels.Contains(VertexChannelNames.Weights())) { /* * context.Logger.LogWarning(null, geometry.Parent.Identity, * string.Format("Mesh {0} has a geometry that does not have a skinning " + * "blend weights channel and will be skipped.", geometry.Parent.Name)); * * geometry.Parent.Geometry.Remove(geometry); */ throw new InvalidContentException(string.Format("Mesh {0} has a geometry " + "that does not have a skinning blend weights channel.", geometry.Parent.Name)); } return(true); }
private static bool MeshHasSkinning(MeshContent mesh) { bool flag; bool flag1; IEnumerator <GeometryContent> enumerator = mesh.Geometry.GetEnumerator(); try { do { flag1 = enumerator.MoveNext(); if (flag1) { GeometryContent current = enumerator.Current; flag1 = current.Vertices.Channels.Contains(VertexChannelNames.Weights()); } else { flag = true; return(flag); } }while (flag1); flag = false; return(flag); } finally { flag1 = enumerator == null; if (!flag1) { enumerator.Dispose(); } } flag = true; return(flag); }
public static bool NeedsSplitting(MeshContent mesh, int maxBones) { SortedDictionary <string, object> skinnedBones = new SortedDictionary <string, object>(); foreach (GeometryContent geom in mesh.Geometry) { VertexChannel <BoneWeightCollection> weightChannel = null; foreach (VertexChannel channel in geom.Vertices.Channels) { if (channel.Name == VertexChannelNames.Weights()) { weightChannel = (VertexChannel <BoneWeightCollection>)channel; break; } } if (weightChannel != null) { foreach (BoneWeightCollection weights in weightChannel) { foreach (BoneWeight weight in weights) { if (!skinnedBones.ContainsKey(weight.BoneName)) { skinnedBones.Add(weight.BoneName, null); } } } } } return(skinnedBones.Keys.Count > maxBones); }
public static string GetXNAName(VertexAttribute attr) { switch (attr.usage) { case COLOR: return(VertexChannelNames.Color(0)); case NORMAL: return(VertexChannelNames.Normal()); case TEX_COORD: return(VertexChannelNames.TextureCoordinate(attr.attrIndex)); case BONE_WEIGHT: return(VertexChannelNames.Weights(attr.attrIndex)); case TANGENT: return(VertexChannelNames.Tangent(0)); case BINORMAL: return(VertexChannelNames.Binormal(0)); } return(null); }
/// <summary> /// This function removes geometry that contains no bone weights, because the ModelProcessor /// will throw an exception if we give it geometry content like that. /// </summary> /// <param name="node"></param> /// <param name="context"></param> static void RemoveInvalidGeometry(NodeContent node, ContentProcessorContext context) { MeshContent meshContent = node as MeshContent; if (meshContent != null) { // Maintain a list of all the geometry that was invalid that we will be removing List <GeometryContent> removeGeometry = new List <GeometryContent>(); foreach (GeometryContent geometry in meshContent.Geometry) { VertexChannelCollection channels = geometry.Vertices.Channels; // Does this geometry contain bone weight information? if (geometry.Vertices.Channels.Contains(VertexChannelNames.Weights(0))) { bool removed = false; VertexChannel <BoneWeightCollection> weights = geometry.Vertices.Channels.Get <BoneWeightCollection>(VertexChannelNames.Weights(0)); foreach (BoneWeightCollection collection in weights) { // If we don't have any weights, then this isn't going to be good. The geometry has no bone weights, // so lets just remove it. if (collection.Count <= 0) { removeGeometry.Add(geometry); removed = true; break; } else { // Otherwise, normalize the weights. This call is probably unnecessary. collection.NormalizeWeights(4); } } //If we removed something from this geometry, just remove the whole geometry - there's no point in going farther if (removed) { break; } } } // Remove all the invalid geometry we found, and log a warning. foreach (GeometryContent geometry in removeGeometry) { meshContent.Geometry.Remove(geometry); context.Logger.LogWarning(null, null, "Mesh part {0} has been removed because it has no bone weights associated with it.", geometry.Name); } } // Recursively call this function for each child foreach (NodeContent child in node.Children) { RemoveInvalidGeometry(child, context); } }
private void GetWeightChannel() { foreach (VertexChannel channel in geom.Vertices.Channels) { if (channel.Name == VertexChannelNames.Weights()) { weightChannel = (VertexChannel <BoneWeightCollection>)channel; break; } } }
private VertexChannel <BoneWeightCollection> GetWeightChannel(GeometryContent geom) { foreach (VertexChannel channel in geom.Vertices.Channels) { if (channel.Name == VertexChannelNames.Weights()) { return((VertexChannel <BoneWeightCollection>)channel); } } return(null); }
/// <summary> /// Checks whether mesh contains skinning information. /// </summary> /// <param name="mesh"></param> /// <returns></returns> static bool MeshHasSkinning(MeshContent mesh) { foreach (GeometryContent geometry in mesh.Geometry) { if (!geometry.Vertices.Channels.Contains(VertexChannelNames.Weights())) { return(false); } } return(true); }
/// <summary> /// Adds all the buffered channels to the mesh and merges duplicate positions/verts /// </summary> private void AddAllChannels() { bool recalcNormal = false; if (model.AnimationOptions.Contains("RecalcNormals") || (normals == null && !hasNormals)) { recalcNormal = true; } else { AddChannel <Vector3>(VertexElementUsage.Normal.ToString() + "0", normals); } if (texCoords != null) { AddChannel <Vector2>("TextureCoordinate0", texCoords); } //for (int i = 0; i < weightIndices.Count; i++) //{ // AddChannel<Byte4>(VertexElementUsage.BlendIndices.ToString() + i.ToString(), // weightIndices[i]); //} //for (int i = 0; i < weights.Count; i++) //{ // AddChannel<Vector4>(VertexElementUsage.BlendWeight.ToString() + i.ToString(), // weights[i]); //} bool isSkinned = false; foreach (BoneWeightCollection bwc in skinInfo) { if (bwc.Count > 0) { isSkinned = true; break; } } if (isSkinned) { AddChannel <BoneWeightCollection>(VertexChannelNames.Weights(), skinInfo.ToArray()); } MeshHelper.MergeDuplicatePositions(mesh, 0); MeshHelper.MergeDuplicateVertices(mesh); if (recalcNormal) { MeshHelper.CalculateNormals(mesh, true); } MeshHelper.OptimizeForCache(mesh); }
protected virtual void ProcessVertexChannel( GeometryContent geometry, int vertexChannelIndex, ContentProcessorContext context) { var channel = geometry.Vertices.Channels[vertexChannelIndex]; // TODO: According to docs, channels with VertexElementUsage.Color -> Color // Channels[VertexChannelNames.Weights] -> { Byte4 boneIndices, Color boneWeights } if (channel.Name.StartsWith(VertexChannelNames.Weights(), StringComparison.Ordinal)) { ProcessWeightsChannel(geometry, vertexChannelIndex, _identity); } }
static bool MeshHasSkinning(MeshContent mesh) { // Checks whether a mesh contains skininng information. foreach (GeometryContent geometry in mesh.Geometry) { if (!geometry.Vertices.Channels.Contains(VertexChannelNames.Weights())) { return(false); } } return(true); }
protected override void ProcessVertexChannel( GeometryContent geometry, int vertexChannelIndex, ContentProcessorContext context) { bool isWeights = geometry.Vertices.Channels[vertexChannelIndex].Name == VertexChannelNames.Weights(); base.ProcessVertexChannel(geometry, vertexChannelIndex, context); if (isWeights) { geometry.Vertices.Channels.ConvertChannelContent <Vector4>("BlendIndices0"); geometry.Vertices.Channels.ConvertChannelContent <Vector4>("BlendWeight0"); } }
protected virtual void ProcessVertexChannel(GeometryContent geometry, int vertexChannelIndex, ContentProcessorContext context) { var channel = geometry.Vertices.Channels[vertexChannelIndex]; // TODO: According to docs, channels with VertexElementUsage.Color -> Color // Channels[VertexChannelNames.Weights] -> { Byte4 boneIndices, Color boneWeights } if (channel.Name.StartsWith(VertexChannelNames.Weights())) { ProcessWeightsChannel(geometry, vertexChannelIndex, _identity); } // Looks like XNA models usually put a default color channel in.. if (!geometry.Vertices.Channels.Contains(VertexChannelNames.Color(0))) { geometry.Vertices.Channels.Add(VertexChannelNames.Color(0), Enumerable.Repeat(Color.White, geometry.Vertices.VertexCount)); } }
private void CreatePaletteIndices(MeshContent mesh) { foreach (GeometryContent meshPart in mesh.Geometry) { int meshIndex = (int)mesh.OpaqueData["MeshIndex"]; BoneIndexer indexer = indexers[meshIndex]; foreach (VertexChannel channel in meshPart.Vertices.Channels) { if (channel.Name == VertexChannelNames.Weights()) { VertexChannel <BoneWeightCollection> vc = (VertexChannel <BoneWeightCollection>)channel; foreach (BoneWeightCollection boneWeights in vc) { foreach (BoneWeight weight in boneWeights) { indexer.GetBoneIndex(weight.BoneName); } } } } } }
/// <summary> /// Return true if the vertex content contains skinning blend indices. /// </summary> /// <param name="vc">The vertex content to check.</param> /// <param name="context">For reporting errors.</param> /// <returns>true if skinning should be applied to these vertices</returns> protected virtual bool VerticesAreSkinned(VertexContent vc, ContentProcessorContext context) { return(vc.Channels.Contains(VertexChannelNames.Weights())); }
public override NodeContent Import(string filename, ContentImporterContext context) { context.Logger.LogMessage("Importing H3D file: {0}", filename); _identity = new ContentIdentity(filename, GetType().Name); _rootNode = new NodeContent() { Identity = _identity, Name = "RootNode" }; var scene = FormatIdentifier.IdentifyAndOpen(filename); var model = scene.Models[0]; if (!scene.Textures.Any()) { var path = Path.Combine(Path.GetDirectoryName(filename), $"{Path.GetFileNameWithoutExtension(filename)}@Textures{Path.GetExtension(filename)}"); if (File.Exists(path)) { context.Logger.LogMessage($"Found texture file {path}. Loading data..."); scene.Merge(FormatIdentifier.IdentifyAndOpen(path, model.Skeleton)); } else { context.Logger.LogMessage($"Couldn't find texture file {path}!"); } } // Textures var textures = new Dictionary <string, Texture2DContent>(); foreach (var texture in scene.Textures) { var bitmapContent = new PixelBitmapContent <Color>(texture.Width, texture.Height) { Identity = _identity, Name = texture.Name }; bitmapContent.SetPixelData(texture.ToRGBA()); var textureContent = new Texture2DContent() { Identity = _identity, Name = texture.Name }; textureContent.Faces[0].Add(bitmapContent); textures.Add(textureContent.Name, textureContent); } // Materials var materials = new Dictionary <string, H3DMaterialContent>(); foreach (var material in model.Materials) { #if DEBUG var hlslCode = new HLSLShaderGenerator(material.MaterialParams) { BoneCount = model.Skeleton.Count }.GetShader(); var glslCode = new GLSLFragmentShaderGenerator(material.MaterialParams).GetFragShader(); #endif var materialContent = new H3DMaterialContent() { Identity = _identity, Name = material.Name, Effect = new EffectContent { Identity = _identity, Name = "H3DEffect", EffectCode = new HLSLShaderGenerator(material.MaterialParams) { BoneCount = model.Skeleton.Count }.GetShader() }, Material = material.Name, FaceCulling = (H3DFaceCulling?)material.MaterialParams.FaceCulling, EmissionColor = material.MaterialParams.EmissionColor.ToXNA(), AmbientColor = material.MaterialParams.AmbientColor.ToXNA(), DiffuseColor = material.MaterialParams.DiffuseColor.ToXNA(), Specular0Color = material.MaterialParams.Specular0Color.ToXNA(), Specular1Color = material.MaterialParams.Specular1Color.ToXNA(), Constant0Color = material.MaterialParams.Constant0Color.ToXNA(), Constant1Color = material.MaterialParams.Constant1Color.ToXNA(), Constant2Color = material.MaterialParams.Constant2Color.ToXNA(), Constant3Color = material.MaterialParams.Constant3Color.ToXNA(), Constant4Color = material.MaterialParams.Constant4Color.ToXNA(), Constant5Color = material.MaterialParams.Constant5Color.ToXNA(), BlendColor = material.MaterialParams.BlendColor.ToXNA(), DepthBufferRead = material.MaterialParams.DepthBufferRead, DepthBufferWrite = material.MaterialParams.DepthBufferWrite, StencilBufferRead = material.MaterialParams.StencilBufferRead, StencilBufferWrite = material.MaterialParams.StencilBufferWrite, }; var texCount = 0; if (material.EnabledTextures[0]) { texCount++; } if (material.EnabledTextures[1]) { texCount++; } if (material.EnabledTextures[2]) { texCount++; } materialContent.TextureList = new Texture2DContent[texCount]; if (material.EnabledTextures[0]) { materialContent.TextureList[0] = textures[material.Texture0Name]; } if (material.EnabledTextures[1]) { materialContent.TextureList[1] = textures[material.Texture1Name]; } if (material.EnabledTextures[2]) { materialContent.TextureList[2] = textures[material.Texture2Name]; } materialContent.TextureSamplerSettings = material.TextureMappers.Select(tm => new TextureSamplerSettings() { WrapU = tm.WrapU.ToXNAWrap(), WrapV = tm.WrapV.ToXNAWrap(), MagFilter = (TextureSamplerSettings.TextureMagFilter)tm.MagFilter, MinFilter = (TextureSamplerSettings.TextureMinFilter)tm.MinFilter }).ToArray(); materials.Add(material.Name, materialContent); } // Geometry var meshes = new List <MeshContent>(); for (var i = 0; i < model.Meshes.Count; i++) { var modelMesh = model.Meshes[i]; if (modelMesh.Type == H3DMeshType.Silhouette) { continue; } var mesh = new MeshContent() { Identity = _identity, Name = $"{model.Materials[modelMesh.MaterialIndex].Name}_node{i}", }; var geometry = new GeometryContent { Identity = _identity, Material = materials[model.Materials[modelMesh.MaterialIndex].Name] }; var vertices = GetWorldSpaceVertices(model.Skeleton, modelMesh); var baseVertex = mesh.Positions.Count; foreach (var vertex in vertices) { mesh.Positions.Add(vertex.Position.ToVector3()); } geometry.Vertices.AddRange(Enumerable.Range(baseVertex, vertices.Length)); foreach (var attribute in modelMesh.Attributes) { if (attribute.Name >= PICAAttributeName.BoneIndex) { continue; } switch (attribute.Name) { case PICAAttributeName.Position: break; // Already added case PICAAttributeName.Normal: geometry.Vertices.Channels.Add(VertexChannelNames.Normal(0), vertices.Select(vertex => vertex.Normal.ToVector3())); break; case PICAAttributeName.Tangent: geometry.Vertices.Channels.Add(VertexChannelNames.Tangent(0), vertices.Select(vertex => vertex.Tangent.ToVector3())); break; case PICAAttributeName.Color: geometry.Vertices.Channels.Add(VertexChannelNames.Color(0), vertices.Select(vertex => vertex.Color.ToColor())); break; case PICAAttributeName.TexCoord0: geometry.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(0), vertices.Select(vertex => vertex.TexCoord0.ToVector2().ToUV())); break; case PICAAttributeName.TexCoord1: geometry.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(1), vertices.Select(vertex => vertex.TexCoord1.ToVector2().ToUV())); break; case PICAAttributeName.TexCoord2: geometry.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(2), vertices.Select(vertex => vertex.TexCoord2.ToVector2().ToUV())); break; } } var vertexOffset = 0; var xnaWeights = new List <BoneWeightCollection>(); foreach (var modelSubMesh in modelMesh.SubMeshes) { geometry.Indices.AddRange(modelSubMesh.Indices.Select(index => (int)index)); var vertexCount = modelSubMesh.MaxIndex + 1 - vertexOffset; var subMeshVertices = vertices.Skip(vertexOffset).Take(vertexCount).ToList(); if (modelSubMesh.Skinning == H3DSubMeshSkinning.Smooth) { foreach (var vertex in subMeshVertices) { var list = new BoneWeightCollection(); for (var index = 0; index < 4; index++) { var bIndex = vertex.Indices[index]; var weight = vertex.Weights[index]; if (weight == 0) { break; } if (bIndex < modelSubMesh.BoneIndicesCount && bIndex > -1) { bIndex = modelSubMesh.BoneIndices[bIndex]; } else { bIndex = 0; } list.Add(new BoneWeight(model.Skeleton[bIndex].Name, weight)); } xnaWeights.Add(list); } } else { foreach (var vertex in vertices) { var bIndex = vertex.Indices[0]; if (bIndex < modelSubMesh.BoneIndices.Length && bIndex > -1) { bIndex = modelSubMesh.BoneIndices[bIndex]; } else { bIndex = 0; } xnaWeights.Add(new BoneWeightCollection() { new BoneWeight(model.Skeleton[bIndex].Name, 0) }); } } vertexOffset += vertexCount; } geometry.Vertices.Channels.Add(VertexChannelNames.Weights(0), xnaWeights); mesh.Geometry.Add(geometry); meshes.Add(mesh); } foreach (var mesh in meshes) { _rootNode.Children.Add(mesh); } var rootBone = ImportBones(model); _rootNode.Children.Add(rootBone); if (!scene.SkeletalAnimations.Any()) { var path = Path.Combine(Path.GetDirectoryName(filename), $"{Path.GetFileNameWithoutExtension(filename)}@Animations{Path.GetExtension(filename)}"); if (File.Exists(path)) { context.Logger.LogMessage($"Found animation file {path}. Loading data..."); scene.Merge(FormatIdentifier.IdentifyAndOpen(path, model.Skeleton)); } else { context.Logger.LogMessage($"Couldn't find animation file {path}!"); } } foreach (var animation in ImportSkeletalAnimations(scene)) { rootBone.Animations.Add(animation.Name, animation); } foreach (var animation in ImportMaterialAnimations(scene)) { _rootNode.Children.Add(animation); } return(_rootNode); }
private MeshContent CreateMesh(Mesh sceneMesh) { var mesh = new MeshContent { Name = sceneMesh.Name }; // Position vertices are shared at the mesh level foreach (var vert in sceneMesh.Vertices) { mesh.Positions.Add(new Vector3(vert.X, vert.Y, vert.Z)); } var geom = new GeometryContent { Material = _materials[sceneMesh.MaterialIndex] }; // Geometry vertices reference 1:1 with the MeshContent parent, // no indirection is necessary. //geom.Vertices.Positions.AddRange(mesh.Positions); geom.Vertices.AddRange(Enumerable.Range(0, sceneMesh.VertexCount)); geom.Indices.AddRange(sceneMesh.GetIndices()); if (sceneMesh.HasBones) { var xnaWeights = new List <BoneWeightCollection>(); for (var i = 0; i < geom.Indices.Count; i++) { var list = new BoneWeightCollection(); for (var boneIndex = 0; boneIndex < sceneMesh.BoneCount; boneIndex++) { var bone = sceneMesh.Bones[boneIndex]; foreach (var weight in bone.VertexWeights) { if (weight.VertexID != i) { continue; } list.Add(new BoneWeight(bone.Name, weight.Weight)); } } if (list.Count > 0) { xnaWeights.Add(list); } } geom.Vertices.Channels.Add(VertexChannelNames.Weights(0), xnaWeights); } // Individual channels go here if (sceneMesh.HasNormals) { geom.Vertices.Channels.Add(VertexChannelNames.Normal(), ToXna(sceneMesh.Normals)); } for (var i = 0; i < sceneMesh.TextureCoordinateChannelCount; i++) { geom.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(i), ToXnaTexCoord(sceneMesh.TextureCoordinateChannels[i])); } mesh.Geometry.Add(geom); return(mesh); }
private MeshContent ExtractMesh(aiMesh aiMesh) { if (!String.IsNullOrEmpty(aiMesh.mName.Data)) { log("modelname " + aiMesh.mName.Data); meshBuilder = MeshBuilder.StartMesh(aiMesh.mName.Data); } else { meshBuilder = MeshBuilder.StartMesh(Path.GetFileNameWithoutExtension(filename)); } if (!aiMesh.HasPositions()) { throw new Exception("MOdel does not have Position"); } // Add additional vertex channels for texture coordinates and normals if (aiMesh.HasTextureCoords(0)) { textureCoordinateDataIndex = meshBuilder.CreateVertexChannel <Vector2>(VertexChannelNames.TextureCoordinate(0)); } else if (aiMesh.HasVertexColors(0)) { colorCoordinateDataIndex = meshBuilder.CreateVertexChannel <Vector4>(VertexChannelNames.Color(0)); } if (aiMesh.HasNormals()) { normalDataIndex = meshBuilder.CreateVertexChannel <Vector3>(VertexChannelNames.Normal()); } if (aiMesh.HasTangentsAndBitangents()) { tangentDataIndex = meshBuilder.CreateVertexChannel <Vector3>(VertexChannelNames.Tangent(0)); binormalDataIndex = meshBuilder.CreateVertexChannel <Vector3>(VertexChannelNames.Binormal(0)); } if (aiMesh.HasBones()) { boneDataIndex = meshBuilder.CreateVertexChannel <BoneWeightCollection>(VertexChannelNames.Weights(0)); } var numFaces = (int)aiMesh.mNumFaces; var numVertices = (int)aiMesh.mNumVertices; var aiPositions = aiMesh.mVertices; var aiNormals = aiMesh.mNormals; var aiTextureCoordsAll = aiMesh.mTextureCoords; var aiTextureCoords = (aiTextureCoordsAll != null) ? aiTextureCoordsAll[0] : null; for (int j = 0; j < aiMesh.mNumVertices; j++) { meshBuilder.CreatePosition(aiMesh.mVertices[j].x, aiMesh.mVertices[j].y, aiMesh.mVertices[j].z); } meshBuilder.SetMaterial(GetMaterial(aiMesh)); var aiFaces = aiMesh.mFaces; var dxIndices = new uint[numFaces * 3]; for (int k = 0; k < numFaces; ++k) { var aiFace = aiFaces[k]; var aiIndices = aiFace.mIndices; for (int j = 0; j < 3; ++j) { int index = (int)aiIndices[j]; if (aiMesh.HasTextureCoords(0)) { meshBuilder.SetVertexChannelData(textureCoordinateDataIndex, new Vector2(aiMesh.mTextureCoords[0][index].x, aiMesh.mTextureCoords[0][index].y)); } else if (aiMesh.HasVertexColors(0)) { meshBuilder.SetVertexChannelData(colorCoordinateDataIndex, new Vector4(aiMesh.mColors[0][index].r, aiMesh.mColors[0][index].g, aiMesh.mColors[0][index].b, aiMesh.mColors[0][index].a)); } if (aiMesh.HasNormals()) { meshBuilder.SetVertexChannelData(normalDataIndex, new Vector3(aiMesh.mNormals[index].x, aiMesh.mNormals[index].y, aiMesh.mNormals[index].z)); } if (aiMesh.HasTangentsAndBitangents()) { meshBuilder.SetVertexChannelData(tangentDataIndex, new Vector3(aiMesh.mTangents[index].x, aiMesh.mTangents[index].y, aiMesh.mTangents[index].z)); meshBuilder.SetVertexChannelData(binormalDataIndex, new Vector3(aiMesh.mBitangents[index].x, aiMesh.mBitangents[index].y, aiMesh.mBitangents[index].z)); } if (aiMesh.HasBones()) { BoneWeightCollection BoneWeightCollection = new BoneWeightCollection(); if (wbone.ContainsKey(index)) { foreach (var item in wbone[index]) { BoneWeightCollection.Add(new BoneWeight(item.Key, item.Value)); } } meshBuilder.SetVertexChannelData(boneDataIndex, BoneWeightCollection); } meshBuilder.AddTriangleVertex(index); } } MeshContent meshContent = meshBuilder.FinishMesh(); return(meshContent); }
public void DefaultEffectTest() { NodeContent input; { input = new NodeContent(); var mesh = new MeshContent() { Name = "Mesh1" }; mesh.Positions.Add(new Vector3(0, 0, 0)); mesh.Positions.Add(new Vector3(1, 0, 0)); mesh.Positions.Add(new Vector3(1, 1, 1)); var geom = new GeometryContent(); geom.Vertices.Add(0); geom.Vertices.Add(1); geom.Vertices.Add(2); geom.Indices.Add(0); geom.Indices.Add(1); geom.Indices.Add(2); geom.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(0), new[] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), }); var wieghts = new BoneWeightCollection(); wieghts.Add(new BoneWeight("bone1", 0.5f)); geom.Vertices.Channels.Add(VertexChannelNames.Weights(0), new[] { wieghts, wieghts, wieghts }); mesh.Geometry.Add(geom); input.Children.Add(mesh); var bone1 = new BoneContent { Name = "bone1", Transform = Matrix.CreateTranslation(0, 1, 0) }; input.Children.Add(bone1); var anim = new AnimationContent() { Name = "anim1", Duration = TimeSpan.Zero }; input.Animations.Add(anim.Name, anim); } var processorContext = new ProcessorContext(TargetPlatform.Windows, "dummy.xnb"); var processor = new ModelProcessor { DefaultEffect = MaterialProcessorDefaultEffect.SkinnedEffect, }; var output = processor.Process(input, processorContext); // TODO: Not sure why, but XNA always returns a BasicMaterialContent // even when we specify SkinnedEffect as the default. We need to fix // the test first before we can enable the assert here. //Assert.IsInstanceOf(typeof(SkinnedMaterialContent), output.Meshes[0].MeshParts[0].Material); }
/// <summary> /// Checks whether a mesh contains skininng information. /// </summary> private static bool MeshHasSkinning(MeshContent mesh) => mesh.Geometry.All(geometry => geometry.Vertices.Channels.Contains(VertexChannelNames.Weights()) || geometry.Vertices.Channels.Contains("BlendWeight0"));
private GeometryContent CreateGeometry(MeshContent mesh, Mesh aiMesh) { var geom = new GeometryContent { Material = _materials[aiMesh.MaterialIndex] }; // Vertices var baseVertex = mesh.Positions.Count; foreach (var vert in aiMesh.Vertices) { mesh.Positions.Add(ToXna(vert)); } geom.Vertices.AddRange(Enumerable.Range(baseVertex, aiMesh.VertexCount)); geom.Indices.AddRange(aiMesh.GetIndices()); if (aiMesh.HasBones) { var xnaWeights = new List <BoneWeightCollection>(); for (var i = 0; i < geom.Indices.Count; i++) { var list = new BoneWeightCollection(); for (var boneIndex = 0; boneIndex < aiMesh.BoneCount; boneIndex++) { var bone = aiMesh.Bones[boneIndex]; foreach (var weight in bone.VertexWeights) { if (weight.VertexID != i) { continue; } list.Add(new BoneWeight(bone.Name, weight.Weight)); } } if (list.Count > 0) { xnaWeights.Add(list); } } geom.Vertices.Channels.Add(VertexChannelNames.Weights(0), xnaWeights); } // Individual channels go here if (aiMesh.HasNormals) { geom.Vertices.Channels.Add(VertexChannelNames.Normal(), ToXna(aiMesh.Normals)); } for (var i = 0; i < aiMesh.TextureCoordinateChannelCount; i++) { geom.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(i), ToXnaTexCoord(aiMesh.TextureCoordinateChannels[i])); } for (var i = 0; i < aiMesh.VertexColorChannelCount; i++) { geom.Vertices.Channels.Add(VertexChannelNames.Color(i), ToXnaColors(aiMesh.VertexColorChannels[i])); } return(geom); }
public override NodeContent Import(string filename, ContentImporterContext context) { var content = new NodeContent(); var reader = XmlReader.Create(filename); var xmlMesh = (XmlMesh) new XmlSerializer(typeof(XmlMesh)).Deserialize(reader); reader.Close(); reader = XmlReader.Create(Path.Combine(Path.GetDirectoryName(filename), xmlMesh.SkeletonLink.Name + ".xml")); var xmlSkeleton = (XmlSkeleton) new XmlSerializer(typeof(XmlSkeleton)).Deserialize(reader); reader.Close(); context.Logger.LogImportantMessage("Bones: " + xmlSkeleton.Bones.Length.ToString()); var bones = new Dictionary <string, BoneContent>(); foreach (var xmlBone in xmlSkeleton.Bones) { context.Logger.LogImportantMessage("{0}", "-- " + xmlBone.Name + ": " + xmlBone.Position.AsVector3().ToString() + ", " + xmlBone.Rotation.Angle.ToString() + "/" + xmlBone.Rotation.Axis.AsVector3().ToString()); var boneContent = new BoneContent() { Name = xmlBone.Name, Transform = Matrix.CreateFromAxisAngle(xmlBone.Rotation.Axis.AsVector3(), xmlBone.Rotation.Angle) * Matrix.CreateTranslation(xmlBone.Position.AsVector3()) }; bones.Add(xmlBone.Name, boneContent); } foreach (var boneParent in xmlSkeleton.BoneParents) { var parent = bones[boneParent.Parent]; var bone = bones[boneParent.Bone]; parent.Children.Add(bone); } var rootBone = bones.Single(x => x.Value.Parent == null); content.Children.Add(rootBone.Value); context.Logger.LogImportantMessage("Submeshes: " + xmlMesh.SubMeshes.Length.ToString()); //context.AddDependency(Path.GetFullPath("HUM_M.MATERIAL")); //var materialFile = File.ReadAllText("HUM_M.MATERIAL"); ////context.Logger.LogImportantMessage("{0}", materialFile); foreach (var xmlSubMesh in xmlMesh.SubMeshes) { context.Logger.LogImportantMessage("Submesh: " + xmlSubMesh.Material); context.Logger.LogImportantMessage("-- Faces: " + xmlSubMesh.Faces.Length.ToString()); if (xmlSubMesh.UseSharedGeometry) { context.Logger.LogImportantMessage("-- Uses Shared Geometry"); } else { context.Logger.LogImportantMessage("-- Vertexbuffers: " + xmlSubMesh.Geometry.VertexBuffers.Length.ToString()); context.Logger.LogImportantMessage("-- Vertices (0): " + xmlSubMesh.Geometry.VertexBuffers[0].Vertices.Length.ToString()); context.Logger.LogImportantMessage("-- Vertices (1): " + xmlSubMesh.Geometry.VertexBuffers[1].Vertices.Length.ToString()); } var builder = MeshBuilder.StartMesh(xmlSubMesh.Material); //if (xmlSubMesh.Material == "Hum_M/Chest") // builder.SetMaterial(new SkinnedMaterialContent { Texture = new ExternalReference<TextureContent>("TL2_ARMORTEST_CHEST.png") }); //else if (xmlSubMesh.Material == "Hum_M/MidLeg") // builder.SetMaterial(new SkinnedMaterialContent { Texture = new ExternalReference<TextureContent>("TL2_ARMORTEST_PANTS.png") }); //else builder.SetMaterial(new SkinnedMaterialContent { Texture = new ExternalReference <TextureContent>("Fiend\\FIEND.dds") }); var normalChannel = builder.CreateVertexChannel <Vector3>(VertexChannelNames.Normal()); var uvChannel = builder.CreateVertexChannel <Vector2>(VertexChannelNames.TextureCoordinate(0)); var weightsChannel = builder.CreateVertexChannel <BoneWeightCollection>(VertexChannelNames.Weights()); var geometry = xmlSubMesh.Geometry; if (xmlSubMesh.UseSharedGeometry) { geometry = xmlMesh.SharedGeometry; } foreach (var vertex in geometry.VertexBuffers[0].Vertices) { builder.CreatePosition(vertex.Position.AsVector3()); } foreach (var face in xmlSubMesh.Faces) { AddTriangleVertex(builder, xmlMesh, xmlSubMesh, xmlSkeleton, face.Vertex1, normalChannel, uvChannel, weightsChannel); AddTriangleVertex(builder, xmlMesh, xmlSubMesh, xmlSkeleton, face.Vertex2, normalChannel, uvChannel, weightsChannel); AddTriangleVertex(builder, xmlMesh, xmlSubMesh, xmlSkeleton, face.Vertex3, normalChannel, uvChannel, weightsChannel); } content.Children.Add(builder.FinishMesh()); } return(content); }
private void ValidateMesh(MeshContent mesh) { foreach (var geometry in mesh.Geometry) { if (GetExternalMaterial(mesh, geometry) != null) { // ----- External material. // The material is defined in an external XML file! // Ignore local material. continue; } // ----- Local material. // Submesh uses the material included in the model. var material = geometry.Material; var channels = geometry.Vertices.Channels; // Check if the geometry vertices contain the right number of texture coordinates. if (material != null && material.Textures.ContainsKey("Texture")) { if (!channels.Contains(VertexChannelNames.TextureCoordinate(0))) { string message = String.Format( CultureInfo.InvariantCulture, "Model \"{0}\" has texture but no texture coordinates.", geometry.Parent.Name); throw new InvalidContentException(message, geometry.Identity); } } if (material is DualTextureMaterialContent) { if (!channels.Contains(VertexChannelNames.TextureCoordinate(1))) { string message = String.Format( CultureInfo.InvariantCulture, "Model \"{0}\" uses DualTextureEffect but has only one set of texture coordinates.", geometry.Parent.Name); throw new InvalidContentException(message, geometry.Identity); } } // Check if the geometry vertices contain blend weights for mesh skinning. if (material is SkinnedMaterialContent) { // If the channel contains "Weights0", then we have a BoneWeightCollection that contains // the necessary data. if (!channels.Contains(VertexChannelNames.Weights())) { // Otherwise, we need "BlendIndices0" AND "BlendWeight0". var blendIndicesName = VertexChannelNames.EncodeName(VertexElementUsage.BlendIndices, 0); var blendWeightsName = VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, 0); if (!channels.Contains(blendIndicesName) || !channels.Contains(blendWeightsName)) { string message = String.Format( CultureInfo.InvariantCulture, "Model \"{0}\" uses mesh skinning but vertices do not have bone weights.", geometry.Parent.Name); throw new InvalidContentException(message, geometry.Identity); } } } } }
/// <summary> /// Go through the vertex channels in the geometry and replace the /// BoneWeightCollection objects with weight and index channels. /// </summary> /// <param name="geometry">The geometry to process.</param> /// <param name="vertexChannelIndex">The index of the vertex channel to process.</param> /// <param name="context">The processor context.</param> protected override void ProcessVertexChannel(GeometryContent geometry, int vertexChannelIndex, ContentProcessorContext context) { bool boneCollectionsWithZeroWeights = false; if (geometry.Vertices.Channels[vertexChannelIndex].Name == VertexChannelNames.Weights()) { int meshIndex = (int)geometry.Parent.OpaqueData["MeshIndex"]; BoneIndexer indexer = indexers[meshIndex]; // Skin channels are passed in from importers as BoneWeightCollection objects VertexChannel <BoneWeightCollection> vc = (VertexChannel <BoneWeightCollection>) geometry.Vertices.Channels[vertexChannelIndex]; int maxBonesPerVertex = 0; for (int i = 0; i < vc.Count; i++) { int count = vc[i].Count; if (count > maxBonesPerVertex) { maxBonesPerVertex = count; } } // Add weights as colors (Converts well to 4 floats) // and indices as packed 4byte vectors. Color[] weightsToAdd = new Color[vc.Count]; Byte4[] indicesToAdd = new Byte4[vc.Count]; // Go through the BoneWeightCollections and create a new // weightsToAdd and indicesToAdd array for each BoneWeightCollection. for (int i = 0; i < vc.Count; i++) { BoneWeightCollection bwc = vc[i]; if (bwc.Count == 0) { boneCollectionsWithZeroWeights = true; continue; } bwc.NormalizeWeights(4); int count = bwc.Count; if (count > maxBonesPerVertex) { maxBonesPerVertex = count; } // Add the appropriate bone indices based on the bone names in the // BoneWeightCollection Vector4 bi = new Vector4(); bi.X = count > 0 ? indexer.GetBoneIndex(bwc[0].BoneName) : (byte)0; bi.Y = count > 1 ? indexer.GetBoneIndex(bwc[1].BoneName) : (byte)0; bi.Z = count > 2 ? indexer.GetBoneIndex(bwc[2].BoneName) : (byte)0; bi.W = count > 3 ? indexer.GetBoneIndex(bwc[3].BoneName) : (byte)0; indicesToAdd[i] = new Byte4(bi); Vector4 bw = new Vector4(); bw.X = count > 0 ? bwc[0].Weight : 0; bw.Y = count > 1 ? bwc[1].Weight : 0; bw.Z = count > 2 ? bwc[2].Weight : 0; bw.W = count > 3 ? bwc[3].Weight : 0; weightsToAdd[i] = new Color(bw); } // Remove the old BoneWeightCollection channel geometry.Vertices.Channels.Remove(vc); // Add the new channels geometry.Vertices.Channels.Add <Byte4>(VertexElementUsage.BlendIndices.ToString(), indicesToAdd); geometry.Vertices.Channels.Add <Color>(VertexElementUsage.BlendWeight.ToString(), weightsToAdd); } else { // No skinning info, so we let the base class process the channel base.ProcessVertexChannel(geometry, vertexChannelIndex, context); } if (boneCollectionsWithZeroWeights) { context.Logger.LogWarning("", geometry.Identity, "BonesWeightCollections with zero weights found in geometry."); } }
protected override void Create() { String usageString = VertexChannelNames.Weights(); _channelIndex = _meshBuilder.CreateVertexChannel <BoneWeightCollection>(usageString); }
public override NodeContent Import(string filename, ContentImporterContext context) { ContentIdentity identity = new ContentIdentity(filename, GetType().Name); const int MAX_BONE_WEIGHTS = 4; VertexBoneWeightLimitConfig boneConfig = new VertexBoneWeightLimitConfig(MAX_BONE_WEIGHTS); AssimpImporter importer = new AssimpImporter(); importer.SetConfig(boneConfig); importer.AttachLogStream(new LogStream((msg, userData) => context.Logger.LogMessage(msg))); Scene scene = importer.ImportFile(filename, PostProcessSteps.FlipUVs | PostProcessSteps.JoinIdenticalVertices | PostProcessSteps.Triangulate | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.FindInvalidData | PostProcessSteps.LimitBoneWeights | PostProcessSteps.FixInFacingNormals); // Root node NodeContent rootNode = new NodeContent { Name = scene.RootNode.Name, Identity = identity, Transform = Matrix.Transpose(ToXna(scene.RootNode.Transform)) }; // Materials MaterialContent[] materials = new MaterialContent[scene.MaterialCount]; for (int m = 0; m < scene.MaterialCount; m++) { materials[m] = new BasicMaterialContent(); materials[m].Identity = identity; // For some reason, there is all kinds of nasty junk in this string: materials[m].Name = CleanInput(scene.Materials[m].Name); for (int t = 0; t < scene.Materials[m].GetTextureCount(TextureType.Diffuse); t++) { TextureSlot diffuseMap = scene.Materials[m].GetTexture(TextureType.Diffuse, t); if (!String.IsNullOrEmpty(diffuseMap.FilePath)) { materials[m].Textures.Add("Texture" + (t > 0 ? t.ToString() : ""), new ExternalReference <TextureContent>(diffuseMap.FilePath, identity)); } } } // Bones // We find 'mesh container' nodes with the best names for those meshes while looking for the bones, // and will need them later when we create the MeshContents. I have a feeling that this won't work // in general, and may need to be made more robust. Dictionary <Mesh, string> meshNames = new Dictionary <Mesh, string>(); Dictionary <Node, BoneContent> nodeToBoneMap = new Dictionary <Node, BoneContent>(); BoneContent skeleton = null; // The root bone for the model. List <Node> hierarchyNodes = scene.RootNode.Children.SelectDeep(n => n.Children).ToList(); foreach (Node node in hierarchyNodes) { BoneContent bone = new BoneContent { Name = node.Name, Transform = Matrix.Transpose(ToXna(node.Transform)) }; if (node.MeshIndices != null) { // This node is a 'mesh container' instead of a bone, so we only care about extracting the name of the mesh. foreach (int meshIndex in node.MeshIndices) { if (!meshNames.ContainsKey(scene.Meshes[meshIndex])) { meshNames.Add(scene.Meshes[meshIndex], node.Name); } } } else if (node.Parent == scene.RootNode) { if (skeleton == null) { // This will be our skeleton so put the animations here: if (scene.HasAnimations) { foreach (Animation assimpAnim in scene.Animations) { if (assimpAnim.HasNodeAnimations) { AnimationContent newAnim = new AnimationContent(); newAnim.Identity = identity; newAnim.Duration = TimeSpan.FromSeconds(assimpAnim.DurationInTicks / assimpAnim.TicksPerSecond); newAnim.Name = assimpAnim.Name; foreach (NodeAnimationChannel nac in assimpAnim.NodeAnimationChannels) { Node animatedNode = hierarchyNodes.Find(n => n.Name == nac.NodeName); AnimationChannel newChan = BuildAnimtionChannel(animatedNode, nac); newAnim.Channels.Add(nac.NodeName, newChan); } if (String.IsNullOrEmpty(assimpAnim.Name)) { bone.Animations.Add("SkelematorNoAnimationName", newAnim); } else { bone.Animations.Add(assimpAnim.Name, newAnim); } } } } rootNode.Children.Add(bone); skeleton = bone; } else { context.Logger.LogWarning(null, identity, "Found multiple skeletons in the model, throwing extras away..."); } } else { BoneContent parent = nodeToBoneMap[node.Parent]; parent.Children.Add(bone); } nodeToBoneMap.Add(node, bone); } // Meshes Dictionary <Mesh, MeshContent> meshes = new Dictionary <Mesh, MeshContent>(); foreach (Mesh sceneMesh in scene.Meshes) { // See comment about meshNames at the beginning of the bone section. MeshBuilder mb = MeshBuilder.StartMesh(meshNames[sceneMesh]); mb.SwapWindingOrder = true; // Appears to require this... int positionIndex = -1; for (int v = 0; v < sceneMesh.VertexCount; v++) { Vector3D vert = sceneMesh.Vertices[v]; // CreatePosition should just return a 0-based index of the newly added vertex. positionIndex = mb.CreatePosition(new Vector3(vert.X, vert.Y, vert.Z)); if (positionIndex != v) { throw new InvalidContentException("Something unexpected happened while building a MeshContent from the Assimp scene mesh's vertices. The scene mesh may contains duplicate vertices."); } } if (positionIndex + 1 < 3) { throw new InvalidContentException("There were not enough vertices in the Assimp scene mesh."); } // Create vertex channels int normalVertexChannelIndex = mb.CreateVertexChannel <Vector3>(VertexChannelNames.Normal()); int[] texCoordVertexChannelIndex = new int[sceneMesh.TextureCoordsChannelCount]; for (int x = 0; x < sceneMesh.TextureCoordsChannelCount; x++) { texCoordVertexChannelIndex[x] = mb.CreateVertexChannel <Vector2>(VertexChannelNames.TextureCoordinate(x)); } int boneWeightVertexChannelIndex = -1; if (sceneMesh.HasBones) { boneWeightVertexChannelIndex = mb.CreateVertexChannel <BoneWeightCollection>(VertexChannelNames.Weights()); } // Prepare vertex channel data BoneWeightCollection[] boneWeightData = null; if (sceneMesh.HasBones) { boneWeightData = new BoneWeightCollection[sceneMesh.VertexCount]; for (int v = 0; v < sceneMesh.VertexCount; v++) { boneWeightData[v] = new BoneWeightCollection(); } foreach (Bone sceneMeshBone in sceneMesh.Bones) { // We have to assume that the bone's name matches up with a node, and therefore one of our BoneContents. foreach (VertexWeight sceneMeshBoneWeight in sceneMeshBone.VertexWeights) { boneWeightData[sceneMeshBoneWeight.VertexID].Add(new BoneWeight(sceneMeshBone.Name, sceneMeshBoneWeight.Weight)); } } for (int v = 0; v < sceneMesh.VertexCount; v++) { if (boneWeightData[v].Count <= 0) { throw new InvalidContentException("Encountered vertices without bone weights."); } boneWeightData[v].NormalizeWeights(); } } // Set the per-geometry data mb.SetMaterial(materials[sceneMesh.MaterialIndex]); mb.SetOpaqueData(new OpaqueDataDictionary()); // Add each vertex for (int f = 0; f < sceneMesh.FaceCount; f++) { if (sceneMesh.Faces[f].IndexCount != 3) { throw new InvalidContentException("Only triangular faces allowed."); } for (int t = 0; t < 3; t++) { mb.SetVertexChannelData(normalVertexChannelIndex, ToXna(sceneMesh.Normals[sceneMesh.Faces[f].Indices[t]])); for (int x = 0; x < sceneMesh.TextureCoordsChannelCount; x++) { mb.SetVertexChannelData(texCoordVertexChannelIndex[x], ToXnaVector2((sceneMesh.GetTextureCoords(x))[sceneMesh.Faces[f].Indices[t]])); } if (sceneMesh.HasBones) { mb.SetVertexChannelData(boneWeightVertexChannelIndex, boneWeightData[sceneMesh.Faces[f].Indices[t]]); } mb.AddTriangleVertex((int)(sceneMesh.Faces[f].Indices[t])); } } MeshContent mesh = mb.FinishMesh(); rootNode.Children.Add(mesh); meshes.Add(sceneMesh, mesh); } return(rootNode); }