Пример #1
0
        private static void AddTriangleVertex(MeshBuilder builder, XmlMesh xmlMesh, XmlSubMesh xmlSubMesh, XmlSkeleton skeleton, int vertexIndex, int normalChannel, int uvChannel, int weightsChannel)
        {
            var geometry = xmlSubMesh.Geometry;

            if (xmlSubMesh.UseSharedGeometry)
            {
                geometry = xmlMesh.SharedGeometry;
            }

            var vertex          = geometry.VertexBuffers[0].Vertices[vertexIndex];
            var uv              = geometry.VertexBuffers[1].Vertices[vertexIndex];
            var boneAssignments = xmlSubMesh.BoneAssignments.Where(x => x.VertexIndex == vertexIndex);

            if (xmlSubMesh.UseSharedGeometry)
            {
                boneAssignments = xmlMesh.SharedBoneAssignments.Where(x => x.VertexIndex == vertexIndex);
            }
            builder.SetVertexChannelData(normalChannel, vertex.Normal.AsVector3());
            builder.SetVertexChannelData(uvChannel, new Vector2(uv.TextureCoordinate.U, uv.TextureCoordinate.V));
            var weights = new BoneWeightCollection();

            foreach (var boneAssignment in boneAssignments)
            {
                weights.Add(new BoneWeight(skeleton.Bones[boneAssignment.BoneIndex].Name, boneAssignment.Weight));
            }
            builder.SetVertexChannelData(weightsChannel, weights);
            builder.AddTriangleVertex(vertexIndex);
        }
Пример #2
0
            public override void SetData(int index)
            {
                int i = _indicesChannel.Indices[index] * _vertexSize + _indicesOffset;
                int j = _weightChannel.Indices[index] * _vertexSize + _weightOffset;

                // Both channels use the same source data
                float[] data = _indicesChannel.Source.Data;

                float[] blendIndices = new float[] { data[i + 0], data[i + 1], data[i + 2], data[i + 3] };
                float[] blendWeights = new float[] { data[j + 0], data[j + 1], data[j + 2], 0 };

                // Fourth blend weight is stored implicitly
                blendWeights[3] = 1 - blendWeights[0] - blendWeights[1] - blendWeights[2];
                if (blendWeights[3] == 1)
                {
                    blendWeights[3] = 0;
                }

                BoneWeightCollection weights = new BoneWeightCollection();

                for (int k = 0; k < blendIndices.Length; k++)
                {
                    int   jointIndex  = (int)blendIndices[k];
                    float jointWeight = blendWeights[k];
                    if (jointWeight <= 0)
                    {
                        continue;
                    }

                    String jointName = GetJointKey(_joints[jointIndex]);

                    weights.Add(new BoneWeight(jointName, jointWeight));
                }

                if (weights.Count > 0)
                {
                    _meshBuilder.SetVertexChannelData(_channelIndex, weights);
                }
            }
Пример #3
0
        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);
        }
Пример #4
0
        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);
        }
Пример #5
0
        public static MeshContent BuildMesh(MMDModel1 model, string filename)
        {
            //メッシュ作成
            MeshContent buildingMesh = new MeshContent();
            //まずは頂点を登録。
            //ひげねこ氏によるとモデルごとのローカル座標である必要があるようだが
            //pmdはモデル一つ=変換必要なし
            foreach (var vec in model.Vertexes)
                buildingMesh.Positions.Add(MMDXMath.ToVector3(vec.Pos));

            //ジオメトリとマテリアルの作成
            //メモ:頂点が3つ合わさって面、面が幾つか集まってジオメトリ。ジオメトリにマテリアルが付随。ジオメトリの集合がメッシュ
            long FaceIndex = 0;
            Dictionary<ushort, int> vertMap = new Dictionary<ushort, int>();
            //ジオメトリとマテリアルの生成
            for (int i = 0; i < model.Materials.Length; i++)
            {
                GeometryContent geometry = new GeometryContent();
                BasicMaterialContent material = new BasicMaterialContent();
                geometry.Material = material;
                //マテリアル設定
                material.VertexColorEnabled = false;//頂点カラー無し
                material.Alpha = model.Materials[i].Alpha;
                material.DiffuseColor = MMDXMath.ToVector3(model.Materials[i].DiffuseColor);
                material.EmissiveColor = MMDXMath.ToVector3(model.Materials[i].MirrorColor);
                material.SpecularColor = MMDXMath.ToVector3(model.Materials[i].SpecularColor);
                material.SpecularPower = model.Materials[i].Specularity;
                if (!string.IsNullOrEmpty(model.Materials[i].TextureFileName))
                    material.Texture = new ExternalReference<TextureContent>(NormalizeFilepath(model.Materials[i].TextureFileName, filename));
                if (!string.IsNullOrEmpty(model.Materials[i].SphereTextureFileName))
                {
                    if (Path.GetExtension(model.Materials[i].SphereTextureFileName).ToLower() == ".sph")
                    {
                        material.OpaqueData.Add("UseSphere", 1);
                    }
                    else if (Path.GetExtension(model.Materials[i].SphereTextureFileName).ToLower() == ".spa")
                    {
                        material.OpaqueData.Add("UseSphere", 2);
                    }
                    else
                        throw new InvalidContentException("スフィアマップは*.sph, *.spaのみ指定可能です: " + model.Materials[i].SphereTextureFileName);
                    material.Textures.Add("Sphere", new ExternalReference<TextureContent>(ProcessSphere(NormalizeFilepath(model.Materials[i].SphereTextureFileName, filename))));
                }
                else
                {
                    material.OpaqueData.Add("UseSphere", 0);
                }
                //トゥーンのテクスチャを入れる
                string toonTexPath = ToonTexManager.Instance.GetToonTexPath(model.Materials[i].ToonIndex, model.ToonFileNames, filename);
                if (!string.IsNullOrEmpty(toonTexPath))
                {
                    material.Textures.Add("ToonTex", new ExternalReference<TextureContent>(toonTexPath));
                    material.OpaqueData.Add("UseToon", true);
                }
                else
                    material.OpaqueData.Add("UseToon", false);
                //一応エッジ情報突っ込んでおく
                material.OpaqueData.Add("Edge", (model.Materials[i].EdgeFlag != 0));
                //ジオメトリのチャンネル設定
                //法線
                geometry.Vertices.Channels.Add(VertexChannelNames.Normal(0), typeof(Vector3), null);
                //テクスチャ
                if (!string.IsNullOrEmpty(model.Materials[i].TextureFileName))
                    geometry.Vertices.Channels.Add(VertexChannelNames.TextureCoordinate(0), typeof(Vector2), null);
                //ボーンウェイト
                geometry.Vertices.Channels.Add(VertexChannelNames.Weights(0), typeof(BoneWeightCollection), null);

                
                //面と頂点をジオメトリに登録
                //このマテリアルに対応する面は今までのマテリアルの面数の合計からこのマテリアルの面数個分
                vertMap.Clear();
                //マテリアルに付随する面を取得
                for (long j = FaceIndex; j < FaceIndex + model.Materials[i].FaceVertCount; j++)
                {
                    //面から頂点番号取得
                    ushort VertIndex = model.FaceVertexes[j];
                    //ジオメトリに登録済みかどうか?
                    int geoVertIndex;
                    if (!vertMap.TryGetValue(VertIndex, out geoVertIndex))
                    {
                        //未登録なので、ジオメトリに登録し、ジオメトリ頂点番号取得
                        geoVertIndex = geometry.Vertices.Add(VertIndex);
                        //頂点マップに登録
                        vertMap.Add(VertIndex, geoVertIndex);
                        //チャンネル情報の登録
                        int channelIndex = 0;
                        //法線登録
                        geometry.Vertices.Channels.Get<Vector3>(channelIndex++)[geoVertIndex] = MMDXMath.ToVector3(model.Vertexes[VertIndex].NormalVector);
                        //テクスチャ座標
                        if (!string.IsNullOrEmpty(model.Materials[i].TextureFileName))
                            geometry.Vertices.Channels.Get<Vector2>(channelIndex++)[geoVertIndex] = MMDXMath.ToVector2(model.Vertexes[VertIndex].UV);
                        //ボーンウェイト
                        BoneWeightCollection boneWeight = new BoneWeightCollection();
                        int boneNum = model.Vertexes[VertIndex].BoneNum[0];
                        if (boneNum >= 0 && boneNum < model.Bones.Length)
                            boneWeight.Add(new BoneWeight(model.Bones[boneNum].BoneName, model.Vertexes[VertIndex].BoneWeight / 100f));
                        boneNum = model.Vertexes[VertIndex].BoneNum[1];
                        if (boneNum >= 0 && boneNum < model.Bones.Length)
                            boneWeight.Add(new BoneWeight(model.Bones[boneNum].BoneName, 1.0f - model.Vertexes[VertIndex].BoneWeight / 100f));
                        geometry.Vertices.Channels.Get<BoneWeightCollection>(channelIndex++)[geoVertIndex] = boneWeight;


                    }
                    //インデックスに登録
                    geometry.Indices.Add(geoVertIndex);
                }
                
                //ジオメトリをモデルに追加
                buildingMesh.Geometry.Add(geometry);
                //面頂点カウント進める
                FaceIndex += model.Materials[i].FaceVertCount;
            }
            //重複頂点データのマージ
            //MeshHelper.MergeDuplicatePositions(buildingMesh, 0);
            //MeshHelper.MergeDuplicateVertices(buildingMesh);
            
            //メッシュ出来たので返却
            return buildingMesh;
        }
Пример #6
0
        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 TestProcessorContext(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);
        }
Пример #7
0
        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);
        }
        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);
        }
Пример #9
0
        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);
        }