/// <summary> /// Make sure all of the geometry contains a vertex color channel. /// </summary> /// <param name="mesh"></param> private static void EnsureVertexColors(MeshContent mesh) { foreach (GeometryContent meshPart in mesh.Geometry) { //if ((meshPart.Name != null) && !meshPart.Name.StartsWith("SCP_")) { bool foundColor = false; foreach (VertexChannel channel in meshPart.Vertices.Channels) { if (channel.Name == VertexChannelNames.Color(0)) { foundColor = true; break; } } if (!foundColor) { int cnt = meshPart.Vertices.VertexCount; Vector4[] colors = new Vector4[cnt]; for (int i = 0; i < cnt; ++i) { colors[i] = Vector4.One; } meshPart.Vertices.Channels.Add <Vector4>( VertexChannelNames.Color(0).ToString(), colors); } } } }
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); }
private static void ColorMaterials(MeshContent mesh) { foreach (GeometryContent meshPart in mesh.Geometry) { Vector4 constColor = MatColor(meshPart); bool foundColor = false; foreach (VertexChannel channel in meshPart.Vertices.Channels) { if (channel.Name == VertexChannelNames.Color(0)) { foundColor = true; VertexChannel <Vector4> colorChannel = (VertexChannel <Vector4>)channel; for (int i = 0; i < colorChannel.Count; ++i) { colorChannel[i] = constColor; } } } if (!foundColor) { int cnt = meshPart.Vertices.VertexCount; Vector4[] colors = new Vector4[cnt]; for (int i = 0; i < cnt; ++i) { colors[i] = constColor; } meshPart.Vertices.Channels.Add <Vector4>( VertexChannelNames.Color(0).ToString(), colors); } } }
private void AddVertexColorChannel(VertexContent content) { if (content.Channels.Contains(VertexChannelNames.Color(0)) == false) { List <Microsoft.Xna.Framework.Color> VertexColors = new List <Microsoft.Xna.Framework.Color>(); for (int i = 0; i < content.VertexCount; i++) { VertexColors.Add(Color.Purple); } content.Channels.Add(VertexChannelNames.Color(0), VertexColors); } }
// Checks if the geometries have a Color vertex channel and updates the VertexColorEnabled // flag in the opaque material data. If necessary, the material is cloned to create one // material with and one without vertex colors. private static void SetVertexColorEnabled(List <GeometryContent> geometries) { var material = geometries[0].Material; bool geometryWithVertexColorsFound = false; bool geometryWithoutVertexColorsFound = false; foreach (GeometryContent geometry in geometries) { if (geometry.Vertices.Channels.Contains(VertexChannelNames.Color(0))) { geometryWithVertexColorsFound = true; } else { geometryWithoutVertexColorsFound = true; } } if (geometryWithVertexColorsFound && geometryWithoutVertexColorsFound) { // There are some geometries that have a vertex color and some that don't. // --> Create a copy of the current material. One material is used for geometry // with vertex colors enabled. The other for the geometry where vertex colors // are disabled. MaterialContent clonedMaterial = (MaterialContent)Activator.CreateInstance(material.GetType()); ContentHelper.CopyMaterialContent(material, clonedMaterial); material.OpaqueData["VertexColorEnabled"] = true; clonedMaterial.OpaqueData["VertexColorEnabled"] = false; foreach (GeometryContent geometry in geometries) { if (!geometry.Vertices.Channels.Contains(VertexChannelNames.Color(0))) { geometry.Material = clonedMaterial; } } } else { if (geometryWithVertexColorsFound) { material.OpaqueData["VertexColorEnabled"] = true; } else { material.OpaqueData.Remove("VertexColorEnabled"); } } }
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 GenerateVertexColorChannel(NodeContent node) { MeshContent mesh = node as MeshContent; if (mesh != null) { foreach (var geometry in mesh.Geometry) { if (!geometry.Vertices.Channels.Contains(VertexChannelNames.Color(0))) { geometry.Vertices.Channels.Add <Color>(VertexChannelNames.Color(0), Enumerable.Range(0, geometry.Vertices.VertexCount).Select(i => Color.White)); } } } foreach (NodeContent child in node.Children) { GenerateVertexColorChannel(child); } }
private void ProcessBasicMaterial(BasicMaterialContent basicMaterial, GeometryContent geometry) { if (basicMaterial == null) { return; } // If the basic material specifies a texture, geometry must have coordinates. if (!geometry.Vertices.Channels.Contains(VertexChannelNames.TextureCoordinate(0))) { throw new InvalidContentException( "Geometry references material with texture, but no texture coordinates were found.", _identity); } // Enable vertex color if the geometry has the channel to support it. if (geometry.Vertices.Channels.Contains(VertexChannelNames.Color(0))) { basicMaterial.VertexColorEnabled = true; } }
protected virtual void ProcessGeometryUsingMaterial(MaterialContent material, IEnumerable <GeometryContent> geometryCollection, ContentProcessorContext context) { var basicMaterial = material as BasicMaterialContent; if (material == null) { material = basicMaterial = new BasicMaterialContent(); } foreach (var geometry in geometryCollection) { for (var i = 0; i < geometry.Vertices.Channels.Count; i++) { ProcessVertexChannel(geometry, i, context); } if (basicMaterial != null) { // If the basic material specifies a texture, geometry must have coordinates. if (!geometry.Vertices.Channels.Contains(VertexChannelNames.TextureCoordinate(0))) { throw new InvalidContentException( "Geometry references material with texture, but no texture coordinates were found.", _identity); } // Enable vertex color if the geometry has the channel to support it. if (geometry.Vertices.Channels.Contains(VertexChannelNames.Color(0))) { basicMaterial.VertexColorEnabled = true; } } } }
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); } } } }
} // 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
} // 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) { VertexChannelCollection channels = geometry.Vertices.Channels; // If the model has only position and normals a UV channel is added. // http://xnafinalengine.codeplex.com/wikipage?title=Compressed%20Vertex%20Data if (channels.Count == 1 && channels.Contains(VertexChannelNames.Normal())) { channels.Add<Vector2>(VertexChannelNames.TextureCoordinate(0), null); } // If the model has position, normal and UV then the data is packed on 32 bytes aliagned vertex data. if (channels.Count == 2 && channels.Contains(VertexChannelNames.Normal()) && channels.Contains(VertexChannelNames.TextureCoordinate(0))) { // No compressed Vertex Data base.ProcessVertexChannel(geometry, vertexChannelIndex, context); } else // If not then the data is compressed. { string name = channels[vertexChannelIndex].Name; if (name == VertexChannelNames.Normal()) { channels.ConvertChannelContent<NormalizedShort4>(vertexChannelIndex); } else if (name == VertexChannelNames.TextureCoordinate(0)) { // Clamp values. /*for (int i = 0; i < channels[vertexChannelIndex].Count; i++) { Vector2 uv = (Vector2)channels[vertexChannelIndex][i]; if (uv.X < 0) uv.X *= -1; if (uv.Y < 0) uv.Y *= -1; Vector2 uvCampled = new Vector2(uv.X - (float)Math.Truncate(uv.X), uv.Y - (float)Math.Truncate(uv.Y)); channels[vertexChannelIndex][i] = uvCampled; } // If the resource has texture coordinates outside the range [-1, 1] the values will be clamped. channels.ConvertChannelContent<NormalizedShort2>(vertexChannelIndex);*/ // Sometimes you can't just clamp values, because the distance between vertices surpass 1 uv unit. // And given that I am not removing the binormals I won't normalize the UVs. 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)) { channels.ConvertChannelContent<NormalizedShort4>(vertexChannelIndex); // If the binormal is removed then the position, the normal, // the tangent and one texture coordinate can be fetched in one single block of 32 bytes. // Still, it is more fast to just pass the value. At least on the test I made. //channels.Remove(VertexChannelNames.Binormal(0)); } else { base.ProcessVertexChannel(geometry, vertexChannelIndex, context); } } } // ProcessVertexChannel
MeshData ProcessMesh(MeshContent mesh, ContentProcessorContext context, string rootPath, Dictionary <string, object> processedContent, SkeletonData skeletonData, AnimationData[] animations, ref int geometryCount) { MeshHelper.TransformScene(mesh, mesh.AbsoluteTransform); string[] normalMapNames = new string[] { "Bump0", "Bump", "NormalMap", "Normalmap", "Normals", "BumpMap" }; MeshHelper.OptimizeForCache(mesh); foreach (GeometryContent geom in mesh.Geometry) { if (geom.Material != null) { string map = MaterialTexture(geom.Material, rootPath, null, null, normalMapNames); if (map != null && map.Length > 0) { generateTangents = true; } } } if (generateTangents) { MeshHelper.CalculateNormals(mesh, false); bool hasNoTangent = !GeometryContainsChannel(mesh, VertexChannelNames.Tangent(0)); bool hasNoBinorm = !GeometryContainsChannel(mesh, VertexChannelNames.Binormal(0)); if (hasNoTangent || hasNoBinorm) { string tangentChannelName = hasNoTangent ? VertexChannelNames.Tangent(0) : null; string binormalChannelName = hasNoBinorm ? VertexChannelNames.Binormal(0) : null; MeshHelper.CalculateTangentFrames(mesh, VertexChannelNames.TextureCoordinate(0), tangentChannelName, binormalChannelName); } } if (swapWinding) { MeshHelper.SwapWindingOrder(mesh); } List <GeometryData> geometry = new List <GeometryData>(); BoneContent skeleton = MeshHelper.FindSkeleton(mesh); Dictionary <string, int> boneIndices = null; if (skeleton != null) { boneIndices = FlattenSkeleton(skeleton); } foreach (GeometryContent geom in mesh.Geometry) { this.ProcessVertexChannels(geom, context, rootPath, boneIndices, null); MeshHelper.MergeDuplicateVertices(geom); MaterialData material = new MaterialData( MaterialValue <float>("Alpha", geom.Material, 1), MaterialValue <float>("SpecularPower", geom.Material, 24), MaterialValue <Vector3>("DiffuseColor", geom.Material, Vector3.One), MaterialValue <Vector3>("EmissiveColor", geom.Material, Vector3.Zero), MaterialValue <Vector3>("SpecularColor", geom.Material, Vector3.Zero), MaterialTexture(geom.Material, rootPath, context, processedContent, "Texture"), MaterialTexture(geom.Material, rootPath, context, processedContent, normalMapNames), MaterialValue <bool>("VertexColorEnabled", geom.Material, true) && geom.Vertices.Channels.Contains(VertexChannelNames.Color(0))); VertexBufferContent vb; VertexElement[] ve; geom.Vertices.CreateVertexBuffer(out vb, out ve, context.TargetPlatform); int[] indices = new int[geom.Indices.Count]; geom.Indices.CopyTo(indices, 0); geometry.Add(new GeometryData(geometryCount++, geom.Name, ve, vb.VertexData, indices, material, skeletonData, animations, context.TargetPlatform == TargetPlatform.Xbox360)); } return(new MeshData(mesh.Name, geometry.ToArray(), animations)); }
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 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 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); }
/// <summary> /// Import a VOX file as Model /// </summary> private NodeContent VoxelProcess(VoxelContent voxel, ContentImporterContext context) { XYZI[] voxels = voxel.Voxels; uint[] palette = voxel.Palette; var scale = voxel.RealSize / voxel.GridSize; Vector3 centerOffset = new Vector3(1f, 0f, 1f) * (voxel.RealSize / -2f); var corner000 = new Point3(0, 0, 0); var corner100 = new Point3(1, 0, 0); var corner010 = new Point3(0, 1, 0); var corner110 = new Point3(1, 1, 0); var corner001 = new Point3(0, 0, 1); var corner101 = new Point3(1, 0, 1); var corner011 = new Point3(0, 1, 1); var corner111 = new Point3(1, 1, 1); var Forward = Vector3.Forward; var Backward = Vector3.Backward; var Left = Vector3.Left; var Right = Vector3.Right; var Up = Vector3.Up; var Down = Vector3.Down; for (int i = 0; i < voxels.Length; i++) { var pt000 = voxels[i].Point.Add(ref corner000); var pt100 = voxels[i].Point.Add(ref corner100); var pt010 = voxels[i].Point.Add(ref corner010); var pt110 = voxels[i].Point.Add(ref corner110); var pt001 = voxels[i].Point.Add(ref corner001); var pt101 = voxels[i].Point.Add(ref corner101); var pt011 = voxels[i].Point.Add(ref corner011); var pt111 = voxels[i].Point.Add(ref corner111); // back var p0 = pt000.ToVector3(); var p1 = pt100.ToVector3(); var p2 = pt010.ToVector3(); var p3 = pt110.ToVector3(); // front var p4 = pt001.ToVector3(); var p5 = pt101.ToVector3(); var p6 = pt011.ToVector3(); var p7 = pt111.ToVector3(); Vector3.Multiply(ref p0, ref scale, out p0); Vector3.Add(ref p0, ref centerOffset, out p0); Vector3.Multiply(ref p1, ref scale, out p1); Vector3.Add(ref p1, ref centerOffset, out p1); Vector3.Multiply(ref p2, ref scale, out p2); Vector3.Add(ref p2, ref centerOffset, out p2); Vector3.Multiply(ref p3, ref scale, out p3); Vector3.Add(ref p3, ref centerOffset, out p3); Vector3.Multiply(ref p4, ref scale, out p4); Vector3.Add(ref p4, ref centerOffset, out p4); Vector3.Multiply(ref p5, ref scale, out p5); Vector3.Add(ref p5, ref centerOffset, out p5); Vector3.Multiply(ref p6, ref scale, out p6); Vector3.Add(ref p6, ref centerOffset, out p6); Vector3.Multiply(ref p7, ref scale, out p7); Vector3.Add(ref p7, ref centerOffset, out p7); vertex.Color.PackedValue = palette[voxels[i].ColorIndex]; if ((voxels[i].SharedSides & Sides.Forward) == 0) { vertex.Normal = Forward; AddVertex(ref p1); AddVertex(ref p3); AddVertex(ref p0); AddVertex(ref p0); AddVertex(ref p3); AddVertex(ref p2); } if ((voxels[i].SharedSides & Sides.Backward) == 0) { vertex.Normal = Backward; AddVertex(ref p4); AddVertex(ref p6); AddVertex(ref p5); AddVertex(ref p5); AddVertex(ref p6); AddVertex(ref p7); } if ((voxels[i].SharedSides & Sides.Left) == 0) { vertex.Normal = Left; AddVertex(ref p2); AddVertex(ref p6); AddVertex(ref p0); AddVertex(ref p0); AddVertex(ref p6); AddVertex(ref p4); } if ((voxels[i].SharedSides & Sides.Right) == 0) { vertex.Normal = Right; AddVertex(ref p1); AddVertex(ref p5); AddVertex(ref p3); AddVertex(ref p3); AddVertex(ref p5); AddVertex(ref p7); } if ((voxels[i].SharedSides & Sides.Up) == 0) { vertex.Normal = Up; AddVertex(ref p7); AddVertex(ref p6); AddVertex(ref p3); AddVertex(ref p3); AddVertex(ref p6); AddVertex(ref p2); } if ((voxels[i].SharedSides & Sides.Down) == 0) { vertex.Normal = Down; AddVertex(ref p5); AddVertex(ref p1); AddVertex(ref p4); AddVertex(ref p4); AddVertex(ref p1); AddVertex(ref p0); } } MeshContent mesh = new MeshContent(); mesh.Name = "voxel"; for (int pi = 0; pi < this.vertices.Count; pi++) { mesh.Positions.Add(this.vertices[pi].Position); } var geom = new GeometryContent(); mesh.Geometry.Add(geom); BasicMaterialContent material = new BasicMaterialContent(); geom.Material = material; for (int pi = 0; pi < this.vertices.Count; pi++) { geom.Vertices.Add(pi); } for (int ii = 0; ii < this.indices.Count; ii++) { geom.Indices.Add(this.indices[ii]); } List <Vector3> normals = new List <Vector3>(); List <Color> colors = new List <Color>(); for (int vi = 0; vi < this.vertices.Count; vi++) { var vertex = vertices[vi]; normals.Add(vertex.Normal); colors.Add(vertex.Color); } geom.Vertices.Channels.Add <Vector3>(VertexChannelNames.Normal(0), normals); geom.Vertices.Channels.Add <Color>(VertexChannelNames.Color(0), colors); return(mesh); }
// Build a SubmeshInfo for each GeometryContent. private SubmeshInfo[] BuildSubmeshInfos(MeshContent mesh, List <MeshContent> inputMorphs) { bool hasMorphTargets = (inputMorphs != null && inputMorphs.Count > 0); // A lookup table that maps each material to its index. // The key is the name of the XML file (string) or the local material (MaterialContent). var materialLookupTable = new Dictionary <object, int>(); int numberOfSubmeshes = mesh.Geometry.Count; var submeshInfos = new SubmeshInfo[numberOfSubmeshes]; for (int i = 0; i < numberOfSubmeshes; i++) { var geometry = mesh.Geometry[i]; // Build morph targets for current submesh. List <DRMorphTargetContent> morphTargets = null; if (hasMorphTargets) { morphTargets = BuildMorphTargets(geometry, inputMorphs, i); if (morphTargets != null && morphTargets.Count > 0) { // When morph targets are used remove the "BINORMAL" channel. (Otherwise, // the number of vertex attributes would exceed the limit. Binormals need // to be reconstructed from normal and tangent in the vertex shader.) string binormalName = VertexChannelNames.Binormal(0); bool containsTangentFrames = geometry.Vertices.Channels.Remove(binormalName); if (containsTangentFrames) { // A submesh cannot use vertex colors and tangents at the same time. // This would also exceed the vertex attribute limit. string colorName = VertexChannelNames.Color(0); if (geometry.Vertices.Channels.Contains(colorName)) { geometry.Vertices.Channels.Remove(colorName); } } } } var submeshInfo = new SubmeshInfo { Geometry = geometry, OriginalIndex = i, VertexBuffer = geometry.Vertices.CreateVertexBuffer(), MorphTargets = morphTargets }; submeshInfo.VertexBufferIndex = GetVertexBufferIndex(submeshInfo.VertexBuffer.VertexDeclaration); // Get material file or local material. object material = (object)GetExternalMaterial(mesh, geometry) ?? geometry.Material; if (material == null) { var message = string.Format(CultureInfo.InvariantCulture, "Mesh \"{0}\" does not have a material.", mesh); throw new InvalidContentException(message, mesh.Identity); } int materialIndex; if (!materialLookupTable.TryGetValue(material, out materialIndex)) { materialIndex = materialLookupTable.Count; materialLookupTable.Add(material, materialIndex); } submeshInfo.MaterialIndex = materialIndex; submeshInfo.Material = material; submeshInfos[i] = submeshInfo; } return(submeshInfos); }