XNA - матричный эквивалент преобразований SpriteBatch.Draw? - PullRequest
4 голосов
/ 01 марта 2012

Скажите, что у меня есть этот интерфейс:

interface Drawable {
    Vector2 DrawPosition { get; }
    Texture2D Texture { get; }
    float Rotation { get; }
    Vector2 Origin { get; }
    Vector2 Scale { get; }
    bool FlipHorizontally { get; }
}

и в классе, который расширяет Microsoft.Xna.Framework.Game, я переопределяю Draw (GameTime), и этот код где-то там:

Drawable d = ...;
spriteBatch.Begin();
spriteBatch.Draw(d.Texture, d.DrawPosition, new Rectangle(0, 0, d.Texture.Width, d.Texture.Height), Color.White, d.Rotation, d.Origin, d.Scale, d.FlipHorizontally ? SpriteEffects.FlipHorizontally : SpriteEffects.None, 0);
spriteBatch.End();

При этом используется перегрузка SpriteBatch.Draw (текстура Texture2D, позиция Vector2, Nullable sourceRectangle, цвет Color, вращение с плавающей точкой, начало координат Vector2, масштаб Vector2, эффекты SpriteEffects, эффекты float layerDepth).

Say Iимел набор вершин, который составляет грубый контур изображения, возвращаемого d.Texture (то есть, если я открою изображение в Microsoft Paint и нарисую каждую точку из набора вершин, это будет очень близко соответствовать).Если бы я хотел построить эти точки так, чтобы они проходили по текстурам с помощью GraphicsDevice.DrawUserPrimitives (), был бы способ преобразовать вершины, используя только матрицы?Главное, чтобы он мог использовать только матрицы, и у меня нет других альтернатив для рисования, потому что мне действительно нужно использовать преобразованные вершины и для других вещей.Я уже пробовал что-то вроде

Matrix.CreateTranslation(new Vector3(-d.Origin, 0))
    * Matrix.CreateScale(new Vector3(d.Scale, 0))
    * Matrix.CreateRotationZ(d.Rotation)
    * Matrix.CreateTranslation(new Vector3(d.DrawPosition, 0)));

, но это довольно сложно.Есть ли решение этой проблемы?

Ответы [ 2 ]

2 голосов
/ 01 марта 2012

Ваш матричный код выглядит правильно для матрицы World (помещает модель в мировое пространство).Так что я думаю, это одна из этих вещей:

  • Ваши примитивы находятся не в том месте в модельном пространстве.Пакет Sprite создает один многоугольник, где (0,0) является верхним левым краем спрайта, а ({ширина текстуры}, {высота текстуры}) является правым нижним.Ваши примитивы должны быть одинакового размера и в одном и том же месте.

  • Ваша матрица Проекция неверна.См. этот ответ .Обратите внимание, что SpriteBatch использует перевернутую (клиент-космическую) систему координат.

  • Ваш неправильный режим отбраковки (не учитывает систему координат переворачивания).

  • Вы столкнулись с проблемой буфера глубины (вам нужно рисовать в дальнем и ближнем плане, и вас что-нибудь отбрасывает?)

Еслиу вас все еще есть проблемы, получите PIX из DirectX SDK и используйте его, чтобы определить, что именно рисует ваша игра.

0 голосов
/ 02 марта 2012

О боже мой, ребята, мне очень жаль. Я просто понял, что причина, по которой он не совпадает, заключается в том, что я ошибся в вершинах! Ну, я думаю, если вам, ребята, нужна помощь в том же, вот моя окончательная версия:

abstract class Drawable
{
    public abstract Vector2 DrawPosition { get; }
    public abstract Texture2D Texture { get; }
    public abstract float Rotation { get; }
    public abstract Vector2 Origin { get; }
    public abstract Vector2 Scale { get; }
    public abstract bool FlipHorizontally { get; }

    public abstract Vector2[] Vertices { get; }

    public Matrix TransformationMatrix
    {
        get
        {
            return Matrix.CreateTranslation(-new Vector3(Texture.Width * Scale.X / 2, 0, 0))
                * Matrix.CreateScale(new Vector3(FlipHorizontally ? -1 : 1, 1, 1))
                * Matrix.CreateTranslation(new Vector3(Texture.Width * Scale.X / 2, 0, 0))
                * Matrix.CreateTranslation(-new Vector3(Origin, 0))
                * Matrix.CreateScale(new Vector3(Scale, 0))
                * Matrix.CreateRotationZ(Rotation)
                * Matrix.CreateTranslation(new Vector3(DrawPosition, 0));
        }
    }
}

class Camera
{
    private readonly Viewport viewport;

    public Matrix GetViewMatrix()
    {
        return Matrix.CreateScale(1, -1, 1) * Matrix.CreateTranslation(0, viewport.Height, 0);
    }

    public Vector2 MouseToWorld(int x, int y)
    {
        return Vector2.Transform(new Vector2(x, y), Matrix.CreateScale(1, -1, 1) * Matrix.CreateTranslation(0, viewport.Height, 0));
    }
}

class Game1 : Microsoft.Xna.Framework.Game
{
    private Drawable avatar;
    private Camera camera;
    ...
    protected override void Initialize() {
        avatar = ...;
        camera = new Camera(graphics.GraphicsDevice.Viewport);
        basicEffect = new BasicEffect(graphics.GraphicsDevice);
        basicEffect.VertexColorEnabled = true;
        basicEffect.Projection = Matrix.CreateOrthographicOffCenter(0, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height, 0, 0, 1);

        base.Initialize();
    }
    ...
    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, camera.GetViewMatrix());
        spriteBatch.Draw(avatar.Texture, avatar.DrawPosition, new Rectangle(0, 0, avatar.Texture.Width, avatar.Texture.Height), Color.White, avatar.Rotation, avatar.Origin, avatar.Scale, avatar.FlipHorizontally ? SpriteEffects.FlipHorizontally : SpriteEffects.None, 0);
        spriteBatch.End();

        basicEffect.CurrentTechnique.Passes[0].Apply();
        VertexPositionColor[] vertices = new VertexPositionColor[avatar.Vertices.Length + 1];
        Matrix m = MakeAffineTransform(avatar);
        for (int i = 0; i < avatar.Vertices.Length; i++)
        {
            vertices[i] = new VertexPositionColor(Vector3.Transform(new Vector3(Vector2.Transform(avatar.Vertices[i], m), 0), camera.GetViewMatrix()), Color.Black);
            Console.WriteLine(vertices[i]);
        }
        vertices[vertices.Length - 1] = vertices[0];
        graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, vertices, 0, vertices.Length - 1);

        base.Draw(gameTime);
    }
    ...
}

это прекрасно работает! Это на самом деле переворачивает начало координат так, чтобы оно находилось в нижнем левом углу, а также переворачивает ось y, так что увеличивающиеся значения идут вверх, а уменьшающиеся значения идут вниз. Камера может быть хорошей базой и может быть легко обновлена ​​(скажем, если вы хотите, чтобы она следовала чему-то на экране), чтобы вы могли передать ей мировые координаты (с началом координат в нижнем левом углу), и это вернем координаты экрана.

...