Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: VertexArray transformations  (Read 1666 times)

0 Members and 2 Guests are viewing this topic.

DevMike

  • Newbie
  • *
  • Posts: 3
    • View Profile
VertexArray transformations
« on: November 16, 2023, 02:08:40 am »
I am writing sprite batching based on VertexArray. Idea is, that every sprite instead of being drawn with Draw method is converted to VertexArray and merged with global VertexArray assigned to sprites atlas texture.
With this technique, I've got only one Draw call per sprites atlas texture.

I want my resulting VertexArray to support sprite Rotation and Scale. I know that there is Sprite.TransformPoint method, but it includes also Sprite position, so I cant use it.

Here is my approach to do this task manualy. Is my approach good or it can be done simplier?


        public static VertexArray CreateVertexArrayFromSprite(this Sprite sprite)
        {
            VertexArray vertices = new VertexArray(PrimitiveType.Quads);

            IntRect currentRect = sprite.TextureRect;
            Vector2f currentPos = sprite.Position;
            float rotation = sprite.Rotation;
            Vector2f scale = sprite.Scale;

            // Calculate the rotated and scaled vertices of the quad manually
            Vertex[] quad = new Vertex[4];
            quad[0] = new Vertex(new Vector2f(currentPos.X, currentPos.Y),
                                 new Vector2f(currentRect.Left, currentRect.Top));
            quad[1] = new Vertex(new Vector2f(currentPos.X + currentRect.Width, currentPos.Y),
                                 new Vector2f(currentRect.Left + currentRect.Width, currentRect.Top));
            quad[2] = new Vertex(new Vector2f(currentPos.X + currentRect.Width, currentPos.Y + currentRect.Height),
                                 new Vector2f(currentRect.Left + currentRect.Width, currentRect.Top + currentRect.Height));
            quad[3] = new Vertex(new Vector2f(currentPos.X, currentPos.Y + currentRect.Height),
                                 new Vector2f(currentRect.Left, currentRect.Top + currentRect.Height));

            // Apply rotation and scale manually
            Vector2f rotationCenter = new Vector2f(currentPos.X + currentRect.Width / 2, currentPos.Y + currentRect.Height / 2);
            for (int i = 0; i < 4; i++)
            {
                quad[i].Position = RotateAndScalePoint(quad[i].Position, rotationCenter, rotation);
                quad[i].Position = new Vector2f(quad[i].Position.X * scale.X, quad[i].Position.Y * scale.Y);
            }

            // Add the quad to the VertexArray
            vertices.Append(quad[0]);
            vertices.Append(quad[1]);
            vertices.Append(quad[2]);
            vertices.Append(quad[3]);

            return vertices;
        }

        // Helper method to rotate and scale a point around a center
        private static Vector2f RotateAndScalePoint(Vector2f point, Vector2f center, float rotation)
        {
            float cosTheta = (float)Math.Cos(Math.PI * rotation / 180.0);
            float sinTheta = (float)Math.Sin(Math.PI * rotation / 180.0);

            float translatedX = point.X - center.X;
            float translatedY = point.Y - center.Y;

            float rotatedX = translatedX * cosTheta - translatedY * sinTheta;
            float rotatedY = translatedX * sinTheta + translatedY * cosTheta;

            return new Vector2f(rotatedX + center.X, rotatedY + center.Y);
        }
 

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11033
    • View Profile
    • development blog
    • Email
Re: VertexArray transformations
« Reply #1 on: November 16, 2023, 12:57:51 pm »
I haven't checked the specific math, but you can also just a sf::Transform which you should be able to extract from the sprite as well and use that to transform the vertices.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

DevMike

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: VertexArray transformations
« Reply #2 on: November 16, 2023, 10:56:36 pm »
Ok, I forgot I have to move vertices of my object to point (0,0) before I apply transformation, and then I have to translate transformed vertices back to position. Now with this code my sprites behaves extacly the same like those I draw with Draw method.

        public static VertexArray CreateMappedVertices(Vector2f position, IntRect textureRect, Transform transform)
        {
            Vector2f[] transformedVertices = new Vector2f[4];

            transformedVertices[0] = transform.TransformPoint(new Vector2f(0, 0)) - position;
            transformedVertices[1] = transform.TransformPoint(new Vector2f(textureRect.Width, 0)) - position;
            transformedVertices[2] = transform.TransformPoint(new Vector2f(textureRect.Width, textureRect.Height)) - position;
            transformedVertices[3] = transform.TransformPoint(new Vector2f(0, textureRect.Height)) - position;

            VertexArray vertices = new(PrimitiveType.Quads);

            Vertex[] quad = new Vertex[4];

            quad[0] = new Vertex(new Vector2f(position.X, position.Y),
                                 new Vector2f(textureRect.Left, textureRect.Top));
            quad[1] = new Vertex(new Vector2f(position.X + transformedVertices[1].X, position.Y + transformedVertices[1].Y),
                                 new Vector2f(textureRect.Left + textureRect.Width, textureRect.Top));
            quad[2] = new Vertex(new Vector2f(position.X + transformedVertices[2].X, position.Y + transformedVertices[2].Y),
                                 new Vector2f(textureRect.Left + textureRect.Width, textureRect.Top + textureRect.Height));
            quad[3] = new Vertex(new Vector2f(position.X + transformedVertices[3].X, position.Y + transformedVertices[3].Y),
                                 new Vector2f(textureRect.Left, textureRect.Top + textureRect.Height));

            vertices.Append(quad[0]);
            vertices.Append(quad[1]);
            vertices.Append(quad[2]);
            vertices.Append(quad[3]);

            return vertices;
        }
 

DevMike

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: VertexArray transformations
« Reply #3 on: November 17, 2023, 12:08:12 am »
And here is refactored compact version if someone needs that kind of function.

        public static VertexArray CreateMappedVertices(Vector2f position, IntRect textureRect, Transform transform)
        {
            VertexArray vertices = new VertexArray(PrimitiveType.Quads);

            for (int i = 0; i < 4; ++i)
            {
                Vector2f point = transform.TransformPoint(new Vector2f(
                    (i == 1 || i == 2) ? textureRect.Width : 0,
                    (i == 2 || i == 3) ? textureRect.Height : 0
                )) - position;

                Vertex vertex = new Vertex(new Vector2f(position.X + point.X, position.Y + point.Y),
                                           new Vector2f(textureRect.Left + ((i == 1 || i == 2) ? textureRect.Width : 0),
                                                        textureRect.Top + ((i == 2 || i == 3) ? textureRect.Height : 0)));

                vertices.Append(vertex);
            }

            return vertices;
        }
 

Garwin

  • Jr. Member
  • **
  • Posts: 59
    • View Profile
Re: VertexArray transformations
« Reply #4 on: November 18, 2023, 08:35:56 am »
It seems to me that you have a lot of memory leaks using "new".

Just a question, how do you expect it to be faster than individual draw calls? You still need to do all transformations and in case you need to "draw" your way texture more times, it means copying a lot of data which then you need to transfer to a graphic card opposite using texture on the graphic card and drawing it several times.

Ruckamongus

  • Jr. Member
  • **
  • Posts: 70
    • View Profile
Re: VertexArray transformations
« Reply #5 on: November 19, 2023, 04:05:34 am »
It seems to me that you have a lot of memory leaks using "new".

Just a question, how do you expect it to be faster than individual draw calls? You still need to do all transformations and in case you need to "draw" your way texture more times, it means copying a lot of data which then you need to transfer to a graphic card opposite using texture on the graphic card and drawing it several times.

This is C# so there won't be a memory leak.

The draw call is usually what takes time, not the matrix math. So "batching" is a very common technique to minimize the time spent on minimizing the number of draw calls. All this function does it creates a vertex array from a rotated/scaled sprite; it still requires mapping to a packed sprite texture. You make few very (or one) very large vertex array and map each to a texture. That way you avoid texture swapping and can draw huge numbers of things with a single call.

 

anything