├── .gitignore ├── LICENSE.md ├── README.md ├── cube.c ├── cube.h ├── cube.md ├── cube.pdf ├── cubenx.png ├── cubeny.png ├── cubenz.png ├── cubepx.png ├── cubepy.png ├── cubepz.png ├── demo.c ├── demo.h ├── demo.md ├── glsl.c ├── glsl.h ├── glsl.md ├── image.c ├── image.h ├── image.md ├── img ├── atlas.png ├── cuben.png ├── cubep.png ├── lorem.png ├── lunar.png ├── noise1.jpg ├── noise1.png ├── noise2.jpg ├── noise2.png ├── plane.png ├── quick.png ├── view1.png ├── view2.png ├── view3a.png ├── view3b.png └── view3c.png ├── math3d.c ├── math3d.h ├── math3d.md ├── noise.c ├── noise.h ├── noise.md ├── plane.c ├── plane.h ├── plane.md ├── type.c ├── type.h └── type.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | *.DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2013 Robert Kooima 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # util3d 2 | 3 | `util3d` is a collection of C modules and headers implementing useful functions for programmers working with 3D graphics in OpenGL. This code is made available under the terms of the [MIT/X11 License](http://www.opensource.org/licenses/mit-license.php) and is free for any and all uses. 4 | 5 | - [`math3d`](math3d.md) — Implements a right-handed 3D mathematics library supporting three-component vectors, four-by-four matrices, quaternions, and Euler angles. 6 | 7 | - [`demo`](demo.md) — Implements a very simple framework for OpenGL demonstration applications. 8 | 9 | - [`image`](image.md) — Provides a basic image I/O library supporting the reading and writing of PNG, TIFF, JPEG, and OpenEXR images. 10 | 11 | - [`cube`](cube.md) — Renders the [3D reference cube](cube.pdf) using OpenGL. Useful for testing 3D transformations and debugging OpenGL applictions. 12 | 13 | - [`obj`](obj.md) — Loads, manipulates, optimizes, renders, and stores 3D geometry using the Wavefront OBJ file format. 14 | 15 | - [`glsl`](glsl.md) — Manages GLSL shader source and program objects. 16 | 17 | - [`type`](type.md) — Renders text using OpenGL. Useful for adding GUI elements and labels to 3D graphics applications. 18 | 19 | - [`noise`](noise.md) — Implements a 3D coherent noise generator using the Simplex method of Ken Perlin. 20 | 21 | - [`plane`](plane.md) — Renders a simple 3D plane using OpenGL. Useful as a basic scene backdrop. 22 | -------------------------------------------------------------------------------- /cube.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2009 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "image.h" 27 | #include "cube.h" 28 | 29 | /*----------------------------------------------------------------------------*/ 30 | 31 | struct cube 32 | { 33 | GLuint tex[6]; 34 | GLuint vbo[1]; 35 | GLuint ebo[1]; 36 | }; 37 | 38 | /*----------------------------------------------------------------------------*/ 39 | 40 | /* The following data give the vertices of the standard unit cube. These */ 41 | /* may be stored in a vertex array or buffer object and used directly with */ 42 | /* a call to glDrawArrays(GL_QUADS, 0, 24). The vertex ordering and texture */ 43 | /* coordinates provided give face orientations in line with the definition */ 44 | /* of GL_TEXTURE_CUBE_MAP. */ 45 | 46 | struct vert 47 | { 48 | GLfloat v[3]; 49 | GLfloat n[3]; 50 | GLfloat t[2]; 51 | }; 52 | 53 | static const struct vert verts[24] = { 54 | 55 | /* +X */ 56 | {{ 1.f, 1.f, 1.f }, { 1.f, 0.f, 0.f }, { 0.f, 1.f }}, 57 | {{ 1.f, -1.f, 1.f }, { 1.f, 0.f, 0.f }, { 0.f, 0.f }}, 58 | {{ 1.f, -1.f, -1.f }, { 1.f, 0.f, 0.f }, { 1.f, 0.f }}, 59 | {{ 1.f, 1.f, -1.f }, { 1.f, 0.f, 0.f }, { 1.f, 1.f }}, 60 | 61 | /* -X */ 62 | {{ -1.f, 1.f, -1.f }, { -1.f, 0.f, 0.f }, { 0.f, 1.f }}, 63 | {{ -1.f, -1.f, -1.f }, { -1.f, 0.f, 0.f }, { 0.f, 0.f }}, 64 | {{ -1.f, -1.f, 1.f }, { -1.f, 0.f, 0.f }, { 1.f, 0.f }}, 65 | {{ -1.f, 1.f, 1.f }, { -1.f, 0.f, 0.f }, { 1.f, 1.f }}, 66 | 67 | /* +Y */ 68 | {{ -1.f, 1.f, -1.f }, { 0.f, 1.f, 0.f }, { 0.f, 0.f }}, 69 | {{ -1.f, 1.f, 1.f }, { 0.f, 1.f, 0.f }, { 0.f, 1.f }}, 70 | {{ 1.f, 1.f, 1.f }, { 0.f, 1.f, 0.f }, { 1.f, 1.f }}, 71 | {{ 1.f, 1.f, -1.f }, { 0.f, 1.f, 0.f }, { 1.f, 0.f }}, 72 | 73 | /* -Y */ 74 | {{ -1.f, -1.f, 1.f }, { 0.f, -1.f, 0.f }, { 0.f, 0.f }}, 75 | {{ -1.f, -1.f, -1.f }, { 0.f, -1.f, 0.f }, { 0.f, 1.f }}, 76 | {{ 1.f, -1.f, -1.f }, { 0.f, -1.f, 0.f }, { 1.f, 1.f }}, 77 | {{ 1.f, -1.f, 1.f }, { 0.f, -1.f, 0.f }, { 1.f, 0.f }}, 78 | 79 | /* +Z */ 80 | {{ -1.f, 1.f, 1.f }, { 0.f, 0.f, 1.f }, { 0.f, 0.f }}, 81 | {{ -1.f, -1.f, 1.f }, { 0.f, 0.f, 1.f }, { 0.f, 1.f }}, 82 | {{ 1.f, -1.f, 1.f }, { 0.f, 0.f, 1.f }, { 1.f, 1.f }}, 83 | {{ 1.f, 1.f, 1.f }, { 0.f, 0.f, 1.f }, { 1.f, 0.f }}, 84 | 85 | /* -Z */ 86 | {{ 1.f, 1.f, -1.f }, { 0.f, 0.f, -1.f }, { 0.f, 0.f }}, 87 | {{ 1.f, -1.f, -1.f }, { 0.f, 0.f, -1.f }, { 0.f, 1.f }}, 88 | {{ -1.f, -1.f, -1.f }, { 0.f, 0.f, -1.f }, { 1.f, 1.f }}, 89 | {{ -1.f, 1.f, -1.f }, { 0.f, 0.f, -1.f }, { 1.f, 0.f }}, 90 | }; 91 | 92 | static const GLushort elems[36] = { 93 | 0, 1, 2, 0, 2, 3, 94 | 4, 5, 6, 4, 6, 7, 95 | 8, 9, 10, 8, 10, 11, 96 | 12, 13, 14, 12, 14, 15, 97 | 16, 17, 18, 16, 18, 19, 98 | 20, 21, 22, 20, 22, 23 99 | }; 100 | 101 | static void init_vbo(struct cube *C) 102 | { 103 | glBindBuffer(GL_ARRAY_BUFFER, C->vbo[0]); 104 | glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); 105 | 106 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, C->ebo[0]); 107 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elems), elems, GL_STATIC_DRAW); 108 | } 109 | 110 | /*----------------------------------------------------------------------------*/ 111 | 112 | static const char *names[6] = { 113 | "cubepx.png", 114 | "cubenx.png", 115 | "cubepy.png", 116 | "cubeny.png", 117 | "cubepz.png", 118 | "cubenz.png", 119 | }; 120 | 121 | /* Load all texture images and initialize all OpenGL exture objects. */ 122 | 123 | static void init_tex(struct cube *C) 124 | { 125 | void *p; 126 | int w; 127 | int h; 128 | int c; 129 | int b; 130 | int i; 131 | 132 | for (i = 0; i < 6; ++i) 133 | if ((p = image_read(names[i], &w, &h, &c, &b))) 134 | { 135 | int f = image_internal_form(c, b); 136 | int e = image_external_form(c); 137 | int t = image_external_type(b); 138 | 139 | glBindTexture(GL_TEXTURE_2D, C->tex[i]); 140 | glTexImage2D (GL_TEXTURE_2D, 0, f, w, h, 0, e, t, p); 141 | 142 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 143 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 144 | } 145 | } 146 | 147 | /*----------------------------------------------------------------------------*/ 148 | 149 | /* Allocate and initialize a new cube object. There must be a current OpenGL */ 150 | /* context at the time. */ 151 | 152 | cube *cube_create(void) 153 | { 154 | cube *C; 155 | 156 | if ((C = (cube *) malloc(sizeof (cube)))) 157 | { 158 | glGenBuffers (1, C->vbo); 159 | glGenBuffers (1, C->ebo); 160 | glGenTextures(6, C->tex); 161 | 162 | init_vbo(C); 163 | init_tex(C); 164 | } 165 | return C; 166 | } 167 | 168 | /* Release the given cube structure and all resources held by it. */ 169 | 170 | void cube_delete(cube *C) 171 | { 172 | assert(C); 173 | 174 | glDeleteTextures(6, C->tex); 175 | glDeleteBuffers (1, C->ebo); 176 | glDeleteBuffers (1, C->vbo); 177 | 178 | free(C); 179 | } 180 | 181 | /*----------------------------------------------------------------------------*/ 182 | 183 | /* Render the given cube structure. */ 184 | 185 | void cube_render(cube *C) 186 | { 187 | const size_t sz = sizeof (GLfloat); 188 | 189 | assert(C); 190 | 191 | /* Enable the necessary array pointers. */ 192 | 193 | glEnable(GL_TEXTURE_2D); 194 | glEnableClientState(GL_VERTEX_ARRAY); 195 | glEnableClientState(GL_NORMAL_ARRAY); 196 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 197 | { 198 | /* Bind the array pointers to the array buffer object. */ 199 | 200 | glBindBuffer(GL_ARRAY_BUFFER, C->vbo[0]); 201 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, C->ebo[0]); 202 | { 203 | size_t i; 204 | 205 | glVertexPointer( 3, GL_FLOAT, sz * 8, (GLvoid *) ( 0)); 206 | glNormalPointer( GL_FLOAT, sz * 8, (GLvoid *) (sz * 3)); 207 | glTexCoordPointer(2, GL_FLOAT, sz * 8, (GLvoid *) (sz * 6)); 208 | 209 | /* Draw six quads. */ 210 | 211 | for (i = 0; i < 6; ++i) 212 | { 213 | glBindTexture(GL_TEXTURE_2D, C->tex[i]); 214 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 215 | (const GLvoid *) (i * 12)); 216 | } 217 | } 218 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 219 | glBindBuffer(GL_ARRAY_BUFFER, 0); 220 | } 221 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); 222 | glDisableClientState(GL_NORMAL_ARRAY); 223 | glDisableClientState(GL_VERTEX_ARRAY); 224 | glDisable(GL_TEXTURE_2D); 225 | } 226 | 227 | /*----------------------------------------------------------------------------*/ 228 | -------------------------------------------------------------------------------- /cube.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2009 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #ifndef UTIL3D_CUBE_H 22 | #define UTIL3D_CUBE_H 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | /*----------------------------------------------------------------------------*/ 29 | 30 | typedef struct cube cube; 31 | 32 | cube *cube_create(void); 33 | void cube_delete(cube *); 34 | void cube_render(cube *); 35 | 36 | /*----------------------------------------------------------------------------*/ 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | #endif 42 | -------------------------------------------------------------------------------- /cube.md: -------------------------------------------------------------------------------- 1 | # cube 2 | 3 | This module renders the 3D reference cube using OpenGL. It is useful for testing object transformations and debugging OpenGL applictions. The implementation gives a simple example of basic rendering with the [ARB_vertex_buffer_object](http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt) specification. 4 | 5 | To better enable a concrete understanding of 3D transforms, a real-world instance of the 3D reference cube may be constructed from paper or cardstock [using this template PDF](cube.pdf). 6 | 7 | - [cube.c](cube.c) 8 | - [cube.h](cube.h) 9 | - [cubepx.png](cubepx.png) 10 | - [cubenx.png](cubenx.png) 11 | - [cubepy.png](cubepy.png) 12 | - [cubeny.png](cubeny.png) 13 | - [cubepz.png](cubepz.png) 14 | - [cubenz.png](cubenz.png) 15 | 16 | ## Compilation 17 | 18 | To use this module, simply link it with your own code. It requires OpenGL, [GLEW](http://glew.sourceforge.net/), and the [`image`](image.html#config) utility. To load the cube texture images, the `image` utility requires the PNG libraries. 19 | 20 | cc -o program program.c cube.c image.c -lpng -lz -lGLEW -lGL -lm 21 | 22 | ## API 23 | 24 | - `cube *cube_create(void)` 25 | 26 | Initialize and return a pointer to a new `cube` object. This object contains OpenGL state, so it may be called only *after* the OpenGL context has been initialized. 27 | 28 | - `void cube_delete(cube *C)` 29 | 30 | Delete cube `C` and release all OpenGL resources held by it. 31 | 32 | - `void cube_render(cube *C)` 33 | 34 | Render cube `C` using the current OpenGL context and state. The necessary texture and vertex array states are pushed to the attribute stack before modification and popped after rendering. 35 | 36 | ## Textures 37 | 38 | The orientation of the texture image applied to each face is significant. It indicates the orientation expected by the OpenGL cube map texture mode. The default textures are each "upright" in their own 2D space. 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 | 51 | On the 3D cube, the four "sides" of the cube remain "upright" as viewed in the default eye space, where the +*y* axis points up. The "top" is oriented with the −*z* axis pointing locally up, and the "bottom" is oriented with the +*z* axis pointing locally up. These orientations must be considered when designing cube map textures. Here is the cube as seen from above and below. 52 | 53 | 54 | 55 | 56 | 57 | 58 |
59 | -------------------------------------------------------------------------------- /cube.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/cube.pdf -------------------------------------------------------------------------------- /cubenx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/cubenx.png -------------------------------------------------------------------------------- /cubeny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/cubeny.png -------------------------------------------------------------------------------- /cubenz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/cubenz.png -------------------------------------------------------------------------------- /cubepx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/cubepx.png -------------------------------------------------------------------------------- /cubepy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/cubepy.png -------------------------------------------------------------------------------- /cubepz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/cubepz.png -------------------------------------------------------------------------------- /demo.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "image.h" 29 | #include "demo.h" 30 | #include "glsl.h" 31 | 32 | /*----------------------------------------------------------------------------*/ 33 | 34 | static int demo_mode; 35 | static demo_init_f demo_init; 36 | static demo_tilt_f demo_tilt; 37 | static demo_quit_f demo_quit; 38 | static demo_step_f demo_step; 39 | static demo_draw_f demo_draw; 40 | 41 | static GLuint clear_vert; 42 | static GLuint clear_frag; 43 | static GLuint clear; 44 | 45 | static struct timeval t0; 46 | static struct timeval t1; 47 | 48 | static double dt; 49 | 50 | /*----------------------------------------------------------------------------*/ 51 | 52 | static int last_time; 53 | 54 | /* Camera state. */ 55 | 56 | static GLfloat position[4] = { 0.0, 0.0, 5.0, 1.0 }; 57 | static GLfloat rotation[2] = { 0.0, 0.0 }; 58 | static GLfloat velocity[3] = { 0.0, 0.0, 0.0 }; 59 | static GLfloat light[2] = { -60.0, 30.0 }; 60 | static GLfloat point[4] = { 0.0, 0.0, 0.0, 0.0 }; 61 | static GLfloat zoom = 0.5; 62 | 63 | /* Camera state at the beginning of a click. */ 64 | 65 | static int click_modifiers; 66 | static int click_button = -1; 67 | static int click_x; 68 | static int click_y; 69 | static GLfloat click_rotation[2]; 70 | static GLfloat click_light[2]; 71 | static GLfloat click_zoom; 72 | 73 | const GLfloat *demo_get(int token) 74 | { 75 | switch (token) 76 | { 77 | case DEMO_POSITION: return position; 78 | case DEMO_ROTATION: return rotation; 79 | case DEMO_LIGHT: return light; 80 | case DEMO_POINT: return point; 81 | case DEMO_ZOOM: return &zoom; 82 | } 83 | return 0; 84 | } 85 | 86 | /*----------------------------------------------------------------------------*/ 87 | 88 | /* Write the current view state to the file named by the DEMO_STATE env var. */ 89 | 90 | static void state_save() 91 | { 92 | const char *filename; 93 | FILE *file; 94 | 95 | if ((filename = getenv("DEMO_STATE"))) 96 | { 97 | if ((file = fopen(filename, "w"))) 98 | { 99 | fprintf(file, "%f %f %f %f %f %f %f %f\n", 100 | position[0], 101 | position[1], 102 | position[2], 103 | rotation[0], 104 | rotation[1], 105 | light[0], 106 | light[1], 107 | zoom); 108 | fclose(file); 109 | } 110 | } 111 | } 112 | 113 | /* Read the current view state from the file named by the DEMO_STATE env var. */ 114 | 115 | static void state_load() 116 | { 117 | const char *filename; 118 | FILE *file; 119 | 120 | if ((filename = getenv("DEMO_STATE"))) 121 | { 122 | if ((file = fopen(filename, "r"))) 123 | { 124 | fscanf(file, "%f %f %f %f %f %f %f %f", 125 | position + 0, 126 | position + 1, 127 | position + 2, 128 | rotation + 0, 129 | rotation + 1, 130 | light + 0, 131 | light + 1, 132 | &zoom); 133 | fclose(file); 134 | } 135 | } 136 | } 137 | 138 | /*----------------------------------------------------------------------------*/ 139 | 140 | static const char *clear_vert_txt = \ 141 | "void main() \n" \ 142 | "{ \n" \ 143 | " gl_TexCoord[0] = gl_Vertex * 0.5 + 0.5; \n" \ 144 | " gl_Position = gl_Vertex; \n" \ 145 | "} \n"; 146 | 147 | static const char *clear_frag_txt = \ 148 | "#version 120 \n" \ 149 | 150 | "uniform float A[64];\n" \ 151 | "uniform vec3 T; \n" \ 152 | "uniform vec3 B; \n" \ 153 | 154 | "void main() \n" \ 155 | "{ \n" \ 156 | " ivec2 p = ivec2(mod(gl_FragCoord.xy - vec2(0.5), 8.0)); \n" \ 157 | " vec3 c = mix(B, T, gl_TexCoord[0].y); \n" \ 158 | " vec3 d = vec3(A[p.x * 8 + p.y]); \n" \ 159 | 160 | " gl_FragColor = vec4(d + c, 1.0); \n" \ 161 | "} \n"; 162 | 163 | static int start(int argc, char **argv) 164 | { 165 | /* Initialize the view and light state. */ 166 | 167 | if (demo_mode == DEMO_FLY) 168 | { 169 | position[1] = 2.0; 170 | rotation[0] = 11.3; 171 | } 172 | 173 | state_load(); 174 | 175 | /* Initialize the screen clearing shader. */ 176 | 177 | clear_vert = glsl_init_shader(GL_VERTEX_SHADER, clear_vert_txt, -1); 178 | clear_frag = glsl_init_shader(GL_FRAGMENT_SHADER, clear_frag_txt, -1); 179 | 180 | if ((clear = glsl_init_program(clear_vert, clear_frag))) 181 | { 182 | glUseProgram(clear); 183 | { 184 | static const GLfloat A[64] = { 185 | 0.00006033f, 0.00295626f, 0.00078431f, 0.00368024f, 186 | 0.00024132f, 0.00313725f, 0.00096530f, 0.00386124f, 187 | 0.00199095f, 0.00102564f, 0.00271493f, 0.00174962f, 188 | 0.00217195f, 0.00120664f, 0.00289593f, 0.00193062f, 189 | 0.00054298f, 0.00343891f, 0.00030165f, 0.00319759f, 190 | 0.00072398f, 0.00361991f, 0.00048265f, 0.00337858f, 191 | 0.00247360f, 0.00150830f, 0.00223228f, 0.00126697f, 192 | 0.00265460f, 0.00168929f, 0.00241327f, 0.00144796f, 193 | 0.00018099f, 0.00307692f, 0.00090497f, 0.00380090f, 194 | 0.00012066f, 0.00301659f, 0.00084464f, 0.00374057f, 195 | 0.00211161f, 0.00114630f, 0.00283560f, 0.00187029f, 196 | 0.00205128f, 0.00108597f, 0.00277526f, 0.00180995f, 197 | 0.00066365f, 0.00355958f, 0.00042232f, 0.00331825f, 198 | 0.00060331f, 0.00349925f, 0.00036199f, 0.00325792f, 199 | 0.00259427f, 0.00162896f, 0.00235294f, 0.00138763f, 200 | 0.00253394f, 0.00156863f, 0.00229261f, 0.00132730f 201 | }; 202 | static const GLfloat T[3] = { 0.4f, 0.4f, 0.4f }; 203 | static const GLfloat B[3] = { 0.2f, 0.2f, 0.2f }; 204 | 205 | glUniform1fv(glGetUniformLocation(clear, "A"), 64, A); 206 | glUniform3fv(glGetUniformLocation(clear, "T"), 3, T); 207 | glUniform3fv(glGetUniformLocation(clear, "B"), 3, B); 208 | } 209 | glUseProgram(0); 210 | } 211 | 212 | /* Initialize the demo's internal state. */ 213 | 214 | gettimeofday(&t0, 0); 215 | 216 | if (demo_init) 217 | return demo_init(argc, argv); 218 | else 219 | return 1; 220 | } 221 | 222 | static int close() 223 | { 224 | if (demo_quit) 225 | demo_quit(); 226 | 227 | glDeleteProgram(clear); 228 | glDeleteShader(clear_frag); 229 | glDeleteShader(clear_vert); 230 | 231 | state_save(); 232 | 233 | exit(0); 234 | } 235 | 236 | /*----------------------------------------------------------------------------*/ 237 | 238 | void demo_clear(const float *T, const float *B) 239 | { 240 | if (clear) 241 | { 242 | glPushAttrib(GL_DEPTH_BUFFER_BIT); 243 | glEnableClientState(GL_VERTEX_ARRAY); 244 | { 245 | static const GLfloat p[4][4] = { 246 | { -1.0f, -1.0f, 1.0f, 1.0f }, 247 | { 1.0f, -1.0f, 1.0f, 1.0f }, 248 | { -1.0f, 1.0f, 1.0f, 1.0f }, 249 | { 1.0f, 1.0f, 1.0f, 1.0f }, 250 | }; 251 | 252 | glDisable(GL_DEPTH_TEST); 253 | 254 | glBindBuffer(GL_ARRAY_BUFFER, 0); 255 | glVertexPointer(4, GL_FLOAT, 0, p); 256 | 257 | glUseProgram(clear); 258 | { 259 | if (T) glUniform3fv(glGetUniformLocation(clear, "T"), 3, T); 260 | if (B) glUniform3fv(glGetUniformLocation(clear, "B"), 3, B); 261 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 262 | } 263 | glUseProgram(0); 264 | } 265 | glDisableClientState(GL_VERTEX_ARRAY); 266 | glPopAttrib(); 267 | } 268 | else glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 269 | } 270 | 271 | /*----------------------------------------------------------------------------*/ 272 | 273 | static void camera_fly(void) 274 | { 275 | glRotatef(rotation[0], 1.0f, 0.0f, 0.0f); 276 | glRotatef(rotation[1], 0.0f, 1.0f, 0.0f); 277 | glTranslatef(-position[0], -position[1], -position[2]); 278 | } 279 | 280 | static void camera_dolly(void) 281 | { 282 | glTranslatef(0.0f, 0.0f, -position[2]); 283 | } 284 | 285 | static void camera_tumble(void) 286 | { 287 | glTranslatef(-position[0], -position[1], -position[2]); 288 | glRotatef(rotation[0], 1.0f, 0.0f, 0.0f); 289 | glRotatef(rotation[1], 0.0f, 1.0f, 0.0f); 290 | } 291 | 292 | static void camera(void) 293 | { 294 | /* Apply the camera transformation. */ 295 | 296 | switch (demo_mode) 297 | { 298 | case DEMO_FLY: camera_fly(); break; 299 | case DEMO_DOLLY: camera_dolly(); break; 300 | case DEMO_TUMBLE: camera_tumble(); break; 301 | } 302 | } 303 | 304 | static void lights() 305 | { 306 | /* Position the global light. */ 307 | 308 | const GLfloat L[4] = { 0.0f, 0.0f, 1.0f, 0.0f }; 309 | 310 | glPushMatrix(); 311 | { 312 | glRotatef(light[1], 0.0f, 1.0f, 0.0f); 313 | glRotatef(light[0], 1.0f, 0.0f, 0.0f); 314 | glLightfv(GL_LIGHT0, GL_POSITION, L); 315 | } 316 | glPopMatrix(); 317 | 318 | /* Position the flashlight. */ 319 | 320 | glLightfv(GL_LIGHT1, GL_POSITION, point); 321 | } 322 | 323 | /*----------------------------------------------------------------------------*/ 324 | 325 | static void pan_camera(GLfloat dx, GLfloat dy) 326 | { 327 | rotation[0] = click_rotation[0] + 90.0 * dy * zoom; 328 | rotation[1] = click_rotation[1] + 180.0 * dx * zoom; 329 | 330 | if (rotation[0] > 90.0) rotation[0] = 90.0; 331 | if (rotation[0] < -90.0) rotation[0] = -90.0; 332 | if (rotation[1] > 180.0) rotation[1] -= 360.0; 333 | if (rotation[1] < -180.0) rotation[1] += 360.0; 334 | 335 | glutPostRedisplay(); 336 | } 337 | 338 | static void pan_light(GLfloat dx, GLfloat dy) 339 | { 340 | light[0] = click_light[0] + 90.0 * dy; 341 | light[1] = click_light[1] + 180.0 * dx; 342 | 343 | if (light[0] > 90.0) light[0] = 90.0; 344 | if (light[0] < -90.0) light[0] = -90.0; 345 | if (light[1] > 180.0) light[1] -= 360.0; 346 | if (light[1] < -180.0) light[1] += 360.0; 347 | 348 | glutPostRedisplay(); 349 | } 350 | 351 | static void zoom_camera(GLfloat dy) 352 | { 353 | zoom = click_zoom + dy; 354 | 355 | if (zoom < 0.01) zoom = 0.01; 356 | 357 | glutPostRedisplay(); 358 | } 359 | 360 | /*----------------------------------------------------------------------------*/ 361 | 362 | static void snap(void) 363 | { 364 | int w = glutGet(GLUT_WINDOW_WIDTH); 365 | int h = glutGet(GLUT_WINDOW_HEIGHT); 366 | void *p; 367 | 368 | if ((p = malloc(w * h * 4))) 369 | { 370 | glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p); 371 | image_flip ( w, h, 4, 1, p); 372 | image_write("out.png", w, h, 4, 1, p); 373 | free(p); 374 | } 375 | } 376 | 377 | static void tilt(void) 378 | { 379 | velocity[0] = 0.f; 380 | velocity[1] = 0.f; 381 | velocity[2] = 0.f; 382 | 383 | if (demo_tilt) 384 | demo_tilt(); 385 | } 386 | /*----------------------------------------------------------------------------*/ 387 | 388 | static void keyboardup(unsigned char key, int x, int y) 389 | { 390 | switch (key) 391 | { 392 | case 'a': velocity[0] += 1.0; break; 393 | case 'd': case 'e': velocity[0] -= 1.0; break; 394 | case 'c': case 'j': velocity[1] += 1.0; break; 395 | case ' ': velocity[1] -= 1.0; break; 396 | case 'w': case ',': velocity[2] += 1.0; break; 397 | case 's': case 'o': velocity[2] -= 1.0; break; 398 | 399 | case 9: tilt(); break; 400 | case 13: snap(); break; 401 | case 27: close(); break; 402 | } 403 | } 404 | 405 | static void keyboard(unsigned char key, int x, int y) 406 | { 407 | switch (key) 408 | { 409 | case 'a': velocity[0] -= 1.0; break; 410 | case 'd': case 'e': velocity[0] += 1.0; break; 411 | case 'c': case 'j': velocity[1] -= 1.0; break; 412 | case ' ': velocity[1] += 1.0; break; 413 | case 'w': case ',': velocity[2] -= 1.0; break; 414 | case 's': case 'o': velocity[2] += 1.0; break; 415 | } 416 | } 417 | 418 | /*----------------------------------------------------------------------------*/ 419 | 420 | static void motion(int x, int y) 421 | { 422 | const int w = glutGet(GLUT_WINDOW_WIDTH); 423 | const int h = glutGet(GLUT_WINDOW_HEIGHT); 424 | 425 | GLfloat H = 0.1f * zoom * w / h; 426 | GLfloat V = 0.1f * zoom; 427 | GLfloat r; 428 | 429 | /* Compute the pointer motion as a fraction of window size. */ 430 | 431 | GLfloat dx = (GLfloat) (x - click_x) / w; 432 | GLfloat dy = (GLfloat) (y - click_y) / h; 433 | 434 | /* Apply the pointer motion to the camera or light. */ 435 | 436 | if (click_button == GLUT_LEFT_BUTTON) 437 | { 438 | if (click_modifiers == 0) pan_camera(dx, dy); 439 | else if (click_modifiers == GLUT_ACTIVE_CTRL) pan_light (dx, dy); 440 | else if (click_modifiers == GLUT_ACTIVE_SHIFT) zoom_camera( dy); 441 | } 442 | 443 | /* Compute the eye-space pointer vector. */ 444 | 445 | point[0] = (2.0f * x / w - 1.0f) * H; 446 | point[1] = -(2.0f * y / h - 1.0f) * V; 447 | point[2] = -(0.1f); 448 | 449 | r = 1.0f / (GLfloat) sqrt(point[0] * point[0] + 450 | point[1] * point[1] + 451 | point[2] * point[2]); 452 | point[0] *= r; 453 | point[1] *= r; 454 | point[2] *= r; 455 | } 456 | 457 | static void mouse(int button, int state, int x, int y) 458 | { 459 | /* Note all camera state at the beginning of a click. */ 460 | 461 | if (state == GLUT_DOWN) 462 | { 463 | click_modifiers = glutGetModifiers(); 464 | click_button = button; 465 | click_x = x; 466 | click_y = y; 467 | click_zoom = zoom; 468 | click_rotation[0] = rotation[0]; 469 | click_rotation[1] = rotation[1]; 470 | click_light[0] = light[0]; 471 | click_light[1] = light[1]; 472 | } 473 | else click_button = -1; 474 | } 475 | 476 | /*----------------------------------------------------------------------------*/ 477 | 478 | static void perf() 479 | { 480 | char str[256]; 481 | double ft; 482 | 483 | /* Compute the frame time. */ 484 | 485 | gettimeofday(&t1, 0); 486 | 487 | ft = (t1.tv_sec - t0.tv_sec) + 488 | (t1.tv_usec - t0.tv_usec) / 1000000.0; 489 | 490 | dt = (dt * 15.0 + ft) / 16.0; 491 | t0 = t1; 492 | 493 | /* Display it in the window title. */ 494 | 495 | sprintf(str, "%5.2f ms %4d fps\n", (1000.0 * dt), (int) (1.0 / dt)); 496 | glutSetWindowTitle(str); 497 | } 498 | 499 | static void reshape(int w, int h) 500 | { 501 | glViewport(0, 0, w, h); 502 | } 503 | 504 | static void display(void) 505 | { 506 | /* Initialize the projection and model-view matrices. */ 507 | 508 | GLfloat V = 0.1f * zoom; 509 | GLfloat H = 0.1f * zoom * glutGet(GLUT_WINDOW_WIDTH) 510 | / glutGet(GLUT_WINDOW_HEIGHT); 511 | 512 | glMatrixMode(GL_PROJECTION); 513 | glLoadIdentity(); 514 | glFrustum(-H, H, -V, V, 0.1, 100.0); 515 | 516 | glMatrixMode(GL_MODELVIEW); 517 | glLoadIdentity(); 518 | 519 | /* Draw the scene. */ 520 | 521 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 522 | 523 | camera(); 524 | lights(); 525 | 526 | if (demo_draw) 527 | demo_draw(); 528 | 529 | glutSwapBuffers(); 530 | perf(); 531 | } 532 | 533 | /*----------------------------------------------------------------------------*/ 534 | 535 | static void idle(void) 536 | { 537 | int curr_time = glutGet(GLUT_ELAPSED_TIME); 538 | 539 | GLfloat speed = 3.0f, dt = (curr_time - last_time) / 1000.0f; 540 | 541 | /* Compute the position change from the speed, time, and velocity. */ 542 | 543 | GLfloat v[3]; 544 | 545 | v[0] = speed * dt * velocity[0]; 546 | v[1] = speed * dt * velocity[1]; 547 | v[2] = speed * dt * velocity[2]; 548 | 549 | /* Apply the position change to the position, possibly in world space. */ 550 | 551 | if (demo_mode == DEMO_FLY) 552 | { 553 | GLfloat M[16]; 554 | 555 | glGetFloatv(GL_MODELVIEW_MATRIX, M); 556 | 557 | position[0] += M[ 0] * v[0] + M[ 1] * v[1] + M[ 2] * v[2]; 558 | position[1] += M[ 4] * v[0] + M[ 5] * v[1] + M[ 6] * v[2]; 559 | position[2] += M[ 8] * v[0] + M[ 9] * v[1] + M[10] * v[2]; 560 | } 561 | else 562 | { 563 | position[0] += v[0]; 564 | position[1] += v[1]; 565 | position[2] += v[2]; 566 | } 567 | 568 | /* Step the demo as needed. */ 569 | 570 | if (demo_step) 571 | demo_step(dt); 572 | 573 | glutPostRedisplay(); 574 | 575 | last_time = curr_time; 576 | } 577 | 578 | /*----------------------------------------------------------------------------*/ 579 | 580 | int demo(int mode, int argc, char *argv[], demo_init_f init, demo_tilt_f tilt, 581 | demo_quit_f quit, demo_draw_f draw, demo_step_f step) 582 | { 583 | demo_mode = mode; 584 | demo_init = init; 585 | demo_tilt = tilt; 586 | demo_quit = quit; 587 | demo_draw = draw; 588 | demo_step = step; 589 | 590 | glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE); 591 | glutInitWindowSize(1024, 768); 592 | glutInit(&argc, argv); 593 | 594 | glutCreateWindow(argv[0]); 595 | 596 | glutKeyboardUpFunc(keyboardup); 597 | glutKeyboardFunc(keyboard); 598 | glutReshapeFunc(reshape); 599 | glutDisplayFunc(display); 600 | glutPassiveMotionFunc(motion); 601 | glutMotionFunc(motion); 602 | glutMouseFunc(mouse); 603 | glutIdleFunc(idle); 604 | 605 | glutIgnoreKeyRepeat(1); 606 | 607 | if (glewInit() == GLEW_OK) 608 | { 609 | if (start(argc, argv)) 610 | glutMainLoop(); 611 | 612 | } 613 | return 0; 614 | } 615 | -------------------------------------------------------------------------------- /demo.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #ifndef UTIL3D_DEMO_H 22 | #define UTIL3D_DEMO_H 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | /*----------------------------------------------------------------------------*/ 29 | 30 | typedef int (*demo_init_f)(int, char **); 31 | typedef void (*demo_tilt_f)(void); 32 | typedef void (*demo_quit_f)(void); 33 | typedef void (*demo_draw_f)(void); 34 | typedef void (*demo_step_f)(float); 35 | 36 | /*----------------------------------------------------------------------------*/ 37 | 38 | enum { 39 | DEMO_STATIC, 40 | DEMO_TUMBLE, 41 | DEMO_DOLLY, 42 | DEMO_FLY 43 | }; 44 | 45 | enum { 46 | DEMO_POSITION, 47 | DEMO_ROTATION, 48 | DEMO_LIGHT, 49 | DEMO_POINT, 50 | DEMO_ZOOM 51 | }; 52 | 53 | int demo(int, int, char **, demo_init_f, demo_tilt_f, demo_quit_f, 54 | demo_draw_f, demo_step_f); 55 | const float *demo_get(int); 56 | 57 | void demo_clear(const float *, const float *); 58 | 59 | /*----------------------------------------------------------------------------*/ 60 | 61 | #ifdef __cplusplus 62 | } 63 | #endif 64 | #endif 65 | -------------------------------------------------------------------------------- /demo.md: -------------------------------------------------------------------------------- 1 | # demo 2 | 3 | This module implements a trivial framework for OpenGL demonstration. It is appropriate for the interactive presentation of rendering techniques and animations, but provides few facilities for mapping user input onto application control. A demo implementation using this module need only provide a function to perform rendering and, optionally, functions for setup, shutdown, and animation. 4 | 5 | - [demo.c](demo.c) 6 | - [demo.h](demo.h) 7 | 8 | The module maintains several pieces of internal state including the position, orientation, and zoom value of the camera, and the direction toward the primary light source. Upon rendering, the camera zoom is loaded into the OpenGL projection matrix, the position and orientation are loaded into the OpenGL model-view matrix, the primary light source direction is loaded into `GL_LIGHT0`, and the mouse pointer vector is loaded into `GL_LIGHT1`. 9 | 10 | The demo module is implemented using [GLUT](http://www.opengl.org/resources/libraries/glut/), and applications are free to co-opt any GLUT features not used internally. Notably, this includes [GLUT's menu functions](http://www.opengl.org/resources/libraries/glut/spec3/node35.html#SECTION00070000000000000000), which applications may use for interactive feature selection and configuration. 11 | 12 | ## Compilation 13 | 14 | To use this module, simply link it with your own code. It requires OpenGL, [GLEW](http://glew.sourceforge.net/), and the [image](image.html) utility (to support the screenshot feature), which also pulls in the PNG and zlib libraries. 15 | 16 | cc -o program program.c demo.c image.c -lpng -lz -lm 17 | 18 | ## API 19 | 20 | - `int demo(int mode, int argc, char **argv, demo_init_f init, demo_tilt_f tilt, demo_quit_f quit, demo_draw_f draw, demo_step_f step)` 21 | 22 | The demo API consists of one primary entry point that receives pointers to all pertainant information. This function initializes the OpenGL context and executes the main application loop, calling the provided functions as needed. It does not return until the application is closed. Any of the function arguments may be `NULL` to disable the related callback. The arguments are as follows: 23 | 24 | - `int mode` 25 | 26 | This argument selects the application's control style, determining how keyboard and mouse events map onto view state. In general: 27 | 28 | - Mouse drag maps onto camera orientation. 29 | - Shift-drag maps onto camera zoom. 30 | - Alt-drag maps onto light source direction. 31 | - The keyboard maps onto camera position in the traditional first-person style. 32 | 33 | However, the meaning of the camera position and orientation depends upon the selected mode. The following modes are defined: 34 | 35 | - `DEMO_STATIC` 36 | 37 | The camera is locked in place. 38 | 39 | - `DEMO_DOLLY` 40 | 41 | The camera zooms and moves forward or back, but does not rotate. 42 | 43 | - `DEMO_TUMBLE` 44 | 45 | The camera zooms and moves normally, and rotates with an inward-looking object tumble. 46 | 47 | - `DEMO_FLY` 48 | 49 | The camera zooms and moves normally, and rotates with an outward-looking first-person view. 50 | 51 | - `int argc, char **argv` 52 | 53 | These arguments should receive the command line argument count and array as passed to the C or C++ `main` function. This allows options to be passed to GLUT, most notably to control window position and size. 54 | 55 | - `int (*demo_init_f)(int argc, char **argv)` 56 | 57 | The `init` function is called after the OpenGL context is created, but before the application main loop begins. This provides an opportunity for the application to initialize any necessary OpenGL state. The `argc` and `argv` arguments give the command line arguments remaining *after* GLUT has finished processing the values passed to `demo`. All arguments left by GLUT remain for use by the application. 58 | 59 | - `void (*demo_tilt_f)(void)` 60 | 61 | The `tilt` function is called when the user presses the "tilt" key. If needed, applications should respond by flushing and reloading all OpenGL resources. This allows changes to be made to assets without requiring that the entire demo state be reset. For example, the user may modify a shader or texture, save it to disk, and reload it in the demo application without disturbing the current view state. 62 | 63 | - `void (*demo_quit_f)(void)` 64 | 65 | The `quit` function is called just prior to application exit. It provides an opportunity to release resources and store state as needed. 66 | 67 | - `void (*demo_draw_f)(void)` 68 | 69 | The `draw` function is called each time the display is repainted. Applications should perform rendering here, but should *not* swap buffers, as the `demo` module does this as part of the performance monitoring mechanism. 70 | 71 | - `void (*demo_step_f)(float dt)` 72 | 73 | The `step` function is called as often as possible, usually whenever there are no other events to be serviced. The `dt` argument gives the amount of time passed in seconds since the previous call. Applications may use this value to update time-varying values and animations. Applications need *not* request a repaint of the display, as this is automatic due to the free-running nature of all demos. 74 | 75 | - `const GLfloat *demo_get(int token)` 76 | 77 | The `demo_get` function allows the application to query the internal state of the demo. Values are returned as a pointer to an array of floats. Valid `token` values and their meanings are as follows: 78 | 79 | - `DEMO_POSITION` 80 | 81 | The current world-space position (x, y, z) of the camera. 82 | 83 | - `DEMO_ROTATION` 84 | 85 | The current orientation (φ, θ) of the camera. 86 | 87 | - `DEMO_LIGHT` 88 | 89 | The current orientation (φ, θ) of the light source. 90 | 91 | - `DEMO_POINT` 92 | 93 | The current eye-space vector (x, y, z) of the mouse pointer. 94 | 95 | - `DEMO_ZOOM` 96 | 97 | The current zoom value of the camera. 98 | 99 | - `void demo_clear(const GLfloat *top, const GLfloat *bottom)` 100 | 101 | The `demo_clear` function clears the screen using a linear gradient from `top` to `bottom`. If the underlying hardware supports programmable fragment shading then an ordered dither is applied in an effort to smooth the gradient across high-resolution or low bit depth displays. 102 | 103 | If the environment variable `DEMO_STATE` is defined at startup then the `demo` module will load camera and light source state from the file named there, if it exists. It will also store camera and light source state to that file upon normal exit. 104 | 105 | ## Keys 106 | 107 | The following keys are bound, though their effect is limited to specific modes: 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
W  Move forward.
S  Move backward.
A  Move left.
D  Move right.
C  Move down.
Space  Move up.
Tab  "Tilt" the application, triggering a reload of assets.
Return  Capture a screenshot and write it to the file `out.png`.
Escape  Exit the demo.
120 | 121 | Dvorak equivalents for all keys are simultaneously bound, without conflict. Screenshots are written in 8-bit RGBA format, with the alpha channel masking the color buffer as rendered. 122 | -------------------------------------------------------------------------------- /glsl.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | /*----------------------------------------------------------------------------*/ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "glsl.h" 29 | 30 | /*----------------------------------------------------------------------------*/ 31 | 32 | char *copy_str(const char *text) 33 | { 34 | /* Copy the given string into a newly-allocated buffer. */ 35 | 36 | size_t len = strlen(text); 37 | char *str = 0; 38 | 39 | if ((len > 0) && (str = (char *) malloc(len + 1))) 40 | memcpy(str, text, len + 1); 41 | 42 | return str; 43 | } 44 | 45 | char *load_str(const char *name) 46 | { 47 | /* Load the named file into a newly-allocated buffer. */ 48 | 49 | FILE *fp = 0; 50 | void *p = 0; 51 | size_t n = 0; 52 | 53 | if ((fp = fopen(name, "rb"))) 54 | { 55 | if (fseek(fp, 0, SEEK_END) == 0) 56 | { 57 | if ((n = (size_t) ftell(fp))) 58 | { 59 | if (fseek(fp, 0, SEEK_SET) == 0) 60 | { 61 | if ((p = calloc(n + 1, 1))) 62 | { 63 | fread(p, 1, n, fp); 64 | } 65 | } 66 | } 67 | } 68 | fclose(fp); 69 | } 70 | return (char *) p; 71 | } 72 | 73 | /*----------------------------------------------------------------------------*/ 74 | 75 | int check_shader_log(GLuint shader) 76 | { 77 | GLchar *p = 0; 78 | GLint s = 0; 79 | GLint n = 0; 80 | 81 | /* Check the shader compile status. If failed, print the log. */ 82 | 83 | glGetShaderiv(shader, GL_COMPILE_STATUS, &s); 84 | glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &n); 85 | 86 | if (s == 0) 87 | { 88 | if ((p = (GLchar *) calloc(n + 1, 1))) 89 | { 90 | glGetShaderInfoLog(shader, n, NULL, p); 91 | 92 | fprintf(stderr, "OpenGL Shader Error:\n%s", p); 93 | free(p); 94 | } 95 | return 0; 96 | } 97 | return 1; 98 | } 99 | 100 | int check_program_log(GLuint program) 101 | { 102 | GLchar *p = 0; 103 | GLint s = 0; 104 | GLint n = 0; 105 | 106 | /* Check the program link status. If failed, print the log. */ 107 | 108 | glGetProgramiv(program, GL_LINK_STATUS, &s); 109 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &n); 110 | 111 | if (s == 0) 112 | { 113 | if ((p = (GLchar *) calloc(n + 1, 1))) 114 | { 115 | glGetProgramInfoLog(program, n, NULL, p); 116 | 117 | fprintf(stderr, "OpenGL Program Error:\n%s", p); 118 | free(p); 119 | } 120 | return 0; 121 | } 122 | return 1; 123 | } 124 | 125 | /*----------------------------------------------------------------------------*/ 126 | 127 | GLuint glsl_init_shader(GLenum type, const char *str, int len) 128 | { 129 | if (str) 130 | { 131 | /* Compile a new shader with the given source. */ 132 | 133 | GLuint shader = glCreateShader(type); 134 | 135 | glShaderSource (shader, 1, (const GLchar **) &str, 136 | (const GLint *) &len); 137 | glCompileShader(shader); 138 | 139 | /* If the shader is valid, return it. Else, delete it. */ 140 | 141 | if (check_shader_log(shader)) 142 | return shader; 143 | else 144 | { 145 | fprintf(stderr, "%s", str); 146 | glDeleteShader(shader); 147 | } 148 | } 149 | return 0; 150 | } 151 | 152 | GLuint glsl_init_program(GLuint shader_vert, 153 | GLuint shader_frag) 154 | { 155 | /* Link a new program object. */ 156 | 157 | GLuint program = glCreateProgram(); 158 | 159 | glBindAttribLocation(program, 6, "my_Tangent"); 160 | 161 | glAttachShader(program, shader_vert); 162 | glAttachShader(program, shader_frag); 163 | 164 | glLinkProgram(program); 165 | 166 | /* If the program is valid, return it. Else, delete it. */ 167 | 168 | if (check_program_log(program)) 169 | return program; 170 | else 171 | glDeleteProgram(program); 172 | 173 | return 0; 174 | } 175 | 176 | /*----------------------------------------------------------------------------*/ 177 | 178 | GLboolean glsl_source(glsl *G, const char *vert_str, int vert_len, 179 | const char *frag_str, int frag_len) 180 | { 181 | if (vert_str && frag_str) 182 | { 183 | /* Compile the shaders. */ 184 | 185 | G->vert_filename = NULL; 186 | G->frag_filename = NULL; 187 | 188 | G->vert_shader = glsl_init_shader(GL_VERTEX_SHADER, vert_str, 189 | (int) vert_len); 190 | G->frag_shader = glsl_init_shader(GL_FRAGMENT_SHADER, frag_str, 191 | (int) frag_len); 192 | 193 | /* Link the program. */ 194 | 195 | if (G->vert_shader && G->frag_shader) 196 | { 197 | G->program = glsl_init_program(G->vert_shader, G->frag_shader); 198 | return GL_TRUE; 199 | } 200 | } 201 | return GL_FALSE; 202 | } 203 | 204 | GLboolean glsl_create(glsl *G, const char *vert_filename, 205 | const char *frag_filename) 206 | { 207 | GLboolean ret = GL_FALSE; 208 | 209 | if (vert_filename && frag_filename) 210 | { 211 | /* Load the shader source. */ 212 | 213 | char *vert_str = load_str(vert_filename); 214 | char *frag_str = load_str(frag_filename); 215 | 216 | /* Compile the shaders. */ 217 | 218 | ret = glsl_source(G, vert_str, -1, frag_str, -1); 219 | 220 | /* Cache the given file names to ease reloading. */ 221 | 222 | G->vert_filename = copy_str(vert_filename); 223 | G->frag_filename = copy_str(frag_filename); 224 | 225 | free(frag_str); 226 | free(vert_str); 227 | } 228 | return ret; 229 | } 230 | 231 | GLboolean glsl_reload(glsl *G) 232 | { 233 | if (G->vert_filename && G->frag_filename) 234 | { 235 | /* Reload the shader source. */ 236 | 237 | char *vert_str = load_str(G->vert_filename); 238 | char *frag_str = load_str(G->frag_filename); 239 | 240 | /* Delete the old program and shaders. */ 241 | 242 | glDeleteProgram(G->program); 243 | glDeleteShader(G->frag_shader); 244 | glDeleteShader(G->vert_shader); 245 | 246 | /* Compile the new shaders. */ 247 | 248 | G->vert_shader = glsl_init_shader(GL_VERTEX_SHADER, vert_str, -1); 249 | G->frag_shader = glsl_init_shader(GL_FRAGMENT_SHADER, frag_str, -1); 250 | 251 | free(frag_str); 252 | free(vert_str); 253 | 254 | /* Compile the new program. */ 255 | 256 | if (G->vert_shader && G->frag_shader) 257 | { 258 | G->program = glsl_init_program(G->vert_shader, G->frag_shader); 259 | return GL_TRUE; 260 | } 261 | } 262 | return GL_FALSE; 263 | } 264 | 265 | void glsl_delete(glsl *G) 266 | { 267 | /* Delete the program and shaders. */ 268 | 269 | glDeleteProgram(G->program); 270 | glDeleteShader(G->frag_shader); 271 | glDeleteShader(G->vert_shader); 272 | 273 | /* Release the filename cache. */ 274 | 275 | if (G->vert_filename) free(G->vert_filename); 276 | if (G->frag_filename) free(G->frag_filename); 277 | } 278 | 279 | /*----------------------------------------------------------------------------*/ 280 | 281 | #include 282 | 283 | GLint glsl_uniform(GLuint program, const char *fmt, ...) 284 | { 285 | GLint loc = 0; 286 | va_list ap; 287 | 288 | va_start(ap, fmt); 289 | { 290 | char str[256]; 291 | vsprintf(str, fmt, ap); 292 | loc = glGetUniformLocation(program, str); 293 | } 294 | va_end(ap); 295 | 296 | return loc; 297 | } 298 | 299 | /*----------------------------------------------------------------------------*/ 300 | -------------------------------------------------------------------------------- /glsl.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | /*----------------------------------------------------------------------------*/ 22 | 23 | #ifndef UTIL3D_GLSL_H 24 | #define UTIL3D_GLSL_H 25 | 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /*----------------------------------------------------------------------------*/ 33 | 34 | struct glsl 35 | { 36 | char *vert_filename; 37 | char *frag_filename; 38 | 39 | GLuint vert_shader; 40 | GLuint frag_shader; 41 | 42 | GLuint program; 43 | }; 44 | 45 | typedef struct glsl glsl; 46 | 47 | /*----------------------------------------------------------------------------*/ 48 | 49 | char *copy_str(const char *); 50 | char *load_str(const char *); 51 | 52 | GLuint glsl_init_shader (GLenum, const char *, int); 53 | GLuint glsl_init_program(GLuint, GLuint); 54 | 55 | int check_shader_log(GLuint); 56 | int check_program_log(GLuint); 57 | 58 | /*----------------------------------------------------------------------------*/ 59 | 60 | GLboolean glsl_source(glsl *, const char *, int, const char *, int); 61 | GLboolean glsl_create(glsl *, const char *, const char *); 62 | GLboolean glsl_reload(glsl *); 63 | void glsl_delete(glsl *); 64 | 65 | GLint glsl_uniform(GLuint, const char *, ...); 66 | 67 | /*----------------------------------------------------------------------------*/ 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | #endif 73 | -------------------------------------------------------------------------------- /glsl.md: -------------------------------------------------------------------------------- 1 | # glsl 2 | 3 | This API consists of an extremely simple structure of OpenGL objects and a small set of functions that operate upon it. 4 | 5 | - [glsl.c](glsl.c) 6 | - [glsl.h](glsl.h) 7 | 8 | The structure contains a program object, vertex and fragment shader objects, and character pointers used to cache shader file names, if provided. 9 | 10 | struct glsl 11 | { 12 | GLuint program; 13 | 14 | GLuint vert_shader; 15 | GLuint frag_shader; 16 | 17 | char *vert_filename; 18 | char *frag_filename; 19 | }; 20 | 21 | typedef struct glsl glsl; 22 | 23 | The functions that initialize and release these resources are as follows. 24 | 25 | - `GLboolean glsl_source(glsl *G, const char *vert_str, int vert_len, const char *frag_str, int frag_len);` 26 | 27 | Initialize a GLSL program object using the given vertex shader source string `vert_str` and fragment shader source string `frag_str`. If these strings are *not* null-terminated then `vert_len` and `frag_len` give their lengths. If they are null-terminated, these lengths may be given as -1. In the event of an error in shader compilation or program linking, the relevant log contents are printed to the standard error stream and `GL_FALSE` is returned. 28 | 29 | - `GLboolean glsl_create(glsl *G, const char *vert_filename, const char *frag_filename);` 30 | 31 | Initialize a GLSL program object using the named vertex shader source file `vert_filename` and fragment shader source file `frag_filename`. These file names are cached within the GLSL structure. On error, relevant log contents are printed to the standard error stream and `GL_FALSE` is returned. 32 | 33 | - `void glsl_delete(glsl *G);` 34 | 35 | Release the shader objects, program object, and cached file names held by the given GLSL structure. 36 | 37 | Once initialized, the `program` entry of the `glsl` structure may be used normally. 38 | 39 | glsl G; 40 | 41 | if (glsl_create(&G, "phong.vert", "phong.frag")) 42 | glUseProgram(G.program); 43 | 44 | A few convenience functions are also provided. 45 | 46 | - `GLint glsl_uniform(GLuint, const char *, ...);` 47 | 48 | Return the location of a GLSL uniform. This is a more powerful form of the `glGetUniformLocation` function taking a variable arguments list that enables the construction of array and structure uniform names in the style of `printf`. For example, to initialize an array of sampler uniforms in a loop: 49 | 50 | for (int i = 0; i < 16; i++) 51 | glUniform1i(glsl_uniform(G.program, "image[%d]", i), i); 52 | 53 | - `GLboolean glsl_reload(glsl *G);` 54 | 55 | If the given GLSL structure was initialized using `glsl_create` and the shader file names are cached then `glsl_reload` flushes and reloadss the current shader and program objects. This allows modified shader files to be activated without stopping and restarting the running application. 56 | 57 | -------------------------------------------------------------------------------- /image.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2009 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #ifndef UTIL3D_IMAGE_H 22 | #define UTIL3D_IMAGE_H 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | /*----------------------------------------------------------------------------*/ 29 | 30 | void image_flip(int, int, int, int, void *); 31 | 32 | /*----------------------------------------------------------------------------*/ 33 | 34 | void *image_read_png(const char *, int *, int *, int *, int *); 35 | void image_write_png(const char *, int, int, int, int, void *); 36 | 37 | void *image_read_jpg(const char *, int *, int *, int *, int *); 38 | void image_write_jpg(const char *, int, int, int, int, void *); 39 | 40 | void *image_read_exr(const char *, int *, int *, int *, int *); 41 | void image_write_exr(const char *, int, int, int, int, void *); 42 | 43 | void *image_read_tif(const char *, int *, int *, int *, int *, int); 44 | void image_write_tif(const char *, int, int, int, int, int, void **); 45 | 46 | /*----------------------------------------------------------------------------*/ 47 | 48 | void *image_read(const char *, int *, int *, int *, int *); 49 | void image_write(const char *, int, int, int, int, void *); 50 | 51 | float *image_read_float(const char *, int *, int *, int *, int *); 52 | void image_write_float(const char *, int, int, int, int, float *); 53 | float *image_scale_float(int, int, int, int, int, const float *); 54 | 55 | /*----------------------------------------------------------------------------*/ 56 | 57 | int image_internal_form(int, int); 58 | int image_external_form(int); 59 | int image_external_type(int); 60 | 61 | /*----------------------------------------------------------------------------*/ 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | #endif 67 | -------------------------------------------------------------------------------- /image.md: -------------------------------------------------------------------------------- 1 | # image 2 | 3 | This module implements a basic image I/O library supporting the reading and writing of PNG and JPEG images. It provides a uniform simplified front-end to [libpng](http://www.libpng.org/), [libtiff](http://www.remotesensing.org/libtiff/), [libjpeg](http://www.ijg.org/), and [OpenEXR](http://www.openexr.com/). Usable color depths and channel counts vary depending on format. 4 | 5 | - [image.c](image.c) 6 | - [image.h](image.h) 7 | 8 | ## Compilation 9 | 10 | To use this module, simply link it with your own code and the supporting libraries for all necessary image formats. 11 | 12 | cc -o program program.c image.c -lpng -ltiff -ljpeg -lIlmImf -lz -lm 13 | 14 | PNG, TIFF, JPEG, and EXR support may be omitted as desired with the definition of `CONFIG_NO_PNG`, `CONFIG_NO_TIF`, `CONFIG_NO_JPG`, or `CONFIG_NO_EXR`. For example, to build with PNG support only: 15 | 16 | cc -DCONFIG_NO_TIF -DCONFIG_NO_JPG -DCONFIG_NO_EXR -o program program.c image.c -lpng -lz -lm 17 | 18 | ## Image I/O 19 | 20 | - `void *image_read(const char *name, int *w, int *h, int *c, int *b)` 21 | 22 | Read the image file named `name`. The return value is a newly-allocated buffer containing the image data. Arguments `w`, `h`, `c`, and `b` point to integers that receive the width, height, channel count, and bytes-per-channel of the image. Return null upon failure. 23 | 24 | - `void image_write(const char *name, int w, int h, int c, int b, const void *p)` 25 | 26 | Write the image file named `name`. Argument `p` points to the buffer of image data. Arguments `w`, `h`, `c`, and `b` give the width, height, channel count, and bytes-per-channel of the image. 27 | 28 | Both the reader and writer functions examine the extension of the given name to determine the format of the file. 29 | 30 | ## Format-specific I/O 31 | 32 | These functions ignore the extension of the given name string. 33 | 34 | - `void *image_read_png(const char *name, int *w, int *h, int *c, int *b)` 35 | - `void image_write_png(const char *name, int w, int h, int c, int b, const void *p)` 36 | 37 | Read or write image file `name`, forcing the file type to PNG. 38 | 39 | - `void *image_read_jpg(const char *name, int *w, int *h, int *c, int *b)` 40 | - `void image_write_jpg(const char *name, int w, int h, int c, int b, const void *p)` 41 | 42 | Read or write image file `name`, forcing the file type to JPEG. 43 | 44 | - `void *image_read_exr(const char *name, int *w, int *h, int *c, int *b)` 45 | - `void image_write_exr(const char *name, int w, int h, int c, int b, const void *p)` 46 | 47 | Read or write image file `name`, forcing the file type to OpenEXR. 48 | 49 | - `void *image_read_tif(const char *name, int *w, int *h, int *c, int *b, int i)` 50 | - `void image_write_tif(const char *name, int w, int h, int c, int b, int n, void **p)` 51 | 52 | Read or write image file `name`, forcing the file type to TIFF. The `i` argument to `image_read_tif` selects the *i*th page of the TIFF for reading. The `n` argument to `image_write_tif` gives the number of image buffer pointers in array `p` for writing. These enable the reading and writing of multi-page TIFF files. 53 | 54 | ## Utilities 55 | 56 | - `void image_flip(int w, int h, int c, int b, void *p)` 57 | 58 | Flip the given image buffer vertically. Arguments `w`, `h`, `c`, and `b` give the width, height, channel count, and bytes-per-channel of the image, and `p` points to the pixel buffer. This function may be used to rectify disagreement over whether the image origin lies at the upper left or the lower left. 59 | 60 | - `GLenum image_internal_form(int c, int b)` 61 | 62 | Return an OpenGL internal texture format enumerator appropriate for an image with `c` channels and `b` bytes per channel: `GL_LUMINANCE`, `GL_LUMINANCE_ALPHA`, `GL_RGB`, `GL_RGBA`, `GL_LUMINANCE16`, `GL_LUMINANCE_ALPHA16`, `GL_RGB16`, or `GL_RGBA16`. 63 | 64 | - `GLenum image_external_form(int c)` 65 | 66 | Return an OpenGL external pixel format enumerator appropriate for an image with `c` channels: `GL_LUMINANCE`, `GL_LUMINANCE_ALPHA`, `GL_RGB`, or `GL_RGBA`. 67 | 68 | - `GLenum image_external_type(int b)` 69 | 70 | Return an OpenGL external pixel type enumerator appropriate for an image with `b` bytes per channel: `GL_UNSIGNED_BYTE` or `GL_UNSIGNED_SHORT`. 71 | 72 | ## Example 73 | 74 | The following code fragment demonstrates the common case of loading an image file to an OpenGL texture. Texture mappings often assume an origin at the lower left of the image, so the image is flipped after reading. The buffer is freed after uploading, as OpenGL has cached the contents internally. 75 | 76 | int w, h, c, b; 77 | 78 | void *p = image_read(name, &w, &h, &c, &b); 79 | 80 | int i = image_internal_form(c, b); 81 | int e = image_external_form(c); 82 | int t = image_external_type(b); 83 | 84 | image_flip(w, h, c, b, p); 85 | 86 | glTexImage2D(GL_TEXTURE_2D, 0, i, w, h, 0, e, t, p); 87 | 88 | free(p); 89 | -------------------------------------------------------------------------------- /img/atlas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/atlas.png -------------------------------------------------------------------------------- /img/cuben.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/cuben.png -------------------------------------------------------------------------------- /img/cubep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/cubep.png -------------------------------------------------------------------------------- /img/lorem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/lorem.png -------------------------------------------------------------------------------- /img/lunar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/lunar.png -------------------------------------------------------------------------------- /img/noise1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/noise1.jpg -------------------------------------------------------------------------------- /img/noise1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/noise1.png -------------------------------------------------------------------------------- /img/noise2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/noise2.jpg -------------------------------------------------------------------------------- /img/noise2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/noise2.png -------------------------------------------------------------------------------- /img/plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/plane.png -------------------------------------------------------------------------------- /img/quick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/quick.png -------------------------------------------------------------------------------- /img/view1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/view1.png -------------------------------------------------------------------------------- /img/view2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/view2.png -------------------------------------------------------------------------------- /img/view3a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/view3a.png -------------------------------------------------------------------------------- /img/view3b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/view3b.png -------------------------------------------------------------------------------- /img/view3c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlk/util3d/1ae9709881d71372cc38076f06fd1604873432a1/img/view3c.png -------------------------------------------------------------------------------- /math3d.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2009 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #include 22 | #include 23 | #include "math3d.h" 24 | 25 | /*----------------------------------------------------------------------------*/ 26 | /* Vector operations */ 27 | 28 | /* Transform homegeneous vector b by matrix M. */ 29 | 30 | void wtransform(real *restrict a, const real *restrict M, 31 | const real *restrict b) 32 | { 33 | assert(a != b); 34 | 35 | a[0] = M[ 0] * b[0] + M[ 4] * b[1] + M[ 8] * b[2] + M[12] * b[3]; 36 | a[1] = M[ 1] * b[0] + M[ 5] * b[1] + M[ 9] * b[2] + M[13] * b[3]; 37 | a[2] = M[ 2] * b[0] + M[ 6] * b[1] + M[10] * b[2] + M[14] * b[3]; 38 | a[3] = M[ 3] * b[0] + M[ 7] * b[1] + M[11] * b[2] + M[15] * b[3]; 39 | } 40 | 41 | /* Transform vector b by matrix M. */ 42 | 43 | void vtransform(real *restrict a, const real *restrict M, 44 | const real *restrict b) 45 | { 46 | assert(a != b); 47 | 48 | a[0] = M[ 0] * b[0] + M[ 4] * b[1] + M[ 8] * b[2]; 49 | a[1] = M[ 1] * b[0] + M[ 5] * b[1] + M[ 9] * b[2]; 50 | a[2] = M[ 2] * b[0] + M[ 6] * b[1] + M[10] * b[2]; 51 | } 52 | 53 | /* Transform position b by matrix M. */ 54 | 55 | void ptransform(real * restrict a, const real *restrict M, 56 | const real *restrict b) 57 | { 58 | assert(a != b); 59 | 60 | a[0] = M[ 0] * b[0] + M[ 4] * b[1] + M[ 8] * b[2] + M[12]; 61 | a[1] = M[ 1] * b[0] + M[ 5] * b[1] + M[ 9] * b[2] + M[13]; 62 | a[2] = M[ 2] * b[0] + M[ 6] * b[1] + M[10] * b[2] + M[14]; 63 | } 64 | 65 | /* Compute the vector spherical linear interpolation a of b and c at t. */ 66 | 67 | void vslerp(real *a, const real *b, const real *c, real t) 68 | { 69 | const real d = vdot(b, c); 70 | 71 | if (d < 1.0) 72 | { 73 | const real k = acos(d); 74 | const real u = sin(k - t * k) / sin(k); 75 | const real v = sin( t * k) / sin(k); 76 | 77 | a[0] = b[0] * u + c[0] * v; 78 | a[1] = b[1] * u + c[1] * v; 79 | a[2] = b[2] * u + c[2] * v; 80 | } 81 | else vcpy(a, b); 82 | } 83 | 84 | /*----------------------------------------------------------------------------*/ 85 | /* Quaternion operations */ 86 | 87 | /* Compute the unchecked spherical quaternion interpolation of b and c at t. */ 88 | 89 | static void slerp4(real *a, const real *b, const real *c, real t) 90 | { 91 | const real d = qdot(b, c); 92 | 93 | if (d < 1.0) 94 | { 95 | const real k = acos(d); 96 | const real u = sin(k - t * k) / sin(k); 97 | const real v = sin( t * k) / sin(k); 98 | 99 | a[0] = b[0] * u + c[0] * v; 100 | a[1] = b[1] * u + c[1] * v; 101 | a[2] = b[2] * u + c[2] * v; 102 | a[3] = b[3] * u + c[3] * v; 103 | } 104 | else qcpy(a, b); 105 | } 106 | 107 | /* Compute the auxiliary spline quaternion between c and d (for qsquad.) */ 108 | 109 | static void qaux(real *a, const real *b, const real *c, const real *d) 110 | { 111 | real s[4]; 112 | real t[4]; 113 | real u[4]; 114 | 115 | qinvert(s, c); 116 | qmultiply(t, s, b); 117 | qmultiply(u, s, d); 118 | 119 | qlog(t, t); 120 | qlog(u, u); 121 | qadd(s, t, u); 122 | qscale(s, s, -0.25); 123 | qexp(s, s); 124 | 125 | qmultiply(a, c, s); 126 | qnormalize(a, a); 127 | } 128 | 129 | /* Compute the quaternion b raised to the power h. */ 130 | 131 | void qpow(real *a, const real *b, real h) 132 | { 133 | const real k = acos(b[3]); 134 | const real s = sqrt(1.0 - b[3] * b[3]); 135 | 136 | a[0] = sin(k * h) * b[0] / s; 137 | a[1] = sin(k * h) * b[1] / s; 138 | a[2] = sin(k * h) * b[2] / s; 139 | a[3] = cos(k * h); 140 | } 141 | 142 | /* Compute the quaternion e raised to the power b. */ 143 | 144 | void qexp(real *a, const real *b) 145 | { 146 | const real k = vlen(b); 147 | 148 | if (k > 0.0) 149 | { 150 | const real s = sin(k); 151 | const real c = cos(k); 152 | 153 | a[0] = b[0] * s / k; 154 | a[1] = b[1] * s / k; 155 | a[2] = b[2] * s / k; 156 | a[3] = c; 157 | } 158 | else 159 | { 160 | a[0] = 0.0; 161 | a[1] = 0.0; 162 | a[2] = 0.0; 163 | a[3] = 0.0; 164 | } 165 | } 166 | 167 | /* Compute the natural logarithm of quaternion b. */ 168 | 169 | void qlog(real *a, const real *b) 170 | { 171 | const real s = sqrt(1.0 - b[3] * b[3]); 172 | 173 | if (s > 0.0) 174 | { 175 | const real k = acos(b[3]); 176 | 177 | a[0] = b[0] * k / s; 178 | a[1] = b[1] * k / s; 179 | a[2] = b[2] * k / s; 180 | a[3] = 0.0; 181 | } 182 | else 183 | { 184 | a[0] = 0.0; 185 | a[1] = 0.0; 186 | a[2] = 0.0; 187 | a[3] = 0.0; 188 | } 189 | } 190 | 191 | /* Compute the quaternion spherical linear interpolation a of b and c at t. */ 192 | 193 | void qslerp(real *a, const real *b, const real *c, real t) 194 | { 195 | real C[4]; 196 | 197 | /* Check the sign to ensure we interpolate the short way around. */ 198 | 199 | qsign(C, b, c); 200 | 201 | slerp4(a, b, C, t); 202 | } 203 | 204 | /* Compute the quaternion spherical quadratic interpolation of c and d at t. */ 205 | 206 | void qsquad(real *a, const real *b, const real *c, 207 | const real *d, const real *e, real t) 208 | { 209 | real A[4], C[4], D[4], E[4], u[4], v[4], w[4]; 210 | 211 | /* Check the signs to ensure we interpolate the short way around. */ 212 | 213 | qsign(C, b, c); 214 | qsign(D, C, d); 215 | qsign(E, D, e); 216 | 217 | /* Compute auxiliary quaternions giving the spline tangent. */ 218 | 219 | qaux(u, b, C, D); 220 | qaux(v, C, D, E); 221 | 222 | /* Interpolate along the spline. */ 223 | 224 | slerp4(A, C, D, t); 225 | slerp4(w, u, v, t); 226 | slerp4(a, A, w, 2.0 * t * (1.0 - t)); 227 | } 228 | 229 | /* Compute the quaternion q giving rotation about vector v through angle a. */ 230 | 231 | void qrotate(real *restrict q, const real *restrict v, real a) 232 | { 233 | const real c = cos(a * 0.5); 234 | const real s = sin(a * 0.5); 235 | 236 | real t[4]; 237 | 238 | t[0] = s * v[0]; 239 | t[1] = s * v[1]; 240 | t[2] = s * v[2]; 241 | t[3] = c; 242 | 243 | qnormalize(q, t); 244 | } 245 | 246 | /* Multiply quaternions b and c. */ 247 | 248 | void qmultiply(real *restrict a, const real *restrict b, 249 | const real *restrict c) 250 | { 251 | assert(a != b); 252 | assert(a != c); 253 | 254 | a[0] = b[0] * c[3] + b[3] * c[0] + b[1] * c[2] - b[2] * c[1]; 255 | a[1] = b[1] * c[3] + b[3] * c[1] + b[2] * c[0] - b[0] * c[2]; 256 | a[2] = b[2] * c[3] + b[3] * c[2] + b[0] * c[1] - b[1] * c[0]; 257 | a[3] = b[3] * c[3] - b[0] * c[0] - b[1] * c[1] - b[2] * c[2]; 258 | 259 | qnormalize(a, a); 260 | } 261 | 262 | /*----------------------------------------------------------------------------*/ 263 | /* Transformation matrices */ 264 | 265 | /* Give the identity matrix M. */ 266 | 267 | void midentity(real *restrict M) 268 | { 269 | M[ 0] = 1.0; M[ 4] = 0.0; M[ 8] = 0.0; M[12] = 0.0; 270 | M[ 1] = 0.0; M[ 5] = 1.0; M[ 9] = 0.0; M[13] = 0.0; 271 | M[ 2] = 0.0; M[ 6] = 0.0; M[10] = 1.0; M[14] = 0.0; 272 | M[ 3] = 0.0; M[ 7] = 0.0; M[11] = 0.0; M[15] = 1.0; 273 | } 274 | 275 | /* Give the matrix M rotating about the X axis through angle a. */ 276 | 277 | void mrotatex(real *restrict M, real a) 278 | { 279 | const real s = sin(a); 280 | const real c = cos(a); 281 | 282 | M[ 0] = 1.0; M[ 4] = 0.0; M[ 8] = 0.0; M[12] = 0.0; 283 | M[ 1] = 0.0; M[ 5] = c; M[ 9] = -s; M[13] = 0.0; 284 | M[ 2] = 0.0; M[ 6] = s; M[10] = c; M[14] = 0.0; 285 | M[ 3] = 0.0; M[ 7] = 0.0; M[11] = 0.0; M[15] = 1.0; 286 | } 287 | 288 | /* Give the matrix M rotating about the Y axis through angle a. */ 289 | 290 | void mrotatey(real *restrict M, real a) 291 | { 292 | const real s = sin(a); 293 | const real c = cos(a); 294 | 295 | M[ 0] = c; M[ 4] = 0.0; M[ 8] = s; M[12] = 0.0; 296 | M[ 1] = 0.0; M[ 5] = 1.0; M[ 9] = 0.0; M[13] = 0.0; 297 | M[ 2] = -s; M[ 6] = 0.0; M[10] = c; M[14] = 0.0; 298 | M[ 3] = 0.0; M[ 7] = 0.0; M[11] = 0.0; M[15] = 1.0; 299 | } 300 | 301 | /* Give the matrix M rotating about the Z axis through angle a. */ 302 | 303 | void mrotatez(real *restrict M, real a) 304 | { 305 | const real s = sin(a); 306 | const real c = cos(a); 307 | 308 | M[ 0] = c; M[ 4] = -s; M[ 8] = 0.0; M[12] = 0.0; 309 | M[ 1] = s; M[ 5] = c; M[ 9] = 0.0; M[13] = 0.0; 310 | M[ 2] = 0.0; M[ 6] = 0.0; M[10] = 1.0; M[14] = 0.0; 311 | M[ 3] = 0.0; M[ 7] = 0.0; M[11] = 0.0; M[15] = 1.0; 312 | } 313 | 314 | /* Give the matrix M giving rotation about vector v through angle a. */ 315 | 316 | void mrotate(real *restrict M, const real *restrict v, real a) 317 | { 318 | const real s = sin(a); 319 | const real c = cos(a); 320 | 321 | real u[3]; 322 | 323 | vnormalize(u, v); 324 | 325 | M[ 0] = u[0] * u[0]; 326 | M[ 1] = u[1] * u[0]; 327 | M[ 2] = u[2] * u[0]; 328 | M[ 4] = u[0] * u[1]; 329 | M[ 5] = u[1] * u[1]; 330 | M[ 6] = u[2] * u[1]; 331 | M[ 8] = u[0] * u[2]; 332 | M[ 9] = u[1] * u[2]; 333 | M[10] = u[2] * u[2]; 334 | 335 | M[ 0] += (1.0 - M[ 0]) * c; 336 | M[ 1] += (0.0 - M[ 1]) * c + u[2] * s; 337 | M[ 2] += (0.0 - M[ 2]) * c - u[1] * s; 338 | M[ 3] = 0.0; 339 | M[ 4] += (0.0 - M[ 4]) * c - u[2] * s; 340 | M[ 5] += (1.0 - M[ 5]) * c; 341 | M[ 6] += (0.0 - M[ 6]) * c + u[0] * s; 342 | M[ 7] = 0.0; 343 | M[ 8] += (0.0 - M[ 8]) * c + u[1] * s; 344 | M[ 9] += (0.0 - M[ 9]) * c - u[0] * s; 345 | M[10] += (1.0 - M[10]) * c; 346 | M[11] = 0.0; 347 | M[12] = 0.0; 348 | M[13] = 0.0; 349 | M[14] = 0.0; 350 | M[15] = 1.0; 351 | } 352 | 353 | /* Möller et al Eq 4.54 gives a very nice formulation for a rotation matrix */ 354 | /* taking one vector onto another. It's worth adding here. */ 355 | 356 | /* Give the matrix M translating along vector v. */ 357 | 358 | void mtranslate(real *restrict M, const real *restrict v) 359 | { 360 | M[ 0] = 1.0; M[ 4] = 0.0; M[ 8] = 0.0; M[12] = v[0]; 361 | M[ 1] = 0.0; M[ 5] = 1.0; M[ 9] = 0.0; M[13] = v[1]; 362 | M[ 2] = 0.0; M[ 6] = 0.0; M[10] = 1.0; M[14] = v[2]; 363 | M[ 3] = 0.0; M[ 7] = 0.0; M[11] = 0.0; M[15] = 1.0; 364 | } 365 | 366 | /* Give the matrix M scaling by vector v. */ 367 | 368 | void mscale(real *restrict M, const real *restrict v) 369 | { 370 | M[ 0] = v[0]; M[ 4] = 0.0; M[ 8] = 0.0; M[12] = 0.0; 371 | M[ 1] = 0.0; M[ 5] = v[1]; M[ 9] = 0.0; M[13] = 0.0; 372 | M[ 2] = 0.0; M[ 6] = 0.0; M[10] = v[2]; M[14] = 0.0; 373 | M[ 3] = 0.0; M[ 7] = 0.0; M[11] = 0.0; M[15] = 1.0; 374 | } 375 | 376 | /* Give the matrix M with basis vectors x, y, and z. */ 377 | 378 | void mbasis(real *restrict M, const real *restrict x, 379 | const real *restrict y, 380 | const real *restrict z) 381 | { 382 | M[ 0] = x[0]; M[ 4] = y[0]; M[ 8] = z[0]; M[12] = 0.0; 383 | M[ 1] = x[1]; M[ 5] = y[1]; M[ 9] = z[1]; M[13] = 0.0; 384 | M[ 2] = x[2]; M[ 6] = y[2]; M[10] = z[2]; M[14] = 0.0; 385 | M[ 3] = 0.0; M[ 7] = 0.0; M[11] = 0.0; M[15] = 1.0; 386 | } 387 | 388 | /* Give the orthogonal projection matrix M with given distances to the left, */ 389 | /* right, bottom, top, near and far clipping planes. */ 390 | 391 | void morthogonal(real *restrict M, real l, real r, 392 | real b, real t, 393 | real n, real f) 394 | { 395 | M[ 0] = 2.0 / (r - l); 396 | M[ 1] = 0.0; 397 | M[ 2] = 0.0; 398 | M[ 3] = 0.0; 399 | M[ 4] = 0.0; 400 | M[ 5] = 2.0 / (t - b); 401 | M[ 6] = 0.0; 402 | M[ 7] = 0.0; 403 | M[ 8] = 0.0; 404 | M[ 9] = 0.0; 405 | M[10] = -2.0 / (f - n); 406 | M[11] = 0.0; 407 | M[12] = -(r + l) / (r - l); 408 | M[13] = -(t + b) / (t - b); 409 | M[14] = -(f + n) / (f - n); 410 | M[15] = 1.0; 411 | } 412 | 413 | /* Give the perspective projection matrix with given distances to the left, */ 414 | /* right, bottom, top, near and far clipping planes. */ 415 | 416 | void mperspective(real *restrict M, real l, real r, 417 | real b, real t, 418 | real n, real f) 419 | { 420 | M[ 0] = (n + n) / (r - l); 421 | M[ 1] = 0.0; 422 | M[ 2] = 0.0; 423 | M[ 3] = 0.0; 424 | M[ 4] = 0.0; 425 | M[ 5] = (n + n) / (t - b); 426 | M[ 6] = 0.0; 427 | M[ 7] = 0.0; 428 | M[ 8] = (r + l) / (r - l); 429 | M[ 9] = (t + b) / (t - b); 430 | M[10] = (n + f) / (n - f); 431 | M[11] = -1.0; 432 | M[12] = 0.0; 433 | M[13] = 0.0; 434 | M[14] = -2.0 * (f * n) / (f - n); 435 | M[15] = 0.0; 436 | } 437 | 438 | /*----------------------------------------------------------------------------*/ 439 | /* Matrix operations */ 440 | 441 | /* Compose matrix N with matrix M. */ 442 | 443 | void mcompose(real *restrict M, const real *restrict N) 444 | { 445 | real T[16]; 446 | 447 | mmultiply(T, M, N); 448 | mcpy (M, T); 449 | } 450 | 451 | /* Compute the inverse I of matrix M. */ 452 | 453 | void minvert(real *restrict I, const real *restrict M) 454 | { 455 | real d, T[16]; 456 | 457 | assert(I != M); 458 | 459 | T[ 0] = +(M[ 5] * (M[10] * M[15] - M[11] * M[14]) - 460 | M[ 9] * (M[ 6] * M[15] - M[ 7] * M[14]) + 461 | M[13] * (M[ 6] * M[11] - M[ 7] * M[10])); 462 | T[ 1] = -(M[ 4] * (M[10] * M[15] - M[11] * M[14]) - 463 | M[ 8] * (M[ 6] * M[15] - M[ 7] * M[14]) + 464 | M[12] * (M[ 6] * M[11] - M[ 7] * M[10])); 465 | T[ 2] = +(M[ 4] * (M[ 9] * M[15] - M[11] * M[13]) - 466 | M[ 8] * (M[ 5] * M[15] - M[ 7] * M[13]) + 467 | M[12] * (M[ 5] * M[11] - M[ 7] * M[ 9])); 468 | T[ 3] = -(M[ 4] * (M[ 9] * M[14] - M[10] * M[13]) - 469 | M[ 8] * (M[ 5] * M[14] - M[ 6] * M[13]) + 470 | M[12] * (M[ 5] * M[10] - M[ 6] * M[ 9])); 471 | 472 | T[ 4] = -(M[ 1] * (M[10] * M[15] - M[11] * M[14]) - 473 | M[ 9] * (M[ 2] * M[15] - M[ 3] * M[14]) + 474 | M[13] * (M[ 2] * M[11] - M[ 3] * M[10])); 475 | T[ 5] = +(M[ 0] * (M[10] * M[15] - M[11] * M[14]) - 476 | M[ 8] * (M[ 2] * M[15] - M[ 3] * M[14]) + 477 | M[12] * (M[ 2] * M[11] - M[ 3] * M[10])); 478 | T[ 6] = -(M[ 0] * (M[ 9] * M[15] - M[11] * M[13]) - 479 | M[ 8] * (M[ 1] * M[15] - M[ 3] * M[13]) + 480 | M[12] * (M[ 1] * M[11] - M[ 3] * M[ 9])); 481 | T[ 7] = +(M[ 0] * (M[ 9] * M[14] - M[10] * M[13]) - 482 | M[ 8] * (M[ 1] * M[14] - M[ 2] * M[13]) + 483 | M[12] * (M[ 1] * M[10] - M[ 2] * M[ 9])); 484 | 485 | T[ 8] = +(M[ 1] * (M[ 6] * M[15] - M[ 7] * M[14]) - 486 | M[ 5] * (M[ 2] * M[15] - M[ 3] * M[14]) + 487 | M[13] * (M[ 2] * M[ 7] - M[ 3] * M[ 6])); 488 | T[ 9] = -(M[ 0] * (M[ 6] * M[15] - M[ 7] * M[14]) - 489 | M[ 4] * (M[ 2] * M[15] - M[ 3] * M[14]) + 490 | M[12] * (M[ 2] * M[ 7] - M[ 3] * M[ 6])); 491 | T[10] = +(M[ 0] * (M[ 5] * M[15] - M[ 7] * M[13]) - 492 | M[ 4] * (M[ 1] * M[15] - M[ 3] * M[13]) + 493 | M[12] * (M[ 1] * M[ 7] - M[ 3] * M[ 5])); 494 | T[11] = -(M[ 0] * (M[ 5] * M[14] - M[ 6] * M[13]) - 495 | M[ 4] * (M[ 1] * M[14] - M[ 2] * M[13]) + 496 | M[12] * (M[ 1] * M[ 6] - M[ 2] * M[ 5])); 497 | 498 | T[12] = -(M[ 1] * (M[ 6] * M[11] - M[ 7] * M[10]) - 499 | M[ 5] * (M[ 2] * M[11] - M[ 3] * M[10]) + 500 | M[ 9] * (M[ 2] * M[ 7] - M[ 3] * M[ 6])); 501 | T[13] = +(M[ 0] * (M[ 6] * M[11] - M[ 7] * M[10]) - 502 | M[ 4] * (M[ 2] * M[11] - M[ 3] * M[10]) + 503 | M[ 8] * (M[ 2] * M[ 7] - M[ 3] * M[ 6])); 504 | T[14] = -(M[ 0] * (M[ 5] * M[11] - M[ 7] * M[ 9]) - 505 | M[ 4] * (M[ 1] * M[11] - M[ 3] * M[ 9]) + 506 | M[ 8] * (M[ 1] * M[ 7] - M[ 3] * M[ 5])); 507 | T[15] = +(M[ 0] * (M[ 5] * M[10] - M[ 6] * M[ 9]) - 508 | M[ 4] * (M[ 1] * M[10] - M[ 2] * M[ 9]) + 509 | M[ 8] * (M[ 1] * M[ 6] - M[ 2] * M[ 5])); 510 | 511 | d = M[ 0] * T[ 0] + M[ 4] * T[ 4] + M[ 8] * T[ 8] + M[12] * T[12]; 512 | 513 | if (fabs(d) > 0.0) 514 | { 515 | d = 1.0 / d; 516 | I[ 0] = T[ 0] * d; 517 | I[ 1] = T[ 4] * d; 518 | I[ 2] = T[ 8] * d; 519 | I[ 3] = T[12] * d; 520 | I[ 4] = T[ 1] * d; 521 | I[ 5] = T[ 5] * d; 522 | I[ 6] = T[ 9] * d; 523 | I[ 7] = T[13] * d; 524 | I[ 8] = T[ 2] * d; 525 | I[ 9] = T[ 6] * d; 526 | I[10] = T[10] * d; 527 | I[11] = T[14] * d; 528 | I[12] = T[ 3] * d; 529 | I[13] = T[ 7] * d; 530 | I[14] = T[11] * d; 531 | I[15] = T[15] * d; 532 | } 533 | } 534 | 535 | /* Give the transpose T of matrix M. */ 536 | 537 | void mtranspose(real *restrict T, const real *restrict M) 538 | { 539 | assert(T != M); 540 | 541 | T[ 0] = M[ 0]; T[ 4] = M[ 1]; T[ 8] = M[ 2]; T[12] = M[ 3]; 542 | T[ 1] = M[ 4]; T[ 5] = M[ 5]; T[ 9] = M[ 6]; T[13] = M[ 7]; 543 | T[ 2] = M[ 8]; T[ 6] = M[ 9]; T[10] = M[10]; T[14] = M[11]; 544 | T[ 3] = M[12]; T[ 7] = M[13]; T[11] = M[14]; T[15] = M[15]; 545 | } 546 | 547 | /* Multiply matrices A and B. */ 548 | 549 | void mmultiply(real *restrict M, const real *restrict A, 550 | const real *restrict B) 551 | { 552 | assert(M != A); 553 | assert(M != B); 554 | 555 | M[ 0] = A[ 0] * B[ 0] + A[ 4] * B[ 1] + A[ 8] * B[ 2] + A[12] * B[ 3]; 556 | M[ 1] = A[ 1] * B[ 0] + A[ 5] * B[ 1] + A[ 9] * B[ 2] + A[13] * B[ 3]; 557 | M[ 2] = A[ 2] * B[ 0] + A[ 6] * B[ 1] + A[10] * B[ 2] + A[14] * B[ 3]; 558 | M[ 3] = A[ 3] * B[ 0] + A[ 7] * B[ 1] + A[11] * B[ 2] + A[15] * B[ 3]; 559 | 560 | M[ 4] = A[ 0] * B[ 4] + A[ 4] * B[ 5] + A[ 8] * B[ 6] + A[12] * B[ 7]; 561 | M[ 5] = A[ 1] * B[ 4] + A[ 5] * B[ 5] + A[ 9] * B[ 6] + A[13] * B[ 7]; 562 | M[ 6] = A[ 2] * B[ 4] + A[ 6] * B[ 5] + A[10] * B[ 6] + A[14] * B[ 7]; 563 | M[ 7] = A[ 3] * B[ 4] + A[ 7] * B[ 5] + A[11] * B[ 6] + A[15] * B[ 7]; 564 | 565 | M[ 8] = A[ 0] * B[ 8] + A[ 4] * B[ 9] + A[ 8] * B[10] + A[12] * B[11]; 566 | M[ 9] = A[ 1] * B[ 8] + A[ 5] * B[ 9] + A[ 9] * B[10] + A[13] * B[11]; 567 | M[10] = A[ 2] * B[ 8] + A[ 6] * B[ 9] + A[10] * B[10] + A[14] * B[11]; 568 | M[11] = A[ 3] * B[ 8] + A[ 7] * B[ 9] + A[11] * B[10] + A[15] * B[11]; 569 | 570 | M[12] = A[ 0] * B[12] + A[ 4] * B[13] + A[ 8] * B[14] + A[12] * B[15]; 571 | M[13] = A[ 1] * B[12] + A[ 5] * B[13] + A[ 9] * B[14] + A[13] * B[15]; 572 | M[14] = A[ 2] * B[12] + A[ 6] * B[13] + A[10] * B[14] + A[14] * B[15]; 573 | M[15] = A[ 3] * B[12] + A[ 7] * B[13] + A[11] * B[14] + A[15] * B[15]; 574 | } 575 | 576 | /* Orthonormalize the rotation of matrix M, preserving the Z direction. */ 577 | 578 | void morthonormalize(real *restrict O, const real *restrict M) 579 | { 580 | assert(O != M); 581 | 582 | vcrs(O + 0, M + 4, M + 8); 583 | vcrs(O + 4, M + 8, O + 0); 584 | 585 | vnormalize(O + 8, M + 8); 586 | vnormalize(O + 4, O + 4); 587 | vnormalize(O + 0, O + 0); 588 | 589 | O[ 3] = M[ 3]; 590 | O[ 7] = M[ 7]; 591 | O[11] = M[11]; 592 | O[12] = M[12]; 593 | O[13] = M[13]; 594 | O[14] = M[14]; 595 | O[15] = M[15]; 596 | } 597 | 598 | /*----------------------------------------------------------------------------*/ 599 | 600 | /* Compute the matrix M given by quaternion q. */ 601 | 602 | void mquaternion(real *restrict M, const real *restrict q) 603 | { 604 | real t[4]; 605 | 606 | qnormalize(t, q); 607 | 608 | vquaternionx(M + 0, t); 609 | vquaterniony(M + 4, t); 610 | vquaternionz(M + 8, t); 611 | 612 | M[ 3] = 0.0; 613 | M[ 7] = 0.0; 614 | M[11] = 0.0; 615 | M[12] = 0.0; 616 | M[13] = 0.0; 617 | M[14] = 0.0; 618 | M[15] = 1.0; 619 | } 620 | 621 | /* Compute the matrix M given by the Euler angles e. */ 622 | 623 | void meuler(real *restrict M, const real *restrict e) 624 | { 625 | const real cx = cos(e[0]), sx = sin(e[0]); 626 | const real cy = cos(e[1]), sy = sin(e[1]); 627 | const real cz = cos(e[2]), sz = sin(e[2]); 628 | 629 | M[ 0] = cy * cz - sx * sy * sz; 630 | M[ 1] = cy * sz + sx * sy * cz; 631 | M[ 2] = -cx * sy; 632 | M[ 3] = 0.0; 633 | M[ 4] = -cx * sz; 634 | M[ 5] = cx * cz; 635 | M[ 6] = sx; 636 | M[ 7] = 0.0; 637 | M[ 8] = sy * cz + sx * cy * sz; 638 | M[ 9] = sy * sz - sx * cy * cz; 639 | M[10] = cx * cy; 640 | M[11] = 0.0; 641 | M[12] = 0.0; 642 | M[13] = 0.0; 643 | M[14] = 0.0; 644 | M[15] = 1.0; 645 | } 646 | 647 | /*----------------------------------------------------------------------------*/ 648 | 649 | /* Compute the quaternion q given by the Euler angles e. */ 650 | 651 | void qeuler(real *restrict q, const real *restrict e) 652 | { 653 | const real cx = cos(e[0] * 0.5), sx = sin(e[0] * 0.5); 654 | const real cy = cos(e[1] * 0.5), sy = sin(e[1] * 0.5); 655 | const real cz = cos(e[2] * 0.5), sz = sin(e[2] * 0.5); 656 | 657 | q[0] = sx * cy * cz - cx * sy * sz; 658 | q[1] = cx * sy * cz + sx * cy * sz; 659 | q[2] = sx * sy * cz + cx * cy * sz; 660 | q[3] = cx * cy * cz - sx * sy * sz; 661 | 662 | qnormalize(q, q); 663 | } 664 | 665 | /* Compute the quaternion q given by rotation matrix M. */ 666 | 667 | void qmatrix(real *restrict q, const real *restrict M) 668 | { 669 | if (1.0 + M[0] + M[5] + M[10] > 0.0) 670 | { 671 | const real s = 0.5 / sqrt(1.0 + M[0] + M[5] + M[10]); 672 | 673 | q[2] = (M[1] - M[4]) * s; 674 | q[1] = (M[8] - M[2]) * s; 675 | q[0] = (M[6] - M[9]) * s; 676 | q[3] = 0.25 / s; 677 | } 678 | else if (M[0] > M[5] && M[0] > M[10]) 679 | { 680 | const real s = 2.0 * sqrt(1.0 + M[0] - M[5] - M[10]); 681 | 682 | q[1] = (M[1] + M[4]) / s; 683 | q[2] = (M[8] + M[2]) / s; 684 | q[3] = (M[6] - M[9]) / s; 685 | q[0] = 0.25 * s; 686 | } 687 | else if (M[5] > M[10]) 688 | { 689 | const real s = 2.0 * sqrt(1.0 + M[5] - M[0] - M[10]); 690 | 691 | q[0] = (M[1] + M[4]) / s; 692 | q[3] = (M[8] - M[2]) / s; 693 | q[2] = (M[6] + M[9]) / s; 694 | q[1] = 0.25 * s; 695 | } 696 | else 697 | { 698 | const real s = 2.0 * sqrt(1.0 + M[10] - M[0] - M[5]); 699 | 700 | q[3] = (M[1] - M[4]) / s; 701 | q[0] = (M[8] + M[2]) / s; 702 | q[1] = (M[6] + M[9]) / s; 703 | q[2] = 0.25 * s; 704 | } 705 | } 706 | 707 | /*----------------------------------------------------------------------------*/ 708 | 709 | /* Extract a set of Euler angles from quaternion q. */ 710 | 711 | void equaternion(real *restrict e, const real *restrict q) 712 | { 713 | real M[16]; 714 | 715 | mquaternion(M, q); 716 | ematrix (e, M); 717 | } 718 | 719 | /* Extract a set of Euler angles from rotation matrix M. */ 720 | 721 | void ematrix(real *restrict e, const real *restrict M) 722 | { 723 | real sx = M[6]; 724 | real cx = sqrt(1.0 - sx * sx); 725 | 726 | if (cx > 0.0) 727 | { 728 | real cy = M[10] / cx; 729 | real sy = -M[ 2] / cx; 730 | real cz = M[ 5] / cx; 731 | real sz = -M[ 4] / cx; 732 | 733 | e[0] = atan2(sx, cx); 734 | e[1] = atan2(sy, cy); 735 | e[2] = atan2(sz, cz); 736 | } 737 | else 738 | { 739 | e[0] = 0.0; 740 | e[1] = 0.0; 741 | e[2] = 0.0; 742 | } 743 | } 744 | -------------------------------------------------------------------------------- /math3d.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2009 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #ifndef UTIL3D_MATH3D_H 22 | #define UTIL3D_MATH3D_H 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | /* When C99 is unavailable, GCC and MSVC allow __inline and __restrict. */ 29 | 30 | #if __STDC_VERSION__ < 199901L 31 | #define inline __inline 32 | #define restrict __restrict 33 | #endif 34 | 35 | /*----------------------------------------------------------------------------*/ 36 | 37 | #ifdef CONFIG_MATH3D_FLOAT 38 | typedef float real; 39 | #else 40 | typedef double real; 41 | #endif 42 | 43 | /*----------------------------------------------------------------------------*/ 44 | 45 | static inline real radians(real a) 46 | { 47 | return a * 0.017453292519943295769236907684886; 48 | } 49 | 50 | static inline real degrees(real a) 51 | { 52 | return a * 57.295779513082320876798154814105170; 53 | } 54 | 55 | static inline real lerp(real a, real b, real t) 56 | { 57 | return (1.0 - t) * a + t * b; 58 | } 59 | 60 | /*----------------------------------------------------------------------------*/ 61 | /* Vector operations */ 62 | 63 | static inline real vdot(const real *a, const real *b) 64 | { 65 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; 66 | } 67 | 68 | static inline real vlen(const real *a) 69 | { 70 | return sqrt(vdot(a, a)); 71 | } 72 | 73 | static inline void vcpy(real *restrict a, const real *restrict b) 74 | { 75 | a[0] = b[0]; 76 | a[1] = b[1]; 77 | a[2] = b[2]; 78 | } 79 | 80 | static inline void vneg(real *a, const real *b) 81 | { 82 | a[0] = -b[0]; 83 | a[1] = -b[1]; 84 | a[2] = -b[2]; 85 | } 86 | 87 | static inline void vmul(real *a, const real *b, real k) 88 | { 89 | a[0] = b[0] * k; 90 | a[1] = b[1] * k; 91 | a[2] = b[2] * k; 92 | } 93 | 94 | static inline void vcrs(real *restrict a, const real *restrict b, 95 | const real *restrict c) 96 | { 97 | a[0] = b[1] * c[2] - b[2] * c[1]; 98 | a[1] = b[2] * c[0] - b[0] * c[2]; 99 | a[2] = b[0] * c[1] - b[1] * c[0]; 100 | } 101 | 102 | static inline void vadd(real *a, const real *b, const real *c) 103 | { 104 | a[0] = b[0] + c[0]; 105 | a[1] = b[1] + c[1]; 106 | a[2] = b[2] + c[2]; 107 | } 108 | 109 | static inline void vsub(real *a, const real *b, const real *c) 110 | { 111 | a[0] = b[0] - c[0]; 112 | a[1] = b[1] - c[1]; 113 | a[2] = b[2] - c[2]; 114 | } 115 | 116 | static inline void vmad(real *a, const real *b, const real *c, real k) 117 | { 118 | a[0] = b[0] + c[0] * k; 119 | a[1] = b[1] + c[1] * k; 120 | a[2] = b[2] + c[2] * k; 121 | } 122 | 123 | static inline void vproject(real *a, const real *b, const real *c) 124 | { 125 | const real k = vdot(b, c); 126 | 127 | a[0] = b[0] - c[0] * k; 128 | a[1] = b[1] - c[1] * k; 129 | a[2] = b[2] - c[2] * k; 130 | } 131 | 132 | static inline void vnormalize(real *a, const real *b) 133 | { 134 | const real k = 1.0 / vlen(b); 135 | 136 | a[0] = b[0] * k; 137 | a[1] = b[1] * k; 138 | a[2] = b[2] * k; 139 | } 140 | 141 | void vtransform(real *restrict, const real *restrict, const real *restrict); 142 | void ptransform(real *restrict, const real *restrict, const real *restrict); 143 | void wtransform(real *restrict, const real *restrict, const real *restrict); 144 | void vslerp (real *, const real *, const real *, real); 145 | 146 | /*----------------------------------------------------------------------------*/ 147 | /* Quaternion operations */ 148 | 149 | static inline real qdot(const real *a, const real *b) 150 | { 151 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; 152 | } 153 | 154 | static inline void qcpy(real *restrict a, const real *restrict b) 155 | { 156 | a[0] = b[0]; 157 | a[1] = b[1]; 158 | a[2] = b[2]; 159 | a[3] = b[3]; 160 | } 161 | 162 | static inline void qadd(real *a, const real *b, const real *c) 163 | { 164 | a[0] = b[0] + c[0]; 165 | a[1] = b[1] + c[1]; 166 | a[2] = b[2] + c[2]; 167 | a[3] = b[3] + c[3]; 168 | } 169 | 170 | static inline void qscale(real *a, const real *b, real k) 171 | { 172 | a[0] = b[0] * k; 173 | a[1] = b[1] * k; 174 | a[2] = b[2] * k; 175 | a[3] = b[3] * k; 176 | } 177 | 178 | static inline void qconjugate(real *a, const real *b) 179 | { 180 | a[0] = -b[0]; 181 | a[1] = -b[1]; 182 | a[2] = -b[2]; 183 | a[3] = b[3]; 184 | } 185 | 186 | static inline void qinvert(real *a, const real *b) 187 | { 188 | const real k = 1.0 / qdot(b, b); 189 | 190 | a[0] = -b[0] * k; 191 | a[1] = -b[1] * k; 192 | a[2] = -b[2] * k; 193 | a[3] = b[3] * k; 194 | } 195 | 196 | static inline void qnormalize(real *a, const real *b) 197 | { 198 | const real k = 1.0 / sqrt(qdot(b, b)); 199 | 200 | a[0] = b[0] * k; 201 | a[1] = b[1] * k; 202 | a[2] = b[2] * k; 203 | a[3] = b[3] * k; 204 | } 205 | 206 | static inline void qsign(real *a, const real *b, const real *c) 207 | { 208 | if (qdot(b, c) > 0.0) 209 | { 210 | a[0] = c[0]; 211 | a[1] = c[1]; 212 | a[2] = c[2]; 213 | a[3] = c[3]; 214 | } 215 | else 216 | { 217 | a[0] = -c[0]; 218 | a[1] = -c[1]; 219 | a[2] = -c[2]; 220 | a[3] = -c[3]; 221 | } 222 | } 223 | 224 | void qpow (real *, const real *, real); 225 | void qexp (real *, const real *); 226 | void qlog (real *, const real *); 227 | void qslerp(real *, const real *, const real *, real); 228 | void qsquad(real *, const real *, const real *, 229 | const real *, const real *, real); 230 | 231 | void qrotate (real *restrict, const real *restrict, real); 232 | void qmultiply(real *restrict, const real *restrict, const real *restrict); 233 | 234 | /*----------------------------------------------------------------------------*/ 235 | /* Tranformation matrices */ 236 | 237 | static inline void mcpy(real *M, const real *N) 238 | { 239 | M[ 0] = N[ 0]; 240 | M[ 1] = N[ 1]; 241 | M[ 2] = N[ 2]; 242 | M[ 3] = N[ 3]; 243 | M[ 4] = N[ 4]; 244 | M[ 5] = N[ 5]; 245 | M[ 6] = N[ 6]; 246 | M[ 7] = N[ 7]; 247 | M[ 8] = N[ 8]; 248 | M[ 9] = N[ 9]; 249 | M[10] = N[10]; 250 | M[11] = N[11]; 251 | M[12] = N[12]; 252 | M[13] = N[13]; 253 | M[14] = N[14]; 254 | M[15] = N[15]; 255 | } 256 | 257 | void midentity (real *restrict); 258 | void mrotatex (real *restrict, real); 259 | void mrotatey (real *restrict, real); 260 | void mrotatez (real *restrict, real); 261 | void mrotate (real *restrict, const real *restrict, real); 262 | void mtranslate (real *restrict, const real *restrict); 263 | void mscale (real *restrict, const real *restrict); 264 | void mbasis (real *restrict, const real *restrict, 265 | const real *restrict, 266 | const real *restrict); 267 | void morthogonal (real *restrict, real, real, real, real, real, real); 268 | void mperspective(real *restrict, real, real, real, real, real, real); 269 | 270 | /*----------------------------------------------------------------------------*/ 271 | /* Matrix operations */ 272 | 273 | void mcompose (real *restrict, const real *restrict); 274 | void minvert (real *restrict, const real *restrict); 275 | void mtranspose(real *restrict, const real *restrict); 276 | void mmultiply (real *restrict, const real *restrict, const real *restrict); 277 | 278 | void morthonormalize(real *restrict, const real *restrict); 279 | 280 | /*----------------------------------------------------------------------------*/ 281 | /* Rotation conversions */ 282 | 283 | void mquaternion(real *restrict, const real *restrict); 284 | void meuler (real *restrict, const real *restrict); 285 | 286 | void qeuler (real *restrict, const real *restrict); 287 | void qmatrix (real *restrict, const real *restrict); 288 | 289 | void equaternion(real *restrict, const real *restrict); 290 | void ematrix (real *restrict, const real *restrict); 291 | 292 | static inline void vquaternionx(real *v, const real *restrict q) 293 | { 294 | v[0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]); 295 | v[1] = 2.0 * (q[0] * q[1] + q[2] * q[3]); 296 | v[2] = 2.0 * (q[0] * q[2] - q[1] * q[3]); 297 | } 298 | 299 | static inline void vquaterniony(real *v, const real *restrict q) 300 | { 301 | v[0] = 2.0 * (q[0] * q[1] - q[2] * q[3]); 302 | v[1] = 1.0 - 2.0 * (q[0] * q[0] + q[2] * q[2]); 303 | v[2] = 2.0 * (q[1] * q[2] + q[0] * q[3]); 304 | } 305 | 306 | static inline void vquaternionz(real *v, const real *restrict q) 307 | { 308 | v[0] = 2.0 * (q[0] * q[2] + q[1] * q[3]); 309 | v[1] = 2.0 * (q[1] * q[2] - q[0] * q[3]); 310 | v[2] = 1.0 - 2.0 * (q[0] * q[0] + q[1] * q[1]); 311 | } 312 | 313 | /*----------------------------------------------------------------------------*/ 314 | 315 | #define vprint(v) \ 316 | printf("%8.3f%8.3f%8.3f\n", (v)[0], (v)[1], (v)[2]) 317 | #define qprint(q) \ 318 | printf("%8.3f%8.3f%8.3f%8.3f\n", (q)[0], (q)[1], (q)[2], (q)[3]) 319 | #define mprint(M) \ 320 | printf("%8.3f%8.3f%8.3f%8.3f\n" \ 321 | "%8.3f%8.3f%8.3f%8.3f\n" \ 322 | "%8.3f%8.3f%8.3f%8.3f\n" \ 323 | "%8.3f%8.3f%8.3f%8.3f\n", (M)[ 0], (M)[ 4], (M)[ 8], (M)[12], \ 324 | (M)[ 1], (M)[ 5], (M)[ 9], (M)[13], \ 325 | (M)[ 2], (M)[ 6], (M)[10], (M)[14], \ 326 | (M)[ 3], (M)[ 7], (M)[11], (M)[15]) 327 | 328 | /*----------------------------------------------------------------------------*/ 329 | 330 | #ifdef __cplusplus 331 | } 332 | #endif 333 | #endif 334 | -------------------------------------------------------------------------------- /math3d.md: -------------------------------------------------------------------------------- 1 | # math3d 2 | 3 | This module implements a right-handed 3D mathematics library supporting three-component vectors, four-by-four matrices, quaternions, and Euler angles. These values are represented using ordinary C arrays of type `real`. 4 | 5 | By default, `real` is `typedef`-ed to `double`. However, `float` is used instead if the preprocessor symbol `CONFIG_MATH3D_FLOAT` is defined during inclusion of `math3d.h` and compilation of `math3d.c`. 6 | 7 | - [math3d.c](math3d.c) 8 | - [math3d.h](math3d.h) 9 | 10 | Array layout is as follows. Note that the quaternion scalar part follows the vector part, allowing vector functions to operate upon the vector part of a quaternion. Also, the matrix representation is column-wise, matching the layout expected by OpenGL. Here, *aij* is the element at row *i*, column *j*. 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |
Vector:xyz
Quaternion:xyzs
Euler:αβγ
Matrix:a00a10a20a30a01a11a21a31a02a12a22a32a03a13a23a33
52 | 53 | Euler angle *α* gives rotation about the *x* axis, *β* about the *y* axis, and *γ* about the *z* axis. (Please beware the difference between the *y* and the Greek gamma *γ*.) These rotations are concatenated in *zxy*-order, as this is a common standard for interactive 3D. Angle *α* ranges from −½ *π* to ½ *π* while *β* and *γ* range from −*π* to *π*. 54 | 55 | The signatures of all functions follow. The first argument to most functions is a pointer to an array to receive the result. Subsequent arguments are constant pointers to operands, or real values. Pointers are declared `restrict` when two or more arguments may not reasonably refer to the same array. Light-weight functions are declared `static inline` in the header while heavy functions are exported from the C module. 56 | 57 | Rotation arguments and Euler angles are given in radians. 58 | 59 | - `real radians(real a)` 60 | 61 | Convert angle `a` from degrees to radians. 62 | 63 | - `real degrees(real a)` 64 | 65 | Convert angle `a` from radians to degrees. 66 | 67 | ## Vector operations 68 | 69 | - `real vlen(const real *a)` 70 | 71 | Length. `||a||` 72 | 73 | - `void vcpy(real *restrict a, const real *restrict b)` 74 | 75 | Copy. `a = b` 76 | 77 | - `void vneg(real *a, const real *b)` 78 | 79 | Negation. `a = -b` 80 | 81 | - `real vdot(const real *a, const real *b)` 82 | 83 | Dot product. `a` ⋅ `b` 84 | 85 | - `void vmul(real *a, const real *b, real k)` 86 | 87 | Scalar product. `a = b * k` 88 | 89 | - `void vcrs(real *restrict a, const real *restrict b, const real *restrict c)` 90 | 91 | Cross product. `a = b `×` c` 92 | 93 | - `void vadd(real *a, const real *b, const real *c)` 94 | 95 | Addition. `a = b + c` 96 | 97 | - `void vsub(real *a, const real *b, const real *c)` 98 | 99 | Subtraction. `a = b - c` 100 | 101 | - `void vmad(real *a, const real *b, const real *c, real k)` 102 | 103 | Scalar-multiply-add. `a = b + c * k` 104 | 105 | - `void vproject(real *a, const real *b, const real *c)` 106 | 107 | Project vector `c` parallel with vector `b`. 108 | 109 | - `void vnormalize(real *a, const real *b)` 110 | 111 | Normalization. `a = b / ||b||` 112 | 113 | - `void vtransform(real *restrict a, const real *restrict M, const real *restrict b)` 114 | 115 | Transform vector `b` by matrix `M` (neglecting translation.) 116 | 117 | - `void ptransform(real *restrict a, const real *restrict M, const real *restrict b)` 118 | 119 | Transform position `b` by matrix `M` (including translation.) 120 | 121 | - `void vslerp(real *a, const real *b, const real *c, real t)` 122 | 123 | Compute the spherical linear interpolation of vectors `b` and `c` at a time `t` between zero and one. 124 | 125 | ## Quaternion operations 126 | 127 | - `real qdot(const real *a, const real *b)` 128 | 129 | Dot product. `a `⋅` b` 130 | 131 | - `void qadd(real *a, const real *b, const real *c)` 132 | 133 | Add. `a = b + c` 134 | 135 | - `void qscale(real *a, const real *b, real k)` 136 | 137 | Scalar multiply. `a = b * k` 138 | 139 | - `void qconjugate(real *a, const real *b)` 140 | 141 | Conjugate. 142 | 143 | - `void qinvert(real *a, const real *b)` 144 | 145 | Inverse. 146 | 147 | - `void qnormalize(real *a, const real *b)` 148 | 149 | Normalization. 150 | 151 | - `void qpow(real *a, const real *b, real h);` 152 | 153 | Compute quaternion `b` raised to the power `h`. 154 | 155 | - `void qexp(real *a, const real *b);` 156 | 157 | Compute *e* raised to the power quaternion `b`. 158 | 159 | - `void qlog(real *a, const real *b);` 160 | 161 | Compute the natural logarithm of quaternion `b`. 162 | 163 | - `void qsign(real *a, const real *b, const real *c)` 164 | 165 | Provide quaternion `c` with the same "sign" as quaternion `b`, selecting `a = c` or `a = -c` such that `a` ⋅ `b` ≥ 0. This ensures that interpolation from `b` to `c` goes "the short way around." 166 | 167 | - `void qslerp(real *a, const real *b, const real *c, real t)` 168 | 169 | Compute the spherical linear interpolation of quaternions `b` and `c` at a time `t` between zero and one. 170 | 171 | - `void qsquad(real *a, const real *b, const real *c, const real *d, const real *e, real t)` 172 | 173 | Compute the spherical quadrangle interpolation of quaternions `c` and `d` at time `t` along the spline through `b`, `c`, `d`, and `e`. 174 | 175 | - `void qrotate(real *restrict q, const real *restrict v, real a)` 176 | 177 | Compute the quaternion giving rotation about vector `v` through angle `a`. 178 | 179 | - `void qmultiply(real *restrict a, const real *restrict b, const real *restrict c)` 180 | 181 | Multiply quaternions `b` and `c`. 182 | 183 | ## Matrix operations 184 | 185 | - `void minvert(real *restrict I, const real *restrict M)` 186 | 187 | Compute the inverse of matrix `M`. 188 | 189 | - `void mtranspose(real *restrict T, const real *restrict M)` 190 | 191 | Return the transpose of matrix `M`. 192 | 193 | - `void mmultiply(real *restrict M, const real *restrict A, const real *restrict B)` 194 | 195 | Multiply matrices `A` and `B`. 196 | 197 | - `void morthonormalize(real *restrict O, const real *restrict M)` 198 | 199 | Compute the orthonormalization of the rotation of matrix `M`, preserving the direction of the `z` axis, and the non-rotation elements of `M`. 200 | 201 | ## Transformation matrices 202 | 203 | - `void midentity(real *restrict M)` 204 | 205 | The identity. 206 | 207 | - `void mrotatex(real *restrict M, real a)` 208 | 209 | Rotation about the *x* axis through angle `a`. 210 | 211 | - `void mrotatey(real *restrict M, real a)` 212 | 213 | Rotation about the *y* axis through angle `a`. 214 | 215 | - `void mrotatez(real *restrict M, real a)` 216 | 217 | Rotation about the *z* axis through angle `a`. 218 | 219 | - `void mrotate(real *restrict M, const real *restrict v, real a)` 220 | 221 | Rotation about vector `v` through angle `a`. 222 | 223 | - `void mtranslate(real *restrict M, const real *restrict v)` 224 | 225 | Translation along vector `v`. 226 | 227 | - `void mscale(real *restrict M, const real *restrict v)` 228 | 229 | Scaling by vector `v`. 230 | 231 | - `void mbasis(real *restrict M, const real *restrict x, const real *restrict y, const real *restrict z)` 232 | 233 | The basis given by vectors `x`, `y`, and `z`. 234 | 235 | - `void morthogonal(real *restrict M,real l, real r,real b, real t,real n, real f)` 236 | 237 | The orthogonal projection with left, right, bottom, top, near, and far clipping plane distances. 238 | 239 | - `void mperspective(real *restrict M,real l, real r,real b, real t,real n, real f)` 240 | 241 | The perspective projection with left, right, bottom, top, near, and far clipping plane distances. 242 | 243 | ## Rotation conversions 244 | 245 | ### To matrix 246 | 247 | - `void mquaternion(real *restrict M, const real *restrict q)` 248 | 249 | Compute the rotation matrix given by quaternion `q`. 250 | 251 | - `void meuler(real *restrict M, const real *restrict e)` 252 | 253 | Compute the rotation matrix given by Euler angles `e`. 254 | 255 | ### To quaternion 256 | 257 | - `void qmatrix(real *restrict q, const real *restrict M)` 258 | 259 | Compute the quaternion given by rotation matrix `M`. 260 | 261 | - `void qeuler(real *restrict q, const real *restrict e)` 262 | 263 | Compute the quaternion given by Euler angles `e`. 264 | 265 | ### To Euler 266 | 267 | - `void ematrix(real *restrict e, const real *restrict M)` 268 | 269 | Extract a set of Euler angles from rotation matrix `M`. 270 | 271 | - `void equaternion(real *restrict e, const real *restrict q)` 272 | 273 | Extract a set of Euler angles from quaternion `q`. 274 | -------------------------------------------------------------------------------- /noise.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2007 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #include 22 | #include 23 | #include "noise.h" 24 | 25 | /* This implementation provides Perlin Simplex Noise. It is a C translation */ 26 | /* of Java given by Ken Perlin in Appendix B of "Real-Time Shading SIGGRAPH */ 27 | /* Course Notes 2001, Chapter 2: Noise Hardware." */ 28 | 29 | /*----------------------------------------------------------------------------*/ 30 | 31 | struct state 32 | { 33 | int A[3]; 34 | int i; 35 | int j; 36 | int k; 37 | double u; 38 | double v; 39 | double w; 40 | }; 41 | 42 | /*----------------------------------------------------------------------------*/ 43 | 44 | static int c(int N, int B) 45 | { 46 | return N >> B & 1; 47 | } 48 | 49 | static int b(int i, int j, int k, int B) 50 | { 51 | static const int T[8] = { 0x15, 0x38, 0x32, 0x2c, 0x0d, 0x13, 0x07, 0x2a }; 52 | 53 | return T[c(i, B) << 2 | 54 | c(j, B) << 1 | 55 | c(k, B)]; 56 | } 57 | 58 | static int shuffle(int i, int j, int k) 59 | { 60 | return (b(i, j, k, 0) + b(j, k, i, 1) + b(k, i, j, 2) + b(i, j, k, 3) + 61 | b(j, k, i, 4) + b(k, i, j, 5) + b(i, j, k, 6) + b(j, k, i, 7)); 62 | } 63 | 64 | double K(int A[3], const int i[3], const double v[3], int a) 65 | { 66 | /* Compute skewed input points. */ 67 | 68 | double s = (A[0] + A[1] + A[2]) / 6.0; 69 | double t; 70 | 71 | double x = v[0] - A[0] + s; 72 | double y = v[1] - A[1] + s; 73 | double z = v[2] - A[2] + s; 74 | 75 | /* Compute the index of the pseudo-random gradient. */ 76 | 77 | int h = shuffle(i[0] + A[0], i[1] + A[1], i[2] + A[2]); 78 | 79 | /* Iterate to the next simplex vertex. */ 80 | 81 | A[a]++; 82 | 83 | /* Find the contribution of this simplex vertex. */ 84 | 85 | if ((t = 0.6 - x * x - y * y - z * z) >= 0) 86 | { 87 | /* Isolate the bits of the gradient index. */ 88 | 89 | int b5 = h >> 5 & 1; 90 | int b4 = h >> 4 & 1; 91 | int b3 = h >> 3 & 1; 92 | int b2 = h >> 2 & 1; 93 | int b = h & 3; 94 | 95 | /* Compute the gradient magnitude using the 3 lower bits. */ 96 | 97 | double p = (b == 1) ? x : ((b == 2) ? y : z); 98 | double q = (b == 1) ? y : ((b == 2) ? z : x); 99 | double r = (b == 1) ? z : ((b == 2) ? x : y); 100 | double m; 101 | 102 | /* Compute the gradient octant using the 3 upper bits. */ 103 | 104 | p = (b5 == ( b3)) ? -p : p; 105 | q = (b5 == (b4 )) ? -q : q; 106 | r = (b5 != (b4 ^ b3)) ? -r : r; 107 | 108 | /* Sum the gradient components giving magnitude. */ 109 | 110 | m = p + ((b == 0) ? q + r : ((b2 == 0) ? q : r)); 111 | 112 | /* Evaluate the spherical kernel giving this vertex's contribution. */ 113 | 114 | return 8 * (t * t * t * t) * m; 115 | } 116 | return 0; 117 | } 118 | 119 | /*----------------------------------------------------------------------------*/ 120 | 121 | double noise_sample(double x, double y, double z) 122 | { 123 | int hi, A[3] = { 0, 0, 0 }; 124 | int lo, i[3]; 125 | double v[3]; 126 | double s; 127 | 128 | /* Find the integer coordinates (i, j, k) in simplex grid skewed space. */ 129 | 130 | s = (x + y + z) / 3.0; 131 | 132 | i[0] = (int) floor(x + s); 133 | i[1] = (int) floor(y + s); 134 | i[2] = (int) floor(z + s); 135 | 136 | /* Find the coordinates relative to the unskewed cube. */ 137 | 138 | s = (i[0] + i[1] + i[2]) / 6.0; 139 | 140 | v[0] = x - i[0] + s; 141 | v[1] = y - i[1] + s; 142 | v[2] = z - i[2] + s; 143 | 144 | /* Determine which simplex contains the input point. */ 145 | 146 | hi = (v[0] >= v[2]) ? ((v[0] >= v[1]) ? 0 : 1) : ((v[1] >= v[2]) ? 1 : 2); 147 | lo = (v[0] < v[2]) ? ((v[0] < v[1]) ? 0 : 1) : ((v[1] < v[2]) ? 1 : 2); 148 | 149 | /* Evaluate the contribution of each vertex of the simplex. */ 150 | 151 | return K(A, i, v, hi ) 152 | + K(A, i, v, 3 - hi - lo) 153 | + K(A, i, v, lo) 154 | + K(A, i, v, 0 ); 155 | } 156 | 157 | /*----------------------------------------------------------------------------*/ 158 | 159 | void noise_buffer(double x, double y, double z, 160 | double f, int w, int h, double *v) 161 | { 162 | /* Fill the given buffer with noise at frequency f. Note the extrema. */ 163 | 164 | double k0 = +DBL_MAX; 165 | double k1 = -DBL_MAX; 166 | 167 | int i; 168 | int j; 169 | 170 | for (i = 0; i < h; ++i) 171 | for (j = 0; j < w; ++j) 172 | { 173 | double dx = f * (j + 0.5) / w; 174 | double dy = f * (i + 0.5) / h; 175 | 176 | double k = noise_sample(x + dx, y + dy, z); 177 | 178 | v[i * w + j] = k; 179 | 180 | if (k0 > k) k0 = k; 181 | if (k1 < k) k1 = k; 182 | } 183 | 184 | /* Normalize the noise to [-1, 1]. */ 185 | 186 | for (i = 0; i < h; ++i) 187 | for (j = 0; j < w; ++j) 188 | v[i * w + j] = 2.0 * (v[i * w + j] - k0) / (k1 - k0) - 1.0; 189 | } 190 | 191 | /*----------------------------------------------------------------------------*/ 192 | -------------------------------------------------------------------------------- /noise.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2007 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #ifndef UTIL3D_NOISE_HPP 22 | #define UTIL3D_NOISE_HPP 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | /*----------------------------------------------------------------------------*/ 29 | 30 | double noise_sample(double, double, double); 31 | void noise_buffer(double, double, double, double, int, int, double *); 32 | 33 | /*----------------------------------------------------------------------------*/ 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | #endif 39 | -------------------------------------------------------------------------------- /noise.md: -------------------------------------------------------------------------------- 1 | # noise 2 | 3 | This module implements a three-dimensional coherent noise generator using the Simplex method of Ken Perlin, as described in Real-Time Shading SIGGRAPH Course Notes (2001), Chapter 2: Noise Hardware. The output is deterministic, but infinite and non-repeating in all directions. 4 | 5 | - [noise.c](noise.c) 6 | - [noise.h](noise.h) 7 | 8 | ## API 9 | 10 | - `double noise_sample(double x, double y, double z)` 11 | 12 | Sample the noise function at the point (`x`, `y`, `z`). The output is in the range −1 to +1, though this bound is extremely loose. 13 | 14 | - `void noise_buffer(double x, double y, double z, double f, int w, int h, double *v)` 15 | 16 | Generate a monochrome image of coherent noise by sampling the noise function along the Z plane. The point (`x`, `y`, `z`) gives the 3D position of the origin of the 2D sampling. The argument `f` gives a frequency coefficient, where a frequency of one maps the width and height of the buffer onto a 1×1 area of the noise function. 17 | 18 | The argument `v` points to the output buffer, and `w` and `h` give the width and height of that buffer. After sampling, the values in the output buffer are normalized to the exact range −1 to +1. 19 | 20 | ## Examples 21 | 22 | Coherent noise is usually sampled in several harmonics. Here, assume `p1` through `p7` are `n`×`n` buffers. We fill each with a block of coherent noise, sampled at (0.0, 0.0, 0.5). 23 | 24 | 25 | noise_buffer(0.0, 0.0, 0.5, 2.0, n, n, p1); 26 | noise_buffer(0.0, 0.0, 0.5, 4.0, n, n, p2); 27 | noise_buffer(0.0, 0.0, 0.5, 8.0, n, n, p3); 28 | noise_buffer(0.0, 0.0, 0.5, 16.0, n, n, p4); 29 | noise_buffer(0.0, 0.0, 0.5, 32.0, n, n, p5); 30 | noise_buffer(0.0, 0.0, 0.5, 64.0, n, n, p6); 31 | noise_buffer(0.0, 0.0, 0.5, 128.0, n, n, p7); 32 | 33 | 34 | Different effects can be achieved through different combinations of these harmonics. For example, the basic 1/f noise divides the contribution of each harmonic by its frequency. The sum 1/2 + 1/4 + 1/8 + … approaches 1 in the limit, so the range of the sum approaches the range of an individual harmonic in the limit. The output in the range −1 to +1 is normalized to the range 0 to 1. 35 | 36 | 37 | for (i = 0; i < n * n; ++i) 38 | 39 | p[i] = (1.0 + p1[i] / 2.0 40 | + p2[i] / 4.0 41 | + p3[i] / 8.0 42 | + p4[i] / 16.0 43 | + p5[i] / 32.0 44 | + p6[i] / 64.0 45 | + p7[i] / 128.0) / 2.0; 46 | 47 | Here is that result: 48 | 49 | ![](img/noise1.jpg) 50 | 51 | The "plasma" effect is achieved by taking the absolute value of each harmonic. 52 | 53 | 54 | for (i = 0; i < n * n; ++i) 55 | 56 | p[i] = (1.0 + fabs(p1[i]) / 2.0 57 | + fabs(p2[i]) / 4.0 58 | + fabs(p3[i]) / 8.0 59 | + fabs(p4[i]) / 16.0 60 | + fabs(p5[i]) / 32.0 61 | + fabs(p6[i]) / 64.0 62 | + fabs(p7[i]) / 128.0) / 2.0; 63 | 64 | Here is that result: 65 | 66 | ![](img/noise2.jpg) 67 | 68 | For more examples and discussion, see Perlin's [noise course notes](http://www.csee.umbc.edu/~olano/s2002c36/ch02.pdf). 69 | -------------------------------------------------------------------------------- /plane.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2009 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "image.h" 27 | #include "plane.h" 28 | 29 | /*----------------------------------------------------------------------------*/ 30 | 31 | struct plane 32 | { 33 | GLuint vbo[1]; 34 | GLuint ebo[2]; 35 | GLfloat line[4]; 36 | GLfloat fill[4]; 37 | GLsizei n; 38 | }; 39 | 40 | /*----------------------------------------------------------------------------*/ 41 | 42 | struct vert 43 | { 44 | GLfloat v[3]; 45 | GLfloat n[3]; 46 | }; 47 | 48 | static struct vert *add_vert(struct vert *v, GLfloat x, GLfloat z) 49 | { 50 | v->v[0] = x; 51 | v->v[1] = 0; 52 | v->v[2] = z; 53 | 54 | v->n[0] = 0; 55 | v->n[1] = 1; 56 | v->n[2] = 0; 57 | 58 | return v + 1; 59 | } 60 | 61 | static GLushort *add_rect(GLushort *e, GLushort i0, GLushort i1, 62 | GLushort i2, GLushort i3) 63 | { 64 | e[0] = i0; 65 | e[1] = i2; 66 | e[2] = i1; 67 | e[3] = i3; 68 | e[4] = i1; 69 | e[5] = i2; 70 | 71 | return e + 6; 72 | } 73 | 74 | static void init_verts(GLuint vbo[1], GLushort n, GLfloat g) 75 | { 76 | GLsizei nv = 4 * (n + 1) * (n + 1), sv = nv * sizeof (struct vert); 77 | struct vert *v; 78 | struct vert *w; 79 | 80 | /* Initialize the vertex array. */ 81 | 82 | if ((w = v = (struct vert *) malloc(sv))) 83 | { 84 | const GLfloat d = 0.50f * n; 85 | 86 | GLushort i, j; 87 | 88 | for (i = 0; i <= n; ++i) 89 | for (j = 0; j <= n; ++j) 90 | { 91 | w = add_vert(w, j - d - g, i - d - g); 92 | w = add_vert(w, j - d + g, i - d - g); 93 | w = add_vert(w, j - d - g, i - d + g); 94 | w = add_vert(w, j - d + g, i - d + g); 95 | } 96 | } 97 | 98 | /* Copy the vertex data to the buffer object. */ 99 | 100 | glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); 101 | glBufferData(GL_ARRAY_BUFFER, sv, v, GL_STATIC_DRAW); 102 | 103 | free(v); 104 | } 105 | 106 | static void init_elems(GLuint ebo[2], GLushort n) 107 | { 108 | GLsizei nf = 6 * (n ) * ( n ), sf = nf * sizeof (GLushort); 109 | GLsizei nl = 6 * (n + 1) * (3 * n + 1), sl = nl * sizeof (GLushort); 110 | 111 | GLushort *f = 0; 112 | GLushort *p = 0; 113 | GLushort *l = 0; 114 | GLushort *q = 0; 115 | GLushort i, j; 116 | 117 | /* Initialize the element arrays. */ 118 | 119 | if ((f = p = (GLushort *) malloc(sf)) && 120 | (l = q = (GLushort *) malloc(sl))) 121 | 122 | for (i = 0; i <= n; ++i) 123 | for (j = 0; j <= n; ++j) 124 | { 125 | const GLushort a = 4 * ((n + 1) * (i ) + j); 126 | const GLushort b = 4 * ((n + 1) * (i + 1) + j); 127 | 128 | q = add_rect(q, a + 0, a + 1, a + 2, a + 3); 129 | if (j < n) q = add_rect(q, a + 1, a + 4, a + 3, a + 6); 130 | if (i < n) q = add_rect(q, a + 2, a + 3, b + 0, b + 1); 131 | if (i < n && j < n) p = add_rect(p, a + 3, a + 6, b + 1, b + 4); 132 | } 133 | 134 | /* Copy the element data to the buffer object. */ 135 | 136 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]); 137 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sf, f, GL_STATIC_DRAW); 138 | 139 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[1]); 140 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sl, l, GL_STATIC_DRAW); 141 | 142 | free(f); 143 | free(l); 144 | } 145 | 146 | /*----------------------------------------------------------------------------*/ 147 | 148 | /* Allocate and initialize a new plane object. There must be a current OpenGL */ 149 | /* context at the time. */ 150 | 151 | plane *plane_create(int n, float g) 152 | { 153 | plane *P; 154 | 155 | if ((P = (plane *) malloc(sizeof (plane)))) 156 | { 157 | const float l[4] = { 0.9f, 0.9f, 0.9f, 1.0f }; 158 | const float f[4] = { 0.8f, 0.8f, 0.8f, 1.0f }; 159 | 160 | glGenBuffers(1, P->vbo); 161 | glGenBuffers(2, P->ebo); 162 | 163 | init_verts(P->vbo, n, g); 164 | init_elems(P->ebo, n); 165 | 166 | plane_color(P, l, f); 167 | 168 | P->n = n; 169 | } 170 | return P; 171 | } 172 | 173 | /* Release the given plane structure and all resources held by it. */ 174 | 175 | void plane_delete(plane *P) 176 | { 177 | assert(P); 178 | 179 | glDeleteBuffers(2, P->ebo); 180 | glDeleteBuffers(1, P->vbo); 181 | 182 | free(P); 183 | } 184 | 185 | /*----------------------------------------------------------------------------*/ 186 | 187 | /* Render the given plane structure. */ 188 | 189 | void plane_render(plane *P) 190 | { 191 | const GLsizei nf = 6 * (P->n ) * ( P->n ); 192 | const GLsizei nl = 6 * (P->n + 1) * (3 * P->n + 1); 193 | 194 | const size_t sz = sizeof (GLfloat); 195 | 196 | glEnable(GL_COLOR_MATERIAL); 197 | { 198 | /* Enable the necessary array pointers. */ 199 | 200 | glEnableClientState(GL_VERTEX_ARRAY); 201 | glEnableClientState(GL_NORMAL_ARRAY); 202 | { 203 | /* Bind the array pointers to the array buffer object. */ 204 | 205 | glBindBuffer(GL_ARRAY_BUFFER, P->vbo[0]); 206 | { 207 | glVertexPointer(3, GL_FLOAT, sz * 6, (GLvoid *) ( 0)); 208 | glNormalPointer( GL_FLOAT, sz * 6, (GLvoid *) (sz * 3)); 209 | 210 | /* Render the squares. */ 211 | 212 | glColor4fv(P->fill); 213 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, P->ebo[0]); 214 | glDrawElements(GL_TRIANGLES, nf, GL_UNSIGNED_SHORT, 0); 215 | 216 | /* Render the lines */ 217 | 218 | glColor4fv(P->line); 219 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, P->ebo[1]); 220 | glDrawElements(GL_TRIANGLES, nl, GL_UNSIGNED_SHORT, 0); 221 | 222 | /* Revert all state. */ 223 | 224 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 225 | } 226 | glBindBuffer(GL_ARRAY_BUFFER, 0); 227 | } 228 | glDisableClientState(GL_NORMAL_ARRAY); 229 | glDisableClientState(GL_VERTEX_ARRAY); 230 | } 231 | glDisable(GL_COLOR_MATERIAL); 232 | } 233 | 234 | void plane_color(plane *P, const float *l, const float *f) 235 | { 236 | P->line[0] = l[0]; 237 | P->line[1] = l[1]; 238 | P->line[2] = l[2]; 239 | P->line[3] = l[3]; 240 | 241 | P->fill[0] = f[0]; 242 | P->fill[1] = f[1]; 243 | P->fill[2] = f[2]; 244 | P->fill[3] = f[3]; 245 | } 246 | 247 | /*----------------------------------------------------------------------------*/ 248 | -------------------------------------------------------------------------------- /plane.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2009 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #ifndef UTIL3D_PLANE_H 22 | #define UTIL3D_PLANE_H 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | /*----------------------------------------------------------------------------*/ 29 | 30 | typedef struct plane plane; 31 | 32 | plane *plane_create(int, float); 33 | void plane_delete(plane *); 34 | void plane_render(plane *); 35 | void plane_color (plane *, const float *, const float *); 36 | 37 | /*----------------------------------------------------------------------------*/ 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | #endif 43 | -------------------------------------------------------------------------------- /plane.md: -------------------------------------------------------------------------------- 1 | # plane 2 | 3 | This module is a very basic OpenGL code that renders a simple gridded plane as on the cover of Edward Tufte's [Visual Explanations](http://www.amazon.com/Visual-Explanations-Quantities-Evidence-Narrative/dp/0961392126). The plane is often useful as a backdrop to simple 3D demos, and can help work through those dreaded blank screen bugs. In addition, it represents an example of basic vertex buffer object creation, population, and rendering. 4 | 5 | - [plane.c](plane.c) 6 | - [plane.h](plane.h) 7 | 8 | ## Compilation 9 | 10 | To use this module, simply link it with your own code. It requires OpenGL and [GLEW](http://glew.sourceforge.net/). 11 | 12 | cc -o program program.c plane.c -lGLEW -lGL -lm 13 | 14 | ## API 15 | 16 | - `plane *plane_create(int n, float g)` 17 | 18 | Create and return a plane structure. `n` gives the number of tiles in each row and column of the grid. `g` determines the line thickness and gives the fraction of a tile consumed by line. Tufte's grid appears to have a `g` value of around 0.02. The plane is oriented with the Y axis up. Each tile is one unit square in 3D space. It may be rotated, translated, and scaled normally. 19 | 20 | - `void plane_delete(plane *P)` 21 | 22 | Delete the plane structure and release all OpenGL resources held by it. 23 | 24 | - `void plane_render(plane *P)` 25 | 26 | Render the plane using OpenGL. 27 | 28 | - `void plane_color (plane *P, const float *L, const float *F)` 29 | 30 | Change the color of the plane. The `L` and `F` parameters each receive an array of four floats giving red, green, blue, and alpha values for the lines and fills, respectively. 31 | 32 | ![](img/plane.png) 33 | -------------------------------------------------------------------------------- /type.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include FT_FREETYPE_H 27 | 28 | #include "type.h" 29 | 30 | /*----------------------------------------------------------------------------*/ 31 | 32 | typedef struct sort sort; 33 | typedef struct vert vert; 34 | 35 | /* A VERT gives the spatial position and atlas coordinate of one corner of a */ 36 | /* glyph rectangle. A glyph has four verts, and a line is a string of glyphs. */ 37 | /* The process of type setting is the computation of verts from sorts. */ 38 | 39 | struct vert 40 | { 41 | float v[3]; 42 | float t[2]; 43 | }; 44 | 45 | /* A SORT gives the position of a specific letter face in an atlas, including */ 46 | /* the UNICODE character code, glyph metrics, and atlas location. */ 47 | 48 | struct sort 49 | { 50 | int c; /* Character code */ 51 | int x; /* Glyph X bearing */ 52 | int y; /* Glyph Y bearing */ 53 | int a; /* Glyph pixel advance */ 54 | int X; /* Atlas X position */ 55 | int Y; /* Atlas Y position */ 56 | int W; /* Atlas width */ 57 | int H; /* Atlas height */ 58 | }; 59 | 60 | /* Sorts are indeed sorted by character. This comparison function allows the */ 61 | /* standard C bsearch and qsort functions to be applied. */ 62 | 63 | static int sortcmp1(const void *A, const void *B) 64 | { 65 | const int *p = (const int *) A; 66 | const sort *S = (const sort *) B; 67 | 68 | if (*p < S->c) return -1; 69 | if (*p > S->c) return +1; 70 | 71 | return 0; 72 | } 73 | 74 | static int sortcmp2(const void *A, const void *B) 75 | { 76 | const sort *S = (const sort *) A; 77 | const sort *T = (const sort *) B; 78 | 79 | if (S->c < T->c) return -1; 80 | if (S->c > T->c) return +1; 81 | 82 | return 0; 83 | } 84 | 85 | /*----------------------------------------------------------------------------*/ 86 | 87 | struct font 88 | { 89 | FT_Library library; /* FreeType library */ 90 | FT_Face face; /* FreeType face */ 91 | int h; /* Height */ 92 | int a; /* Normal space advance */ 93 | float k; /* Letter space factor */ 94 | 95 | sort *sorts; /* Sort array */ 96 | int n; /* Number of sorts renderered */ 97 | 98 | GLuint atlas; /* Atlas texture object */ 99 | int s; /* Size of the atlas */ 100 | int x; /* Current atlas cursor position */ 101 | int y; /* Current atlas cursor position */ 102 | int m; /* Height of the current atlas line */ 103 | }; 104 | 105 | struct line 106 | { 107 | GLuint vbo; /* Vertex buffer object */ 108 | vert *v; /* Vertex buffer */ 109 | font *F; /* Font upref */ 110 | int n; /* Number of characters */ 111 | }; 112 | 113 | /*----------------------------------------------------------------------------*/ 114 | 115 | /* Decode and return the next UTF8 character refered to by the given string */ 116 | /* handle. See that *((*p)++) business down there? Avoid doing that. */ 117 | 118 | static int utf8(const char **p) 119 | { 120 | if ((**p & 0x80) == 0x00) 121 | { 122 | int a = *((*p)++); 123 | 124 | return a; 125 | } 126 | if ((**p & 0xE0) == 0xC0) 127 | { 128 | int a = *((*p)++) & 0x1F; 129 | int b = *((*p)++) & 0x3F; 130 | 131 | return (a << 6) | b; 132 | } 133 | if ((**p & 0xF0) == 0xE0) 134 | { 135 | int a = *((*p)++) & 0x0F; 136 | int b = *((*p)++) & 0x3F; 137 | int c = *((*p)++) & 0x3F; 138 | 139 | return (a << 12) | (b << 6) | c; 140 | } 141 | if ((**p & 0xF8) == 0xF0) 142 | { 143 | int a = *((*p)++) & 0x07; 144 | int b = *((*p)++) & 0x3F; 145 | int c = *((*p)++) & 0x3F; 146 | int d = *((*p)++) & 0x3F; 147 | 148 | return (a << 18) | (b << 12) | (c << 8) | d; 149 | } 150 | return 0; 151 | } 152 | 153 | /*----------------------------------------------------------------------------*/ 154 | 155 | /* Compute a reasonable size for the atlas. Find the first power of two value */ 156 | /* larger than a multiple of the font base line. This is quite arbitrary. */ 157 | 158 | static int atlas_size(int h) 159 | { 160 | int s = 1; 161 | 162 | while (s < h * 4) 163 | s *= 2; 164 | 165 | return s; 166 | } 167 | 168 | static GLuint atlas_create(int s) 169 | { 170 | GLubyte *p = (GLubyte *) calloc(s, s); 171 | GLuint o; 172 | 173 | glGenTextures(1, &o); 174 | glBindTexture(GL_TEXTURE_2D, o); 175 | 176 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 177 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 178 | 179 | glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, s, s, 0, 180 | GL_ALPHA, GL_UNSIGNED_BYTE, p); 181 | free(p); 182 | 183 | return o; 184 | } 185 | 186 | static void atlas_delete(GLuint o) 187 | { 188 | glDeleteTextures(1, &o); 189 | } 190 | 191 | /*----------------------------------------------------------------------------*/ 192 | 193 | static void font_sort_create(font *F, sort *S, int c) 194 | { 195 | /* Render the glyph and cache its metrics. */ 196 | 197 | FT_Load_Glyph(F->face, FT_Get_Char_Index(F->face, c), FT_LOAD_DEFAULT); 198 | 199 | S->x = F->face->glyph->metrics.horiBearingX >> 6; 200 | S->y = F->face->glyph->metrics.horiBearingY >> 6; 201 | S->W = F->face->glyph->metrics.width >> 6; 202 | S->H = F->face->glyph->metrics.height >> 6; 203 | S->a = F->face->glyph->advance.x >> 6; 204 | 205 | FT_Render_Glyph(F->face->glyph, FT_RENDER_MODE_NORMAL); 206 | 207 | /* Assign this sort a position in the atlas. */ 208 | 209 | if (F->x + S->W >= F->s) 210 | { 211 | F->y += F->m + 1; 212 | F->x = 0; 213 | F->m = 0; 214 | } 215 | 216 | S->X = F->x; 217 | S->Y = F->y; 218 | F->x += S->W + 1; 219 | F->m = S->H > F->m ? S->H : F->m; 220 | 221 | /* Upload the glyph image to the atlas. */ 222 | 223 | glPushAttrib(GL_PIXEL_MODE_BIT); 224 | { 225 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 226 | 227 | glPixelTransferf(GL_RED_BIAS, 1.0f); 228 | glPixelTransferf(GL_GREEN_BIAS, 1.0f); 229 | glPixelTransferf(GL_BLUE_BIAS, 1.0f); 230 | 231 | glBindTexture (GL_TEXTURE_2D, F->atlas); 232 | glTexSubImage2D(GL_TEXTURE_2D, 0, S->X, S->Y, S->W, S->H, GL_ALPHA, 233 | GL_UNSIGNED_BYTE, F->face->glyph->bitmap.buffer); 234 | } 235 | glPopAttrib(); 236 | } 237 | 238 | /* Locate and return the sort for the given character. If no sort is found, */ 239 | /* insert and initialize a new one. This is a bit slow to insert a new sort */ 240 | /* (being lazily written with realloc and qsort) but each character will be */ 241 | /* used many times and inserted at most once, so the penalty is minimal. */ 242 | 243 | static sort *font_sort_search(font *F, int c) 244 | { 245 | size_t sz = sizeof (sort); 246 | sort *S = NULL; 247 | sort *T = NULL; 248 | 249 | if ((S = (sort *) bsearch(&c, F->sorts, (F->n), sz, sortcmp1)) == 0) 250 | { 251 | if ((T = (sort *) realloc(F->sorts, (F->n + 1) * sz))) 252 | { 253 | S = T + F->n; 254 | S->c = c; 255 | F->n += 1; 256 | F->sorts = T; 257 | 258 | font_sort_create(F, S, c); 259 | 260 | qsort(F->sorts, F->n, sz, sortcmp2); 261 | 262 | return font_sort_search(F, c); 263 | } 264 | } 265 | return S; 266 | } 267 | 268 | /*----------------------------------------------------------------------------*/ 269 | 270 | static void warn(const char *mesg, const char *name) 271 | { 272 | if (name) 273 | fprintf(stderr, "type : %s : %s\n", mesg, name); 274 | else 275 | fprintf(stderr, "type : %s\n", mesg); 276 | } 277 | 278 | /* Try to use the given memory buffer as a raw font resource. Failing that, */ 279 | /* assume the pointer gives the name of a font file. */ 280 | 281 | static int font_load(const void *ptr, size_t len, font *F) 282 | { 283 | if (FT_New_Memory_Face(F->library, (const FT_Byte *) ptr, len, 0, &F->face)) 284 | { 285 | if (FT_New_Face(F->library, (const char *) ptr, 0, &F->face)) 286 | { 287 | return 0; 288 | } 289 | else return 1; 290 | } 291 | else return 1; 292 | } 293 | 294 | /* Initialize FreeType and load the given font at the given size. Create a */ 295 | /* new empty texture atlas. */ 296 | 297 | font *font_create(const void *ptr, size_t len, int size, float k) 298 | { 299 | font *F = NULL; 300 | 301 | assert(ptr); 302 | assert(size > 0); 303 | 304 | if ((F = (font *) calloc(1, sizeof (font)))) 305 | { 306 | if (FT_Init_FreeType(&F->library) == 0) 307 | { 308 | if (font_load(ptr, len, F)) 309 | { 310 | if (FT_Set_Pixel_Sizes(F->face, 0, size) == 0) 311 | { 312 | FT_UInt i = FT_Get_Char_Index(F->face, ' '); 313 | 314 | FT_Load_Glyph(F->face, i, FT_LOAD_DEFAULT); 315 | 316 | F->k = k; 317 | F->a = F->face->glyph->advance.x >> 6; 318 | F->h = F->face->size->metrics.height >> 6; 319 | F->s = atlas_size (F->h); 320 | F->atlas = atlas_create(F->s); 321 | } 322 | else warn("Failure to set font pixel size", NULL); 323 | } 324 | else warn("Failure to load font", NULL); 325 | } 326 | else warn("Failure to initialize FreeType", NULL); 327 | } 328 | return F; 329 | } 330 | 331 | /* Release all resources held by the given font structure. */ 332 | 333 | void font_delete(font *F) 334 | { 335 | assert(F); 336 | 337 | atlas_delete(F->atlas); 338 | 339 | FT_Done_Face(F->face); 340 | FT_Done_FreeType(F->library); 341 | 342 | free(F->sorts); 343 | free(F); 344 | } 345 | 346 | int font_height(font *F) 347 | { 348 | assert(F); 349 | return F->h; 350 | } 351 | 352 | /*----------------------------------------------------------------------------*/ 353 | 354 | static int line_set(vert *v, sort *S, int s, int x, float k, const double *M) 355 | { 356 | /* Compute the texture coordinate rectangle. */ 357 | 358 | float tl = (float) S->X / s; 359 | float tr = (float) (S->X + S->W) / s; 360 | float tt = (float) S->Y / s; 361 | float tb = (float) (S->Y + S->H) / s; 362 | 363 | /* Compute the vertex rectangle. */ 364 | 365 | float vl = (float) S->x + x; 366 | float vr = (float) S->x + S->W + x; 367 | float vt = (float) S->y; 368 | float vb = (float) S->y - S->H; 369 | 370 | /* Set the attributes of each vertex. */ 371 | 372 | v[0].v[0] = (float) (M[0] * vl + M[4] * vb + M[12]); 373 | v[0].v[1] = (float) (M[1] * vl + M[5] * vb + M[13]); 374 | v[0].v[2] = (float) (M[2] * vl + M[6] * vb + M[14]); 375 | v[0].t[0] = tl; 376 | v[0].t[1] = tb; 377 | 378 | v[1].v[0] = (float) (M[0] * vr + M[4] * vb + M[12]); 379 | v[1].v[1] = (float) (M[1] * vr + M[5] * vb + M[13]); 380 | v[1].v[2] = (float) (M[2] * vr + M[6] * vb + M[14]); 381 | v[1].t[0] = tr; 382 | v[1].t[1] = tb; 383 | 384 | v[2].v[0] = (float) (M[0] * vr + M[4] * vt + M[12]); 385 | v[2].v[1] = (float) (M[1] * vr + M[5] * vt + M[13]); 386 | v[2].v[2] = (float) (M[2] * vr + M[6] * vt + M[14]); 387 | v[2].t[0] = tr; 388 | v[2].t[1] = tt; 389 | 390 | v[3].v[0] = (float) (M[0] * vl + M[4] * vt + M[12]); 391 | v[3].v[1] = (float) (M[1] * vl + M[5] * vt + M[13]); 392 | v[3].v[2] = (float) (M[2] * vl + M[6] * vt + M[14]); 393 | v[3].t[0] = tl; 394 | v[3].t[1] = tt; 395 | 396 | return (int) (x + S->a * k); 397 | } 398 | 399 | static int layout(vert *v, const char *str, int exp, double *mat, font *F) 400 | { 401 | const char *p; 402 | sort *S; 403 | 404 | int s = 0; 405 | int n = 0; 406 | int e = 0; 407 | int x = 0; 408 | int i = 0; 409 | int c; 410 | 411 | FT_UInt r = 0; 412 | FT_UInt l = 0; 413 | FT_Vector k; 414 | 415 | /* Count the number of spaces and non-spaces in this string. */ 416 | 417 | for (p = str; (c = utf8(&p));) 418 | if (c == ' ') 419 | s++; 420 | else 421 | n++; 422 | 423 | /* Find a sort and a kern, and set each character. */ 424 | 425 | for (p = str; (c = utf8(&p));) 426 | 427 | if (c == ' ') 428 | { 429 | x += (int) (F->a * F->k + (exp * (e + 1) / s) - (exp * e / s)); 430 | e += 1; 431 | } 432 | else if ((S = font_sort_search(F, c))) 433 | { 434 | r = FT_Get_Char_Index(F->face, c); 435 | FT_Get_Kerning (F->face, l, r, FT_KERNING_DEFAULT, &k); 436 | l = r; 437 | 438 | x = line_set(v + 4 * i, S, F->s, x + (k.x >> 6), F->k, mat); 439 | i++; 440 | } 441 | 442 | return i; 443 | } 444 | 445 | /* Initialize a new line and its array of vertices. Request a sort for each */ 446 | /* character of the given string. Set the vertices of the line accordingly. */ 447 | 448 | line *line_layout(int strc, const char * const *strv, int *expv, double *matv, font *F) 449 | { 450 | const size_t sz = sizeof (vert); 451 | const char *p = NULL; 452 | 453 | line *L = NULL; 454 | int n = 0; 455 | int i; 456 | int c; 457 | 458 | double I[16] = { 459 | 1.0, 0.0, 0.0, 0.0, 460 | 0.0, 1.0, 0.0, 0.0, 461 | 0.0, 0.0, 1.0, 0.0, 462 | 0.0, 0.0, 0.0, 1.0, 463 | }; 464 | 465 | assert(strv); 466 | assert(F); 467 | 468 | /* Count the total number of non-spaces in the text. */ 469 | 470 | for (i = 0; i < strc; i++) 471 | for (p = strv[i]; (c = utf8(&p));) 472 | if (c != ' ') 473 | n++; 474 | 475 | /* Allocate and initialize a line structure. */ 476 | 477 | if ((L = (line *) calloc(1, sizeof (line)))) 478 | { 479 | /* Allocate storage for the vertex array. */ 480 | 481 | if ((L->v = (vert *) calloc(n * 4, sz))) 482 | { 483 | /* Typeset each string in turn. */ 484 | 485 | int j = 0; 486 | for (i = 0; i < strc; i++) 487 | { 488 | I[13] = -i * F->h; 489 | j += layout(L->v + 4 * j, strv[i], expv ? expv[i] : 0, 490 | matv ? matv + i * 16 : I, F); 491 | } 492 | 493 | /* Copy the data to a vertex buffer object. */ 494 | 495 | glGenBuffers(1, &L->vbo); 496 | glBindBuffer(GL_ARRAY_BUFFER, L->vbo); 497 | glBufferData(GL_ARRAY_BUFFER, 4 * sz * n, L->v, GL_STATIC_DRAW); 498 | } 499 | 500 | L->n = n; 501 | L->F = F; 502 | } 503 | return L; 504 | } 505 | 506 | /* Create a single line of text with default expansion. This is just a short */ 507 | /* cut that invokes the layout function with default arguments. */ 508 | 509 | line *line_create(const char *str, font *F) 510 | { 511 | return line_layout(1, &str, NULL, NULL, F); 512 | } 513 | 514 | void line_delete(line *L) 515 | { 516 | if (L) 517 | { 518 | glDeleteBuffers(1, &L->vbo); 519 | free(L->v); 520 | free(L); 521 | } 522 | } 523 | 524 | /* Compute the unexpanded pixel length of the given string when rendered with */ 525 | /* the given font at its current size. */ 526 | 527 | int line_length(const char *str, font *F) 528 | { 529 | FT_UInt r = 0; 530 | FT_UInt l = 0; 531 | FT_Vector k; 532 | 533 | const char *p; 534 | sort *S; 535 | int c; 536 | int x = 0; 537 | 538 | assert(F); 539 | 540 | for (p = str; (c = utf8(&p));) 541 | 542 | if (c == ' ') 543 | { 544 | x += (int) (F->a * F->k); 545 | } 546 | else if ((S = font_sort_search(F, c))) 547 | { 548 | r = FT_Get_Char_Index(F->face, c); 549 | FT_Get_Kerning (F->face, l, r, FT_KERNING_DEFAULT, &k); 550 | l = r; 551 | x += (int) (S->a * F->k); 552 | } 553 | 554 | return x; 555 | } 556 | 557 | void line_render(line *L) 558 | { 559 | if (L) 560 | { 561 | const size_t sv = sizeof (vert); 562 | const size_t sf = sizeof (float); 563 | 564 | glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); 565 | { 566 | glEnableClientState(GL_VERTEX_ARRAY); 567 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 568 | 569 | /* Activate the atlas texture and vertex buffer. */ 570 | 571 | glBindTexture(GL_TEXTURE_2D, L->F->atlas); 572 | glBindBuffer (GL_ARRAY_BUFFER, L->vbo); 573 | 574 | /* Set the location and layout of the vertex attributes. */ 575 | 576 | glVertexPointer (3, GL_FLOAT, sv, (GLvoid *) (0)); 577 | glTexCoordPointer(2, GL_FLOAT, sv, (GLvoid *) (3 * sf)); 578 | 579 | /* Render all text. */ 580 | 581 | glDrawArrays(GL_QUADS, 0, L->n * 4); 582 | } 583 | glPopClientAttrib(); 584 | } 585 | } 586 | 587 | /*----------------------------------------------------------------------------*/ -------------------------------------------------------------------------------- /type.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010 Robert Kooima */ 2 | /* */ 3 | /* Permission is hereby granted, free of charge, to any person obtaining a */ 4 | /* copy of this software and associated documentation files (the "Software"), */ 5 | /* to deal in the Software without restriction, including without limitation */ 6 | /* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ 7 | /* and/or sell copies of the Software, and to permit persons to whom the */ 8 | /* Software is furnished to do so, subject to the following conditions: */ 9 | /* */ 10 | /* The above copyright notice and this permission notice shall be included in */ 11 | /* all copies or substantial portions of the Software. */ 12 | /* */ 13 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ 14 | /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ 15 | /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL */ 16 | /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ 17 | /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */ 18 | /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */ 19 | /* DEALINGS IN THE SOFTWARE. */ 20 | 21 | #ifndef UTIL3D_TYPE_H 22 | #define UTIL3D_TYPE_H 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | /*----------------------------------------------------------------------------*/ 29 | 30 | typedef struct font font; 31 | typedef struct line line; 32 | 33 | /*----------------------------------------------------------------------------*/ 34 | 35 | font *font_create(const void *, size_t, int, float); 36 | int font_height(font *); 37 | void font_delete(font *); 38 | 39 | line *line_layout(int, const char * const *, int *, double *, font *); 40 | line *line_create(const char *, font *); 41 | int line_length(const char *, font *); 42 | void line_render(line *); 43 | void line_delete(line *); 44 | 45 | /*----------------------------------------------------------------------------*/ 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | #endif -------------------------------------------------------------------------------- /type.md: -------------------------------------------------------------------------------- 1 | # type 2 | 3 | This module implements a mechanism for rendering text using OpenGL. This may be used to add GUI elements and labels in both 3D scenes and in 2D overlays. 4 | 5 | - [type.c](type.c) 6 | - [type.h](type.h) 7 | 8 | A `line` of text is drawn as a series of textured quads using a single `font`. Each quad appears as a single glyph. All quads in a `line` are stored in a single vertex buffer object, and all glyphs in a `font` are represented in a single texture. 9 | 10 | Despite its name, one `line` may give many thousands of distinct textual elements, each with an independant 3D transformation. This mechanism enables the very efficient display of great quantities of text with only a single texture binding and a single vertex batch. 11 | 12 | ## Compilation 13 | 14 | To use this module, simply link it with your own code. It requires the [FreeType 2](http://www.freetype.org) library, OpenGL, and [GLEW](http://glew.sourceforge.net/). 15 | 16 | cc -o program program.c type.c -lfreetype2 -lGLEW -lGL -lm 17 | 18 | ## Font API 19 | 20 | - `font *font_create(const void *data, size_t len, int size, float spacing)` 21 | 22 | Create a new font object with the given pixel size and letter spacing. The font file format may be TrueType, OpenType, Type 1, or any other of several formats supported by FreeType. The font data is given in one of two ways: 23 | 24 | - `data` gives the font file name and `len` is unused. Or... 25 | 26 | - `data` gives a font data buffer and `len` gives its length. 27 | 28 | The `size` argument determines both the height of the rendered text in 3D units, and also the resolution of rendered glyphs in pixels. Letter `spacing` allows the space between glyphs to be tuned. Most applications will use a value of 1.0, though slight compression (0.95) or expansion (1.10) is appropriate for some fonts. 29 | 30 | - `void font_delete(font *F)` 31 | 32 | Delete font `F` and release all resources held by it. 33 | 34 | - `int font_height(font *F)` 35 | 36 | Return the normal line spacing of font `F`. For many fonts this value will be similar to the `size` requested from `font_create`, but this is not guaranteed. When laying out multiple lines of text, applications should prefer this value. 37 | 38 | ## Line API 39 | 40 | - `line *line_create(const char *str, font *F)` 41 | 42 | Create a new line of text set using font `F`. The `str` argument is ASCII, and allows UTF8-encoded UNICODE. 43 | 44 | - `line *line_layout(int strc, char **strv, int *expv, double *matv, font *F)` 45 | 46 | Lay out a series of lines of text. This function is a generalization of `line_create` and is called by it with defaul parameters. `strc` gives the number of strings and `strv` gives an array of pointers to strings. `expv` gives an expansion parameter for each string giving the total number of pixels to be inserted into the white space of the line to justify it. This value is calculed using `line_length` described below. Pass `NULL` if no expansion is needed. `matv` gives a 4 x 4 transformation matrix for each string. Each matrix is a list of 16 double precision floats, as expected by OpenGL. Pass `NULL` if no transformation is needed. 47 | 48 | - `void line_delete(line *L)` 49 | 50 | Delete line `L` and release all resources held by it. 51 | 52 | - `int line_length(line *L)` 53 | 54 | Return the length of line `L`. One pixel equals one unit, so this value gives both the size of the text geometry in 3D units and the horizontal resolution of the rendered text in pixels. 55 | 56 | - `void line_render(line *L)` 57 | 58 | Render line `L` using OpenGL. 59 | 60 | ## Basic Rendering 61 | 62 | To generate renderable text, one need only create a `font` structure and one or more `line` structures. Here we load a TrueType font of Times Italic at a size of 18, with a normal letter spacing. 63 | 64 | font *F = font_create("Timesi.TTF", 0, 18, 1.0); 65 | line *L = line_create("The quick brown fox jumps over the lazy dog.", F); 66 | 67 | To render properly, a few pieces of OpenGL state must be set. Text is rendered as textured rectangles, so texture mapping must be enabled. These rectangles may overlap one another, so the depth mask should be false (or the depth test disabled entirely) in order to ensure overlapped glyphs remain visible. Lighting may or may not be desirable for text and, if left enabled, text will respond normally to any light sources in the scene. 68 | 69 | glEnable(GL_TEXTURE_2D); 70 | glDepthMask(GL_FALSE); 71 | glDisable(GL_LIGHTING); 72 | 73 | To allow the scene to be visible through the text, glyphs are stored in the alpha channel of the texture image. So to appear correctly, they must be blended with the scene using the source alpha value. 74 | 75 | glEnable(GL_BLEND); 76 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 77 | 78 | With these set, applications need only set a material and the scale, and render. The text will appear in the scene like any other 3D object. 79 | 80 | glColor4d(0.0, 0.0, 0.0, 1.0); 81 | glScaled(0.01, 0.01, 0.01); 82 | line_render(Q); 83 | 84 | More commonly, text is rendered as a 2D overlay of the scene. To accomplish this, apply an orthogonal projection with the same extent as the window. This will map each unit in eye space to a single pixel in window space, and the text will appear precisely as FreeType rendered it. 85 | 86 | glMatrixMode(GL_PROJECTION); 87 | glLoadIdentity(); 88 | glOrtho(0, WIN_W, 0, WIN_H, 0, 1); 89 | glMatrixMode(GL_MODELVIEW); 90 | glLoadIdentity(); 91 | line_render(Q); 92 | 93 | Here is the output of these two examples. Note the sampling artifacts in the scene-embedded text. Higher quality may be achieved with a larger font size, though perfectly sampled text can never be achieved when rendering to a perspective projection. 94 | 95 | ![](img/quick.png) 96 | 97 | ## Layout 98 | 99 | This next example demonstrates the use of `line_layout` to typeset and justify a body of text and, in so doing, explains the use of that function's expansion argument. When presenting fully justified text, each line of input must have roughly the same number of characters. Each line of output will have very nearly the same number of horizontal pixels. To accomplish this, we must determine the exact number of pixels to be inserted. 100 | 101 | Assume that we have already divided our text into strings of roughly equal character count, possibly using an automatic mechanism such as Knuth's algorithm: 102 | 103 | const char *strv[] = { 104 | "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do", 105 | "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut", 106 | "enim ad minim veniam, quis nostrud exercitation ullamco laboris", 107 | "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in", 108 | "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla", 109 | "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in", 110 | "culpa qui of""\xEF\xAC\x81""cia deserunt mollit anim id est laborum." 111 | }; 112 | int strc = 7; 113 | 114 | Note the use of UTF8 in the last line to include the UNICODE "fi" ligature in "officia." 115 | 116 | To equalize the lengths of these strings, we begin by using `line_length` to measure the natural length of each. We record these lengths, and note the longest. Then we subtract the longest from each length, giving the amount of expansion that each requires. Traditionally, the last line is not fully justified, so its expansion is set to zero. 117 | 118 | for (i = 0; i < strc - 1; ++i) 119 | { 120 | expv[i] = line_length(strv[i], F); 121 | if (m < expv[i]) 122 | m = expv[i]; 123 | } 124 | 125 | for (i = 0; i < strc - 1; ++i) 126 | expv[i] = m - expv[i]; 127 | 128 | expv[strc - 1] = 0; 129 | 130 | L = line_layout(strc, strv, expv, NULL, F); 131 | 132 | The array of strings and array of expansion values is passed to `line_layout`. In this example, the transformation matrix is unsused and a NULL pointer is passed. In this case `line_layout` will set all lines in a column. 133 | 134 | The result is a single `line` structure that encapsulates several lines of text, Here is the output, rendered in Times Italic at 18: 135 | 136 | ![](img/lorem.png) 137 | 138 | As a peek behind the curtain, here is the texture atlas generated during the execution of this example. It's square, and only the top half is used. A little bit of texture memory goes a long way when rendering text. 139 | 140 | ![](img/atlas.png) 141 | 142 | The use of the transformation matrix parameter exposes the full generality of `line_layout`. In this image, nearly 9000 lunar feature labels were typeset and transformed to their correct locations with appropriate scaling. All 9000 labels were rendered using a single vertex array and only a tiny texture. 143 | 144 | ![](img/lunar.png) 145 | --------------------------------------------------------------------------------