internal ModelMesh(W3dMesh w3dMesh, AssetLoadContext loadContext) { SetNameAndInstanceId("W3DMesh", w3dMesh.Header.MeshName); W3dShaderMaterial w3dShaderMaterial; ShaderResourcesBase shaderResources; if (w3dMesh.MaterialPasses.Count == 1 && w3dMesh.MaterialPasses[0].ShaderMaterialIds != null) { if (w3dMesh.MaterialPasses[0].ShaderMaterialIds.Items.Length > 1) { throw new NotSupportedException(); } var shaderMaterialID = w3dMesh.MaterialPasses[0].ShaderMaterialIds.Items[0]; w3dShaderMaterial = w3dMesh.ShaderMaterials.Items[(int)shaderMaterialID]; var effectName = w3dShaderMaterial.Header.TypeName.Replace(".fx", string.Empty); shaderResources = loadContext.ShaderResources.GetShaderMaterialResources(effectName); } else { w3dShaderMaterial = null; shaderResources = loadContext.ShaderResources.FixedFunction; } _depthPipeline = loadContext.ShaderResources.MeshDepth.Pipeline; MeshParts = new List <ModelMeshPart>(); if (w3dShaderMaterial != null) { MeshParts.Add(CreateModelMeshPartShaderMaterial( loadContext, w3dMesh, w3dMesh.MaterialPasses[0], w3dShaderMaterial, (ShaderMaterialShaderResources)shaderResources)); } else { var vertexMaterials = CreateMaterials(w3dMesh); var shadingConfigurations = new FixedFunctionShaderResources.ShadingConfiguration[w3dMesh.Shaders.Items.Count]; for (var i = 0; i < shadingConfigurations.Length; i++) { shadingConfigurations[i] = CreateShadingConfiguration(w3dMesh.Shaders.Items[i]); } for (var i = 0; i < w3dMesh.MaterialPasses.Count; i++) { CreateModelMeshPartsFixedFunction( loadContext, w3dMesh, w3dMesh.MaterialPasses[i], vertexMaterials, shadingConfigurations, MeshParts); } } BoundingBox = new AxisAlignedBoundingBox( w3dMesh.Header.Min, w3dMesh.Header.Max); _shaderSet = shaderResources.ShaderSet; Skinned = w3dMesh.IsSkinned; Hidden = w3dMesh.Header.Attributes.HasFlag(W3dMeshFlags.Hidden); CameraOriented = (w3dMesh.Header.Attributes & W3dMeshFlags.GeometryTypeMask) == W3dMeshFlags.GeometryTypeCameraOriented; _vertexBuffer = AddDisposable(loadContext.GraphicsDevice.CreateStaticBuffer( MemoryMarshal.AsBytes(new ReadOnlySpan <MeshShaderResources.MeshVertex.Basic>(CreateVertices(w3dMesh, w3dMesh.IsSkinned))), BufferUsage.VertexBuffer)); _indexBuffer = AddDisposable(loadContext.GraphicsDevice.CreateStaticBuffer( CreateIndices(w3dMesh), BufferUsage.IndexBuffer)); var hasHouseColor = w3dMesh.Header.MeshName.StartsWith("HOUSECOLOR"); _meshConstantsResourceSet = loadContext.ShaderResources.Mesh.GetCachedMeshResourceSet( Skinned, hasHouseColor); PostInitialize(loadContext); }
private ModelMeshPart CreateModelMeshPartShaderMaterial( AssetLoadContext context, W3dMesh w3dMesh, W3dMaterialPass w3dMaterialPass, W3dShaderMaterial w3dShaderMaterial, ShaderMaterialShaderResources shaderResources) { var texCoords = new MeshShaderResources.MeshVertex.TexCoords[w3dMesh.Header.NumVertices]; if (w3dMaterialPass.TexCoords != null) { for (var i = 0; i < texCoords.Length; i++) { texCoords[i].UV0 = w3dMaterialPass.TexCoords.Items[i]; } } // TODO: Extract state properties from shader material. var blendEnabled = false; var pipeline = shaderResources.Pipeline; var materialResourceSetBuilder = AddDisposable(new ShaderMaterialResourceSetBuilder( context.GraphicsDevice, shaderResources)); foreach (var w3dShaderProperty in w3dShaderMaterial.Properties) { switch (w3dShaderProperty.PropertyType) { case W3dShaderMaterialPropertyType.Texture: var texture = context.AssetStore.Textures.GetByName(w3dShaderProperty.StringValue) ?? context.StandardGraphicsResources.PlaceholderTexture; materialResourceSetBuilder.SetTexture(w3dShaderProperty.PropertyName, texture); break; case W3dShaderMaterialPropertyType.Bool: materialResourceSetBuilder.SetConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Bool); break; case W3dShaderMaterialPropertyType.Float: materialResourceSetBuilder.SetConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Float); break; case W3dShaderMaterialPropertyType.Vector2: materialResourceSetBuilder.SetConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Vector2); break; case W3dShaderMaterialPropertyType.Vector3: materialResourceSetBuilder.SetConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Vector3); break; case W3dShaderMaterialPropertyType.Vector4: materialResourceSetBuilder.SetConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Vector4); break; case W3dShaderMaterialPropertyType.Int: materialResourceSetBuilder.SetConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Int); break; default: throw new NotSupportedException(); } } var materialResourceSet = materialResourceSetBuilder.CreateResourceSet(); var texCoordsVertexBuffer = AddDisposable(context.GraphicsDevice.CreateStaticBuffer( texCoords, BufferUsage.VertexBuffer)); return(new ModelMeshPart( texCoordsVertexBuffer, 0, (uint)w3dMesh.Triangles.Items.Length * 3, blendEnabled, pipeline, pipeline, // TODO materialResourceSet)); }
// One ModelMeshPart for each unique shader in a W3D_CHUNK_MATERIAL_PASS. private ModelMeshPart CreateModelMeshPart( AssetLoadContext context, DeviceBuffer texCoordsVertexBuffer, uint startIndex, uint indexCount, W3dMesh w3dMesh, FixedFunctionShaderResources.VertexMaterial[] vertexMaterials, FixedFunctionShaderResources.ShadingConfiguration[] shadingConfigurations, uint vertexMaterialID, uint shaderID, uint numTextureStages, uint?textureIndex0, uint?textureIndex1) { var w3dShader = w3dMesh.Shaders.Items[(int)shaderID]; var cullMode = w3dMesh.Header.Attributes.HasFlag(W3dMeshFlags.TwoSided) ? FaceCullMode.None : FaceCullMode.Back; var depthWriteEnabled = w3dShader.DepthMask == W3dShaderDepthMask.WriteEnable; var depthComparison = w3dShader.DepthCompare.ToComparison(); var blendEnabled = w3dShader.SrcBlend != W3dShaderSrcBlendFunc.One || w3dShader.DestBlend != W3dShaderDestBlendFunc.Zero; var sourceFactor = w3dShader.SrcBlend.ToBlend(); var destinationColorFactor = w3dShader.DestBlend.ToBlend(false); var destinationAlphaFactor = w3dShader.DestBlend.ToBlend(true); var pipeline = context.ShaderResources.FixedFunction.GetCachedPipeline( cullMode, depthWriteEnabled, depthComparison, blendEnabled, sourceFactor, destinationColorFactor, destinationAlphaFactor); var pipelineBlend = context.ShaderResources.FixedFunction.GetCachedPipeline( cullMode, depthWriteEnabled, depthComparison, true, BlendFactor.SourceAlpha, BlendFactor.InverseSourceAlpha, BlendFactor.InverseSourceAlpha); var materialConstantsBuffer = AddDisposable(context.GraphicsDevice.CreateStaticBuffer( new FixedFunctionShaderResources.MaterialConstantsType { Material = vertexMaterials[vertexMaterialID], Shading = shadingConfigurations[shaderID], NumTextureStages = (int)numTextureStages }, BufferUsage.UniformBuffer)); var materialResourceSet = AddDisposable(context.ShaderResources.FixedFunction.CreateMaterialResourceSet( materialConstantsBuffer, CreateTexture(context, w3dMesh, textureIndex0) ?? context.StandardGraphicsResources.NullTexture, CreateTexture(context, w3dMesh, textureIndex1) ?? context.StandardGraphicsResources.NullTexture)); return(new ModelMeshPart( texCoordsVertexBuffer, startIndex, indexCount, blendEnabled, pipeline, pipelineBlend, materialResourceSet)); }
// One ModelMeshMaterialPass for each W3D_CHUNK_MATERIAL_PASS private void CreateModelMeshPartsFixedFunction( AssetLoadContext context, W3dMesh w3dMesh, W3dMaterialPass w3dMaterialPass, FixedFunctionShaderResources.VertexMaterial[] vertexMaterials, FixedFunctionShaderResources.ShadingConfiguration[] shadingConfigurations, List <ModelMeshPart> meshParts) { var hasTextureStage0 = w3dMaterialPass.TextureStages.Count > 0; var textureStage0 = hasTextureStage0 ? w3dMaterialPass.TextureStages[0] : null; var hasTextureStage1 = w3dMaterialPass.TextureStages.Count > 1 && w3dMaterialPass.TextureStages[1].TexCoords != null; var textureStage1 = hasTextureStage1 ? w3dMaterialPass.TextureStages[1] : null; var numTextureStages = hasTextureStage0 && hasTextureStage1 ? 2u : hasTextureStage0 ? 1u : 0u; var texCoords = new MeshShaderResources.MeshVertex.TexCoords[w3dMesh.Header.NumVertices]; if (hasTextureStage0) { for (var i = 0; i < texCoords.Length; i++) { // TODO: What to do when this is null? if (textureStage0.TexCoords != null) { texCoords[i].UV0 = textureStage0.TexCoords.Items[i]; } if (hasTextureStage1) { texCoords[i].UV1 = textureStage1.TexCoords.Items[i]; } } } var texCoordsVertexBuffer = AddDisposable(context.GraphicsDevice.CreateStaticBuffer( texCoords, BufferUsage.VertexBuffer)); // Optimisation for a fairly common case. if (w3dMaterialPass.VertexMaterialIds.Items.Length == 1 && w3dMaterialPass.ShaderIds.Items.Length == 1 && w3dMaterialPass.TextureStages.Count == 1 && w3dMaterialPass.TextureStages[0].TextureIds.Items.Count == 1) { meshParts.Add(CreateModelMeshPart( context, texCoordsVertexBuffer, 0, w3dMesh.Header.NumTris * 3, w3dMesh, vertexMaterials, shadingConfigurations, w3dMaterialPass.VertexMaterialIds.Items[0], w3dMaterialPass.ShaderIds.Items[0], numTextureStages, w3dMaterialPass.TextureStages[0].TextureIds.Items[0], null)); } else { // Expand ShaderIds and TextureIds, if they have a single entry // (which means same ID for all faces) IEnumerable <uint?> getExpandedTextureIds(List <uint?> ids) { if (ids == null) { for (var i = 0; i < w3dMesh.Header.NumTris; i++) { yield return(null); } } else if (ids.Count == 1) { var result = ids[0]; for (var i = 0; i < w3dMesh.Header.NumTris; i++) { yield return(result); } } else { foreach (var id in ids) { yield return(id); } } } IEnumerable <uint> getExpandedShaderIds() { var ids = w3dMaterialPass.ShaderIds.Items; if (ids.Length == 1) { var result = ids[0]; for (var i = 0; i < w3dMesh.Header.NumTris; i++) { yield return(result); } } else { foreach (var id in ids) { yield return(id); } } } IEnumerable <uint> getExpandedVertexMaterialIDs() { var ids = w3dMaterialPass.VertexMaterialIds.Items; if (ids.Length == 1) { var result = ids[0]; for (var i = 0; i < w3dMesh.Header.NumTris; i++) { yield return(result); } } else { for (var i = 0; i < w3dMesh.Header.NumTris; i++) { var triangle = w3dMesh.Triangles.Items[i]; var materialID0 = ids[(int)triangle.VIndex0]; var materialID1 = ids[(int)triangle.VIndex1]; var materialID2 = ids[(int)triangle.VIndex2]; if (materialID0 != materialID1 || materialID1 != materialID2) { throw new NotSupportedException(); } yield return(materialID0); } foreach (var id in ids) { yield return(id); } } } var combinedIds = getExpandedVertexMaterialIDs() .Zip(getExpandedShaderIds(), (x, y) => new { VertexMaterialID = x, ShaderID = y }) .Zip(getExpandedTextureIds(textureStage0?.TextureIds.Items), (x, y) => new { x.VertexMaterialID, x.ShaderID, TextureIndex0 = y }) .Zip(getExpandedTextureIds(textureStage1?.TextureIds.Items), (x, y) => new CombinedMaterialPermutation { VertexMaterialID = x.VertexMaterialID, ShaderID = x.ShaderID, TextureIndex0 = x.TextureIndex0, TextureIndex1 = y }); var combinedId = combinedIds.First(); var startIndex = 0u; var indexCount = 0u; foreach (var newCombinedId in combinedIds) { if (combinedId != newCombinedId) { meshParts.Add(CreateModelMeshPart( context, texCoordsVertexBuffer, startIndex, indexCount, w3dMesh, vertexMaterials, shadingConfigurations, combinedId.VertexMaterialID, combinedId.ShaderID, numTextureStages, combinedId.TextureIndex0, combinedId.TextureIndex1)); startIndex = startIndex + indexCount; indexCount = 0; } combinedId = newCombinedId; indexCount += 3; } if (indexCount > 0) { meshParts.Add(CreateModelMeshPart( context, texCoordsVertexBuffer, startIndex, indexCount, w3dMesh, vertexMaterials, shadingConfigurations, combinedId.VertexMaterialID, combinedId.ShaderID, numTextureStages, combinedId.TextureIndex0, combinedId.TextureIndex1)); } } }
// One ModelMeshPart for each unique shader in a W3D_CHUNK_MATERIAL_PASS. private ModelMeshPart CreateModelMeshPart( ContentManager contentManager, uint startIndex, uint indexCount, W3dMesh w3dMesh, FixedFunction.VertexMaterial[] vertexMaterials, FixedFunction.ShadingConfiguration[] shadingConfigurations, uint vertexMaterialID, uint shaderID, uint numTextureStages, uint?textureIndex0, uint?textureIndex1) { var w3dShader = w3dMesh.Shaders[shaderID]; var rasterizerState = RasterizerStateDescriptionUtility.DefaultFrontIsCounterClockwise; rasterizerState.CullMode = w3dMesh.Header.Attributes.HasFlag(W3dMeshFlags.TwoSided) ? FaceCullMode.None : FaceCullMode.Back; var depthState = DepthStencilStateDescription.DepthOnlyLessEqual; depthState.DepthWriteEnabled = w3dShader.DepthMask == W3dShaderDepthMask.WriteEnable; depthState.DepthComparison = w3dShader.DepthCompare.ToComparison(); var blendState = new BlendStateDescription( RgbaFloat.White, new BlendAttachmentDescription( w3dShader.SrcBlend != W3dShaderSrcBlendFunc.One || w3dShader.DestBlend != W3dShaderDestBlendFunc.Zero, w3dShader.SrcBlend.ToBlend(), w3dShader.DestBlend.ToBlend(false), BlendFunction.Add, w3dShader.SrcBlend.ToBlend(), w3dShader.DestBlend.ToBlend(true), BlendFunction.Add)); var effectMaterial = new FixedFunctionMaterial(contentManager, contentManager.EffectLibrary.FixedFunction) { PipelineState = new EffectPipelineState( rasterizerState, depthState, blendState, RenderPipeline.GameOutputDescription) }; var materialConstantsBuffer = AddDisposable(contentManager.GraphicsDevice.CreateStaticBuffer( new FixedFunction.MaterialConstantsType { Material = vertexMaterials[vertexMaterialID], Shading = shadingConfigurations[shaderID], NumTextureStages = numTextureStages }, BufferUsage.UniformBuffer)); effectMaterial.SetMaterialConstants(materialConstantsBuffer); effectMaterial.SetTexture0(CreateTexture(contentManager, w3dMesh, textureIndex0)); effectMaterial.SetTexture1(CreateTexture(contentManager, w3dMesh, textureIndex1)); return(new ModelMeshPart( startIndex, indexCount, effectMaterial)); }
// One ModelMeshMaterialPass for each W3D_CHUNK_MATERIAL_PASS private static ModelMeshMaterialPass CreateModelMeshMaterialPass( GraphicsDevice graphicsDevice, ResourceUploadBatch uploadBatch, W3dMesh w3dMesh, W3dMaterialPass w3dMaterialPass) { var hasTextureStage0 = w3dMaterialPass.TextureStages.Count > 0; var textureStage0 = hasTextureStage0 ? w3dMaterialPass.TextureStages[0] : null; var hasTextureStage1 = w3dMaterialPass.TextureStages.Count > 1; var textureStage1 = hasTextureStage1 ? w3dMaterialPass.TextureStages[1] : null; var numTextureStages = hasTextureStage0 && hasTextureStage1 ? 2u : hasTextureStage0 ? 1u : 0u; var texCoords = new MeshTexCoords[w3dMesh.Header.NumVertices]; if (hasTextureStage0) { for (var i = 0; i < texCoords.Length; i++) { // TODO: What to do when this is null? if (textureStage0.TexCoords != null) { texCoords[i].UV0 = textureStage0.TexCoords[i]; } if (hasTextureStage1) { texCoords[i].UV1 = textureStage1.TexCoords[i]; } } } var textureIndices = new MeshTextureIndex[w3dMesh.Header.NumTris]; if (hasTextureStage0) { if (textureStage0.TextureIds.Length == 1) { var textureID = textureStage0.TextureIds[0]; for (var i = 0; i < textureIndices.Length; i++) { textureIndices[i].IndexStage0 = textureID; } } else { for (var i = 0; i < textureIndices.Length; i++) { textureIndices[i].IndexStage0 = textureStage0.TextureIds[i]; } } } if (hasTextureStage1) { if (textureStage1.TextureIds.Length == 1) { var textureID = textureStage1.TextureIds[0]; for (var i = 0; i < textureIndices.Length; i++) { textureIndices[i].IndexStage1 = textureID; } } else { for (var i = 0; i < textureIndices.Length; i++) { textureIndices[i].IndexStage1 = textureStage1.TextureIds[i]; } } } var materialIndices = w3dMaterialPass.VertexMaterialIds; if (materialIndices.Length == 1) { var materialID = materialIndices[0]; materialIndices = new uint[w3dMesh.Header.NumVertices]; for (var i = 0; i < w3dMesh.Header.NumVertices; i++) { materialIndices[i] = materialID; } } var meshParts = new List <ModelMeshPart>(); if (w3dMaterialPass.ShaderIds.Length == 1) { meshParts.Add(CreateModelMeshPart( 0, w3dMesh.Header.NumTris * 3, w3dMesh, w3dMesh.Shaders[w3dMaterialPass.ShaderIds[0]])); } else { var shaderID = w3dMaterialPass.ShaderIds[0]; var startIndex = 0u; var indexCount = 0u; for (var i = 0; i < w3dMaterialPass.ShaderIds.Length; i++) { var newShaderID = w3dMaterialPass.ShaderIds[i]; if (shaderID != newShaderID) { meshParts.Add(CreateModelMeshPart( startIndex, indexCount, w3dMesh, w3dMesh.Shaders[shaderID])); startIndex = (uint)(i * 3); indexCount = 0; } shaderID = newShaderID; indexCount += 3; } if (indexCount > 0) { meshParts.Add(CreateModelMeshPart( startIndex, indexCount, w3dMesh, w3dMesh.Shaders[shaderID])); } } return(new ModelMeshMaterialPass( graphicsDevice, uploadBatch, numTextureStages, texCoords, textureIndices, materialIndices, meshParts)); }
private ModelMeshMaterialPass CreateModelMeshMaterialPassShaderMaterial( ContentManager contentManager, W3dMesh w3dMesh, W3dMaterialPass w3dMaterialPass, W3dShaderMaterial w3dShaderMaterial, Effect effect) { var texCoords = new MeshVertex.TexCoords[w3dMesh.Header.NumVertices]; if (w3dMaterialPass.TexCoords != null) { for (var i = 0; i < texCoords.Length; i++) { texCoords[i].UV0 = w3dMaterialPass.TexCoords[i]; } } var meshParts = new List <ModelMeshPart>(); // TODO: Extract state properties from shader material. var rasterizerState = RasterizerStateDescriptionUtility.DefaultFrontIsCounterClockwise; var depthState = DepthStencilStateDescription.DepthOnlyLessEqual; var blendState = BlendStateDescription.SingleDisabled; var material = new ShaderMaterial(contentManager, effect) { PipelineState = new EffectPipelineState( rasterizerState, depthState, blendState, RenderPipeline.GameOutputDescription) }; var materialConstantsResourceBinding = effect.GetParameter("MaterialConstants").ResourceBinding; var materialConstantsBuffer = AddDisposable(contentManager.GraphicsDevice.ResourceFactory.CreateBuffer( new BufferDescription( materialConstantsResourceBinding.Size, BufferUsage.UniformBuffer | BufferUsage.Dynamic))); var materialConstantsBytes = new byte[materialConstantsResourceBinding.Size]; void setMaterialConstant <T>(string name, T value) where T : struct { var constantBufferField = materialConstantsResourceBinding.GetField(name); var valueBytes = StructInteropUtility.ToBytes(ref value); if (valueBytes.Length != constantBufferField.Size) { throw new InvalidOperationException(); } Buffer.BlockCopy( valueBytes, 0, materialConstantsBytes, constantBufferField.Offset, constantBufferField.Size); } foreach (var w3dShaderProperty in w3dShaderMaterial.Properties) { switch (w3dShaderProperty.PropertyType) { case W3dShaderMaterialPropertyType.Texture: var texture = CreateTexture(contentManager, w3dShaderProperty.StringValue); material.SetProperty(w3dShaderProperty.PropertyName, texture); break; case W3dShaderMaterialPropertyType.Bool: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Bool); break; case W3dShaderMaterialPropertyType.Color: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Color); break; case W3dShaderMaterialPropertyType.Float: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Float); break; case W3dShaderMaterialPropertyType.Vector2: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Vector2); break; case W3dShaderMaterialPropertyType.Vector3: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Vector3); break; case W3dShaderMaterialPropertyType.Int: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Int); break; default: throw new NotSupportedException(); } } contentManager.GraphicsDevice.UpdateBuffer(materialConstantsBuffer, 0, materialConstantsBytes); material.SetProperty("MaterialConstants", materialConstantsBuffer); meshParts.Add(new ModelMeshPart( 0, w3dMesh.Header.NumTris * 3, material)); return(new ModelMeshMaterialPass( contentManager.GraphicsDevice, texCoords, meshParts)); }
private ModelMesh CreateModelMesh( ContentManager contentManager, W3dMesh w3dMesh, ModelBone parentBone, int numBones) { W3dShaderMaterial w3dShaderMaterial; Effect effect = null; if (w3dMesh.MaterialPasses.Length == 1 && w3dMesh.MaterialPasses[0].ShaderMaterialId != null) { var shaderMaterialID = w3dMesh.MaterialPasses[0].ShaderMaterialId.Value; w3dShaderMaterial = w3dMesh.ShaderMaterials.Materials[(int)shaderMaterialID]; var effectName = w3dShaderMaterial.Header.TypeName.Replace(".fx", string.Empty); effect = contentManager.EffectLibrary.GetEffect( effectName, MeshVertex.VertexDescriptors); } else { w3dShaderMaterial = null; effect = contentManager.EffectLibrary.FixedFunction; } var vertexMaterials = CreateMaterials(w3dMesh); var shadingConfigurations = new FixedFunction.ShadingConfiguration[w3dMesh.Shaders.Length]; for (var i = 0; i < shadingConfigurations.Length; i++) { shadingConfigurations[i] = CreateShadingConfiguration(w3dMesh.Shaders[i]); } var materialPasses = new ModelMeshMaterialPass[w3dMesh.MaterialPasses.Length]; if (w3dShaderMaterial != null) { materialPasses[0] = CreateModelMeshMaterialPassShaderMaterial( contentManager, w3dMesh, w3dMesh.MaterialPasses[0], w3dShaderMaterial, effect); } else { for (var i = 0; i < materialPasses.Length; i++) { materialPasses[i] = CreateModelMeshMaterialPassFixedFunction( contentManager, w3dMesh, w3dMesh.MaterialPasses[i], vertexMaterials, shadingConfigurations); } } var boundingBox = new BoundingBox( w3dMesh.Header.Min, w3dMesh.Header.Max); var isSkinned = (w3dMesh.Header.Attributes & W3dMeshFlags.GeometryTypeMask) == W3dMeshFlags.GeometryTypeSkin; var cameraOriented = (w3dMesh.Header.Attributes & W3dMeshFlags.GeometryTypeMask) == W3dMeshFlags.GeometryTypeCameraOriented; return(new ModelMesh( contentManager.GraphicsDevice, w3dMesh.Header.MeshName, CreateVertices(w3dMesh, isSkinned), CreateIndices(w3dMesh), effect, materialPasses, isSkinned, parentBone, (uint)numBones, boundingBox, w3dMesh.Header.Attributes.HasFlag(W3dMeshFlags.Hidden), cameraOriented)); }
private ModelMesh CreateModelMesh( ContentManager contentManager, W3dMesh w3dMesh) { W3dShaderMaterial w3dShaderMaterial; ShaderResourcesBase shaderResources; Pipeline depthPipeline; if (w3dMesh.MaterialPasses.Count == 1 && w3dMesh.MaterialPasses[0].ShaderMaterialIds != null) { if (w3dMesh.MaterialPasses[0].ShaderMaterialIds.Items.Length > 1) { throw new NotSupportedException(); } var shaderMaterialID = w3dMesh.MaterialPasses[0].ShaderMaterialIds.Items[0]; w3dShaderMaterial = w3dMesh.ShaderMaterials.Items[(int)shaderMaterialID]; var effectName = w3dShaderMaterial.Header.TypeName.Replace(".fx", string.Empty); shaderResources = contentManager.ShaderResources.GetShaderMaterialResources(effectName); depthPipeline = contentManager.ShaderResources.MeshDepth.TriangleListPipeline; } else { w3dShaderMaterial = null; shaderResources = contentManager.ShaderResources.FixedFunction; depthPipeline = contentManager.ShaderResources.MeshDepth.TriangleStripPipeline; } var vertexMaterials = CreateMaterials(w3dMesh); var shadingConfigurations = new FixedFunctionShaderResources.ShadingConfiguration[w3dMesh.Shaders.Items.Count]; for (var i = 0; i < shadingConfigurations.Length; i++) { shadingConfigurations[i] = CreateShadingConfiguration(w3dMesh.Shaders.Items[i]); } var meshParts = new List <ModelMeshPart>(); if (w3dShaderMaterial != null) { meshParts.Add(CreateModelMeshPartShaderMaterial( contentManager, w3dMesh, w3dMesh.MaterialPasses[0], w3dShaderMaterial, (ShaderMaterialShaderResources)shaderResources)); } else { for (var i = 0; i < w3dMesh.MaterialPasses.Count; i++) { CreateModelMeshPartsFixedFunction( contentManager, w3dMesh, w3dMesh.MaterialPasses[i], vertexMaterials, shadingConfigurations, meshParts); } } var boundingBox = new BoundingBox( w3dMesh.Header.Min, w3dMesh.Header.Max); var cameraOriented = (w3dMesh.Header.Attributes & W3dMeshFlags.GeometryTypeMask) == W3dMeshFlags.GeometryTypeCameraOriented; var hasHouseColor = w3dMesh.Header.MeshName.StartsWith("HOUSECOLOR"); return(new ModelMesh( contentManager.GraphicsDevice, contentManager.ShaderResources, w3dMesh.Header.MeshName, shaderResources.ShaderSet, depthPipeline, MemoryMarshal.AsBytes(new ReadOnlySpan <MeshShaderResources.MeshVertex.Basic>(CreateVertices(w3dMesh, w3dMesh.IsSkinned))), CreateIndices(w3dMesh, w3dShaderMaterial != null), meshParts, w3dMesh.IsSkinned, boundingBox, w3dMesh.Header.Attributes.HasFlag(W3dMeshFlags.Hidden), cameraOriented, hasHouseColor)); }
// One ModelMeshPart for each unique shader in a W3D_CHUNK_MATERIAL_PASS. private ModelMeshPart CreateModelMeshPart( ContentManager contentManager, uint startIndex, uint indexCount, W3dMesh w3dMesh, FixedFunctionMaterial.VertexMaterial[] vertexMaterials, FixedFunctionMaterial.ShadingConfiguration[] shadingConfigurations, uint vertexMaterialID, uint shaderID, uint numTextureStages, uint?textureIndex0, uint?textureIndex1) { var w3dShader = w3dMesh.Shaders[shaderID]; var rasterizerState = RasterizerStateDescription.CullBackSolid; rasterizerState.CullMode = w3dMesh.Header.Attributes.HasFlag(W3dMeshFlags.TwoSided) ? CullMode.None : CullMode.CullBack; var depthState = DepthStencilStateDescription.Default; depthState.IsDepthEnabled = true; depthState.IsDepthWriteEnabled = w3dShader.DepthMask == W3dShaderDepthMask.WriteEnable; depthState.DepthComparison = w3dShader.DepthCompare.ToComparison(); var blendState = BlendStateDescription.Opaque; blendState.Enabled = w3dShader.SrcBlend != W3dShaderSrcBlendFunc.One || w3dShader.DestBlend != W3dShaderDestBlendFunc.Zero; blendState.SourceBlend = w3dShader.SrcBlend.ToBlend(); blendState.SourceAlphaBlend = w3dShader.SrcBlend.ToBlend(); blendState.DestinationBlend = w3dShader.DestBlend.ToBlend(false); blendState.DestinationAlphaBlend = w3dShader.DestBlend.ToBlend(true); var effectMaterial = new FixedFunctionMaterial(contentManager.EffectLibrary.FixedFunction); effectMaterial.PipelineState = new EffectPipelineState( rasterizerState, depthState, blendState); var materialConstantsBuffer = AddDisposable(Buffer <FixedFunctionMaterial.MaterialConstants> .CreateStatic( contentManager.GraphicsDevice, new FixedFunctionMaterial.MaterialConstants { Material = vertexMaterials[vertexMaterialID], Shading = shadingConfigurations[shaderID], NumTextureStages = numTextureStages }, BufferBindFlags.ConstantBuffer)); effectMaterial.SetMaterialConstants(materialConstantsBuffer); effectMaterial.SetTexture0(CreateTexture(contentManager, w3dMesh, textureIndex0)); effectMaterial.SetTexture1(CreateTexture(contentManager, w3dMesh, textureIndex1)); return(new ModelMeshPart( startIndex, indexCount, effectMaterial)); }
private ModelMeshMaterialPass CreateModelMeshMaterialPassShaderMaterial( ContentManager contentManager, W3dMesh w3dMesh, W3dMaterialPass w3dMaterialPass, W3dShaderMaterial w3dShaderMaterial, Effect effect) { var texCoords = new MeshVertex.TexCoords[w3dMesh.Header.NumVertices]; if (w3dMaterialPass.TexCoords != null) { for (var i = 0; i < texCoords.Length; i++) { texCoords[i].UV0 = w3dMaterialPass.TexCoords[i]; } } var meshParts = new List <ModelMeshPart>(); // TODO: Extract state properties from shader material. var rasterizerState = RasterizerStateDescription.CullBackSolid; var depthState = DepthStencilStateDescription.Default; var blendState = BlendStateDescription.Opaque; var material = new ShaderMaterial(effect); material.PipelineState = new EffectPipelineState( rasterizerState, depthState, blendState); var materialConstantsResourceBinding = effect.GetParameter("MaterialConstants").ResourceBinding; var materialConstantsBuffer = AddDisposable(OpenSage.LowLevel.Graphics3D.Buffer.CreateDynamic( contentManager.GraphicsDevice, (uint)materialConstantsResourceBinding.ConstantBufferSizeInBytes, BufferBindFlags.ConstantBuffer)); var materialConstantsBytes = new byte[materialConstantsResourceBinding.ConstantBufferSizeInBytes]; void setMaterialConstant <T>(string name, T value) where T : struct { var constantBufferField = materialConstantsResourceBinding.GetConstantBufferField(name); var valueBytes = StructInteropUtility.ToBytes(ref value); if (valueBytes.Length != constantBufferField.Size) { throw new InvalidOperationException(); } System.Buffer.BlockCopy( valueBytes, 0, materialConstantsBytes, constantBufferField.Offset, constantBufferField.Size); } foreach (var w3dShaderProperty in w3dShaderMaterial.Properties) { switch (w3dShaderProperty.PropertyType) { case W3dShaderMaterialPropertyType.Texture: var texture = CreateTexture(contentManager, w3dShaderProperty.StringValue); material.SetProperty(w3dShaderProperty.PropertyName, texture); break; case W3dShaderMaterialPropertyType.Bool: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Bool); break; case W3dShaderMaterialPropertyType.Color: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Color); break; case W3dShaderMaterialPropertyType.Float: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Float); break; case W3dShaderMaterialPropertyType.Vector2: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Vector2); break; case W3dShaderMaterialPropertyType.Vector3: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Vector3); break; case W3dShaderMaterialPropertyType.Int: setMaterialConstant(w3dShaderProperty.PropertyName, w3dShaderProperty.Value.Int); break; default: throw new NotSupportedException(); } } materialConstantsBuffer.SetData(materialConstantsBytes); material.SetProperty("MaterialConstants", materialConstantsBuffer); meshParts.Add(new ModelMeshPart( 0, w3dMesh.Header.NumTris * 3, material)); return(new ModelMeshMaterialPass( contentManager.GraphicsDevice, texCoords, meshParts)); }