c – Why is this projection matrix not working?

I’m completely new to CG, I have been given an exercise in college to write ac program to display a bunch of 3d points as an isometric wireframe (not allowed to use engines). I have made a projection matrix but I’m not getting the results I was expecting, which is making me doubt my own understanding of it.

My code:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#define IMG_W 500
#define IMG_H 500
# ifndef BUFFER_SIZE
#  define BUFFER_SIZE 42
# endif
# ifndef NUM_OF_FD
#  define NUM_OF_FD 256
# endif

typedef struct s_point
{
    double  x;
    double  y;
    double  z;
}   t_point;

typedef struct s_pmatrix
{
    double  m[4][4];
}   t_pmatrix;

/*
    Locates the first occurrence of 'c' in the string pointed to by 's'.
    The terminating null character is considered to be part of the string,
    therefore if 'c' is '', locates the terminating ''
*/
char    *ft_strchr(const char *s, int c)
{
    char    chr;

    chr = (char)c;
    if (s == NULL)
        return (NULL);
    while (*s != '')
    {
        if (*s == chr)
            return ((char *)s);
        s++;
    }
    return (NULL);
}

size_t  ft_strlen(const char *s)
{
    size_t  i;

    if (!s)
        return (0);
    i = 0;
    while (s[i])
    {
        i++;
    }
    return (i);
}

char    *ft_substr(char const *s, unsigned int start, size_t len)
{
    char    *str;

    if (!s)
        return (NULL);
    if (len > ft_strlen(s))
        len = ft_strlen(s);
    if (start > ft_strlen(s))
        len = 0;
    str = malloc(sizeof(char) * (len + 1));
    if (!str)
        return (NULL);
    str = memcpy(str, &s[start], len);
    str[len] = '';
    return (str);
}

/*
    Duplicate string s1 from index start utill index end.
    Memory for the new string is obtained with malloc, and can be freed with free.
*/
char    *ft_strldup(char *s1, int start, int end)
{
    char    *ptr;
    int     i;

    i = 0;
    ptr = malloc(end - start + 1);
    if (!ptr)
        return (NULL);
    while (start < end)
        ptr[i++] = s1[start++];
    ptr[i] = 0;
    return (ptr);
}

/*
    Allocates (with malloc) and returns a new string,
    result of the concatenation of s1 and s2
    S1 is freed
*/
char    *ft_strjoinfree(char *s1, char *s2)
{
    int     i;
    char    *str;
    char    *hld;
    size_t  size;

    if (!s1 && !s2)
        return (NULL);
    i = 0;
    size = (ft_strlen(s1) + ft_strlen(s2) + 1);
    str = malloc(sizeof(char) * size);
    if (!str)
        return (NULL);
    hld = s1;
    if (s1)
        while (*s1)
            str[i++] = *s1++;
    if (s2)
        while (*s2)
            str[i++] = *s2++;
    str[i] = '';
    if (hld)
        free(hld);
    return (str);
}

/**
 * @brief Splits backup on 'n' into line wich is returned
 *      and temp wich is stored into backup[fd] after 
 *      it (backup[fd]) has been free'd
 * 
 * @param backup backup from get_next_line
 * @param fd file descriptor
 * @return char* 
 */
char    *returner(char **backup, int fd)
{
    int     index;
    char    *line;
    char    *temp;

    index = ft_strchr(backup[fd], 'n') - backup[fd] + 1;
    line = ft_strldup(backup[fd], 0, index);
    temp = ft_strldup(backup[fd], index, ft_strlen(backup[fd]));
    free(backup[fd]);
    backup[fd] = temp;
    return (line);
}


char    *cycle(char **backup, int fd, char *buf)
{
    int size;

    size = read(fd, buf, BUFFER_SIZE);
    buf[size] = '';
    while (size > 0)
    {
        backup[fd] = ft_strjoinfree(backup[fd], buf);
        if (ft_strchr(backup[fd], 'n') != NULL)
            return (returner(backup, fd));
        size = read(fd, buf, BUFFER_SIZE);
        buf[size] = '';
    }
    return (NULL);
}


/**
 * @brief If backup doesn't have a 'n' from a previous call
 *      cycle through the do while untill you get a 'n' or a null.
 *      if it does have a 'n': split backup on 'n' 
 *      into line wich is returned (and temp wich is stored 
 *      back into backup[fd] after it has been free'd)
 *      If it is a null and backup[fd] is not '' return what you
 *      have else return NULL 
 * 
 * @param fd file descriptor
 * @return char* 
 */
char    *get_next_line(int fd)
{
    char        buf[BUFFER_SIZE + 1];
    static char *backup[NUM_OF_FD];
    char        *line;

    if (fd < 0 || fd > NUM_OF_FD)
        return (NULL);
    line = NULL;
    if (ft_strchr(backup[fd], 'n') == NULL)
    {
        line = cycle(backup, fd, buf);
        if (line != NULL)
            return (line);
    }
    else
        return (returner(backup, fd));
    if (backup[fd] != NULL && *backup[fd] != '')
    {
        line = backup[fd];
        backup[fd] = NULL;
        return (line);
    }
    free(line);
    free(backup[fd]);
    backup[fd] = NULL;
    return (NULL);
}


static int  count(char const *s, char c)
{
    int count;

    count = 0;
    while (*s)
    {
        if (*s == c)
            s++;
        else
        {
            count++;
            while (*s && *s != c)
                s++;
        }
    }
    return (count);
}

//Not static so it can be used to destruct
//the array after split is used
char    **ft_delete_split_arr(char **arr)
{
    int i;

    i = 0;
    while (arr[i])
    {
        free(arr[i]);
        i++;
    }
    free(arr);
    return (NULL);
}

static int  getnextc(char const *s, char c)
{
    int i;

    i = 0;
    while (s[i] != '' && s[i] != c)
        i++;
    return (i);
}

char    **ft_split(char const *s, char c)
{
    char    **arr;
    int     end;
    int     j;

    if (!s)
        return (NULL);
    arr = calloc((count(s, c) + 1), sizeof(arr));
    if (!arr)
        return (NULL);
    j = 0;
    while (*s)
    {
        if (*s == c)
            s++;
        else
        {
            end = getnextc(s, c);
            arr[j] = ft_substr(s, 0, end);
            if (!arr[j++])
                return (ft_delete_split_arr(arr));
            s += end;
        }
    }
    return (arr);
}


/**
 * @brief If inputed j (aka x) is zero it assumes 
 * it is the first time running and counts
 * how many strings are in array arr.
 * 
 * However if j is not zero it again counts how many
 * strings are in array arr and compares it with j
 * If different it quits the program
 */
void    check_items_inline(char *line, char **arr, int *j)
{
    int         j2;

    if (*j == 0)
        while (arr[*j] != 0)
            (*j)++;
    else
    {
        j2 = 0;
        while (arr[j2] != 0)
            j2++;
        if (*j != j2)
        {
            ft_delete_split_arr(arr);
            free(line);
            exit(3);
        }
    }
}

void    set_world_matrix(t_pmatrix *wc)
{
    wc->m[0][0] = 1;
    wc->m[1][1] = 1;
    wc->m[2][2] = 1;
    wc->m[3][3] = 1;
    //wc->m[3][1] = -10; 
    //wc->m[3][2] = -20; 
}

void    set_projection_matrix(t_pmatrix *pm, double n, double f, double fov)
{
    double scale;
    
    scale = 1.0 / tan(fov * 0.5 * M_PI / 180.0);
    pm->m[0][0] = scale;
    pm->m[1][1] = scale;
    pm->m[2][2] = (-f) / (f - n);
    pm->m[3][2] = (-f * n) / (f - n);
    pm->m[2][3] = -1.0;
    pm->m[3][3] = 0.0;
}

/**
 * @brief normalizes a 1*3 matrix/vector to 1*4 and then 
 * multiplies it by a 4*4 matrix
 * 
 *
 * @param i input
 * @param o output
 * @param m matrix to multiply with
 */
void    multiply_matrix_vector(t_point *i, t_point *o, t_pmatrix *m)
{
    double w;

    w = 1;
    o->x = i->x * m->m[0][0] + i->y * m->m[1][0] + i->z * m->m[2][0] + w * m->m[3][0];
    o->y = i->x * m->m[0][1] + i->y * m->m[1][1] + i->z * m->m[2][1] + w * m->m[3][1];
    o->z = i->x * m->m[0][2] + i->y * m->m[1][2] + i->z * m->m[2][2] + w * m->m[3][2];
    w = i->x * m->m[0][3] + i->y * m->m[1][3] + i->z * m->m[2][3] + w * m->m[3][3];
    if (w != 0.0f)
    {
        o->x /= w; 
        o->y /= w; 
        o->z /= w;
    }
}

int main(int argc, char **argv)
{
    int     fd;
    char    *line;
    char    **arr;
    char    *pos;
    int     x;
    int     y;
    int     tx;
    int     ty;

    t_pmatrix pm;
    t_pmatrix wc;
    

    //open file
    if (argc != 2)
        exit(1);
    fd = open(argv[1], O_RDONLY);
    if (fd < 0)
        exit(2);

    //check file and get sizes
    line = get_next_line(fd);
    x = 0;
    y = 0;
    while (line != NULL)
    {
        printf("%s", line);
        pos = ft_strchr(line, 'n');
        *pos="";
        arr = ft_split(line, ' ');
        check_items_inline(line, arr, &x);
        ft_delete_split_arr(arr);
        free(line);
        line = NULL;
        line = get_next_line(fd);
        y++;
    }
    close(fd);

    //input vector array
    t_point *i = malloc(sizeof(t_point) * (x * y));
    t_point *b = malloc(sizeof(t_point) * (x * y));
    //output vector array
    t_point *o = malloc(sizeof(t_point) * (x * y));
    
    
    double fov = 90; 
    double n = 0.1; 
    double f = 100; 

    set_world_matrix(&wc);
    set_projection_matrix(&pm, n, f, fov);

    fd = open(argv[1], O_RDONLY);
    if (fd < 0)
        exit(2);

    tx = x;
    ty = y;
    y = 0;
    line = get_next_line(fd);
    while (line != NULL)
    {
        printf("%s", line);
        pos = ft_strchr(line, 'n');
        *pos="";
        arr = ft_split(line, ' ');
        x = 0;
        while (x < tx)
        {
            i[y * tx + x].x = x + 5;
            i[y * tx + x].y = y + 5;
            i[y * tx + x].z = (atoi(arr[x]));
            x++;
        }
        ft_delete_split_arr(arr);
        free(line);
        line = NULL;
        line = get_next_line(fd);
        y++;
    }
    close(fd);

    //initialize arguments;

    int xp;
    int yp;
    
    for (int k = 0; k < ty; k++)
    {
        for (int l = 0; l < tx; l++)
        {
            printf("x:%lfny:%lfnz:%lfn===n", i[k * tx + l].x, i[k * tx + l].y, i[k * tx + l].z);
            multiply_matrix_vector(&i[k * tx + l], &b[k * tx + l], &wc);
            multiply_matrix_vector(&b[k * tx + l], &o[k * tx + l], &pm);
            //if (o[k * tx + l].x < -1 || o[k * tx + l].x > 1 || o[k * tx + l].y < -1 || o[k * tx + l].y > 1) 
            //  continue;
            xp = (int)((o[k * tx + l].x + 1) * 0.5 * IMG_W); 
            yp = (int)((1 - (o[k * tx + l].y + 1) * 0.5) * IMG_H); 
            printf("x:%dny:%dn===n", xp, yp);

        }
    }
}

The matrix I’m using:

The input file:

0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
0  0 10 10  0  0 10 10  0  0  0 10 10 10 10 10  0  0  0
0  0 10 10  0  0 10 10  0  0  0  0  0  0  0 10 10  0  0
0  0 10 10  0  0 10 10  0  0  0  0  0  0  0 10 10  0  0
0  0 10 10 10 10 10 10  0  0  0  0 10 10 10 10  0  0  0
0  0  0 10 10 10 10 10  0  0  0 10 10  0  0  0  0  0  0
0  0  0  0  0  0 10 10  0  0  0 10 10  0  0  0  0  0  0
0  0  0  0  0  0 10 10  0  0  0 10 10 10 10 10 10  0  0
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

Where:

  • “The horizontal position corresponds to its axis.” X
  • “The vertical position corresponds to its ordinate.” Y
  • “The value corresponds to its altitude” Z

That said I assumed that the projection matrix was sideways but the values ​​I’m getting don’t make any sense. How is my world oriented compared to my camera?

I need help understanding what I’m seeing.

And if anything is wrong what do I have to fix?

Leave a Comment