/// <summary> /// Creates the vertex channel using the MeshBuilder (no data is added yet). /// </summary> protected virtual void Create() { var usage = _colladaVertexChannel.Description.VertexElementUsage; int usageIndex = _colladaVertexChannel.Description.UsageIndex; // Construct correct usage string String usageString = VertexChannelNames.EncodeName(usage, usageIndex); // Generic standard channel (TexCoord, Normal, Binormal, etc.) switch (_colladaVertexChannel.Description.VertexElementFormat) { case VertexElementFormat.Vector4: _channelIndex = _meshBuilder.CreateVertexChannel <Vector4>(usageString); break; case VertexElementFormat.Vector3: _channelIndex = _meshBuilder.CreateVertexChannel <Vector3>(usageString); break; case VertexElementFormat.Vector2: _channelIndex = _meshBuilder.CreateVertexChannel <Vector2>(usageString); break; case VertexElementFormat.Single: _channelIndex = _meshBuilder.CreateVertexChannel <Single>(usageString); break; default: throw new Exception("Unexpected vertex element format"); } }
private static void ProcessWeightsChannel(ContentProcessorContext context, string asset, GeometryContent geometry, int vertexChannelIndex, Dictionary <string, int> boneIndices, Dictionary <int, int> boneRemap) { if (boneIndices == null) { throw new InvalidContentException("Mesh has bone weights with no skeleton"); } VertexChannelCollection channels = geometry.Vertices.Channels; VertexChannel channel2 = channels[vertexChannelIndex]; VertexChannel <BoneWeightCollection> channel = channel2 as VertexChannel <BoneWeightCollection>; Byte4[] outputIndices = new Byte4[channel.Count]; Vector4[] outputWeights = new Vector4[channel.Count]; for (int i = 0; i < channel.Count; i++) { BoneWeightCollection inputWeights = channel[i]; ConvertVertexWeights(context, asset, inputWeights, boneIndices, outputIndices, outputWeights, i, geometry, boneRemap); } int usageIndex = VertexChannelNames.DecodeUsageIndex(channel.Name); string name = VertexChannelNames.EncodeName(VertexElementUsage.BlendIndices, usageIndex); string str = VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, usageIndex); channels.Insert <Byte4>(vertexChannelIndex + 1, name, outputIndices); channels.Insert <Vector4>(vertexChannelIndex + 2, str, outputWeights); channels.RemoveAt(vertexChannelIndex); }
static void ProcessWeightsChannel(GeometryContent geometry, int vertexChannelIndex) { // create a map of Name->Index of the bones BoneContent skeleton = MeshHelper.FindSkeleton(geometry.Parent); Dictionary <string, int> boneIndices = new Dictionary <string, int>(); IList <BoneContent> flattenedBones = MeshHelper.FlattenSkeleton(skeleton); for (int i = 0; i < flattenedBones.Count; i++) { boneIndices.Add(flattenedBones[i].Name, i); } // convert all of our bone weights into the correct indices and weight values VertexChannel <BoneWeightCollection> inputWeights = geometry.Vertices.Channels[vertexChannelIndex] as VertexChannel <BoneWeightCollection>; Vector4[] outputIndices = new Vector4[inputWeights.Count]; Vector4[] outputWeights = new Vector4[inputWeights.Count]; for (int i = 0; i < inputWeights.Count; i++) { ConvertWeights(inputWeights[i], boneIndices, outputIndices, outputWeights, i, geometry); } // create our new channel names int usageIndex = VertexChannelNames.DecodeUsageIndex(inputWeights.Name); string indicesName = VertexChannelNames.EncodeName(VertexElementUsage.BlendIndices, usageIndex); string weightsName = VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, usageIndex); // add in the index and weight channels geometry.Vertices.Channels.Insert(vertexChannelIndex + 1, indicesName, outputIndices); geometry.Vertices.Channels.Insert(vertexChannelIndex + 2, weightsName, outputWeights); // remove the original weights channel geometry.Vertices.Channels.RemoveAt(vertexChannelIndex); }
static void ProcessWeightsChannel(GeometryContent geometry, int vertexChannelIndex, SerializableSkeleton skeleton) { Dictionary <string, int> boneIndices = skeleton.boneIndexByName; // convert all of our bone weights into the correct indices and weight values VertexChannel <BoneWeightCollection> inputWeights = geometry.Vertices.Channels[vertexChannelIndex] as VertexChannel <BoneWeightCollection>; Vector4[] outputIndices = new Vector4[inputWeights.Count]; Vector4[] outputWeights = new Vector4[inputWeights.Count]; for (int i = 0; i < inputWeights.Count; i++) { ConvertWeights(inputWeights[i], boneIndices, outputIndices, outputWeights, i, geometry); } // create our new channel names int usageIndex = VertexChannelNames.DecodeUsageIndex(inputWeights.Name); string indicesName = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.BlendIndices, usageIndex); string weightsName = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.BlendWeight, usageIndex); // add in the index and weight channels geometry.Vertices.Channels.Insert(vertexChannelIndex + 1, indicesName, outputIndices); geometry.Vertices.Channels.Insert(vertexChannelIndex + 2, weightsName, outputWeights); // remove the original weights channel geometry.Vertices.Channels.RemoveAt(vertexChannelIndex); }
private static void ProcessWeightsChannel( GeometryContent geometry, int vertexChannelIndex, ContentIdentity identity) { // NOTE: Portions of this code is from the XNA CPU Skinning // sample under Ms-PL, (c) Microsoft Corporation. // create a map of Name->Index of the bones var skeleton = MeshHelper.FindSkeleton(geometry.Parent); if (skeleton == null) { throw new InvalidContentException( "Skeleton not found. Meshes that contain a Weights vertex channel cannot " + "be processed without access to the skeleton data.", identity); } var boneIndices = new Dictionary <string, byte>(); var flattenedBones = MeshHelper.FlattenSkeleton(skeleton); if (flattenedBones.Count > byte.MaxValue) { throw new NotSupportedException("The flattened skeleton contains more than 255 bones."); } for (int i = 0; i < flattenedBones.Count; i++) { boneIndices.Add(flattenedBones[i].Name, (byte)i); } var vertexChannel = geometry.Vertices.Channels[vertexChannelIndex]; if (!(vertexChannel is VertexChannel <BoneWeightCollection> inputWeights)) { throw new InvalidContentException( string.Format( "Vertex channel \"{0}\" is the wrong type. It has element type {1}. Type {2} is expected.", vertexChannel.Name, vertexChannel.ElementType.FullName, typeof(BoneWeightCollection).FullName), identity); } var outputIndices = new Byte4[inputWeights.Count]; var outputWeights = new Vector4[inputWeights.Count]; for (var i = 0; i < inputWeights.Count; i++) { ConvertWeights(inputWeights[i], boneIndices, outputIndices, outputWeights, i); } // create our new channel names var usageIndex = VertexChannelNames.DecodeUsageIndex(inputWeights.Name); var indicesName = VertexChannelNames.EncodeName(VertexElementUsage.BlendIndices, usageIndex); var weightsName = VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, usageIndex); // add in the index and weight channels geometry.Vertices.Channels.Insert(vertexChannelIndex + 1, indicesName, outputIndices); geometry.Vertices.Channels.Insert(vertexChannelIndex + 2, weightsName, outputWeights); // remove the original weights channel geometry.Vertices.Channels.RemoveAt(vertexChannelIndex); }
private void ProcessGeometry(GeometryContent xnaGeometry) { // find and process the geometry's bone weights for (int i = 0; i < xnaGeometry.Vertices.Channels.Count; i++) { string channelName = xnaGeometry.Vertices.Channels[i].Name; string baseName = VertexChannelNames.DecodeBaseName(channelName); if (baseName == "Weights") { ProcessWeightsChannel(xnaGeometry, i, outputModel.skeleton); } } // retrieve the four vertex channels we require for CPU skinning. we ignore any // other channels the model might have. string normalName = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.Normal, 0); string texCoordName = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.TextureCoordinate, 0); string blendWeightName = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.BlendWeight, 0); string blendIndexName = VertexChannelNames.EncodeName(Microsoft.Xna.Framework.Graphics.VertexElementUsage.BlendIndices, 0); VertexChannel <Vector3> normals = xnaGeometry.Vertices.Channels[normalName] as VertexChannel <Vector3>; VertexChannel <Vector2> texCoords = xnaGeometry.Vertices.Channels[texCoordName] as VertexChannel <Vector2>; VertexChannel <Vector4> blendWeights = xnaGeometry.Vertices.Channels[blendWeightName] as VertexChannel <Vector4>; VertexChannel <Vector4> blendIndices = xnaGeometry.Vertices.Channels[blendIndexName] as VertexChannel <Vector4>; // create our array of vertices int triangleCount = xnaGeometry.Indices.Count / 3; SerializableVertex[] vertices = new SerializableVertex[xnaGeometry.Vertices.VertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i] = new SerializableVertex { position = xnaGeometry.Vertices.Positions[i], normal = normals[i], texture = texCoords[i], blendweights = blendWeights[i], blendindices = blendIndices[i] }; } int[] indices = new int[xnaGeometry.Indices.Count]; for (int i = 0; i < xnaGeometry.Indices.Count; i++) { indices[i] = xnaGeometry.Indices[i]; } SerializableMesh mesh = new SerializableMesh(); mesh.name = string.Format("mesh_{0}_{1}", outputModel.meshList.Count, xnaGeometry.Name); mesh.textureName = GetTextureName(xnaGeometry); mesh.vertices = vertices; mesh.indices = indices; outputModel.meshList.Add(mesh); }
void ProcessGeometry(GeometryContent geometry) { // find and process the geometry's bone weights for (int i = 0; i < geometry.Vertices.Channels.Count; i++) { string channelName = geometry.Vertices.Channels[i].Name; string baseName = VertexChannelNames.DecodeBaseName(channelName); if (baseName == "Weights") { ProcessWeightsChannel(geometry, i); } } // retrieve the four vertex channels we require for CPU skinning. we ignore any // other channels the model might have. string normalName = VertexChannelNames.EncodeName(VertexElementUsage.Normal, 0); string texCoordName = VertexChannelNames.EncodeName(VertexElementUsage.TextureCoordinate, 0); string blendWeightName = VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, 0); string blendIndexName = VertexChannelNames.EncodeName(VertexElementUsage.BlendIndices, 0); VertexChannel <Vector3> normals = geometry.Vertices.Channels[normalName] as VertexChannel <Vector3>; VertexChannel <Vector2> texCoords = geometry.Vertices.Channels[texCoordName] as VertexChannel <Vector2>; VertexChannel <Vector4> blendWeights = geometry.Vertices.Channels[blendWeightName] as VertexChannel <Vector4>; VertexChannel <Vector4> blendIndices = geometry.Vertices.Channels[blendIndexName] as VertexChannel <Vector4>; // create our array of vertices int triangleCount = geometry.Indices.Count / 3; CpuVertex[] vertices = new CpuVertex[geometry.Vertices.VertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i] = new CpuVertex { Position = geometry.Vertices.Positions[i], Normal = normals[i], TextureCoordinate = texCoords[i], BlendWeights = blendWeights[i], BlendIndices = blendIndices[i] }; } // Convert the input material. MaterialContent material = ProcessMaterial(geometry.Material); // Add the new piece of geometry to our output model. outputModel.AddModelPart(triangleCount, geometry.Indices, vertices, material as BasicMaterialContent); }
protected virtual void ProcessGeometryUsingMaterial(MaterialContent material, IEnumerable <GeometryContent> geometryCollection, ContentProcessorContext context) { // If we don't get a material then assign a default one. if (material == null) { material = MaterialProcessor.CreateDefaultMaterial(DefaultEffect); } // Test requirements from the assigned material. int textureChannels; bool vertexWeights = false; if (material is DualTextureMaterialContent) { textureChannels = 2; } else if (material is SkinnedMaterialContent) { textureChannels = 1; vertexWeights = true; } else if (material is EnvironmentMapMaterialContent) { textureChannels = 1; } else if (material is AlphaTestMaterialContent) { textureChannels = 1; } else { // Just check for a "Texture" which should cover custom Effects // and BasicEffect which can have an optional texture. textureChannels = material.Textures.ContainsKey("Texture") ? 1 : 0; } // By default we must set the vertex color property // to match XNA behavior. material.OpaqueData["VertexColorEnabled"] = false; // If we run into a geometry that requires vertex // color we need a seperate material for it. var colorMaterial = material.Clone(); colorMaterial.OpaqueData["VertexColorEnabled"] = true; foreach (var geometry in geometryCollection) { // Process the geometry. for (var i = 0; i < geometry.Vertices.Channels.Count; i++) { ProcessVertexChannel(geometry, i, context); } // Verify we have the right number of texture coords. for (var i = 0; i < textureChannels; i++) { if (!geometry.Vertices.Channels.Contains(VertexChannelNames.TextureCoordinate(i))) { throw new InvalidContentException( string.Format("The mesh \"{0}\", using {1}, contains geometry that is missing texture coordinates for channel {2}.", geometry.Parent.Name, MaterialProcessor.GetDefaultEffect(material), i), _identity); } } // Do we need to enable vertex color? if (geometry.Vertices.Channels.Contains(VertexChannelNames.Color(0))) { geometry.Material = colorMaterial; } else { geometry.Material = material; } // Do we need vertex weights? if (vertexWeights) { var weightsName = VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, 0); if (!geometry.Vertices.Channels.Contains(weightsName)) { throw new InvalidContentException( string.Format("The skinned mesh \"{0}\" contains geometry without any vertex weights.", geometry.Parent.Name), _identity); } } } }
// Converts a channel of type BoneWeightCollection to two new channels: // Byte4 indices + Vector4 weights private void ProcessWeightsChannel(GeometryContent geometry, int vertexChannelIndex) { if (_skeleton == null) { // No skeleton? Remove BoneWeightCollection. geometry.Vertices.Channels.RemoveAt(vertexChannelIndex); return; } if (_skeleton.NumberOfBones > 255) { string message = String.Format( CultureInfo.InvariantCulture, "Too many bones in skeleton. Actual number of bones: {0}. Allowed number of bones: {1}.", _skeleton.NumberOfBones, 255); throw new InvalidContentException(message, _rootBone.Identity); } var channels = geometry.Vertices.Channels; var channel = channels[vertexChannelIndex]; var boneWeightChannel = channel as VertexChannel <BoneWeightCollection>; if (boneWeightChannel == null) { string message = String.Format( CultureInfo.InvariantCulture, "Vertex channel \"{0}\" has wrong content type. Actual type: {1}. Expected type: {2}.", channel.Name, channel.ElementType, typeof(BoneWeightCollection)); throw new InvalidContentException(message, geometry.Parent.Identity); } // Create two channels (Byte4 indices + Vector4 weights) from a BoneWeight channel. Byte4[] boneIndices = new Byte4[boneWeightChannel.Count]; Vector4[] boneWeights = new Vector4[boneWeightChannel.Count]; for (int i = 0; i < boneWeightChannel.Count; i++) { // Convert bone weights for vertex i. var boneWeightCollection = boneWeightChannel[i]; if (boneWeightCollection == null) { string message = String.Format( CultureInfo.InvariantCulture, "NULL entry found in channel \"{0}\". Expected element type: {1}.", boneWeightChannel.Name, typeof(BoneWeightCollection)); throw new InvalidContentException(message, geometry.Parent.Identity); } ConvertBoneWeights(boneWeightCollection, boneIndices, boneWeights, i, geometry); } // The current channel has the name "WeightsN", where N is the usage index. // Get the usage index. int usageIndex = VertexChannelNames.DecodeUsageIndex(boneWeightChannel.Name); // Store the converted bone information in two new channels called "BlendIndicesN" // and "BlendWeightsN". string blendIndices = VertexChannelNames.EncodeName(VertexElementUsage.BlendIndices, usageIndex); if (channels.Contains(blendIndices)) { string message = String.Format( CultureInfo.InvariantCulture, "Cannot store converted blend indices for vertex channel \"{0}\", because a vertex channel called \"{1}\" already exists.", boneWeightChannel.Name, blendIndices); throw new InvalidContentException(message, geometry.Parent.Identity); } string blendWeights = VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, usageIndex); if (channels.Contains(blendWeights)) { string message = String.Format( CultureInfo.InvariantCulture, "Cannot store converted blend weights for vertex channel \"{0}\", because a vertex channel called \"{1}\" already exists.", boneWeightChannel.Name, blendWeights); throw new InvalidContentException(message, geometry.Parent.Identity); } // Insert the new channels after "WeightsN" and remove "WeightsN". channels.Insert(vertexChannelIndex + 1, blendIndices, boneIndices); channels.Insert(vertexChannelIndex + 2, blendWeights, boneWeights); channels.RemoveAt(vertexChannelIndex); }
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); } } } } }
} // Process #endregion #region Process Vertex Channel /// <summary> /// Processes geometry content vertex channels at the specified index. /// </summary> protected override void ProcessVertexChannel(GeometryContent geometry, int vertexChannelIndex, ContentProcessorContext context) { // Compressed Vertex Data VertexChannelCollection channels = geometry.Vertices.Channels; string name = channels[vertexChannelIndex].Name; if (name == VertexChannelNames.Normal()) { channels.ConvertChannelContent <NormalizedShort4>(vertexChannelIndex); } else if (name == VertexChannelNames.TextureCoordinate(0)) { // If the resource has texture coordinates outside the range [-1, 1] the values will be clamped. channels.ConvertChannelContent <HalfVector2>(vertexChannelIndex); } else if (name == VertexChannelNames.TextureCoordinate(1)) { channels.Remove(VertexChannelNames.TextureCoordinate(1)); } else if (name == VertexChannelNames.TextureCoordinate(2)) { channels.Remove(VertexChannelNames.TextureCoordinate(2)); } else if (name == VertexChannelNames.TextureCoordinate(3)) { channels.Remove(VertexChannelNames.TextureCoordinate(3)); } else if (name == VertexChannelNames.TextureCoordinate(4)) { channels.Remove(VertexChannelNames.TextureCoordinate(4)); } else if (name == VertexChannelNames.TextureCoordinate(5)) { channels.Remove(VertexChannelNames.TextureCoordinate(5)); } else if (name == VertexChannelNames.TextureCoordinate(6)) { channels.Remove(VertexChannelNames.TextureCoordinate(6)); } else if (name == VertexChannelNames.TextureCoordinate(7)) { channels.Remove(VertexChannelNames.TextureCoordinate(7)); } else if (name == VertexChannelNames.Color(0)) { channels.Remove(VertexChannelNames.Color(0)); } else if (name == VertexChannelNames.Tangent(0)) { channels.ConvertChannelContent <NormalizedShort4>(vertexChannelIndex); } else if (name == VertexChannelNames.Binormal(0)) { // Not need to get rid of the binormal data because the model will use more than 32 bytes per vertex. // We can actually try to align the data to 64 bytes per vertex. channels.ConvertChannelContent <NormalizedShort4>(vertexChannelIndex); } else { // Blend indices, blend weights and everything else. // Don't use "BlendWeight0" as a name, nor weights0. Both names don't work. base.ProcessVertexChannel(geometry, vertexChannelIndex, context); channels.ConvertChannelContent <Byte4>("BlendIndices0"); channels.ConvertChannelContent <NormalizedShort4>(VertexChannelNames.EncodeName(VertexElementUsage.BlendWeight, 0)); } } // ProcessVertexChannel