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

Author Topic: Creating simple 3D objects SFML  (Read 12531 times)

0 Members and 1 Guest are viewing this topic.

FleshyOverlord

  • Newbie
  • *
  • Posts: 22
    • View Profile
Creating simple 3D objects SFML
« on: April 20, 2019, 06:39:41 am »
For anyone who wants to create simple, rough, and fast 3D models I found a method from Scratch (thanks to kevin11) that works perfectly in SFML. If you separate a 3D object into 2D layers you can stack these layers to create a 3D effects and you can even rotate the objects along the y-axis. This can and will, however, be a bottleneck in your program. If there are any ideas on how to optimize this process they would be very helpful.
« Last Edit: April 20, 2019, 06:41:30 am by FleshyOverlord »

fallahn

  • Sr. Member
  • ****
  • Posts: 492
  • Buns.
    • View Profile
    • Trederia
Re: Creating simple 3D objects SFML
« Reply #1 on: April 20, 2019, 09:55:41 am »
Hey this looks like a nice idea for a unique style! If by bottleneck you mean drawing so many extra sprites you could improve performance by drawing all the parts with a single vertex array or vertex buffer.

FleshyOverlord

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Creating simple 3D objects SFML
« Reply #2 on: April 20, 2019, 08:37:11 pm »
Thank you for the help!

fallahn

  • Sr. Member
  • ****
  • Posts: 492
  • Buns.
    • View Profile
    • Trederia
Re: Creating simple 3D objects SFML
« Reply #3 on: April 20, 2019, 10:49:00 pm »
I believe that, if you're using sf::Quads, a vertex array will draw them in the order in which they were added. I don't know if this is official OpenGL spec or just how it happens to work though (ie there might be some corner cases where it doesn't work). You'll also have to put all your images into a single texture.

FRex

  • Hero Member
  • *****
  • Posts: 1845
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Creating simple 3D objects SFML
« Reply #4 on: April 21, 2019, 12:10:41 am »
Not sure if what is presented here (e.g. at 1:50) is the same thing or not, it looks similar: https://www.youtube.com/watch?v=kjFlIxGzABY
Back to C++ gamedev with SFML in May 2023

FleshyOverlord

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Creating simple 3D objects SFML
« Reply #5 on: April 21, 2019, 02:01:07 am »
Thank you for the video link, it is an exact replica of the kind of 3D rendering I am doing.

FleshyOverlord

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Creating simple 3D objects SFML
« Reply #6 on: April 21, 2019, 03:13:41 am »
Is it possible to have multiple sf::Transforms  (rotations and scales) for one renderstate? Each transform would have to correspond with one quad in a vertex array and each vertex quad would have a unique center point based on its texture. I am asking this because I need to rotate, scale, (and possibly translate though that is not as important) each vertex quad in a VertexArray around its own center point instead of around one center point.


In the code below I was able to resize my robot (the red figure in the image below) but not rotate the 22 layers that make up the robot around their own center point. Instead, they were rotated around the head of the robot as you can see below.
        sf::Texture* tex = new sf::Texture;
        tex->loadFromFile("spritesheet.png");
        sf::RenderStates rendState;

//tile Width and Height
//all the tiles are the same size
        float tileWH = tex->getSize().y;
        rendState.texture = tex;
                for (int j = 0; j < 22; j++) {
                        int i = (22 - j);
                        float yVal = (350.0f + (i*0.8f));
                        float left = xVal;
                        float top = yVal;

                        sf::Vertex * quad = &verticies[j * 4];

                        quad[0].position = sf::Vector2f(left, top + tileWH);
                        quad[1].position = sf::Vector2f(left, top);
                        quad[2].position = sf::Vector2f(left + tileWH, top);
                        quad[3].position = sf::Vector2f(left + tileWH, top + tileWH);
                       
                        quad[0].texCoords = sf::Vector2f(tileWH*i, tileWH);
                        quad[1].texCoords = sf::Vector2f(tileWH*i, 0.0f);
                        quad[2].texCoords = sf::Vector2f(tileWH*i + tileWH, 0.0f);
                        quad[3].texCoords = sf::Vector2f(tileWH*i + tileWH, tileWH);
                        sf::Transform transform;
                        sf::Transform secondTransfrom;
                        sf::Vector2f center = sf::Vector2f((tileWH / (2.0f)) + left, (tileWH / (2.0f)) + top);
                       
//rotation is only around the head of the robot instead of each image layer
                        secondTransfrom.rotate(angle, center);
//scaling is working
                        transform.scale(sf::Vector2f(5.0f, 5.0f), center);
                        rendState.transform *= secondTransfrom;
                        rendState.transform *= transform;
                        */

                }
                window->draw(verticies, rendState);

 
:
« Last Edit: April 21, 2019, 05:38:46 am by FleshyOverlord »

fallahn

  • Sr. Member
  • ****
  • Posts: 492
  • Buns.
    • View Profile
    • Trederia
Re: Creating simple 3D objects SFML
« Reply #7 on: April 21, 2019, 10:09:09 am »
Technically no, although you can pre-transform vertices when you build your vertex array. In theory it could be done a bit like this (untested code):


struct BodyPart final : public sf::Transformable
{
    std::array<sf::Vector2f, 4u>  quadPoints = {}; //set these to the default positions of a quad used for a body part
};

enum PartID
{
    Legs, Body, Head, Count //start with legs because presumably you want to draw from the bottom up
};

class Robot final : public sf::Drawable, public sf::Transformable
{
public:
    Robot()
    {
        //for each body part set the initial quad point positions
        //eg {-10.f, -10.f} , {10.f, -10.f}, {10.f, 10.f}, {-10.f, 10.f} for a 20 unit quad that rotates around the centre
    }

    void update()
    {
        //update the transform for each body part
        m_bodyParts[PartID::Head].setRotation(20.f);
        m_bodyParts[PartID::Body].setScale(1.2f, 1.2f);
        //...etc

        //build the vertex array by creating a transformed quad for each part
        m_vertices.clear();
        for(const auto& part : m_bodyParts)
        {
            auto quadPoints = part.quadPoints; //make sure this is a copy so as not to modify the orignal part
            const auto& transform = part.getTransform();
            for(auto& point : quadPoints)
            {
                point = transform.transformPoint(point); //the point is now rotated/scaled by the bodypart transform
            }

            for(auto i = 0; i < SliceCount; ++i) //slice count is number of quads in this body part
            {
                //create a new slice by using the quadPoints array as positions
                //and adding any texture coordinates. Emplace the vertex into m_vertices
                //don't forget to decrement the Y value of the position by the thickness of a slice.
                for(auto& point : quadPoints)
                {
                    m_vertices.emplace_back(point, someTexCoord);
                    point.y -= sliceThickness;
                }
            }
    }
private:
    std::array<BodyPart, PartID::Count> m_bodyParts;
    std::vector<sf::Vertex> m_vertices;

    void draw(sf::RenderTarget& rt, sf::RenderStates) const override
    {
        states.transform *= getTransform(); //this now controls the world transform of the entire robot
        rt.draw(m_vertices.data(), m_vertices.size(), sf::Quads, states);
    }
};

 

To summarise:
Keep the positions for each part separate along with their own transform
When rebuilding the vertex array apply the transform of that part directly to *a copy* of the quad base position
Draw the vertex array using a transform which controls the position/rotation/scale of the robot in world coordinates.

FleshyOverlord

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Creating simple 3D objects SFML
« Reply #8 on: April 21, 2019, 05:29:29 pm »
Thank you so much for the in-depth response fallahn! I found all I needed to do was apply the sf::Transform to each point of the quad using sf::Transform::transformPoint which I was unsure of how to use until now. To draw 100 of the models only requires about 30% of my GPU (GTX1060 on a lenovo laptop) and requires minimal CPU usage(10% max) on a core i7(laptop);


sf::Transform transform;
sf::Vector2f center = sf::Vector2f((tileWH / (2.0f)) + left, (tileWH / (2.0f)) + top);

transform.scale(sf::Vector2f(5.0f, 5.0f), center);
transform.rotate(angle, center);

quad[0].position = transform.transformPoint(sf::Vector2f(left, top + tileWH));
quad[1].position = transform.transformPoint(sf::Vector2f(left, top));
quad[2].position = transform.transformPoint(sf::Vector2f(left + tileWH, top));
quad[3].position = transform.transformPoint(sf::Vector2f(left + tileWH, top + tileWH));
                       
 

A working image of the code.
« Last Edit: April 21, 2019, 05:38:00 pm by FleshyOverlord »

fallahn

  • Sr. Member
  • ****
  • Posts: 492
  • Buns.
    • View Profile
    • Trederia
Re: Creating simple 3D objects SFML
« Reply #9 on: April 21, 2019, 09:52:41 pm »
You're welcome, it's looking good! It sounds like an interesting technique, I'm sure I shall be experimenting with it myself sometime :D

FleshyOverlord

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: Creating simple 3D objects SFML
« Reply #10 on: April 23, 2019, 12:55:35 am »
I am just spitting ideas out now but to rotate objects along their pitch axis you could stretch all the quads that make up each vertex array and change the order of rendering for each layer when you exceed 180 degrees.

Hapax

  • Hero Member
  • *****
  • Posts: 3346
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Creating simple 3D objects SFML
« Reply #11 on: April 28, 2019, 09:19:02 pm »
I like this idea; it's always interesting to see another technique of giving the 'feel' of 3D without actual 3D stuff.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

 

anything