/*
 * This file was generated automatically by ExtUtils::ParseXS version 3.51 from the
 * contents of Matrix.xs. Do not edit this file, edit Matrix.xs instead.
 *
 *    ANY CHANGES MADE HERE WILL BE LOST!
 *
 */

#line 1 "lib/OpenGL/Matrix.xs"
/*  Copyright (c) 2015 Bob Free. All rights reserved.
 *  This program is free software; you can redistribute it and/or
 *  modify it under the same terms as Perl itself.
 */

#include <stdio.h>
#include <float.h>

#include "pgopogl.h"

#ifdef HAVE_GL
#include "gl_util.h"
#endif

#define PI (3.14159265359)

#define needs_2D(mat, function) \
if (mat->dimension_count != 2)   \
{croak("OpenGL::Matrix::" function " requires a 2D matrix");}

#define needs_4x4(mat, function) \
if (mat->dimension_count != 2 || mat->dimensions[0] != 4 || mat->dimensions[1] != 4)   \
{croak("OpenGL::Matrix::" function " requires a 4x4 matrix");}

static int get_index(OpenGL__Matrix mat, int col, int row)
{
    int cols = mat->dimensions[0];
    int rows = mat->dimensions[1];
    return(row*cols + col);
}

static OpenGL__Matrix new_matrix(int cols, int rows)
{
	int mat_len = sizeof(oga_struct);
	OpenGL__Matrix mat = malloc(mat_len);
	memset(mat, 0, mat_len);

	int count = cols;
	mat->dimension_count = 1;
	if (rows)
	{
	    count *= rows;
    	mat->dimension_count++;
	}
	mat->dimensions[0] = cols;
	mat->dimensions[1] = rows;

	mat->type_count = 1;
	mat->item_count = count;
	mat->total_types_width = gl_type_size(GL_FLOAT);
	if (mat->total_types_width < 0) croak("unknown type");
	mat->data_length = mat->total_types_width * mat->item_count;
	
	mat->types = malloc(sizeof(GLenum) * mat->type_count);
	mat->type_offset = malloc(sizeof(GLint) * mat->type_count);
	mat->data = malloc(mat->data_length);
	mat->free_data = 1;

	mat->type_offset[0] = 0;
	mat->types[0] = GL_FLOAT;
	
	return(mat);
}

static double vec_length(double* vec, int dimensions)
{
  GLfloat ret = 0;
  int i = 0;
  for (; i<dimensions; i++) ret += pow(vec[i], 2);
  return(pow(ret, .5));
}

static void fetch_arrayref(GLfloat* array, int maxlen, SV* sv, char* function, char* var)
{
    if (!SvROK(sv))
    {
        croak("OpenGL::Matrix::%s %s is not a reference", function, var);
    }

    SV * tmpSV = (SV*)SvRV(sv);
    if (SvTYPE(tmpSV) != SVt_PVAV)
    {
        croak("OpenGL::Matrix::%s %s is not an arrayref", function, var);
    }
    
    AV* arrayref = (AV*)tmpSV;
    int len = av_len(arrayref)+1;
    if (len > maxlen) len = maxlen;
    int i = 0;
    for (; i<len; i++)
    {
        SV** elem = av_fetch(arrayref, i, 0);
        if (elem != NULL)
        {
            array[i] = (GLfloat)SvNV(*elem);
        }
    }
}

static void set_data_identity(GLfloat * data, int size)
{
    int offset = 0;
    int i = 0;
    int j;
	for (; i<size; i++)
	{
	    for(j=0; j<size; j++)
	    {
	        data[offset++] = (i == j) ? 1.0 : 0.0;
	    }
	}
}

static void set_data_frustum(GLfloat * data,
    GLfloat left, GLfloat right, GLfloat top, GLfloat bottom, GLfloat n, GLfloat f)
{
    GLfloat width = right-left;
    GLfloat height = bottom-top;
    GLfloat depth = f-n;

    data[0]     = n*2.0/width;
    data[1]     = 0.0;
    data[2]     = 0.0;
    data[3]     = 0.0;
    data[4]     = 0.0;
    data[5]     = n*2.0/height;
    data[6]     = 0.0;
    data[7]     = 0.0;
    data[8]     = (right+left)/width;
    data[9]     = (bottom+top)/height;
    data[10]    = -(f+n)/depth;
    data[11]    = -1.0;
    data[12]    = 0.0;
    data[13]    = 0.0;
    data[14]    = -(f*n*2.0)/depth;
    data[15]    = 0.0;
}

static int inverse_lookup[] = {0,3,6,9,1,4,7,10,2,5,8,11};


#line 152 "lib/OpenGL/Matrix.c"
#ifndef PERL_UNUSED_VAR
#  define PERL_UNUSED_VAR(var) if (0) var = var
#endif

#ifndef dVAR
#  define dVAR		dNOOP
#endif


/* This stuff is not part of the API! You have been warned. */
#ifndef PERL_VERSION_DECIMAL
#  define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
#endif
#ifndef PERL_DECIMAL_VERSION
#  define PERL_DECIMAL_VERSION \
	  PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
#endif
#ifndef PERL_VERSION_GE
#  define PERL_VERSION_GE(r,v,s) \
	  (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
#endif
#ifndef PERL_VERSION_LE
#  define PERL_VERSION_LE(r,v,s) \
	  (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
#endif

/* XS_INTERNAL is the explicit static-linkage variant of the default
 * XS macro.
 *
 * XS_EXTERNAL is the same as XS_INTERNAL except it does not include
 * "STATIC", ie. it exports XSUB symbols. You probably don't want that
 * for anything but the BOOT XSUB.
 *
 * See XSUB.h in core!
 */


/* TODO: This might be compatible further back than 5.10.0. */
#if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1)
#  undef XS_EXTERNAL
#  undef XS_INTERNAL
#  if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING)
#    define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name)
#    define XS_INTERNAL(name) STATIC XSPROTO(name)
#  endif
#  if defined(__SYMBIAN32__)
#    define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
#    define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name)
#  endif
#  ifndef XS_EXTERNAL
#    if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus)
#      define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__)
#      define XS_INTERNAL(name) STATIC void name(pTHX_ CV* cv __attribute__unused__)
#    else
#      ifdef __cplusplus
#        define XS_EXTERNAL(name) extern "C" XSPROTO(name)
#        define XS_INTERNAL(name) static XSPROTO(name)
#      else
#        define XS_EXTERNAL(name) XSPROTO(name)
#        define XS_INTERNAL(name) STATIC XSPROTO(name)
#      endif
#    endif
#  endif
#endif

/* perl >= 5.10.0 && perl <= 5.15.1 */


/* The XS_EXTERNAL macro is used for functions that must not be static
 * like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL
 * macro defined, the best we can do is assume XS is the same.
 * Dito for XS_INTERNAL.
 */
#ifndef XS_EXTERNAL
#  define XS_EXTERNAL(name) XS(name)
#endif
#ifndef XS_INTERNAL
#  define XS_INTERNAL(name) XS(name)
#endif

/* Now, finally, after all this mess, we want an ExtUtils::ParseXS
 * internal macro that we're free to redefine for varying linkage due
 * to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use
 * XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to!
 */

#undef XS_EUPXS
#if defined(PERL_EUPXS_ALWAYS_EXPORT)
#  define XS_EUPXS(name) XS_EXTERNAL(name)
#else
   /* default to internal */
#  define XS_EUPXS(name) XS_INTERNAL(name)
#endif

#ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
#define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)

/* prototype to pass -Wmissing-prototypes */
STATIC void
S_croak_xs_usage(const CV *const cv, const char *const params);

STATIC void
S_croak_xs_usage(const CV *const cv, const char *const params)
{
    const GV *const gv = CvGV(cv);

    PERL_ARGS_ASSERT_CROAK_XS_USAGE;

    if (gv) {
        const char *const gvname = GvNAME(gv);
        const HV *const stash = GvSTASH(gv);
        const char *const hvname = stash ? HvNAME(stash) : NULL;

        if (hvname)
	    Perl_croak_nocontext("Usage: %s::%s(%s)", hvname, gvname, params);
        else
	    Perl_croak_nocontext("Usage: %s(%s)", gvname, params);
    } else {
        /* Pants. I don't think that it should be possible to get here. */
	Perl_croak_nocontext("Usage: CODE(0x%" UVxf ")(%s)", PTR2UV(cv), params);
    }
}
#undef  PERL_ARGS_ASSERT_CROAK_XS_USAGE

#define croak_xs_usage        S_croak_xs_usage

#endif

/* NOTE: the prototype of newXSproto() is different in versions of perls,
 * so we define a portable version of newXSproto()
 */
#ifdef newXS_flags
#define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
#else
#define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
#endif /* !defined(newXS_flags) */

#if PERL_VERSION_LE(5, 21, 5)
#  define newXS_deffile(a,b) Perl_newXS(aTHX_ a,b,file)
#else
#  define newXS_deffile(a,b) Perl_newXS_deffile(aTHX_ a,b)
#endif

#line 296 "lib/OpenGL/Matrix.c"

XS_EUPXS(XS_OpenGL__Matrix_new); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_new)
{
    dVAR; dXSARGS;
    if (items < 3)
       croak_xs_usage(cv,  "Class, cols, rows, ...");
    {
	GLsizei	cols = (int)SvIV(ST(1))
;
	GLsizei	rows = (int)SvIV(ST(2))
;
	OpenGL__Matrix	RETVAL;
#line 151 "lib/OpenGL/Matrix.xs"
	{
	    OpenGL__Matrix mat = new_matrix(cols, rows);

		if (items > 3)
		{
    	    oga_struct * src_mat = INT2PTR(OpenGL__Array, SvIV((SV*)SvRV(ST(3))));

    	    if (mat && src_mat->type_count == 1 && src_mat->types[0] == GL_FLOAT)
    	    {
    		    int src_offset;
    		    int offset = 0;
                if (src_mat->dimension_count == 2)
                {
                    int src_cols = src_mat->dimensions[0];
                    int src_rows = src_mat->dimensions[1];

                    GLfloat * data = (GLfloat*)mat->data;
                    GLfloat * src_data = (GLfloat*)src_mat->data;

                    int i = 0;
                    int j;
                    for (; i < rows; i++)
                    {
                        src_offset = i * src_cols;
                        for (j = 0; j < cols; j++)
                        {
                            if (i < src_rows && j < src_cols)
                            {
                                data[offset] = src_data[src_offset++];
                            }
                            else
                            {
                                data[offset] = (i == j) ? 1.0 : 0.0;
                            }
                            offset++;
                        }
                    }
                }
                else if (mat->item_count <= src_mat->item_count)
                {
                    memcpy(mat->data, src_mat->data, mat->data_length);
                }
                else
                {
                    memcpy(mat->data, src_mat->data, src_mat->data_length);
                    int diff = mat->data_length - src_mat->data_length;
                    memset(mat->data+src_mat->data_length, 0.0, diff);
                }
    	    }
		}

		RETVAL = mat;
	}
#line 364 "lib/OpenGL/Matrix.c"
	{
	    SV * RETVALSV;
	    RETVALSV = sv_newmortal();
	    sv_setref_pv(RETVALSV, "OpenGL::Matrix", (void*)RETVAL);
	    ST(0) = RETVALSV;
	}
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_new_identity); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_new_identity)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "Class, size");
    {
	GLsizei	size = (int)SvIV(ST(1))
;
	OpenGL__Matrix	RETVAL;
#line 213 "lib/OpenGL/Matrix.xs"
	{
	    OpenGL__Matrix mat = new_matrix(size, size);
		set_data_identity((GLfloat*)mat->data, size);

		RETVAL = mat;
	}
#line 393 "lib/OpenGL/Matrix.c"
	{
	    SV * RETVALSV;
	    RETVALSV = sv_newmortal();
	    sv_setref_pv(RETVALSV, "OpenGL::Matrix", (void*)RETVAL);
	    ST(0) = RETVALSV;
	}
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_element); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_element)
{
    dVAR; dXSARGS;
    if (items < 3)
       croak_xs_usage(cv,  "mat, col, row, ...");
    {
	OpenGL__Matrix	mat;
	GLsizei	col = (int)SvIV(ST(1))
;
	GLsizei	row = (int)SvIV(ST(2))
;
	GLfloat	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::element",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 231 "lib/OpenGL/Matrix.xs"
	{
        needs_2D(mat, "element");
        if (col >= mat->dimensions[0])
        {
            croak("OpenGL::Matrix::element col exceeds matrix width");
        }
        if (row >= mat->dimensions[1])
        {
            croak("OpenGL::Matrix::element row exceeds matrix height");
        }

	    GLfloat * data = mat->data;
	    int index = get_index(mat, col, row);

	    RETVAL = data[index];

	    if (items > 3)
	    {
	        data[index] = (GLfloat)SvNV(ST(3));
	    }
	}
#line 455 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHn((double)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_row); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_row)
{
    dVAR; dXSARGS;
    if (items < 2)
       croak_xs_usage(cv,  "mat, row, ...");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	OpenGL__Matrix	mat;
	GLsizei	row = (int)SvIV(ST(1))
;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::row",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 263 "lib/OpenGL/Matrix.xs"
	{
        needs_2D(mat, "row");
        if (row >= mat->dimensions[1])
        {
            croak("OpenGL::Matrix::element row exceeds matrix height");
        }

	    GLfloat * data = mat->data;
		int cols = mat->dimensions[0];
	    int index = row * cols;

		EXTEND(sp, cols);

        int i=0;
		for (; i<cols; i++)
		{
		    PUSHs(sv_2mortal(newSViv(data[index++])));
		}

	    if (items > 2)
	    {
            SV * sv = ST(2);
            fetch_arrayref(data+index, cols, sv, "row", "arrayref");
	    }
	}
#line 515 "lib/OpenGL/Matrix.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_OpenGL__Matrix_column); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_column)
{
    dVAR; dXSARGS;
    if (items < 2)
       croak_xs_usage(cv,  "mat, col, ...");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
	OpenGL__Matrix	mat;
	GLsizei	col = (int)SvIV(ST(1))
;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::column",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 297 "lib/OpenGL/Matrix.xs"
	{
        needs_2D(mat, "column");

		int cols = mat->dimensions[0];
        if (col >= cols)
        {
            croak("OpenGL::Matrix::element col exceeds matrix width");
        }

	    GLfloat * data = mat->data;
		int rows = mat->dimensions[1];
	    int index = col;

		EXTEND(sp, rows);

        int i=0;
		for (; i<rows; i++)
		{
		    PUSHs(sv_2mortal(newSViv(data[index])));
		    index += cols;
		}

	    if (items > 2)
	    {
	        GLfloat array[rows];
            SV * sv = ST(2);
            fetch_arrayref(array, rows, sv, "column", "arrayref");

            int offset = col;
            for (i=0; i<rows; i++)
            {
                data[offset] = array[i];
                offset += cols;
            }
	    }
	}
#line 585 "lib/OpenGL/Matrix.c"
	PUTBACK;
	return;
    }
}


XS_EUPXS(XS_OpenGL__Matrix_set_quaternion); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_set_quaternion)
{
    dVAR; dXSARGS;
    if (items < 2)
       croak_xs_usage(cv,  "mat, degrees, ...");
    {
	OpenGL__Matrix	mat;
	GLfloat	degrees = (float)SvNV(ST(1))
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::set_quaternion",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 341 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "set_quaternion");

    	GLfloat vec[3];
	    int count = items - 2;
	    if (count == 3)
	    {
	        int i=0;
	        for (; i<count; i++)
	        {
			    vec[i] = (GLfloat)SvNV(ST(i+2));
	        }
	    }
	    else if (count == 1)
	    {
	        SV * sv = ST(2);
	        fetch_arrayref(vec, 3, sv, "set_quaternion", "vec");
	    }
	    else
	    {
	        croak("OpenGL::Matrix::set_quaternion requires"
	            " a 3 element xyz vector in either an array or an arrayref");
	    }

	    double  a_2 = degrees * PI / 360.0;
	    double  sin_a_2 = sin(a_2);
	    double  x = vec[0] * sin_a_2;
	    double  y = vec[1] * sin_a_2;
	    double  z = vec[2] * sin_a_2;
	    GLfloat w = cos(a_2);

	    double  x2 = pow(x,2);
        double  y2 = pow(y,2);
        double  z2 = pow(z,2);

	    GLfloat * data = (GLfloat*)mat->data;
	    data[0]     = 1-2*y2-2*z2;
	    data[1]     = 2*x*y-2*w*z;
	    data[2]     = 2*x*z+2*w*y;
	    data[3]     = 0.0;
	    data[4]     = 2*x*y+2*w*z;
	    data[5]     = 1-2*x2-2*z2;
	    data[6]     = 2*y*z+2*w*x;
	    data[7]     = 0.0;
	    data[8]     = 2*x*z-2*w*y;
	    data[9]     = 2*y*z-2*w*x;
	    data[10]    = 1-2*x2-2*y2;
	    data[11]    = 0.0;
	    data[12]    = 0.0;
	    data[13]    = 0.0;
	    data[14]    = 0.0;
	    data[15]    = 1.0;

        RETVAL = 0;
	}
#line 674 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_set_frustum); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_set_frustum)
{
    dVAR; dXSARGS;
    if (items != 7)
       croak_xs_usage(cv,  "mat, left, right, top, bottom, n, f");
    {
	OpenGL__Matrix	mat;
	GLfloat	left = (float)SvNV(ST(1))
;
	GLfloat	right = (float)SvNV(ST(2))
;
	GLfloat	top = (float)SvNV(ST(3))
;
	GLfloat	bottom = (float)SvNV(ST(4))
;
	GLfloat	n = (float)SvNV(ST(5))
;
	GLfloat	f = (float)SvNV(ST(6))
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::set_frustum",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 411 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "set_frustum");

        set_data_frustum((GLfloat*)mat->data, left, right, top, bottom, n, f);

        RETVAL = 0;
	}
#line 726 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_set_perspective); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_set_perspective)
{
    dVAR; dXSARGS;
    if (items != 6)
       croak_xs_usage(cv,  "mat, width, height, n, f, fov");
    {
	OpenGL__Matrix	mat;
	GLfloat	width = (float)SvNV(ST(1))
;
	GLfloat	height = (float)SvNV(ST(2))
;
	GLfloat	n = (float)SvNV(ST(3))
;
	GLfloat	f = (float)SvNV(ST(4))
;
	GLfloat	fov = (float)SvNV(ST(5))
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::set_perspective",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 432 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "set_perspective");

        double aspect = width/height;
        double h_2 = n*tan(fov*PI/360);
        double w_2 = h_2*aspect;
        set_data_frustum((GLfloat*)mat->data, -w_2, w_2, -h_2, h_2, n, f);

        RETVAL = 0;
	}
#line 779 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_set_ortho); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_set_ortho)
{
    dVAR; dXSARGS;
    if (items != 7)
       croak_xs_usage(cv,  "mat, left, right, top, bottom, n, f");
    {
	OpenGL__Matrix	mat;
	GLfloat	left = (float)SvNV(ST(1))
;
	GLfloat	right = (float)SvNV(ST(2))
;
	GLfloat	top = (float)SvNV(ST(3))
;
	GLfloat	bottom = (float)SvNV(ST(4))
;
	GLfloat	n = (float)SvNV(ST(5))
;
	GLfloat	f = (float)SvNV(ST(6))
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::set_ortho",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 457 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "set_ortho");

        GLfloat width = right-left;
        GLfloat height = bottom-top;
        GLfloat depth = f-n;

		GLfloat * data = (GLfloat*)mat->data;
        data[0]     = 2/width;
        data[1]     = 0.0;
        data[2]     = 0.0;
        data[3]     = 0.0;
        data[4]     = 0.0;
        data[5]     = 2/height;
        data[6]     = 0.0;
        data[7]     = 0.0;
        data[8]     = 0.0;
        data[9]     = 0.0;
        data[10]    = -2/depth;
        data[11]    = 0.0;
        data[12]    = (right+left)/width;
        data[13]    = (bottom+top)/height;
        data[14]    = -(f+n)/depth;
        data[15]    = 1.0;

        RETVAL = 0;
	}
#line 851 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_set_lookat); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_set_lookat)
{
    dVAR; dXSARGS;
    if (items != 4)
       croak_xs_usage(cv,  "mat, sv_eye, sv_at, sv_up");
    {
	OpenGL__Matrix	mat;
	SV *	sv_eye = ST(1)
;
	SV *	sv_at = ST(2)
;
	SV *	sv_up = ST(3)
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::set_lookat",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 496 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "set_lookat");

    	GLfloat eye_vec[3];
    	GLfloat at_vec[3];
    	GLfloat up_vec[3];

        fetch_arrayref(eye_vec, 3, sv_eye, "set_lookat", "eye_vec");
        fetch_arrayref(at_vec, 3, sv_at, "set_lookat", "at_vec");
        fetch_arrayref(up_vec, 3, sv_up, "set_lookat", "up_vec");

	    GLfloat * data = (GLfloat*)mat->data;

        double  zaxis[] =
        {
          eye_vec[0] - at_vec[0],
          eye_vec[1] - at_vec[1],
          eye_vec[2] - at_vec[2]
        };

        if(!zaxis[0] && !zaxis[1] && !zaxis[2])
        {
          set_data_identity(data, 4);
        }
        else
        {
            double  z = vec_length(zaxis, 3);

            // Normalize distance
            zaxis[0] /= z;
            zaxis[1] /= z;
            zaxis[2] /= z;

            double  xaxis[] =
            {
                up_vec[1]*zaxis[2] - up_vec[2]*zaxis[1],
                up_vec[2]*zaxis[0] - up_vec[0]*zaxis[2],
                up_vec[0]*zaxis[1] - up_vec[1]*zaxis[0],
            };
            double  x = vec_length(xaxis, 3);

            if (x)
            {
                // Normalize xaxis
                xaxis[0] /= x;
                xaxis[1] /= x;
                xaxis[2] /= x;
            }
            else
            {
                xaxis[2] = 0;
            }

            double  yaxis[] =
            {
                zaxis[1]*xaxis[2] - zaxis[2]*xaxis[1],
                zaxis[2]*xaxis[0] - zaxis[0]*xaxis[2],
                zaxis[0]*xaxis[1] - zaxis[1]*xaxis[0]
            };
            double  y = vec_length(yaxis, 3);

            if (y)
            {
                // Normalize yaxis
                yaxis[0] /= y;
                yaxis[1] /= y;
                yaxis[2] /= y;
            }
            else
            {
                yaxis[0] = yaxis[1] = yaxis[2] = 0;
            }

            data[0]     = xaxis[0];
            data[1]     = yaxis[0];
            data[2]     = zaxis[0];
            data[3]     = 0.0;
            data[4]     = xaxis[1];
            data[5]     = yaxis[1];
            data[6]     = zaxis[1];
            data[7]     = 0.0;
            data[8]     = xaxis[2];
            data[9]     = yaxis[2];
            data[10]    = zaxis[2];
            data[11]    = 0.0;
            data[12]    = -(xaxis[0]*eye_vec[0] + xaxis[1]*eye_vec[1] + xaxis[2]*eye_vec[2]);
            data[13]    = -(yaxis[0]*eye_vec[0] + yaxis[1]*eye_vec[1] + yaxis[2]*eye_vec[2]);
            data[14]    = -(zaxis[0]*eye_vec[0] + zaxis[1]*eye_vec[1] + zaxis[2]*eye_vec[2]);
            data[15]    = 1.0;
        }

        RETVAL = 0;
	}
#line 983 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_translate); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_translate)
{
    dVAR; dXSARGS;
    if (items != 4)
       croak_xs_usage(cv,  "mat, x, y, z");
    {
	OpenGL__Matrix	mat;
	GLfloat	x = (float)SvNV(ST(1))
;
	GLfloat	y = (float)SvNV(ST(2))
;
	GLfloat	z = (float)SvNV(ST(3))
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::translate",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 601 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "translate");

        GLfloat * data = (GLfloat*)mat->data;
        int size = mat->dimensions[0];
        int offset = size * (size-1);

        data[offset++] += x;
        data[offset++] += y;
        data[offset] += z;

        RETVAL = 0;
	}
#line 1035 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_scale); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_scale)
{
    dVAR; dXSARGS;
    if (items != 4)
       croak_xs_usage(cv,  "mat, x, y, z");
    {
	OpenGL__Matrix	mat;
	GLfloat	x = (float)SvNV(ST(1))
;
	GLfloat	y = (float)SvNV(ST(2))
;
	GLfloat	z = (float)SvNV(ST(3))
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::scale",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 626 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "scale");

        GLfloat * data = (GLfloat*)mat->data;
        int size = mat->dimensions[0];
        int offset = 0;

        data[offset] *= x;
        offset += size+1;
        data[offset] *= y;
        offset += size+1;
        data[offset] *= z;

        RETVAL = 0;
	}
#line 1089 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_rotate_x); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_rotate_x)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "mat, degrees");
    {
	OpenGL__Matrix	mat;
	GLfloat	degrees = (float)SvNV(ST(1))
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::rotate_x",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 651 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "rotate_x");

        GLfloat *   data = (GLfloat*)mat->data;
        double      a = degrees * PI / 180.0;
        double      y = sin(a);
        double      x = cos(a);

        GLfloat     row1[] = {data[4], data[5], data[6], data[7]};
        GLfloat     row2[] = {data[8], data[9], data[10], data[11]};

        data[4]     = x*row1[0] + y*row2[0];
        data[5]     = x*row1[1] + y*row2[1];
        data[6]     = x*row1[2] + y*row2[2];
        data[7]     = x*row1[3] + y*row2[3];
        data[8]     = x*row2[0] - y*row1[0];
        data[9]     = x*row2[1] - y*row1[1];
        data[10]    = x*row2[2] - y*row1[2];
        data[11]    = x*row2[3] - y*row1[3];

		RETVAL = 0;
	}
#line 1146 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_rotate_y); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_rotate_y)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "mat, degrees");
    {
	OpenGL__Matrix	mat;
	GLfloat	degrees = (float)SvNV(ST(1))
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::rotate_y",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 683 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "rotate_y");

        GLfloat *   data = (GLfloat*)mat->data;
        double      a = degrees * PI / 180.0;
        double      y = sin(a);
        double      x = cos(a);

        GLfloat     row0[] = {data[0], data[1], data[2], data[3]};
        GLfloat     row2[] = {data[8], data[9], data[10], data[11]};

        data[0]     = x*row0[0] - y*row2[0];
        data[1]     = x*row0[1] - y*row2[1];
        data[2]     = x*row0[2] - y*row2[2];
        data[3]     = x*row0[3] - y*row2[3];
        data[8]     = x*row2[0] + y*row0[0];
        data[9]     = x*row2[1] + y*row0[1];
        data[10]    = x*row2[2] + y*row0[2];
        data[11]    = x*row2[3] + y*row0[3];

		RETVAL = 0;
	}
#line 1203 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_rotate_z); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_rotate_z)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "mat, degrees");
    {
	OpenGL__Matrix	mat;
	GLfloat	degrees = (float)SvNV(ST(1))
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::rotate_z",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 715 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "rotate_z");

        GLfloat *   data = (GLfloat*)mat->data;
        double      a = degrees * PI / 180.0;
        double      y = sin(a);
        double      x = cos(a);

        GLfloat     row0[] = {data[0], data[1], data[2], data[3]};
        GLfloat     row1[] = {data[4], data[5], data[6], data[7]};

        data[0]     = x*row0[0] + y*row1[0];
        data[1]     = x*row0[1] + y*row1[1];
        data[2]     = x*row0[2] + y*row1[2];
        data[3]     = x*row0[3] + y*row1[3];
        data[4]     = x*row1[0] - y*row0[0];
        data[5]     = x*row1[1] - y*row0[1];
        data[6]     = x*row1[2] - y*row0[2];
        data[7]     = x*row1[3] - y*row0[3];

		RETVAL = 0;
	}
#line 1260 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_transpose); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_transpose)
{
    dVAR; dXSARGS;
    if (items != 1)
       croak_xs_usage(cv,  "mat");
    {
	OpenGL__Matrix	mat;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::transpose",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 746 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "transpose");

        GLfloat *   data = (GLfloat*)mat->data;
        GLfloat     m1  = data[1];
        GLfloat     m2  = data[2];
        GLfloat     m3  = data[3];
        GLfloat     m6  = data[6];
        GLfloat     m7  = data[7];
        GLfloat     m11 = data[11];

        data[1]     = data[4];
        data[2]     = data[8];
        data[3]     = data[12];
        data[4]     = m1;
        data[6]     = data[9];
        data[7]     = data[13];
        data[8]     = m2;
        data[9]     = m6;
        data[11]    = data[14];
        data[12]    = m3;
        data[13]    = m7;
        data[14]    = m11;

		RETVAL = 0;
	}
#line 1319 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_new_product); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_new_product)
{
    dVAR; dXSARGS;
    if (items != 3)
       croak_xs_usage(cv,  "Class, mat1, mat2");
    {
	OpenGL__Matrix	mat1;
	OpenGL__Matrix	mat2;
	OpenGL__Matrix	RETVAL;

	if (SvROK(ST(1)) && sv_derived_from(ST(1), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(1)));
	    mat1 = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(1)) ? "" : SvOK(ST(1)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::new_product",
			"mat1", "OpenGL::Matrix",
			refstr, ST(1)
		);
	}
;

	if (SvROK(ST(2)) && sv_derived_from(ST(2), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(2)));
	    mat2 = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(2)) ? "" : SvOK(ST(2)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::new_product",
			"mat2", "OpenGL::Matrix",
			refstr, ST(2)
		);
	}
;
#line 782 "lib/OpenGL/Matrix.xs"
	{
        needs_4x4(mat1, "new_product mat1");
        needs_4x4(mat2, "new_product mat2");
        OpenGL__Matrix mat = new_matrix(4, 4);

        GLfloat *   m1 = (GLfloat*)mat1->data;
        GLfloat *   m2 = (GLfloat*)mat2->data;
        GLfloat *   data = (GLfloat*)mat->data;

        data[0]  = m2[0]*m1[0]  + m2[1]*m1[4]  + m2[2]*m1[8]   + m2[3]*m1[12];
        data[1]  = m2[0]*m1[1]  + m2[1]*m1[5]  + m2[2]*m1[9]   + m2[3]*m1[13];
        data[2]  = m2[0]*m1[2]  + m2[1]*m1[6]  + m2[2]*m1[10]  + m2[3]*m1[14];
        data[3]  = m2[0]*m1[3]  + m2[1]*m1[7]  + m2[2]*m1[11]  + m2[3]*m1[15];
        data[4]  = m2[4]*m1[0]  + m2[5]*m1[4]  + m2[6]*m1[8]   + m2[7]*m1[12];
        data[5]  = m2[4]*m1[1]  + m2[5]*m1[5]  + m2[6]*m1[9]   + m2[7]*m1[13];
        data[6]  = m2[4]*m1[2]  + m2[5]*m1[6]  + m2[6]*m1[10]  + m2[7]*m1[14];
        data[7]  = m2[4]*m1[3]  + m2[5]*m1[7]  + m2[6]*m1[11]  + m2[7]*m1[15];
        data[8]  = m2[8]*m1[0]  + m2[9]*m1[4]  + m2[10]*m1[8]  + m2[11]*m1[12];
        data[9]  = m2[8]*m1[1]  + m2[9]*m1[5]  + m2[10]*m1[9]  + m2[11]*m1[13];
        data[10] = m2[8]*m1[2]  + m2[9]*m1[6]  + m2[10]*m1[10] + m2[11]*m1[14];
        data[11] = m2[8]*m1[3]  + m2[9]*m1[7]  + m2[10]*m1[11] + m2[11]*m1[15];
        data[12] = m2[12]*m1[0] + m2[13]*m1[4] + m2[14]*m1[8]  + m2[15]*m1[12];
        data[13] = m2[12]*m1[1] + m2[13]*m1[5] + m2[14]*m1[9]  + m2[15]*m1[13];
        data[14] = m2[12]*m1[2] + m2[13]*m1[6] + m2[14]*m1[10] + m2[15]*m1[14];
        data[15] = m2[12]*m1[3] + m2[13]*m1[7] + m2[14]*m1[11] + m2[15]*m1[15];

		RETVAL = mat;
	}
#line 1394 "lib/OpenGL/Matrix.c"
	{
	    SV * RETVALSV;
	    RETVALSV = sv_newmortal();
	    sv_setref_pv(RETVALSV, "OpenGL::Matrix", (void*)RETVAL);
	    ST(0) = RETVALSV;
	}
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_dot_product); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_dot_product)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "mat1, mat2");
    {
	OpenGL__Matrix	mat1;
	OpenGL__Matrix	mat2;
	GLfloat	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat1 = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::dot_product",
			"mat1", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;

	if (SvROK(ST(1)) && sv_derived_from(ST(1), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(1)));
	    mat2 = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(1)) ? "" : SvOK(ST(1)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::dot_product",
			"mat2", "OpenGL::Matrix",
			refstr, ST(1)
		);
	}
;
#line 820 "lib/OpenGL/Matrix.xs"
	{
	    if (mat1->item_count != mat2->item_count)
	    {
	        croak("OpenGL::Matrix::dot_product requires an equal size matrix");
	    }

        GLfloat *   m1 = (GLfloat*)mat1->data;
        GLfloat *   m2 = (GLfloat*)mat2->data;

        GLfloat total = 0;
        int i=0;
        for (; i<mat1->item_count; i++)
        {
            total += m1[i] * m2[i];
        }

		RETVAL = total;
	}
#line 1464 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHn((double)RETVAL);
    }
    XSRETURN(1);
}


XS_EUPXS(XS_OpenGL__Matrix_invert); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_OpenGL__Matrix_invert)
{
    dVAR; dXSARGS;
    if (items != 2)
       croak_xs_usage(cv,  "mat, transpose");
    {
	OpenGL__Matrix	mat;
	GLboolean	transpose = (bool)SvTRUE(ST(1))
;
	GLint	RETVAL;
	dXSTARG;

	if (SvROK(ST(0)) && sv_derived_from(ST(0), "OpenGL::Matrix")) {
	    IV tmp = SvIV((SV*)SvRV(ST(0)));
	    mat = INT2PTR(OpenGL__Matrix,tmp);
	}
	else {
		const char* refstr = SvROK(ST(0)) ? "" : SvOK(ST(0)) ? "scalar " : "undef";
	    Perl_croak_nocontext("%s: Expected %s to be of type %s; got %s%" SVf " instead",
			"OpenGL::Matrix::invert",
			"mat", "OpenGL::Matrix",
			refstr, ST(0)
		);
	}
;
#line 848 "lib/OpenGL/Matrix.xs"
	{
	    needs_4x4(mat, "invert");

        GLfloat *   data = (GLfloat*)mat->data;
        double      m[] =
        {
            data[0]*data[5]   - data[1]*data[4],
            data[0]*data[6]   - data[2]*data[4],
            data[0]*data[7]   - data[3]*data[4],
            data[1]*data[6]   - data[2]*data[5],
            data[1]*data[7]   - data[3]*data[5],
            data[2]*data[7]   - data[3]*data[6],
            data[8]*data[13]  - data[9]*data[12],
            data[8]*data[14]  - data[10]*data[12],
            data[8]*data[15]  - data[11]*data[12],
            data[9]*data[14]  - data[10]*data[13],
            data[9]*data[15]  - data[11]*data[13],
            data[10]*data[15] - data[11]*data[14],
        };

        double det = m[0]*m[11] - m[1]*m[10] + m[2]*m[9] +
            m[3]*m[8] - m[4]*m[7] + m[5]*m[6];

        if (fabs(det) < FLT_EPSILON)
        {
            // Matrix not invertable
            RETVAL = -1;
        }
        else
        {
            double      d = 1.0/det;
            GLfloat     a[16];
            memcpy(a, data, sizeof(a));

            data[0]  = d * (a[5]*m[11]  - a[6]*m[10] + a[7]*m[9]);
            data[5]  = d * (a[0]*m[11]  - a[2]*m[8]  + a[3]*m[7]);
            data[10] = d * (a[12]*m[4]  - a[13]*m[2] + a[15]*m[0]);
            data[15] = d * (a[8]*m[3]   - a[9]*m[1]  + a[10]*m[0]);

            if (transpose)
            {
                data[4]  = d * (-a[1]*m[11] + a[2]*m[10] - a[3]*m[9]);
                data[8]  = d * (a[13]*m[5]  - a[14]*m[4] + a[15]*m[3]);
                data[12] = d * (-a[9]*m[5]  + a[10]*m[4] - a[11]*m[3]);
                data[1]  = d * (-a[4]*m[11] + a[6]*m[8]  - a[7]*m[7]);
                data[9]  = d * (-a[12]*m[5] + a[14]*m[2] - a[15]*m[1]);
                data[13] = d * (a[8]*m[5]   - a[10]*m[2] + a[11]*m[1]);
                data[2]  = d * (a[4]*m[10]  - a[5]*m[8]  + a[7]*m[6]);
                data[6]  = d * (-a[0]*m[10] + a[1]*m[8]  - a[3]*m[6]);
                data[14] = d * (-a[8]*m[4]  + a[9]*m[2]  - a[11]*m[0]);
                data[3]  = d * (-a[4]*m[9]  + a[5]*m[7]  - a[6]*m[6]);
                data[7]  = d * (a[0]*m[9]   - a[1]*m[7]  + a[2]*m[6]);
                data[11] = d * (-a[12]*m[3] + a[13]*m[1] - a[14]*m[0]);
            }
            else
            {
                data[1]  = d * (-a[1]*m[11] + a[2]*m[10] - a[3]*m[9]);
                data[2]  = d * (a[13]*m[5]  - a[14]*m[4] + a[15]*m[3]);
                data[3]  = d * (-a[9]*m[5]  + a[10]*m[4] - a[11]*m[3]);
                data[4]  = d * (-a[4]*m[11] + a[6]*m[8]  - a[7]*m[7]);
                data[6]  = d * (-a[12]*m[5] + a[14]*m[2] - a[15]*m[1]);
                data[7]  = d * (a[8]*m[5]   - a[10]*m[2] + a[11]*m[1]);
                data[8]  = d * (a[4]*m[10]  - a[5]*m[8]  + a[7]*m[6]);
                data[9]  = d * (-a[0]*m[10] + a[1]*m[8]  - a[3]*m[6]);
                data[11] = d * (-a[8]*m[4]  + a[9]*m[2]  - a[11]*m[0]);
                data[12] = d * (-a[4]*m[9]  + a[5]*m[7]  - a[6]*m[6]);
                data[13] = d * (a[0]*m[9]   - a[1]*m[7]  + a[2]*m[6]);
                data[14] = d * (-a[12]*m[3] + a[13]*m[1] - a[14]*m[0]);
            }

            RETVAL = 0;
        }
	}
#line 1572 "lib/OpenGL/Matrix.c"
	XSprePUSH;
	PUSHi((IV)RETVAL);
    }
    XSRETURN(1);
}

#ifdef __cplusplus
extern "C" {
#endif
XS_EXTERNAL(boot_OpenGL__Matrix); /* prototype to pass -Wmissing-prototypes */
XS_EXTERNAL(boot_OpenGL__Matrix)
{
#if PERL_VERSION_LE(5, 21, 5)
    dVAR; dXSARGS;
#else
    dVAR; dXSBOOTARGSXSAPIVERCHK;
#endif
#if PERL_VERSION_LE(5, 8, 999) /* PERL_VERSION_LT is 5.33+ */
    char* file = __FILE__;
#else
    const char* file = __FILE__;
#endif

    PERL_UNUSED_VAR(file);

    PERL_UNUSED_VAR(cv); /* -W */
    PERL_UNUSED_VAR(items); /* -W */
#if PERL_VERSION_LE(5, 21, 5)
    XS_VERSION_BOOTCHECK;
#  ifdef XS_APIVERSION_BOOTCHECK
    XS_APIVERSION_BOOTCHECK;
#  endif
#endif

        newXS_deffile("OpenGL::Matrix::new", XS_OpenGL__Matrix_new);
        newXS_deffile("OpenGL::Matrix::new_identity", XS_OpenGL__Matrix_new_identity);
        newXS_deffile("OpenGL::Matrix::element", XS_OpenGL__Matrix_element);
        newXS_deffile("OpenGL::Matrix::row", XS_OpenGL__Matrix_row);
        newXS_deffile("OpenGL::Matrix::column", XS_OpenGL__Matrix_column);
        newXS_deffile("OpenGL::Matrix::set_quaternion", XS_OpenGL__Matrix_set_quaternion);
        newXS_deffile("OpenGL::Matrix::set_frustum", XS_OpenGL__Matrix_set_frustum);
        newXS_deffile("OpenGL::Matrix::set_perspective", XS_OpenGL__Matrix_set_perspective);
        newXS_deffile("OpenGL::Matrix::set_ortho", XS_OpenGL__Matrix_set_ortho);
        newXS_deffile("OpenGL::Matrix::set_lookat", XS_OpenGL__Matrix_set_lookat);
        newXS_deffile("OpenGL::Matrix::translate", XS_OpenGL__Matrix_translate);
        newXS_deffile("OpenGL::Matrix::scale", XS_OpenGL__Matrix_scale);
        newXS_deffile("OpenGL::Matrix::rotate_x", XS_OpenGL__Matrix_rotate_x);
        newXS_deffile("OpenGL::Matrix::rotate_y", XS_OpenGL__Matrix_rotate_y);
        newXS_deffile("OpenGL::Matrix::rotate_z", XS_OpenGL__Matrix_rotate_z);
        newXS_deffile("OpenGL::Matrix::transpose", XS_OpenGL__Matrix_transpose);
        newXS_deffile("OpenGL::Matrix::new_product", XS_OpenGL__Matrix_new_product);
        newXS_deffile("OpenGL::Matrix::dot_product", XS_OpenGL__Matrix_dot_product);
        newXS_deffile("OpenGL::Matrix::invert", XS_OpenGL__Matrix_invert);
#if PERL_VERSION_LE(5, 21, 5)
#  if PERL_VERSION_GE(5, 9, 0)
    if (PL_unitcheckav)
        call_list(PL_scopestack_ix, PL_unitcheckav);
#  endif
    XSRETURN_YES;
#else
    Perl_xs_boot_epilog(aTHX_ ax);
#endif
}

#ifdef __cplusplus
}
#endif
