Revision: 76973
          http://sourceforge.net/p/brlcad/code/76973
Author:   starseeker
Date:     2020-08-27 12:04:42 +0000 (Thu, 27 Aug 2020)
Log Message:
-----------
Now that we're in a branch, add the subset of the renderer code used in the 
test.

Modified Paths:
--------------
    brlcad/branches/swrast/src/libdm/tests/CMakeLists.txt

Added Paths:
-----------
    brlcad/branches/swrast/src/libdm/tests/renderer/
    brlcad/branches/swrast/src/libdm/tests/renderer/LICENSE
    brlcad/branches/swrast/src/libdm/tests/renderer/api.h
    brlcad/branches/swrast/src/libdm/tests/renderer/camera.c
    brlcad/branches/swrast/src/libdm/tests/renderer/camera.h
    brlcad/branches/swrast/src/libdm/tests/renderer/darray.c
    brlcad/branches/swrast/src/libdm/tests/renderer/darray.h
    brlcad/branches/swrast/src/libdm/tests/renderer/draw2d.c
    brlcad/branches/swrast/src/libdm/tests/renderer/draw2d.h
    brlcad/branches/swrast/src/libdm/tests/renderer/graphics.c
    brlcad/branches/swrast/src/libdm/tests/renderer/graphics.h
    brlcad/branches/swrast/src/libdm/tests/renderer/image.c
    brlcad/branches/swrast/src/libdm/tests/renderer/image.h
    brlcad/branches/swrast/src/libdm/tests/renderer/macro.h
    brlcad/branches/swrast/src/libdm/tests/renderer/maths.c
    brlcad/branches/swrast/src/libdm/tests/renderer/maths.h
    brlcad/branches/swrast/src/libdm/tests/renderer/mesh.c
    brlcad/branches/swrast/src/libdm/tests/renderer/mesh.h
    brlcad/branches/swrast/src/libdm/tests/renderer/platform.h
    brlcad/branches/swrast/src/libdm/tests/renderer/private.c
    brlcad/branches/swrast/src/libdm/tests/renderer/private.h
    brlcad/branches/swrast/src/libdm/tests/renderer/scene.c
    brlcad/branches/swrast/src/libdm/tests/renderer/scene.h
    brlcad/branches/swrast/src/libdm/tests/renderer/scenes/
    brlcad/branches/swrast/src/libdm/tests/renderer/scenes/blinn_scenes.c
    brlcad/branches/swrast/src/libdm/tests/renderer/scenes/blinn_shader.c
    brlcad/branches/swrast/src/libdm/tests/renderer/scenes/blinn_shader.h
    brlcad/branches/swrast/src/libdm/tests/renderer/scenes/cache_helper.c
    brlcad/branches/swrast/src/libdm/tests/renderer/scenes/scene_helper.c
    brlcad/branches/swrast/src/libdm/tests/renderer/skeleton.c
    brlcad/branches/swrast/src/libdm/tests/renderer/skeleton.h
    brlcad/branches/swrast/src/libdm/tests/renderer/texture.c
    brlcad/branches/swrast/src/libdm/tests/renderer/texture.h

Modified: brlcad/branches/swrast/src/libdm/tests/CMakeLists.txt
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/CMakeLists.txt       2020-08-27 
11:55:38 UTC (rev 76972)
+++ brlcad/branches/swrast/src/libdm/tests/CMakeLists.txt       2020-08-27 
12:04:42 UTC (rev 76973)
@@ -44,7 +44,6 @@
     ${TK_INCLUDE_PATH}
     )
 
-if (0)
   set(renderer_srcs
     renderer/camera.c
     renderer/darray.c
@@ -62,7 +61,6 @@
     renderer/scenes/scene_helper.c
     )
  BRLCAD_ADDEXEC(rendered_img "rendered_image.cpp;${renderer_srcs}" 
"libdm;libbu;${TCL_LIBRARY};${TK_LIBRARY}" TEST_USEDATA)
-endif (0)
 
  BRLCAD_ADDEXEC(tcl_img tcl_img.cpp "libdm;libbu;${TCL_LIBRARY};${TK_LIBRARY}" 
TEST_USEDATA)
  BRLCAD_ADDEXEC(tcl_tevent tcl_tevent.cpp 
"libdm;libbu;${TCL_LIBRARY};${TK_LIBRARY}" TEST_USEDATA)

Added: brlcad/branches/swrast/src/libdm/tests/renderer/LICENSE
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/LICENSE                     
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/LICENSE     2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Zhou Le
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/LICENSE
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/api.h
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/api.h                       
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/api.h       2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,46 @@
+#ifndef API_H
+#define API_H
+
+#define SMALL_FASTF 1.0e-37
+#define NEAR_ZERO(val, epsilon) (((val) > -epsilon) && ((val) < epsilon))
+#define NEAR_EQUAL(_a, _b, _tol) NEAR_ZERO((_a) - (_b), (_tol))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "camera.h"
+#include "darray.h"
+#include "draw2d.h"
+#include "graphics.h"
+#include "image.h"
+#include "macro.h"
+#include "maths.h"
+#include "mesh.h"
+#include "scene.h"
+#include "skeleton.h"
+#include "texture.h"
+
+scene_t *scene_from_file(const char *filename, mat4_t root);
+scene_t *blinn_lighthouse_scene(void);
+
+/* mesh related functions */
+mesh_t *cache_acquire_mesh(const char *filename);
+void cache_release_mesh(mesh_t *mesh);
+
+/* skeleton related functions */
+skeleton_t *cache_acquire_skeleton(const char *filename);
+void cache_release_skeleton(skeleton_t *skeleton);
+
+/* texture related functions */
+texture_t *cache_acquire_texture(const char *filename, usage_t usage);
+void cache_release_texture(texture_t *texture);
+
+/* misc cache functions */
+void cache_cleanup(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/api.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/camera.c
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/camera.c                    
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/camera.c    2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,107 @@
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include "camera.h"
+#include "macro.h"
+#include "maths.h"
+
+/*
+ * for orbital camera controls, see
+ * 
https://github.com/mrdoob/three.js/blob/master/examples/js/controls/OrbitControls.js
+ */
+
+static const float NEAR = 0.1f;
+static const float FAR = 10000;
+static const float FOVY = TO_RADIANS(60);
+static const vec3_t UP = {0, 1, 0};
+
+struct camera {
+    vec3_t position;
+    vec3_t target;
+    float aspect;
+};
+
+/* camera creating/releasing */
+
+camera_t *camera_create(vec3_t position, vec3_t target, float aspect) {
+    camera_t *camera;
+
+    assert(vec3_length(vec3_sub(position, target)) > EPSILON && aspect > 0);
+
+    camera = (camera_t*)malloc(sizeof(camera_t));
+    camera->position = position;
+    camera->target = target;
+    camera->aspect = aspect;
+
+    return camera;
+}
+
+void camera_release(camera_t *camera) {
+    free(camera);
+}
+
+/* camera updating */
+
+void camera_set_transform(camera_t *camera, vec3_t position, vec3_t target) {
+    assert(vec3_length(vec3_sub(position, target)) > EPSILON);
+    camera->position = position;
+    camera->target = target;
+}
+
+static vec3_t calculate_pan(vec3_t from_camera, motion_t motion) {
+    vec3_t forward = vec3_normalize(from_camera);
+    vec3_t left = vec3_cross(UP, forward);
+    vec3_t up = vec3_cross(forward, left);
+
+    float distance = vec3_length(from_camera);
+    float factor = distance * (float)tan(FOVY / 2) * 2;
+    vec3_t delta_x = vec3_mul(left, motion.pan.x * factor);
+    vec3_t delta_y = vec3_mul(up, motion.pan.y * factor);
+    return vec3_add(delta_x, delta_y);
+}
+
+static vec3_t calculate_offset(vec3_t from_target, motion_t motion) {
+    float radius = vec3_length(from_target);
+    float theta = (float)atan2(from_target.x, from_target.z);  /* azimuth */
+    float phi = (float)acos(from_target.y / radius);           /* polar */
+    float factor = PI * 2;
+    vec3_t offset;
+
+    radius *= (float)pow(0.95, motion.dolly);
+    theta -= motion.orbit.x * factor;
+    phi -= motion.orbit.y * factor;
+    phi = float_clamp(phi, EPSILON, PI - EPSILON);
+
+    offset.x = radius * (float)sin(phi) * (float)sin(theta);
+    offset.y = radius * (float)cos(phi);
+    offset.z = radius * (float)sin(phi) * (float)cos(theta);
+
+    return offset;
+}
+
+void camera_update_transform(camera_t *camera, motion_t motion) {
+    vec3_t from_target = vec3_sub(camera->position, camera->target);
+    vec3_t from_camera = vec3_sub(camera->target, camera->position);
+    vec3_t pan = calculate_pan(from_camera, motion);
+    vec3_t offset = calculate_offset(from_target, motion);
+    camera->target = vec3_add(camera->target, pan);
+    camera->position = vec3_add(camera->target, offset);
+}
+
+/* property retrieving */
+
+vec3_t camera_get_position(camera_t *camera) {
+    return camera->position;
+}
+
+vec3_t camera_get_forward(camera_t *camera) {
+    return vec3_normalize(vec3_sub(camera->target, camera->position));
+}
+
+mat4_t camera_get_view_matrix(camera_t *camera) {
+    return mat4_lookat(camera->position, camera->target, UP);
+}
+
+mat4_t camera_get_proj_matrix(camera_t *camera) {
+    return mat4_perspective(FOVY, camera->aspect, NEAR, FAR);
+}


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/camera.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/camera.h
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/camera.h                    
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/camera.h    2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,23 @@
+#ifndef CAMERA_H
+#define CAMERA_H
+
+#include "maths.h"
+
+typedef struct camera camera_t;
+typedef struct {vec2_t orbit; vec2_t pan; float dolly;} motion_t;
+
+/* camera creating/releasing */
+camera_t *camera_create(vec3_t position, vec3_t target, float aspect);
+void camera_release(camera_t *camera);
+
+/* camera updating */
+void camera_set_transform(camera_t *camera, vec3_t position, vec3_t target);
+void camera_update_transform(camera_t *camera, motion_t motion);
+
+/* property retrieving */
+vec3_t camera_get_position(camera_t *camera);
+vec3_t camera_get_forward(camera_t *camera);
+mat4_t camera_get_view_matrix(camera_t *camera);
+mat4_t camera_get_proj_matrix(camera_t *camera);
+
+#endif


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/camera.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/darray.c
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/darray.c                    
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/darray.c    2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,47 @@
+#include <assert.h>
+#include <stdlib.h>
+#include "darray.h"
+
+/*
+ * for type-safe dynamic array, see
+ * https://github.com/nothings/stb/blob/master/stretchy_buffer.h
+ */
+
+#define DARRAY_RAW_DATA(darray) ((int*)(darray) - 2)
+#define DARRAY_CAPACITY(darray) (DARRAY_RAW_DATA(darray)[0])
+#define DARRAY_OCCUPIED(darray) (DARRAY_RAW_DATA(darray)[1])
+
+int darray_size(void *darray) {
+    return darray != NULL ? DARRAY_OCCUPIED(darray) : 0;
+}
+
+void darray_free(void *darray) {
+    if (darray != NULL) {
+        free(DARRAY_RAW_DATA(darray));
+    }
+}
+
+void *darray_hold(void *darray, int count, int item_size) {
+    int header_size = sizeof(int) * 2;
+    assert(count > 0 && item_size > 0);
+    if (darray == NULL) {
+        int raw_size = header_size + item_size * count;
+        int *base = (int*)malloc(raw_size);
+        base[0] = count;  /* capacity */
+        base[1] = count;  /* occupied */
+        return base + 2;
+    } else if (DARRAY_OCCUPIED(darray) + count <= DARRAY_CAPACITY(darray)) {
+        DARRAY_OCCUPIED(darray) += count;
+        return darray;
+    } else {
+        int needed_size = DARRAY_OCCUPIED(darray) + count;
+        int double_curr = DARRAY_CAPACITY(darray) * 2;
+        int capacity = needed_size > double_curr ? needed_size : double_curr;
+        int occupied = needed_size;
+        int raw_size = header_size + item_size * capacity;
+        int *base = (int*)realloc(DARRAY_RAW_DATA(darray), raw_size);
+        base[0] = capacity;
+        base[1] = occupied;
+        return base + 2;
+    }
+}


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/darray.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/darray.h
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/darray.h                    
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/darray.h    2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,14 @@
+#ifndef DARRAY_H
+#define DARRAY_H
+
+#define darray_push(darray, value, type)                                       
   \
+    do {                                                                    \
+        (darray) = (type *)darray_hold((darray), 1, sizeof(*(darray)));        
     \
+        (darray)[darray_size(darray) - 1] = (value);                        \
+    } while (0)
+
+void *darray_hold(void *darray, int count, int item_size);
+int darray_size(void *darray);
+void darray_free(void *darray);
+
+#endif


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/darray.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/draw2d.c
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/draw2d.c                    
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/draw2d.c    2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,133 @@
+#include <stdlib.h>
+#include "draw2d.h"
+#include "graphics.h"
+#include "maths.h"
+#include "texture.h"
+
+static void swap_integers(int *a, int *b) {
+    int t = *a;
+    *a = *b;
+    *b = t;
+}
+
+static int lerp_integers(int a, int b, float t) {
+    return (int)(a + (b - a) * t);
+}
+
+static void convert_color(vec4_t input, unsigned char output[4]) {
+    output[0] = float_to_uchar(input.x);
+    output[1] = float_to_uchar(input.y);
+    output[2] = float_to_uchar(input.z);
+    output[3] = float_to_uchar(input.w);
+}
+
+static void convert_point(framebuffer_t *framebuffer, vec2_t input,
+                          int *row, int *col) {
+    *row = (int)((framebuffer->height - 1) * float_saturate(input.y) + 0.5f);
+    *col = (int)((framebuffer->width - 1) * float_saturate(input.x) + 0.5f);
+}
+
+static void draw_point(framebuffer_t *framebuffer, unsigned char color[4],
+                       int row, int col) {
+    int index = (row * framebuffer->width + col) * 4;
+    int i;
+    for (i = 0; i < 4; i++) {
+        framebuffer->color_buffer[index + i] = color[i];
+    }
+}
+
+/*
+ * for Bresenham's line algorithm, see
+ * 
https://github.com/ssloy/tinyrenderer/wiki/Lesson-1:-Bresenham%E2%80%99s-Line-Drawing-Algorithm
+ */
+static void draw_line(framebuffer_t *framebuffer, unsigned char color[4],
+                      int row0, int col0, int row1, int col1) {
+    int row_distance = abs(row0 - row1);
+    int col_distance = abs(col0 - col1);
+    if (row_distance == 0 && col_distance == 0) {
+        draw_point(framebuffer, color, row0, col0);
+    } else if (row_distance > col_distance) {
+        int row;
+        if (row0 > row1) {
+            swap_integers(&row0, &row1);
+            swap_integers(&col0, &col1);
+        }
+        for (row = row0; row <= row1; row++) {
+            float t = (float)(row - row0) / (float)row_distance;
+            int col = lerp_integers(col0, col1, t);
+            draw_point(framebuffer, color, row, col);
+        }
+    } else {
+        int col;
+        if (col0 > col1) {
+            swap_integers(&col0, &col1);
+            swap_integers(&row0, &row1);
+        }
+        for (col = col0; col <= col1; col++) {
+            float t = (float)(col - col0) / (float)col_distance;
+            int row = lerp_integers(row0, row1, t);
+            draw_point(framebuffer, color, row, col);
+        }
+    }
+}
+
+static void draw_texture(framebuffer_t *framebuffer, texture_t *texture,
+                         int row, int col, int width, int height) {
+    int src_r, src_c;
+    for (src_r = 0; src_r < height; src_r++) {
+        for (src_c = 0; src_c < width; src_c++) {
+            int dst_r = row + src_r;
+            int dst_c = col + src_c;
+            int src_index = src_r * texture->width + src_c;
+            int dst_index = (dst_r * framebuffer->width + dst_c) * 4;
+            vec4_t *src_pixel = &texture->buffer[src_index];
+            unsigned char *dst_pixel = &framebuffer->color_buffer[dst_index];
+            dst_pixel[0] += float_to_uchar(src_pixel->x);
+            dst_pixel[1] += float_to_uchar(src_pixel->y);
+            dst_pixel[2] += float_to_uchar(src_pixel->z);
+        }
+    }
+}
+
+void draw2d_draw_point(framebuffer_t *framebuffer, vec4_t color_,
+                       vec2_t point) {
+    unsigned char color[4];
+    int row, col;
+    convert_color(color_, color);
+    convert_point(framebuffer, point, &row, &col);
+    draw_point(framebuffer, color, row, col);
+}
+
+void draw2d_draw_line(framebuffer_t *framebuffer, vec4_t color_,
+                      vec2_t point0, vec2_t point1) {
+    unsigned char color[4];
+    int row0, col0, row1, col1;
+    convert_color(color_, color);
+    convert_point(framebuffer, point0, &row0, &col0);
+    convert_point(framebuffer, point1, &row1, &col1);
+    draw_line(framebuffer, color, row0, col0, row1, col1);
+}
+
+void draw2d_draw_triangle(framebuffer_t *framebuffer, vec4_t color_,
+                          vec2_t point0, vec2_t point1, vec2_t point2) {
+    unsigned char color[4];
+    int row0, col0, row1, col1, row2, col2;
+    convert_color(color_, color);
+    convert_point(framebuffer, point0, &row0, &col0);
+    convert_point(framebuffer, point1, &row1, &col1);
+    convert_point(framebuffer, point2, &row2, &col2);
+    draw_line(framebuffer, color, row0, col0, row1, col1);
+    draw_line(framebuffer, color, row1, col1, row2, col2);
+    draw_line(framebuffer, color, row2, col2, row0, col0);
+}
+
+void draw2d_draw_texture(framebuffer_t *framebuffer, texture_t *texture,
+                         vec2_t origin) {
+    int row, col, width, height;
+    convert_point(framebuffer, origin, &row, &col);
+    width = framebuffer->width - col;
+    height = framebuffer->height - row;
+    width = width < texture->width ? width : texture->width;
+    height = height < texture->height ? height : texture->height;
+    draw_texture(framebuffer, texture, row, col, width, height);
+}


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/draw2d.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/draw2d.h
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/draw2d.h                    
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/draw2d.h    2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,17 @@
+#ifndef DRAW2D_H
+#define DRAW2D_H
+
+#include "graphics.h"
+#include "maths.h"
+#include "texture.h"
+
+void draw2d_draw_point(framebuffer_t *framebuffer, vec4_t color,
+                       vec2_t point);
+void draw2d_draw_line(framebuffer_t *framebuffer, vec4_t color,
+                      vec2_t point0, vec2_t point1);
+void draw2d_draw_triangle(framebuffer_t *framebuffer, vec4_t color,
+                          vec2_t point0, vec2_t point1, vec2_t point2);
+void draw2d_draw_texture(framebuffer_t *framebuffer, texture_t *texture,
+                         vec2_t origin);
+
+#endif


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/draw2d.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/graphics.c
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/graphics.c                  
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/graphics.c  2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,592 @@
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "graphics.h"
+#include "macro.h"
+#include "maths.h"
+
+/* framebuffer management */
+
+framebuffer_t *framebuffer_create_existing(unsigned char *color_buffer, int 
width, int height) {
+    int depth_buffer_size = sizeof(float) * width * height;
+    vec4_t default_color = {0, 0, 0, 1};
+    float default_depth = 1;
+    framebuffer_t *framebuffer;
+
+    assert(width > 0 && height > 0);
+
+    framebuffer = (framebuffer_t*)malloc(sizeof(framebuffer_t));
+    framebuffer->width = width;
+    framebuffer->height = height;
+    framebuffer->color_buffer = color_buffer;
+    framebuffer->depth_buffer = (float*)malloc(depth_buffer_size);
+
+    framebuffer_clear_color(framebuffer, default_color);
+    framebuffer_clear_depth(framebuffer, default_depth);
+
+    return framebuffer;
+}
+
+void framebuffer_release_existing(framebuffer_t *framebuffer) {
+    free(framebuffer->depth_buffer);
+    free(framebuffer);
+}
+
+framebuffer_t *framebuffer_create(int width, int height) {
+    int color_buffer_size = width * height * 4;
+    int depth_buffer_size = sizeof(float) * width * height;
+    vec4_t default_color = {0, 0, 0, 1};
+    float default_depth = 1;
+    framebuffer_t *framebuffer;
+
+    assert(width > 0 && height > 0);
+
+    framebuffer = (framebuffer_t*)malloc(sizeof(framebuffer_t));
+    framebuffer->width = width;
+    framebuffer->height = height;
+    framebuffer->color_buffer = (unsigned char*)malloc(color_buffer_size);
+    framebuffer->depth_buffer = (float*)malloc(depth_buffer_size);
+
+    framebuffer_clear_color(framebuffer, default_color);
+    framebuffer_clear_depth(framebuffer, default_depth);
+
+    return framebuffer;
+}
+
+void framebuffer_release(framebuffer_t *framebuffer) {
+    free(framebuffer->color_buffer);
+    free(framebuffer->depth_buffer);
+    free(framebuffer);
+}
+
+void framebuffer_clear_color(framebuffer_t *framebuffer, vec4_t color) {
+    int num_pixels = framebuffer->width * framebuffer->height;
+    int i;
+    for (i = 0; i < num_pixels; i++) {
+        framebuffer->color_buffer[i * 4 + 0] = float_to_uchar(color.x);
+        framebuffer->color_buffer[i * 4 + 1] = float_to_uchar(color.y);
+        framebuffer->color_buffer[i * 4 + 2] = float_to_uchar(color.z);
+        framebuffer->color_buffer[i * 4 + 3] = float_to_uchar(color.w);
+    }
+}
+
+void framebuffer_clear_depth(framebuffer_t *framebuffer, float depth) {
+    int num_pixels = framebuffer->width * framebuffer->height;
+    int i;
+    for (i = 0; i < num_pixels; i++) {
+        framebuffer->depth_buffer[i] = depth;
+    }
+}
+
+/* program management */
+
+#define MAX_VARYINGS 10
+
+struct program {
+    vertex_shader_t *vertex_shader;
+    fragment_shader_t *fragment_shader;
+    int sizeof_attribs;
+    int sizeof_varyings;
+    int sizeof_uniforms;
+    int double_sided;
+    int enable_blend;
+    /* for shaders */
+    void *shader_attribs[3];
+    void *shader_varyings;
+    void *shader_uniforms;
+    /* for clipping */
+    vec4_t in_coords[MAX_VARYINGS];
+    vec4_t out_coords[MAX_VARYINGS];
+    void *in_varyings[MAX_VARYINGS];
+    void *out_varyings[MAX_VARYINGS];
+};
+
+program_t *program_create(
+        vertex_shader_t *vertex_shader, fragment_shader_t *fragment_shader,
+        int sizeof_attribs, int sizeof_varyings, int sizeof_uniforms,
+        int double_sided, int enable_blend) {
+    program_t *program;
+    int i;
+
+    assert(sizeof_attribs > 0 && sizeof_varyings > 0 && sizeof_uniforms > 0);
+    assert(sizeof_varyings % sizeof(float) == 0);
+
+    program = (program_t*)malloc(sizeof(program_t));
+
+    program->vertex_shader = vertex_shader;
+    program->fragment_shader = fragment_shader;
+    program->sizeof_attribs = sizeof_attribs;
+    program->sizeof_varyings = sizeof_varyings;
+    program->sizeof_uniforms = sizeof_uniforms;
+    program->double_sided = double_sided;
+    program->enable_blend = enable_blend;
+
+    for (i = 0; i < 3; i++) {
+        program->shader_attribs[i] = malloc(sizeof_attribs);
+        memset(program->shader_attribs[i], 0, sizeof_attribs);
+    }
+    program->shader_varyings = malloc(sizeof_varyings);
+    memset(program->shader_varyings, 0, sizeof_varyings);
+    program->shader_uniforms = malloc(sizeof_uniforms);
+    memset(program->shader_uniforms, 0, sizeof_uniforms);
+    for (i = 0; i < MAX_VARYINGS; i++) {
+        program->in_varyings[i] = malloc(sizeof_varyings);
+        memset(program->in_varyings[i], 0, sizeof_varyings);
+        program->out_varyings[i] = malloc(sizeof_varyings);
+        memset(program->out_varyings[i], 0, sizeof_varyings);
+    }
+
+    return program;
+}
+
+void program_release(program_t *program) {
+    int i;
+    for (i = 0; i < 3; i++) {
+        free(program->shader_attribs[i]);
+    }
+    free(program->shader_varyings);
+    free(program->shader_uniforms);
+    for (i = 0; i < MAX_VARYINGS; i++) {
+        free(program->in_varyings[i]);
+        free(program->out_varyings[i]);
+    }
+    free(program);
+}
+
+void *program_get_attribs(program_t *program, int nth_vertex) {
+    assert(nth_vertex >= 0 && nth_vertex < 3);
+    return program->shader_attribs[nth_vertex];
+}
+
+void *program_get_uniforms(program_t *program) {
+    return program->shader_uniforms;
+}
+
+/* graphics pipeline */
+
+/*
+ * for triangle clipping, see
+ * http://fabiensanglard.net/polygon_codec/
+ * http://graphics.idav.ucdavis.edu/education/GraphicsNotes/Clipping.pdf
+ */
+
+typedef enum {
+    POSITIVE_W,
+    POSITIVE_X,
+    NEGATIVE_X,
+    POSITIVE_Y,
+    NEGATIVE_Y,
+    POSITIVE_Z,
+    NEGATIVE_Z
+} plane_t;
+
+static int is_inside_plane(vec4_t coord, plane_t plane) {
+    switch (plane) {
+        case POSITIVE_W:
+            return coord.w >= EPSILON;
+        case POSITIVE_X:
+            return coord.x <= +coord.w;
+        case NEGATIVE_X:
+            return coord.x >= -coord.w;
+        case POSITIVE_Y:
+            return coord.y <= +coord.w;
+        case NEGATIVE_Y:
+            return coord.y >= -coord.w;
+        case POSITIVE_Z:
+            return coord.z <= +coord.w;
+        case NEGATIVE_Z:
+            return coord.z >= -coord.w;
+        default:
+            assert(0);
+            return 0;
+    }
+}
+
+static float get_intersect_ratio(vec4_t prev, vec4_t curr, plane_t plane) {
+    switch (plane) {
+        case POSITIVE_W:
+            return (prev.w - EPSILON) / (prev.w - curr.w);
+        case POSITIVE_X:
+            return (prev.w - prev.x) / ((prev.w - prev.x) - (curr.w - curr.x));
+        case NEGATIVE_X:
+            return (prev.w + prev.x) / ((prev.w + prev.x) - (curr.w + curr.x));
+        case POSITIVE_Y:
+            return (prev.w - prev.y) / ((prev.w - prev.y) - (curr.w - curr.y));
+        case NEGATIVE_Y:
+            return (prev.w + prev.y) / ((prev.w + prev.y) - (curr.w + curr.y));
+        case POSITIVE_Z:
+            return (prev.w - prev.z) / ((prev.w - prev.z) - (curr.w - curr.z));
+        case NEGATIVE_Z:
+            return (prev.w + prev.z) / ((prev.w + prev.z) - (curr.w + curr.z));
+        default:
+            assert(0);
+            return 0;
+    }
+}
+
+static int clip_against_plane(
+        plane_t plane, int in_num_vertices, int varying_num_floats,
+        vec4_t in_coords[MAX_VARYINGS], void *in_varyings[MAX_VARYINGS],
+        vec4_t out_coords[MAX_VARYINGS], void *out_varyings[MAX_VARYINGS]) {
+    int out_num_vertices = 0;
+    int i, j;
+
+    assert(in_num_vertices >= 3 && in_num_vertices <= MAX_VARYINGS);
+    for (i = 0; i < in_num_vertices; i++) {
+        int prev_index = (i - 1 + in_num_vertices) % in_num_vertices;
+        int curr_index = i;
+        vec4_t prev_coord = in_coords[prev_index];
+        vec4_t curr_coord = in_coords[curr_index];
+        float *prev_varyings = (float*)in_varyings[prev_index];
+        float *curr_varyings = (float*)in_varyings[curr_index];
+        int prev_inside = is_inside_plane(prev_coord, plane);
+        int curr_inside = is_inside_plane(curr_coord, plane);
+
+        if (prev_inside != curr_inside) {
+            vec4_t *dest_coord = &out_coords[out_num_vertices];
+            float *dest_varyings = (float*)out_varyings[out_num_vertices];
+            float ratio = get_intersect_ratio(prev_coord, curr_coord, plane);
+
+            *dest_coord = vec4_lerp(prev_coord, curr_coord, ratio);
+            /*
+             * since this computation is performed in clip space before
+             * division by w, clipped varying values are perspective-correct
+             */
+            for (j = 0; j < varying_num_floats; j++) {
+                dest_varyings[j] = float_lerp(prev_varyings[j],
+                                              curr_varyings[j],
+                                              ratio);
+            }
+            out_num_vertices += 1;
+        }
+
+        if (curr_inside) {
+            vec4_t *dest_coord = &out_coords[out_num_vertices];
+            float *dest_varyings = (float*)out_varyings[out_num_vertices];
+            int sizeof_varyings = varying_num_floats * sizeof(float);
+
+            *dest_coord = curr_coord;
+            memcpy(dest_varyings, curr_varyings, sizeof_varyings);
+            out_num_vertices += 1;
+        }
+    }
+    assert(out_num_vertices <= MAX_VARYINGS);
+    return out_num_vertices;
+}
+
+#define CLIP_IN2OUT(plane)                                                  \
+    do {                                                                    \
+        num_vertices = clip_against_plane(                                  \
+            plane, num_vertices, varying_num_floats,                        \
+            in_coords, in_varyings, out_coords, out_varyings);              \
+        if (num_vertices < 3) {                                             \
+            return 0;                                                       \
+        }                                                                   \
+    } while (0)
+
+#define CLIP_OUT2IN(plane)                                                  \
+    do {                                                                    \
+        num_vertices = clip_against_plane(                                  \
+            plane, num_vertices, varying_num_floats,                        \
+            out_coords, out_varyings, in_coords, in_varyings);              \
+        if (num_vertices < 3) {                                             \
+            return 0;                                                       \
+        }                                                                   \
+    } while (0)
+
+static int is_vertex_visible(vec4_t v) {
+    return fabs(v.x) <= v.w && fabs(v.y) <= v.w && fabs(v.z) <= v.w;
+}
+
+static int clip_triangle(
+        int sizeof_varyings,
+        vec4_t in_coords[MAX_VARYINGS], void *in_varyings[MAX_VARYINGS],
+        vec4_t out_coords[MAX_VARYINGS], void *out_varyings[MAX_VARYINGS]) {
+    int v0_visible = is_vertex_visible(in_coords[0]);
+    int v1_visible = is_vertex_visible(in_coords[1]);
+    int v2_visible = is_vertex_visible(in_coords[2]);
+    if (v0_visible && v1_visible && v2_visible) {
+        out_coords[0] = in_coords[0];
+        out_coords[1] = in_coords[1];
+        out_coords[2] = in_coords[2];
+        memcpy(out_varyings[0], in_varyings[0], sizeof_varyings);
+        memcpy(out_varyings[1], in_varyings[1], sizeof_varyings);
+        memcpy(out_varyings[2], in_varyings[2], sizeof_varyings);
+        return 3;
+    } else {
+        int varying_num_floats = sizeof_varyings / sizeof(float);
+        int num_vertices = 3;
+        CLIP_IN2OUT(POSITIVE_W);
+        CLIP_OUT2IN(POSITIVE_X);
+        CLIP_IN2OUT(NEGATIVE_X);
+        CLIP_OUT2IN(POSITIVE_Y);
+        CLIP_IN2OUT(NEGATIVE_Y);
+        CLIP_OUT2IN(POSITIVE_Z);
+        CLIP_IN2OUT(NEGATIVE_Z);
+        return num_vertices;
+    }
+}
+
+/*
+ * for facing determination, see subsection 3.5.1 of
+ * https://www.khronos.org/registry/OpenGL/specs/es/2.0/es_full_spec_2.0.pdf
+ *
+ * this is the same as (but more efficient than)
+ *     vec3_t ab = vec3_sub(b, a);
+ *     vec3_t ac = vec3_sub(c, a);
+ *     return vec3_cross(ab, ac).z <= 0;
+ */
+static int is_back_facing(vec3_t ndc_coords[3]) {
+    vec3_t a = ndc_coords[0];
+    vec3_t b = ndc_coords[1];
+    vec3_t c = ndc_coords[2];
+    float signed_area = a.x * b.y - a.y * b.x +
+                        b.x * c.y - b.y * c.x +
+                        c.x * a.y - c.y * a.x;
+    return signed_area <= 0;
+}
+
+/*
+ * for viewport transformation, see subsection 2.12.1 of
+ * https://www.khronos.org/registry/OpenGL/specs/es/2.0/es_full_spec_2.0.pdf
+ */
+static vec3_t viewport_transform(int width, int height, vec3_t ndc_coord) {
+    float x = (ndc_coord.x + 1) * 0.5f * (float)width;   /* [-1, 1] -> [0, w] 
*/
+    float y = (ndc_coord.y + 1) * 0.5f * (float)height;  /* [-1, 1] -> [0, h] 
*/
+    float z = (ndc_coord.z + 1) * 0.5f;                  /* [-1, 1] -> [0, 1] 
*/
+    return vec3_new(x, y, z);
+}
+
+typedef struct {int min_x, min_y, max_x, max_y;} bbox_t;
+
+static int min_integer(int a, int b) {
+    return a < b ? a : b;
+}
+
+static int max_integer(int a, int b) {
+    return a > b ? a : b;
+}
+
+static bbox_t find_bounding_box(vec2_t abc[3], int width, int height) {
+    vec2_t min = vec2_min(vec2_min(abc[0], abc[1]), abc[2]);
+    vec2_t max = vec2_max(vec2_max(abc[0], abc[1]), abc[2]);
+    bbox_t bbox;
+    double dminx = floor(min.x);
+    double dminy = floor(min.y);
+    double dmaxx = ceil(max.x);
+    double dmaxy = ceil(max.y);
+    bbox.min_x = max_integer((int)dminx, 0);
+    bbox.min_y = max_integer((int)dminy, 0);
+    bbox.max_x = min_integer((int)dmaxx, width - 1);
+    bbox.max_y = min_integer((int)dmaxy, height - 1);
+    return bbox;
+}
+
+/*
+ * for barycentric coordinates, see
+ * http://blackpawn.com/texts/pointinpoly/
+ *
+ * solve
+ *     P = A + s * AB + t * AC  -->  AP = s * AB + t * AC
+ * then
+ *     s = (AC.y * AP.x - AC.x * AP.y) / (AB.x * AC.y - AB.y * AC.x)
+ *     t = (AB.x * AP.y - AB.y * AP.x) / (AB.x * AC.y - AB.y * AC.x)
+ *
+ * notice
+ *     P = A + s * AB + t * AC
+ *       = A + s * (B - A) + t * (C - A)
+ *       = (1 - s - t) * A + s * B + t * C
+ * then
+ *     weight_A = 1 - s - t
+ *     weight_B = s
+ *     weight_C = t
+ */
+static vec3_t calculate_weights(vec2_t abc[3], vec2_t p) {
+    vec2_t a = abc[0];
+    vec2_t b = abc[1];
+    vec2_t c = abc[2];
+    vec2_t ab = vec2_sub(b, a);
+    vec2_t ac = vec2_sub(c, a);
+    vec2_t ap = vec2_sub(p, a);
+    float factor = 1 / (ab.x * ac.y - ab.y * ac.x);
+    float s = (ac.y * ap.x - ac.x * ap.y) * factor;
+    float t = (ab.x * ap.y - ab.y * ap.x) * factor;
+    vec3_t weights = vec3_new(1 - s - t, s, t);
+    return weights;
+}
+
+/*
+ * for depth interpolation, see subsection 3.5.1 of
+ * https://www.khronos.org/registry/OpenGL/specs/es/2.0/es_full_spec_2.0.pdf
+ */
+static float interpolate_depth(float screen_depths[3], vec3_t weights) {
+    float depth0 = screen_depths[0] * weights.x;
+    float depth1 = screen_depths[1] * weights.y;
+    float depth2 = screen_depths[2] * weights.z;
+    return depth0 + depth1 + depth2;
+}
+
+/*
+ * for perspective correct interpolation, see
+ * 
https://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf
+ * https://www.khronos.org/registry/OpenGL/specs/es/2.0/es_full_spec_2.0.pdf
+ *
+ * equation 15 in reference 1 (page 2) is a simplified 2d version of
+ * equation 3.5 in reference 2 (page 58) which uses barycentric coordinates
+ */
+static void interpolate_varyings(
+        void *src_varyings[3], void *dst_varyings,
+        int sizeof_varyings, vec3_t weights, float recip_w[3]) {
+    int num_floats = sizeof_varyings / sizeof(float);
+    float *src0 = (float*)src_varyings[0];
+    float *src1 = (float*)src_varyings[1];
+    float *src2 = (float*)src_varyings[2];
+    float *dst = (float*)dst_varyings;
+    float weight0 = recip_w[0] * weights.x;
+    float weight1 = recip_w[1] * weights.y;
+    float weight2 = recip_w[2] * weights.z;
+    float normalizer = 1 / (weight0 + weight1 + weight2);
+    int i;
+    for (i = 0; i < num_floats; i++) {
+        float sum = src0[i] * weight0 + src1[i] * weight1 + src2[i] * weight2;
+        dst[i] = sum * normalizer;
+    }
+}
+
+static void draw_fragment(framebuffer_t *framebuffer, program_t *program,
+                          int backface, int index, float depth) {
+    vec4_t color;
+    int discard;
+
+    /* execute fragment shader */
+    discard = 0;
+    color = program->fragment_shader(program->shader_varyings,
+                                     program->shader_uniforms,
+                                     &discard,
+                                     backface);
+    if (discard) {
+        return;
+    }
+    color = vec4_saturate(color);
+
+    /* perform blending */
+    if (program->enable_blend) {
+        /* out_color = src_color * src_alpha + dst_color * (1 - src_alpha) */
+        unsigned char dst_r = framebuffer->color_buffer[index * 4 + 0];
+        unsigned char dst_g = framebuffer->color_buffer[index * 4 + 1];
+        unsigned char dst_b = framebuffer->color_buffer[index * 4 + 2];
+        color.x = color.x * color.w + float_from_uchar(dst_r) * (1 - color.w);
+        color.y = color.y * color.w + float_from_uchar(dst_g) * (1 - color.w);
+        color.z = color.z * color.w + float_from_uchar(dst_b) * (1 - color.w);
+    }
+
+    /* write color and depth */
+    framebuffer->color_buffer[index * 4 + 0] = float_to_uchar(color.x);
+    framebuffer->color_buffer[index * 4 + 1] = float_to_uchar(color.y);
+    framebuffer->color_buffer[index * 4 + 2] = float_to_uchar(color.z);
+    framebuffer->depth_buffer[index] = depth;
+}
+
+static int rasterize_triangle(framebuffer_t *framebuffer, program_t *program,
+                              vec4_t clip_coords[3], void *varyings[3]) {
+    int width = framebuffer->width;
+    int height = framebuffer->height;
+    vec3_t ndc_coords[3];
+    vec2_t screen_coords[3];
+    float screen_depths[3];
+    float recip_w[3];
+    int backface;
+    bbox_t bbox;
+    int i, x, y;
+
+    /* perspective division */
+    for (i = 0; i < 3; i++) {
+        vec3_t clip_coord = vec3_from_vec4(clip_coords[i]);
+        ndc_coords[i] = vec3_div(clip_coord, clip_coords[i].w);
+    }
+
+    /* back-face culling */
+    backface = is_back_facing(ndc_coords);
+    if (backface && !program->double_sided) {
+        return 1;
+    }
+
+    /* reciprocals of w */
+    for (i = 0; i < 3; i++) {
+        recip_w[i] = 1 / clip_coords[i].w;
+    }
+
+    /* viewport mapping */
+    for (i = 0; i < 3; i++) {
+        vec3_t window_coord = viewport_transform(width, height, ndc_coords[i]);
+        screen_coords[i] = vec2_new(window_coord.x, window_coord.y);
+        screen_depths[i] = window_coord.z;
+    }
+
+    /* perform rasterization */
+    bbox = find_bounding_box(screen_coords, width, height);
+    for (x = bbox.min_x; x <= bbox.max_x; x++) {
+        for (y = bbox.min_y; y <= bbox.max_y; y++) {
+            vec2_t point = vec2_new((float)x + 0.5f, (float)y + 0.5f);
+            vec3_t weights = calculate_weights(screen_coords, point);
+            int weight0_okay = weights.x > -EPSILON;
+            int weight1_okay = weights.y > -EPSILON;
+            int weight2_okay = weights.z > -EPSILON;
+            if (weight0_okay && weight1_okay && weight2_okay) {
+                int index = y * width + x;
+                float depth = interpolate_depth(screen_depths, weights);
+                /* early depth testing */
+                if (depth <= framebuffer->depth_buffer[index]) {
+                    interpolate_varyings(varyings, program->shader_varyings,
+                                         program->sizeof_varyings,
+                                         weights, recip_w);
+                    draw_fragment(framebuffer, program, backface, index, 
depth);
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+void graphics_draw_triangle(framebuffer_t *framebuffer, program_t *program) {
+    int num_vertices;
+    int i;
+
+    /* execute vertex shader */
+    for (i = 0; i < 3; i++) {
+        vec4_t clip_coord = program->vertex_shader(program->shader_attribs[i],
+                                                   program->in_varyings[i],
+                                                   program->shader_uniforms);
+        program->in_coords[i] = clip_coord;
+    }
+
+    /* triangle clipping */
+    num_vertices = clip_triangle(program->sizeof_varyings,
+                                 program->in_coords, program->in_varyings,
+                                 program->out_coords, program->out_varyings);
+
+    /* triangle assembly */
+    for (i = 0; i < num_vertices - 2; i++) {
+        int index0 = 0;
+        int index1 = i + 1;
+        int index2 = i + 2;
+        vec4_t clip_coords[3];
+        void *varyings[3];
+        int is_culled;
+
+        clip_coords[0] = program->out_coords[index0];
+        clip_coords[1] = program->out_coords[index1];
+        clip_coords[2] = program->out_coords[index2];
+        varyings[0] = program->out_varyings[index0];
+        varyings[1] = program->out_varyings[index1];
+        varyings[2] = program->out_varyings[index2];
+
+        is_culled = rasterize_triangle(framebuffer, program,
+                                       clip_coords, varyings);
+        if (is_culled) {
+            break;
+        }
+    }
+}


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/graphics.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/graphics.h
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/graphics.h                  
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/graphics.h  2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,37 @@
+#ifndef GRAPHICS_H
+#define GRAPHICS_H
+
+#include "maths.h"
+
+typedef struct {
+    int width, height;
+    unsigned char *color_buffer;
+    float *depth_buffer;
+} framebuffer_t;
+
+typedef struct program program_t;
+typedef vec4_t vertex_shader_t(void *attribs, void *varyings, void *uniforms);
+typedef vec4_t fragment_shader_t(void *varyings, void *uniforms,
+                                 int *discard, int backface);
+
+/* framebuffer management */
+framebuffer_t *framebuffer_create(int width, int height);
+framebuffer_t *framebuffer_create_existing(unsigned char *b, int width, int 
height);
+void framebuffer_release_existing(framebuffer_t *framebuffer);
+void framebuffer_release(framebuffer_t *framebuffer);
+void framebuffer_clear_color(framebuffer_t *framebuffer, vec4_t color);
+void framebuffer_clear_depth(framebuffer_t *framebuffer, float depth);
+
+/* program management */
+program_t *program_create(
+    vertex_shader_t *vertex_shader, fragment_shader_t *fragment_shader,
+    int sizeof_attribs, int sizeof_varyings, int sizeof_uniforms,
+    int double_sided, int enable_blend);
+void program_release(program_t *program);
+void *program_get_attribs(program_t *program, int nth_vertex);
+void *program_get_uniforms(program_t *program);
+
+/* graphics pipeline */
+void graphics_draw_triangle(framebuffer_t *framebuffer, program_t *program);
+
+#endif


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/graphics.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/image.c
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/image.c                     
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/image.c     2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,499 @@
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "image.h"
+#include "macro.h"
+#include "maths.h"
+#include "private.h"
+
+/* image creating/releasing */
+
+image_t *image_create(int width, int height, int channels, format_t format) {
+    int num_elems = width * height * channels;
+    image_t *image;
+
+    assert(width > 0 && height > 0 && channels >= 1 && channels <= 4);
+    assert(format == FORMAT_LDR || format == FORMAT_HDR);
+
+    image = (image_t*)malloc(sizeof(image_t));
+    image->format = format;
+    image->width = width;
+    image->height = height;
+    image->channels = channels;
+    image->ldr_buffer = NULL;
+    image->hdr_buffer = NULL;
+
+    if (format == FORMAT_LDR) {
+        int size = sizeof(unsigned char) * num_elems;
+        image->ldr_buffer = (unsigned char*)malloc(size);
+        memset(image->ldr_buffer, 0, size);
+    } else {
+        int size = sizeof(float) * num_elems;
+        image->hdr_buffer = (float*)malloc(size);
+        memset(image->hdr_buffer, 0, size);
+    }
+
+    return image;
+}
+
+void image_release(image_t *image) {
+    free(image->ldr_buffer);
+    free(image->hdr_buffer);
+    free(image);
+}
+
+static image_t *load_tga_image(const char *filename);
+static image_t *load_hdr_image(const char *filename);
+
+image_t *image_load(const char *filename) {
+    const char *extension = private_get_extension(filename);
+    if (strcmp(extension, "tga") == 0) {
+        return load_tga_image(filename);
+    } else if (strcmp(extension, "hdr") == 0) {
+        return load_hdr_image(filename);
+    } else {
+        assert(0);
+        return NULL;
+    }
+}
+
+static void save_tga_image(image_t *image, const char *filename);
+static void save_hdr_image(image_t *image, const char *filename);
+
+void image_save(image_t *image, const char *filename) {
+    const char *extension = private_get_extension(filename);
+    if (strcmp(extension, "tga") == 0) {
+        save_tga_image(image, filename);
+    } else if (strcmp(extension, "hdr") == 0) {
+        save_hdr_image(image, filename);
+    } else {
+        assert(0);
+    }
+}
+
+/* image processing */
+
+static void swap_bytes(unsigned char *a, unsigned char *b) {
+    unsigned char t = *a;
+    *a = *b;
+    *b = t;
+}
+
+static void swap_floats(float *a, float *b) {
+    float t = *a;
+    *a = *b;
+    *b = t;
+}
+
+static unsigned char *get_ldr_pixel(image_t *image, int row, int col) {
+    int index = (row * image->width + col) * image->channels;
+    return &image->ldr_buffer[index];
+}
+
+static float *get_hdr_pixel(image_t *image, int row, int col) {
+    int index = (row * image->width + col) * image->channels;
+    return &image->hdr_buffer[index];
+}
+
+void image_flip_h(image_t *image) {
+    int half_width = image->width / 2;
+    int r, c, k;
+    for (r = 0; r < image->height; r++) {
+        for (c = 0; c < half_width; c++) {
+            int flipped_c = image->width - 1 - c;
+            if (image->format == FORMAT_LDR) {
+                unsigned char *pixel1 = get_ldr_pixel(image, r, c);
+                unsigned char *pixel2 = get_ldr_pixel(image, r, flipped_c);
+                for (k = 0; k < image->channels; k++) {
+                    swap_bytes(&pixel1[k], &pixel2[k]);
+                }
+            } else {
+                float *pixel1 = get_hdr_pixel(image, r, c);
+                float *pixel2 = get_hdr_pixel(image, r, flipped_c);
+                for (k = 0; k < image->channels; k++) {
+                    swap_floats(&pixel1[k], &pixel2[k]);
+                }
+            }
+        }
+    }
+}
+
+void image_flip_v(image_t *image) {
+    int half_height = image->height / 2;
+    int r, c, k;
+    for (r = 0; r < half_height; r++) {
+        for (c = 0; c < image->width; c++) {
+            int flipped_r = image->height - 1 - r;
+            if (image->format == FORMAT_LDR) {
+                unsigned char *pixel1 = get_ldr_pixel(image, r, c);
+                unsigned char *pixel2 = get_ldr_pixel(image, flipped_r, c);
+                for (k = 0; k < image->channels; k++) {
+                    swap_bytes(&pixel1[k], &pixel2[k]);
+                }
+            } else {
+                float *pixel1 = get_hdr_pixel(image, r, c);
+                float *pixel2 = get_hdr_pixel(image, flipped_r, c);
+                for (k = 0; k < image->channels; k++) {
+                    swap_floats(&pixel1[k], &pixel2[k]);
+                }
+            }
+        }
+    }
+}
+
+/* tga codec */
+
+#define TGA_HEADER_SIZE 18
+
+static unsigned char read_byte(FILE *file) {
+    int byte = fgetc(file);
+    assert(byte != EOF);
+    return (unsigned char)byte;
+}
+
+static void read_bytes(FILE *file, void *buffer, int size) {
+    int count = (int)fread(buffer, 1, size, file);
+    assert(count == size);
+    UNUSED_VAR(count);
+}
+
+static void write_bytes(FILE *file, void *buffer, int size) {
+    int count = (int)fwrite(buffer, 1, size, file);
+    assert(count == size);
+    UNUSED_VAR(count);
+}
+
+static int get_num_elems(image_t *image) {
+    return image->width * image->height * image->channels;
+}
+
+static void read_tga_header(FILE *file, int *width, int *height, int *channels,
+                            int *is_rle, int *flip_h, int *flip_v) {
+    unsigned char header[TGA_HEADER_SIZE];
+    int depth, idlength, imgtype, imgdesc;
+
+    read_bytes(file, header, TGA_HEADER_SIZE);
+
+    *width = header[12] | (header[13] << 8);
+    *height = header[14] | (header[15] << 8);
+    assert(*width > 0 && *height > 0);
+
+    depth = header[16];
+    assert(depth == 8 || depth == 24 || depth == 32);
+    *channels = depth / 8;
+
+    idlength = header[0];
+    assert(idlength == 0);
+    UNUSED_VAR(idlength);
+
+    imgtype = header[2];
+    assert(imgtype == 2 || imgtype == 3 || imgtype == 10 || imgtype == 11);
+    *is_rle = imgtype == 10 || imgtype == 11;
+
+    imgdesc = header[17];
+    *flip_h = imgdesc & 0x10;
+    *flip_v = imgdesc & 0x20;
+}
+
+static void load_tga_rle_payload(FILE *file, image_t *image) {
+    int num_elems = get_num_elems(image);
+    int curr_size = 0;
+    while (curr_size < num_elems) {
+        unsigned char header = read_byte(file);
+        int rle_packet = header & 0x80;
+        int num_pixels = (header & 0x7F) + 1;
+        unsigned char pixel[4];
+        int i, j;
+        assert(curr_size + num_pixels * image->channels <= num_elems);
+        if (rle_packet) {                                   /* rle packet */
+            for (j = 0; j < image->channels; j++) {
+                pixel[j] = read_byte(file);
+            }
+            for (i = 0; i < num_pixels; i++) {
+                for (j = 0; j < image->channels; j++) {
+                    image->ldr_buffer[curr_size++] = pixel[j];
+                }
+            }
+        } else {                                            /* raw packet */
+            for (i = 0; i < num_pixels; i++) {
+                for (j = 0; j < image->channels; j++) {
+                    image->ldr_buffer[curr_size++] = read_byte(file);
+                }
+            }
+        }
+    }
+    assert(curr_size == num_elems);
+}
+
+static image_t *load_tga_image(const char *filename) {
+    int width, height, channels;
+    int is_rle, flip_h, flip_v;
+    image_t *image;
+    FILE *file;
+
+    file = fopen(filename, "rb");
+    assert(file != NULL);
+    read_tga_header(file, &width, &height, &channels,
+                    &is_rle, &flip_h, &flip_v);
+    image = image_create(width, height, channels, FORMAT_LDR);
+    if (is_rle) {
+        load_tga_rle_payload(file, image);
+    } else {
+        read_bytes(file, image->ldr_buffer, get_num_elems(image));
+    }
+    fclose(file);
+
+    if (flip_h) {
+        image_flip_h(image);
+    }
+    if (flip_v) {
+        image_flip_v(image);
+    }
+    if (channels >= 3) {
+        int r, c;
+        for (r = 0; r < image->height; r++) {
+            for (c = 0; c < image->width; c++) {
+                unsigned char *pixel = get_ldr_pixel(image, r, c);
+                swap_bytes(&pixel[0], &pixel[2]);           /* bgr to rgb */
+            }
+        }
+    }
+
+    return image;
+}
+
+static void save_tga_image(image_t *image, const char *filename) {
+    unsigned char header[TGA_HEADER_SIZE];
+    FILE *file;
+
+    assert(image->format == FORMAT_LDR);
+
+    file = fopen(filename, "wb");
+    assert(file != NULL);
+
+    memset(header, 0, TGA_HEADER_SIZE);
+    header[2] = image->channels == 1 ? 3 : 2;               /* image type */
+    header[12] = image->width & 0xFF;                       /* width, lsb */
+    header[13] = (image->width >> 8) & 0xFF;                /* width, msb */
+    header[14] = image->height & 0xFF;                      /* height, lsb */
+    header[15] = (image->height >> 8) & 0xFF;               /* height, msb */
+    header[16] = (image->channels * 8) & 0xFF;              /* image depth */
+    write_bytes(file, header, TGA_HEADER_SIZE);
+
+    if (image->channels >= 3) {
+        int r, c;
+        for (r = 0; r < image->height; r++) {
+            for (c = 0; c < image->width; c++) {
+                unsigned char *pixel = get_ldr_pixel(image, r, c);
+                unsigned char channels[4];
+                memcpy(channels, pixel, image->channels);
+                swap_bytes(&channels[0], &channels[2]);     /* rgb to bgr */
+                write_bytes(file, channels, image->channels);
+            }
+        }
+    } else {
+        write_bytes(file, image->ldr_buffer, get_num_elems(image));
+    }
+
+    fclose(file);
+}
+
+/* hdr codec */
+
+static void read_line(FILE *file, char line[LINE_SIZE]) {
+    if (fgets(line, LINE_SIZE, file) == NULL) {
+        assert(0);
+    }
+}
+
+static int starts_with(const char *string, const char *prefix) {
+    return strncmp(string, prefix, strlen(prefix)) == 0;
+}
+
+static void rgbe_to_floats(unsigned char rgbe[4], float floats[3]) {
+    float rm = rgbe[0];                             /* red mantissa */
+    float gm = rgbe[1];                             /* green mantissa */
+    float bm = rgbe[2];                             /* blue mantissa */
+    int eb = rgbe[3];                               /* exponent biased */
+    if (eb == 0) {
+        floats[0] = floats[1] = floats[2] = 0;
+    } else {
+        int ev = eb - 128;                          /* exponent value */
+        float factor = (float)((1.0 / 256) * pow(2, ev));
+        floats[0] = rm * factor;                    /* red value */
+        floats[1] = gm * factor;                    /* green value */
+        floats[2] = bm * factor;                    /* blue value */
+    }
+}
+
+static void floats_to_rgbe(float floats[3], unsigned char rgbe[4]) {
+    float rv = floats[0];                           /* red value */
+    float gv = floats[1];                           /* green value */
+    float bv = floats[2];                           /* blue value */
+    float max_v = float_max(float_max(rv, gv), bv);
+    if (max_v < 1e-32f) {
+        rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
+    } else {
+        int ev;                                     /* exponent value */
+        float max_m = (float)frexp(max_v, &ev);
+        float factor = (1 / max_v) * max_m * 256;
+        rgbe[0] = (unsigned char)(rv * factor);     /* red mantissa */
+        rgbe[1] = (unsigned char)(gv * factor);     /* green mantissa */
+        rgbe[2] = (unsigned char)(bv * factor);     /* blue mantissa */
+        rgbe[3] = (unsigned char)(ev + 128);        /* exponent biased */
+    }
+}
+
+static void read_hdr_header(FILE *file, int *width, int *height) {
+    char line[LINE_SIZE];
+    int header_found = 0;
+    int format_found = 0;
+    int items;
+
+    read_line(file, line);
+    assert(starts_with(line, "#?"));
+
+    while (1) {
+        read_line(file, line);
+        if (strlen(line) == 1 && line[0] == '\n') {
+            header_found = 1;
+            break;
+        } else if (starts_with(line, "FORMAT=")) {
+            assert(starts_with(line, "FORMAT=32-bit_rle_rgbe"));
+            format_found = 1;
+        } else if (starts_with(line, "GAMMA=")) {
+            /* ignore gamma */
+        } else if (starts_with(line, "EXPOSURE=")) {
+            /* ignore exposure */
+        } else if (starts_with(line, "#")) {
+            /* ignore comments */
+        } else {
+            assert(0);
+        }
+    }
+    assert(header_found != 0);
+    assert(format_found != 0);
+    UNUSED_VAR(header_found);
+    UNUSED_VAR(format_found);
+
+    read_line(file, line);
+    items = sscanf(line, "-Y %d +X %d", height, width);
+    assert(items == 2 && *width > 0 && *height > 0);
+    UNUSED_VAR(items);
+}
+
+static void read_hdr_flat_scanline(FILE *file, image_t *image, int row) {
+    int i;
+    for (i = 0; i < image->width; i++) {
+        float *pixel = get_hdr_pixel(image, row, i);
+        unsigned char rgbe[4];
+        read_bytes(file, rgbe, 4);
+        rgbe_to_floats(rgbe, pixel);
+    }
+}
+
+static void read_hdr_rle_scanline(FILE *file, image_t *image, int row) {
+    unsigned char *channels[4];
+    int i, j;
+
+    for (i = 0; i < 4; i++) {
+        channels[i] = (unsigned char*)malloc(image->width);
+    }
+    for (i = 0; i < 4; i++) {
+        int size = 0;
+        while (size < image->width) {
+            unsigned char byte = read_byte(file);
+            if (byte > 128) {
+                int count = byte - 128;
+                unsigned char value = read_byte(file);
+                assert(count > 0 && size + count <= image->width);
+                for (j = 0; j < count; j++) {
+                    channels[i][size++] = value;
+                }
+            } else {
+                int count = byte;
+                assert(count > 0 && size + count <= image->width);
+                for (j = 0; j < count; j++) {
+                    channels[i][size++] = read_byte(file);
+                }
+            }
+        }
+        assert(size == image->width);
+    }
+
+    for (i = 0; i < image->width; i++) {
+        float *pixel = get_hdr_pixel(image, row, i);
+        unsigned char rgbe[4];
+        for (j = 0; j < 4; j++) {
+            rgbe[j] = channels[j][i];
+        }
+        rgbe_to_floats(rgbe, pixel);
+    }
+    for (i = 0; i < 4; i++) {
+        free(channels[i]);
+    }
+}
+
+static void read_hdr_scanline(FILE *file, image_t *image, int row) {
+    if (image->width < 8 || image->width > 0x7fff) {
+        read_hdr_flat_scanline(file, image, row);
+    } else {
+        unsigned char bytes[4];
+        read_bytes(file, bytes, 4);
+        if (bytes[0] != 2 || bytes[1] != 2 || bytes[2] & 0x80) {
+            fseek(file, -4, SEEK_CUR);
+            read_hdr_flat_scanline(file, image, row);
+        } else {
+            assert(bytes[2] * 256 + bytes[3] == image->width);
+            read_hdr_rle_scanline(file, image, row);
+        }
+    }
+}
+
+static image_t *load_hdr_image(const char *filename) {
+    int width, height;
+    image_t *image;
+    FILE *file;
+    int i;
+
+    file = fopen(filename, "rb");
+    assert(file != NULL);
+    read_hdr_header(file, &width, &height);
+    image = image_create(width, height, 3, FORMAT_HDR);
+    for (i = 0; i < height; i++) {
+        int row = height - 1 - i;
+        read_hdr_scanline(file, image, row);
+    }
+    fclose(file);
+
+    return image;
+}
+
+static void save_hdr_image(image_t *image, const char *filename) {
+    FILE *file;
+    int r, c;
+
+    assert(image->format == FORMAT_HDR && image->channels >= 3);
+
+    file = fopen(filename, "wb");
+    assert(file != NULL);
+
+    fputs("#?RADIANCE\n", file);
+    fputs("FORMAT=32-bit_rle_rgbe\n", file);
+    fputs("\n", file);
+    fprintf(file, "-Y %d +X %d\n", image->height, image->width);
+
+    for (r = 0; r < image->height; r++) {
+        for (c = 0; c < image->width; c++) {
+            int flipped_r = image->height - 1 - r;
+            float *pixel = get_hdr_pixel(image, flipped_r, c);
+            unsigned char rgbe[4];
+            floats_to_rgbe(pixel, rgbe);
+            write_bytes(file, rgbe, 4);
+        }
+    }
+
+    fclose(file);
+}


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/image.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/image.h
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/image.h                     
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/image.h     2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,26 @@
+#ifndef IMAGE_H
+#define IMAGE_H
+
+typedef enum {
+    FORMAT_LDR,
+    FORMAT_HDR
+} format_t;
+
+typedef struct {
+    format_t format;
+    int width, height, channels;
+    unsigned char *ldr_buffer;
+    float *hdr_buffer;
+} image_t;
+
+/* image creating/releasing */
+image_t *image_create(int width, int height, int channels, format_t format);
+void image_release(image_t *image);
+image_t *image_load(const char *filename);
+void image_save(image_t *image, const char *filename);
+
+/* image processing */
+void image_flip_h(image_t *image);
+void image_flip_v(image_t *image);
+
+#endif


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/image.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/macro.h
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/macro.h                     
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/macro.h     2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,16 @@
+#ifndef MACRO_H
+#define MACRO_H
+
+#define EPSILON 1e-5f
+#define PI 3.1415927f
+
+#define TO_RADIANS(degrees) ((PI / 180) * (degrees))
+#define TO_DEGREES(radians) ((180 / PI) * (radians))
+
+#define LINE_SIZE 256
+#define PATH_SIZE 256
+
+#define UNUSED_VAR(x) ((void)(x))
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+
+#endif


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/macro.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/maths.c
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/maths.c                     
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/maths.c     2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,951 @@
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include "macro.h"
+#include "maths.h"
+
+/* float related functions */
+
+float float_min(float a, float b) {
+    return a < b ? a : b;
+}
+
+float float_max(float a, float b) {
+    return a > b ? a : b;
+}
+
+float float_lerp(float a, float b, float t) {
+    return a + (b - a) * t;
+}
+
+float float_clamp(float f, float min, float max) {
+    return f < min ? min : (f > max ? max : f);
+}
+
+float float_saturate(float f) {
+    return f < 0 ? 0 : (f > 1 ? 1 : f);
+}
+
+float float_from_uchar(unsigned char value) {
+    return value / 255.0f;
+}
+
+unsigned char float_to_uchar(float value) {
+    return (unsigned char)(value * 255);
+}
+
+float float_srgb2linear(float value) {
+    return (float)pow(value, 2.2);
+}
+
+float float_linear2srgb(float value) {
+    return (float)pow(value, 1 / 2.2);
+}
+
+/*
+ * for aces filmic tone mapping curve, see
+ * https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
+ */
+float float_aces(float value) {
+    float a = 2.51f;
+    float b = 0.03f;
+    float c = 2.43f;
+    float d = 0.59f;
+    float e = 0.14f;
+    value = (value * (a * value + b)) / (value * (c * value + d) + e);
+    return float_saturate(value);
+}
+
+void float_print(const char *name, float f) {
+    printf("float %s = %f\n", name, f);
+}
+
+/* vec2 related functions */
+
+vec2_t vec2_new(float x, float y) {
+    vec2_t v;
+    v.x = x;
+    v.y = y;
+    return v;
+}
+
+vec2_t vec2_min(vec2_t a, vec2_t b) {
+    float x = float_min(a.x, b.x);
+    float y = float_min(a.y, b.y);
+    return vec2_new(x, y);
+}
+
+vec2_t vec2_max(vec2_t a, vec2_t b) {
+    float x = float_max(a.x, b.x);
+    float y = float_max(a.y, b.y);
+    return vec2_new(x, y);
+}
+
+vec2_t vec2_add(vec2_t a, vec2_t b) {
+    return vec2_new(a.x + b.x, a.y + b.y);
+}
+
+vec2_t vec2_sub(vec2_t a, vec2_t b) {
+    return vec2_new(a.x - b.x, a.y - b.y);
+}
+
+vec2_t vec2_mul(vec2_t v, float factor) {
+    return vec2_new(v.x * factor, v.y * factor);
+}
+
+vec2_t vec2_div(vec2_t v, float divisor) {
+    return vec2_mul(v, 1 / divisor);
+}
+
+float vec2_length(vec2_t v) {
+    return (float)sqrt(v.x * v.x + v.y * v.y);
+}
+
+/*
+ * for edge function, see
+ * 
https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/rasterization-stage
+ */
+float vec2_edge(vec2_t s, vec2_t e, vec2_t v) {
+    return (v.x - s.x) * (e.y - s.y) - (v.y - s.y) * (e.x - s.x);
+}
+
+void vec2_print(const char *name, vec2_t v) {
+    printf("vec2 %s =\n", name);
+    printf("    %12f    %12f\n", v.x, v.y);
+}
+
+/* vec3 related functions */
+
+vec3_t vec3_new(float x, float y, float z) {
+    vec3_t v;
+    v.x = x;
+    v.y = y;
+    v.z = z;
+    return v;
+}
+
+vec3_t vec3_from_vec4(vec4_t v) {
+    return vec3_new(v.x, v.y, v.z);
+}
+
+vec3_t vec3_min(vec3_t a, vec3_t b) {
+    float x = float_min(a.x, b.x);
+    float y = float_min(a.y, b.y);
+    float z = float_min(a.z, b.z);
+    return vec3_new(x, y, z);
+}
+
+vec3_t vec3_max(vec3_t a, vec3_t b) {
+    float x = float_max(a.x, b.x);
+    float y = float_max(a.y, b.y);
+    float z = float_max(a.z, b.z);
+    return vec3_new(x, y, z);
+}
+
+vec3_t vec3_add(vec3_t a, vec3_t b) {
+    return vec3_new(a.x + b.x, a.y + b.y, a.z + b.z);
+}
+
+vec3_t vec3_sub(vec3_t a, vec3_t b) {
+    return vec3_new(a.x - b.x, a.y - b.y, a.z - b.z);
+}
+
+vec3_t vec3_mul(vec3_t v, float factor) {
+    return vec3_new(v.x * factor, v.y * factor, v.z * factor);
+}
+
+vec3_t vec3_div(vec3_t v, float divisor) {
+    return vec3_mul(v, 1 / divisor);
+}
+
+vec3_t vec3_negate(vec3_t v) {
+    return vec3_new(-v.x, -v.y, -v.z);
+}
+
+float vec3_length(vec3_t v) {
+    return (float)sqrt(vec3_dot(v, v));
+}
+
+vec3_t vec3_normalize(vec3_t v) {
+    return vec3_div(v, vec3_length(v));
+}
+
+float vec3_dot(vec3_t a, vec3_t b) {
+    return a.x * b.x + a.y * b.y + a.z * b.z;
+}
+
+vec3_t vec3_cross(vec3_t a, vec3_t b) {
+    float x = a.y * b.z - a.z * b.y;
+    float y = a.z * b.x - a.x * b.z;
+    float z = a.x * b.y - a.y * b.x;
+    return vec3_new(x, y, z);
+}
+
+vec3_t vec3_lerp(vec3_t a, vec3_t b, float t) {
+    float x = float_lerp(a.x, b.x, t);
+    float y = float_lerp(a.y, b.y, t);
+    float z = float_lerp(a.z, b.z, t);
+    return vec3_new(x, y, z);
+}
+
+vec3_t vec3_saturate(vec3_t v) {
+    float x = float_saturate(v.x);
+    float y = float_saturate(v.y);
+    float z = float_saturate(v.z);
+    return vec3_new(x, y, z);
+}
+
+vec3_t vec3_modulate(vec3_t a, vec3_t b) {
+    return vec3_new(a.x * b.x, a.y * b.y, a.z * b.z);
+}
+
+void vec3_print(const char *name, vec3_t v) {
+    printf("vec3 %s =\n", name);
+    printf("    %12f    %12f    %12f\n", v.x, v.y, v.z);
+}
+
+/* vec4 related functions */
+
+vec4_t vec4_new(float x, float y, float z, float w) {
+    vec4_t v;
+    v.x = x;
+    v.y = y;
+    v.z = z;
+    v.w = w;
+    return v;
+}
+
+vec4_t vec4_from_vec3(vec3_t v, float w) {
+    return vec4_new(v.x, v.y, v.z, w);
+}
+
+vec4_t vec4_add(vec4_t a, vec4_t b) {
+    return vec4_new(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
+}
+
+vec4_t vec4_sub(vec4_t a, vec4_t b) {
+    return vec4_new(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
+}
+
+vec4_t vec4_mul(vec4_t v, float factor) {
+    return vec4_new(v.x * factor, v.y * factor, v.z * factor, v.w * factor);
+}
+
+vec4_t vec4_div(vec4_t v, float divisor) {
+    return vec4_mul(v, 1 / divisor);
+}
+
+vec4_t vec4_lerp(vec4_t a, vec4_t b, float t) {
+    float x = float_lerp(a.x, b.x, t);
+    float y = float_lerp(a.y, b.y, t);
+    float z = float_lerp(a.z, b.z, t);
+    float w = float_lerp(a.w, b.w, t);
+    return vec4_new(x, y, z, w);
+}
+
+vec4_t vec4_saturate(vec4_t v) {
+    float x = float_saturate(v.x);
+    float y = float_saturate(v.y);
+    float z = float_saturate(v.z);
+    float w = float_saturate(v.w);
+    return vec4_new(x, y, z, w);
+}
+
+vec4_t vec4_modulate(vec4_t a, vec4_t b) {
+    return vec4_new(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
+}
+
+void vec4_print(const char *name, vec4_t v) {
+    printf("vec4 %s =\n", name);
+    printf("    %12f    %12f    %12f    %12f\n", v.x, v.y, v.z, v.w);
+}
+
+/* quat related functions */
+
+quat_t quat_new(float x, float y, float z, float w) {
+    quat_t q;
+    q.x = x;
+    q.y = y;
+    q.z = z;
+    q.w = w;
+    return q;
+}
+
+float quat_dot(quat_t a, quat_t b) {
+    return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
+}
+
+float quat_length(quat_t q) {
+    return (float)sqrt(quat_dot(q, q));
+}
+
+quat_t quat_normalize(quat_t q) {
+    float factor = 1 / quat_length(q);
+    return quat_new(q.x * factor, q.y * factor, q.z * factor, q.w * factor);
+}
+
+/*
+ * for spherical linear interpolation, see
+ * 3D Math Primer for Graphics and Game Development, 2nd Edition, Chapter 8
+ */
+quat_t quat_slerp(quat_t a, quat_t b, float t) {
+    float cos_angle = quat_dot(a, b);
+    if (cos_angle < 0) {
+        b = quat_new(-b.x, -b.y, -b.z, -b.w);
+        cos_angle = -cos_angle;
+    }
+    if (cos_angle > 1 - EPSILON) {
+        float x = float_lerp(a.x, b.x, t);
+        float y = float_lerp(a.y, b.y, t);
+        float z = float_lerp(a.z, b.z, t);
+        float w = float_lerp(a.w, b.w, t);
+        return quat_new(x, y, z, w);
+    } else {
+        float angle = (float)acos(cos_angle);
+        float sin_angle = (float)sin(angle);
+        float angle_a = (1 - t) * angle;
+        float angle_b = t * angle;
+        float factor_a = (float)sin(angle_a) / sin_angle;
+        float factor_b = (float)sin(angle_b) / sin_angle;
+        float x = factor_a * a.x + factor_b * b.x;
+        float y = factor_a * a.y + factor_b * b.y;
+        float z = factor_a * a.z + factor_b * b.z;
+        float w = factor_a * a.w + factor_b * b.w;
+        return quat_new(x, y, z, w);
+    }
+}
+
+void quat_print(const char *name, quat_t q) {
+    printf("quat %s =\n", name);
+    printf("    %12f    %12f    %12f    %12f\n", q.x, q.y, q.z, q.w);
+}
+
+/* mat3 related functions */
+
+mat3_t mat3_identity(void) {
+    mat3_t m = {{
+        {1, 0, 0},
+        {0, 1, 0},
+        {0, 0, 1},
+    }};
+    return m;
+}
+
+mat3_t mat3_from_cols(vec3_t c0, vec3_t c1, vec3_t c2) {
+    mat3_t m;
+    m.m[0][0] = c0.x;
+    m.m[1][0] = c0.y;
+    m.m[2][0] = c0.z;
+    m.m[0][1] = c1.x;
+    m.m[1][1] = c1.y;
+    m.m[2][1] = c1.z;
+    m.m[0][2] = c2.x;
+    m.m[1][2] = c2.y;
+    m.m[2][2] = c2.z;
+    return m;
+}
+
+mat3_t mat3_from_mat4(mat4_t m) {
+    mat3_t n;
+    n.m[0][0] = m.m[0][0];
+    n.m[0][1] = m.m[0][1];
+    n.m[0][2] = m.m[0][2];
+    n.m[1][0] = m.m[1][0];
+    n.m[1][1] = m.m[1][1];
+    n.m[1][2] = m.m[1][2];
+    n.m[2][0] = m.m[2][0];
+    n.m[2][1] = m.m[2][1];
+    n.m[2][2] = m.m[2][2];
+    return n;
+}
+
+mat3_t mat3_combine(mat3_t m[4], vec4_t weights_) {
+    mat3_t combined = {{{0}}};
+    float weights[4];
+    int i, r, c;
+
+    weights[0] = weights_.x;
+    weights[1] = weights_.y;
+    weights[2] = weights_.z;
+    weights[3] = weights_.w;
+
+    for (i = 0; i < 4; i++) {
+        float weight = weights[i];
+        if (weight > 0) {
+            mat3_t source = m[i];
+            for (r = 0; r < 3; r++) {
+                for (c = 0; c < 3; c++) {
+                    combined.m[r][c] += weight * source.m[r][c];
+                }
+            }
+        }
+    }
+
+    return combined;
+}
+
+vec3_t mat3_mul_vec3(mat3_t m, vec3_t v) {
+    float product[3];
+    int i;
+    for (i = 0; i < 3; i++) {
+        float a = m.m[i][0] * v.x;
+        float b = m.m[i][1] * v.y;
+        float c = m.m[i][2] * v.z;
+        product[i] = a + b + c;
+    }
+    return vec3_new(product[0], product[1], product[2]);
+}
+
+mat3_t mat3_mul_mat3(mat3_t a, mat3_t b) {
+    mat3_t m = {{{0}}};
+    int i, j, k;
+    for (i = 0; i < 3; i++) {
+        for (j = 0; j < 3; j++) {
+            for (k = 0; k < 3; k++) {
+                m.m[i][j] += a.m[i][k] * b.m[k][j];
+            }
+        }
+    }
+    return m;
+}
+
+mat3_t mat3_inverse(mat3_t m) {
+    return mat3_transpose(mat3_inverse_transpose(m));
+}
+
+mat3_t mat3_transpose(mat3_t m) {
+    mat3_t transpose;
+    int i, j;
+    for (i = 0; i < 3; i++) {
+        for (j = 0; j < 3; j++) {
+            transpose.m[i][j] = m.m[j][i];
+        }
+    }
+    return transpose;
+}
+
+/*
+ * for determinant, adjoint, and inverse, see
+ * 3D Math Primer for Graphics and Game Development, 2nd Edition, Chapter 6
+ */
+
+static float mat3_determinant(mat3_t m) {
+    float a = +m.m[0][0] * (m.m[1][1] * m.m[2][2] - m.m[1][2] * m.m[2][1]);
+    float b = -m.m[0][1] * (m.m[1][0] * m.m[2][2] - m.m[1][2] * m.m[2][0]);
+    float c = +m.m[0][2] * (m.m[1][0] * m.m[2][1] - m.m[1][1] * m.m[2][0]);
+    return a + b + c;
+}
+
+static mat3_t mat3_adjoint(mat3_t m) {
+    mat3_t adjoint;
+    adjoint.m[0][0] = +(m.m[1][1] * m.m[2][2] - m.m[2][1] * m.m[1][2]);
+    adjoint.m[0][1] = -(m.m[1][0] * m.m[2][2] - m.m[2][0] * m.m[1][2]);
+    adjoint.m[0][2] = +(m.m[1][0] * m.m[2][1] - m.m[2][0] * m.m[1][1]);
+    adjoint.m[1][0] = -(m.m[0][1] * m.m[2][2] - m.m[2][1] * m.m[0][2]);
+    adjoint.m[1][1] = +(m.m[0][0] * m.m[2][2] - m.m[2][0] * m.m[0][2]);
+    adjoint.m[1][2] = -(m.m[0][0] * m.m[2][1] - m.m[2][0] * m.m[0][1]);
+    adjoint.m[2][0] = +(m.m[0][1] * m.m[1][2] - m.m[1][1] * m.m[0][2]);
+    adjoint.m[2][1] = -(m.m[0][0] * m.m[1][2] - m.m[1][0] * m.m[0][2]);
+    adjoint.m[2][2] = +(m.m[0][0] * m.m[1][1] - m.m[1][0] * m.m[0][1]);
+    return adjoint;
+}
+
+mat3_t mat3_inverse_transpose(mat3_t m) {
+    mat3_t adjoint, inverse_transpose;
+    float determinant, inv_determinant;
+    int i, j;
+
+    adjoint = mat3_adjoint(m);
+    determinant = mat3_determinant(m);
+    inv_determinant = 1 / determinant;
+    for (i = 0; i < 3; i++) {
+        for (j = 0; j < 3; j++) {
+            inverse_transpose.m[i][j] = adjoint.m[i][j] * inv_determinant;
+        }
+    }
+    return inverse_transpose;
+}
+
+void mat3_print(const char *name, mat3_t m) {
+    int i, j;
+    printf("mat3 %s =\n", name);
+    for (i = 0; i < 3; i++) {
+        for (j = 0; j < 3; j++) {
+            printf("    %12f", m.m[i][j]);
+        }
+        printf("\n");
+    }
+}
+
+/* mat4 related functions */
+
+mat4_t mat4_identity(void) {
+    mat4_t m = {{
+        {1, 0, 0, 0},
+        {0, 1, 0, 0},
+        {0, 0, 1, 0},
+        {0, 0, 0, 1},
+    }};
+    return m;
+}
+
+mat4_t mat4_from_quat(quat_t q) {
+    mat4_t m = mat4_identity();
+    float xx = q.x * q.x;
+    float xy = q.x * q.y;
+    float xz = q.x * q.z;
+    float xw = q.x * q.w;
+    float yy = q.y * q.y;
+    float yz = q.y * q.z;
+    float yw = q.y * q.w;
+    float zz = q.z * q.z;
+    float zw = q.z * q.w;
+
+    m.m[0][0] = 1 - 2 * (yy + zz);
+    m.m[0][1] = 2 * (xy - zw);
+    m.m[0][2] = 2 * (xz + yw);
+
+    m.m[1][0] = 2 * (xy + zw);
+    m.m[1][1] = 1 - 2 * (xx + zz);
+    m.m[1][2] = 2 * (yz - xw);
+
+    m.m[2][0] = 2 * (xz - yw);
+    m.m[2][1] = 2 * (yz + xw);
+    m.m[2][2] = 1 - 2 * (xx + yy);
+
+    return m;
+}
+
+mat4_t mat4_from_trs(vec3_t t, quat_t r, vec3_t s) {
+    mat4_t translation = mat4_translate(t.x, t.y, t.z);
+    mat4_t rotation = mat4_from_quat(r);
+    mat4_t scale = mat4_scale(s.x, s.y, s.z);
+    return mat4_mul_mat4(translation, mat4_mul_mat4(rotation, scale));
+}
+
+mat4_t mat4_combine(mat4_t m[4], vec4_t weights_) {
+    mat4_t combined = {{{0}}};
+    float weights[4];
+    int i, r, c;
+
+    weights[0] = weights_.x;
+    weights[1] = weights_.y;
+    weights[2] = weights_.z;
+    weights[3] = weights_.w;
+
+    for (i = 0; i < 4; i++) {
+        float weight = weights[i];
+        if (weight > 0) {
+            mat4_t source = m[i];
+            for (r = 0; r < 4; r++) {
+                for (c = 0; c < 4; c++) {
+                    combined.m[r][c] += weight * source.m[r][c];
+                }
+            }
+        }
+    }
+
+    return combined;
+}
+
+vec4_t mat4_mul_vec4(mat4_t m, vec4_t v) {
+    float product[4];
+    int i;
+    for (i = 0; i < 4; i++) {
+        float a = m.m[i][0] * v.x;
+        float b = m.m[i][1] * v.y;
+        float c = m.m[i][2] * v.z;
+        float d = m.m[i][3] * v.w;
+        product[i] = a + b + c + d;
+    }
+    return vec4_new(product[0], product[1], product[2], product[3]);
+}
+
+mat4_t mat4_mul_mat4(mat4_t a, mat4_t b) {
+    mat4_t m = {{{0}}};
+    int i, j, k;
+    for (i = 0; i < 4; i++) {
+        for (j = 0; j < 4; j++) {
+            for (k = 0; k < 4; k++) {
+                m.m[i][j] += a.m[i][k] * b.m[k][j];
+            }
+        }
+    }
+    return m;
+}
+
+mat4_t mat4_inverse(mat4_t m) {
+    return mat4_transpose(mat4_inverse_transpose(m));
+}
+
+mat4_t mat4_transpose(mat4_t m) {
+    mat4_t transpose;
+    int i, j;
+    for (i = 0; i < 4; i++) {
+        for (j = 0; j < 4; j++) {
+            transpose.m[i][j] = m.m[j][i];
+        }
+    }
+    return transpose;
+}
+
+/*
+ * for determinant, minor, cofactor, adjoint, and inverse, see
+ * 3D Math Primer for Graphics and Game Development, 2nd Edition, Chapter 6
+ */
+
+static float mat4_minor(mat4_t m, int r, int c) {
+    mat3_t cut_down;
+    int i, j;
+    for (i = 0; i < 3; i++) {
+        for (j = 0; j < 3; j++) {
+            int row = i < r ? i : i + 1;
+            int col = j < c ? j : j + 1;
+            cut_down.m[i][j] = m.m[row][col];
+        }
+    }
+    return mat3_determinant(cut_down);
+}
+
+static float mat4_cofactor(mat4_t m, int r, int c) {
+    float sign = (r + c) % 2 == 0 ? 1.0f : -1.0f;
+    float minor = mat4_minor(m, r, c);
+    return sign * minor;
+}
+
+static mat4_t mat4_adjoint(mat4_t m) {
+    mat4_t adjoint;
+    int i, j;
+    for (i = 0; i < 4; i++) {
+        for (j = 0; j < 4; j++) {
+            adjoint.m[i][j] = mat4_cofactor(m, i, j);
+        }
+    }
+    return adjoint;
+}
+
+mat4_t mat4_inverse_transpose(mat4_t m) {
+    mat4_t adjoint, inverse_transpose;
+    float determinant, inv_determinant;
+    int i, j;
+
+    adjoint = mat4_adjoint(m);
+    determinant = 0;
+    for (i = 0; i < 4; i++) {
+        determinant += m.m[0][i] * adjoint.m[0][i];
+    }
+    inv_determinant = 1 / determinant;
+    for (i = 0; i < 4; i++) {
+        for (j = 0; j < 4; j++) {
+            inverse_transpose.m[i][j] = adjoint.m[i][j] * inv_determinant;
+        }
+    }
+    return inverse_transpose;
+}
+
+void mat4_print(const char *name, mat4_t m) {
+    int i, j;
+    printf("mat4 %s =\n", name);
+    for (i = 0; i < 4; i++) {
+        for (j = 0; j < 4; j++) {
+            printf("    %12f", m.m[i][j]);
+        }
+        printf("\n");
+    }
+}
+
+/* transformation matrices */
+
+/*
+ * tx, ty, tz: the x, y, and z coordinates of a translation vector
+ *
+ *  1  0  0 tx
+ *  0  1  0 ty
+ *  0  0  1 tz
+ *  0  0  0  1
+ *
+ * see http://docs.gl/gl2/glTranslate
+ */
+mat4_t mat4_translate(float tx, float ty, float tz) {
+    mat4_t m = mat4_identity();
+    m.m[0][3] = tx;
+    m.m[1][3] = ty;
+    m.m[2][3] = tz;
+    return m;
+}
+
+/*
+ * sx, sy, sz: scale factors along the x, y, and z axes, respectively
+ *
+ * sx  0  0  0
+ *  0 sy  0  0
+ *  0  0 sz  0
+ *  0  0  0  1
+ *
+ * see http://docs.gl/gl2/glScale
+ */
+mat4_t mat4_scale(float sx, float sy, float sz) {
+    mat4_t m = mat4_identity();
+    //assert(sx != 0 && sy != 0 && sz != 0);
+    m.m[0][0] = sx;
+    m.m[1][1] = sy;
+    m.m[2][2] = sz;
+    return m;
+}
+
+/*
+ * angle: the angle of rotation, in radians
+ * vx, vy, vz: the x, y, and z coordinates of a vector, respectively
+ *
+ * nx*nx*(1-c)+c     ny*nx*(1-c)-s*nz  nz*nx*(1-c)+s*ny  0
+ * nx*ny*(1-c)+s*nz  ny*ny*(1-c)+c     nz*ny*(1-c)-s*nx  0
+ * nx*nz*(1-c)-s*ny  ny*nz*(1-c)+s*nx  nz*nz*(1-c)+c     0
+ * 0                 0                 0                 1
+ *
+ * nx, ny, nz: the normalized coordinates of the vector, respectively
+ * s, c: sin(angle), cos(angle)
+ *
+ * see http://docs.gl/gl2/glRotate
+ */
+mat4_t mat4_rotate(float angle, float vx, float vy, float vz) {
+    vec3_t n = vec3_normalize(vec3_new(vx, vy, vz));
+    float c = (float)cos(angle);
+    float s = (float)sin(angle);
+    mat4_t m = mat4_identity();
+
+    m.m[0][0] = n.x * n.x * (1 - c) + c;
+    m.m[0][1] = n.y * n.x * (1 - c) - s * n.z;
+    m.m[0][2] = n.z * n.x * (1 - c) + s * n.y;
+
+    m.m[1][0] = n.x * n.y * (1 - c) + s * n.z;
+    m.m[1][1] = n.y * n.y * (1 - c) + c;
+    m.m[1][2] = n.z * n.y * (1 - c) - s * n.x;
+
+    m.m[2][0] = n.x * n.z * (1 - c) - s * n.y;
+    m.m[2][1] = n.y * n.z * (1 - c) + s * n.x;
+    m.m[2][2] = n.z * n.z * (1 - c) + c;
+
+    return m;
+}
+
+/*
+ * angle: the angle of rotation, in radians
+ *
+ *  1  0  0  0
+ *  0  c -s  0
+ *  0  s  c  0
+ *  0  0  0  1
+ *
+ * see http://www.songho.ca/opengl/gl_anglestoaxes.html
+ */
+mat4_t mat4_rotate_x(float angle) {
+    float c = (float)cos(angle);
+    float s = (float)sin(angle);
+    mat4_t m = mat4_identity();
+    m.m[1][1] = c;
+    m.m[1][2] = -s;
+    m.m[2][1] = s;
+    m.m[2][2] = c;
+    return m;
+}
+
+/*
+ * angle: the angle of rotation, in radians
+ *
+ *  c  0  s  0
+ *  0  1  0  0
+ * -s  0  c  0
+ *  0  0  0  1
+ *
+ * see http://www.songho.ca/opengl/gl_anglestoaxes.html
+ */
+mat4_t mat4_rotate_y(float angle) {
+    float c = (float)cos(angle);
+    float s = (float)sin(angle);
+    mat4_t m = mat4_identity();
+    m.m[0][0] = c;
+    m.m[0][2] = s;
+    m.m[2][0] = -s;
+    m.m[2][2] = c;
+    return m;
+}
+
+/*
+ * angle: the angle of rotation, in radians
+ *
+ *  c -s  0  0
+ *  s  c  0  0
+ *  0  0  1  0
+ *  0  0  0  1
+ *
+ * see http://www.songho.ca/opengl/gl_anglestoaxes.html
+ */
+mat4_t mat4_rotate_z(float angle) {
+    float c = (float)cos(angle);
+    float s = (float)sin(angle);
+    mat4_t m = mat4_identity();
+    m.m[0][0] = c;
+    m.m[0][1] = -s;
+    m.m[1][0] = s;
+    m.m[1][1] = c;
+    return m;
+}
+
+/*
+ * eye: the position of the eye point
+ * target: the position of the target point
+ * up: the direction of the up vector
+ *
+ * x_axis.x  x_axis.y  x_axis.z  -dot(x_axis,eye)
+ * y_axis.x  y_axis.y  y_axis.z  -dot(y_axis,eye)
+ * z_axis.x  z_axis.y  z_axis.z  -dot(z_axis,eye)
+ *        0         0         0                 1
+ *
+ * z_axis: normalize(eye-target), the backward vector
+ * x_axis: normalize(cross(up,z_axis)), the right vector
+ * y_axis: cross(z_axis,x_axis), the up vector
+ *
+ * see http://www.songho.ca/opengl/gl_camera.html
+ */
+mat4_t mat4_lookat(vec3_t eye, vec3_t target, vec3_t up) {
+    vec3_t z_axis = vec3_normalize(vec3_sub(eye, target));
+    vec3_t x_axis = vec3_normalize(vec3_cross(up, z_axis));
+    vec3_t y_axis = vec3_cross(z_axis, x_axis);
+    mat4_t m = mat4_identity();
+
+    m.m[0][0] = x_axis.x;
+    m.m[0][1] = x_axis.y;
+    m.m[0][2] = x_axis.z;
+
+    m.m[1][0] = y_axis.x;
+    m.m[1][1] = y_axis.y;
+    m.m[1][2] = y_axis.z;
+
+    m.m[2][0] = z_axis.x;
+    m.m[2][1] = z_axis.y;
+    m.m[2][2] = z_axis.z;
+
+    m.m[0][3] = -vec3_dot(x_axis, eye);
+    m.m[1][3] = -vec3_dot(y_axis, eye);
+    m.m[2][3] = -vec3_dot(z_axis, eye);
+
+    return m;
+}
+
+/*
+ * left, right: the coordinates for the left and right clipping planes
+ * bottom, top: the coordinates for the bottom and top clipping planes
+ * near, far: the distances to the near and far depth clipping planes
+ *
+ * 2/(r-l)        0         0  -(r+l)/(r-l)
+ *       0  2/(t-b)         0  -(t+b)/(t-b)
+ *       0        0  -2/(f-n)  -(f+n)/(f-n)
+ *       0        0         0             1
+ *
+ * see http://docs.gl/gl2/glOrtho
+ */
+mat4_t mat4_ortho(float left, float right, float bottom, float top,
+                  float near, float far) {
+    float x_range = right - left;
+    float y_range = top - bottom;
+    float z_range = far - near;
+    mat4_t m = mat4_identity();
+    assert(x_range > 0 && y_range > 0 && z_range > 0);
+    m.m[0][0] = 2 / x_range;
+    m.m[1][1] = 2 / y_range;
+    m.m[2][2] = -2 / z_range;
+    m.m[0][3] = -(left + right) / x_range;
+    m.m[1][3] = -(bottom + top) / y_range;
+    m.m[2][3] = -(near + far) / z_range;
+    return m;
+}
+
+/*
+ * left, right: the coordinates for the left and right clipping planes
+ * bottom, top: the coordinates for the bottom and top clipping planes
+ * near, far: the distances to the near and far depth clipping planes
+ *
+ * 2n/(r-l)         0   (r+l)/(r-l)           0
+ *        0  2n/(t-b)   (t+b)/(t-b)           0
+ *        0         0  -(f+n)/(f-n)  -2fn/(f-n)
+ *        0         0            -1           0
+ *
+ * see http://docs.gl/gl2/glFrustum
+ */
+mat4_t mat4_frustum(float left, float right, float bottom, float top,
+                    float near, float far) {
+    float x_range = right - left;
+    float y_range = top - bottom;
+    float z_range = far - near;
+    mat4_t m = mat4_identity();
+    assert(near > 0 && far > 0);
+    assert(x_range > 0 && y_range > 0 && z_range > 0);
+    m.m[0][0] = 2 * near / x_range;
+    m.m[1][1] = 2 * near / y_range;
+    m.m[0][2] = (left + right) / x_range;
+    m.m[1][2] = (bottom + top) / y_range;
+    m.m[2][2] = -(near + far) / z_range;
+    m.m[2][3] = -2 * near * far / z_range;
+    m.m[3][2] = -1;
+    m.m[3][3] = 0;
+    return m;
+}
+
+/*
+ * right: the coordinates for the right clipping planes (left == -right)
+ * top: the coordinates for the top clipping planes (bottom == -top)
+ * near, far: the distances to the near and far depth clipping planes
+ *
+ * 1/r    0         0             0
+ *   0  1/t         0             0
+ *   0    0  -2/(f-n)  -(f+n)/(f-n)
+ *   0    0         0             1
+ *
+ * this is the same as
+ *     float left = -right;
+ *     float bottom = -top;
+ *     mat4_ortho(left, right, bottom, top, near, far);
+ *
+ * see http://www.songho.ca/opengl/gl_projectionmatrix.html
+ */
+mat4_t mat4_orthographic(float right, float top, float near, float far) {
+    float z_range = far - near;
+    mat4_t m = mat4_identity();
+    assert(right > 0 && top > 0 && z_range > 0);
+    m.m[0][0] = 1 / right;
+    m.m[1][1] = 1 / top;
+    m.m[2][2] = -2 / z_range;
+    m.m[2][3] = -(near + far) / z_range;
+    return m;
+}
+
+/*
+ * fovy: the field of view angle in the y direction, in radians
+ * aspect: the aspect ratio, defined as width divided by height
+ * near, far: the distances to the near and far depth clipping planes
+ *
+ * 1/(aspect*tan(fovy/2))              0             0           0
+ *                      0  1/tan(fovy/2)             0           0
+ *                      0              0  -(f+n)/(f-n)  -2fn/(f-n)
+ *                      0              0            -1           0
+ *
+ * this is the same as
+ *     float half_h = near * (float)tan(fovy / 2);
+ *     float half_w = half_h * aspect;
+ *     mat4_frustum(-half_w, half_w, -half_h, half_h, near, far);
+ *
+ * see http://www.songho.ca/opengl/gl_projectionmatrix.html
+ */
+mat4_t mat4_perspective(float fovy, float aspect, float near, float far) {
+    float z_range = far - near;
+    mat4_t m = mat4_identity();
+    assert(fovy > 0 && aspect > 0);
+    assert(near > 0 && far > 0 && z_range > 0);
+    m.m[1][1] = 1 / (float)tan(fovy / 2);
+    m.m[0][0] = m.m[1][1] / aspect;
+    m.m[2][2] = -(near + far) / z_range;
+    m.m[2][3] = -2 * near * far / z_range;
+    m.m[3][2] = -1;
+    m.m[3][3] = 0;
+    return m;
+}


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/maths.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/maths.h
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/maths.h                     
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/maths.h     2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,114 @@
+#ifndef MATHS_H
+#define MATHS_H
+
+typedef struct {float x, y;} vec2_t;
+typedef struct {float x, y, z;} vec3_t;
+typedef struct {float x, y, z, w;} vec4_t;
+typedef struct {float x, y, z, w;} quat_t;
+typedef struct {float m[3][3];} mat3_t;
+typedef struct {float m[4][4];} mat4_t;
+
+/* float related functions */
+float float_min(float a, float b);
+float float_max(float a, float b);
+float float_lerp(float a, float b, float t);
+float float_clamp(float f, float min, float max);
+float float_saturate(float f);
+float float_from_uchar(unsigned char value);
+unsigned char float_to_uchar(float value);
+float float_srgb2linear(float value);
+float float_linear2srgb(float value);
+float float_aces(float value);
+void float_print(const char *name, float f);
+
+/* vec2 related functions */
+vec2_t vec2_new(float x, float y);
+vec2_t vec2_min(vec2_t a, vec2_t b);
+vec2_t vec2_max(vec2_t a, vec2_t b);
+vec2_t vec2_add(vec2_t a, vec2_t b);
+vec2_t vec2_sub(vec2_t a, vec2_t b);
+vec2_t vec2_mul(vec2_t v, float factor);
+vec2_t vec2_div(vec2_t v, float divisor);
+float vec2_length(vec2_t v);
+float vec2_edge(vec2_t start, vec2_t end, vec2_t v);
+void vec2_print(const char *name, vec2_t v);
+
+/* vec3 related functions */
+vec3_t vec3_new(float x, float y, float z);
+vec3_t vec3_from_vec4(vec4_t v);
+vec3_t vec3_min(vec3_t a, vec3_t b);
+vec3_t vec3_max(vec3_t a, vec3_t b);
+vec3_t vec3_add(vec3_t a, vec3_t b);
+vec3_t vec3_sub(vec3_t a, vec3_t b);
+vec3_t vec3_mul(vec3_t v, float factor);
+vec3_t vec3_div(vec3_t v, float divisor);
+vec3_t vec3_negate(vec3_t v);
+float vec3_length(vec3_t v);
+vec3_t vec3_normalize(vec3_t v);
+float vec3_dot(vec3_t a, vec3_t b);
+vec3_t vec3_cross(vec3_t a, vec3_t b);
+vec3_t vec3_lerp(vec3_t a, vec3_t b, float t);
+vec3_t vec3_saturate(vec3_t v);
+vec3_t vec3_modulate(vec3_t a, vec3_t b);
+void vec3_print(const char *name, vec3_t v);
+
+/* vec4 related functions */
+vec4_t vec4_new(float x, float y, float z, float w);
+vec4_t vec4_from_vec3(vec3_t v, float w);
+vec4_t vec4_add(vec4_t a, vec4_t b);
+vec4_t vec4_sub(vec4_t a, vec4_t b);
+vec4_t vec4_mul(vec4_t v, float factor);
+vec4_t vec4_div(vec4_t v, float divisor);
+vec4_t vec4_lerp(vec4_t a, vec4_t b, float t);
+vec4_t vec4_saturate(vec4_t v);
+vec4_t vec4_modulate(vec4_t a, vec4_t b);
+void vec4_print(const char *name, vec4_t v);
+
+/* quat related functions */
+quat_t quat_new(float x, float y, float z, float w);
+float quat_dot(quat_t a, quat_t b);
+float quat_length(quat_t q);
+quat_t quat_normalize(quat_t q);
+quat_t quat_slerp(quat_t a, quat_t b, float t);
+void quat_print(const char *name, quat_t q);
+
+/* mat3 related functions */
+mat3_t mat3_identity(void);
+mat3_t mat3_from_cols(vec3_t c0, vec3_t c1, vec3_t c2);
+mat3_t mat3_from_mat4(mat4_t m);
+mat3_t mat3_combine(mat3_t m[4], vec4_t weights);
+vec3_t mat3_mul_vec3(mat3_t m, vec3_t v);
+mat3_t mat3_mul_mat3(mat3_t a, mat3_t b);
+mat3_t mat3_inverse(mat3_t m);
+mat3_t mat3_transpose(mat3_t m);
+mat3_t mat3_inverse_transpose(mat3_t m);
+void mat3_print(const char *name, mat3_t m);
+
+/* mat4 related functions */
+mat4_t mat4_identity(void);
+mat4_t mat4_from_quat(quat_t q);
+mat4_t mat4_from_trs(vec3_t t, quat_t r, vec3_t s);
+mat4_t mat4_combine(mat4_t m[4], vec4_t weights);
+vec4_t mat4_mul_vec4(mat4_t m, vec4_t v);
+mat4_t mat4_mul_mat4(mat4_t a, mat4_t b);
+mat4_t mat4_inverse(mat4_t m);
+mat4_t mat4_transpose(mat4_t m);
+mat4_t mat4_inverse_transpose(mat4_t m);
+void mat4_print(const char *name, mat4_t m);
+
+/* transformation matrices */
+mat4_t mat4_translate(float tx, float ty, float tz);
+mat4_t mat4_scale(float sx, float sy, float sz);
+mat4_t mat4_rotate(float angle, float vx, float vy, float vz);
+mat4_t mat4_rotate_x(float angle);
+mat4_t mat4_rotate_y(float angle);
+mat4_t mat4_rotate_z(float angle);
+mat4_t mat4_lookat(vec3_t eye, vec3_t target, vec3_t up);
+mat4_t mat4_ortho(float left, float right, float bottom, float top,
+                  float near, float far);
+mat4_t mat4_frustum(float left, float right, float bottom, float top,
+                    float near, float far);
+mat4_t mat4_orthographic(float right, float top, float near, float far);
+mat4_t mat4_perspective(float fovy, float aspect, float near, float far);
+
+#endif


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/maths.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/mesh.c
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/mesh.c                      
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/mesh.c      2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,200 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "darray.h"
+#include "macro.h"
+#include "maths.h"
+#include "mesh.h"
+#include "private.h"
+
+struct mesh {
+    int num_faces;
+    vertex_t *vertices;
+    vec3_t center;
+};
+
+/* mesh loading/releasing */
+
+static mesh_t *build_mesh(
+        vec3_t *positions, vec2_t *texcoords, vec3_t *normals,
+        vec4_t *tangents, vec4_t *joints, vec4_t *weights,
+        int *position_indices, int *texcoord_indices, int *normal_indices) {
+    vec3_t bbox_min = vec3_new(+1e6, +1e6, +1e6);
+    vec3_t bbox_max = vec3_new(-1e6, -1e6, -1e6);
+    int num_indices = darray_size(position_indices);
+    int num_faces = num_indices / 3;
+    vertex_t *vertices;
+    mesh_t *mesh;
+    int i;
+
+    assert(num_faces > 0 && num_faces * 3 == num_indices);
+    assert(darray_size(position_indices) == num_indices);
+    assert(darray_size(texcoord_indices) == num_indices);
+    assert(darray_size(normal_indices) == num_indices);
+
+    vertices = (vertex_t*)malloc(sizeof(vertex_t) * num_indices);
+    for (i = 0; i < num_indices; i++) {
+        int position_index = position_indices[i];
+        int texcoord_index = texcoord_indices[i];
+        int normal_index = normal_indices[i];
+        assert(position_index >= 0 && position_index < darray_size(positions));
+        assert(texcoord_index >= 0 && texcoord_index < darray_size(texcoords));
+        assert(normal_index >= 0 && normal_index < darray_size(normals));
+        vertices[i].position = positions[position_index];
+        vertices[i].texcoord = texcoords[texcoord_index];
+        vertices[i].normal = normals[normal_index];
+
+        if (tangents) {
+            int tangent_index = position_index;
+            assert(tangent_index >= 0 && tangent_index < 
darray_size(tangents));
+            vertices[i].tangent = tangents[tangent_index];
+        } else {
+            vertices[i].tangent = vec4_new(1, 0, 0, 1);
+        }
+
+        if (joints) {
+            int joint_index = position_index;
+            assert(joint_index >= 0 && joint_index < darray_size(joints));
+            vertices[i].joint = joints[joint_index];
+        } else {
+            vertices[i].joint = vec4_new(0, 0, 0, 0);
+        }
+
+        if (weights) {
+            int weight_index = position_index;
+            assert(weight_index >= 0 && weight_index < darray_size(weights));
+            vertices[i].weight = weights[weight_index];
+        } else {
+            vertices[i].weight = vec4_new(0, 0, 0, 0);
+        }
+
+        bbox_min = vec3_min(bbox_min, vertices[i].position);
+        bbox_max = vec3_max(bbox_max, vertices[i].position);
+    }
+
+    mesh = (mesh_t*)malloc(sizeof(mesh_t));
+    mesh->num_faces = num_faces;
+    mesh->vertices = vertices;
+    mesh->center = vec3_div(vec3_add(bbox_min, bbox_max), 2);
+
+    return mesh;
+}
+
+static mesh_t *load_obj(const char *filename) {
+    vec3_t *positions = NULL;
+    vec2_t *texcoords = NULL;
+    vec3_t *normals = NULL;
+    vec4_t *tangents = NULL;
+    vec4_t *joints = NULL;
+    vec4_t *weights = NULL;
+    int *position_indices = NULL;
+    int *texcoord_indices = NULL;
+    int *normal_indices = NULL;
+    char line[LINE_SIZE];
+    mesh_t *mesh;
+    FILE *file;
+
+    file = fopen(filename, "rb");
+    assert(file != NULL);
+    while (1) {
+        int items;
+        if (fgets(line, LINE_SIZE, file) == NULL) {
+            break;
+        } else if (strncmp(line, "v ", 2) == 0) {               /* position */
+            vec3_t position;
+            items = sscanf(line, "v %f %f %f",
+                           &position.x, &position.y, &position.z);
+            assert(items == 3);
+            darray_push(positions, position, vec3_t);
+        } else if (strncmp(line, "vt ", 3) == 0) {              /* texcoord */
+            vec2_t texcoord;
+            items = sscanf(line, "vt %f %f",
+                           &texcoord.x, &texcoord.y);
+            assert(items == 2);
+            darray_push(texcoords, texcoord, vec2_t);
+        } else if (strncmp(line, "vn ", 3) == 0) {              /* normal */
+            vec3_t normal;
+            items = sscanf(line, "vn %f %f %f",
+                           &normal.x, &normal.y, &normal.z);
+            assert(items == 3);
+            darray_push(normals, normal, vec3_t);
+        } else if (strncmp(line, "f ", 2) == 0) {               /* face */
+            int i;
+            int pos_indices[3], uv_indices[3], n_indices[3];
+            items = sscanf(line, "f %d/%d/%d %d/%d/%d %d/%d/%d",
+                           &pos_indices[0], &uv_indices[0], &n_indices[0],
+                           &pos_indices[1], &uv_indices[1], &n_indices[1],
+                           &pos_indices[2], &uv_indices[2], &n_indices[2]);
+            assert(items == 9);
+            for (i = 0; i < 3; i++) {
+                darray_push(position_indices, pos_indices[i] - 1, int);
+                darray_push(texcoord_indices, uv_indices[i] - 1, int);
+                darray_push(normal_indices, n_indices[i] - 1, int);
+            }
+        } else if (strncmp(line, "# ext.tangent ", 14) == 0) {  /* tangent */
+            vec4_t tangent;
+            items = sscanf(line, "# ext.tangent %f %f %f %f",
+                           &tangent.x, &tangent.y, &tangent.z, &tangent.w);
+            assert(items == 4);
+            darray_push(tangents, tangent, vec4_t);
+        } else if (strncmp(line, "# ext.joint ", 12) == 0) {    /* joint */
+            vec4_t joint;
+            items = sscanf(line, "# ext.joint %f %f %f %f",
+                           &joint.x, &joint.y, &joint.z, &joint.w);
+            assert(items == 4);
+            darray_push(joints, joint, vec4_t);
+        } else if (strncmp(line, "# ext.weight ", 13) == 0) {   /* weight */
+            vec4_t weight;
+            items = sscanf(line, "# ext.weight %f %f %f %f",
+                           &weight.x, &weight.y, &weight.z, &weight.w);
+            assert(items == 4);
+            darray_push(weights, weight, vec4_t);
+        }
+        UNUSED_VAR(items);
+    }
+    fclose(file);
+
+    mesh = build_mesh(positions, texcoords, normals, tangents, joints, weights,
+                      position_indices, texcoord_indices, normal_indices);
+    darray_free(positions);
+    darray_free(texcoords);
+    darray_free(normals);
+    darray_free(tangents);
+    darray_free(joints);
+    darray_free(weights);
+    darray_free(position_indices);
+    darray_free(texcoord_indices);
+    darray_free(normal_indices);
+
+    return mesh;
+}
+
+mesh_t *mesh_load(const char *filename) {
+    const char *extension = private_get_extension(filename);
+    if (strcmp(extension, "obj") == 0) {
+        return load_obj(filename);
+    } else {
+        assert(0);
+        return NULL;
+    }
+}
+
+void mesh_release(mesh_t *mesh) {
+    free(mesh->vertices);
+    free(mesh);
+}
+
+/* vertex retrieving */
+
+int mesh_get_num_faces(mesh_t *mesh) {
+    return mesh->num_faces;
+}
+
+vertex_t *mesh_get_vertices(mesh_t *mesh) {
+    return mesh->vertices;
+}
+
+vec3_t mesh_get_center(mesh_t *mesh) {
+    return mesh->center;
+}


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/mesh.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/mesh.h
===================================================================
--- brlcad/branches/swrast/src/libdm/tests/renderer/mesh.h                      
        (rev 0)
+++ brlcad/branches/swrast/src/libdm/tests/renderer/mesh.h      2020-08-27 
12:04:42 UTC (rev 76973)
@@ -0,0 +1,26 @@
+#ifndef MESH_H
+#define MESH_H
+
+#include "maths.h"
+
+typedef struct mesh mesh_t;
+
+typedef struct {
+    vec3_t position;
+    vec2_t texcoord;
+    vec3_t normal;
+    vec4_t tangent;
+    vec4_t joint;
+    vec4_t weight;
+} vertex_t;
+
+/* mesh loading/releasing */
+mesh_t *mesh_load(const char *filename);
+void mesh_release(mesh_t *mesh);
+
+/* vertex retrieving */
+int mesh_get_num_faces(mesh_t *mesh);
+vertex_t *mesh_get_vertices(mesh_t *mesh);
+vec3_t mesh_get_center(mesh_t *mesh);
+
+#endif


Property changes on: brlcad/branches/swrast/src/libdm/tests/renderer/mesh.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/swrast/src/libdm/tests/renderer/platform.h

@@ Diff output truncated at 100000 characters. @@
This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



_______________________________________________
BRL-CAD Source Commits mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/brlcad-commits

Reply via email to