Acme-MITHALDU-BleedingOpenGL

 view release on metacpan or  search on metacpan

pogl_matrix.xs  view on Meta::CPAN

/*  Last saved: Sun 06 Sep 2009 02:10:28 PM */

/*  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.
 */

/* OpenGL::Matrix */
#define IN_POGL_MATRIX_XS

#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);
	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_frustrum(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;

pogl_matrix.xs  view on Meta::CPAN

    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};


MODULE = Acme::MITHALDU::BleedingOpenGL::Matrix		PACKAGE = Acme::MITHALDU::BleedingOpenGL::Matrix

#ifdef IN_POGL_MATRIX_XS

#//# $mat = OpenGL::Matrix->new($cols, $rows[, (OGM)matrix]);
#//- Constructor for 2D Matrix OGM - populated with matrix if provided
OpenGL::Matrix
new(Class, cols, rows, ...)
	GLsizei	cols
	GLsizei	rows
	CODE:
	{
	    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;
	}
	OUTPUT:
		RETVAL

#//# $mat = OpenGL::Matrix->new_identity($size);
#//- Constructor for 2D Identity Matrix OGM
OpenGL::Matrix
new_identity(Class, size)
	GLsizei	size
	CODE:
	{
	    OpenGL__Matrix mat = new_matrix(size, size);
		set_data_identity((GLfloat*)mat->data, size);

		RETVAL = mat;
	}
	OUTPUT:
		RETVAL

#//# $value = $mat->element($col, $row[, $new_value]);
#//- Get/Set the value of a 2D Matrix element
#//- When setting a new_value, returns the previous value
GLfloat
element(mat, col, row, ...)
    OpenGL::Matrix	mat
    GLsizei	col
    GLsizei	row
	CODE:
	{
        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));
	    }
	}
	OUTPUT:
		RETVAL

#//# @row = $mat->row($row[, $arrayref]);
#//- Get/Set the value of a 2D Matrix row
#//- When setting new values, returns the previous row values

pogl_matrix.xs  view on Meta::CPAN

        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;
	}
	OUTPUT:
	    RETVAL

#//# $status = $mat->set_lookat($eye_vec, $at_vec, $up_vec);
#//- Set 4x4 LookAt Matrix; returns 0 if successful
GLint
set_lookat(mat, sv_eye, sv_at, sv_up)
	OpenGL::Matrix	mat
	SV * sv_eye
	SV * sv_at
	SV * sv_up
	CODE:
	{
	    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;
	}
	OUTPUT:
	    RETVAL

#//# $status = $mat->translate($x, $y, $z);
#//- Translate a 4x4 Matrix; returns 0 if successful
GLint
translate(mat, x, y, z)
	OpenGL::Matrix	mat
	GLfloat         x
	GLfloat         y
	GLfloat         z
	CODE:
	{
	    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;
	}
	OUTPUT:
	    RETVAL



( run in 1.434 second using v1.01-cache-2.11-cpan-99c4e6809bf )