src/gl/utils.c
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "utils.h"
#include "TinyPngOut.h"
Various helper functions
A helper function to write a PPM file from a RGB buffer. Downsampling by averaging is performed if samples is larger than one.
void gl_write_image (FILE * fp, const GLubyte * buffer,
unsigned width, unsigned height, unsigned samples)
{
const GLubyte *ptr = buffer;
if (samples < 1)
= 1;
samples if (samples > 4)
= 4;
samples
/= samples, height /= samples;
width fprintf (fp, "P6 %d %d 255\n", width, height);
int x, y, j, k;
for (y = height - 1; y >= 0; y--)
for (x = 0; x < width; x++) {
int r = 0, g = 0, b = 0;
for (j = 0; j < samples; j++)
for (k = 0; k < samples; k++) {
int i = (((y*samples + j)*width + x)*samples + k)*4;
if (ptr)
+= ptr[i], g += ptr[i+1], b += ptr[i+2];
r }
(r/samples/samples, fp); /* write red */
fputc (g/samples/samples, fp); /* write green */
fputc (b/samples/samples, fp); /* write blue */
fputc }
}
A helper function to write a PNG file from a RGB buffer. Downsampling by averaging is performed if samples is larger than one.
void gl_write_image_png (FILE * fp, const GLubyte * buffer,
unsigned width, unsigned height, unsigned samples)
{
const GLubyte *ptr = buffer;
if (samples < 1)
= 1;
samples if (samples > 4)
= 4;
samples
/= samples, height /= samples;
width struct TinyPngOut pngout;
enum TinyPngOut_Status status = TinyPngOut_init (&pngout, width, height, fp);
if (status != TINYPNGOUT_OK) {
fprintf (stderr, "error: TinyPngOut init failed\n");
return;
}
int x, y, j, k;
for (y = height - 1; y >= 0; y--)
for (x = 0; x < width; x++) {
int r = 0, g = 0, b = 0;
for (j = 0; j < samples; j++)
for (k = 0; k < samples; k++) {
int i = (((y*samples + j)*width + x)*samples + k)*4;
if (ptr)
+= ptr[i], g += ptr[i+1], b += ptr[i+2];
r }
uint8_t pixel[3] = { r/samples/samples, g/samples/samples, b/samples/samples };
= TinyPngOut_write (&pngout, pixel, 1);
status if (status != TINYPNGOUT_OK) {
fprintf (stderr, "error: TinyPngOut write failed\n");
return;
}
}
}
This is the basic OpenGL setup.
void init_gl() {
[4] = { 0.0, 0.0, 50.0, 0.0 };
GLfloat light0_pos
(GL_CULL_FACE);
glDisable (GL_DEPTH_TEST);
glEnable (GL_NORMALIZE);
glEnable
/* speedups */
(GL_DITHER);
glEnable (GL_SMOOTH);
glShadeModel (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glHint (GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
glHint
/* light */
(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glLightModeli (GL_LIGHT0, GL_POSITION, light0_pos);
glLightfv [4] = { 0.8, 0.8, 0.8, 1 };
GLfloat diffuse(GL_LIGHT0, GL_DIFFUSE, diffuse);
glLightfv (GL_LIGHT0);
glEnable (GL_LIGHTING);
glEnable
(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glColorMaterial (GL_COLOR_MATERIAL);
glEnable }
void gl_draw_texture (GLuint id, int width, int height)
{
(GL_PROJECTION);
glMatrixMode ();
glPushMatrix();
glLoadIdentity(0.0, width, 0.0, height, -1.0, 1.0);
glOrtho (GL_MODELVIEW);
glMatrixMode ();
glPushMatrix
();
glLoadIdentity(GL_LIGHTING);
glDisable
(1,1,1);
glColor3f (GL_TEXTURE_2D);
glEnable (GL_TEXTURE_2D, id);
glBindTexture
(GL_QUADS);
glBegin(0, 0); glVertex3f (0, 0, 0);
glTexCoord2f(0, 1); glVertex3f (0, 100, 0);
glTexCoord2f(1, 1); glVertex3f (100, 100, 0);
glTexCoord2f(1, 0); glVertex3f (100, 0, 0);
glTexCoord2f();
glEnd
(GL_TEXTURE_2D);
glDisable();
glPopMatrix
(GL_PROJECTION);
glMatrixMode();
glPopMatrix
(GL_MODELVIEW);
glMatrixMode}
#define RC(r,c) m[(r)+(c)*4]
#define RCM(m,r,c) (m)[(r)+(c)*4]
void matrix_multiply (float * m, const float * n)
{
float o[16];
int i;
for (i = 0; i < 16; i++) o[i] = m[i];
(0,0)=RCM(o,0,0)*RCM(n,0,0)+RCM(o,0,1)*RCM(n,1,0)+
RC(o,0,2)*RCM(n,2,0)+RCM(o,0,3)*RCM(n,3,0);
RCM(0,1)=RCM(o,0,0)*RCM(n,0,1)+RCM(o,0,1)*RCM(n,1,1)+
RC(o,0,2)*RCM(n,2,1)+RCM(o,0,3)*RCM(n,3,1);
RCM(0,2)=RCM(o,0,0)*RCM(n,0,2)+RCM(o,0,1)*RCM(n,1,2)+
RC(o,0,2)*RCM(n,2,2)+RCM(o,0,3)*RCM(n,3,2);
RCM(0,3)=RCM(o,0,0)*RCM(n,0,3)+RCM(o,0,1)*RCM(n,1,3)+
RC(o,0,2)*RCM(n,2,3)+RCM(o,0,3)*RCM(n,3,3);
RCM(1,0)=RCM(o,1,0)*RCM(n,0,0)+RCM(o,1,1)*RCM(n,1,0)+
RC(o,1,2)*RCM(n,2,0)+RCM(o,1,3)*RCM(n,3,0);
RCM(1,1)=RCM(o,1,0)*RCM(n,0,1)+RCM(o,1,1)*RCM(n,1,1)+
RC(o,1,2)*RCM(n,2,1)+RCM(o,1,3)*RCM(n,3,1);
RCM(1,2)=RCM(o,1,0)*RCM(n,0,2)+RCM(o,1,1)*RCM(n,1,2)+
RC(o,1,2)*RCM(n,2,2)+RCM(o,1,3)*RCM(n,3,2);
RCM(1,3)=RCM(o,1,0)*RCM(n,0,3)+RCM(o,1,1)*RCM(n,1,3)+
RC(o,1,2)*RCM(n,2,3)+RCM(o,1,3)*RCM(n,3,3);
RCM(2,0)=RCM(o,2,0)*RCM(n,0,0)+RCM(o,2,1)*RCM(n,1,0)+
RC(o,2,2)*RCM(n,2,0)+RCM(o,2,3)*RCM(n,3,0);
RCM(2,1)=RCM(o,2,0)*RCM(n,0,1)+RCM(o,2,1)*RCM(n,1,1)+
RC(o,2,2)*RCM(n,2,1)+RCM(o,2,3)*RCM(n,3,1);
RCM(2,2)=RCM(o,2,0)*RCM(n,0,2)+RCM(o,2,1)*RCM(n,1,2)+
RC(o,2,2)*RCM(n,2,2)+RCM(o,2,3)*RCM(n,3,2);
RCM(2,3)=RCM(o,2,0)*RCM(n,0,3)+RCM(o,2,1)*RCM(n,1,3)+
RC(o,2,2)*RCM(n,2,3)+RCM(o,2,3)*RCM(n,3,3);
RCM(3,0)=RCM(o,3,0)*RCM(n,0,0)+RCM(o,3,1)*RCM(n,1,0)+
RC(o,3,2)*RCM(n,2,0)+RCM(o,3,3)*RCM(n,3,0);
RCM(3,1)=RCM(o,3,0)*RCM(n,0,1)+RCM(o,3,1)*RCM(n,1,1)+
RC(o,3,2)*RCM(n,2,1)+RCM(o,3,3)*RCM(n,3,1);
RCM(3,2)=RCM(o,3,0)*RCM(n,0,2)+RCM(o,3,1)*RCM(n,1,2)+
RC(o,3,2)*RCM(n,2,2)+RCM(o,3,3)*RCM(n,3,2);
RCM(3,3)=RCM(o,3,0)*RCM(n,0,3)+RCM(o,3,1)*RCM(n,1,3)+
RC(o,3,2)*RCM(n,2,3)+RCM(o,3,3)*RCM(n,3,3);
RCM}
void vector_multiply (float * v, const float * m)
{
float o[4];
int i;
for (i = 0; i < 4; i++) o[i] = v[i];
[0]=RC(0,0)*o[0]+RC(0,1)*o[1]+RC(0,2)*o[2]+RC(0,3)*o[3];
v[1]=RC(1,0)*o[0]+RC(1,1)*o[1]+RC(1,2)*o[2]+RC(1,3)*o[3];
v[2]=RC(2,0)*o[0]+RC(2,1)*o[1]+RC(2,2)*o[2]+RC(2,3)*o[3];
v[3]=RC(3,0)*o[0]+RC(3,1)*o[1]+RC(3,2)*o[2]+RC(3,3)*o[3];
v}
void gl_check_error()
{
switch (glGetError()) {
case GL_NO_ERROR: return;
case GL_INVALID_ENUM: fprintf (stderr, "OpenGL: invalid enum\n"); break;
case GL_INVALID_VALUE: fprintf (stderr, "OpenGL: invalid value\n"); break;
case GL_INVALID_OPERATION: fprintf (stderr, "OpenGL: invalid operation\n");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
fprintf (stderr, "OpenGL: invalid framebuffer operation\n"); break;
case GL_OUT_OF_MEMORY:
fprintf (stderr, "OpenGL: out of memory\n"); break;
case GL_STACK_UNDERFLOW:
fprintf (stderr, "OpenGL: stack underflow\n"); break;
case GL_STACK_OVERFLOW:
fprintf (stderr, "OpenGL: stack overflow\n"); break;
}
();
abort}
void gl_get_frustum (Frustum * f)
{
[4];
GLint v(GL_VIEWPORT, v);
glGetIntegerv gl_check_error();
->width = v[2];
f(GL_MODELVIEW_MATRIX, f->m);
glGetFloatv gl_check_error();
(GL_PROJECTION_MATRIX, f->p);
glGetFloatv gl_check_error();
float p[16];
int i;
for (i = 0; i < 16; i++) p[i] = f->p[i];
matrix_multiply (p, f->m);
/* right */
->n[0][0] = p[3] - p[0];
f->n[0][1] = p[7] - p[4];
f->n[0][2] = p[11] - p[8];
f->d[0] = p[15] - p[12];
f
/* left */
->n[1][0] = p[3] + p[0];
f->n[1][1] = p[7] + p[4];
f->n[1][2] = p[11] + p[8];
f->d[1] = p[15] + p[12];
f
/* top */
->n[2][0] = p[3] - p[1];
f->n[2][1] = p[7] - p[5];
f->n[2][2] = p[11] - p[9];
f->d[2] = p[15] - p[13];
f
/* bottom */
->n[3][0] = p[3] + p[1];
f->n[3][1] = p[7] + p[5];
f->n[3][2] = p[11] + p[9];
f->d[3] = p[15] + p[13];
f
/* front */
->n[4][0] = p[3] + p[2];
f->n[4][1] = p[7] + p[6];
f->n[4][2] = p[11] + p[10];
f->d[4] = p[15] + p[14];
f
/* back */
->n[5][0] = p[3] - p[2];
f->n[5][1] = p[7] - p[6];
f->n[5][2] = p[11] - p[10];
f->d[5] = p[15] - p[14];
f
for (i = 0; i < 6; i++) {
float n = sqrt(f->n[i][0]*f->n[i][0] +
->n[i][1]*f->n[i][1] +
f->n[i][2]*f->n[i][2]);
fif (n > 0.) {
->n[i][0] /= n; f->n[i][1] /= n; f->n[i][2] /= n;
f->d[i] /= n;
f}
}
}
/*
Returns 0 if the sphere is outside the view frustum, 1, if it is
inside and -1 if it is partly inside.
*/
int sphere_in_frustum (double x, double y, double z, double r, Frustum * f)
{
int I1 = 0, i, I = 1;
for (i = 0; i < 6; i++) {
double d = f->n[i][0]*x + f->n[i][1]*y + f->n[i][2]*z + f->d[i];
if (d < -r) {
= 0;
I break;
}
if (d < r)
= -1;
I }
if (I == 1)
return 1;
if (I == -1)
= -1;
I1 return I1;
}
/*
Returns the diameter (in pixels) of a sphere projected on the
screen.
*/
float sphere_diameter (double x, double y, double z, double r, Frustum * f)
{
float v[4];
[0] = x; v[1] = y; v[2] = z; v[3] = 1.;
vvector_multiply (v, f->m);
[0] = r;
vvector_multiply (v, f->p);
float rp = v[3] == 0. ? 0 : v[0]*f->width/v[3];
return rp;
}
/*
Replacement for gluPerspective in GLU from:
https://www.khronos.org/opengl/wiki/GluPerspective_code
*/
staticvoid glhFrustumf2(double *matrix, double left, double right,
double bottom, double top,
double znear, double zfar)
{
double temp, temp2, temp3, temp4;
= 2.0 * znear;
temp = right - left;
temp2 = top - bottom;
temp3 = zfar - znear;
temp4 [0] = temp / temp2;
matrix[1] = 0.0;
matrix[2] = 0.0;
matrix[3] = 0.0;
matrix[4] = 0.0;
matrix[5] = temp / temp3;
matrix[6] = 0.0;
matrix[7] = 0.0;
matrix[8] = (right + left) / temp2;
matrix[9] = (top + bottom) / temp3;
matrix[10] = (-zfar - znear) / temp4;
matrix[11] = -1.0;
matrix[12] = 0.0;
matrix[13] = 0.0;
matrix[14] = (-temp * zfar) / temp4;
matrix[15] = 0.0;
matrix}
void gl_perspective (double fovy, double aspect, double znear, double zfar)
{
double matrix[16];
double ymax, xmax;
= znear * tanf(fovy*M_PI/360.0);
ymax // ymin = -ymax;
// xmin = -ymax * aspectRatio;
= ymax * aspect;
xmax glhFrustumf2 (matrix, -xmax, xmax, -ymax, ymax, znear, zfar);
(GL_PROJECTION);
glMatrixMode (matrix);
glLoadMatrixd }
// derived from: Mesa-7.8.2/src/glu/sgi/libutil/project.c:234
int gl_project (float objx, float objy, float objz,
const float modelMatrix[16],
const float projMatrix[16],
const int viewport[4],
float *winx, float *winy, float *winz)
{
float in[4] = { objx, objy, objz, 1. };
vector_multiply (in, modelMatrix);
vector_multiply (in, projMatrix);
if (in[3] == 0.0) return 0;
*winx = viewport[0] + viewport[2]*(in[0]/in[3] + 1.)/2.;
*winy = viewport[1] + viewport[3]*(in[1]/in[3] + 1.)/2.;
*winz = (in[2]/in[3] + 1.)/2.;
return 1;
}