Trammell Hudson avatar Trammell Hudson committed 3eef295

started porting wireframe rendering from inPulse tree

Comments (0)

Files changed (15)

 # Makefile for Pebble watch projects.
 #
 # The makefile should define:
-# 	OBJS with a list of source files.
+# 	SRCS with a list of source files.
 #	APP := project to build
 #	FONTS := list of fonts to bundle
 #
 	-fPIE \
 	-I$(PEBBLE_HOME)/include \
 	-I. \
+	-I../../common \
 	-I$O \
 	-DNDEBUG \
 	-Wp,-MMD,$(dir $@).$(notdir $@).d \
 
 all: $O/$(APP).pbw
 
+#
+# Generate the list of object files from the SRCS list.
+# Every object requires that the resource_ids.auto.h be up to date
+#
+OBJS :=
+add_srcs = \
+	$(eval $2: $1) \
+	$(eval OBJS += $2) \
+
+$(foreach f,$(SRCS),$(call add_srcs,$f,$O/$(notdir $(f:.c=.o))))
+
+$(OBJS): $O/src/resource_ids.auto.h
+#$(eval $(info OBJS=$(OBJS)))
+
 $O/$(APP).pbw: $O/app_resources.pbpack
-$O/$(APP).elf: $(addprefix $O/,$(OBJS))
+$O/$(APP).elf: $(OBJS)
 	$(LD) \
 		$(LDFLAGS) \
 		-o $@ \
 		$^ \
 		$(LDLIBS) \
 
-$O/%.o: %.c
+$O/%.o:
 	$(CC) $(CFLAGS) -c -o $@ $<
 
 
 		'{"friendlyVersion":"'$(VERSION)'","versionDefName":"RESOURCES"}'
 
 
-#
-# Every object requires that the resource_ids.auto.h be up to date
-#
-$(addprefix $O/,$(OBJS)): $O/src/resource_ids.auto.h
-
 clean:
 	$(RM) \
 		$O/*.{o,a,pbw,pfo,elf,bin,map} \
+/** \file
+ * Format integers
+ */
+#include "fmt.h"
+
+static char
+hexdigit(
+	uint8_t x
+)
+{
+	if (x <= 9)
+		return '0' + x - 0;
+	else
+	if (x <= 15)
+		return 'A' + x - 0xA;
+	else
+		return '?';
+}
+
+
+unsigned
+fmt_i16(
+	char * buf,
+	int16_t v_in,
+	int base
+)
+{
+	uint16_t v = v_in;
+
+	if (v_in < 0)
+	{
+		buf[0] = '-';
+		v = -v_in;
+	} else
+	if (v_in > 0)
+		buf[0] = '+';
+	else
+		buf[0] = ' ';
+
+	if (base == 10)
+	{
+		buf[5] = hexdigit(v % 10); v /= 10;
+		buf[4] = hexdigit(v % 10); v /= 10;
+		buf[3] = hexdigit(v % 10); v /= 10;
+		buf[2] = hexdigit(v % 10); v /= 10;
+		buf[1] = hexdigit(v % 10); v /= 10;
+		return 6;
+	} else
+	if (base == 16)
+	{
+		buf[4] = hexdigit(v % 16); v /= 16;
+		buf[3] = hexdigit(v % 16); v /= 16;
+		buf[2] = hexdigit(v % 16); v /= 16;
+		buf[1] = hexdigit(v % 16);
+		return 5;
+	}
+
+	buf[1] = '?';
+	return 2;
+}
+
+
+unsigned
+fmt_i32(
+	char * buf,
+	int32_t v_in,
+	int base
+)
+{
+	uint32_t v = v_in;
+
+	if (v_in < 0)
+	{
+		buf[0] = '-';
+		v = -v_in;
+	} else
+	if (v_in > 0)
+		buf[0] = '+';
+	else
+		buf[0] = ' ';
+
+	if (base == 10)
+	{
+		buf[10] = hexdigit(v % 10); v /= 10;
+		buf[9] = hexdigit(v % 10); v /= 10;
+		buf[8] = hexdigit(v % 10); v /= 10;
+		buf[7] = hexdigit(v % 10); v /= 10;
+		buf[6] = hexdigit(v % 10); v /= 10;
+		buf[5] = hexdigit(v % 10); v /= 10;
+		buf[4] = hexdigit(v % 10); v /= 10;
+		buf[3] = hexdigit(v % 10); v /= 10;
+		buf[2] = hexdigit(v % 10); v /= 10;
+		buf[1] = hexdigit(v % 10); v /= 10;
+		return 11;
+	} else
+	if (base == 16)
+	{
+		buf[8] = hexdigit(v % 16); v /= 16;
+		buf[7] = hexdigit(v % 16); v /= 16;
+		buf[6] = hexdigit(v % 16); v /= 16;
+		buf[5] = hexdigit(v % 16);
+		buf[4] = hexdigit(v % 16); v /= 16;
+		buf[3] = hexdigit(v % 16); v /= 16;
+		buf[2] = hexdigit(v % 16); v /= 16;
+		buf[1] = hexdigit(v % 16);
+		return 9;
+	}
+
+	buf[1] = '?';
+	return 2;
+}
+#ifndef _fmt_h_
+#define _fmt_h_
+
+#include <stdint.h>
+
+unsigned
+fmt_i16(
+	char * buf,
+	int16_t v,
+	int base
+);
+
+unsigned
+fmt_i32(
+	char * buf,
+	int32_t v,
+	int base
+);
+
+#endif
+3D cubes!

cube/src/camera.c

+#include <stdint.h>
+#include <math.h>
+#include <pebble_os.h>
+#include "draw.h"
+#include "camera.h"
+
+/* sin_lookup() takes deg * 32768 / 90.
+ * returns -65536 to 65536
+ */
+static const int scale = 65536;
+
+void
+camera_setup(
+	camera_t * c,
+	int32_t eye_z,
+	int32_t a[3]
+)
+{
+	int32_t sx = sin_lookup(a[0]);
+	int32_t cx = cos_lookup(a[0]);
+	int32_t sy = sin_lookup(a[1]);
+	int32_t cy = cos_lookup(a[1]);
+	int32_t sz = sin_lookup(a[2]);
+	int32_t cz = cos_lookup(a[2]);
+
+	c->r[0][0] = ( cy * cz) / scale;
+	c->r[0][1] = ((-cy * sz) + (sx * sy * cz) / scale) / scale;
+	c->r[0][2] = (( sx * sz) + (cx * sy * cz) / scale) / scale;
+
+	c->r[1][0] = ( cx * sz) / scale;
+	c->r[1][1] = (( cx * cz) + (sx * sy * sz) / scale) / scale;
+	c->r[1][2] = ((-sx * cz) + (cx * sy * sz) / scale) / scale;
+
+	c->r[2][0] = (-sy);
+	c->r[2][1] = ( sx * cy) / scale;
+	c->r[2][2] = ( cx * cy) / scale;
+
+	c->eye_z = eye_z;
+}
+
+
+int
+camera_project(
+	const camera_t * c,
+	const vertex_t * const v,
+	pixel_t * const pixel
+)
+{
+	int32_t p[3] = { 0, 0, c->eye_z * scale };
+	for (int i = 0 ; i < 3 ; i++)
+		for (int j = 0 ; j < 3 ; j++)
+			p[i] += c->r[i][j] * v->x[j];
+
+	if (p[2] <= 0)
+	{
+		// The point is behind us
+		pixel->x = pixel->y = -1;
+		return 0;
+	}
+
+	// Smaller == wider angle view
+	const int32_t zoom = 1;
+
+	// Transform to screen coordinate frame,
+	// limiting to the actual boundaries
+	int32_t px = (p[1] * c->eye_z * zoom) / p[2] + VSCREEN_WIDTH / 2;
+	if (px < 0)
+		px = 0;
+	if (px >= VSCREEN_WIDTH-1)
+		px = VSCREEN_WIDTH-1;
+	pixel->x = px;
+
+	int32_t py = (p[0] * c->eye_z * zoom) / p[2] + VSCREEN_HEIGHT / 2;
+	if (py < 0)
+		py = 0;
+	if (py >= VSCREEN_HEIGHT-1)
+		py = VSCREEN_HEIGHT-1;
+	pixel->y = py;
+
+	return 1;
+}

cube/src/camera.h

+/** \file
+ * Camera projection transformations.
+ */
+#ifndef _camera_h_
+#define _camera_h_
+
+#include <stdint.h>
+#include "coords.h"
+
+typedef struct
+{
+	int32_t eye_z;
+	int8_t r[3][3];
+} camera_t ;
+
+
+/** Initialize the camera to be positioned at x and with euler angles a.
+ */
+void
+camera_setup(
+	camera_t * c,
+	int32_t eye_z,
+	int32_t a[3]
+);
+
+
+
+/** Project a 3D point to the 2D image.
+ * Returns 0 if the point is behind us and should not be drawn.
+ */
+int
+camera_project(
+	const camera_t * c,
+	const vertex_t * x,
+	pixel_t * pixel_out
+);
+
+#endif

cube/src/coords.h

+/** \file
+ * Coordinate systems used in the wireframe and camera framework.
+ */
+#ifndef _coords_h_
+#define _coords_h_
+
+#include <stdint.h>
+
+
+typedef struct
+{
+	int8_t x[3];
+} __attribute__((__packed__))
+vertex_t;
+
+
+typedef struct
+{
+	uint8_t v0;
+	uint8_t v1;
+} __attribute__((__packed__))
+edge_t;
+
+
+typedef struct
+{
+	uint8_t x;
+	uint8_t y;
+	//int8_t z; // depth, right now -1 == behind the camera, 1 == infront
+} __attribute__((__packed__))
+pixel_t;
+
+#endif
+/** \file
+ * Show off the 3D accelerometer data.
+ */
+#include "pebble_os.h"
+#include "pebble_app.h"
+#include "pebble_fonts.h"
+
+PBL_APP_INFO(
+	"Accelerometer",
+	"hudson",
+	1, // Version
+	INVALID_RESOURCE,
+	APP_INFO_WATCH_FACE
+);
+
+
+static Window window;
+static TextLayer time_layer;
+static Layer layer;
+
+static void
+bargraph_layer_update(
+	Layer * const me,
+	GContext * ctx
+)
+{
+/*
+	// Draw a white rectangle across the entire bottom
+	const unsigned w = me->bounds.size.w;
+	const unsigned h = me->bounds.size.h;
+	graphics_context_set_fill_color(ctx, GColorWhite);
+	graphics_fill_rect(ctx, GRect(0, 0, w, h), 0, GCornersAll);
+
+	graphics_context_set_fill_color(ctx, GColorBlack);
+	graphics_fill_rect(ctx, GRect(0, 0, (w * remaining) / 30, h), 4, GCornersRight);
+*/
+}
+
+
+static unsigned
+fmt_i16(
+	char * buf,
+	int16_t v_in
+)
+{
+	uint16_t v = v_in;
+
+	if (v_in < 0)
+	{
+		buf[0] = '-';
+		v = -v_in;
+	} else
+	if (v_in > 0)
+		buf[0] = '+';
+	else
+		buf[0] = ' ';
+
+	buf[5] = '0' + v % 10; v /= 10;
+	buf[4] = '0' + v % 10; v /= 10;
+	buf[3] = '0' + v % 10; v /= 10;
+	buf[2] = '0' + v % 10; v /= 10;
+	buf[1] = '0' + v % 10;
+
+	return 6;
+}
+
+
+static void
+handle_tick(
+	AppContextRef ctx,
+	PebbleTickEvent * const event
+)
+{
+	(void) ctx;
+	const PblTm * const ptm = event->tick_time;
+	static char time_buffer[32];
+	unsigned off = 0;
+
+	struct AccelData accel = { 12345, 9999, -9999 };
+	accel_get_xyz(&accel);
+	off += fmt_i16(time_buffer + off, accel.x);
+	time_buffer[off++] = '\n';
+	off += fmt_i16(time_buffer + off, accel.y);
+	time_buffer[off++] = '\n';
+	off += fmt_i16(time_buffer + off, accel.z);
+	time_buffer[off++] = '\n';
+
+	string_format_time(
+		time_buffer + off,
+		sizeof(time_buffer) - off,
+		"%H:%M:%S",
+		ptm
+	);
+
+	text_layer_set_text(&time_layer, time_buffer);
+}
+
+
+static void
+handle_init(
+	AppContextRef ctx
+)
+{
+	(void) ctx;
+
+	window_init(&window, "Main");
+	window_stack_push(&window, true);
+
+	text_layer_init(&time_layer, GRect(0,0,144,168));
+	text_layer_set_text(&time_layer, "");
+	text_layer_set_font(&time_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18));
+	layer_add_child(&window.layer, &time_layer.layer);
+
+/*
+	// Bargraph layer is across the bottom
+	const unsigned bargraph_height = 20;
+	layer_init(&bargraph_layer, GRect(0,168-bargraph_height-15,144,bargraph_height));
+	bargraph_layer.update_proc = bargraph_layer_update;
+
+	layer_add_child(&window.layer, &bargraph_layer);
+*/
+}
+
+
+void
+pbl_main(
+	void * const params
+)
+{
+	PebbleAppHandlers handlers = {
+		.init_handler	= &handle_init,
+		.tick_info	= {
+			.tick_handler = &handle_tick,
+			.tick_units = SECOND_UNIT,
+		},
+	};
+
+	app_event_loop(params, &handlers);
+}
+/** \file
+ * Vertex and edge definition for a cube
+ */
+#ifndef EDGE_COLOR
+#define EDGE_COLOR { 0, 0xFF, 0, 0 }
+#endif
+
+static const vertex_t vertices[] = {
+	{ { -50, -50, -50 } },
+	{ { -50, -50,  50 } },
+	{ { -50,  50, -50 } },
+	{ { -50,  50,  50 } },
+	{ {  50, -50, -50 } },
+	{ {  50, -50,  50 } },
+	{ {  50,  50, -50 } },
+	{ {  50,  50,  50 } },
+};
+static const edge_t edges[] = {
+	{ 0, 1 },
+	{ 0, 2 },
+	{ 0, 4 },
+	{ 1, 3 },
+	{ 1, 5 },
+	{ 2, 3 },
+	{ 2, 6 },
+	{ 3, 7 },
+	{ 4, 5 },
+	{ 4, 6 },
+	{ 5, 7 },
+	{ 6, 7 },
+};
+/** \file
+ * Drawing functions.
+ */
+#include "draw.h"
+#include "mathutil.h"
+
+void
+draw_pixel(
+	color24_t c,
+	uint32_t x,
+	uint32_t y
+)
+{
+	if (x >= VSCREEN_WIDTH || y >= VSCREEN_HEIGHT)
+		return;
+
+	x >>= VSCREEN_SHIFT;
+	y >>= VSCREEN_SHIFT;
+	pulse_set_draw_window(x, y, x, y);
+	pulse_draw_point24(c);
+}
+
+
+void
+draw_line(
+	color24_t c,
+	uint32_t x0,
+	uint32_t y0,
+	uint32_t x1,
+	uint32_t y1
+)
+{
+#if 0
+	const int32_t dx = abs(x1 - x0);
+	const int32_t dy = abs(y1 - y0);
+
+	const int32_t sx = x0 < x1 ? 1 : -1;
+	const int32_t sy = y0 < y1 ? 1 : -1;
+	int32_t err = dx - dy;
+
+#if VSCREEN_SHIFT != 0
+	uint32_t last_px = -1;
+	uint32_t last_py = -1;
+#endif
+
+	while(1)
+	{
+#if VSCREEN_SHIFT != 0
+		uint32_t px = x0 >> VSCREEN_SHIFT;
+		uint32_t py = y0 >> VSCREEN_SHIFT;
+		if (px != last_px || py != last_py)
+		{
+			last_px = px;
+			last_py = py;
+			draw_pixel(c, x0, y0);
+		}
+#else
+		draw_pixel(c, x0, y0);
+#endif
+
+		if (x0 == x1 && y0 == y1)
+			break;
+		int32_t e2 = 2 * err;
+		if (e2 > -dy)
+		{
+			err -= dy;
+			x0 += sx;
+		}
+		if (e2 < +dx)
+		{
+			err += dx;
+			y0 += sy;
+		}
+	}
+#endif
+}
+
+static char buf[2];
+struct PWidgetTextDynamic font;
+
+void
+draw_monostring(
+	uint32_t x,
+	uint32_t y,
+	color24_t color,
+	const char * s
+)
+{
+	pulse_init_dynamic_text_widget(
+		&font,
+		buf,
+		FONT,
+		color,
+		PWTS_TRUNCATE
+	);
+
+	uint32_t orig_x = x >> VSCREEN_SHIFT;
+
+	struct PWTextBox box = {
+		.left		= orig_x,
+		.top		= y >> VSCREEN_SHIFT,
+		.right		= SCREEN_WIDTH - 1,
+		.bottom		= SCREEN_HEIGHT - 1,
+	};
+
+	buf[1] = '\0';
+
+	while ((buf[0] = *s++))
+	{
+		if (buf[0] == '\n')
+		{
+			box.left = orig_x;
+			box.top += FONT_HEIGHT;
+		} else {
+			pulse_render_text(&box, &font);
+			box.left += FONT_WIDTH;
+		}
+	}
+}
+/** \file
+ * Line and pixel drawing routines.
+ * Using a virtual screen buffer that is twice the resolution
+ * along each axis so that anti-aliasing can be done later.
+ */
+#ifndef _draw_h_
+
+#define SCREEN_WIDTH 144
+#define SCREEN_HEIGHT 168
+
+#define VSCREEN_SHIFT 1
+#define VSCREEN_WIDTH (SCREEN_WIDTH << VSCREEN_SHIFT)
+#define VSCREEN_HEIGHT (SCREEN_HEIGHT << VSCREEN_SHIFT)
+
+
+/* Draw a pixel in vscreen coordinates */
+void
+draw_pixel(
+	int c,
+	int x,
+	int y
+);
+
+
+/* Draw a non-antialised line using Bresenham's_line_algorithm
+ * in vscreen coordinates */
+void
+draw_line(
+	int c,
+	int x0,
+	int y0,
+	int x1,
+	int y1
+);
+
+
+#endif

cube/src/mathutil.h

+/** \file
+ * Math utility functions
+ */
+#ifndef _mathutil_h_
+#define _mathutil_h_
+
+#include <stdint.h>
+
+static inline int32_t
+iabs(
+	int32_t x
+)
+{
+	if (x < 0)
+		return -x;
+	return x;
+}
+
+
+static inline uint32_t
+limit(
+	int32_t x,
+	int32_t min,
+	int32_t max
+)
+{
+	if (x < min)
+		return min;
+	if (x > max)
+		return max;
+	return x;
+}
+
+
+#endif

cube/src/teatime.c

+/** \file
+ * Show off the 3D accelerometer data.
+ */
+#include <pebble_os.h>
+#include <pebble_app.h>
+#include <pebble_fonts.h>
+#include "draw.h"
+#include "mathutil.h"
+#include "camera.h"
+#include "wireframe.h"
+#include "fmt.h"
+
+PBL_APP_INFO(
+	"Teatime",
+	"hudson",
+	1, // Version
+	INVALID_RESOURCE,
+	APP_INFO_WATCH_FACE
+);
+
+
+static Window window;
+static TextLayer time_layer;
+static Layer layer;
+
+static camera_t camera;
+static int32_t euler[3];
+
+// Bring in a model to render
+//#include "teapot.h"
+#include "cube.h"
+
+#define ARRAY_COUNT(x) (sizeof(x) / sizeof(*x))
+static pixel_t pixels[ARRAY_COUNT(vertices)];
+
+
+static void
+layer_update(
+	Layer * const me,
+	GContext * ctx
+)
+{
+	euler[0] += 1;
+	static uint8_t t;
+	if (1 || (t++) % 30 == 0)
+	{
+		euler[1] += 1024;
+		//if (euler[1]++ == 64)
+			//euler[1] = -63;
+	}
+
+	camera_setup(&camera, 30000, euler);
+
+	// Fill in the screen with black
+	const unsigned w = me->bounds.size.w;
+	const unsigned h = me->bounds.size.h;
+	graphics_context_set_fill_color(ctx, GColorBlack);
+	graphics_fill_rect(ctx, GRect(0, t, 12, 12), 0, GCornersAll);
+	//graphics_context_set_fill_color(ctx, GColorWhite);
+
+#if 1
+	pixel_t temp_pixels[ARRAY_COUNT(pixels)];
+	wireframe_draw(
+		ctx,
+		&camera,
+		ARRAY_COUNT(vertices),
+		vertices,
+		ARRAY_COUNT(edges),
+		edges,
+		pixels,
+		temp_pixels
+	);
+#else
+	for (int i = 0 ; i < ARRAY_COUNT(vertices) ; i++)
+	{
+		const vertex_t * const v = &vertices[i];
+		pixel_t * const p = &pixels[i];
+		draw_pixel(COLOR_BLACK24, p->x, p->y);
+		camera_project(&camera, v, p);
+		draw_pixel(COLOR_GREEN, p->x, p->y);
+	}
+#endif
+}
+
+
+static void
+handle_tick(
+	AppContextRef ctx,
+	PebbleTickEvent * const event
+)
+{
+	(void) ctx;
+	const PblTm * const ptm = event->tick_time;
+	static char time_buffer[64];
+	unsigned off = 0;
+
+/*
+	for (int i = 0 ; i < 8 ; i ++)
+	{
+		off += fmt_i32(time_buffer + off, sin_lookup(8192*i), 10);
+		time_buffer[off++] = '\n';
+	}
+*/
+
+	string_format_time(
+		time_buffer + off,
+		sizeof(time_buffer) - off,
+		"%H:%M:%S",
+		ptm
+	);
+
+	text_layer_set_text(&time_layer, time_buffer);
+	layer_mark_dirty(&layer);
+}
+
+
+static void
+handle_init(
+	AppContextRef ctx
+)
+{
+	(void) ctx;
+
+	window_init(&window, "Main");
+	window_stack_push(&window, true);
+
+	text_layer_init(&time_layer, GRect(0,0,144,168));
+	text_layer_set_text(&time_layer, "");
+	text_layer_set_font(&time_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18));
+	layer_add_child(&window.layer, &time_layer.layer);
+
+	// Bargraph layer is across the bottom
+	layer_init(&layer, GRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT));
+	layer.update_proc = layer_update;
+
+	layer_add_child(&window.layer, &layer);
+}
+
+
+void
+pbl_main(
+	void * const params
+)
+{
+	PebbleAppHandlers handlers = {
+		.init_handler	= &handle_init,
+		.tick_info	= {
+			.tick_handler = &handle_tick,
+			.tick_units = SECOND_UNIT,
+		},
+	};
+
+	app_event_loop(params, &handlers);
+}

cube/src/wireframe.c

+/** \file
+ * Wireframe drawing routine.
+ */
+#include "wireframe.h"
+#include "draw.h"
+#include <string.h>
+
+void
+wireframe_draw(
+	GContext * ctx,
+	const camera_t * camera,
+	const int num_vertices,
+	const vertex_t * vertices,
+	const int num_edges,
+	const edge_t * edges,
+	pixel_t * pixels,
+	pixel_t * temp
+)
+{
+	for (int i = 0 ; i < num_vertices ; i++)
+		camera_project(camera, &vertices[i], &temp[i]);
+	
+	for (int i = 0 ; i < num_edges ; i++)
+	{
+		const edge_t * const e = &edges[i];
+		const pixel_t * old_p0 = &pixels[e->v0];
+		const pixel_t * old_p1 = &pixels[e->v1];
+
+/*
+		if (old_p0->x != (uint8_t) -1 && old_p1->x != (uint8_t) -1)
+			graphics_draw_line(
+				ctx,
+				old_p0->x, old_p0->y,
+				old_p1->x, old_p1->y
+			);
+*/
+
+		const pixel_t * new_p0 = &temp[e->v0];
+		const pixel_t * new_p1 = &temp[e->v1];
+
+		if (new_p0->x != (uint8_t) -1
+		&&  new_p1->x != (uint8_t) -1)
+			graphics_draw_line(
+				ctx,
+				GPoint(new_p0->x, new_p0->y),
+				GPoint(new_p1->x, new_p1->y)
+			);
+	}
+
+	// Now copy the new locations to the permanent place
+	memcpy(pixels, temp, num_vertices * sizeof(*pixels));
+}

cube/src/wireframe.h

+/** \file
+ * 3D Wireframe structures.
+ *
+ * A list of vertices in model frame and a list of edges connecting
+ * those vertices.
+ *
+ * Map the projection of each vertex into screen space, un-draw the
+ * old edges and then draw the new edges.
+ */
+#ifndef _wireframe_h_
+#define _wireframe_h_
+
+#include <pebble_os.h>
+#include "camera.h"
+#include "coords.h"
+
+
+void
+wireframe_draw(
+	GContext * ctx,
+	const camera_t * camera,
+	const int num_vertex,
+	const vertex_t * v,
+	const int num_edges,
+	const edge_t * e,
+	pixel_t * screen_pixels,
+	pixel_t * temp
+);
+
+
+#endif
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.