commit f53a4f16ac181ce8a7f2ab532be501a94522dfea
parent 53502e5e5763822f98bed21ae35ef762d3d0346a
Author: Kyle Milz <kyle@getaddrinfo.net>
Date: Sat, 19 Mar 2016 16:59:03 -0600
viewer: port over glyphy demo code
Diffstat:
25 files changed, 3220 insertions(+), 56 deletions(-)
diff --git a/viewer/Makefile b/viewer/Makefile
@@ -1,5 +1,7 @@
PROG = scv_viewer
-SRCS = viewer.cxx text.cxx af_unix.cxx
+SRCS = viewer.cxx text.cxx af_unix.cxx demo-atlas.cxx demo-buffer.cxx
+SRCS += demo-font.cxx demo-glstate.cxx demo-shader.cxx demo-view.cxx trackball.c
+SRCS += matrix4x4.c
CXXFLAGS += -std=c++11
CXXFLAGS += `pkg-config gl glew ftgl --cflags`
diff --git a/viewer/af_unix.cxx b/viewer/af_unix.cxx
@@ -21,7 +21,7 @@ af_unix::af_unix(int f) :
void
af_unix::set_listen()
{
-#ifdef __APPLE__
+#if defined(__APPLE__)
// OS X socket() doesn't take SOCK_NONBLOCK
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
err(1, "socket");
@@ -29,7 +29,7 @@ af_unix::set_listen()
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
err(1, "fcntl(F_GETFL)");
- fcntl(fd, F_SETFL, flags & O_NONBLOCK);
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
if (flags < 0)
err(1, "fcntl(F_SETFL)");
#else
diff --git a/viewer/demo-atlas-glsl.h b/viewer/demo-atlas-glsl.h
@@ -0,0 +1,18 @@
+static const char *demo_atlas_glsl =
+"uniform sampler2D u_atlas_tex;\n"
+"uniform ivec4 u_atlas_info;\n"
+"\n"
+"#define GLYPHY_TEXTURE1D_EXTRA_DECLS , sampler2D _tex, ivec4 _atlas_info, ivec2 _atlas_pos\n"
+"#define GLYPHY_TEXTURE1D_EXTRA_ARGS , _tex, _atlas_info, _atlas_pos\n"
+"#define GLYPHY_DEMO_EXTRA_ARGS , u_atlas_tex, u_atlas_info, gi.atlas_pos\n"
+"\n"
+"vec4\n"
+"glyphy_texture1D_func (int offset GLYPHY_TEXTURE1D_EXTRA_DECLS)\n"
+"{\n"
+" ivec2 item_geom = _atlas_info.zw;\n"
+" vec2 pos = (vec2 (_atlas_pos.xy * item_geom +\n"
+" ivec2 (mod (float (offset), float (item_geom.x)), offset / item_geom.x)) +\n"
+" + vec2 (.5, .5)) / vec2(_atlas_info.xy);\n"
+" return texture2D (_tex, pos);\n"
+"}\n"
+;
diff --git a/viewer/demo-atlas.cxx b/viewer/demo-atlas.cxx
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "demo-atlas.h"
+
+
+struct demo_atlas_t {
+ unsigned int refcount;
+
+ GLuint tex_unit;
+ GLuint tex_name;
+ GLuint tex_w;
+ GLuint tex_h;
+ GLuint item_w;
+ GLuint item_h_q; /* height quantum */
+ GLuint cursor_x;
+ GLuint cursor_y;
+};
+
+
+demo_atlas_t *
+demo_atlas_create (unsigned int w,
+ unsigned int h,
+ unsigned int item_w,
+ unsigned int item_h_quantum)
+{
+ TRACE();
+
+ demo_atlas_t *at = (demo_atlas_t *) calloc (1, sizeof (demo_atlas_t));
+ at->refcount = 1;
+
+ glGetIntegerv (GL_ACTIVE_TEXTURE, (GLint *) &at->tex_unit);
+ glGenTextures (1, &at->tex_name);
+ at->tex_w = w;
+ at->tex_h = h;
+ at->item_w = item_w;
+ at->item_h_q = item_h_quantum;
+ at->cursor_x = 0;
+ at->cursor_y = 0;
+
+ demo_atlas_bind_texture (at);
+
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ gl(TexImage2D) (GL_TEXTURE_2D, 0, GL_RGBA, at->tex_w, at->tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ return at;
+}
+
+demo_atlas_t *
+demo_atlas_reference (demo_atlas_t *at)
+{
+ if (at) at->refcount++;
+ return at;
+}
+
+void
+demo_atlas_destroy (demo_atlas_t *at)
+{
+ if (!at || --at->refcount)
+ return;
+
+ glDeleteTextures (1, &at->tex_name);
+ free (at);
+}
+
+void
+demo_atlas_bind_texture (demo_atlas_t *at)
+{
+ glActiveTexture (at->tex_unit);
+ glBindTexture (GL_TEXTURE_2D, at->tex_name);
+}
+
+void
+demo_atlas_set_uniforms (demo_atlas_t *at)
+{
+ GLuint program;
+ glGetIntegerv (GL_CURRENT_PROGRAM, (GLint *) &program);
+
+ glUniform4i (glGetUniformLocation (program, "u_atlas_info"),
+ at->tex_w, at->tex_h, at->item_w, at->item_h_q);
+ glUniform1i (glGetUniformLocation (program, "u_atlas_tex"), at->tex_unit - GL_TEXTURE0);
+}
+
+void
+demo_atlas_alloc (demo_atlas_t *at,
+ glyphy_rgba_t *data,
+ unsigned int len,
+ unsigned int *px,
+ unsigned int *py)
+{
+ GLuint w, h, x, y;
+
+ w = at->item_w;
+ h = (len + w - 1) / w;
+
+ if (at->cursor_y + h > at->tex_h) {
+ /* Go to next column */
+ at->cursor_x += at->item_w;
+ at->cursor_y = 0;
+ }
+
+ if (at->cursor_x + w <= at->tex_w &&
+ at->cursor_y + h <= at->tex_h)
+ {
+ x = at->cursor_x;
+ y = at->cursor_y;
+ at->cursor_y += (h + at->item_h_q - 1) & ~(at->item_h_q - 1);
+ } else
+ die ("Ran out of atlas memory");
+
+ demo_atlas_bind_texture (at);
+ if (w * h == len)
+ gl(TexSubImage2D) (GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ else {
+ gl(TexSubImage2D) (GL_TEXTURE_2D, 0, x, y, w, h - 1, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ /* Upload the last row separately */
+ gl(TexSubImage2D) (GL_TEXTURE_2D, 0, x, y + h - 1, len - (w * (h - 1)), 1, GL_RGBA, GL_UNSIGNED_BYTE,
+ data + w * (h - 1));
+ }
+
+ *px = x / at->item_w;
+ *py = y / at->item_h_q;
+}
diff --git a/viewer/demo-atlas.h b/viewer/demo-atlas.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod, Maysum Panju
+ */
+
+#ifndef DEMO_ATLAS_H
+#define DEMO_ATLAS_H
+
+#include "demo-common.h"
+
+
+typedef struct demo_atlas_t demo_atlas_t;
+
+demo_atlas_t *
+demo_atlas_create (unsigned int w,
+ unsigned int h,
+ unsigned int item_w,
+ unsigned int item_h_quantum);
+
+demo_atlas_t *
+demo_atlas_reference (demo_atlas_t *at);
+
+void
+demo_atlas_destroy (demo_atlas_t *at);
+
+
+void
+demo_atlas_alloc (demo_atlas_t *at,
+ glyphy_rgba_t *data,
+ unsigned int len,
+ unsigned int *px,
+ unsigned int *py);
+
+void
+demo_atlas_bind_texture (demo_atlas_t *at);
+
+void
+demo_atlas_set_uniforms (demo_atlas_t *at);
+
+
+#endif /* DEMO_ATLAS_H */
diff --git a/viewer/demo-buffer.cxx b/viewer/demo-buffer.cxx
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "demo-buffer.h"
+
+struct demo_buffer_t {
+ unsigned int refcount;
+
+ glyphy_point_t cursor;
+ std::vector<glyph_vertex_t> *vertices;
+ glyphy_extents_t ink_extents;
+ glyphy_extents_t logical_extents;
+ bool dirty;
+ GLuint buf_name;
+};
+
+demo_buffer_t *
+demo_buffer_create (void)
+{
+ demo_buffer_t *buffer = (demo_buffer_t *) calloc (1, sizeof (demo_buffer_t));
+ buffer->refcount = 1;
+
+ buffer->vertices = new std::vector<glyph_vertex_t>;
+ glGenBuffers (1, &buffer->buf_name);
+
+ demo_buffer_clear (buffer);
+
+ return buffer;
+}
+
+demo_buffer_t *
+demo_buffer_reference (demo_buffer_t *buffer)
+{
+ if (buffer) buffer->refcount++;
+ return buffer;
+}
+
+void
+demo_buffer_destroy (demo_buffer_t *buffer)
+{
+ if (!buffer || --buffer->refcount)
+ return;
+
+ glDeleteBuffers (1, &buffer->buf_name);
+ delete buffer->vertices;
+ free (buffer);
+}
+
+
+void
+demo_buffer_clear (demo_buffer_t *buffer)
+{
+ buffer->vertices->clear ();
+ glyphy_extents_clear (&buffer->ink_extents);
+ glyphy_extents_clear (&buffer->logical_extents);
+ buffer->dirty = true;
+}
+
+void
+demo_buffer_extents (demo_buffer_t *buffer,
+ glyphy_extents_t *ink_extents,
+ glyphy_extents_t *logical_extents)
+{
+ if (ink_extents)
+ *ink_extents = buffer->ink_extents;
+ if (logical_extents)
+ *logical_extents = buffer->logical_extents;
+}
+
+void
+demo_buffer_move_to (demo_buffer_t *buffer,
+ const glyphy_point_t *p)
+{
+ buffer->cursor = *p;
+}
+
+void
+demo_buffer_current_point (demo_buffer_t *buffer,
+ glyphy_point_t *p)
+{
+ *p = buffer->cursor;
+}
+
+void
+demo_buffer_add_text (demo_buffer_t *buffer,
+ const char *utf8,
+ demo_font_t *font,
+ double font_size)
+{
+ FT_Face face = demo_font_get_face (font);
+ glyphy_point_t top_left = buffer->cursor;
+ buffer->cursor.y += font_size /* * font->ascent */;
+ unsigned int unicode;
+ for (const unsigned char *p = (const unsigned char *) utf8; *p; p++) {
+ if (*p < 128) {
+ unicode = *p;
+ } else {
+ unsigned int j;
+ if (*p < 0xE0) {
+ unicode = *p & ~0xE0;
+ j = 1;
+ } else if (*p < 0xF0) {
+ unicode = *p & ~0xF0;
+ j = 2;
+ } else {
+ unicode = *p & ~0xF8;
+ j = 3;
+ continue;
+ }
+ p++;
+ for (; j && *p; j--, p++)
+ unicode = (unicode << 6) | (*p & ~0xC0);
+ p--;
+ }
+
+ if (unicode == '\n') {
+ buffer->cursor.y += font_size;
+ buffer->cursor.x = top_left.x;
+ continue;
+ }
+
+ unsigned int glyph_index = FT_Get_Char_Index (face, unicode);
+ glyph_info_t gi;
+ demo_font_lookup_glyph (font, glyph_index, &gi);
+
+ /* Update ink extents */
+ glyphy_extents_t ink_extents;
+ demo_shader_add_glyph_vertices (buffer->cursor, font_size, &gi, buffer->vertices, &ink_extents);
+ glyphy_extents_extend (&buffer->ink_extents, &ink_extents);
+
+ /* Update logical extents */
+ glyphy_point_t corner;
+ corner.x = buffer->cursor.x;
+ corner.y = buffer->cursor.y - font_size;
+ glyphy_extents_add (&buffer->logical_extents, &corner);
+ corner.x = buffer->cursor.x + font_size * gi.advance;
+ corner.y = buffer->cursor.y;
+ glyphy_extents_add (&buffer->logical_extents, &corner);
+
+ buffer->cursor.x += font_size * gi.advance;
+ }
+
+ buffer->dirty = true;
+}
+
+void
+demo_buffer_draw (demo_buffer_t *buffer)
+{
+ GLint program;
+ glGetIntegerv (GL_CURRENT_PROGRAM, &program);
+ GLuint a_glyph_vertex_loc = glGetAttribLocation (program, "a_glyph_vertex");
+ glBindBuffer (GL_ARRAY_BUFFER, buffer->buf_name);
+ if (buffer->dirty) {
+ glBufferData (GL_ARRAY_BUFFER, sizeof (glyph_vertex_t) * buffer->vertices->size (), (const char *) &(*buffer->vertices)[0], GL_STATIC_DRAW);
+ buffer->dirty = false;
+ }
+ glEnableVertexAttribArray (a_glyph_vertex_loc);
+ glVertexAttribPointer (a_glyph_vertex_loc, 4, GL_FLOAT, GL_FALSE, sizeof (glyph_vertex_t), 0);
+ glDrawArrays (GL_TRIANGLES, 0, buffer->vertices->size ());
+ glDisableVertexAttribArray (a_glyph_vertex_loc);
+}
diff --git a/viewer/demo-buffer.h b/viewer/demo-buffer.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef DEMO_BUFFER_H
+#define DEMO_BUFFER_H
+
+#include "demo-common.h"
+#include "demo-font.h"
+#include "demo-shader.h"
+
+typedef struct demo_buffer_t demo_buffer_t;
+
+demo_buffer_t *
+demo_buffer_create (void);
+
+demo_buffer_t *
+demo_buffer_reference (demo_buffer_t *buffer);
+
+void
+demo_buffer_destroy (demo_buffer_t *buffer);
+
+
+void
+demo_buffer_clear (demo_buffer_t *buffer);
+
+void
+demo_buffer_extents (demo_buffer_t *buffer,
+ glyphy_extents_t *ink_extents,
+ glyphy_extents_t *logical_extents);
+
+void
+demo_buffer_move_to (demo_buffer_t *buffer,
+ const glyphy_point_t *p);
+
+void
+demo_buffer_current_point (demo_buffer_t *buffer,
+ glyphy_point_t *p);
+
+void
+demo_buffer_add_text (demo_buffer_t *buffer,
+ const char *utf8,
+ demo_font_t *font,
+ double font_size);
+
+void
+demo_buffer_draw (demo_buffer_t *buffer);
+
+
+#endif /* DEMO_BUFFER_H */
diff --git a/viewer/demo-common.h b/viewer/demo-common.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod, Maysum Panju
+ */
+
+#ifndef DEMO_COMMON_H
+#define DEMO_COMMON_H
+
+#include "../glyphy/glyphy.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+
+#include <algorithm>
+#include <vector>
+
+#include <GL/glew.h>
+#include <GLES2/gl2.h>
+#include <OpenGL/OpenGL.h>
+
+#if defined(__APPLE__)
+# include <GLUT/glut.h>
+#else
+# include <GL/glut.h>
+#endif
+
+
+#define STRINGIZE1(Src) #Src
+#define STRINGIZE(Src) STRINGIZE1(Src)
+
+#define ARRAY_LEN(Array) (sizeof (Array) / sizeof (*Array))
+
+
+#define MIN_FONT_SIZE 10
+#define TOLERANCE (1./2048)
+
+#define LOGI(...) ((void) fprintf (stdout, __VA_ARGS__))
+#define LOGW(...) ((void) fprintf (stderr, __VA_ARGS__))
+#define LOGE(...) ((void) fprintf (stderr, __VA_ARGS__), abort ())
+
+#define gl(name) \
+ for (GLint __ee, __ii = 0; \
+ __ii < 1; \
+ (__ii++, \
+ (__ee = glGetError()) && \
+ (fprintf (stderr, "gl" #name " failed with error %04X on line %d\n", __ee, __LINE__), abort (), 0))) \
+ gl##name
+
+
+static inline void
+die (const char *msg)
+{
+ fprintf (stderr, "%s\n", msg);
+ exit (1);
+}
+
+template <typename T>
+T clamp (T v, T m, T M)
+{
+ return v < m ? m : v > M ? M : v;
+}
+
+#define DEMO_FUNC __func__
+
+struct auto_trace_t
+{
+ auto_trace_t (const char *func_) : func (func_)
+ { printf ("Enter: %s\n", func); }
+
+ ~auto_trace_t (void)
+ { printf ("Leave: %s\n", func); }
+
+ private:
+ const char * const func;
+};
+
+#define TRACE() auto_trace_t trace(DEMO_FUNC)
+
+#endif /* DEMO_COMMON_H */
diff --git a/viewer/demo-font.cxx b/viewer/demo-font.cxx
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "demo-font.h"
+
+#include "../glyphy/glyphy-freetype.h"
+
+#include <ext/hash_map>
+
+using namespace __gnu_cxx; /* This is ridiculous */
+
+
+typedef hash_map<unsigned int, glyph_info_t> glyph_cache_t;
+
+struct demo_font_t {
+ unsigned int refcount;
+
+ FT_Face face;
+ glyph_cache_t *glyph_cache;
+ demo_atlas_t *atlas;
+ glyphy_arc_accumulator_t *acc;
+
+ /* stats */
+ unsigned int num_glyphs;
+ double sum_error;
+ unsigned int sum_endpoints;
+ double sum_fetch;
+ unsigned int sum_bytes;
+};
+
+demo_font_t *
+demo_font_create (FT_Face face,
+ demo_atlas_t *atlas)
+{
+ demo_font_t *font = (demo_font_t *) calloc (1, sizeof (demo_font_t));
+ font->refcount = 1;
+
+ font->face = face;
+ font->glyph_cache = new glyph_cache_t ();
+ font->atlas = demo_atlas_reference (atlas);
+ font->acc = glyphy_arc_accumulator_create ();
+
+ font->num_glyphs = 0;
+ font->sum_error = 0;
+ font->sum_endpoints = 0;
+ font->sum_fetch = 0;
+ font->sum_bytes = 0;
+
+ return font;
+}
+
+demo_font_t *
+demo_font_reference (demo_font_t *font)
+{
+ if (font) font->refcount++;
+ return font;
+}
+
+void
+demo_font_destroy (demo_font_t *font)
+{
+ if (!font || --font->refcount)
+ return;
+
+ glyphy_arc_accumulator_destroy (font->acc);
+ demo_atlas_destroy (font->atlas);
+ delete font->glyph_cache;
+ free (font);
+}
+
+
+FT_Face
+demo_font_get_face (demo_font_t *font)
+{
+ return font->face;
+}
+
+demo_atlas_t *
+demo_font_get_atlas (demo_font_t *font)
+{
+ return font->atlas;
+}
+
+
+static glyphy_bool_t
+accumulate_endpoint (glyphy_arc_endpoint_t *endpoint,
+ vector<glyphy_arc_endpoint_t> *endpoints)
+{
+ endpoints->push_back (*endpoint);
+ return true;
+}
+
+static void
+encode_ft_glyph (demo_font_t *font,
+ unsigned int glyph_index,
+ double tolerance_per_em,
+ glyphy_rgba_t *buffer,
+ unsigned int buffer_len,
+ unsigned int *output_len,
+ unsigned int *nominal_width,
+ unsigned int *nominal_height,
+ glyphy_extents_t *extents,
+ double *advance)
+{
+/* Used for testing only */
+#define SCALE (1. * (1 << 0))
+
+ FT_Face face = font->face;
+ if (FT_Err_Ok != FT_Load_Glyph (face,
+ glyph_index,
+ FT_LOAD_NO_BITMAP |
+ FT_LOAD_NO_HINTING |
+ FT_LOAD_NO_AUTOHINT |
+ FT_LOAD_NO_SCALE |
+ FT_LOAD_LINEAR_DESIGN |
+ FT_LOAD_IGNORE_TRANSFORM))
+ die ("Failed loading FreeType glyph");
+
+ if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
+ die ("FreeType loaded glyph format is not outline");
+
+ unsigned int upem = face->units_per_EM;
+ double tolerance = upem * tolerance_per_em; /* in font design units */
+ double faraway = double (upem) / (MIN_FONT_SIZE * M_SQRT2);
+ vector<glyphy_arc_endpoint_t> endpoints;
+
+ glyphy_arc_accumulator_reset (font->acc);
+ glyphy_arc_accumulator_set_tolerance (font->acc, tolerance);
+ glyphy_arc_accumulator_set_callback (font->acc,
+ (glyphy_arc_endpoint_accumulator_callback_t) accumulate_endpoint,
+ &endpoints);
+
+ if (FT_Err_Ok != glyphy_freetype(outline_decompose) (&face->glyph->outline, font->acc))
+ die ("Failed converting glyph outline to arcs");
+
+ assert (glyphy_arc_accumulator_get_error (font->acc) <= tolerance);
+
+ if (endpoints.size ())
+ {
+#if 0
+ /* Technically speaking, we want the following code,
+ * however, crappy fonts have crappy flags. So we just
+ * fixup unconditionally... */
+ if (face->glyph->outline.flags & FT_OUTLINE_EVEN_ODD_FILL)
+ glyphy_outline_winding_from_even_odd (&endpoints[0], endpoints.size (), false);
+ else if (face->glyph->outline.flags & FT_OUTLINE_REVERSE_FILL)
+ glyphy_outline_reverse (&endpoints[0], endpoints.size ());
+#else
+ glyphy_outline_winding_from_even_odd (&endpoints[0], endpoints.size (), false);
+#endif
+ }
+
+ if (SCALE != 1.)
+ for (unsigned int i = 0; i < endpoints.size (); i++)
+ {
+ endpoints[i].p.x /= SCALE;
+ endpoints[i].p.y /= SCALE;
+ }
+
+ double avg_fetch_achieved;
+ if (!glyphy_arc_list_encode_blob (endpoints.size () ? &endpoints[0] : NULL, endpoints.size (),
+ buffer,
+ buffer_len,
+ faraway / SCALE,
+ 4, /* UNUSED */
+ &avg_fetch_achieved,
+ output_len,
+ nominal_width,
+ nominal_height,
+ extents))
+ die ("Failed encoding arcs");
+
+ glyphy_extents_scale (extents, 1. / upem, 1. / upem);
+ glyphy_extents_scale (extents, SCALE, SCALE);
+
+ *advance = face->glyph->metrics.horiAdvance / (double) upem;
+
+ if (0)
+ LOGI ("gid%3u: endpoints%3d; err%3g%%; tex fetch%4.1f; mem%4.1fkb\n",
+ glyph_index,
+ (unsigned int) glyphy_arc_accumulator_get_num_endpoints (font->acc),
+ round (100 * glyphy_arc_accumulator_get_error (font->acc) / tolerance),
+ avg_fetch_achieved,
+ (*output_len * sizeof (glyphy_rgba_t)) / 1024.);
+
+ font->num_glyphs++;
+ font->sum_error += glyphy_arc_accumulator_get_error (font->acc) / tolerance;
+ font->sum_endpoints += glyphy_arc_accumulator_get_num_endpoints (font->acc);
+ font->sum_fetch += avg_fetch_achieved;
+ font->sum_bytes += (*output_len * sizeof (glyphy_rgba_t));
+}
+
+static void
+_demo_font_upload_glyph (demo_font_t *font,
+ unsigned int glyph_index,
+ glyph_info_t *glyph_info)
+{
+ glyphy_rgba_t buffer[4096 * 16];
+ unsigned int output_len;
+
+ encode_ft_glyph (font,
+ glyph_index,
+ TOLERANCE,
+ buffer, ARRAY_LEN (buffer),
+ &output_len,
+ &glyph_info->nominal_w,
+ &glyph_info->nominal_h,
+ &glyph_info->extents,
+ &glyph_info->advance);
+
+ glyph_info->is_empty = glyphy_extents_is_empty (&glyph_info->extents);
+ if (!glyph_info->is_empty)
+ demo_atlas_alloc (font->atlas, buffer, output_len,
+ &glyph_info->atlas_x, &glyph_info->atlas_y);
+}
+
+void
+demo_font_lookup_glyph (demo_font_t *font,
+ unsigned int glyph_index,
+ glyph_info_t *glyph_info)
+{
+ if (font->glyph_cache->find (glyph_index) == font->glyph_cache->end ()) {
+ _demo_font_upload_glyph (font, glyph_index, glyph_info);
+ (*font->glyph_cache)[glyph_index] = *glyph_info;
+ } else
+ *glyph_info = (*font->glyph_cache)[glyph_index];
+}
+
+void
+demo_font_print_stats (demo_font_t *font)
+{
+ LOGI ("%3d glyphs; avg num endpoints%6.2f; avg error%5.1f%%; avg tex fetch%5.2f; avg %5.2fkb per glyph\n",
+ font->num_glyphs,
+ (double) font->sum_endpoints / font->num_glyphs,
+ 100. * font->sum_error / font->num_glyphs,
+ font->sum_fetch / font->num_glyphs,
+ font->sum_bytes / 1024. / font->num_glyphs);
+}
diff --git a/viewer/demo-font.h b/viewer/demo-font.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef DEMO_FONT_H
+#define DEMO_FONT_H
+
+#include "demo-common.h"
+#include "demo-atlas.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+
+typedef struct {
+ glyphy_extents_t extents;
+ double advance;
+ glyphy_bool_t is_empty; /* has no outline; eg. space; don't draw it */
+ unsigned int nominal_w;
+ unsigned int nominal_h;
+ unsigned int atlas_x;
+ unsigned int atlas_y;
+} glyph_info_t;
+
+
+typedef struct demo_font_t demo_font_t;
+
+demo_font_t *
+demo_font_create (FT_Face face,
+ demo_atlas_t *atlas);
+
+demo_font_t *
+demo_font_reference (demo_font_t *font);
+
+void
+demo_font_destroy (demo_font_t *font);
+
+
+FT_Face
+demo_font_get_face (demo_font_t *font);
+
+demo_atlas_t *
+demo_font_get_atlas (demo_font_t *font);
+
+
+void
+demo_font_lookup_glyph (demo_font_t *font,
+ unsigned int glyph_index,
+ glyph_info_t *glyph_info);
+
+void
+demo_font_print_stats (demo_font_t *font);
+
+
+#endif /* DEMO_FONT_H */
diff --git a/viewer/demo-fshader-glsl.h b/viewer/demo-fshader-glsl.h
@@ -0,0 +1,87 @@
+static const char *demo_fshader_glsl =
+"uniform float u_contrast;\n"
+"uniform float u_gamma_adjust;\n"
+"uniform float u_outline_thickness;\n"
+"uniform bool u_outline;\n"
+"uniform float u_boldness;\n"
+"uniform bool u_debug;\n"
+"\n"
+"varying vec4 v_glyph;\n"
+"\n"
+"\n"
+"#define SQRT2_2 0.70710678118654757 /* 1 / sqrt(2.) */\n"
+"#define SQRT2 1.4142135623730951\n"
+"\n"
+"struct glyph_info_t {\n"
+" ivec2 nominal_size;\n"
+" ivec2 atlas_pos;\n"
+"};\n"
+"\n"
+"glyph_info_t\n"
+"glyph_info_decode (vec4 v)\n"
+"{\n"
+" glyph_info_t gi;\n"
+" gi.nominal_size = (ivec2 (mod (v.zw, 256.)) + 2) / 4;\n"
+" gi.atlas_pos = ivec2 (v_glyph.zw) / 256;\n"
+" return gi;\n"
+"}\n"
+"\n"
+"\n"
+"float\n"
+"antialias (float d)\n"
+"{\n"
+" return smoothstep (-.75, +.75, d);\n"
+"}\n"
+"\n"
+"void\n"
+"main()\n"
+"{\n"
+" vec2 p = v_glyph.xy;\n"
+" glyph_info_t gi = glyph_info_decode (v_glyph);\n"
+"\n"
+" /* isotropic antialiasing */\n"
+" vec2 dpdx = dFdx (p);\n"
+" vec2 dpdy = dFdy (p);\n"
+" float m = length (vec2 (length (dpdx), length (dpdy))) * SQRT2_2;\n"
+"\n"
+" vec4 color = vec4 (0,0,0,1);\n"
+"\n"
+" float gsdist = glyphy_sdf (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS);\n"
+" float sdist = gsdist / m * u_contrast;\n"
+"\n"
+" if (!u_debug) {\n"
+" sdist -= u_boldness * 10.;\n"
+" if (u_outline)\n"
+" sdist = abs (sdist) - u_outline_thickness * .5;\n"
+" if (sdist > 1.)\n"
+" discard;\n"
+" float alpha = antialias (-sdist);\n"
+" if (u_gamma_adjust != 1.)\n"
+" alpha = pow (alpha, 1./u_gamma_adjust);\n"
+" color = vec4 (color.rgb,color.a * alpha);\n"
+" } else {\n"
+" color = vec4 (0,0,0,0);\n"
+"\n"
+" // Color the inside of the glyph a light red\n"
+" color += vec4 (.5,0,0,.5) * smoothstep (1., -1., sdist);\n"
+"\n"
+" float udist = abs (sdist);\n"
+" float gudist = abs (gsdist);\n"
+" // Color the outline red\n"
+" color += vec4 (1,0,0,1) * smoothstep (2., 1., udist);\n"
+" // Color the distance field in green\n"
+" if (!glyphy_isinf (udist))\n"
+" color += vec4(0,.4,0,.4 - (abs(gsdist) / max(float(gi.nominal_size.x), float(gi.nominal_size.y))) * 4.);\n"
+"\n"
+" float pdist = glyphy_point_dist (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS);\n"
+" // Color points green\n"
+" color = mix (vec4 (0,1,0,.5), color, smoothstep (.05, .06, pdist));\n"
+"\n"
+" glyphy_arc_list_t arc_list = glyphy_arc_list (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS);\n"
+" // Color the number of endpoints per cell blue\n"
+" color += vec4 (0,0,1,.1) * float(arc_list.num_endpoints) * 32./255.;\n"
+" }\n"
+"\n"
+" gl_FragColor = color;\n"
+"}\n"
+;
diff --git a/viewer/demo-glstate.cxx b/viewer/demo-glstate.cxx
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "demo-glstate.h"
+
+struct demo_glstate_t {
+ unsigned int refcount;
+
+ GLuint program;
+ demo_atlas_t *atlas;
+
+ /* Uniforms */
+ double u_debug;
+ double u_contrast;
+ double u_gamma_adjust;
+ double u_outline;
+ double u_outline_thickness;
+ double u_boldness;
+};
+
+demo_glstate_t *
+demo_glstate_create (void)
+{
+ TRACE();
+
+ demo_glstate_t *st = (demo_glstate_t *) calloc (1, sizeof (demo_glstate_t));
+ st->refcount = 1;
+
+ st->program = demo_shader_create_program ();
+ st->atlas = demo_atlas_create (2048, 1024, 64, 8);
+
+ st->u_debug = false;
+ st->u_contrast = 1.0;
+ st->u_gamma_adjust = 1.0;
+ st->u_outline = false;
+ st->u_outline_thickness = 1.0;
+ st->u_boldness = 0.;
+
+ return st;
+}
+
+demo_glstate_t *
+demo_glstate_reference (demo_glstate_t *st)
+{
+ if (st) st->refcount++;
+ return st;
+}
+
+void
+demo_glstate_destroy (demo_glstate_t *st)
+{
+ if (!st || --st->refcount)
+ return;
+
+ demo_atlas_destroy (st->atlas);
+ glDeleteProgram (st->program);
+
+ free (st);
+}
+
+
+static void
+set_uniform (GLuint program, const char *name, double *p, double value)
+{
+ *p = value;
+ glUniform1f (glGetUniformLocation (program, name), value);
+ LOGI ("Setting %s to %g\n", name + 2, value);
+}
+
+#define SET_UNIFORM(name, value) set_uniform (st->program, #name, &st->name, value)
+
+void
+demo_glstate_setup (demo_glstate_t *st)
+{
+ glUseProgram (st->program);
+
+ demo_atlas_set_uniforms (st->atlas);
+
+ SET_UNIFORM (u_debug, st->u_debug);
+ SET_UNIFORM (u_contrast, st->u_contrast);
+ SET_UNIFORM (u_gamma_adjust, st->u_gamma_adjust);
+ SET_UNIFORM (u_outline, st->u_outline);
+ SET_UNIFORM (u_outline_thickness, st->u_outline_thickness);
+ SET_UNIFORM (u_boldness, st->u_boldness);
+
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+demo_atlas_t *
+demo_glstate_get_atlas (demo_glstate_t *st)
+{
+ return st->atlas;
+}
+
+void
+demo_glstate_scale_gamma_adjust (demo_glstate_t *st, double factor)
+{
+ SET_UNIFORM (u_gamma_adjust, clamp (st->u_gamma_adjust * factor, .1, 10.));
+}
+
+void
+demo_glstate_scale_contrast (demo_glstate_t *st, double factor)
+{
+ SET_UNIFORM (u_contrast, clamp (st->u_contrast * factor, .1, 10.));
+}
+
+void
+demo_glstate_toggle_debug (demo_glstate_t *st)
+{
+ SET_UNIFORM (u_debug, 1 - st->u_debug);
+}
+
+void
+demo_glstate_set_matrix (demo_glstate_t *st, float mat[16])
+{
+ glUniformMatrix4fv (glGetUniformLocation (st->program, "u_matViewProjection"), 1, GL_FALSE, mat);
+}
+
+void
+demo_glstate_toggle_outline (demo_glstate_t *st)
+{
+ SET_UNIFORM (u_outline, 1 - st->u_outline);
+}
+
+void
+demo_glstate_scale_outline_thickness (demo_glstate_t *st, double factor)
+{
+ SET_UNIFORM (u_outline_thickness, clamp (st->u_outline_thickness * factor, .5, 3.));
+}
+
+void
+demo_glstate_adjust_boldness (demo_glstate_t *st, double adjustment)
+{
+ SET_UNIFORM (u_boldness, clamp (st->u_boldness + adjustment, -.2, .7));
+}
diff --git a/viewer/demo-glstate.h b/viewer/demo-glstate.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef DEMO_GLSTATE_H
+#define DEMO_GLSTATE_H
+
+#include "demo-common.h"
+#include "demo-buffer.h"
+
+#include "demo-atlas.h"
+#include "demo-shader.h"
+
+typedef struct demo_glstate_t demo_glstate_t;
+
+demo_glstate_t *
+demo_glstate_create (void);
+
+demo_glstate_t *
+demo_glstate_reference (demo_glstate_t *st);
+
+void
+demo_glstate_destroy (demo_glstate_t *st);
+
+
+void
+demo_glstate_setup (demo_glstate_t *st);
+
+demo_atlas_t *
+demo_glstate_get_atlas (demo_glstate_t *st);
+
+void
+demo_glstate_scale_gamma_adjust (demo_glstate_t *st, double factor);
+
+void
+demo_glstate_scale_contrast (demo_glstate_t *st, double factor);
+
+void
+demo_glstate_toggle_debug (demo_glstate_t *st);
+
+void
+demo_glstate_set_matrix (demo_glstate_t *st, float mat[16]);
+
+void
+demo_glstate_toggle_outline (demo_glstate_t *st);
+
+void
+demo_glstate_scale_outline_thickness (demo_glstate_t *st, double factor);
+
+void
+demo_glstate_adjust_boldness (demo_glstate_t *st, double adjustment);
+
+
+#endif /* DEMO_GLSTATE_H */
diff --git a/viewer/demo-shader.cxx b/viewer/demo-shader.cxx
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "demo-shader.h"
+
+#include "demo-atlas-glsl.h"
+#include "demo-vshader-glsl.h"
+#include "demo-fshader-glsl.h"
+
+
+static unsigned int
+glyph_encode (unsigned int atlas_x , /* 7 bits */
+ unsigned int atlas_y, /* 7 bits */
+ unsigned int corner_x, /* 1 bit */
+ unsigned int corner_y, /* 1 bit */
+ unsigned int nominal_w, /* 6 bits */
+ unsigned int nominal_h /* 6 bits */)
+{
+ assert (0 == (atlas_x & ~0x7F));
+ assert (0 == (atlas_y & ~0x7F));
+ assert (0 == (corner_x & ~1));
+ assert (0 == (corner_y & ~1));
+ assert (0 == (nominal_w & ~0x3F));
+ assert (0 == (nominal_h & ~0x3F));
+
+ unsigned int x = (((atlas_x << 6) | nominal_w) << 1) | corner_x;
+ unsigned int y = (((atlas_y << 6) | nominal_h) << 1) | corner_y;
+
+ return (x << 16) | y;
+}
+
+static void
+glyph_vertex_encode (double x, double y,
+ unsigned int corner_x, unsigned int corner_y,
+ const glyph_info_t *gi,
+ glyph_vertex_t *v)
+{
+ unsigned int encoded = glyph_encode (gi->atlas_x, gi->atlas_y,
+ corner_x, corner_y,
+ gi->nominal_w, gi->nominal_h);
+ v->x = x;
+ v->y = y;
+ v->g16hi = encoded >> 16;
+ v->g16lo = encoded & 0xFFFF;
+}
+
+void
+demo_shader_add_glyph_vertices (const glyphy_point_t &p,
+ double font_size,
+ glyph_info_t *gi,
+ std::vector<glyph_vertex_t> *vertices,
+ glyphy_extents_t *extents)
+{
+ if (gi->is_empty)
+ return;
+
+ glyph_vertex_t v[4];
+
+#define ENCODE_CORNER(_cx, _cy) \
+ do { \
+ double _vx = p.x + font_size * ((1-_cx) * gi->extents.min_x + _cx * gi->extents.max_x); \
+ double _vy = p.y - font_size * ((1-_cy) * gi->extents.min_y + _cy * gi->extents.max_y); \
+ glyph_vertex_encode (_vx, _vy, _cx, _cy, gi, &v[_cx * 2 + _cy]); \
+ } while (0)
+ ENCODE_CORNER (0, 0);
+ ENCODE_CORNER (0, 1);
+ ENCODE_CORNER (1, 0);
+ ENCODE_CORNER (1, 1);
+#undef ENCODE_CORNER
+
+ vertices->push_back (v[0]);
+ vertices->push_back (v[1]);
+ vertices->push_back (v[2]);
+
+ vertices->push_back (v[1]);
+ vertices->push_back (v[2]);
+ vertices->push_back (v[3]);
+
+ if (extents) {
+ glyphy_extents_clear (extents);
+ for (unsigned int i = 0; i < 4; i++) {
+ glyphy_point_t p = {v[i].x, v[i].y};
+ glyphy_extents_add (extents, &p);
+ }
+ }
+}
+
+
+
+
+static GLuint
+compile_shader (GLenum type,
+ GLsizei count,
+ const GLchar** sources)
+{
+ TRACE();
+
+ GLuint shader;
+ GLint compiled;
+
+ if (!(shader = glCreateShader (type)))
+ return shader;
+
+ glShaderSource (shader, count, sources, 0);
+ glCompileShader (shader);
+
+ glGetShaderiv (shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint info_len = 0;
+ LOGW ("%s shader failed to compile\n",
+ type == GL_VERTEX_SHADER ? "Vertex" : "Fragment");
+ glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &info_len);
+
+ if (info_len > 0) {
+ char *info_log = (char*) malloc (info_len);
+ glGetShaderInfoLog (shader, info_len, NULL, info_log);
+
+ LOGW ("%s\n", info_log);
+ free (info_log);
+ }
+
+ abort ();
+ }
+
+ return shader;
+}
+
+static GLuint
+link_program (GLuint vshader,
+ GLuint fshader)
+{
+ TRACE();
+
+ GLuint program;
+ GLint linked;
+
+ program = glCreateProgram ();
+ glAttachShader (program, vshader);
+ glAttachShader (program, fshader);
+ glLinkProgram (program);
+ glDeleteShader (vshader);
+ glDeleteShader (fshader);
+
+ glGetProgramiv (program, GL_LINK_STATUS, &linked);
+ if (!linked) {
+ GLint info_len = 0;
+ LOGW ("Program failed to link\n");
+ glGetProgramiv (program, GL_INFO_LOG_LENGTH, &info_len);
+
+ if (info_len > 0) {
+ char *info_log = (char*) malloc (info_len);
+ glGetProgramInfoLog (program, info_len, NULL, info_log);
+
+ LOGW ("%s\n", info_log);
+ free (info_log);
+ }
+
+ abort ();
+ }
+
+ return program;
+}
+
+#ifdef GL_ES_VERSION_2_0
+# define GLSL_HEADER_STRING \
+ "#extension GL_OES_standard_derivatives : enable\n" \
+ "precision highp float;\n" \
+ "precision highp int;\n"
+#else
+# define GLSL_HEADER_STRING \
+ "#version 110\n"
+#endif
+
+GLuint
+demo_shader_create_program (void)
+{
+ TRACE();
+
+ GLuint vshader, fshader, program;
+ const GLchar *vshader_sources[] = {GLSL_HEADER_STRING,
+ demo_vshader_glsl};
+ vshader = compile_shader (GL_VERTEX_SHADER, ARRAY_LEN (vshader_sources), vshader_sources);
+ const GLchar *fshader_sources[] = {GLSL_HEADER_STRING,
+ demo_atlas_glsl,
+ glyphy_common_shader_source (),
+ "#define GLYPHY_SDF_PSEUDO_DISTANCE 1\n",
+ glyphy_sdf_shader_source (),
+ demo_fshader_glsl};
+ fshader = compile_shader (GL_FRAGMENT_SHADER, ARRAY_LEN (fshader_sources), fshader_sources);
+
+ program = link_program (vshader, fshader);
+ return program;
+}
diff --git a/viewer/demo-shader.h b/viewer/demo-shader.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef DEMO_SHADERS_H
+#define DEMO_SHADERS_H
+
+#include "demo-common.h"
+#include "demo-font.h"
+
+
+struct glyph_vertex_t {
+ /* Position */
+ GLfloat x;
+ GLfloat y;
+ /* Glyph info */
+ GLfloat g16hi;
+ GLfloat g16lo;
+};
+
+void
+demo_shader_add_glyph_vertices (const glyphy_point_t &p,
+ double font_size,
+ glyph_info_t *gi,
+ std::vector<glyph_vertex_t> *vertices,
+ glyphy_extents_t *extents);
+
+
+GLuint
+demo_shader_create_program (void);
+
+
+#endif /* DEMO_SHADERS_H */
diff --git a/viewer/demo-view.cxx b/viewer/demo-view.cxx
@@ -0,0 +1,656 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "demo-view.h"
+
+extern "C" {
+#include "trackball.h"
+#include "matrix4x4.h"
+}
+
+#include <sys/time.h>
+
+
+struct demo_view_t {
+ unsigned int refcount;
+
+ demo_glstate_t *st;
+
+ /* Output */
+ GLint vsync;
+ glyphy_bool_t srgb;
+ glyphy_bool_t fullscreen;
+
+ /* Mouse handling */
+ int buttons;
+ int modifiers;
+ bool dragged;
+ bool click_handled;
+ double beginx, beginy;
+ double lastx, lasty, lastt;
+ double dx,dy, dt;
+
+ /* Transformation */
+ float quat[4];
+ double scale;
+ glyphy_point_t translate;
+ double perspective;
+
+ /* Animation */
+ float rot_axis[3];
+ float rot_speed;
+ bool animate;
+ int num_frames;
+ long fps_start_time;
+ long last_frame_time;
+ bool has_fps_timer;
+
+ /* Window geometry just before going fullscreen */
+ int x;
+ int y;
+ int width;
+ int height;
+};
+
+demo_view_t *static_vu;
+
+demo_view_t *
+demo_view_create (demo_glstate_t *st)
+{
+ TRACE();
+
+ demo_view_t *vu = (demo_view_t *) calloc (1, sizeof (demo_view_t));
+ vu->refcount = 1;
+
+ vu->st = st;
+ demo_view_reset (vu);
+
+ assert (!static_vu);
+ static_vu = vu;
+
+ return vu;
+}
+
+demo_view_t *
+demo_view_reference (demo_view_t *vu)
+{
+ if (vu) vu->refcount++;
+ return vu;
+}
+
+void
+demo_view_destroy (demo_view_t *vu)
+{
+ if (!vu || --vu->refcount)
+ return;
+
+ assert (static_vu == vu);
+ static_vu = NULL;
+
+ free (vu);
+}
+
+
+#define ANIMATION_SPEED 1. /* Default speed, in radians second. */
+void
+demo_view_reset (demo_view_t *vu)
+{
+ vu->perspective = 4;
+ vu->scale = 1;
+ vu->translate.x = vu->translate.y = 0;
+ trackball (vu->quat , 0.0, 0.0, 0.0, 0.0);
+ vset (vu->rot_axis, 0., 0., 1.);
+ vu->rot_speed = ANIMATION_SPEED / 1000.;
+}
+
+
+static void
+demo_view_scale_gamma_adjust (demo_view_t *vu, double factor)
+{
+ demo_glstate_scale_gamma_adjust (vu->st, factor);
+}
+
+static void
+demo_view_scale_contrast (demo_view_t *vu, double factor)
+{
+ demo_glstate_scale_contrast (vu->st, factor);
+}
+
+static void
+demo_view_scale_perspective (demo_view_t *vu, double factor)
+{
+ vu->perspective = clamp (vu->perspective * factor, .01, 100.);
+}
+
+static void
+demo_view_toggle_outline (demo_view_t *vu)
+{
+ demo_glstate_toggle_outline (vu->st);
+}
+
+static void
+demo_view_scale_outline_thickness (demo_view_t *vu, double factor)
+{
+ demo_glstate_scale_outline_thickness (vu->st, factor);
+}
+
+
+static void
+demo_view_adjust_boldness (demo_view_t *vu, double factor)
+{
+ demo_glstate_adjust_boldness (vu->st, factor);
+}
+
+
+static void
+demo_view_scale (demo_view_t *vu, double factor)
+{
+ vu->scale *= factor;
+}
+
+static void
+demo_view_translate (demo_view_t *vu, double dx, double dy)
+{
+ vu->translate.x += dx / vu->scale;
+ vu->translate.y += dy / vu->scale;
+}
+
+static void
+demo_view_apply_transform (demo_view_t *vu, float *mat)
+{
+ int viewport[4];
+ glGetIntegerv (GL_VIEWPORT, viewport);
+ GLint width = viewport[2];
+ GLint height = viewport[3];
+
+ // View transform
+ m4Scale (mat, vu->scale, vu->scale, 1);
+ m4Translate (mat, vu->translate.x, vu->translate.y, 0);
+
+ // Perspective
+ {
+ double d = std::max (width, height);
+ double near = d / vu->perspective;
+ double far = near + d;
+ double factor = near / (2 * near + d);
+ m4Frustum (mat, -width * factor, width * factor, -height * factor, height * factor, near, far);
+ m4Translate (mat, 0, 0, -(near + d * .5));
+ }
+
+ // Rotate
+ float m[4][4];
+ build_rotmatrix (m, vu->quat);
+ m4MultMatrix(mat, &m[0][0]);
+
+ // Fix 'up'
+ m4Scale (mat, 1, -1, 1);
+}
+
+
+/* return current time in milli-seconds */
+static long
+current_time (void)
+{
+ return glutGet (GLUT_ELAPSED_TIME);
+}
+
+static void
+next_frame (demo_view_t *vu)
+{
+ glutPostRedisplay ();
+}
+
+static void
+timed_step (int ms)
+{
+ demo_view_t *vu = static_vu;
+ if (vu->animate) {
+ glutTimerFunc (ms, timed_step, ms);
+ next_frame (vu);
+ }
+}
+
+static void
+idle_step (void)
+{
+ demo_view_t *vu = static_vu;
+ if (vu->animate) {
+ next_frame (vu);
+ }
+ else
+ glutIdleFunc (NULL);
+}
+
+static void
+print_fps (int ms)
+{
+ demo_view_t *vu = static_vu;
+ if (vu->animate) {
+ glutTimerFunc (ms, print_fps, ms);
+ long t = current_time ();
+ LOGI ("%gfps\n", vu->num_frames * 1000. / (t - vu->fps_start_time));
+ vu->num_frames = 0;
+ vu->fps_start_time = t;
+ } else
+ vu->has_fps_timer = false;
+}
+
+static void
+start_animation (demo_view_t *vu)
+{
+ vu->num_frames = 0;
+ vu->last_frame_time = vu->fps_start_time = current_time ();
+ //glutTimerFunc (1000/60, timed_step, 1000/60);
+ glutIdleFunc (idle_step);
+ if (!vu->has_fps_timer) {
+ vu->has_fps_timer = true;
+ glutTimerFunc (5000, print_fps, 5000);
+ }
+}
+
+static void
+demo_view_toggle_animation (demo_view_t *vu)
+{
+ vu->animate = !vu->animate;
+ if (vu->animate)
+ start_animation (vu);
+}
+
+
+static void
+demo_view_toggle_vsync (demo_view_t *vu)
+{
+ vu->vsync = !vu->vsync;
+ LOGI ("Setting vsync %s.\n", vu->vsync ? "on" : "off");
+#if defined(__APPLE__)
+ CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &vu->vsync);
+#elif defined(__WGLEW__)
+ if (wglewIsSupported ("WGL_EXT_swap_control"))
+ wglSwapIntervalEXT (vu->vsync);
+ else
+ LOGW ("WGL_EXT_swal_control not supported; failed to set vsync\n");
+#elif defined(__GLXEW_H__)
+ if (glxewIsSupported ("GLX_SGI_swap_control"))
+ glXSwapIntervalSGI (vu->vsync);
+ else
+ LOGW ("GLX_SGI_swap_control not supported; failed to set vsync\n");
+#else
+ LOGW ("No vsync extension found; failed to set vsync\n");
+#endif
+}
+
+static void
+demo_view_toggle_srgb (demo_view_t *vu)
+{
+ vu->srgb = !vu->srgb;
+ LOGI ("Setting sRGB framebuffer %s.\n", vu->srgb ? "on" : "off");
+#if defined(GL_FRAMEBUFFER_SRGB) && defined(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT)
+ GLboolean available = false;
+ if ((glewIsSupported ("GL_ARB_framebuffer_sRGB") || glewIsSupported ("GL_EXT_framebuffer_sRGB")) &&
+ (glGetBooleanv (GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &available), available)) {
+ if (vu->srgb)
+ glEnable (GL_FRAMEBUFFER_SRGB);
+ else
+ glDisable (GL_FRAMEBUFFER_SRGB);
+ } else
+#endif
+ LOGW ("No sRGB framebuffer extension found; failed to set sRGB framebuffer\n");
+}
+
+static void
+demo_view_toggle_fullscreen (demo_view_t *vu)
+{
+ vu->fullscreen = !vu->fullscreen;
+ if (vu->fullscreen) {
+ vu->x = glutGet (GLUT_WINDOW_X);
+ vu->y = glutGet (GLUT_WINDOW_Y);
+ vu->width = glutGet (GLUT_WINDOW_WIDTH);
+ vu->height = glutGet (GLUT_WINDOW_HEIGHT);
+ glutFullScreen ();
+ } else {
+ glutReshapeWindow (vu->width, vu->height);
+ glutPositionWindow (vu->x, vu->y);
+ }
+}
+
+static void
+demo_view_toggle_debug (demo_view_t *vu)
+{
+ demo_glstate_toggle_debug (vu->st);
+}
+
+
+void
+demo_view_reshape_func (demo_view_t *vu, int width, int height)
+{
+ glViewport (0, 0, width, height);
+ glutPostRedisplay ();
+}
+
+#define STEP 1.05
+void
+demo_view_keyboard_func (demo_view_t *vu, unsigned char key, int x, int y)
+{
+ switch (key)
+ {
+ case '\033':
+ case 'q':
+ exit (0);
+ break;
+
+ case ' ':
+ demo_view_toggle_animation (vu);
+ break;
+ case 'v':
+ demo_view_toggle_vsync (vu);
+ break;
+
+ case 'f':
+ demo_view_toggle_fullscreen (vu);
+ break;
+
+ case 'd':
+ demo_view_toggle_debug (vu);
+ break;
+
+ case 'o':
+ demo_view_toggle_outline (vu);
+ break;
+ case 'p':
+ demo_view_scale_outline_thickness (vu, STEP);
+ break;
+ case 'i':
+ demo_view_scale_outline_thickness (vu, 1. / STEP);
+ break;
+
+ case '0':
+ demo_view_adjust_boldness (vu, +.01);
+ break;
+ case '9':
+ demo_view_adjust_boldness (vu, -.01);
+ break;
+
+
+ case 'a':
+ demo_view_scale_contrast (vu, STEP);
+ break;
+ case 'z':
+ demo_view_scale_contrast (vu, 1. / STEP);
+ break;
+ case 'g':
+ demo_view_scale_gamma_adjust (vu, STEP);
+ break;
+ case 'b':
+ demo_view_scale_gamma_adjust (vu, 1. / STEP);
+ break;
+ case 'c':
+ demo_view_toggle_srgb (vu);
+ break;
+
+ case '=':
+ demo_view_scale (vu, STEP);
+ break;
+ case '-':
+ demo_view_scale (vu, 1. / STEP);
+ break;
+
+ case 'k':
+ demo_view_translate (vu, 0, -.1);
+ break;
+ case 'j':
+ demo_view_translate (vu, 0, +.1);
+ break;
+ case 'h':
+ demo_view_translate (vu, +.1, 0);
+ break;
+ case 'l':
+ demo_view_translate (vu, -.1, 0);
+ break;
+
+ case 'r':
+ demo_view_reset (vu);
+ break;
+
+ default:
+ return;
+ }
+ glutPostRedisplay ();
+}
+
+void
+demo_view_special_func (demo_view_t *vu, int key, int x, int y)
+{
+ switch (key)
+ {
+ case GLUT_KEY_UP:
+ demo_view_translate (vu, 0, -.1);
+ break;
+ case GLUT_KEY_DOWN:
+ demo_view_translate (vu, 0, +.1);
+ break;
+ case GLUT_KEY_LEFT:
+ demo_view_translate (vu, +.1, 0);
+ break;
+ case GLUT_KEY_RIGHT:
+ demo_view_translate (vu, -.1, 0);
+ break;
+
+ default:
+ return;
+ }
+ glutPostRedisplay ();
+}
+
+void
+demo_view_mouse_func (demo_view_t *vu, int button, int state, int x, int y)
+{
+ if (state == GLUT_DOWN) {
+ vu->buttons |= (1 << button);
+ vu->click_handled = false;
+ } else
+ vu->buttons &= !(1 << button);
+ vu->modifiers = glutGetModifiers ();
+
+ switch (button)
+ {
+ case GLUT_RIGHT_BUTTON:
+ switch (state) {
+ case GLUT_DOWN:
+ if (vu->animate) {
+ demo_view_toggle_animation (vu);
+ vu->click_handled = true;
+ }
+ break;
+ case GLUT_UP:
+ if (!vu->animate)
+ {
+ if (!vu->dragged && !vu->click_handled)
+ demo_view_toggle_animation (vu);
+ else if (vu->dt) {
+ double speed = hypot (vu->dx, vu->dy) / vu->dt;
+ if (speed > 0.1)
+ demo_view_toggle_animation (vu);
+ }
+ vu->dx = vu->dy = vu->dt = 0;
+ }
+ break;
+ }
+ break;
+
+#if !defined(GLUT_WHEEL_UP)
+#define GLUT_WHEEL_UP 3
+#define GLUT_WHEEL_DOWN 4
+#endif
+
+ case GLUT_WHEEL_UP:
+ demo_view_scale (vu, STEP);
+ break;
+
+ case GLUT_WHEEL_DOWN:
+ demo_view_scale (vu, 1. / STEP);
+ break;
+ }
+
+ vu->beginx = vu->lastx = x;
+ vu->beginy = vu->lasty = y;
+ vu->dragged = false;
+
+ glutPostRedisplay ();
+}
+
+void
+demo_view_motion_func (demo_view_t *vu, int x, int y)
+{
+ vu->dragged = true;
+
+ int viewport[4];
+ glGetIntegerv (GL_VIEWPORT, viewport);
+ GLuint width = viewport[2];
+ GLuint height = viewport[3];
+
+ if (vu->buttons & (1 << GLUT_LEFT_BUTTON))
+ {
+ if (vu->modifiers & GLUT_ACTIVE_SHIFT) {
+ /* adjust contrast/gamma */
+ demo_view_scale_gamma_adjust (vu, 1 - ((y - vu->lasty) / height));
+ demo_view_scale_contrast (vu, 1 + ((x - vu->lastx) / width));
+ } else {
+ /* translate */
+ demo_view_translate (vu,
+ +2 * (x - vu->lastx) / width,
+ -2 * (y - vu->lasty) / height);
+ }
+ }
+
+ if (vu->buttons & (1 << GLUT_RIGHT_BUTTON))
+ {
+ if (vu->modifiers & GLUT_ACTIVE_SHIFT) {
+ /* adjust perspective */
+ demo_view_scale_perspective (vu, 1 - ((y - vu->lasty) / height) * 5);
+ } else {
+ /* rotate */
+ float dquat[4];
+ trackball (dquat,
+ (2.0*vu->lastx - width) / width,
+ ( height - 2.0*vu->lasty) / height,
+ ( 2.0*x - width) / width,
+ ( height - 2.0*y) / height );
+
+ vu->dx = x - vu->lastx;
+ vu->dy = y - vu->lasty;
+ vu->dt = current_time () - vu->lastt;
+
+ add_quats (dquat, vu->quat, vu->quat);
+
+ if (vu->dt) {
+ vcopy (dquat, vu->rot_axis);
+ vnormal (vu->rot_axis);
+ vu->rot_speed = 2 * acos (dquat[3]) / vu->dt;
+ }
+ }
+ }
+
+ if (vu->buttons & (1 << GLUT_MIDDLE_BUTTON))
+ {
+ /* scale */
+ double factor = 1 - ((y - vu->lasty) / height) * 5;
+ demo_view_scale (vu, factor);
+ /* adjust translate so we scale centered at the drag-begin mouse position */
+ demo_view_translate (vu,
+ +(2. * vu->beginx / width - 1) * (1 - factor),
+ -(2. * vu->beginy / height - 1) * (1 - factor));
+ }
+
+ vu->lastx = x;
+ vu->lasty = y;
+ vu->lastt = current_time ();
+
+ glutPostRedisplay ();
+}
+
+void
+demo_view_print_help (demo_view_t *vu)
+{
+ LOGI ("Welcome to GLyphy demo\n");
+}
+
+
+static void
+advance_frame (demo_view_t *vu, long dtime)
+{
+ if (vu->animate) {
+ float dquat[4];
+ axis_to_quat (vu->rot_axis, vu->rot_speed * dtime, dquat);
+ add_quats (dquat, vu->quat, vu->quat);
+ vu->num_frames++;
+ }
+}
+
+void
+demo_view_display (demo_view_t *vu, demo_buffer_t *buffer)
+{
+ long new_time = current_time ();
+ advance_frame (vu, new_time - vu->last_frame_time);
+ vu->last_frame_time = new_time;
+
+ int viewport[4];
+ glGetIntegerv (GL_VIEWPORT, viewport);
+ GLint width = viewport[2];
+ GLint height = viewport[3];
+
+
+ float mat[16];
+
+ m4LoadIdentity (mat);
+
+ demo_view_apply_transform (vu, mat);
+
+ // Buffer best-fit
+ glyphy_extents_t extents;
+ demo_buffer_extents (buffer, NULL, &extents);
+ double content_scale = .9 * std::min (width / (extents.max_x - extents.min_x),
+ height / (extents.max_y - extents.min_y));
+ m4Scale (mat, content_scale, content_scale, 1);
+ // Center buffer
+ m4Translate (mat,
+ -(extents.max_x + extents.min_x) / 2.,
+ -(extents.max_y + extents.min_y) / 2., 0);
+
+ demo_glstate_set_matrix (vu->st, mat);
+
+ glClearColor (1, 1, 1, 1);
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ demo_buffer_draw (buffer);
+
+ glutSwapBuffers ();
+}
+
+void
+demo_view_setup (demo_view_t *vu)
+{
+ if (!vu->vsync)
+ demo_view_toggle_vsync (vu);
+ if (!vu->srgb)
+ demo_view_toggle_srgb (vu);
+ demo_glstate_setup (vu->st);
+}
diff --git a/viewer/demo-view.h b/viewer/demo-view.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012 Google, Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef DEMO_VIEW_H
+#define DEMO_VIEW_H
+
+#include "demo-common.h"
+#include "demo-buffer.h"
+#include "demo-glstate.h"
+
+typedef struct demo_view_t demo_view_t;
+
+demo_view_t *
+demo_view_create (demo_glstate_t *st);
+
+demo_view_t *
+demo_view_reference (demo_view_t *vu);
+
+void
+demo_view_destroy (demo_view_t *vu);
+
+
+void
+demo_view_reset (demo_view_t *vu);
+
+void
+demo_view_reshape_func (demo_view_t *vu, int width, int height);
+
+void
+demo_view_keyboard_func (demo_view_t *vu, unsigned char key, int x, int y);
+
+void
+demo_view_special_func (demo_view_t *view, int key, int x, int y);
+
+void
+demo_view_mouse_func (demo_view_t *vu, int button, int state, int x, int y);
+
+void
+demo_view_motion_func (demo_view_t *vu, int x, int y);
+
+void
+demo_view_print_help (demo_view_t *vu);
+
+void
+demo_view_display (demo_view_t *vu, demo_buffer_t *buffer);
+
+void
+demo_view_setup (demo_view_t *vu);
+
+
+#endif /* DEMO_VIEW_H */
diff --git a/viewer/demo-vshader-glsl.h b/viewer/demo-vshader-glsl.h
@@ -0,0 +1,24 @@
+static const char *demo_vshader_glsl =
+"uniform mat4 u_matViewProjection;\n"
+"\n"
+"attribute vec4 a_glyph_vertex;\n"
+"\n"
+"varying vec4 v_glyph;\n"
+"\n"
+"vec4\n"
+"glyph_vertex_transcode (vec2 v)\n"
+"{\n"
+" ivec2 g = ivec2 (v);\n"
+" ivec2 corner = ivec2 (mod (v, 2.));\n"
+" g /= 2;\n"
+" ivec2 nominal_size = ivec2 (mod (vec2(g), 64.));\n"
+" return vec4 (corner * nominal_size, g * 4);\n"
+"}\n"
+"\n"
+"void\n"
+"main()\n"
+"{\n"
+" gl_Position = u_matViewProjection * vec4 (a_glyph_vertex.xy, 0, 1);\n"
+" v_glyph = glyph_vertex_transcode (a_glyph_vertex.zw);\n"
+"}\n"
+;
diff --git a/viewer/matrix4x4.c b/viewer/matrix4x4.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2009, Mozilla Corp
+ * Copyright (c) 2012, Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the <organization> nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Based on sample code from the OpenGL(R) ES 2.0 Programming Guide, which carriers
+ * the following header:
+ *
+ * Book: OpenGL(R) ES 2.0 Programming Guide
+ * Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+ * ISBN-10: 0321502795
+ * ISBN-13: 9780321502797
+ * Publisher: Addison-Wesley Professional
+ * URLs: http://safari.informit.com/9780321563835
+ * http://www.opengles-book.com
+ */
+
+/*
+ * Ported from JavaScript to C by Behdad Esfahbod, 2012.
+ * Added MultMatrix. Converting from fixed-function OpenGL matrix
+ * operations to these functions should be as simple as renaming the
+ * 'gl' prefix to 'm4' and adding the matrix argument to the call.
+ *
+ * The C version lives at http://code.google.com/p/matrix4x4-c/
+ */
+
+#include "matrix4x4.h"
+#include <math.h>
+
+/*
+ * A simple 4x4 matrix utility implementation
+ */
+
+
+float *
+m4LoadIdentity (float *mat) {
+ unsigned int i;
+ for (i = 0; i < 16; i++)
+ mat[i] = 0;
+ mat[0*4+0] = 1.0;
+ mat[1*4+1] = 1.0;
+ mat[2*4+2] = 1.0;
+ mat[3*4+3] = 1.0;
+ return mat;
+}
+
+/* Copies other matrix into mat */
+float *
+m4Copy (float *mat, const float *other) {
+ unsigned int i;
+ for (i = 0; i < 16; i++) {
+ mat[i] = other[i];
+ }
+ return mat;
+}
+
+float *
+m4Multiply (float *mat, const float *right) {
+ float tmp[16];
+ unsigned int i;
+
+ for (i = 0; i < 4; i++) {
+ tmp[i*4+0] =
+ (mat[i*4+0] * right[0*4+0]) +
+ (mat[i*4+1] * right[1*4+0]) +
+ (mat[i*4+2] * right[2*4+0]) +
+ (mat[i*4+3] * right[3*4+0]) ;
+
+ tmp[i*4+1] =
+ (mat[i*4+0] * right[0*4+1]) +
+ (mat[i*4+1] * right[1*4+1]) +
+ (mat[i*4+2] * right[2*4+1]) +
+ (mat[i*4+3] * right[3*4+1]) ;
+
+ tmp[i*4+2] =
+ (mat[i*4+0] * right[0*4+2]) +
+ (mat[i*4+1] * right[1*4+2]) +
+ (mat[i*4+2] * right[2*4+2]) +
+ (mat[i*4+3] * right[3*4+2]) ;
+
+ tmp[i*4+3] =
+ (mat[i*4+0] * right[0*4+3]) +
+ (mat[i*4+1] * right[1*4+3]) +
+ (mat[i*4+2] * right[2*4+3]) +
+ (mat[i*4+3] * right[3*4+3]) ;
+ }
+
+ return m4Copy (mat, tmp);
+}
+
+float
+m4Get (float *mat, unsigned int row, unsigned int col) {
+ return mat[4*row+col];
+}
+
+float *
+m4MultMatrix (float *mat, const float *left) {
+ float tmp[16];
+ return m4Copy (mat, m4Multiply (m4Copy (tmp, left), mat));
+}
+
+float *
+m4Scale (float *mat, float sx, float sy, float sz) {
+ mat[0*4+0] *= sx;
+ mat[0*4+1] *= sx;
+ mat[0*4+2] *= sx;
+ mat[0*4+3] *= sx;
+
+ mat[1*4+0] *= sy;
+ mat[1*4+1] *= sy;
+ mat[1*4+2] *= sy;
+ mat[1*4+3] *= sy;
+
+ mat[2*4+0] *= sz;
+ mat[2*4+1] *= sz;
+ mat[2*4+2] *= sz;
+ mat[2*4+3] *= sz;
+
+ return mat;
+}
+
+float *
+m4Translate (float *mat, float tx, float ty, float tz) {
+ mat[3*4+0] += mat[0*4+0] * tx + mat[1*4+0] * ty + mat[2*4+0] * tz;
+ mat[3*4+1] += mat[0*4+1] * tx + mat[1*4+1] * ty + mat[2*4+1] * tz;
+ mat[3*4+2] += mat[0*4+2] * tx + mat[1*4+2] * ty + mat[2*4+2] * tz;
+ mat[3*4+3] += mat[0*4+3] * tx + mat[1*4+3] * ty + mat[2*4+3] * tz;
+
+ return mat;
+}
+
+float *
+m4Rotate (float *mat, float angle, float x, float y, float z) {
+ float mag = sqrt(x*x + y*y + z*z);
+ float sinAngle = sin(angle * M_PI / 180.0);
+ float cosAngle = cos(angle * M_PI / 180.0);
+
+ float xx, yy, zz, xy, yz, zx, xs, ys, zs;
+ float oneMinusCos;
+
+ float rotMat[16];
+
+ if (mag <= 0)
+ return mat;
+
+ m4LoadIdentity (rotMat);
+
+ x /= mag;
+ y /= mag;
+ z /= mag;
+
+ xx = x * x;
+ yy = y * y;
+ zz = z * z;
+ xy = x * y;
+ yz = y * z;
+ zx = z * x;
+ xs = x * sinAngle;
+ ys = y * sinAngle;
+ zs = z * sinAngle;
+ oneMinusCos = 1.0 - cosAngle;
+
+ rotMat[0*4+0] = (oneMinusCos * xx) + cosAngle;
+ rotMat[0*4+1] = (oneMinusCos * xy) - zs;
+ rotMat[0*4+2] = (oneMinusCos * zx) + ys;
+ rotMat[0*4+3] = 0.0;
+
+ rotMat[1*4+0] = (oneMinusCos * xy) + zs;
+ rotMat[1*4+1] = (oneMinusCos * yy) + cosAngle;
+ rotMat[1*4+2] = (oneMinusCos * yz) - xs;
+ rotMat[1*4+3] = 0.0;
+
+ rotMat[2*4+0] = (oneMinusCos * zx) - ys;
+ rotMat[2*4+1] = (oneMinusCos * yz) + xs;
+ rotMat[2*4+2] = (oneMinusCos * zz) + cosAngle;
+ rotMat[2*4+3] = 0.0;
+
+ rotMat[3*4+0] = 0.0;
+ rotMat[3*4+1] = 0.0;
+ rotMat[3*4+2] = 0.0;
+ rotMat[3*4+3] = 1.0;
+
+ return m4Copy (mat, m4Multiply (rotMat, mat));
+}
+
+float *
+m4Frustum (float *mat, float left, float right, float bottom, float top, float nearZ, float farZ) {
+ float deltaX = right - left;
+ float deltaY = top - bottom;
+ float deltaZ = farZ - nearZ;
+
+ float frust[16];
+
+ if ( (nearZ <= 0.0) || (farZ <= 0.0) ||
+ (deltaX <= 0.0) || (deltaY <= 0.0) || (deltaZ <= 0.0) )
+ return mat;
+
+ m4LoadIdentity (frust);
+
+ frust[0*4+0] = 2.0 * nearZ / deltaX;
+ frust[0*4+1] = frust[0*4+2] = frust[0*4+3] = 0.0;
+
+ frust[1*4+1] = 2.0 * nearZ / deltaY;
+ frust[1*4+0] = frust[1*4+2] = frust[1*4+3] = 0.0;
+
+ frust[2*4+0] = (right + left) / deltaX;
+ frust[2*4+1] = (top + bottom) / deltaY;
+ frust[2*4+2] = -(nearZ + farZ) / deltaZ;
+ frust[2*4+3] = -1.0;
+
+ frust[3*4+2] = -2.0 * nearZ * farZ / deltaZ;
+ frust[3*4+0] = frust[3*4+1] = frust[3*4+3] = 0.0;
+
+ return m4Copy (mat, m4Multiply (frust, mat));
+}
+
+float *
+m4Perspective (float *mat, float fovy, float aspect, float nearZ, float farZ) {
+ float frustumH = tan(fovy / 360.0 * M_PI) * nearZ;
+ float frustumW = frustumH * aspect;
+
+ return m4Frustum(mat, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ);
+}
+
+float *
+m4Ortho (float *mat, float left, float right, float bottom, float top, float nearZ, float farZ) {
+ float deltaX = right - left;
+ float deltaY = top - bottom;
+ float deltaZ = farZ - nearZ;
+
+ float ortho[16];
+
+ if ( (deltaX == 0.0) || (deltaY == 0.0) || (deltaZ == 0.0) )
+ return mat;
+
+ m4LoadIdentity (ortho);
+
+ ortho[0*4+0] = 2.0 / deltaX;
+ ortho[3*4+0] = -(right + left) / deltaX;
+ ortho[1*4+1] = 2.0 / deltaY;
+ ortho[3*4+1] = -(top + bottom) / deltaY;
+ ortho[2*4+2] = -2.0 / deltaZ;
+ ortho[3*4+2] = -(nearZ + farZ) / deltaZ;
+
+ return m4Copy (mat, m4Multiply (ortho, mat));
+}
+
+/* In-place inversion */
+float *
+m4Invert (float *mat) {
+ float tmp_0 = m4Get(mat,2,2) * m4Get(mat,3,3);
+ float tmp_1 = m4Get(mat,3,2) * m4Get(mat,2,3);
+ float tmp_2 = m4Get(mat,1,2) * m4Get(mat,3,3);
+ float tmp_3 = m4Get(mat,3,2) * m4Get(mat,1,3);
+ float tmp_4 = m4Get(mat,1,2) * m4Get(mat,2,3);
+ float tmp_5 = m4Get(mat,2,2) * m4Get(mat,1,3);
+ float tmp_6 = m4Get(mat,0,2) * m4Get(mat,3,3);
+ float tmp_7 = m4Get(mat,3,2) * m4Get(mat,0,3);
+ float tmp_8 = m4Get(mat,0,2) * m4Get(mat,2,3);
+ float tmp_9 = m4Get(mat,2,2) * m4Get(mat,0,3);
+ float tmp_10 = m4Get(mat,0,2) * m4Get(mat,1,3);
+ float tmp_11 = m4Get(mat,1,2) * m4Get(mat,0,3);
+ float tmp_12 = m4Get(mat,2,0) * m4Get(mat,3,1);
+ float tmp_13 = m4Get(mat,3,0) * m4Get(mat,2,1);
+ float tmp_14 = m4Get(mat,1,0) * m4Get(mat,3,1);
+ float tmp_15 = m4Get(mat,3,0) * m4Get(mat,1,1);
+ float tmp_16 = m4Get(mat,1,0) * m4Get(mat,2,1);
+ float tmp_17 = m4Get(mat,2,0) * m4Get(mat,1,1);
+ float tmp_18 = m4Get(mat,0,0) * m4Get(mat,3,1);
+ float tmp_19 = m4Get(mat,3,0) * m4Get(mat,0,1);
+ float tmp_20 = m4Get(mat,0,0) * m4Get(mat,2,1);
+ float tmp_21 = m4Get(mat,2,0) * m4Get(mat,0,1);
+ float tmp_22 = m4Get(mat,0,0) * m4Get(mat,1,1);
+ float tmp_23 = m4Get(mat,1,0) * m4Get(mat,0,1);
+
+ float t0 = ((tmp_0 * m4Get(mat,1,1) + tmp_3 * m4Get(mat,2,1) + tmp_4 * m4Get(mat,3,1)) -
+ (tmp_1 * m4Get(mat,1,1) + tmp_2 * m4Get(mat,2,1) + tmp_5 * m4Get(mat,3,1)));
+ float t1 = ((tmp_1 * m4Get(mat,0,1) + tmp_6 * m4Get(mat,2,1) + tmp_9 * m4Get(mat,3,1)) -
+ (tmp_0 * m4Get(mat,0,1) + tmp_7 * m4Get(mat,2,1) + tmp_8 * m4Get(mat,3,1)));
+ float t2 = ((tmp_2 * m4Get(mat,0,1) + tmp_7 * m4Get(mat,1,1) + tmp_10 * m4Get(mat,3,1)) -
+ (tmp_3 * m4Get(mat,0,1) + tmp_6 * m4Get(mat,1,1) + tmp_11 * m4Get(mat,3,1)));
+ float t3 = ((tmp_5 * m4Get(mat,0,1) + tmp_8 * m4Get(mat,1,1) + tmp_11 * m4Get(mat,2,1)) -
+ (tmp_4 * m4Get(mat,0,1) + tmp_9 * m4Get(mat,1,1) + tmp_10 * m4Get(mat,2,1)));
+
+ float d = 1.0 / (m4Get(mat,0,0) * t0 + m4Get(mat,1,0) * t1 + m4Get(mat,2,0) * t2 + m4Get(mat,3,0) * t3);
+
+ float out_00 = d * t0;
+ float out_01 = d * t1;
+ float out_02 = d * t2;
+ float out_03 = d * t3;
+
+ float out_10 = d * ((tmp_1 * m4Get(mat,1,0) + tmp_2 * m4Get(mat,2,0) + tmp_5 * m4Get(mat,3,0)) -
+ (tmp_0 * m4Get(mat,1,0) + tmp_3 * m4Get(mat,2,0) + tmp_4 * m4Get(mat,3,0)));
+ float out_11 = d * ((tmp_0 * m4Get(mat,0,0) + tmp_7 * m4Get(mat,2,0) + tmp_8 * m4Get(mat,3,0)) -
+ (tmp_1 * m4Get(mat,0,0) + tmp_6 * m4Get(mat,2,0) + tmp_9 * m4Get(mat,3,0)));
+ float out_12 = d * ((tmp_3 * m4Get(mat,0,0) + tmp_6 * m4Get(mat,1,0) + tmp_11 * m4Get(mat,3,0)) -
+ (tmp_2 * m4Get(mat,0,0) + tmp_7 * m4Get(mat,1,0) + tmp_10 * m4Get(mat,3,0)));
+ float out_13 = d * ((tmp_4 * m4Get(mat,0,0) + tmp_9 * m4Get(mat,1,0) + tmp_10 * m4Get(mat,2,0)) -
+ (tmp_5 * m4Get(mat,0,0) + tmp_8 * m4Get(mat,1,0) + tmp_11 * m4Get(mat,2,0)));
+
+ float out_20 = d * ((tmp_12 * m4Get(mat,1,3) + tmp_15 * m4Get(mat,2,3) + tmp_16 * m4Get(mat,3,3)) -
+ (tmp_13 * m4Get(mat,1,3) + tmp_14 * m4Get(mat,2,3) + tmp_17 * m4Get(mat,3,3)));
+ float out_21 = d * ((tmp_13 * m4Get(mat,0,3) + tmp_18 * m4Get(mat,2,3) + tmp_21 * m4Get(mat,3,3)) -
+ (tmp_12 * m4Get(mat,0,3) + tmp_19 * m4Get(mat,2,3) + tmp_20 * m4Get(mat,3,3)));
+ float out_22 = d * ((tmp_14 * m4Get(mat,0,3) + tmp_19 * m4Get(mat,1,3) + tmp_22 * m4Get(mat,3,3)) -
+ (tmp_15 * m4Get(mat,0,3) + tmp_18 * m4Get(mat,1,3) + tmp_23 * m4Get(mat,3,3)));
+ float out_23 = d * ((tmp_17 * m4Get(mat,0,3) + tmp_20 * m4Get(mat,1,3) + tmp_23 * m4Get(mat,2,3)) -
+ (tmp_16 * m4Get(mat,0,3) + tmp_21 * m4Get(mat,1,3) + tmp_22 * m4Get(mat,2,3)));
+
+ float out_30 = d * ((tmp_14 * m4Get(mat,2,2) + tmp_17 * m4Get(mat,3,2) + tmp_13 * m4Get(mat,1,2)) -
+ (tmp_16 * m4Get(mat,3,2) + tmp_12 * m4Get(mat,1,2) + tmp_15 * m4Get(mat,2,2)));
+ float out_31 = d * ((tmp_20 * m4Get(mat,3,2) + tmp_12 * m4Get(mat,0,2) + tmp_19 * m4Get(mat,2,2)) -
+ (tmp_18 * m4Get(mat,2,2) + tmp_21 * m4Get(mat,3,2) + tmp_13 * m4Get(mat,0,2)));
+ float out_32 = d * ((tmp_18 * m4Get(mat,1,2) + tmp_23 * m4Get(mat,3,2) + tmp_15 * m4Get(mat,0,2)) -
+ (tmp_22 * m4Get(mat,3,2) + tmp_14 * m4Get(mat,0,2) + tmp_19 * m4Get(mat,1,2)));
+ float out_33 = d * ((tmp_22 * m4Get(mat,2,2) + tmp_16 * m4Get(mat,0,2) + tmp_21 * m4Get(mat,1,2)) -
+ (tmp_20 * m4Get(mat,1,2) + tmp_23 * m4Get(mat,2,2) + tmp_17 * m4Get(mat,0,2)));
+
+ mat[0*4+0] = out_00;
+ mat[0*4+1] = out_01;
+ mat[0*4+2] = out_02;
+ mat[0*4+3] = out_03;
+ mat[1*4+0] = out_10;
+ mat[1*4+1] = out_11;
+ mat[1*4+2] = out_12;
+ mat[1*4+3] = out_13;
+ mat[2*4+0] = out_20;
+ mat[2*4+1] = out_21;
+ mat[2*4+2] = out_22;
+ mat[2*4+3] = out_23;
+ mat[3*4+0] = out_30;
+ mat[3*4+1] = out_31;
+ mat[3*4+2] = out_32;
+ mat[3*4+3] = out_33;
+ return mat;
+}
+
+/* Puts the inverse of other matrix into mat */
+float *
+m4Inverse (float *mat, const float *other) {
+ m4Copy (mat, other);
+ m4Invert (mat);
+ return mat;
+}
+
+/* In-place transpose */
+float *
+m4Transpose (float *mat) {
+ float tmp = mat[0*4+1];
+ mat[0*4+1] = mat[1*4+0];
+ mat[1*4+0] = tmp;
+
+ tmp = mat[0*4+2];
+ mat[0*4+2] = mat[2*4+0];
+ mat[2*4+0] = tmp;
+
+ tmp = mat[0*4+3];
+ mat[0*4+3] = mat[3*4+0];
+ mat[3*4+0] = tmp;
+
+ tmp = mat[1*4+2];
+ mat[1*4+2] = mat[2*4+1];
+ mat[2*4+1] = tmp;
+
+ tmp = mat[1*4+3];
+ mat[1*4+3] = mat[3*4+1];
+ mat[3*4+1] = tmp;
+
+ tmp = mat[2*4+3];
+ mat[2*4+3] = mat[3*4+2];
+ mat[3*4+2] = tmp;
+
+ return mat;
+}
diff --git a/viewer/matrix4x4.h b/viewer/matrix4x4.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2009, Mozilla Corp
+ * Copyright (c) 2012, Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the <organization> nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Based on sample code from the OpenGL(R) ES 2.0 Programming Guide, which carriers
+ * the following header:
+ *
+ * Book: OpenGL(R) ES 2.0 Programming Guide
+ * Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+ * ISBN-10: 0321502795
+ * ISBN-13: 9780321502797
+ * Publisher: Addison-Wesley Professional
+ * URLs: http://safari.informit.com/9780321563835
+ * http://www.opengles-book.com
+ */
+
+/*
+ * Ported from JavaScript to C by Behdad Esfahbod, 2012.
+ * Added MultMatrix. Converting from fixed-function OpenGL matrix
+ * operations to these functions should be as simple as renaming the
+ * 'gl' prefix to 'm4' and adding the matrix argument to the call.
+ *
+ * The C version lives at http://code.google.com/p/matrix4x4-c/
+ */
+
+/*
+ * A simple 4x4 matrix utility implementation
+ */
+
+#ifndef MATRIX4x4_H
+#define MATRIX4x4_H
+
+/* Copies other matrix into mat */
+float *
+m4Copy (float *mat, const float *other);
+
+float *
+m4Multiply (float *mat, const float *right);
+
+float *
+m4MultMatrix (float *mat, const float *left);
+
+float
+m4Get (float *mat, unsigned int row, unsigned int col);
+
+float *
+m4Scale (float *mat, float sx, float sy, float sz);
+
+float *
+m4Translate (float *mat, float tx, float ty, float tz);
+
+float *
+m4Rotate (float *mat, float angle, float x, float y, float z);
+
+float *
+m4Frustum (float *mat, float left, float right, float bottom, float top, float nearZ, float farZ);
+
+float *
+m4Perspective (float *mat, float fovy, float aspect, float nearZ, float farZ);
+
+float *
+m4Ortho (float *mat, float left, float right, float bottom, float top, float nearZ, float farZ);
+
+/* In-place inversion */
+float *
+m4Invert (float *mat);
+
+/* Puts the inverse of other matrix into mat */
+float *
+m4Inverse (float *mat, const float *other);
+
+/* In-place transpose */
+float *
+m4Transpose (float *mat);
+
+float *
+m4LoadIdentity (float *mat);
+
+#endif
diff --git a/viewer/text.cxx b/viewer/text.cxx
@@ -9,12 +9,8 @@
#include "text.h"
text::text(af_unix *sock) :
- socket(sock),
- font(FTGLPixmapFont("DejaVuSansMono.ttf"))
+ socket(sock)
{
- if (font.Error())
- errx(1, "%s", "font error");
-
assert(socket->read_all(num_tus) == 8);
for (int i = 0; i < num_tus; i++) {
@@ -29,15 +25,6 @@ text::text(af_unix *sock) :
assert(socket->read_all(num_lines) == 8);
execution_counts.resize(num_lines);
}
-
- font.FaceSize(24);
-
- font.Render(file_name.c_str());
- int vertical = num_lines * 24;
- for (auto &line : source_file_contents) {
- font.Render(line.c_str(), line.size(), FTPoint(0, vertical, 0));
- vertical -= 24;
- }
}
void
@@ -69,14 +56,4 @@ text::idle()
// Send response back
uint8_t msg_type = 1;
assert(socket->write_all(&msg_type, 1) == 1);
-
- int vertical = num_lines * 24;
- for (auto &count : execution_counts) {
- std::stringstream ss;
- ss << count;
- std::string s_count = ss.str();
-
- font.Render(&s_count[0], s_count.size(), FTPoint(600, vertical, 0));
- vertical -= 24;
- }
}
diff --git a/viewer/text.h b/viewer/text.h
@@ -2,7 +2,6 @@
#define TEXT_H
#include <vector>
-#include <FTGL/ftgl.h>
#include "af_unix.h"
#include "draw.h"
@@ -24,8 +23,6 @@ private:
std::vector<std::string> source_file_contents;
std::vector<uint64_t> execution_counts;
-
- FTGLPixmapFont font;
};
#endif
diff --git a/viewer/trackball.c b/viewer/trackball.c
@@ -0,0 +1,337 @@
+#include <stdio.h>
+/*
+ * (c) Copyright 1993, 1994, Silicon Graphics, Inc.
+ * ALL RIGHTS RESERVED
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose and without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies and that both the copyright notice
+ * and this permission notice appear in supporting documentation, and that
+ * the name of Silicon Graphics, Inc. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific,
+ * written prior permission.
+ *
+ * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
+ * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
+ * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
+ * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
+ * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
+ * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
+ * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * US Government Users Restricted Rights
+ * Use, duplication, or disclosure by the Government is subject to
+ * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
+ * (c)(1)(ii) of the Rights in Technical Data and Computer Software
+ * clause at DFARS 252.227-7013 and/or in similar or successor
+ * clauses in the FAR or the DOD or NASA FAR Supplement.
+ * Unpublished-- rights reserved under the copyright laws of the
+ * United States. Contractor/manufacturer is Silicon Graphics,
+ * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311.
+ *
+ * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
+ */
+/*
+ * Trackball code:
+ *
+ * Implementation of a virtual trackball.
+ * Implemented by Gavin Bell, lots of ideas from Thant Tessman and
+ * the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129.
+ *
+ * Vector manip code:
+ *
+ * Original code from:
+ * David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli
+ *
+ * Much mucking with by:
+ * Gavin Bell
+ */
+#if defined(_WIN32)
+#pragma warning (disable:4244) /* disable bogus conversion warnings */
+#endif
+#include <math.h>
+#include "trackball.h"
+
+/*
+ * This size should really be based on the distance from the center of
+ * rotation to the point on the object underneath the mouse. That
+ * point would then track the mouse as closely as possible. This is a
+ * simple example, though, so that is left as an Exercise for the
+ * Programmer.
+ */
+#define TRACKBALLSIZE (0.5f)
+
+/*
+ * Local function prototypes (not defined in trackball.h)
+ */
+static float tb_project_to_sphere(float, float, float);
+static void normalize_quat(float [4]);
+
+void
+vzero(float *v)
+{
+ v[0] = 0.0;
+ v[1] = 0.0;
+ v[2] = 0.0;
+}
+
+void
+vset(float *v, float x, float y, float z)
+{
+ v[0] = x;
+ v[1] = y;
+ v[2] = z;
+}
+
+void
+vsub(const float *src1, const float *src2, float *dst)
+{
+ dst[0] = src1[0] - src2[0];
+ dst[1] = src1[1] - src2[1];
+ dst[2] = src1[2] - src2[2];
+}
+
+void
+vcopy(const float *v1, float *v2)
+{
+ register int i;
+ for (i = 0 ; i < 3 ; i++)
+ v2[i] = v1[i];
+}
+
+void
+vcross(const float *v1, const float *v2, float *cross)
+{
+ float temp[3];
+
+ temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
+ temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
+ temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
+ vcopy(temp, cross);
+}
+
+float
+vlength(const float *v)
+{
+ return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+}
+
+void
+vscale(float *v, float div)
+{
+ v[0] *= div;
+ v[1] *= div;
+ v[2] *= div;
+}
+
+void
+vnormal(float *v)
+{
+ vscale(v,1.0/vlength(v));
+}
+
+float
+vdot(const float *v1, const float *v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void
+vadd(const float *src1, const float *src2, float *dst)
+{
+ dst[0] = src1[0] + src2[0];
+ dst[1] = src1[1] + src2[1];
+ dst[2] = src1[2] + src2[2];
+}
+
+/*
+ * Ok, simulate a track-ball. Project the points onto the virtual
+ * trackball, then figure out the axis of rotation, which is the cross
+ * product of P1 P2 and O P1 (O is the center of the ball, 0,0,0)
+ * Note: This is a deformed trackball-- is a trackball in the center,
+ * but is deformed into a hyperbolic sheet of rotation away from the
+ * center. This particular function was chosen after trying out
+ * several variations.
+ *
+ * It is assumed that the arguments to this routine are in the range
+ * (-1.0 ... 1.0)
+ */
+void
+trackball(float q[4], float p1x, float p1y, float p2x, float p2y)
+{
+ float a[3]; /* Axis of rotation */
+ float phi; /* how much to rotate about axis */
+ float p1[3], p2[3], d[3];
+ float t;
+
+ if (p1x == p2x && p1y == p2y) {
+ /* Zero rotation */
+ vzero(q);
+ q[3] = 1.0;
+ return;
+ }
+
+ /*
+ * First, figure out z-coordinates for projection of P1 and P2 to
+ * deformed sphere
+ */
+ vset(p1,p1x,p1y,tb_project_to_sphere(TRACKBALLSIZE,p1x,p1y));
+ vset(p2,p2x,p2y,tb_project_to_sphere(TRACKBALLSIZE,p2x,p2y));
+
+ /*
+ * Now, we want the cross product of P1 and P2
+ */
+ vcross(p2,p1,a);
+
+ /*
+ * Figure out how much to rotate around that axis.
+ */
+ vsub(p1,p2,d);
+ t = vlength(d) / (2.0*TRACKBALLSIZE);
+
+ /*
+ * Avoid problems with out-of-control values...
+ */
+ if (t > 1.0) t = 1.0;
+ if (t < -1.0) t = -1.0;
+ phi = 2.0 * asin(t);
+
+ axis_to_quat(a,phi,q);
+}
+
+/*
+ * Given an axis and angle, compute quaternion.
+ */
+void
+axis_to_quat(float a[3], float phi, float q[4])
+{
+ vcopy(a,q);
+ vnormal(q);
+ vscale(q,sin(phi/2.0));
+ q[3] = cos(phi/2.0);
+}
+
+/*
+ * Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
+ * if we are away from the center of the sphere.
+ */
+static float
+tb_project_to_sphere(float r, float x, float y)
+{
+ float d, t, z;
+
+ d = sqrt(x*x + y*y);
+ if (d < r * 0.70710678118654752440) { /* Inside sphere */
+ z = sqrt(r*r - d*d);
+ } else { /* On hyperbola */
+ t = r / 1.41421356237309504880;
+ z = t*t / d;
+ }
+ return z;
+}
+
+/*
+ * Given two rotations, e1 and e2, expressed as quaternion rotations,
+ * figure out the equivalent single rotation and stuff it into dest.
+ *
+ * This routine also normalizes the result every RENORMCOUNT times it is
+ * called, to keep error from creeping in.
+ *
+ * NOTE: This routine is written so that q1 or q2 may be the same
+ * as dest (or each other).
+ */
+
+#define RENORMCOUNT 97
+
+void
+add_quats(float q1[4], float q2[4], float dest[4])
+{
+ static int count=0;
+ float t1[4], t2[4], t3[4];
+ float tf[4];
+
+#if 0
+printf("q1 = %f %f %f %f\n", q1[0], q1[1], q1[2], q1[3]);
+printf("q2 = %f %f %f %f\n", q2[0], q2[1], q2[2], q2[3]);
+#endif
+
+ vcopy(q1,t1);
+ vscale(t1,q2[3]);
+
+ vcopy(q2,t2);
+ vscale(t2,q1[3]);
+
+ vcross(q2,q1,t3);
+ vadd(t1,t2,tf);
+ vadd(t3,tf,tf);
+ tf[3] = q1[3] * q2[3] - vdot(q1,q2);
+
+#if 0
+printf("tf = %f %f %f %f\n", tf[0], tf[1], tf[2], tf[3]);
+#endif
+
+ dest[0] = tf[0];
+ dest[1] = tf[1];
+ dest[2] = tf[2];
+ dest[3] = tf[3];
+
+ if (++count > RENORMCOUNT) {
+ count = 0;
+ normalize_quat(dest);
+ }
+}
+
+/*
+ * Quaternions always obey: a^2 + b^2 + c^2 + d^2 = 1.0
+ * If they don't add up to 1.0, dividing by their magnitued will
+ * renormalize them.
+ *
+ * Note: See the following for more information on quaternions:
+ *
+ * - Shoemake, K., Animating rotation with quaternion curves, Computer
+ * Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985.
+ * - Pletinckx, D., Quaternion calculus as a basic tool in computer
+ * graphics, The Visual Computer 5, 2-13, 1989.
+ */
+static void
+normalize_quat(float q[4])
+{
+ int i;
+ float mag;
+
+ mag = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
+ for (i = 0; i < 4; i++) q[i] /= mag;
+}
+
+/*
+ * Build a rotation matrix, given a quaternion rotation.
+ *
+ */
+void
+build_rotmatrix(float m[4][4], float q[4])
+{
+ m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]);
+ m[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]);
+ m[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]);
+ m[0][3] = 0.0;
+
+ m[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]);
+ m[1][1]= 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]);
+ m[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]);
+ m[1][3] = 0.0;
+
+ m[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]);
+ m[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]);
+ m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]);
+ m[2][3] = 0.0;
+
+ m[3][0] = 0.0;
+ m[3][1] = 0.0;
+ m[3][2] = 0.0;
+ m[3][3] = 1.0;
+}
+
diff --git a/viewer/trackball.h b/viewer/trackball.h
@@ -0,0 +1,109 @@
+/*
+ * (c) Copyright 1993, 1994, Silicon Graphics, Inc.
+ * ALL RIGHTS RESERVED
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose and without fee is hereby granted, provided that the above
+ * copyright notice appear in all copies and that both the copyright notice
+ * and this permission notice appear in supporting documentation, and that
+ * the name of Silicon Graphics, Inc. not be used in advertising
+ * or publicity pertaining to distribution of the software without specific,
+ * written prior permission.
+ *
+ * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
+ * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ * GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
+ * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
+ * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
+ * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
+ * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
+ * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * US Government Users Restricted Rights
+ * Use, duplication, or disclosure by the Government is subject to
+ * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
+ * (c)(1)(ii) of the Rights in Technical Data and Computer Software
+ * clause at DFARS 252.227-7013 and/or in similar or successor
+ * clauses in the FAR or the DOD or NASA FAR Supplement.
+ * Unpublished-- rights reserved under the copyright laws of the
+ * United States. Contractor/manufacturer is Silicon Graphics,
+ * Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311.
+ *
+ * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
+ */
+/*
+ * trackball.h
+ * A virtual trackball implementation
+ * Written by Gavin Bell for Silicon Graphics, November 1988.
+ */
+
+void
+vzero(float *v);
+
+void
+vset(float *v, float x, float y, float z);
+
+void
+vsub(const float *src1, const float *src2, float *dst);
+
+void
+vcopy(const float *v1, float *v2);
+
+void
+vcross(const float *v1, const float *v2, float *cross);
+
+float
+vlength(const float *v);
+
+void
+vscale(float *v, float div);
+
+void
+vnormal(float *v);
+
+float
+vdot(const float *v1, const float *v2);
+
+void
+vadd(const float *src1, const float *src2, float *dst);
+
+
+/*
+ * Pass the x and y coordinates of the last and current positions of
+ * the mouse, scaled so they are from (-1.0 ... 1.0).
+ *
+ * The resulting rotation is returned as a quaternion rotation in the
+ * first paramater.
+ */
+void
+trackball(float q[4], float p1x, float p1y, float p2x, float p2y);
+
+/*
+ * Given two quaternions, add them together to get a third quaternion.
+ * Adding quaternions to get a compound rotation is analagous to adding
+ * translations to get a compound translation. When incrementally
+ * adding rotations, the first argument here should be the new
+ * rotation, the second and third the total rotation (which will be
+ * over-written with the resulting new total rotation).
+ */
+void
+add_quats(float *q1, float *q2, float *dest);
+
+/*
+ * A useful function, builds a rotation matrix in Matrix based on
+ * given quaternion.
+ */
+void
+build_rotmatrix(float m[4][4], float q[4]);
+
+/*
+ * This function computes a quaternion based on an axis (defined by
+ * the given vector) and an angle about which to rotate. The angle is
+ * expressed in radians. The result is put into the third argument.
+ */
+void
+axis_to_quat(float a[3], float phi, float q[4]);
+
diff --git a/viewer/viewer.cxx b/viewer/viewer.cxx
@@ -3,12 +3,16 @@
#include <iostream>
#include <vector>
-#include <GL/glew.h>
-#include <GL/freeglut.h>
-
#include "af_unix.h"
#include "text.h"
+#include "demo-buffer.h"
+#include "demo-font.h"
+#include "demo-view.h"
+
+demo_glstate_t *st;
+demo_view_t *vu;
+demo_buffer_t *buffer;
class window {
public:
@@ -20,7 +24,13 @@ private:
static std::vector<drawable*> drawables;
static af_unix socket;
static void display();
+ static void reshape_func(int, int);
+ static void keyboard_func(unsigned char, int, int);
+ static void special_func(int, int, int);
+ static void mouse_func(int, int, int, int);
+ static void motion_func(int, int);
static void idle();
+
};
// fuckin c++
@@ -30,55 +40,89 @@ af_unix window::socket;
window::window(int argc, char *argv[])
{
glutInit(&argc, argv);
- glutInitContextVersion(2, 0);
- glutInitDisplayMode(GLUT_RGB);
glutInitWindowSize(1600, 1200);
- glutCreateWindow("Basic Text");
+ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
+ int window = glutCreateWindow("Source Code Visualizer");
+ glutReshapeFunc(reshape_func);
+ glutDisplayFunc(display);
+ glutKeyboardFunc(keyboard_func);
+ glutSpecialFunc(special_func);
+ glutMouseFunc(mouse_func);
+ glutMotionFunc(motion_func);
+ glutIdleFunc(idle);
GLenum glew_status = glewInit();
-
if (GLEW_OK != glew_status)
errx(1, "%s", glewGetErrorString(glew_status));
-
- if (!GLEW_VERSION_2_0)
+ if (!glewIsSupported("GL_VERSION_2_0"))
errx(1, "No support for OpenGL 2.0 found");
- glutDisplayFunc(window::display);
- glutIdleFunc(idle);
+ st = demo_glstate_create();
+ vu = demo_view_create(st);
+ demo_view_print_help(vu);
+
+ FT_Library ft_library;
+ FT_Init_FreeType(&ft_library);
+
+ FT_Face ft_face = NULL;
+ FT_New_Face(ft_library, "DejaVuSansMono.ttf", /* face_index */ 0, &ft_face);
+
+ demo_font_t *font = demo_font_create(ft_face, demo_glstate_get_atlas(st));
+
+ buffer = demo_buffer_create();
+ glyphy_point_t top_left = { 0, 0 };
+ demo_buffer_move_to(buffer, &top_left);
+ const char *the_text = ">>>>>>> HELLO WORLD <<<<<<<<";
+ demo_buffer_add_text(buffer, the_text, font, 1);
+
+ demo_font_print_stats(font);
+
+ demo_view_setup(vu);
// This creates the socket with SOCK_NONBLOCK
socket.set_listen();
}
void
-window::start()
+window::reshape_func(int width, int height)
{
- glutMainLoop();
+ demo_view_reshape_func(vu, width, height);
}
void
-window::add(drawable &d)
+window::keyboard_func(unsigned char key, int x, int y)
{
- drawables.push_back(&d);
+ demo_view_keyboard_func(vu, key, x, y);
}
void
-window::display(void)
+window::special_func(int key, int x, int y)
{
- /* White background */
- glClearColor(0, 0, 0, 1);
- glClear(GL_COLOR_BUFFER_BIT);
+ demo_view_special_func(vu, key, x, y);
+}
- /* Enable blending, necessary for our alpha texture */
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+void
+window::mouse_func(int button, int state, int x, int y)
+{
+ demo_view_mouse_func(vu, button, state, x, y);
+}
- for (auto &d : drawables)
- d->draw();
+void
+window::motion_func(int x, int y)
+{
+ demo_view_motion_func(vu, x, y);
+}
- std::cerr << "window__display" << std::endl;
+void
+window::start()
+{
+ glutMainLoop();
+}
- glutSwapBuffers();
+void
+window::display(void)
+{
+ demo_view_display(vu, buffer);
}
void