public void OnDraw(Draw message)
        {
            if (!Enabled)
            {
                return;
            }

            var content = ((ContentSystem)Manager.GetSystem(ContentSystem.TypeId)).Content;

            // Update all our backgrounds.
            for (var i = _backgrounds.Count - 1; i >= 0; i--)
            {
                // Current background being handled.
                var background = _backgrounds[i];

                // Load textures for backgrounds.
                for (var j = 0; j < background.TextureNames.Length; j++)
                {
                    // Skip textures we already loaded.
                    if (background.Textures[j] != null)
                    {
                        continue;
                    }

                    // Otherwise load the texture.
                    background.Textures[j] = content.Load <Texture2D>(background.TextureNames[j]);
                }

                // Stop if we're already at full alpha.
                if (background.Alpha >= 1.0f)
                {
                    break;
                }

                // Update alpha for transitioning.
                background.Alpha += message.ElapsedMilliseconds / background.TransitionMilliseconds;

                // Stop if one background reaches 100%.
                if (background.Alpha >= 1.0f)
                {
                    // Set it to 100% to avoid it being over.
                    background.Alpha = 1.0f;

                    // Remove all lower ones.
                    for (var j = i - 1; j >= 0; j--)
                    {
                        _backgrounds.RemoveAt(j);
                    }

                    // And stop.
                    break;
                }
            }

            // Get the transformation to use.
            var transform   = GetTransform();
            var translation = GetTranslation();

            // Decompose the matrix, because we need the scale for our rectangles.
            // Otherwise we cannot tile the texture properly.
            Vector3    scale;
            Quaternion rotation;
            Vector3    localTranslation;

            transform.Decompose(out scale, out rotation, out localTranslation);
            scale *= 2;

            // Get the bounds to render to. We oversize this a little to allow using
            // that margin for offsetting, thus making it possible to translate the
            // background by fractions. This makes background movement more fluent,
            // as it will not snap to full pixels, but interpolate properly.
            var destinationRectangle = _spriteBatch.GraphicsDevice.Viewport.Bounds;

            destinationRectangle.Inflate(1, 1);

            // Get the "default" source rectangle. We scale it as necessary, and also
            // offset it so that the scaling will be relative to the center.
            var centeredSourceRect = new Rectangle(
                0,
                0,
                (int)(destinationRectangle.Width / scale.X),
                (int)(destinationRectangle.Height / scale.Y));

            centeredSourceRect.X = -centeredSourceRect.Width / 2;
            centeredSourceRect.Y = -centeredSourceRect.Height / 2;

            // Draw all backgrounds, bottom up (oldest first).
            _spriteBatch.Begin(
                SpriteSortMode.Deferred,
                BlendState.AlphaBlend,
                SamplerState.AnisotropicWrap,
                DepthStencilState.None,
                RasterizerState.CullNone);
            foreach (var background in _backgrounds)
            {
                // Draw each texture for the background.
                for (var j = 0; j < background.Textures.Length; j++)
                {
                    // Scale the translation with the texture's level.
                    var offset = WorldUnitConversion.ToScreenUnits(translation * background.Levels[j]);

                    // Modulo it with the texture sizes for repetition, but keeping the
                    // values in a range where float precision is good.
                    offset.X %= background.Textures[j].Width;
                    offset.Y %= background.Textures[j].Height;

                    // Compute our actual source rectangle and our fractional offset
                    // which we use for smooth movement.
                    var sourceRect = centeredSourceRect;
                    var intOffset  = new Point((int)offset.X, (int)offset.Y);
                    sourceRect.Offset(-intOffset.X, -intOffset.Y);
                    var floatOffset = new Vector2(-(float)(offset.X - intOffset.X), -(float)(offset.Y - intOffset.Y));

                    // Render the texture.
                    _spriteBatch.Draw(
                        background.Textures[j],
                        destinationRectangle,
                        sourceRect,
                        background.Colors[j] * background.Alpha,
                        0,
                        floatOffset,
                        SpriteEffects.None,
                        1f);
                }
            }
            _spriteBatch.End();
        }
Exemple #2
0
        public void OnDraw(Draw message)
        {
            if (!Enabled)
            {
                return;
            }

            // Bail if we don't have a soundbank.
            if (_soundBank == null)
            {
                Enabled = false;
                return;
            }

            // No need to check *all* the time... this saves quite some performance
            // if a lot of sound emitting objects are in range.
            if (message.Frame % 3 != 0)
            {
                return;
            }

            var index = (IndexSystem)Manager.GetSystem(IndexSystem.TypeId);

            Debug.Assert(index != null);

            // Update listener information.
            var listenerPosition = GetListenerPosition();
            var listenerVelocity = GetListenerVelocity() * 4;

            _listener.Velocity = ToV3(ref listenerVelocity);

            // Iterate all sounds in range of the listener. All sounds remaining
            // in the current list of sounds playing will be stopped, as they are
            // out of range. The ones in range will be removed from that list and
            // added to our reusable list.
            index[IndexId].Find(listenerPosition, _maxAudibleDistance, _reusableNeighborList);
            foreach (IIndexable neighbor in _reusableNeighborList.Select(Manager.GetComponentById))
            {
                // Get the sound component of the neighbor.
                var sound = (Sound)Manager.GetComponent(neighbor.Entity, Sound.TypeId);

                // Skip this neighbor if its sound is not enabled.
                if (!sound.Enabled)
                {
                    continue;
                }

                // Get sound position and velocity.
                var emitterPosition = ((ITransform)Manager.GetComponent(neighbor.Entity, TransformTypeId)).Position;

                // The velocity is optional, so we must check if it exists.
                var neighborVelocity = (IVelocity)Manager.GetComponent(neighbor.Entity, VelocityTypeId);
                var emitterVelocity  = neighborVelocity != null ? neighborVelocity.LinearVelocity * 4 : Vector2.Zero;

                // Check whether to update or start playing.
                if (_playingSounds.ContainsKey(neighbor.Entity))
                {
                    // We already know this one so just apply 3d effect.
                    var cue = _playingSounds[neighbor.Entity];

                    // Make sure cue is not stopped (how ever that may have happened...)
                    if (!cue.IsStopped)
                    {
                        // Do not stop it.
                        _playingSounds.Remove(neighbor.Entity);

                        // We make the emitter position relative to the listener, which is
                        // equivalent to having the listener at the actual origin at all
                        // times, so we don't have to to update its position.
                        var relativeEmitterPosition = (Vector2)WorldUnitConversion.ToScreenUnits(emitterPosition - listenerPosition);

                        // Get position and velocity of emitter.
                        _emitter.Position = ToV3(ref relativeEmitterPosition);
                        _emitter.Velocity = ToV3(ref emitterVelocity);

                        // Apply new surround effect.
                        cue.Apply3D(_listener, _emitter);

                        // Add it to the new list of playing sounds.
                        _reusablePlayingSounds.Add(neighbor.Entity, cue);
                    }
                    else
                    {
                        // Dispose it. It will be restarted in the next update,
                        // if still in range.
                        cue.Dispose();
                        // Don't dispose it again.
                        _playingSounds.Remove(neighbor.Entity);
                    }
                }
                else
                {
                    // Sound is not yet playing, start it.
                    var cue = Play(sound.SoundName, ref emitterPosition, ref emitterVelocity);
                    if (cue != null)
                    {
                        _reusablePlayingSounds.Add(neighbor.Entity, cue);
                    }
                }
            }

            // Clear for next update.
            _reusableNeighborList.Clear();

            // Stop all sound that's not in range.
            foreach (var cue in _playingSounds)
            {
                cue.Value.Stop(AudioStopOptions.Immediate);
                cue.Value.Dispose();
            }
            _playingSounds.Clear();

            // Swap the two sound dictionaries.
            var tmp = _reusablePlayingSounds;

            _reusablePlayingSounds = _playingSounds;
            _playingSounds         = tmp;
        }