Page 1 of 1

BoundingSphereForceFieldShape

Posted: Thu May 02, 2013 2:17 pm
by trick
hi,
maybe for completeness, you could add a bounding sphere force field shape like the one i build on my own from your bounding box shape template:

Code: Select all

    /// <summary>
    /// Defines the area in which a force field works using an entity's shape.
    /// </summary>
    public class BoundingSphereForceFieldShape : ForceFieldShape
    {
        private readonly List<Entity> affectedEntities = new List<Entity>();
        private readonly RawList<BroadPhaseEntry> affectedEntries = new RawList<BroadPhaseEntry>();

        /// <summary>
        /// Constructs a new force field shape using a bounding box.
        /// </summary>
        /// <param name="box">Bounding box to use.</param>
        public BoundingSphereForceFieldShape(BoundingSphere sphere)
        {
            BoundingSphere = sphere;
        }

        /// <summary>
        /// Gets or sets the bounding box used by the shape.
        /// </summary>
        public BoundingSphere BoundingSphere { get; set; }

        /// <summary>
        /// Determines the possibly involved entities.
        /// </summary>
        /// <returns>Possibly involved entities.</returns>
        public override IList<Entity> GetPossiblyAffectedEntities()
        {
            affectedEntities.Clear();
            ForceField.QueryAccelerator.GetEntries(BoundingSphere, affectedEntries);

            for (int i = 0; i < affectedEntries.Count; i++) // changed 'count' to 'Count', guess it's internal and not available in my assembly
            {
                var EntityCollidable = affectedEntries[i] as EntityCollidable;
                if (EntityCollidable != null)
                    affectedEntities.Add(EntityCollidable.Entity);
            }
            affectedEntries.Clear();
            return affectedEntities;
        }

        /// <summary>
        /// Determines if the entity is affected by the force field.
        /// </summary>
        /// <param name="testEntity">Entity to test.</param>
        /// <returns>Whether the entity is affected.</returns>
        public override bool IsEntityAffected(Entity testEntity)
        {
            return true;
        }
    }
i used it for something like this:

Code: Select all

    public class ExplosionForceField : ForceField
    {
        public ExplosionForceField(Vector3 position, float speed, float range, float maxForce, float angularRatio, float dt)
            : base(new BoundingSphereForceFieldShape(new BoundingSphere(position, 0.0f)))
        {
            this.position = position;
            this.speed = speed;
            this.range = range;
            this.angularRatio = angularRatio;
            this.oneOverWaveFrontRange = 1.0f / (range / 20.0f);
            this.waveFrontExpansion = 0.5f * range;
            this.maxImpulse = maxForce * dt * speed / range;

            ForceWakeUp = true;
        }

        /// <summary>
        /// Calculates the impulse to apply to the center of mass of physically simulated bodies within the volume.
        /// </summary>
        /// <param name="e">Target of the impulse.</param>
        /// <param name="dt">Time since the last frame in simulation seconds.</param>
        /// <param name="impulse">Force to apply at the given position.</param>
        protected override void CalculateImpulse(Entity e, float dt, out Vector3 impulse)
        {
            Vector3 delta = e.Position - position;
            float entityPosition = delta.Length();

            if (entityPosition == 0.0f) // entity exactly at position of explosion? we better do nothing, since we can't calculate an impulse direction
            {
                impulse = Vector3.Zero;
                return;
            }

            if (System.Math.Abs(entityPosition - x) < waveFrontExpansion)
            {
                // wavefront propagates omnidirectional with a maximum at its current position (x) and an exponential decay to both directions  
                Vector3 direction = delta * (1.0f / entityPosition); // normalize
                float scalarImpulse = (currentMaxImpulse * (float)System.Math.Exp(-System.Math.Abs(entityPosition - x) * oneOverWaveFrontRange));
                impulse = direction * scalarImpulse;

                if (angularRatio > 0.0f)
                {
                    Vector3 angularMomentum;
                    Vector3.Cross(ref up, ref impulse, out angularMomentum); // make rotation perpendicular to direction
                    e.AngularMomentum += angularMomentum * angularRatio;
                }

                impulse.Validate();
            }
            else
                impulse = Vector3.Zero;
        }

        protected override void PreUpdate()
        {
            base.PreUpdate();

            Space space = ((Space)Space);

            t += space.TimeStepSettings.TimeStepDuration;
            x = t * speed;

            currentMaxImpulse = maxImpulse * t;

            if (x > range)
                space.SpaceObjectBuffer.Remove(this);
            else
                ((BoundingSphereForceFieldShape)Shape).BoundingSphere = new BoundingSphere(position, x + waveFrontExpansion);
        }

        private float t = 0.0f;
        private Vector3 position;
        private float speed;
        private float range;
        private float oneOverWaveFrontRange;
        private float waveFrontExpansion;
        private float x;
        private float maxImpulse;
        private float currentMaxImpulse;
        private float angularRatio;
        private Vector3 up = Vector3.UnitY;
    }
also i got a question:
IsEntityAffected always returns true. shouldn't it do this only if the given entity is in the affectedEntities IList or how is it meant to be used? may i store a bool in the entities tag and therefor just return false in this method if the tag was set to false instead of giving back a Vector3.Zero in some cases in the CalculateImpulse of the force field? on the other hand, the field shape shouldn't make any special assumptions about the entities and even the force field its serving for...

Re: BoundingSphereForceFieldShape

Posted: Thu May 02, 2013 7:07 pm
by Norbo
maybe for completeness, you could add a bounding sphere force field shape
Added. There is a good chance, however, that the whole 'force field system' will end up getting purged from the main library and put into the Demos or something. It's not doing anything particularly special, it isn't very general, and it's not quite a perfect fit for most possible uses.
IsEntityAffected always returns true. shouldn't it do this only if the given entity is in the affectedEntities IList or how is it meant to be used?
The IsEntityAffected method is just a second pruning pass used within a multithreaded context; it executes only on entities contained in the affectedEntities list. (For some shapes, a more appropriate name for that list would be "possiblyAffectedEntities", matching the function name.) In some cases, it's possible to gain some performance by moving work out of the single threaded "GetPossiblyAffectedEntities" query and into the multithreaded "IsEntityAffected" function. For extremely simple shapes like boxes and spheres, there is no such check needed, so it just returns true.

Using this for a temporary explosion with limited impact is slightly unnatural, though. The multithreading used by the force field is probably counterproductive when there's only a dozen objects to push. In this kind of situation, it might be better to use the core logic directly, rather than working through the force fields. Check the Explosion class in the BEPUphysicsDemos for an example.
may i store a bool in the entities tag and therefor just return false in this method if the tag was set to false instead of giving back a Vector3.Zero in some cases in the CalculateImpulse of the force field?
You could indeed if you wanted to.