2021-04-19 03:49:55 +00:00
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
2018-06-19 09:06:54 +00:00
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using System ;
using System.Text.RegularExpressions ;
2020-04-14 14:37:41 +00:00
using Stride.Core ;
using Stride.Core.Mathematics ;
using Stride.Graphics ;
using Stride.Particles.Sorters ;
using Stride.Particles.VertexLayouts ;
using Stride.Rendering ;
using Stride.Rendering.Materials ;
using Stride.Rendering.Materials.ComputeColors ;
using Stride.Shaders ;
using Stride.Particles.Materials ;
2018-06-19 09:06:54 +00:00
namespace ParticlesSample.Materials
{
[DataContract("ParticleCustomMaterial")]
[Display("ParticleCustomMaterial")]
public class ParticleCustomMaterial : ParticleMaterialSimple
{
private ShaderSource shaderBaseColor ;
private ShaderSource shaderBaseScalar ;
[DataMemberIgnore]
public override string EffectName
{
get { return effectName ; }
protected set { effectName = value ; }
}
private string effectName = "ParticleCustomEffect" ;
/// <summary>
/// <see cref="IComputeColor"/> allows several channels to be blended together, including textures, vertex streams and fixed values.
/// </summary>
/// <userdoc>
/// Emissive component ignores light and defines a fixed color this particle should use (emit) when rendered.
/// </userdoc>
[DataMember(100)]
[Display("Emissive")]
public IComputeColor ComputeColor
{
get { return computeColor ; }
set { computeColor = value ; }
}
private IComputeColor computeColor = new ComputeTextureColor ( ) ;
/// <summary>
/// <see cref="UVBuilder"/> defines how the base coordinates of the particle shape should be modified for texture scrolling, animation, etc.
/// </summary>
/// <userdoc>
/// If left blank, the texture coordinates will be the original ones from the shape builder, usually (0, 0, 1, 1). Or you can define a custom texture coordinate builder which modifies the original coordinates for the sprite.
/// </userdoc>
[DataMember(200)]
[Display("TexCoord0")]
public UVBuilder UVBuilder0 ;
private readonly AttributeDescription texCoord0 = new AttributeDescription ( "TEXCOORD" ) ;
/// <summary>
/// <see cref="IComputeColor"/> allows several channels to be blended together, including textures, vertex streams and fixed values.
/// </summary>
/// <userdoc>
/// Alpha component which defines how opaque (1) or transparent (0) the color will be
/// </userdoc>
[DataMember(300)]
[Display("Alpha")]
public IComputeScalar ComputeScalar
{
get { return computeScalar ; }
set { computeScalar = value ; }
}
private IComputeScalar computeScalar = new ComputeTextureScalar ( ) ;
/// <summary>
/// <see cref="UVBuilder"/> defines how the base coordinates of the particle shape should be modified for texture scrolling, animation, etc.
/// </summary>
/// <userdoc>
/// If left blank, the texture coordinates will be the original ones from the shape builder, usually (0, 0, 1, 1). Or you can define a custom texture coordinate builder which modifies the original coordinates for the sprite.
/// </userdoc>
[DataMember(400)]
[Display("TexCoord1")]
public UVBuilder UVBuilder1 ;
private readonly AttributeDescription texCoord1 = new AttributeDescription ( "TEXCOORD1" ) ;
protected override void InitializeCore ( RenderContext context )
{
base . InitializeCore ( context ) ;
UpdateShaders ( context . GraphicsDevice ) ;
}
/// <inheritdoc />
public override void Setup ( RenderContext context )
{
base . Setup ( context ) ;
UpdateShaders ( context . GraphicsDevice ) ;
}
public override void ValidateEffect ( RenderContext context , ref EffectValidator effectValidator )
{
effectValidator . ValidateParameter ( ParticleCustomShaderKeys . BaseColor , shaderBaseColor ) ;
effectValidator . ValidateParameter ( ParticleCustomShaderKeys . BaseIntensity , shaderBaseScalar ) ;
}
private void UpdateShaders ( GraphicsDevice graphicsDevice )
{
if ( ComputeColor ! = null & & ComputeScalar ! = null )
{
var shaderGeneratorContext = new ShaderGeneratorContext ( graphicsDevice )
{
Parameters = Parameters ,
ColorSpace = graphicsDevice . ColorSpace
} ;
// Don't forget to set the proper color space!
shaderGeneratorContext . ColorSpace = graphicsDevice . ColorSpace ;
var newShaderBaseColor = ComputeColor . GenerateShaderSource ( shaderGeneratorContext , new MaterialComputeColorKeys ( ParticleCustomShaderKeys . EmissiveMap , ParticleCustomShaderKeys . EmissiveValue , Color . White ) ) ;
var newShaderBaseScalar = ComputeScalar . GenerateShaderSource ( shaderGeneratorContext , new MaterialComputeColorKeys ( ParticleCustomShaderKeys . IntensityMap , ParticleCustomShaderKeys . IntensityValue , Color . White ) ) ;
// Check if shader code has changed
if ( ! newShaderBaseColor . Equals ( shaderBaseColor ) | | ! newShaderBaseScalar . Equals ( shaderBaseScalar ) )
{
shaderBaseColor = newShaderBaseColor ;
shaderBaseScalar = newShaderBaseScalar ;
Parameters . Set ( ParticleCustomShaderKeys . BaseColor , shaderBaseColor ) ;
Parameters . Set ( ParticleCustomShaderKeys . BaseIntensity , shaderBaseScalar ) ;
// TODO: Is this necessary?
HasVertexLayoutChanged = true ;
}
}
}
public override void UpdateVertexBuilder ( ParticleVertexBuilder vertexBuilder )
{
base . UpdateVertexBuilder ( vertexBuilder ) ;
var code = shaderBaseColor ! = null ? shaderBaseColor . ToString ( ) : null ;
if ( code ! = null & & code . Contains ( "COLOR0" ) )
{
vertexBuilder . AddVertexElement ( ParticleVertexElements . Color ) ;
}
// There are two UV builders, building texCoord0 and texCoord1
// Which set is referenced can be set by the user in the IComputeColor tree
vertexBuilder . AddVertexElement ( ParticleVertexElements . TexCoord [ 0 ] ) ;
vertexBuilder . AddVertexElement ( ParticleVertexElements . TexCoord [ 1 ] ) ;
}
public override unsafe void PatchVertexBuffer ( ref ParticleBufferState bufferState , Vector3 invViewX , Vector3 invViewY , ref ParticleList sorter )
{
// If you want, you can integrate the base builder here and not call it. It should result in slight speed up
base . PatchVertexBuffer ( ref bufferState , invViewX , invViewY , ref sorter ) ;
// Update the non-default coordinates first, because they update off the default ones
if ( UVBuilder1 ! = null ) UVBuilder1 . BuildUVCoordinates ( ref bufferState , ref sorter , texCoord1 ) ;
// Update the default coordinates last
if ( UVBuilder0 ! = null ) UVBuilder0 . BuildUVCoordinates ( ref bufferState , ref sorter , texCoord0 ) ;
// If the particles have color field, the base class should have already passed the information
if ( HasColorField )
return ;
// If there is no color stream we don't need to fill anything
var colAttribute = bufferState . GetAccessor ( VertexAttributes . Color ) ;
if ( colAttribute . Size < = 0 )
return ;
// Since the particles don't have their own color field, set the default color to white
var color = 0xFFFFFFFF ;
bufferState . StartOver ( ) ;
foreach ( var particle in sorter )
{
bufferState . SetAttributePerParticle ( colAttribute , ( IntPtr ) ( & color ) ) ;
bufferState . NextParticle ( ) ;
}
bufferState . StartOver ( ) ;
}
}
}