Mostrando entradas con la etiqueta Primeros Pasos. Mostrar todas las entradas
Mostrando entradas con la etiqueta Primeros Pasos. Mostrar todas las entradas

lunes, 4 de noviembre de 2013

Proyecto Visual Studio configurado para OGL 4.3

No tengo mucho tiempo últimamente de aprender y mucho menos de escribir post, por lo que la frecuencia de publicación la he reducido y se mantendrá así durante una temporada.

Lo que si he hecho ha sido configurar un proyecto para Visual Studio 2012 que funciona tanto en la versión Express como en la Ultimate. Tiene metidas las librerias GLEW, GL3W, vmath, loadShaders y SDL para tenerse que preocupar únicamente de la programación.

Ademas tiene metidos los ejemplos que están colgados en el blog hasta el ejemplo de geometry instancing. He modificado un par de cosas (#includes, localización relativa de los shaders, algún cast...) pero nada significativo. Iré actualizando el archivo según vaya actualizando el blog para añadir los nuevos ejemplos.

Espero que os sea de utilidad. Un saludo

https://db.tt/MTBftZDo
Descargar Proyecto

domingo, 21 de julio de 2013

Ampliando librerías para OpenGL: GLEW, GL3W, vmath, ShaderLoader

Para poder seguir avanzando vamos a tener que utilizar unas librerías que nos van a hacer la vida más fácil.
  • GLEW es una librería que en tiempo de ejecución determina que extensiones de OpenGL son soportadas por el cliente.
  • GL3W librería que carga solo la funcionalidad básica de OpenGL 3/4.
    Esta librería se puede obtener directamente desde GitHub y hace falta Python para compilar el script que te descargas. Sólo hace falta para generar el script, después lo podemos desinstalar: 
    https://github.com/skaslev/gl3w
  • vmath y el cargador de shaders son dos herramientas que he obtenido con el set de ejemplos del libro: "OpenGL Programming Guide. Eight Edition"
    Se trata de dos librerías que, facilitan las operaciones con vectores y matrices (
    vmath) y la carga de shaders (LoadShaders). El código de las mismas es el siguiente:
    vmath.h
    #ifndef __VMATH_H__
    #define __VMATH_H__
    
    
    #define _USE_MATH_DEFINES  1 // Include constants defined in math.h
    #include <math.h>
    
    namespace vmath
    {
    
    template <typename T> 
    inline T radians(T angleInRadians)
    {
     return angleInRadians * static_cast<T>(180.0/M_PI);
    }
    
    template <const bool cond>
    class ensure
    {
    public:
        inline ensure() { switch (false) { case false: case cond: break; } }
    };
    
    template <typename T, const int len> class vecN;
    
    template <typename T, const int len>
    class vecN
    {
    public:
        typedef class vecN<T,len> my_type;
    
        // Default constructor does nothing, just like built-in types
        inline vecN()
        {
            // Uninitialized variable
        }
    
        // Copy constructor
        inline vecN(const vecN& that)
        {
            assign(that);
        }
    
        // Construction from scalar
        inline vecN(T s)
        {
            int n;
            for (n = 0; n < len; n++)
            {
                data[n] = s;
            }
        }
    
        // Assignment operator
        inline vecN& operator=(const vecN& that)
        {
            assign(that);
            return *this;
        }
    
        inline vecN operator+(const vecN& that) const
        {
            my_type result;
            int n;
            for (n = 0; n < len; n++)
                result.data[n] = data[n] + that.data[n];
            return result;
        }
    
        inline vecN& operator+=(const vecN& that)
        {
            return (*this = *this + that);
        }
    
        inline vecN operator-() const
        {
            my_type result;
            int n;
            for (n = 0; n < len; n++)
                result.data[n] = -data[n];
            return result;
        }
    
        inline vecN operator-(const vecN& that) const
        {
            my_type result;
            int n;
            for (n = 0; n < len; n++)
                result.data[n] = data[n] - that.data[n];
            return result;
        }
    
        inline vecN& operator-=(const vecN& that)
        {
            return (*this = *this - that);
        }
    
        inline vecN operator*(const vecN& that) const
        {
            my_type result;
            int n;
            for (n = 0; n < len; n++)
                result.data[n] = data[n] * that.data[n];
            return result;
        }
    
        inline vecN& operator*=(const vecN& that)
        {
            return (*this = *this * that);
        }
    
        inline vecN operator*(const T& that) const
        {
            my_type result;
            int n;
            for (n = 0; n < len; n++)
                result.data[n] = data[n] * that;
            return result;
        }
    
        inline vecN& operator*=(const T& that)
        {
            assign(*this * that);
    
            return *this;
        }
    
        inline vecN operator/(const vecN& that) const
        {
            my_type result;
            int n;
            for (n = 0; n < len; n++)
                result.data[n] = data[n] * that.data[n];
            return result;
        }
    
        inline vecN& operator/=(const vecN& that)
        {
            assign(*this * that);
    
            return *this;
        }
    
        inline vecN operator/(const T& that) const
        {
            my_type result;
            int n;
            for (n = 0; n < len; n++)
                result.data[n] = data[n] / that;
            return result;
        }
    
        inline vecN& operator/(const T& that)
        {
            assign(*this / that);
        }
    
        inline T& operator[](int n) { return data[n]; }
        inline const T& operator[](int n) const { return data[n]; }
    
        inline static int size(void) { return len; }
    
        inline operator const T* () const { return &data[0]; }
    
    protected:
        T data[len];
    
        inline void assign(const vecN& that)
        {
            int n;
            for (n = 0; n < len; n++)
                data[n] = that.data[n];
        }
    };
    
    template <typename T>
    class Tvec2 : public vecN<T,2>
    {
    public:
        typedef vecN<T,2> base;
    
        // Uninitialized variable
        inline Tvec2() {}
        // Copy constructor
        inline Tvec2(const base& v) : base(v) {}
    
        // vec2(x, y);
        inline Tvec2(T x, T y)
        {
            base::data[0] = x;
            base::data[1] = y;
        }
    };
    
    template <typename T>
    class Tvec3 : public vecN<T,3>
    {
    public:
        typedef vecN<T,3> base;
    
        // Uninitialized variable
        inline Tvec3() {}
    
        // Copy constructor
        inline Tvec3(const base& v) : base(v) {}
    
        // vec3(x, y, z);
        inline Tvec3(T x, T y, T z)
        {
            base::data[0] = x;
            base::data[1] = y;
            base::data[2] = z;
        }
    
        // vec3(v, z);
        inline Tvec3(const Tvec2<T>& v, T z)
        {
            base::data[0] = v[0];
            base::data[1] = v[1];
            base::data[2] = z;
        }
    
        // vec3(x, v)
        inline Tvec3(T x, const Tvec2<T>& v)
        {
            base::data[0] = x;
            base::data[1] = v[0];
            base::data[2] = v[1];
        }
    };
    
    template <typename T>
    class Tvec4 : public vecN<T,4>
    {
    public:
        typedef vecN<T,4> base;
    
        // Uninitialized variable
        inline Tvec4() {}
    
        // Copy constructor
        inline Tvec4(const base& v) : base(v) {}
    
        // vec4(x, y, z, w);
        inline Tvec4(T x, T y, T z, T w)
        {
            base::data[0] = x;
            base::data[1] = y;
            base::data[2] = z;
            base::data[3] = w;
        }
    
        // vec4(v, z, w);
        inline Tvec4(const Tvec2<T>& v, T z, T w)
        {
            base::data[0] = v[0];
            base::data[1] = v[1];
            base::data[2] = z;
            base::data[3] = w;
        }
    
        // vec4(x, v, w);
        inline Tvec4(T x, const Tvec2<T>& v, T w)
        {
            base::data[0] = x;
            base::data[1] = v[0];
            base::data[2] = v[1];
            base::data[3] = w;
        }
    
        // vec4(x, y, v);
        inline Tvec4(T x, T y, const Tvec2<T>& v)
        {
            base::data[0] = x;
            base::data[1] = y;
            base::data[2] = v[0];
            base::data[3] = v[1];
        }
    
        // vec4(v1, v2);
        inline Tvec4(const Tvec2<T>& u, const Tvec2<T>& v)
        {
            base::data[0] = u[0];
            base::data[1] = u[1];
            base::data[2] = v[0];
            base::data[3] = v[1];
        }
    
        // vec4(v, w);
        inline Tvec4(const Tvec3<T>& v, T w)
        {
            base::data[0] = v[0];
            base::data[1] = v[1];
            base::data[2] = v[2];
            base::data[3] = w;
        }
    
        // vec4(x, v);
        inline Tvec4(T x, const Tvec3<T>& v)
        {
            base::data[0] = x;
            base::data[1] = v[0];
            base::data[2] = v[1];
            base::data[3] = v[2];
        }
    };
    
    typedef Tvec2<float> vec2;
    typedef Tvec2<int> ivec2;
    typedef Tvec2<unsigned int> uvec2;
    typedef Tvec2<double> dvec2;
    
    typedef Tvec3<float> vec3;
    typedef Tvec3<int> ivec3;
    typedef Tvec3<unsigned int> uvec3;
    typedef Tvec3<double> dvec3;
    
    typedef Tvec4<float> vec4;
    typedef Tvec4<int> ivec4;
    typedef Tvec4<unsigned int> uvec4;
    typedef Tvec4<double> dvec4;
    
    template <typename T, int n>
    static inline const vecN<T,n> operator * (T x, const vecN<T,n>& v)
    {
        return v * x;
    }
    
    template <typename T>
    static inline const Tvec2<T> operator / (T x, const Tvec2<T>& v)
    {
        return Tvec2<T>(x / v[0], x / v[1]);
    }
    
    template <typename T>
    static inline const Tvec3<T> operator / (T x, const Tvec3<T>& v)
    {
        return Tvec3<T>(x / v[0], x / v[1], x / v[2]);
    }
    
    template <typename T>
    static inline const Tvec4<T> operator / (T x, const Tvec4<T>& v)
    {
        return Tvec4<T>(x / v[0], x / v[1], x / v[2], x / v[3]);
    }
    
    template <typename T, int len>
    static inline T dot(const vecN<T,len>& a, const vecN<T,len>& b)
    {
        int n;
        T total = T(0);
        for (n = 0; n < len; n++)
        {
            total += a[n] * b[n];
        }
        return total;
    }
    
    template <typename T>
    static inline vecN<T,3> cross(const vecN<T,3>& a, const vecN<T,3>& b)
    {
        return Tvec3<T>(a[1] * b[2] - b[1] * a[2],
                        a[2] * b[0] - b[2] * a[0],
                        a[0] * b[1] - b[0] * a[1]);
    }
    
    template <typename T, int len>
    static inline T length(const vecN<T,len>& v)
    {
        T result(0);
    
        for (int i = 0; i < v.size(); ++i)
        {
            result += v[i] * v[i];
        }
    
        return (T)sqrt(result);
    }
    
    template <typename T, int len>
    static inline vecN<T,len> normalize(const vecN<T,len>& v)
    {
        return v / length(v);
    }
    
    template <typename T, int len>
    static inline T distance(const vecN<T,len>& a, const vecN<T,len>& b)
    {
        return length(b - a);
    }
    
    template <typename T, const int w, const int h>
    class matNM
    {
    public:
        typedef class matNM<T,w,h> my_type;
        typedef class vecN<T,h> vector_type;
    
        // Default constructor does nothing, just like built-in types
        inline matNM()
        {
            // Uninitialized variable
        }
    
        // Copy constructor
        inline matNM(const matNM& that)
        {
            assign(that);
        }
    
        // Construction from element type
        // explicit to prevent assignment from T
        explicit inline matNM(T f)
        {
            for (int n = 0; n < w; n++)
            {
                data[n] = f;
            }
        }
    
        // Construction from vector
        inline matNM(const vector_type& v)
        {
            for (int n = 0; n < w; n++)
            {
                data[n] = v;
            }
        }
    
        // Assignment operator
        inline matNM& operator=(const my_type& that)
        {
            assign(that);
            return *this;
        }
    
        inline matNM operator+(const my_type& that) const
        {
            my_type result;
            int n;
            for (n = 0; n < w; n++)
                result.data[n] = data[n] + that.data[n];
            return result;
        }
    
        inline my_type& operator+=(const my_type& that)
        {
            return (*this = *this + that);
        }
    
        inline my_type operator-(const my_type& that) const
        {
            my_type result;
            int n;
            for (n = 0; n < w; n++)
                result.data[n] = data[n] - that.data[n];
            return result;
        }
    
        inline my_type& operator-=(const my_type& that)
        {
            return (*this = *this - that);
        }
    
        // Matrix multiply.
        // TODO: This only works for square matrices. Need more template skill to make a non-square version.
        inline my_type operator*(const my_type& that) const
        {
            ensure<w == h>();
    
            my_type result(0);
    
            for (int j = 0; j < w; j++)
            {
                for (int i = 0; i < h; i++)
                {
                    T sum(0);
    
                    for (int n = 0; n < w; n++)
                    {
                        sum += data[n][i] * that[j][n];
                    }
    
                    result[j][i] = sum;
                }
            }
    
            return result;
        }
    
        inline my_type& operator*=(const my_type& that)
        {
            return (*this = *this * that);
        }
    
        inline vector_type& operator[](int n) { return data[n]; }
        inline const vector_type& operator[](int n) const { return data[n]; }
        inline operator T*() { return &data[0][0]; }
        inline operator const T*() const { return &data[0][0]; }
    
        inline matNM<T,h,w> transpose(void) const
        {
            matNM<T,h,w> result;
            int x, y;
    
            for (y = 0; y < w; y++)
            {
                for (x = 0; x < h; x++)
                {
                    result[x][y] = data[y][x];
                }
            }
    
            return result;
        }
    
        static inline my_type identity()
        {
            ensure<w == h>();
    
            my_type result(0);
    
            for (int i = 0; i < w; i++)
            {
                result[i][i] = 1;
            }
    
            return result;
        }
    
        static inline int width(void) { return w; }
        static inline int height(void) { return h; }
    
    protected:
        // Column primary data (essentially, array of vectors)
        vecN<T,h> data[w];
    
        // Assignment function - called from assignment operator and copy constructor.
        inline void assign(const matNM& that)
        {
            int n;
            for (n = 0; n < w; n++)
                data[n] = that.data[n];
        }
    };
    
    /*
    template <typename T, const int N>
    class TmatN : public matNM<T,N,N>
    {
    public:
        typedef matNM<T,N,N> base;
        typedef TmatN<T,N> my_type;
    
        inline TmatN() {}
        inline TmatN(const my_type& that) : base(that) {}
        inline TmatN(float f) : base(f) {}
        inline TmatN(const vecN<T,4>& v) : base(v) {}
    
        inline my_type transpose(void)
        {
            my_type result;
            int x, y;
    
            for (y = 0; y < h; y++)
            {
                for (x = 0; x < h; x++)
                {
                    result[x][y] = data[y][x];
                }
            }
    
            return result;
        }
    };
    */
    
    template <typename T>
    class Tmat4 : public matNM<T,4,4>
    {
    public:
        typedef matNM<T,4,4> base;
        typedef Tmat4<T> my_type;
    
        inline Tmat4() {}
        inline Tmat4(const my_type& that) : base(that) {}
        inline Tmat4(const base& that) : base(that) {}
        inline Tmat4(const vecN<T,4>& v) : base(v) {}
        inline Tmat4(const vecN<T,4>& v0,
                     const vecN<T,4>& v1,
                     const vecN<T,4>& v2,
                     const vecN<T,4>& v3)
        {
            base::data[0] = v0;
            base::data[1] = v1;
            base::data[2] = v2;
            base::data[3] = v3;
        }
    };
    
    typedef Tmat4<float> mat4;
    typedef Tmat4<int> imat4;
    typedef Tmat4<unsigned int> umat4;
    typedef Tmat4<double> dmat4;
    
    static inline mat4 frustum(float left, float right, float bottom, float top, float n, float f)
    {
        mat4 result(mat4::identity());
    
        if ((right == left) ||
            (top == bottom) ||
            (n == f) ||
            (n < 0.0) ||
            (f < 0.0))
           return result;
    
        result[0][0] = (2.0f * n) / (right - left);
        result[1][1] = (2.0f * n) / (top - bottom);
    
        result[2][0] = (right + left) / (right - left);
        result[2][1] = (top + bottom) / (top - bottom);
        result[2][2] = -(f + n) / (f - n);
        result[2][3]= -1.0f;
    
        result[3][2] = -(2.0f * f * n) / (f - n);
        result[3][3] =  0.0f;
    
        return result;
    }
    
    static inline mat4 perspective(float fovy /* in degrees */, float aspect, float n, float f)
    {
     float  top = n * tan(radians(0.5f*fovy)); // bottom = -top
     float  right = top * aspect; // left = -right
     return frustum(-right, right, -top, top, n, f);
    }
    
    template <typename T>
    static inline Tmat4<T> lookat(vecN<T,3> eye, vecN<T,3> center, vecN<T,3> up)
    {
        const Tvec3<T> f = normalize(center - eye);
        const Tvec3<T> upN = normalize(up);
        const Tvec3<T> s = cross(f, upN);
        const Tvec3<T> u = cross(s, f);
        const Tmat4<T> M = Tmat4<T>(Tvec4<T>(s[0], u[0], -f[0], T(0)),
                                    Tvec4<T>(s[1], u[1], -f[1], T(0)),
                                    Tvec4<T>(s[2], u[2], -f[2], T(0)),
                                    Tvec4<T>(T(0), T(0), T(0), T(1)));
    
    
        return M * translate(-eye);
    }
    
    template <typename T>
    static inline Tmat4<T> translate(T x, T y, T z)
    {
        return Tmat4<T>(Tvec4<T>(1.0f, 0.0f, 0.0f, 0.0f),
                        Tvec4<T>(0.0f, 1.0f, 0.0f, 0.0f),
                        Tvec4<T>(0.0f, 0.0f, 1.0f, 0.0f),
                        Tvec4<T>(x, y, z, 1.0f));
    }
    
    template <typename T>
    static inline Tmat4<T> translate(const vecN<T,3>& v)
    {
        return translate(v[0], v[1], v[2]);
    }
    
    template <typename T>
    static inline Tmat4<T> scale(T x, T y, T z)
    {
        return Tmat4<T>(Tvec4<T>(x, 0.0f, 0.0f, 0.0f),
                        Tvec4<T>(0.0f, y, 0.0f, 0.0f),
                        Tvec4<T>(0.0f, 0.0f, z, 0.0f),
                        Tvec4<T>(0.0f, 0.0f, 0.0f, 1.0f));
    }
    
    template <typename T>
    static inline Tmat4<T> scale(const Tvec4<T>& v)
    {
        return scale(v[0], v[1], v[2]);
    }
    
    template <typename T>
    static inline Tmat4<T> scale(T x)
    {
        return Tmat4<T>(Tvec4<T>(x, 0.0f, 0.0f, 0.0f),
                        Tvec4<T>(0.0f, x, 0.0f, 0.0f),
                        Tvec4<T>(0.0f, 0.0f, x, 0.0f),
                        Tvec4<T>(0.0f, 0.0f, 0.0f, 1.0f));
    }
    
    template <typename T>
    static inline Tmat4<T> rotate(T angle, T x, T y, T z)
    {
        Tmat4<T> result;
    
        const T x2 = x * x;
        const T y2 = y * y;
        const T z2 = z * z;
        float rads = float(angle) * 0.0174532925f;
        const float c = cosf(rads);
        const float s = sinf(rads);
        const float omc = 1.0f - c;
    
        result[0] = Tvec4<T>(T(x2 * omc + c), T(y * x * omc + z * s), T(x * z * omc - y * s), T(0));
        result[1] = Tvec4<T>(T(x * y * omc - z * s), T(y2 * omc + c), T(y * z * omc + x * s), T(0));
        result[2] = Tvec4<T>(T(x * z * omc + y * s), T(y * z * omc - x * s), T(z2 * omc + c), T(0));
        result[3] = Tvec4<T>(T(0), T(0), T(0), T(1));
    
        return result;
    }
    
    template <typename T>
    static inline Tmat4<T> rotate(T angle, const vecN<T,3>& v)
    {
        return rotate<T>(angle, v[0], v[1], v[2]);
    }
    
    #ifdef min
    #undef min
    #endif
    
    template <typename T>
    static inline T min(T a, T b)
    {
        return a < b ? a : b;
    }
    
    #ifdef max
    #undef max
    #endif
    
    template <typename T>
    static inline T max(T a, T b)
    {
        return a >= b ? a : b;
    }
    
    template <typename T, const int N>
    static inline vecN<T,N> min(const vecN<T,N>& x, const vecN<T,N>& y)
    {
        vecN<T,N> t;
        int n;
    
        for (n = 0; n < N; n++)
        {
            t[n] = min(x[n], y[n]);
        }
    
        return t;
    }
    
    template <typename T, const int N>
    static inline vecN<T,N> max(const vecN<T,N>& x, const vecN<T,N>& y)
    {
        vecN<T,N> t;
        int n;
    
        for (n = 0; n < N; n++)
        {
            t[n] = max<T>(x[n], y[n]);
        }
    
        return t;
    }
    
    template <typename T, const int N>
    static inline vecN<T,N> clamp(const vecN<T,N>& x, const vecN<T,N>& minVal, const vecN<T,N>& maxVal)
    {
        return min<T>(max<T>(x, minVal), maxVal);
    }
    
    template <typename T, const int N>
    static inline vecN<T,N> smoothstep(const vecN<T,N>& edge0, const vecN<T,N>& edge1, const vecN<T,N>& x)
    {
        vecN<T,N> t;
        t = clamp((x - edge0) / (edge1 - edge0), vecN<T,N>(T(0)), vecN<T,N>(T(1)));
        return t * t * (vecN<T,N>(T(3)) - vecN<T,N>(T(2)) * t);
    }
    
    template <typename T, const int N, const int M>
    static inline matNM<T,N,M> matrixCompMult(const matNM<T,N,M>& x, const matNM<T,N,M>& y)
    {
        matNM<T,N,M> result;
        int i, j;
    
        for (j = 0; j < M; ++j)
        {
            for (i = 0; i < N; ++i)
            {
                result[i][j] = x[i][j] * y[i][j];
            }
        }
    
        return result;
    }
    
    template <typename T, const int N, const int M>
    static inline vecN<T,N> operator*(const vecN<T,M>& vec, const matNM<T,N,M>& mat)
    {
        int n, m;
        vecN<T,N> result(T(0));
    
        for (m = 0; m < M; m++)
        {
            for (n = 0; n < N; n++)
            {
                result[n] += vec[m] * mat[n][m];
            }
        }
    
        return result;
    }
    
    };
    
    #endif /* __VMATH_H__ */
    
    

    LoadShaders.h
    //////////////////////////////////////////////////////////////////////////////
    //
    //  --- LoadShaders.h ---
    //
    //////////////////////////////////////////////////////////////////////////////
    
    #ifndef __LOAD_SHADERS_H__
    #define __LOAD_SHADERS_H__
    
    #include <GL/gl.h>
    
    #ifdef __cplusplus
    extern "C" {
    #endif  // __cplusplus
    
    //----------------------------------------------------------------------------
    //
    //  LoadShaders() takes an array of ShaderFile structures, each of which
    //    contains the type of the shader, and a pointer a C-style character
    //    string (i.e., a NULL-terminated array of characters) containing the
    //    entire shader source.
    //
    //  The array of structures is terminated by a final Shader with the
    //    "type" field set to GL_NONE.
    //
    //  LoadShaders() returns the shader program value (as returned by
    //    glCreateProgram()) on success, or zero on failure. 
    //
    
    typedef struct {
        GLenum       type;
        const char*  filename;
        GLuint       shader;
    } ShaderInfo;
    
    GLuint LoadShaders( ShaderInfo* );
    
    //----------------------------------------------------------------------------
    
    #ifdef __cplusplus
    };
    #endif // __cplusplus
    
    #endif // __LOAD_SHADERS_H__
    
    

    LoadShaders.cpp
    //////////////////////////////////////////////////////////////////////////////
    //
    //  --- LoadShaders.cxx ---
    //
    //////////////////////////////////////////////////////////////////////////////
    
    #include <cstdlib>
    #include <iostream>
    #include <cstdio>
    
    #define GLEW_STATIC
    #include <GL/glew.h>
    #include "LoadShaders.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif // __cplusplus
    
    //----------------------------------------------------------------------------
    
    static const GLchar*
    ReadShader( const char* filename )
    {
    
        FILE* infile = fopen( filename, "rb" );
    
        if ( !infile ) {
    #ifdef _DEBUG
            std::cerr << "Unable to open file '" << filename << "'" << std::endl;
    #endif /* DEBUG */
            return NULL;
        }
    
        fseek( infile, 0, SEEK_END );
        int len = ftell( infile );
        fseek( infile, 0, SEEK_SET );
    
        GLchar* source = new GLchar[len+1];
    
        fread( source, 1, len, infile );
        fclose( infile );
    
        source[len] = 0;
    
        return const_cast<const GLchar*>(source);
    }
    
    //----------------------------------------------------------------------------
    
    GLuint
    LoadShaders( ShaderInfo* shaders )
    {
        if(glewInit()){
            std::cerr << "Unable to initialize GLEW..." << std::endl;
        }
        if ( shaders == NULL ) { return 0; }
    
        GLuint program = glCreateProgram();
    
        ShaderInfo* entry = shaders;
        while ( entry->type != GL_NONE ) {
            GLuint shader = glCreateShader( entry->type );
    
            entry->shader = shader;
    
            const GLchar* source = ReadShader( entry->filename );
            if ( source == NULL ) {
                for ( entry = shaders; entry->type != GL_NONE; ++entry ) {
                    glDeleteShader( entry->shader );
                    entry->shader = 0;
                }
    
                return 0;
            }
    
            glShaderSource( shader, 1, &source, NULL );
            delete [] source;
    
            glCompileShader( shader );
    
            GLint compiled;
            glGetShaderiv( shader, GL_COMPILE_STATUS, &compiled );
            if ( !compiled ) {
    #ifdef _DEBUG
                GLsizei len;
                glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &len );
    
                GLchar* log = new GLchar[len+1];
                glGetShaderInfoLog( shader, len, &len, log );
                std::cerr << "Shader compilation failed: " << log << std::endl;
                delete [] log;
    #endif /* DEBUG */
    
                return 0;
            }
    
            glAttachShader( program, shader );
            
            ++entry;
        }
    
    #ifdef GL_VERSION_4_1
        if ( GLEW_VERSION_4_1 ) {
            // glProgramParameteri( program, GL_PROGRAM_SEPARABLE, GL_TRUE );
        }
    #endif /* GL_VERSION_4_1 */
        
        glLinkProgram( program );
    
        GLint linked;
        glGetProgramiv( program, GL_LINK_STATUS, &linked );
        if ( !linked ) {
    #ifdef _DEBUG
            GLsizei len;
            glGetProgramiv( program, GL_INFO_LOG_LENGTH, &len );
    
            GLchar* log = new GLchar[len+1];
            glGetProgramInfoLog( program, len, &len, log );
            std::cerr << "Shader linking failed: " << log << std::endl;
            delete [] log;
    #endif /* DEBUG */
    
            for ( entry = shaders; entry->type != GL_NONE; ++entry ) {
                glDeleteShader( entry->shader );
                entry->shader = 0;
            }
            
            return 0;
        }
    
        return program;
    }
    
    //----------------------------------------------------------------------------
    #ifdef __cplusplus
    }
    #endif // __cplusplus

    No se si son las mejores pero nos vienen al pelo.
Ademas, hay que cambiar las opciones de las librerías incluidas en el proyecto, ahora hay que añadir: glew32. El orden de inclusión importa por lo que copio, pego y modifico el que ya teníamos en el post de configuración de Eclipse.
  1. mingw32
  2. glew32
  3. opengl32
  4. SDL2main
  5. SDL2

sábado, 6 de julio de 2013

OpenGL 4.3 Pipeline

Vamos a explicar como funciona el pipeline de OpenGL 4.3.
Como es lógico junto con la API, el pipeline ha ido evolucionando a si que vamos a analizar la última versión. Los pasos que se le han añadido en la 4.3 son Tesselation y Compute, este último no lo vamos a ver aquí.


Foto que pertenece al libro oficial de programación en OpenGL: OpenGL Programming Guide Eight Edition. Que recomiendo encarecidamente su adquisición si tienes un nivel de ingles leído decente.
La foto ha sido obtenida de la página http://my.safaribooksonline.com que nos permite una previsualización del principio de todos los capítulos

Antes de nada: ¿que es un Shader?. Los que hemos jugado a minecraft nos suena la palabra shader y lo asociamos a "petar el PC".
Un Shader es un programa que se le pasa a la GPU y que nos permite cambiar información de las primitivas que se envían. Cuando escribimos un Shader podemos indicar una serie de parámetros de entrada como si fuera una función de cualquier lenguaje de programación, sólo que no tiene nada que ver la forma de ser llamada.
Cuando creamos un programa en OpenGL, básicamente definimos los vértices, color, texturas...

  • La primera de las fases (Vertex Shader) recibe los vértices colores de los vértices y texturas y con ello podemos modificar la posición de los vértices  el color y posición de la textura. Esta fase es obligatoria, pero el Shader puede ser tan simple como "coge vértice y pásalo".
  • Tessellation Shader recibe todos los vértices ya modificados por el vertex shader y permite crear más primitivas para poder hacer unos modelos más agradables. Esta fase es opcional.


  • Geometry Shader puede manipular las primitivas e incluso añadir nuevas. También es opcional.
  • Primitive Assembly, organiza toda la información que le llega en geometría dejándolo preparado para las siguientes dos fases.
  • Clipping, algunas veces alguno de los vértices está fuera de la ventana. Adapta esos vértices para que todos los píxel se queden dentro de la ventana.
  • Rasterization es la fase en la que se calculan todos los candidatos a ser uno de los píxel que aparezca en la pantalla. Cada uno de estos píxel se les llama fragment, como se puede deducir, son pasados al Fragment Shader.
  • El Fragment Shader es la última parte programable del pipeline, modifica el color de los píxel y puede modificar incluso la profundidad (posición en z). También puede terminar el procesado del fragmento descartándolo. Este shader es obligatorio, aunque al igual que el vertex puede ser tan simple como: "coge fragmento y pásalo"
  • Por último hay un per-fragment operation, que se encarga de decidir si un píxel debe ser pintado en función de su posición en z y lo que ya había en el z-buffer.

De todas estas fases sólo son programables los Shader (marcados en azul), el resto las maneja automáticamente la GPU.

La programación de shaders es casi imprescindible y tremendamente potente, una GPU esta especializada en cálculos en coma flotante y tratamiento de imágenes. Poder delegar cálculos a la gráfica ya es un avance, pero poder manejar incluso cada píxel que se vaya a dibujar deja una infinidad de posibilidades para realismo en gráficos.

jueves, 4 de julio de 2013

Inicialización de OpenGL

Hoy vamos a hacer nuestra primera inicialización de un contexto OpenGL en SDL.
Para ello me he basado en el "Hola Mundo II".
En el archivo de cabecera hemos hecho algunos cambios:
#ifndef DEMO_3_H_
#define DEMO_3_H_
#include "SDL2/SDL.h"

class Demo_3 {
private:
    bool running;
    SDL_Window* window;
    SDL_GLContext ctxt;

    int aux;

    static const uint32_t   WIN_HEIGHT = 512; //px
    static const uint32_t   WIN_WIDTH  = 512; //px
    static const char*      WIN_TITLE; //px
public:

    Demo_3();
    int Execute(){ return OnExecute(); }
    bool Init(){ return OnInit(); }
    void Loop(){ return OnLoop(); }
    void Render(){ return OnRender(); }
    void Cleanup(){ return OnCleanup(); }
    void Event(SDL_Event* Event){ OnEvent(Event); }



    int OnExecute();

    bool OnInit();
    void OnEvent(SDL_Event* Event);
    void OnLoop();
    void OnRender();
    void OnCleanup();



    void setupOpenGl();

};


#endif /* DEMO_3_H_ */
Hemos añadido dos variables, una es el contexto de OpenGL (SDL_GLContext) que es el lugar donde se va a dibujar, y aux, que es una variable dummy para poder realizar una pequeña interactuación con el entorno.
Al final del todo he añadido una función llamada: setupOpenGl que inicializa todo lo necesario para utilizar el contexto.
El header como siempre tiene poco que explicar, vayámonos al .cpp
#include "Demo_3.h"
#include "GL/gl.h"
#include <iostream>

const char* Demo_3::WIN_TITLE = "Titulo de la Ventana";

Demo_3::Demo_3() : running(false), window(NULL), ctxt(NULL), aux(0){}

void Demo_3::OnEvent(SDL_Event* event) {
    switch (event->type) {
        case SDL_KEYUP:
            switch(event->key.keysym.sym){
                case SDLK_KP_PLUS:
                    aux++;
                    aux %= 3;
                    break;
                case SDLK_v:
                    std::cout << glGetString(GL_VERSION) << std::endl;
                    break;
                case SDLK_ESCAPE:
                    running = false;
                    break;
                default:
                    break;
            }
            break;
        case SDL_QUIT:
            running = false;
            break;
        default:
            break;
    }
}
void Demo_3::OnLoop() {}
void Demo_3::OnRender() {

    glClearColor(aux==0? 1:0, aux==1? 1:0, aux==2? 1:0, 1.0);
    glClear( GL_COLOR_BUFFER_BIT);
    SDL_GL_SwapWindow(window);
}

void Demo_3::OnCleanup() {
    SDL_GL_DeleteContext(ctxt);
    SDL_DestroyWindow(window);
    SDL_Quit();
}

bool Demo_3::OnInit() {
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
        return false;

    window = SDL_CreateWindow(WIN_TITLE,
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
            WIN_WIDTH, WIN_HEIGHT,
            SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
    if(!window)
        return false;

    setupOpenGl();

    running = true;

    return true;
}

int Demo_3::OnExecute() {
    if (!Init())
        return -1;

    SDL_Event event;

    while (running) {

        while (SDL_PollEvent(&event))
            Event(&event);

        Loop();
        Render();
    }

    Cleanup();

    return 0;
}


void Demo_3::setupOpenGl(){
   //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
   //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
   SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);
   ctxt = SDL_GL_CreateContext(window);

   //vsync ON
   SDL_GL_SetSwapInterval(1);

}
Empecemos por OnInit, como siempre inicializa SDL y crea la ventana, pero si habéis estado atentos hay un flag más: SDL_WINDOW_OPENGL, esto le dice a la ventana que se prepare para el entorno OpenGL, el segundo cambio es la llamada a setupOpenGl.
setupOpenGl configura los atributos del entorno OpenGL, crea el contexto y activa la sincronización vertical, vayamos linea por linea:
  • Las dos primeras lineas que están comentadas especifican que versión del contexto OpenGL deben utilizar, en este caso estarían indicando la versión 3.2. Si omites esas dos líneas crea el contexto con la versión más reciente utilizada en el ordenador, en mi caso en la actualidad 4.3.0 que es el entorno que vamos a aprender a usar.
  • La siguiente línea activa el "Doble Buffer", si no se activa, cuando tengamos objetos dibujados en el entorno, la imagen parpadeara. Esto es debido a que OpenGL utilizaría el mismo buffer para escribir que para mandar la imagen a la pantalla. Al utilizar Doble Buffer el ordenador utilizaría un buffer para escribir y otro para mandar la imagen a la pantalla, en el siguiente ciclo, los buffer se intercambiarían, el de la pantalla pasaría a rellenarse y el relleno pasaría a la pantalla.
    En los juegos siempre sale la opción: triple buffer!!. Informándome por Internet he leído que el triple buffer en OpenGL no existe por linea de comandos, pero existe un triple buffer simulado. El problema de esto es que NVidia (e imagino que ATI tendrá algo parecido) por lo visto tiene una opción para activar el triple buffer, con lo cual con esa simulación estaríamos haciendo un cuadrabuffer lo que reventaría definitivamente nuestra aplicación. Por lo tanto nos quedamos con nuestro doble buffer. Como nota curiosa dicen que un buffer ocupa unos 25-30Mb en memoria, lo que supone relativamente poco para las actuales GPUs ya que, las peores tienen aproximadamente 512Mb. La noticia donde lo leí tenia su tiempo, quizá no tenían en cuenta la resolución HD720p ni HD1080p, o quizá si.
  • La cuarta linea configura si no me equivoco lo que se denomina Z-Buffer. El Z-Buffer es el buffer de profundidad. Cuando toda la linea de ejecución de la tarjeta gráfica se ejecuta, se comprueba en que posición de profundidad se haya ese píxel y si había uno con mas profundidad se sustituye por el nuevo. Pensemos en una mesa en una medio de una habitación, tenemos el buffer que contiene el suelo renderizado, cuando renderice la mesa decidirá, que la mesa está por encima del suelo y, que se tiene que ver. Sin embargo si tomamos cierta perspectiva, una de las cuatro patas de la mesa no debería verse o debería verse parcialmente, aún pudiéndose dibujar después. Si el z-buffer no estuviera activado, cada cosa se dibujaría en el orden de dibujado uno encima de otro no permitiendo entrelazamiento de capas. Por ejemplo, un pañuelo enrollado a un palo en forma de espiral: verías el palo y un pañuelo detrás o un pañuelo tapando un palo, nunca un palo con un pañuelo enrollado.
  • Volviendo a la línea, aumentamos el buffer, de 16bit (que es el actual "por defecto") a 32bit.
  • En la siguiente linea le pedimos a SDL que nos cree un entorno OpenGL en la ventana proporcionada, lo enlace y nos pase una referencia al contexto.
  • Por último activamos la sincronización vertical para evitar el Screen Tearing. Pincha en el enlace para más información.
En la función de inicialización no queda nada que comentar, lo siguiente es la gestión de eventos, he añadido un evento a la letra v para ver la versión que nuestro ordenador ha elegido de OpenGL. También le he añadido un evento a la tecla "+" del pad numérico, para cambiar el color del fondo de nuestra ventana mediante la variable aux.
Lo siguiente es la función Render, que hace tres llamadas:
  1. glClearColor(aux==0? 1:0, aux==1? 1:0, aux==2? 1:0, 1.0); Le dice con que color tiene que rellenar el buffer del contexto OpenGL cuando se llame a glClear. He utilizado aux para seleccionar rojo, verde y azul. Para ahorrar lineas he utilizado el operador ternario, o un "if rápido" como yo lo llamo, que para los que no lo conozcan: la condición se coloca antes del interrogante y después se coloca la instrucción si se cumple, y despues de los dos puntos, la instrucción si no se cumple. El último valor es Alpha o transparencia que colocamos a 1 (significa opaco, 0 = transparente, para el resto de números < 1 y >0 sería translúcido.).
    Para los que tengáis experiencia de diseño en otros entornos y lenguajes como puede ser CSS en html, os resultará extraño que los valores oscilen entre 0.0 y 1.0 en lugar de 0 y 255, pero es así xD.
  2. glClear( GL_COLOR_BUFFER_BIT); Le indica que debe limpiar un buffer, la constante indica el buffer de dibujo, veremos que hay más buffers que se pueden limpiar.
  3. SDL_GL_SwapWindow(window); Esto intercambia los buffers como hemos explicado antes.
Por último pero no menos importante tenemos el cleanup al que hemos añadido una línea que destruye el contexto OpenGL.
Muy importante que no se nos olvide nunca limpiar todo porque puede quedar memoria residual en el ordenador. A nadie le gusta que después de abrir unos cuantos programas tengas que reiniciar porque tu pobre RAM está llena de mierda.

miércoles, 3 de julio de 2013

Entrada por Eventos con SDL

En este tutorial vamos a aprender a capturar algunos eventos de teclado y ratón. Os dejo una página del sitio oficial para que veáis todos los eventos que queráis capturar. Aun así habrá otro tutorial con Eventos Avanzado.

Al igual que en el tutorial anterior, tenemos dividido el código fundamentalmente en la cabecera (.h) y la implementacion (.cpp). Con respecto al anterior tutorial, la cabecera prácticamente no ha cambiado, donde antes ponía Demo_1, ahora pone Demo_2, a si que os la dejo aquí para copiar pero doy por hecha la explicación.
#ifndef DEMO_2_H_
#define DEMO_2_H_
#include "SDL2/SDL.h"

class Demo_2 {
private:
    bool running;
    SDL_Window* mainwindow;
    static const uint32_t WIN_HEIGHT = 512; //px
    static const uint32_t WIN_WIDTH  = 512; //px
public:

    Demo_2();
    int Execute(){ return OnExecute(); }
    bool Init(){ return OnInit(); }
    void Loop(){ return OnLoop(); }
    void Render(){ return OnRender(); }
    void Cleanup(){ return OnCleanup(); }
    void Event(SDL_Event* Event){ OnEvent(Event); }



    int OnExecute();

    bool OnInit();
    void OnEvent(SDL_Event* Event);
    void OnLoop();
    void OnRender();
    void OnCleanup();

};


#endif
Con respecto a la implementación si se han producido un par de cambios, pongo el código y lo comentamos.
#include "Demo_2.h"
#include <iostream>

Demo_2::Demo_2() : running(false), mainwindow(NULL) {}

void Demo_2::OnEvent(SDL_Event* event) {
    switch (event->type) {
        case SDL_MOUSEBUTTONUP:
            std::cout << "MOUSE_BUTTON_UP: x:" << event->button.x << "| y:"
                    << event->button.y
                    << "| button1:" << (event->button.button == SDL_BUTTON_LEFT)
                    << "| button2:" << (event->button.button == SDL_BUTTON_RIGHT)
                    << "| button3:" << (event->button.button == SDL_BUTTON_MIDDLE)
                    << "| clickState:" << event->motion.state
                    << std::endl << std::endl;
            break;
        case SDL_MOUSEBUTTONDOWN:
            std::cout << "MOUSE_BUTTON_DOWN: x:" << event->button.x
                    << "| y:" << event->button.y
                    << "| button1:" << (event->button.button == SDL_BUTTON_LEFT)
                    << "| button2:" << (event->button.button == SDL_BUTTON_RIGHT)
                    << "| button3:" << (event->button.button == SDL_BUTTON_MIDDLE)
                    << "| clickState:" << event->motion.state
                    << std::endl << std::endl;
            break;
        case SDL_MOUSEMOTION:
            std::cout << "MOUSE_MOTION: x:" << event->motion.x
                    << "| y:" << event->motion.y
                    << "| xrel:" << event->motion.xrel
                    << "| yrel:" << event->motion.yrel
                    << "| clickState:" << event->motion.state
                    << "| button1:" << ((event->motion.state  & 1) == 1)
                    << "| button2:" << ((event->motion.state  & 4) == 4)
                    << "| button3:" << ((event->motion.state  & 2) == 2)
                    << std::endl << std::endl;
            break;
        case SDL_KEYUP:
            running = event->key.keysym.sym != SDLK_ESCAPE;
            break;
        case SDL_QUIT:
            running = false;
            break;
        default:
            break;
    }
}
void Demo_2::OnLoop() {}
void Demo_2::OnRender() {}

void Demo_2::OnCleanup() {
    SDL_DestroyWindow(mainwindow);
    SDL_Quit();
}

bool Demo_2::OnInit() {
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
        return false;

    mainwindow = SDL_CreateWindow("Titulo de la Ventana",
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
            WIN_WIDTH, WIN_HEIGHT,
            SDL_WINDOW_SHOWN);
    if(!mainwindow)
        return false;

    running = true;

    return true;
}

int Demo_2::OnExecute() {
    if (!Init())
        return -1;

    SDL_Event event;

    while (running) {

        while (SDL_PollEvent(&event))
            Event(&event);

        Loop();
        Render();
    }

    Cleanup();

    return 0;
}
He quitado todas las trazas para dejar la salida limpia para lo que nos concierne, el Input, que ahora tiene las trazas pertinentes.
Los principales cambios se han producido en OnExecute y en OnEvent.

  • OnExecute: Hemos quitado el contador de 10 vueltas y ahora tenemos un objeto del tipo SDL_Event para capturar los eventos.
    Después tenemos igual que antes el bucle en el que tenemos la función Loop y Render y al principio hemos añadido otro bucle. Este bucle se encarga de sacar todos los eventos (SDL_PollEvent) que se han producido en esa vuelta de bucle y los envía a la función Event para tratar. Si os fijáis, lo que hacemos es pasarle el puntero al evento, por eso el PollEvent puede guardar en tu estructura el evento.
    Por lo demás el OnExecute no tiene mucho más que comentar.
  • OnEvent: Recibimos un evento, ¿Que forma tiene un evento?
    Según la página oficial esta es su representación de evento.
    typedef union{
      Uint8 type;
      SDL_ActiveEvent active;
      SDL_KeyboardEvent key;
      SDL_MouseMotionEvent motion;
      SDL_MouseButtonEvent button;
      SDL_JoyAxisEvent jaxis;
      SDL_JoyBallEvent jball;
      SDL_JoyHatEvent jhat;
      SDL_JoyButtonEvent jbutton;
      SDL_ResizeEvent resize;
      SDL_ExposeEvent expose;
      SDL_QuitEvent quit;
      SDL_UserEvent user;
      SDL_SywWMEvent syswm;
    } SDL_Event;
    Y si ahora vemos otro evento cualquiera vemos que es de la forma:
    typedef struct{
      Uint8 type;
      Uint8 state;
      SDL_keysym keysym;
    } SDL_KeyboardEvent;
    Todos empiezan con Uint8 type; y después tienen mas información. Si os fijáis y sabéis lo que son, la estructura del Evento es una Union y no una struct, por eso todas las implementaciones de evento deben tener el type al principio. 
    Para los que no sepan lo que es, una Union hace que todo lo que este dentro de las llaves ocupe la misma posición de memoria, dependiendo del tipo de evento se rellenan mas o menos bits. Esto que han hecho es una especie de simulación de herencia o interfaz para struct. La explicación se ha quedado un poco pobre, si no te ha quedado claro mira en Internet que encontraras mucha información.
    Por ello lo primero que hace la función OnEvent es mirar que tipo de evento estamos tratando, para ello hacemos un switch con el type del evento.
    Hemos capturado 5 tipos como ejemplo, aquí podéis encontrar todos (Input Events):
    http://wiki.libsdl.org/APIByCategory
    Después de capturar el tipo de evento accedemos al tipo de evento correspondiente en la estructura, por ejemplo: 
    event->button Y a la propiedad que deseemos, SDL_MOUSEBUTTONUP al ser un evento de ratón podemos capturar la posicion x e y, el botón del ratón que se ha liberado (lógicamente solo puede ser uno a la vez, por muy rápidos que seamos es casi imposible liberar dos a la vez) y el estado en el que está el ratón.
    En el caso de mousemotion existen xrel e yrel que te dan la posición relativa a la anterior posición del ratón. El caso del estado te da información acerca de que botones están pulsados con una mascara binaria, para poder obtener cuales están pulsados utilizo la & binaria y no la && lógica. He comprobado que obtiene hasta 5 botones del ratón en mi caso. Imagino que no dependerá del ratón, a si que, si quieren mas botones de ratón que se los bindeen a teclas especiales :P.
    Para ejemplificar el teclado he puesto que cuando se presiona la tecla Escape (Esc) se termine el programa. Esa porción de código es suficientemente simple, no me paro más.
    Por ultimo SDL_QUIT corresponde al evento de hacer click en la X de la ventana, también salimos del programa. Si no lo configuramos, no podremos salir dándole a la X.
Para probar el ejemplo igual que en el anterior, crearos un main con un objeto del tipo Demo_2 y llamad a Execute(). Deberiais tener una ventana y una traza en la consola de los eventos que hemos capturado, el mouse motion y el mouse click no se capturan si no estas encima de la ventana.
Aquí se termina este tutorial, espero os haya sido útil. Os dejo un enlace a la documentación de la API, en el apartado Input Events aparecen todos los eventos: http://wiki.libsdl.org/APIByCategory

martes, 2 de julio de 2013

Primer Programa

Vamos a hacer un programa basado en el primer Hola Mundo pero dándole un par de vueltas de rosca.
Este tutorial es una fusión de dos que he encontrado, uno es el de la pagina de sdl y otro es un tutorial de sdl 1.2. Las páginas, por si alguien está interesado,  son:


Un juego tiene una estructura básica de 4 pasos:

  1. Inicialización de todo.
  2. GameLoop o bucle de juego que se divide en 3:
    1. Control de Eventos.
    2. Procesamiento del estado de juego y cambios que se realizan en el Modelo.
    3. Renderización o procesamiento de los gráficos.
  3. Liberación de recursos 
  4. Finalización del programa.
Un tutorial que vi decía: "No pongas todo en el main", y de 100 lineas se quedo en 70 con dos funciones (gran de avance... ¬¬). Imagino que lo hizo así y no lo complicó más para que los "alumnos" menos avanzados no se perdieran, pero yo he elegido la estructura que viene en el primer enlace, porque me parece fácil de entender si tienes ya cierto rodaje en la orientación a objetos.

Creamos una clase para ello creamos dos ficheros que yo he llamado Demo_1.h y Demo_1.cpp. El primero contiene la declaración de la clase y el segundo su definición como ya sabréis.
El GameLoop es un bucle que hay que indicarle cuando ha de parar por eso se suele poner con un while. Para poder indicarlo necesitamos una variable booleana que cambiemos cuando queramos finalizar. Como nuestro juego se va a desarrollar en una ventana gráfica y no en consola necesitamos una ventana de sdl.
Además de todo eso ponemos la definición de los 5 pasos en forma de funciones y dos funciones más: el constructor y una función para iniciar el juego.
Veréis que hay funciones del tipo OnXXXXX y XXXXX, es cosa mía, el ejemplo trata cada fase como un evento (OnExecute) que a mi me parece mejor llamar con una función imperativa (Execute), son simplemente alias de la función.


#ifndef DEMO_1_H_
#define DEMO_1_H_
#include "SDL2/SDL.h"

class Demo_1 {
private:
    SDL_Window* mainwindow;
    bool running;
    static const uint32_t WIN_HEIGHT = 512; //px
    static const uint32_t WIN_WIDTH  = 512; //px

public:
    Demo_1();
    int Execute(){ return OnExecute(); }
    bool Init(){ return OnInit(); }
    void Loop(){ return OnLoop(); }
    void Render(){ return OnRender(); }
    void Cleanup(){ return OnCleanup(); }
    void Event(SDL_Event* Event){ OnEvent(Event); }



    int OnExecute();

    bool OnInit();
    void OnEvent(SDL_Event* Event);
    void OnLoop();
    void OnRender();
    void OnCleanup();

};


#endif

OnInit es de tipo bool porque debemos saber si se ha hecho bien la inicialización de todos los componentes y la carga de imágenes. Execute devuelve un int de la misma manera que lo devuelven los main:
  • 0: ejecución correcta. 
  • De-otra-manera: ejecución incorrecta.
El resto son procedimientos de los que no necesitamos información que devolver.

Vamos a analizar ahora el .cpp:

El constructor sólo inicializa la ventana a null y la variable del gameLoop a false.
OnEvent, OnLoop y OnRender, los modificaremos mas tarde, de momento no necesitamos nada de ellos. He escrito por la salida estándar para que podamos ver el gameloop como funciona.
OnCleanup simplemente libera la ventana y llama al SDL_Quit.


#include "Demo_1.h"
#include <iostream>

Demo_1::Demo_1(): mainwindow(0), running(false){}

void Demo_1::OnEvent(SDL_Event* Event) {
    std::cout << "Event" << std::endl;
}
void Demo_1::OnLoop() {
    std::cout << "Looping" << std::endl;
}
void Demo_1::OnRender() {
    std::cout << "Rendering" << std::endl;
}

void Demo_1::OnCleanup() {
    std::cout << "Cleaning" << std::endl;
    SDL_DestroyWindow(mainwindow);
    SDL_Quit();
}

bool Demo_1::OnInit() {
    std::cout << "Init!" << std::endl;
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
        return false;

    mainwindow = SDL_CreateWindow("Titulo de la Ventana",
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
            WIN_WIDTH, WIN_HEIGHT,
            SDL_WINDOW_SHOWN);
    if(!mainwindow)
        return false;

    running = true;

    return true;
}

int Demo_1::OnExecute() {
    if (!Init())
        return -1;

    int times = 10;
    while (running) {
        Event(NULL);
        Loop();
        Render();
        times--;
        if(!times)
            running = false;
    }

    SDL_Delay(2000);

    Cleanup();

    return 0;
}
OnInit inicializa la API de SDL y crea una ventana, de esa función podemos decir que el primer argumento es como bien se entiende: el título de la ventana, el segundo y tercero son las posiciones x e y de la ventana con respecto a tu monitor (SDL_WINDOWPOS_CENTERED, indica que la ventana estará centrada en ese eje), las siguientes son la altura y la anchura de la ventana que tenemos definidas con constantes y el último parámetro son flags que preparan propiedades de la ventana, en este caso le indicamos que se muestre (SDL_WINDOW_SHOWN), en posteriores tutoriales utilizaremos más.
Realizamos un control de errores e indicamos al programa que ha empezado a correr nuestro juego.
OnExecute básicamente lleva a cabo la estructura que hemos explicado antes. He creado una variable para que el bucle no se quede en un ciclo infinito ya que todavía no sabemos manejar eventos. Después del bucle espera 2 segundos ( SDL_Delay(milisec) ), para poder apreciar la ventana que vamos a manejar. Más tarde limpia los recursos y finaliza con ejecución exitosa (return 0;).
Para poder ejecutar este código nos creamos un main con el siguiente código:
    
#include "SDL2/SDL.h"
int main(int argc, char* argv[]) {
    Demo_1 d;
    return d.Execute();
}
Esto debería de mostrar una ventana y cerrarla a los dos segundos y dejar un log en la consola mostrando la forma del gameLoop ejecutado 10 veces.
Para los que no os lo han explicado nunca y no se os ha ocurrido pensarlo, se puede ver que el init solo se ejecuta una vez al igual que el Cleanup, lo que significa que, si estas dos funciones tienen una carga de trabajo exagerada, es relativamente poco importante. Todo el mundo espera si un programa tiene que cargar al principio, sin embargo las funciones que deberían estar optimizadas son el Loop, el Render y la gestión de Eventos, ya que, siendo optimistas, deberían ejecutarse más de 60 veces por segundo (60fps), y si eso se viera ralentizado, afectaría a la experiencia de juego.

lunes, 1 de julio de 2013

Hola Mundo III

Este último Hola Mundo vamos a utilizar la carga de imágenes. Debemos usar un .bmp estilo Windows a si que lo mejor es pillar el paint y guardar un "hello" como bmp y ponerlo en la misma carpeta del proyecto, si quieres guardarlo en alguna carpeta hay que cambiar la direccion.


#include <iostream>
#include "SDL2/SDL.h"

int main(int argc, char** argv){
    if (SDL_Init(SDL_INIT_EVERYTHING) == -1){
        std::cout << SDL_GetError() << std::endl;
        return 1;
    }
    SDL_Window *win = NULL;
    win = SDL_CreateWindow("Hello World!", 100, 100, 640, 480, SDL_WINDOW_SHOWN);
    if (win == NULL){
        std::cout << SDL_GetError() << std::endl;
        return 1;
    }

    SDL_Renderer *ren = NULL;
    ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (ren == NULL){
        std::cout << SDL_GetError() << std::endl;
        return 1;
    }

    SDL_Surface *bmp = NULL;
    bmp = SDL_LoadBMP("hello.bmp");
    if (bmp == NULL){
        std::cout << SDL_GetError() << std::endl;
        return 1;
    }

    SDL_Texture *tex = NULL;
    tex = SDL_CreateTextureFromSurface(ren, bmp);
    SDL_FreeSurface(bmp);

    SDL_RenderClear(ren);
    SDL_RenderCopy(ren, tex, NULL, NULL);
    SDL_RenderPresent(ren);

    SDL_Delay(2000);

    SDL_DestroyTexture(tex);
    SDL_DestroyRenderer(ren);
    SDL_DestroyWindow(win);

    SDL_Quit();

    return 0;
}

Hola Mundo II

Este si lo considero un "Hola Mundo" en condiciones, de hecho quizá se pasa un poco.
Cuando lo ejecutéis debería salir una ventana en color rojo, a los dos segundos cambiarse a verde y después a azul. Por último se cierra y termina el programa.
No me voy a parar a explicarlo, esto simplemente es para ver que lo tenemos bien configurado y que funciona.

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
/* If using gl3.h */
/* Ensure we are using opengl's core profile only */
#include <GL/gl.h>

#include <SDL2/SDL.h>
#define PROGRAM_NAME "Tutorial1"

/* A simple function that prints a message, the error code returned by SDL,
 * and quits the application */
void sdldie(const char *msg) {
    printf("%s: %s\n", msg, SDL_GetError());
    SDL_Quit();
    exit(1);
}

void checkSDLError(int line = -1) {
#ifndef NDEBUG
    const char *error = SDL_GetError();
    if (*error != '\0') {
        printf("SDL Error: %s\n", error);
        if (line != -1)
            printf(" + line: %i\n", line);
        SDL_ClearError();
    }
#endif
}

/* Our program's entry point */
int main(int argc, char *argv[]) {
    SDL_Window *mainwindow; /* Our window handle */
    SDL_GLContext maincontext; /* Our opengl context handle */

    if (SDL_Init(SDL_INIT_VIDEO) < 0) /* Initialize SDL's Video subsystem */
        sdldie("Unable to initialize SDL"); /* Or die on error */

    /* Request opengl 3.2 context.
     * SDL doesn't have the ability to choose which profile at this time of writing,
     * but it should default to the core profile */
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);

    /* Turn on double buffering with a 24bit Z buffer.
     * You may need to change this to 16 or 32 for your system */
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);

    /* Create our window centered at 512x512 resolution */
    mainwindow = SDL_CreateWindow(PROGRAM_NAME, SDL_WINDOWPOS_CENTERED,
    SDL_WINDOWPOS_CENTERED, 512, 512, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
    if (!mainwindow) /* Die if creation failed */
        sdldie("Unable to create window");

    checkSDLError(__LINE__);

    /* Create our opengl context and attach it to our window */
    maincontext = SDL_GL_CreateContext(mainwindow);
    checkSDLError(__LINE__);

    /* This makes our buffer swap syncronized with the monitor's vertical refresh */
    SDL_GL_SetSwapInterval(1);

    /* Clear our buffer with a red background */
    glClearColor(1.0, 0.0, 0.0, 1.0);
    glClear( GL_COLOR_BUFFER_BIT);
    /* Swap our back buffer to the front */
    SDL_GL_SwapWindow(mainwindow);
    /* Wait 2 seconds */
    SDL_Delay(2000);

    /* Same as above, but green */
    glClearColor(0.0, 1.0, 0.0, 1.0);
    glClear( GL_COLOR_BUFFER_BIT);
    SDL_GL_SwapWindow(mainwindow);
    SDL_Delay(2000);

    /* Same as above, but blue */
    glClearColor(0.0, 0.0, 1.0, 1.0);
    glClear( GL_COLOR_BUFFER_BIT);
    SDL_GL_SwapWindow(mainwindow);
    SDL_Delay(2000);

    /* Delete our opengl context, destroy our window, and shutdown SDL */
    SDL_GL_DeleteContext(maincontext);
    SDL_DestroyWindow(mainwindow);
    SDL_Quit();

    return 0;
}

Hola Mundo

Aquí tenemos el primer "Hola Mundo" aunque no lo considero ni eso porque no suelta ni una ventana. Aún así debería de no dar ningún tipo de error.
No me voy a parar a explicarlo, esto simplemente es para ver que lo tenemos bien configurado y que funciona.

#include <stdio.h>
#include "SDL2/SDL.h"

int main( int argc, char* args[] )
{
    //Start SDL
    if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
        fprintf(stderr, "No se puede iniciar SDL: %s\n",
        SDL_GetError());
        return 1;
    }

    //Quit SDL
    SDL_Quit();

    return 0;
}

domingo, 30 de junio de 2013

Instalando SDL en Windows para Eclipse Kepler

Este tutorial esta desfasado. Cuando tenga tiempo, lo actualizare.

¿Porqué Windows? Mi ordenador de sobremesa esta dedicado a videojuegos y de momento si quieres poder jugar a casi cualquier juego tienes que tener Windows. No creo que sea muy diferente la instalación en Linux. Si consigo que salga adelante, lo instalare también.
¿Porque Eclipse? Porque soy fan incondicional de ese IDE.

Los enlaces que vamos a usar son:

 Lo primero es instalar MinGW, y Eclipse C++ para lo cual necesitamos la JDK.
  1. La JDK se instala fácilmente, no me voy a parar en eso, hay millones de tutoriales.
  2. MinGW se instala muy bien con esa guía. Muy importante cambiar las variables de entorno. No me hago responsable de posibles fallos vuestros al hacerlo xD.
  3. Eclipse CDT se baja directamente de la pagina oficial. Necesitamos el MinGW porque no trae un compilador de serie.
  4. De la pagina de SDL hay que descargarse la API, yo me he descargado todo y voy a ir tirando.
    En la portada pone:
    SDL 2.0 is now in Release Candidate status!
    Please download the SDLSDL_imageSDL_mixer libraries and report bugs at our bug tracker
    .
    Pues si nos lo pide por favor (Please) vamos a hacerle caso, entramos en los 3 enlaces y nos descargamos el tar.gz (MinGW) de las Development Libraries.
    Una vez descargados y descomprimidos tienen dos build, uno de 64bit y otro de 32 bit. Yo me he instalado los de 32 para empezar.
    Instalación: 
    1. Carpeta "SDL2-2.0.0/i686-w64-mingw32/"
      Ahora dentro tenemos 4 carpetas, el interior de la carpeta include (carpeta SDL) debe ir dentro del directorio donde hayamos instalado MinGW, dentro de la carpeta: include (Sí, se llama igual).
      El interior de la carpeta lib, dentro de la carpeta lib de MinGW.
      El interior de bin (SDL2.dll) es la librería como tal, se puede poner relativa al proyecto o instalarla en Windows. Yo la he instalado en Windows dentro de la carpeta System32 aunque recomiendan no hacerlo por temas de desarrollo en paralelo con varios proyectos a la vez y para poder portar el juego. Para lo que nos interesa que es aprender a usarlo de momento es suficiente y sencillo de hacer.
    2. Carpeta "SDL2_image-2.0.0\i686-w64-mingw32/"
      Mismos pasos de antes.
    3. "SDL2_mixer-2.0.0\i686-w64-mingw32"
      Mismos pasos de antes.
  5. Ya casi hemos terminado. Abrimos el Eclipse CDT, creamos un Workspace para él.
    Creamos un nuevo proyecto de C++: File -> New -> C++ Proyect, cuando te den a elegir, elige MinGW.
  6. Ya tenemos un proyecto de C++, vamos a transformarlo en un proyecto de C++-SDL-OpenGL:
    Encima del proyecto, Click Derecho -> Properties -> C/C++ Build -> Settings.
    Nos aparecerá una ventana para la configuración de librerías y demás. Vamos en la pestaña que debería estar abierta (Tool Settings) al apartado de MinGW ->Libraries y en el cuadro de la derecha: "Libraries (-l)" añadimos: 
    1. mingw32
    2. opengl32
    3. SDL2main
    4. SDL2
    Estas son las librerías básicas a añadir. En algunos ejemplos iremos añadiendo otras para poder utilizar todas las funcionalidades.
  7. Para los linuxeros como instalar SDL en Ubuntu y Linux mint: http://nothingtocode.blogspot.com.es/2013/07/setting-up-sdl2-in-ubuntu-or-linux-mint.html
Ya tenemos nuestro entorno preparado para empezar con el desarrollo en SDL. Para comprobar que esta todo bien configurado probad los Hola Mundo:


Cada vez que queráis empezar un nuevo proyecto hay que volver a configurar las propiedades por lo que el paso 5 y 6 os los terminareis aprendiendo de memoria.

Por cierto: para probar los programas hay que pinchar primero en el martillo para que compile y después darle al circulo verde con el triangulo (parece un botón de "play"/"reproducir"). El código puede dar error hasta que pinchemos en el martillo, una vez compilado el código no debería de dar error, si lo da, tenemos algo mal escrito o algo mal configurado.



sábado, 29 de junio de 2013

El porqué de este Blog

Siempre he estado interesado en la creación de videojuegos. Hace 5 años entre en el ciclo formativo de grado superior de informática llamado DAI (Desarrollo de Aplicaciones Informáticas). Allí aprendí a programar.
Me gusto tanto que entré en el Grado en Ingeniería Informática, en la Universidad Complutense de Madrid y en mis ratos libres me dedico a investigar sobre el desarrollo de videojuegos.
A mediados de este curso estuve probando un motor gráfico pero se me hacia muy raro todo, necesitaba una formación desde 0 para poder implementar lo que el motor gráfico no tenia implementado. 
Había jugado y trasteado con GLUT y OpenGL anteriormente pero mire por Internet una alternativa a GLUT, ya que tiene muchos años y no parecía ser lo que yo quería, y encontre SDL, SFML y Allegro.
Los tres parecían tener, a priori, todo lo que necesitaba: Manejo sencillo de I/O por periféricos, manejo de ventanas, audio, multiplataforma y que soporte o este implementado en C++ y OpenGL.

El primero que mire fue Allegro pero me tiré demasiado tiempo intentando instalarlo y no lo conseguí, en todos sitios decían que había que ejecutar un archivo .bat que no estaba por ningún lado, además que en el tutorial ponía que, después de instalarlo, tenía que instalar 8 librerías más para el resto de utilidades. El caso es que, no se si estaba torpe o vago, pero no conseguí instalarlo de manera satisfactoria y pase al siguiente.
SDL o SFML? Pues de manera un poco aleatoria me decidí a probar SDL conseguí instalarlo ayer y he empezado a aprender.
Pros y Contras: 
  • Pros: Tiene funciones para todo aparentemente, desde abrir la ventana hasta manejar el audio y periféricos como teclado o incluso joystick. Además tiene algo que me parece fundamental para la era en la que vivimos: Los juegos se pueden portar a IPhone, y están trabajando para que se puedan portar a Android.
  • Contras: Todavía no está para Android y la versión 2.0 ha salido hace muy poco, creo que aproximadamente un año a sí que hay muy pocos tutoriales y todos los que encuentro se basan en la versión anterior.
Por eso me he decidido a crear este Blog, voy a ir anotando todos los progresos tanto para la gente que busque información sobre SDL como para mi, si dejo el proyecto y decido retomarlo en algún momento.

Doy por hecho un cierto nivel de programación orientada a objetos. En este blog no se pretende enseñar a programar. El objetivo es ir mostrando mis progresos en el proceso de aprender a utilizar esta herramienta y compartirlos con todos aquellos que les pueda interesar ahora o en un futuro.
Espero que os sea de utilidad y de vuestro agrado.

¡Saludos!