3
\$\begingroup\$

I am trying to implement graphic objects that can be either Sprite, Base, Point or some others, but not very many types at all. That's why I do not make separate classes. All types exist in 1 class due to unions and few short switches. I use directly objects, not pointers, and no virtual methods. The question is also, do you agree that it's a good idea to mark everything inline in this particular case.

In this exact code, unions don't look useful as the code is raw and classes unfinished.

Declarations:

#ifndef GFX_HPP_INCLUDED
#define GFX_HPP_INCLUDED

#include <vector>

namespace keng {

class gfx {

public: // types
    enum types {
        sprite,
        base,
        point
    };
    static const int base_default_size = 10;

private: // default base
    static gfx* const default_base;

public: // special construct methods
    static gfx* create_sprite(
        int position = 0,
        int map = 0,
        int x = 0,
        int y = 0,
        float scale = 1.0f,
        gfx* base = default_base
    );

    static gfx* create_base(
        int position = 0,
        int size = 0,
        float scale = 1.0f,
        gfx* base = default_base
    );

    static gfx* create_point(
        int position = 0,
        int x = 0,
        int y = 0,
        float scale = 1.0f,
        gfx* base = default_base
    );

public: // switching methods
    void render();

    ~gfx();

private: // untitled special constructors
    // sprite
    gfx(
        int map,
        int x,
        int y,
        float scale
    );

    // base
    gfx(
        int size,
        float scale
    );

    // point
    gfx(
        int x,
        int y,
        float scale
    );

private: // help
    typedef std::vector<std::vector<gfx>> gfx_container_t;

    template<typename... Args>
    static gfx* construct(gfx* base, int position, Args... args);

private: // type data
    int type;

private: // switching data
  union {
    int map; // sprite
    gfx_container_t* gfx_container; // base
  };
  union {
    int x; // sprite / point
  };
  union {
    int y; // sprite / point
  };
  union {
    float scale; // sprite / base / point
  };
  union {
    int map_x; // sprite
  };
  union {
    int map_y; // sprite
  };
  union {
    bool mirror_x; // sprite
  };
  union {
    bool mirror_y; // sprite
  };

}; // class gfx

} // namespace keng

#include "gfx.hh"

#endif // GFX_HPP_INCLUDED

Definitions:

#ifndef GFX_HH_INCLUDED
#define GFX_HH_INCLUDED

#include "spriteset.hpp"

using namespace keng;

// default gfx

gfx* const gfx::default_base = new gfx(base_default_size, 1.0f);

// special construct methods

inline gfx* gfx::create_sprite(
    int position,

    int map,
    int x,
    int y,
    float scale,

    gfx* base
)
{
    return construct(
        base,
        position,

        map,
        x,
        y,
        scale
    );
}

inline gfx* gfx::create_base(
    int position,

    int size,
    float scale,

    gfx* base
)
{
    return construct(
        base,
        position,

        size,
        scale
    );
}

inline gfx* gfx::create_point(
    int position,

    int x,
    int y,
    float scale,

    gfx* base
)
{
    return construct(
        base,
        position,

        x,
        y,
        scale
    );
}

// switching methods

inline void gfx::render() {
    switch (type) {
        case sprite:
        {
            int vertex_x1 = mirror_x * Spriteset::width[map];
            int vertex_x2 = (1 - mirror_x) * Spriteset::width[map];
            int vertex_y1 = mirror_y * Spriteset::height[map];
            int vertex_y2 = (1 - mirror_y) * Spriteset::height[map];
            GLfloat texture_x1 = Spriteset::normed_width[map] * map_x;
            GLfloat texture_x2 = texture_x1 + Spriteset::normed_width[map];
            GLfloat texture_y1 = Spriteset::normed_height[map] * map_y;
            GLfloat texture_y2 = texture_y1 + Spriteset::normed_height[map];
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, Spriteset::texture[map]);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glBegin(GL_POLYGON);
                glTexCoord2f(texture_x1, texture_y1);
                glVertex2f(vertex_x1, vertex_y1);
                glTexCoord2f(texture_x1, texture_y2);
                glVertex2f(vertex_x1, vertex_y2);
                glTexCoord2f(texture_x2, texture_y2);
                glVertex2f(vertex_x2, vertex_y2);
                glTexCoord2f(texture_x2, texture_y1);
                glVertex2f(vertex_x2, vertex_y1);
            glEnd();
            glDisable(GL_TEXTURE_2D);
            break;
        }

        case base:
            for (std::vector<gfx>& vg : *gfx_container)
                for (gfx& g : vg)
                    g.render();
            break;

        case point:
            glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
            glBegin(GL_POINTS);
                glVertex2f(.0f, .0f);
            glEnd();
            break;
    }
}

inline gfx::~gfx() {
    switch (type) {
        case sprite:
            break;

        case base:
            delete gfx_container;
            break;

        case point:
            break;
    }
}

// untitled special constructors

inline gfx::gfx(
    int map,
    int x,
    int y,
    float scale
)
:
    type(sprite),

    map(map),
    x(x),
    y(y),
    scale(scale)
{
}

inline gfx::gfx(
    int size,
    float scale
)
:
    type(base),

    gfx_container(new gfx_container_t(size)),
    scale(scale)
{
}

inline gfx::gfx(
    int x,
    int y,
    float scale
)
:
    type(point),

    x(x),
    y(y),
    scale(scale)
{
}

// help

template<typename... Args>
inline gfx* gfx::construct(gfx* base, int position, Args... args) {
    gfx* result = new gfx(args...);
    (*base->gfx_container)[position].push_back(*result);
    return result;
}

#endif // GFX_HH_INCLUDED
\$\endgroup\$
1
\$\begingroup\$

Being honest with you, this implementation is very confusing. I really can't believe you are so performance constrained that you can't pay for a virtual function call, and have resorted to this approach because of that. I mean, manual de-virtualization would be the last thing I'd think of as an optimization. The fact that you use fixed function OpenGL to draw the sprites is you number 1 performance cost. That's probably where 99% of the render() time is spent. If you're into optimizing the rendering, first thing is to change that immediate mode draw by buffered draws.

And still, it is expected than when optimizing code, some parts of it might become less readable, but there is a limit to how much you'd "disfigure" the code just to gain a tiny bit of runtime performance. So my only advice here would be: please do make that into a couple classes and use virtual methods if needed be. It will make this code so much more readable and easier to maintain. C++ introduced the concepts of inheritance and virtual dispatch so that we, the programmers, wouldn't have to do this kinds of godawful hacks we used to do back with C and Assembly.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.