// Is there a particle effector property for a particle parameter that is not set? public static string CheckForUninitializedEffectorProperties(ParticleSystem particleSystem) { string message = "Following effector properties are required but not set: "; bool uninitializedPropertyFound = false; foreach (var effector in particleSystem.Effectors) { foreach (var propertyInfo in GetProperties(effector)) { // Handle effector properties with a ParticleParameterAttribute. var particleParameterAttribute = propertyInfo.GetCustomAttributes(typeof(ParticleParameterAttribute), true).Cast<ParticleParameterAttribute>().FirstOrDefault(); if (particleParameterAttribute != null && !particleParameterAttribute.Optional) { var parameterName = propertyInfo.GetValue(effector, null) as string; if (string.IsNullOrEmpty(parameterName)) { // The property is a mandatory particle parameter name and is not set. uninitializedPropertyFound = true; message += string.Format("{0}.{1} ", effector.GetType().Name, propertyInfo.Name); } } } } return uninitializedPropertyFound ? message : null; }
protected virtual void CloneCore(ParticleSystem source) { Name = source.Name; Enabled = source.Enabled; MaxNumberOfParticles = source.MaxNumberOfParticles; InitialDelay = source.InitialDelay; PreloadDuration = source.PreloadDuration; PreloadDeltaTime = source.PreloadDeltaTime; TimeScaling = source.TimeScaling; EnableMultithreading = source.EnableMultithreading; ReferenceFrame = source.ReferenceFrame; // Cloning the particle parameter collection is tricky because the parameters are generics. // We use an internal method of the parameter to do the job. foreach (var parameter in source.Parameters) ((IParticleParameterInternal)parameter).AddCopyToCollection(Parameters); foreach (var effector in source.Effectors) Effectors.Add(effector.Clone()); if (source.Children != null) { Children = new ParticleSystemCollection(); foreach (var particleSystem in source.Children) Children.Add(particleSystem.Clone()); } Pose = source.Pose; Shape = source.Shape.Clone(); }
public static ParticleSystem CreateWaterFall(ContentManager contentManager) { var ps = new ParticleSystem { Name = "WaterFall", // Preload 2 seconds of the effect using a larger time step. PreloadDuration = TimeSpan.FromSeconds(2), PreloadDeltaTime = TimeSpan.FromSeconds(0.1), Children = new ParticleSystemCollection { CreateSpray(contentManager), CreateWater(contentManager), } }; // This parent particle system defines the uniform Gravity parameter for the child // particle systems. Uniform particle parameters can be "inherited" - if a child // does not have a required uniform parameter, it uses the parameter of the parent. ps.Parameters.AddUniform<Vector3F>("Gravity").DefaultValue = new Vector3F(0, -1f, 0); ParticleSystemValidator.Validate(ps); ParticleSystemValidator.Validate(ps.Children[0]); ParticleSystemValidator.Validate(ps.Children[1]); return ps; }
public RibbonSample(Microsoft.Xna.Framework.Game game) : base(game) { GraphicsScreen.DrawReticle = true; GameObjectService.Objects.Add(new GrabObject(Services)); // Load a sphere model. _modelNode = ContentManager.Load<ModelNode>("Particles/Sphere").Clone(); GraphicsScreen.Scene.Children.Add(_modelNode); // Add gravity and damping to the physics simulation. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Create a rigid body for the sphere. _rigidBody = new RigidBody(new SphereShape(0.5f)) { Pose = new Pose(new Vector3F(-3, 0, 0)), LinearVelocity = new Vector3F(10, 10, -3f), }; Simulation.RigidBodies.Add(_rigidBody); _particleSystem = RibbonEffect.Create(ContentManager); ParticleSystemService.ParticleSystems.Add(_particleSystem); _particleSystemNode = new ParticleSystemNode(_particleSystem); GraphicsScreen.Scene.Children.Add(_particleSystemNode); }
public RingOfFireSample(Microsoft.Xna.Framework.Game game) : base(game) { // Create a new "empty" particle system. var particleSystem = new ParticleSystem(); // Particle systems can have child particle systems. // Add a fire and a smoke effect as children. var fire = Fire.Create(ContentManager); var smoke = Smoke.Create(ContentManager); // The smoke effect from the previous sample. particleSystem.Children = new ParticleSystemCollection { fire, smoke }; // If we need to, we can modify the predefined effects. // Change the smoke particle lifetime. smoke.Parameters.Get<float>(ParticleParameterNames.Lifetime).DefaultValue = 4; // Change the smoke's start positions to a ring. smoke.Effectors.OfType<StartPositionEffector>().First().Distribution = new CircleDistribution { InnerRadius = 2, OuterRadius = 2 }; // Position the particle system (including its child) in the level. particleSystem.Pose = new Pose(new Vector3F(0, 3, 0)); // We only need to add the parent particle system to the particle system service. // The service will automatically update the parent system each frame. The parent // system will automatically update its children. ParticleSystemService.ParticleSystems.Add(particleSystem); // Add the particle system to the scene graph. _particleSystemNode = new ParticleSystemNode(particleSystem); GraphicsScreen.Scene.Children.Add(_particleSystemNode); }
public RainSample(Microsoft.Xna.Framework.Game game) : base(game) { _particleSystem = Rain.Create(ContentManager); ParticleSystemService.ParticleSystems.Add(_particleSystem); _particleSystemNode = new ParticleSystemNode(_particleSystem); GraphicsScreen.Scene.Children.Add(_particleSystemNode); }
public static int CountNumberOfParticles(ParticleSystem particleSystem) { int count = 0; count += particleSystem.NumberOfLivingParticles; if (particleSystem.Children != null) count += CountNumberOfParticles(particleSystem.Children); return count; }
public static ParticleSystem Create(ContentManager contentManager) { var ps = new ParticleSystem { Name = "Grass", MaxNumberOfParticles = 400, }; // The grass particles do not die. ps.Parameters.AddUniform<float>(ParticleParameterNames.Lifetime).DefaultValue = float.PositiveInfinity; // We create all particles instantly. Up to 400 particles. Then the emission stops. ps.Effectors.Add(new StreamEmitter { DefaultEmissionRate = 400 * 60, EmissionLimit = 400, }); ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartPositionEffector { Parameter = ParticleParameterNames.Position, Distribution = new BoxDistribution { MinValue = new Vector3F(-10, 0.4f, -10), MaxValue = new Vector3F(10, 0.4f, 10) } }); ps.Parameters.AddVarying<float>(ParticleParameterNames.SizeX); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.SizeX, Distribution = new UniformDistributionF(0.6f, 1), }); ps.Parameters.AddVarying<float>(ParticleParameterNames.SizeY); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.SizeY, Distribution = new UniformDistributionF(0.6f, 1), }); ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Color); ps.Effectors.Add(new StartValueEffector<Vector3F> { Parameter = ParticleParameterNames.Color, Distribution = new LineSegmentDistribution { Start = new Vector3F(0.82f, 0.92f, 1) * 0.9f, End = new Vector3F(1, 1, 1) } }); ps.Parameters.AddUniform<Texture2D>(ParticleParameterNames.Texture).DefaultValue = contentManager.Load<Texture2D>("Particles/Grass"); ps.Parameters.AddUniform<BillboardOrientation>(ParticleParameterNames.BillboardOrientation).DefaultValue = BillboardOrientation.AxialViewPlaneAligned; ps.Parameters.AddUniform<bool>(ParticleParameterNames.IsDepthSorted).DefaultValue = true; ParticleSystemValidator.Validate(ps); return ps; }
public static ParticleSystem Create(ContentManager contentManager) { var ps = new ParticleSystem { Name = "Decals", MaxNumberOfParticles = 50, }; ps.Parameters.AddUniform<float>(ParticleParameterNames.Lifetime).DefaultValue = 5; ps.Parameters.AddUniform<float>(ParticleParameterNames.Size).DefaultValue = 0.3f; // Following particle parameters are initialized externally: ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Parameters.AddVarying<Vector3F>("Normal"); ps.Parameters.AddVarying<Vector3F>("Axis"); ps.Parameters.AddUniform<Vector3F>(ParticleParameterNames.Color).DefaultValue = new Vector3F(0.667f, 0.667f, 0.667f); ps.Parameters.AddVarying<float>(ParticleParameterNames.Alpha); ps.Effectors.Add(new SingleLinearSegment3Effector { OutputParameter = ParticleParameterNames.Alpha, Time0 = 0, Value0 = 1, Time1 = 0.9f, Value1 = 1, Time2 = 1, Value2 = 0, }); ps.Parameters.AddVarying<float>(ParticleParameterNames.Angle); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.Angle, Distribution = new UniformDistributionF(-0.5f, 0.5f), }); ps.Parameters.AddUniform<Texture2D>(ParticleParameterNames.Texture).DefaultValue = contentManager.Load<Texture2D>("Particles/BulletHole"); // Particle billboards use a custom billboard orientation: ps.Parameters.AddUniform<BillboardOrientation>(ParticleParameterNames.BillboardOrientation).DefaultValue = BillboardOrientation.WorldOriented; ps.Parameters.AddUniform<BlendState>(ParticleParameterNames.BlendState).DefaultValue = BlendState.AlphaBlend; // If the user places too many decals, then we run out of particles. If the // MaxNumberOfParticles limit is reached, no more decals can be placed. To // avoid this we add the ReserveParticleEffector, which kills old particles // if the MaxNumberOfParticles limit is reached. ps.Effectors.Add(new ReserveParticleEffector()); ParticleSystemValidator.Validate(ps); return ps; }
public FlameJetSample(Microsoft.Xna.Framework.Game game) : base(game) { _flameJet = FlameJet.Create(ContentManager); _flameJet.Pose = new Pose(new Vector3F(0, 2, 0), Matrix33F.CreateRotationY(ConstantsF.PiOver2)); ParticleSystemService.ParticleSystems.Add(_flameJet); _particleSystemNode = new ParticleSystemNode(_flameJet); GraphicsScreen.Scene.Children.Add(_particleSystemNode); }
// Creates a particle system that display a single particle: a bright billboard // for a "flash" effect. private ParticleSystem CreateFlash(ContentManager contentManager) { var ps = new ParticleSystem { Name = "Flash", MaxNumberOfParticles = 1, ReferenceFrame = ParticleReferenceFrame.World, // Optimization tip: Use same random number generator as parent. Random = Random, }; ps.Parameters.AddUniform<float>(ParticleParameterNames.Lifetime).DefaultValue = 0.3f; ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartPositionEffector { Parameter = ParticleParameterNames.Position, DefaultValue = Vector3F.Zero, }); ps.Parameters.AddVarying<float>(ParticleParameterNames.Size); ps.Parameters.AddUniform<float>("StartSize").DefaultValue = 0.0f; ps.Parameters.AddUniform<float>("EndSize").DefaultValue = 40.0f; ps.Effectors.Add(new SingleLerpEffector { ValueParameter = ParticleParameterNames.Size, FactorParameter = ParticleParameterNames.NormalizedAge, StartParameter = "StartSize", EndParameter = "EndSize", }); ps.Parameters.AddVarying<float>(ParticleParameterNames.Alpha); ps.Parameters.AddUniform<float>("TargetAlpha").DefaultValue = 0.8f; ps.Effectors.Add(new SingleFadeEffector { ValueParameter = ParticleParameterNames.Alpha, TargetValueParameter = "TargetAlpha", TimeParameter = ParticleParameterNames.NormalizedAge, FadeInStart = 0.0f, FadeInEnd = 0.2f, FadeOutStart = 0.75f, FadeOutEnd = 1.0f, }); ps.Parameters.AddUniform<Vector3F>(ParticleParameterNames.Color).DefaultValue = new Vector3F(1, 1, 216.0f / 255.0f); ps.Parameters.AddUniform<Texture2D>(ParticleParameterNames.Texture).DefaultValue = contentManager.Load<Texture2D>("Particles/Flash"); ps.Parameters.AddUniform<float>(ParticleParameterNames.BlendMode).DefaultValue = 0; return ps; }
private void CreateParticleSystem() { // Load a sphere model. var modelNode = ContentManager.Load<ModelNode>("Particles/Sphere"); var meshNode = (MeshNode)modelNode.Children[0]; // Add gravity and damping to the physics simulation. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Create two instances of the sphere model. _meshNode0 = meshNode.Clone(); GraphicsScreen.Scene.Children.Add(_meshNode0); _meshNode1 = meshNode.Clone(); GraphicsScreen.Scene.Children.Add(_meshNode1); // Create a rigid body for the left sphere. _rigidBody0 = new RigidBody(new SphereShape(0.5f)) { Pose = new Pose(new Vector3F(-3, 4, 0)), }; Simulation.RigidBodies.Add(_rigidBody0); // Create a rigid body for the right sphere. (Sharing the same shape, mass and material.) _rigidBody1 = new RigidBody(_rigidBody0.Shape, _rigidBody0.MassFrame, _rigidBody0.Material) { Pose = new Pose(new Vector3F(3, 4, 0)), }; Simulation.RigidBodies.Add(_rigidBody1); // Extract basic triangle mesh from the sphere model. var triangleMesh = meshNode.Mesh.Submeshes[0].ToTriangleMesh(); // Create a particle system for the left ball. This particle system uses // ReferenceFrame == ParticleReferenceFrame.World - which is the default for all // particle systems. Particles are all relative to world space. The particle system pose // determines the start positions and direction (when the StartPositionEffector and // StartDirectionEffector are in use). Particles do not move with the particle system. _particleSystem0 = GlowingMeshEffect.Create(triangleMesh, ContentManager); _particleSystem0.ReferenceFrame = ParticleReferenceFrame.World; ParticleSystemService.ParticleSystems.Add(_particleSystem0); _particleSystemNode0 = new ParticleSystemNode(_particleSystem0); _meshNode0.Children = new SceneNodeCollection { _particleSystemNode0 }; // Create a particle system for the right ball. This particle system uses // ReferenceFrame == ParticleReferenceFrame.Local. Particles are all relative to the // particle system pose. Particles move with the particle system. _particleSystem1 = GlowingMeshEffect.Create(triangleMesh, ContentManager); _particleSystem1.ReferenceFrame = ParticleReferenceFrame.Local; ParticleSystemService.ParticleSystems.Add(_particleSystem1); _particleSystemNode1 = new ParticleSystemNode(_particleSystem1); _meshNode1.Children = new SceneNodeCollection { _particleSystemNode1 }; }
public DecalSample(Microsoft.Xna.Framework.Game game) : base(game) { GraphicsScreen.DrawReticle = true; SetCamera(new Vector3F(0, 2, 6), 0, 0); _decals = Decals.Create(ContentManager); ParticleSystemService.ParticleSystems.Add(_decals); _particleSystemNode = new ParticleSystemNode(_decals); GraphicsScreen.Scene.Children.Add(_particleSystemNode); }
public AnimatedTextureSample(Microsoft.Xna.Framework.Game game) : base(game) { _beeSwarm = BeeSwarm.Create(ContentManager); _beeSwarm.Pose = new Pose(new Vector3F(0, 4, 0)); // Create 100 bees. _beeSwarm.AddParticles(100); ParticleSystemService.ParticleSystems.Add(_beeSwarm); _particleSystemNode = new ParticleSystemNode(_beeSwarm); GraphicsScreen.Scene.Children.Add(_particleSystemNode); }
public void Initialize(ContentManager contentManager) { if (ParticleSystemNode == null) { // This is the first time this instance is used. var ps = new ParticleSystem { Name = "Teleport" + _count, ReferenceFrame = ParticleReferenceFrame.Local, Children = new ParticleSystemCollection { CreateSparkles(contentManager), CreateFastBeams(contentManager), CreateSlowBeams(contentManager), } }; // Add a uniform float particle parameter that contains the particle system time. ps.Parameters.AddUniform<float>("Time"); ps.Effectors.Add(new TimeToSingleEffector { Parameter = "Time" }); // Add a uniform GlobalAlpha parameter. This parameter controls the alpha of all // child particle systems. ps.Parameters.AddUniform<float>("GlobalAlpha"); ps.Effectors.Add(new SingleFadeEffector { ValueParameter = "GlobalAlpha", TimeParameter = "Time", FadeInStart = 0, FadeInEnd = 2, FadeOutStart = 2, FadeOutEnd = 3, }); ParticleSystemValidator.Validate(ps); ParticleSystemValidator.Validate(ps.Children[0]); ParticleSystemValidator.Validate(ps.Children[1]); ParticleSystemValidator.Validate(ps.Children[2]); ParticleSystemNode = new ParticleSystemNode(ps) { Name = "TeleportNode" + _count }; _count++; } else { ParticleSystem.Reset(); ParticleSystemNode.PoseWorld = Pose.Identity; } }
public static void Validate(ParticleSystem particleSystem) { var message1 = CheckForUninitializedEffectorProperties(particleSystem); var message2 = CheckForMissingParticleParameters(particleSystem); var message3 = CheckForUninitializedParticleParameters(particleSystem); if (message1 != null || message2 != null || message3 != null) { Debug.WriteLine("----- \"{0}\" (type {1}):", particleSystem.Name, particleSystem.GetType().Name); if (message1 != null) Debug.WriteLine(message1); if (message2 != null) Debug.WriteLine(message2); if (message3 != null) Debug.WriteLine(message3); } }
// Is there a particle parameter that is required by an effector but is missing in the // particle system? public static string CheckForMissingParticleParameters(ParticleSystem particleSystem) { string message = "Parameters missing in particle system: "; List<string> missing = new List<string>(); foreach (var effector in particleSystem.Effectors) { foreach (var propertyInfo in GetProperties(effector)) { // Handle effector properties with a ParticleParameterAttribute. var particleParameterAttribute = propertyInfo.GetCustomAttributes(typeof(ParticleParameterAttribute), true).Cast<ParticleParameterAttribute>().FirstOrDefault(); if (particleParameterAttribute != null) { var parameterName = propertyInfo.GetValue(effector, null) as string; if (string.IsNullOrEmpty(parameterName)) continue; // Property is probably not in use. if (particleSystem.Parameters.Contains(parameterName)) continue; // Parameter was found in the particle system. if (missing.Contains(parameterName)) continue; // Parameter was already reported as missing. if (particleSystem.Parent != null && particleSystem.Parent.Parameters.Any(p => p.IsUniform && p.Name == parameterName)) continue; // Parameter is uniform and was found in the parent particle system. // The particle parameter is missing. missing.Add(parameterName); message += string.Format("\"{0}\" ", parameterName); } } } return missing.Count > 0 ? message : null; }
private static ParticleSystem CreateSmoke(ContentManager content) { ParticleSystem ps = new ParticleSystem { Name = "CampfireSmoke", MaxNumberOfParticles = 24, PreloadDuration = new TimeSpan(0, 0, 0, 2), }; // Make all computations relative to the pose (position and orientation) of the // particle system to allow instancing. (I.e. the particle system can be added // more than once to the scene.) ps.ReferenceFrame = ParticleReferenceFrame.Local; // Each particle lives for a random time span. ps.Parameters.AddVarying<float>(ParticleParameterNames.Lifetime); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.Lifetime, Distribution = new UniformDistributionF(2.0f, 2.4f), }); // Add an effector that emits particles at a constant rate. ps.Effectors.Add(new StreamEmitter { DefaultEmissionRate = 10, }); // Particle positions start on a circular area (in the xy-plane). ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartPositionEffector { Parameter = ParticleParameterNames.Position, Distribution = new CircleDistribution { OuterRadius = 0.4f, InnerRadius = 0 } }); // Set default axis of billboards. (Usually Vector3F.Up, but in this case the // particle system is rotated.) ps.Parameters.AddUniform<Vector3F>(ParticleParameterNames.Axis).DefaultValue = Vector3F.Forward; // Particles move in up direction with a slight random deviation with a random speed. ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Direction); ps.Effectors.Add(new StartDirectionEffector { Parameter = ParticleParameterNames.Direction, Distribution = new DirectionDistribution { Deviation = 0.15f, Direction = Vector3F.Forward }, }); ps.Parameters.AddVarying<float>(ParticleParameterNames.LinearSpeed); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.LinearSpeed, Distribution = new UniformDistributionF(0, 1), }); // The LinearVelocityEffector uses the Direction and LinearSpeed to update the Position // of particles. ps.Effectors.Add(new LinearVelocityEffector()); // Lets apply a damping (= exponential decay) to the LinearSpeed using the SingleDampingEffector. ps.Parameters.AddUniform<float>(ParticleParameterNames.Damping).DefaultValue = 1.0f; ps.Effectors.Add(new SingleDampingEffector { // Following parameters are equal to the default values. No need to set them. //ValueParameter = ParticleParameterNames.LinearSpeed, //DampingParameter = ParticleParameterNames.Damping, }); // To create a wind effect, we apply an acceleration to all particles. ps.Parameters.AddUniform<Vector3F>("Wind").DefaultValue = new Vector3F(-1, -0.5f, -3);//new Vector3F(-1, 3, -0.5f); ps.Effectors.Add(new LinearAccelerationEffector { AccelerationParameter = "Wind" }); // Each particle starts with a random rotation angle and a random angular speed. ps.Parameters.AddVarying<float>(ParticleParameterNames.Angle); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.Angle, Distribution = new UniformDistributionF(-ConstantsF.PiOver2, ConstantsF.PiOver2), }); ps.Parameters.AddVarying<float>(ParticleParameterNames.AngularSpeed); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.AngularSpeed, Distribution = new UniformDistributionF(-2f, 2f), }); // The AngularVelocityEffector uses the AngularSpeed to update the particle Angle. ps.Effectors.Add(new AngularVelocityEffector { AngleParameter = ParticleParameterNames.Angle, SpeedParameter = ParticleParameterNames.AngularSpeed, }); // Each particle gets a random start and end size. ps.Parameters.AddVarying<float>("StartSize"); ps.Effectors.Add(new StartValueEffector<float> { Parameter = "StartSize", Distribution = new UniformDistributionF(0.5f, 0.7f), }); ps.Parameters.AddVarying<float>("EndSize"); ps.Effectors.Add(new StartValueEffector<float> { Parameter = "EndSize", Distribution = new UniformDistributionF(1.0f, 1.4f), }); // The Size is computed from linear interpolation between the StartSize and the EndSize. ps.Parameters.AddVarying<float>(ParticleParameterNames.Size); ps.Effectors.Add(new SingleLerpEffector { ValueParameter = ParticleParameterNames.Size, FactorParameter = ParticleParameterNames.NormalizedAge, StartParameter = "StartSize", EndParameter = "EndSize", }); // The Color slowly changes linearly from light gray to a darker gray. ps.Parameters.AddUniform<Vector3F>("StartColor").DefaultValue = new Vector3F(0.1f, 0.1f, 0.1f); ps.Parameters.AddUniform<Vector3F>("EndColor").DefaultValue = new Vector3F(0.01f, 0.01f, 0.01f); ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Color); ps.Effectors.Add(new Vector3FLerpEffector { ValueParameter = ParticleParameterNames.Color, StartParameter = "StartColor", EndParameter = "EndColor", }); // The Alpha value is 0 for a short time, then it fades in to the TargetAlpha and finally // it fades out again. ps.Parameters.AddVarying<float>(ParticleParameterNames.Alpha); ps.Parameters.AddUniform<float>("TargetAlpha").DefaultValue = 1.0f; ps.Effectors.Add(new SingleFadeEffector { ValueParameter = ParticleParameterNames.Alpha, TargetValueParameter = "TargetAlpha", TimeParameter = ParticleParameterNames.NormalizedAge, FadeInStart = 0.36f, FadeInEnd = 0.6f, FadeOutStart = 0.6f, FadeOutEnd = 1.0f, }); // DigitalRune Graphics supports a "Texture" parameter of type Texture2D or // PackedTexture. The texture "Smoke2.png" contains a tile set, which can be // described using the PackedTexture class. ps.Parameters.AddUniform<PackedTexture>(ParticleParameterNames.Texture).DefaultValue = new PackedTexture("Smoke", content.Load<Texture2D>("Campfire/Smoke2"), Vector2F.Zero, Vector2F.One, 2, 1); // Each particle chooses a random image of the tile set when it is created. // The "AnimationTime" parameter selects an image: // 0 = start of animation = first image in tile set // 1 = end of animation = last image in tile set) ps.Parameters.AddVarying<float>(ParticleParameterNames.AnimationTime); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.AnimationTime, Distribution = new UniformDistributionF(0, 1), // Random value between 0 and 1. }); // The smoke effect uses a mix of additive blending (BlendMode = 0) // and alpha blending (BlendMode = 1). ps.Parameters.AddUniform<float>(ParticleParameterNames.BlendMode).DefaultValue = 0.5f; // Optional: Set a bounding shape for frustum culling. The bounding shape needs // to be large enough to include all smoke particles. ps.Shape = new TransformedShape(new GeometricObject(new BoxShape(3, 3, 4), new Pose(new Vector3F(-1, 0, -3)))); ps.Parameters.AddUniform<int>(ParticleParameterNames.DrawOrder).DefaultValue = 1; return ps; }
// Check out the ParticleSample ("Samples/DigitalRune.Particles/ParticleSample") // to learn more about DigitalRune Particles. Also, make sure to read the class // documentation of the ParticleSystemNode. The documentation describes all particle // parameters that are supported by DigitalRune Graphics! private static ParticleSystem CreateFire(ContentManager content) { ParticleSystem ps = new ParticleSystem { Name = "Campfire", MaxNumberOfParticles = 25 }; // Make all computations relative to the pose (position and orientation) of the // particle system to allow instancing. (I.e. the particle system can be added // more than once to the scene.) ps.ReferenceFrame = ParticleReferenceFrame.Local; // Each particle lives for a random time span. ps.Parameters.AddVarying<float>(ParticleParameterNames.Lifetime); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.Lifetime, Distribution = new UniformDistributionF(0.8f, 1.2f), }); // Add an effector that emits particles at a constant rate. ps.Effectors.Add(new StreamEmitter { DefaultEmissionRate = 20, }); // Particle positions start on a circular area (in the xy-plane). ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartPositionEffector { Parameter = ParticleParameterNames.Position, Distribution = new CircleDistribution { OuterRadius = 0.4f, InnerRadius = 0 } }); // Set default axis of billboards. (Usually Vector3F.Up, but in this case the // particle system is rotated.) ps.Parameters.AddUniform<Vector3F>(ParticleParameterNames.Axis).DefaultValue = Vector3F.Forward; // Particles move in forward direction with a random speed. ps.Parameters.AddUniform<Vector3F>(ParticleParameterNames.Direction).DefaultValue = Vector3F.Forward; ps.Parameters.AddVarying<float>(ParticleParameterNames.LinearSpeed); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.LinearSpeed, Distribution = new UniformDistributionF(0, 1), }); // The LinearVelocityEffector uses the Direction and LinearSpeed to update the Position // of particles. ps.Effectors.Add(new LinearVelocityEffector()); // Lets apply a damping (= exponential decay) to the LinearSpeed using the SingleDampingEffector. ps.Parameters.AddUniform<float>(ParticleParameterNames.Damping).DefaultValue = 1.0f; ps.Effectors.Add(new SingleDampingEffector { // Following parameters are equal to the default values. No need to set them. //ValueParameter = ParticleParameterNames.LinearSpeed, //DampingParameter = ParticleParameterNames.Damping, }); // To create a wind effect, we apply an acceleration to all particles. ps.Parameters.AddUniform<Vector3F>("Wind").DefaultValue = new Vector3F(-1, -0.5f, -3);//new Vector3F(-1, 3, -0.5f); ps.Effectors.Add(new LinearAccelerationEffector { AccelerationParameter = "Wind" }); // Each particle starts with a random rotation angle and a random angular speed. ps.Parameters.AddVarying<float>(ParticleParameterNames.Angle); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.Angle, Distribution = new UniformDistributionF(-ConstantsF.Pi, ConstantsF.Pi), }); ps.Parameters.AddVarying<float>(ParticleParameterNames.AngularSpeed); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.AngularSpeed, Distribution = new UniformDistributionF(-2f, 2f), }); // The AngularVelocityEffector uses the AngularSpeed to update the particle Angle. ps.Effectors.Add(new AngularVelocityEffector()); // All particle have the same size. ps.Parameters.AddUniform<float>(ParticleParameterNames.Size).DefaultValue = 0.8f; // Particle alpha fades in to 1 and then back out to 0. ps.Parameters.AddVarying<float>(ParticleParameterNames.Alpha); ps.Effectors.Add(new SingleFadeEffector { ValueParameter = ParticleParameterNames.Alpha, FadeInStart = 0.0f, FadeInEnd = 0.3f, FadeOutStart = 0.7f, FadeOutEnd = 1.0f, }); ps.Parameters.AddUniform<Vector3F>(ParticleParameterNames.Color).DefaultValue = new Vector3F(5, 5, 5); // DigitalRune Graphics supports a "Texture" parameter of type Texture2D or // PackedTexture. The texture "FireParticles.tga" is a tile set, which can be // described using a PackedTexture. ps.Parameters.AddUniform<PackedTexture>(ParticleParameterNames.Texture).DefaultValue = new PackedTexture("FireParticles", content.Load<Texture2D>("Campfire/FireParticles"), Vector2F.Zero, Vector2F.One, 4, 1); // Each particle chooses a random image of the tile set when it is created. // The "AnimationTime" parameter selects an image: // 0 = start of animation = first image in tile set // 1 = end of animation = last image in tile set) ps.Parameters.AddVarying<float>(ParticleParameterNames.AnimationTime); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.AnimationTime, Distribution = new UniformDistributionF(0, 1), // Random value between 0 and 1. }); // The fire effect uses additive blending (BlendMode = 0). ps.Parameters.AddUniform<float>(ParticleParameterNames.BlendMode).DefaultValue = 0; // Enable soft particles. ps.Parameters.AddUniform<float>(ParticleParameterNames.Softness).DefaultValue = float.NaN; // NaN = automatic // Optional: Set a bounding shape for frustum culling. The bounding shape needs // to be large enough to include all fire particles. ps.Shape = new TransformedShape(new GeometricObject(new BoxShape(2.5f, 2.5f, 2.5f), new Pose(new Vector3F(0, 0, -1)))); return ps; }
private ParticleSystem CreateExplosionDistortionParticleSystem() { ParticleSystem ps = new ParticleSystem { Name = "ExplosionDistortion", MaxNumberOfParticles = 60, }; ps.ReferenceFrame = ParticleReferenceFrame.Local; // Lifetime ps.Parameters.AddUniform<float>(ParticleParameterNames.Lifetime).DefaultValue = 0.5f; // Position ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartPositionEffector { Parameter = ParticleParameterNames.Position, DefaultValue = Vector3F.Zero, }); // Velocity ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Direction); ps.Effectors.Add(new StartDirectionEffector { Parameter = ParticleParameterNames.Direction, Distribution = new SphereDistribution { InnerRadius = 1.0f, OuterRadius = 1.0f }, }); ps.Parameters.AddVarying<float>(ParticleParameterNames.LinearSpeed); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.LinearSpeed, Distribution = new UniformDistributionF(10, 20), }); ps.Effectors.Add(new LinearVelocityEffector()); // Angle ps.Parameters.AddVarying<float>(ParticleParameterNames.Angle); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.Angle, Distribution = new UniformDistributionF(-ConstantsF.Pi, ConstantsF.Pi), }); // Angular Velocity ps.Parameters.AddVarying<float>(ParticleParameterNames.AngularSpeed); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.AngularSpeed, Distribution = new UniformDistributionF(-0.4f, 0.4f), }); ps.Effectors.Add(new AngularVelocityEffector()); // Size ps.Parameters.AddVarying<float>("StartSize"); ps.Effectors.Add(new StartValueEffector<float> { Parameter = "StartSize", Distribution = new UniformDistributionF(2f, 4.0f), }); ps.Parameters.AddVarying<float>("EndSize"); ps.Effectors.Add(new StartValueEffector<float> { Parameter = "EndSize", Distribution = new UniformDistributionF(6f, 16.0f), }); ps.Parameters.AddVarying<float>(ParticleParameterNames.Size); ps.Effectors.Add(new SingleLerpEffector { ValueParameter = ParticleParameterNames.Size, FactorParameter = ParticleParameterNames.NormalizedAge, StartParameter = "StartSize", EndParameter = "EndSize", }); // Alpha ps.Parameters.AddVarying<float>(ParticleParameterNames.Alpha); ps.Parameters.AddUniform<float>("TargetAlpha").DefaultValue = 1.0f; ps.Effectors.Add(new SingleFadeEffector { ValueParameter = ParticleParameterNames.Alpha, TargetValueParameter = "TargetAlpha", TimeParameter = ParticleParameterNames.NormalizedAge, FadeInStart = 0.0f, FadeInEnd = 0.05f, FadeOutStart = 0.5f, FadeOutEnd = 1.0f, }); // Texture ps.Parameters.AddUniform<Texture2D>(ParticleParameterNames.Texture).DefaultValue = ContentManager.Load<Texture2D>("Particles/Distortion"); // Softness ps.Parameters.AddUniform<float>(ParticleParameterNames.Softness).DefaultValue = float.NaN; // NaN = automatic // Bounding shape ps.Shape = new SphereShape(10); return ps; }
private ParticleSystem CreateNovaDistortionParticleSystem() { ParticleSystem ps = new ParticleSystem { Name = "NovaDistortion", MaxNumberOfParticles = 80, }; ps.ReferenceFrame = ParticleReferenceFrame.Local; // Lifetime ps.Parameters.AddUniform<float>(ParticleParameterNames.Lifetime).DefaultValue = 0.3f; // Position ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartPositionEffector { Parameter = ParticleParameterNames.Position, DefaultValue = Vector3F.Zero, }); // Velocity ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Direction); ps.Effectors.Add(new StartDirectionEffector { Parameter = ParticleParameterNames.Direction, Distribution = new CircleDistribution { InnerRadius = 1.0f, OuterRadius = 1.0f }, }); ps.Parameters.AddUniform<float>(ParticleParameterNames.LinearSpeed).DefaultValue = 20; ps.Effectors.Add(new LinearVelocityEffector()); // Size ps.Parameters.AddUniform<float>(ParticleParameterNames.Size).DefaultValue = 2; // Alpha ps.Parameters.AddVarying<float>(ParticleParameterNames.Alpha); ps.Parameters.AddUniform<float>("TargetAlpha").DefaultValue = 1.0f; ps.Effectors.Add(new SingleFadeEffector { ValueParameter = ParticleParameterNames.Alpha, TargetValueParameter = "TargetAlpha", TimeParameter = ParticleParameterNames.NormalizedAge, FadeInStart = 0.0f, FadeInEnd = 0.01f, FadeOutStart = 0.90f, FadeOutEnd = 1.0f, }); // Texture ps.Parameters.AddUniform<Texture2D>(ParticleParameterNames.Texture).DefaultValue = ContentManager.Load<Texture2D>("Particles/Distortion"); // Softness ps.Parameters.AddUniform<float>(ParticleParameterNames.Softness).DefaultValue = float.NaN; // NaN = automatic // Bounding shape ps.Shape = new BoxShape(15, 15, 2); return ps; }
// Is there a particle parameter that is not initialized by a start value effector? public static string CheckForUninitializedParticleParameters(ParticleSystem particleSystem) { // Get the names of all varying particle parameters List<string> parameters = particleSystem.Parameters.Where(p => !p.IsUniform).Select(p => p.Name).ToList(); // "NormalizedAge" is the only parameter handled by the particle system itself. parameters.Remove(ParticleParameterNames.NormalizedAge); foreach (var effector in particleSystem.Effectors) { foreach (var propertyInfo in GetProperties(effector)) { // Handle effector properties with a ParticleParameterAttribute. var particleParameterAttribute = propertyInfo.GetCustomAttributes(typeof(ParticleParameterAttribute), true).Cast<ParticleParameterAttribute>().FirstOrDefault(); if (particleParameterAttribute != null) { var parameterName = propertyInfo.GetValue(effector, null) as string; if (!string.IsNullOrEmpty(parameterName) && particleParameterAttribute.Usage == ParticleParameterUsage.Out) { // The effector initializes this particle parameter. parameters.Remove(parameterName); } } } } if (parameters.Count > 0) { string message = "Parameters possibly not initialized by any start value effector: "; foreach (var parameter in parameters) message += string.Format("\"{0}\" ", parameter); return message; } return null; }
public static ParticleSystem Create(ContentManager contentManager) { var ps = new ParticleSystem { Name = "BeeSwarm", MaxNumberOfParticles = 100, }; ps.Parameters.AddUniform<float>(ParticleParameterNames.Lifetime).DefaultValue = float.PositiveInfinity; ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartPositionEffector { Parameter = ParticleParameterNames.Position, DefaultValue = new Vector3F(0, 0, 0) }); ps.Parameters.AddUniform<float>(ParticleParameterNames.SizeY).DefaultValue = 0.1f; // The SizeX is varying because the BeeEffector sets a negative size if the bee should look in the // opposite direction. ps.Parameters.AddVarying<float>(ParticleParameterNames.SizeX); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.SizeX, DefaultValue = 0.1f, }); ps.Parameters.AddVarying<float>(ParticleParameterNames.LinearSpeed); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.LinearSpeed, Distribution = new UniformDistributionF(1, 2), }); // The BeeEffector creates the random movement of the bees. ps.Parameters.AddVarying<Vector3F>("TargetPosition"); ps.Parameters.AddUniform<Pose>("CameraPose").DefaultValue = Pose.Identity; ps.Effectors.Add(new BeeEffector { PositionParameter = ParticleParameterNames.Position, TargetPositionParameter = "TargetPosition", SpeedParameter = ParticleParameterNames.LinearSpeed, SizeXParameter = ParticleParameterNames.SizeX, CameraPoseParameter = "CameraPose", InvertLookDirection = true, MaxRange = 4.0f, }); // The texture is a set of 3 images. ps.Parameters.AddUniform<PackedTexture>(ParticleParameterNames.Texture).DefaultValue = new PackedTexture( "Bee", contentManager.Load<Texture2D>("Particles/beeWingFlap"), Vector2F.Zero, Vector2F.One, 3, 1); // The Frame particle parameter stores the index of the animation frame and the // AnimationTime particle parameter stores the current progress in seconds. ps.Parameters.AddVarying<int>("Frame"); ps.Parameters.AddVarying<float>("AnimationTime"); // Initialize the AnimationTime with a random value, otherwise all bees would look // the same. ps.Effectors.Add(new StartValueEffector<float> { Parameter = "AnimationTime", Distribution = new UniformDistributionF(0, 0.125f), }); // The AnimationEffector advances the AnimationTime and sets the Frame. // It changes frames at 24 fps. ps.Effectors.Add(new AnimationEffector { AnimationTimeParameter = "AnimationTime", FramesPerSecond = 24, NumberOfFrames = 3, }); ParticleSystemValidator.Validate(ps); return ps; }
public static ParticleSystem Create(ContentManager contentManager) { var ps = new ParticleSystem { Name = "BouncingSparks", MaxNumberOfParticles = 200, }; ps.Parameters.AddUniform<float>(ParticleParameterNames.Lifetime).DefaultValue = 3; ps.Effectors.Add(new StreamEmitter { DefaultEmissionRate = 40, }); ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartPositionEffector { Parameter = ParticleParameterNames.Position, Distribution = new LineSegmentDistribution { Start = new Vector3F(-0.2f, 0, -6), End = new Vector3F(0.2f, 0, -6) } }); // The particles are rendered using axial billboards. ps.Parameters.AddUniform<BillboardOrientation>(ParticleParameterNames.BillboardOrientation).DefaultValue = BillboardOrientation.AxialViewPlaneAligned; // The "Axis" parameter defines the up-axis of the particle billboard. In this // case the "Axis" is the direction of each particle. ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Axis); ps.Effectors.Add(new StartDirectionEffector { Parameter = ParticleParameterNames.Axis, Distribution = new DirectionDistribution { Deviation = 0.3f, Direction = new Vector3F(1, 0.5f, -1f).Normalized }, }); ps.Parameters.AddVarying<float>(ParticleParameterNames.LinearSpeed); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.LinearSpeed, Distribution = new UniformDistributionF(15, 20), }); ps.Effectors.Add(new LinearVelocityEffector { DirectionParameter = ParticleParameterNames.Axis, }); ps.Parameters.AddUniform<float>(ParticleParameterNames.Damping).DefaultValue = 0.5f; ps.Effectors.Add(new SingleDampingEffector()); ps.Parameters.AddUniform<Vector3F>(ParticleParameterNames.LinearAcceleration).DefaultValue = new Vector3F(0, -5f, 0); ps.Effectors.Add(new LinearAccelerationEffector { DirectionParameter = ParticleParameterNames.Axis }); // Create a collision plane effector for the ground. // We do not position the collision plane at height. Instead, we add a small offset otherwise // the particle billboards would be cut off by the visible ground. ps.Effectors.Add(new CollisionPlaneEffector { DirectionParameter = ParticleParameterNames.Axis, Plane = new Plane(new Vector3F(0, 1, 0), 0.03f), }); // Add more collision plane effectors for the 4 walls of our sandbox. const float offset = 0.08f; ps.Effectors.Add(new CollisionPlaneEffector { DirectionParameter = ParticleParameterNames.Axis, Plane = new Plane(new Vector3F(-1, 0, 0), -10 + offset), }); ps.Effectors.Add(new CollisionPlaneEffector { DirectionParameter = ParticleParameterNames.Axis, Plane = new Plane(new Vector3F(0, 0, 1), -10 + offset), }); ps.Effectors.Add(new CollisionPlaneEffector { DirectionParameter = ParticleParameterNames.Axis, Plane = new Plane(new Vector3F(1, 0, 0), -10 + offset), }); ps.Effectors.Add(new CollisionPlaneEffector { DirectionParameter = ParticleParameterNames.Axis, Plane = new Plane(new Vector3F(0, 0, -1), -10 + offset), }); // Particles billboards get stretched in the y-direction. The stretch is time-dependent. // (The y-direction of a particle is defined by the "Axis" parameter.) ps.Parameters.AddUniform<float>(ParticleParameterNames.SizeX).DefaultValue = 0.05f; ps.Parameters.AddVarying<float>(ParticleParameterNames.SizeY); ps.Effectors.Add(new SingleLinearSegment3Effector { OutputParameter = ParticleParameterNames.SizeY, Time0 = 0, Value0 = 0.05f, Time1 = 0.01f, Value1 = 0.5f, Time2 = 1, Value2 = 0.03f, }); ps.Parameters.AddUniform<Vector3F>("StartColor").DefaultValue = new Vector3F(1.0f, 1.0f, 0.8f); ps.Parameters.AddUniform<Vector3F>("EndColor").DefaultValue = new Vector3F(1.0f, 0.3f, 0.0f); ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Color); ps.Effectors.Add(new Vector3FLerpEffector { ValueParameter = ParticleParameterNames.Color, StartParameter = "StartColor", EndParameter = "EndColor", }); ps.Parameters.AddVarying<float>(ParticleParameterNames.Alpha); ps.Parameters.AddUniform<float>("TargetAlpha").DefaultValue = 1f; ps.Effectors.Add(new SingleFadeEffector { ValueParameter = ParticleParameterNames.Alpha, TargetValueParameter = "TargetAlpha", FadeInStart = 0f, FadeInEnd = 0.0f, FadeOutStart = 0.99f, FadeOutEnd = 1f, }); ps.Parameters.AddUniform<Texture2D>(ParticleParameterNames.Texture).DefaultValue = contentManager.Load<Texture2D>("Particles/Spark"); ps.Parameters.AddUniform<float>(ParticleParameterNames.BlendMode).DefaultValue = 0.0f; return ps; }
public BasicParticlesSample(Microsoft.Xna.Framework.Game game) : base(game) { // Create a new "empty" particle system. _particleSystem = new ParticleSystem(); // Names are optional, but very useful for debugging. _particleSystem.Name = "MyFirstParticleSystem"; // The particle system uses pre-allocated arrays. We should define an upper limit for // the number of particles that can be alive at the same moment. _particleSystem.MaxNumberOfParticles = 200; // The particle system's Pose defines the position and orientation of the particle system // in the world. _particleSystem.Pose = new Pose(new Vector3F(0, 2, 0)); // The properties of the particles in the particle system are defined using // "particle parameters" (in the collection _particleSystem.Parameters). // Per default, there is only one parameter: "NormalizedAge" - which is managed // by the particle system itself and is the age of a particle in the range 0 - 1. // All our particles should live for 1 second after they have been created. Therefore, // we add a "uniform" parameter called "Lifetime" and set it to 1. var lifetimeParameter = _particleSystem.Parameters.AddUniform<float>("Lifetime"); lifetimeParameter.DefaultValue = 1f; // Each particle should have a position value. Therefore, we add a "varying" parameter // called "Position". "Varying" means that each particle has its own position value. // The particle system will internally allocate a Vector3F array to store all particle // positions. _particleSystem.Parameters.AddVarying<Vector3F>("Position"); // When particles are created, we want them to appear at random position in a spherical // volume. We add an effector which initializes the particle "Positions" of newly created // particles. _particleSystem.Effectors.Add(new StartPositionEffector { // This effector should initialize the "Position" parameter. // Parameter = "Position", // "Position" is the default value anyway. // The start values should be chosen from this random value distribution: Distribution = new SphereDistribution { OuterRadius = 2 } }); // The particles should slowly fade in and out to avoid sudden appearance and disappearance. // We add a varying particle parameter called "Alpha" to store the alpha value per particle. _particleSystem.Parameters.AddVarying<float>("Alpha"); // The SingleFadeEffector animates a float parameter from 0 to a target value and // back to 0. _particleSystem.Effectors.Add(new SingleFadeEffector { // If TargetValueParameter is not set, then the target value is 1. //TargetValueParameter = 1, // The fade-in/out times are relative to a time parameter. // By default the "NormalizedAge" of the particles is used. //TimeParameter = "NormalizedAge", // The Alpha value should be animated. ValueParameter = "Alpha", // The fade-in/out times relative to the normalized age. FadeInStart = 0.0f, FadeInEnd = 0.3f, FadeOutStart = 0.5f, FadeOutEnd = 1.0f, }); // Next, we choose a texture for the particles. All particles use the same texture // parameter, which means the parameter is "uniform". var textureParameter = _particleSystem.Parameters.AddUniform<Texture2D>("Texture"); textureParameter.DefaultValue = ContentManager.Load<Texture2D>("Particles/LensFlare"); // The blend mode is a value between 0 and 1, where 0 means additive blending // 1 means alpha blending. Values between 0 and 1 are allowed. The particles in // this example should be drawn using additive alpha blending. var blendModeParameter = _particleSystem.Parameters.AddUniform<float>("BlendMode"); blendModeParameter.DefaultValue = 0.0f; // There is a lot to configure. Did we forget anything? - We can use an optional helper method // to validate our particle system. Uninitialized or missing parameters are printed to the // Console. Check the Visual Studio Output window for any messages. ParticleSystemValidator.Validate(_particleSystem); // Adding the particle system to a ParticleSystemService is optional but very useful // because the service will update the particle system for us in each frame. ParticleSystemService.ParticleSystems.Add(_particleSystem); // To render the particle effect, we need to create a scene node and add it to the // scene graph. _particleSystemNode = new ParticleSystemNode(_particleSystem); GraphicsScreen.Scene.Children.Add(_particleSystemNode); // A tip for the future: // The class ParticleParameterNames is a collection of strings that can be used for // common particle parameters. It is recommended to use the particle parameter names in // this class to avoid problems because of typing errors in the source code. }
public void Update(ParticleSystem particleSystem) { Particles.Clear(); var numberOfParticles = particleSystem.NumberOfActiveParticles; if (numberOfParticles == 0) { // Clear texture reference to allow garbage collection. // (Only relevant, if user switches texture.) Texture = null; return; } // Pose (relative to root particle system) var parent = particleSystem.Parent; if (parent == null) { Pose = Pose.Identity; } else { // Collect all poses except for the root particle system pose. Pose = particleSystem.Pose; while (parent.Parent != null) { Pose = parent.Pose * Pose; parent = parent.Parent; } } // ReferenceFrame ReferenceFrame = particleSystem.ReferenceFrame; // ----- Uniform particle parameters // Texture var packedTextureParameter = TextureParameter as IParticleParameter<PackedTexture>; if (packedTextureParameter != null) { Texture = packedTextureParameter.DefaultValue; } else { var textureParameter = TextureParameter as IParticleParameter<Texture2D>; if (textureParameter != null) { var texture = textureParameter.DefaultValue; if (texture != null) { if (Texture == null || Texture.TextureAtlas != texture) Texture = new PackedTexture(texture); } else { Texture = null; } } } // Particles are not rendered without a texture. if (Texture == null) return; float aspectRatio = 1.0f; if (Texture != null) { var texture = Texture.TextureAtlas; float textureAspectRatio = (float)texture.Width / texture.Height; Vector2F texCoordTopLeft = Texture.Offset; Vector2F texCoordBottomRight = Texture.Offset + (Texture.Scale / new Vector2F(Texture.NumberOfColumns, Texture.NumberOfRows)); aspectRatio = textureAspectRatio * (texCoordBottomRight.X - texCoordTopLeft.X) / (texCoordBottomRight.Y - texCoordTopLeft.Y); } // AlphaTest AlphaTest = (AlphaTestParameter != null) ? AlphaTestParameter.DefaultValue : 0.0f; // BillboardOrientation BillboardOrientation = (BillboardOrientationParameter != null) ? BillboardOrientationParameter.DefaultValue : BillboardOrientation.ViewPlaneAligned; // DrawOrder DrawOrder = (DrawOrderParameter != null) ? DrawOrderParameter.DefaultValue : 0; // IsDepthSorted IsDepthSorted = (IsDepthSortedParameter != null) ? IsDepthSortedParameter.DefaultValue : false; // Softness Softness = (SoftnessParameter != null) ? SoftnessParameter.DefaultValue : 0; if (Numeric.IsNaN(Softness)) Softness = -1; // ParticleType (particles vs. ribbons) IsRibbon = (TypeParameter != null) ? (TypeParameter.DefaultValue == ParticleType.Ribbon) : false; // StartsAtOrigin //StartsAtOrigin = (StartsAtOriginParameter != null) ? StartsAtOriginParameter.DefaultValue : false; // TextureTiling TextureTiling = (TextureTilingParameter != null) ? TextureTilingParameter.DefaultValue : 0; // ----- Varying particle parameters Particles.AddRange(numberOfParticles); // Values are set below. var targetArray = Particles.Array; // Determine default size of particles. If one dimension is missing, calculate the // missing value using the aspect ratio of the texture. Vector2F size = Vector2F.One; if (SizeParameter != null) size = new Vector2F(SizeParameter.DefaultValue); if (SizeXParameter != null) size.X = SizeXParameter.DefaultValue; if (SizeYParameter != null) size.Y = SizeYParameter.DefaultValue; if (SizeParameter == null && SizeXParameter != null && SizeYParameter == null) size.Y = size.X / aspectRatio; if (SizeParameter == null && SizeXParameter == null && SizeYParameter != null) size.X = size.Y * aspectRatio; // Initialize particles with default values. var defaultParticle = new Particle { Position = (PositionParameter != null) ? PositionParameter.DefaultValue : new Vector3F(), Normal = (NormalParameter != null) ? NormalParameter.DefaultValue : Vector3F.UnitZ, Axis = (AxisParameter != null) ? AxisParameter.DefaultValue : Vector3F.Up, Size = size, Angle = (AngleParameter != null) ? AngleParameter.DefaultValue : 0.0f, Color = (ColorParameter != null) ? ColorParameter.DefaultValue : Vector3F.One, Alpha = (AlphaParameter != null) ? AlphaParameter.DefaultValue : 1.0f, BlendMode = (BlendModeParameter != null) ? BlendModeParameter.DefaultValue : 1.0f, // AnimationTime is initialized with NormalizedAge below. }; for (int i = 0; i < numberOfParticles; i++) targetArray[i] = defaultParticle; int startIndex = particleSystem.ParticleStartIndex; int totalCount = numberOfParticles; int count0 = totalCount; int endIndex0 = startIndex + count0; int endIndex1 = 0; if (endIndex0 > particleSystem.MaxNumberOfParticles) { count0 = particleSystem.MaxNumberOfParticles - startIndex; endIndex0 = particleSystem.MaxNumberOfParticles; endIndex1 = numberOfParticles - count0; } // NormalizedAge if (NormalizedAgeParameter != null && !NormalizedAgeParameter.IsUniform) { var sourceArray = NormalizedAgeParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) { targetArray[targetIndex].IsAlive = (sourceArray[sourceIndex] < 1.0f); targetArray[targetIndex].AnimationTime = sourceArray[sourceIndex]; } for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) { targetArray[targetIndex].IsAlive = (sourceArray[sourceIndex] < 1.0f); targetArray[targetIndex].AnimationTime = sourceArray[sourceIndex]; } } // Position if (PositionParameter != null && !PositionParameter.IsUniform) { var sourceArray = PositionParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].Position = sourceArray[sourceIndex]; for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].Position = sourceArray[sourceIndex]; } // Normal if (NormalParameter != null && !NormalParameter.IsUniform) { var sourceArray = NormalParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].Normal = sourceArray[sourceIndex]; for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].Normal = sourceArray[sourceIndex]; } // Axis if (AxisParameter != null && !AxisParameter.IsUniform) { var sourceArray = AxisParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].Axis = sourceArray[sourceIndex]; for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].Axis = sourceArray[sourceIndex]; } // Size if (SizeParameter != null && !SizeParameter.IsUniform) { var sourceArray = SizeParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].Size = new Vector2F(sourceArray[sourceIndex]); for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].Size = new Vector2F(sourceArray[sourceIndex]); } if (SizeXParameter != null && !SizeXParameter.IsUniform) { var sourceArray = SizeXParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].Size.X = sourceArray[sourceIndex]; for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].Size.X = sourceArray[sourceIndex]; } if (SizeYParameter != null && !SizeYParameter.IsUniform) { var sourceArray = SizeYParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].Size.Y = sourceArray[sourceIndex]; for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].Size.Y = sourceArray[sourceIndex]; } if (SizeParameter == null && SizeXParameter != null && !SizeXParameter.IsUniform && SizeYParameter == null) { for (int i = 0; i < numberOfParticles; i++) targetArray[i].Size.Y = targetArray[i].Size.X / aspectRatio; } if (SizeParameter == null && SizeXParameter == null && SizeYParameter != null && !SizeYParameter.IsUniform) { for (int i = 0; i < numberOfParticles; i++) targetArray[i].Size.X = targetArray[i].Size.Y * aspectRatio; } // Angle if (AngleParameter != null && !AngleParameter.IsUniform) { var sourceArray = AngleParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].Angle = sourceArray[sourceIndex]; for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].Angle = sourceArray[sourceIndex]; } // Color if (ColorParameter != null && !ColorParameter.IsUniform) { var sourceArray = ColorParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].Color = sourceArray[sourceIndex]; for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].Color = sourceArray[sourceIndex]; } // Alpha if (AlphaParameter != null && !AlphaParameter.IsUniform) { var sourceArray = AlphaParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].Alpha = sourceArray[sourceIndex]; for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].Alpha = sourceArray[sourceIndex]; } // AnimationTime if (AnimationTimeParameter != null) { // AnimationTime has been initialized with NormalizedAge for automatic animations. // But the "AnimationTime" parameter is set explicitly! if (AnimationTimeParameter.IsUniform) { float animationTime = AnimationTimeParameter.DefaultValue; for (int i = 0; i < numberOfParticles; i++) targetArray[i].AnimationTime = animationTime; } else { var sourceArray = AnimationTimeParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].AnimationTime = sourceArray[sourceIndex]; for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].AnimationTime = sourceArray[sourceIndex]; } } // BlendMode if (BlendModeParameter != null && !BlendModeParameter.IsUniform) { var sourceArray = BlendModeParameter.Values; for (int sourceIndex = startIndex, targetIndex = 0; sourceIndex < endIndex0; sourceIndex++, targetIndex++) targetArray[targetIndex].BlendMode = sourceArray[sourceIndex]; for (int sourceIndex = 0, targetIndex = count0; sourceIndex < endIndex1; sourceIndex++, targetIndex++) targetArray[targetIndex].BlendMode = sourceArray[sourceIndex]; } }
//-------------------------------------------------------------- public void RequeryParameters(ParticleSystem particleSystem) { var parameters = particleSystem.Parameters; // Uniform particle parameters. TextureParameter = parameters.GetUnchecked<PackedTexture>(ParticleParameterNames.Texture); if (TextureParameter == null) TextureParameter = parameters.GetUnchecked<Texture2D>(ParticleParameterNames.Texture); AlphaTestParameter = parameters.Get<float>(ParticleParameterNames.AlphaTest); BillboardOrientationParameter = parameters.Get<BillboardOrientation>(ParticleParameterNames.BillboardOrientation); DrawOrderParameter = parameters.Get<int>(ParticleParameterNames.DrawOrder); IsDepthSortedParameter = parameters.Get<bool>(ParticleParameterNames.IsDepthSorted); TypeParameter = parameters.Get<ParticleType>(ParticleParameterNames.Type); TextureTilingParameter = parameters.Get<int>(ParticleParameterNames.TextureTiling); // Uniform or varying particle parameters. NormalizedAgeParameter = parameters.Get<float>(ParticleParameterNames.NormalizedAge); PositionParameter = parameters.Get<Vector3F>(ParticleParameterNames.Position); NormalParameter = parameters.Get<Vector3F>(ParticleParameterNames.Normal); AxisParameter = parameters.Get<Vector3F>(ParticleParameterNames.Axis); SizeParameter = parameters.Get<float>(ParticleParameterNames.Size); SizeXParameter = parameters.Get<float>(ParticleParameterNames.SizeX); SizeYParameter = parameters.Get<float>(ParticleParameterNames.SizeY); AngleParameter = parameters.Get<float>(ParticleParameterNames.Angle); ColorParameter = parameters.Get<Vector3F>(ParticleParameterNames.Color); AlphaParameter = parameters.Get<float>(ParticleParameterNames.Alpha); AnimationTimeParameter = parameters.Get<float>(ParticleParameterNames.AnimationTime); BlendModeParameter = parameters.Get<float>(ParticleParameterNames.BlendMode); SoftnessParameter = parameters.Get<float>(ParticleParameterNames.Softness); //StartsAtOriginParameter = parameters.Get<bool>(ParticleParameterNames.StartsAtOrigin); }
public static ParticleSystem Create(ContentManager contentManager) { ParticleSystem ps = new ParticleSystem { Name = "Smoke", MaxNumberOfParticles = 200, }; // All particles should live for 5 seconds. ps.Parameters.AddUniform<float>(ParticleParameterNames.Lifetime).DefaultValue = 5; // Add an effector that emits particles at a constant rate. ps.Effectors.Add(new StreamEmitter { DefaultEmissionRate = 30, }); // The reference frame can be either "Local" or "World" (Default). // - "Local" means that the particle positions, directions, velocities, etc. // are relative to the ParticleSystemNode in the scene graph. // - "World" means that those values are given in world space. The position // of the ParticleSystemNode in the scene graph does not affect the particles. // (For more information check out sample "11-ReferenceFrame".) ps.ReferenceFrame = ParticleReferenceFrame.Local; // Particle positions start in the center of the particle system. ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartPositionEffector { Parameter = ParticleParameterNames.Position, DefaultValue = Vector3F.Zero, }); // Particles move in the up direction with a random deviation of 0.5 radians and a // random speed. ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Direction); ps.Effectors.Add(new StartDirectionEffector { Parameter = ParticleParameterNames.Direction, Distribution = new DirectionDistribution { Deviation = 0.5f, Direction = Vector3F.Up }, }); ps.Parameters.AddVarying<float>(ParticleParameterNames.LinearSpeed); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.LinearSpeed, Distribution = new UniformDistributionF(0.5f, 1), }); // The LinearVelocityEffector uses the Direction and LinearSpeed to update the Position // of particles. ps.Effectors.Add(new LinearVelocityEffector { // Following parameters are equal to the default values. No need to set them. //PositionParameter = ParticleParameterNames.Position, //DirectionParameter = ParticleParameterNames.Direction, //SpeedParameter = ParticleParameterNames.LinearSpeed, }); // To create a wind effect, we apply an acceleration to all particles. ps.Parameters.AddUniform<Vector3F>(ParticleParameterNames.LinearAcceleration).DefaultValue = new Vector3F(0.2f, -0.1f, 0); ps.Effectors.Add(new LinearAccelerationEffector { // Following parameters are equal to the default values. No need to set them. //AccelerationParameter = ParticleParameterNames.LinearAcceleration, //DirectionParameter = ParticleParameterNames.Direction, //SpeedParameter = ParticleParameterNames.LinearSpeed, }); // Each particle starts with a random rotation angle and a random angular speed. ps.Parameters.AddVarying<float>(ParticleParameterNames.Angle); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.Angle, Distribution = new UniformDistributionF(-ConstantsF.Pi, ConstantsF.Pi), }); ps.Parameters.AddVarying<float>(ParticleParameterNames.AngularSpeed); ps.Effectors.Add(new StartValueEffector<float> { Parameter = ParticleParameterNames.AngularSpeed, Distribution = new UniformDistributionF(-2, 2), }); // The AngularVelocityEffector uses the AngularSpeed to update the particle Angle. ps.Effectors.Add(new AngularVelocityEffector { // Following parameters are equal to the default values. No need to set them. //AngleParameter = ParticleParameterNames.Angle, //SpeedParameter = ParticleParameterNames.AngularSpeed, }); ps.Parameters.AddVarying<float>("StartSize"); ps.Effectors.Add(new StartValueEffector<float> { Parameter = "StartSize", Distribution = new UniformDistributionF(0.1f, 0.5f), }); ps.Parameters.AddVarying<float>("EndSize"); ps.Effectors.Add(new StartValueEffector<float> { Parameter = "EndSize", Distribution = new UniformDistributionF(2, 4), }); ps.Parameters.AddVarying<float>(ParticleParameterNames.Size); ps.Effectors.Add(new SingleLerpEffector { ValueParameter = ParticleParameterNames.Size, StartParameter = "StartSize", EndParameter = "EndSize", }); // Particle alpha fades in to a target value of 1 and then back out to 0. ps.Parameters.AddVarying<float>(ParticleParameterNames.Alpha); ps.Parameters.AddUniform<float>("TargetAlpha").DefaultValue = 1f; ps.Effectors.Add(new SingleFadeEffector { ValueParameter = ParticleParameterNames.Alpha, TargetValueParameter = "TargetAlpha", FadeInStart = 0f, FadeInEnd = 0.2f, FadeOutStart = 0.7f, FadeOutEnd = 1f, }); ps.Parameters.AddUniform<Texture2D>(ParticleParameterNames.Texture).DefaultValue = contentManager.Load<Texture2D>("Particles/Smoke"); ParticleSystemValidator.Validate(ps); return ps; }
public static ParticleSystem Create(ContentManager contentManager) { var ps = new ParticleSystem { Name = "Ribbon", MaxNumberOfParticles = 50, }; // Ribbons are enabled by setting the "Type" to ParticleType.Ribbon. Consecutive // living particles are connected and rendered as ribbons (quad strips). At least // two living particles are required to create a ribbon. Dead particles // ("NormalizedAge" ≥ 1) can be used as delimiters to terminate one ribbon and // start the next ribbon. ps.Parameters.AddUniform<ParticleType>(ParticleParameterNames.Type).DefaultValue = ParticleType.Ribbon; ps.Parameters.AddUniform<float>(ParticleParameterNames.Lifetime).DefaultValue = 1; ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartPositionEffector()); // The parameter "Axis" determines the orientation of the ribbon. // We could use a fixed orientation. It is also possible to "twist" the ribbon // by using a varying parameter. //ps.Parameters.AddUniform<Vector3F>(ParticleParameterNames.Axis).DefaultValue = // Vector3F.Up; ps.Effectors.Add(new RibbonEffector()); ps.Effectors.Add(new ReserveParticleEffector { Reserve = 1 }); ps.Parameters.AddUniform<float>(ParticleParameterNames.Size).DefaultValue = 1; ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Color); ps.Effectors.Add(new StartValueEffector<Vector3F> { Parameter = ParticleParameterNames.Color, Distribution = new BoxDistribution { MinValue = new Vector3F(0.5f, 0.5f, 0.5f), MaxValue = new Vector3F(1, 1, 1) } }); ps.Parameters.AddVarying<float>(ParticleParameterNames.Alpha); ps.Effectors.Add(new FuncEffector<float, float> { InputParameter = ParticleParameterNames.NormalizedAge, OutputParameter = ParticleParameterNames.Alpha, Func = age => 6.7f * age * (1 - age) * (1 - age), }); ps.Parameters.AddUniform<Texture2D>(ParticleParameterNames.Texture).DefaultValue = contentManager.Load<Texture2D>("Particles/Ribbon"); // The parameter "TextureTiling" defines how the texture spreads across the ribbon. // 0 ... no tiling, // 1 ... repeat every particle, // n ... repeat every n-th particle ps.Parameters.AddUniform<int>(ParticleParameterNames.TextureTiling).DefaultValue = 1; ps.Parameters.AddUniform<float>(ParticleParameterNames.BlendMode).DefaultValue = 0; ParticleSystemValidator.Validate(ps); return ps; }
//-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="ParticleSystemData" /> class. /// </summary> /// <param name="particleSystem">The particle system.</param> public ParticleSystemData(ParticleSystem particleSystem) { RequeryParameters(particleSystem); Particles = new ArrayList<Particle>(Math.Max(particleSystem.NumberOfActiveParticles, 8)); }