cubemapper

changeset 0:8fc9e1d3aad2

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 27 Jul 2017 20:36:12 +0300
parents
children d7a29cb7ac8d
files .hgignore Makefile src/app.cc src/app.h src/geom.cc src/geom.h src/main.cc src/mesh.cc src/mesh.h src/meshgen.cc src/meshgen.h src/opengl.cc src/opengl.h src/texture.cc src/texture.h
diffstat 15 files changed, 3457 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/.hgignore	Thu Jul 27 20:36:12 2017 +0300
     1.3 @@ -0,0 +1,12 @@
     1.4 +\.o$
     1.5 +\.d$
     1.6 +\.swp$
     1.7 +^data/
     1.8 +^Debug/
     1.9 +^Release/
    1.10 +\.sdf$
    1.11 +\.opensdf$
    1.12 +\.suo$
    1.13 +\.jpg$
    1.14 +\.png$
    1.15 +^cubemapper$
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/Makefile	Thu Jul 27 20:36:12 2017 +0300
     2.3 @@ -0,0 +1,38 @@
     2.4 +# -- options --
     2.5 +PREFIX = /usr/local
     2.6 +opt = -O0
     2.7 +dbg = -g
     2.8 +# -------------
     2.9 +
    2.10 +src = $(wildcard src/*.cc)
    2.11 +obj = $(src:.cc=.o)
    2.12 +dep = $(obj:.o=.d)
    2.13 +bin = cubemapper
    2.14 +
    2.15 +CXXFLAGS = -pedantic -Wall $(opt) $(dbg) $(inc)
    2.16 +LDFLAGS = $(libs) $(libgl_$(sys)) -lm
    2.17 +
    2.18 +sys = $(shell uname -s)
    2.19 +libgl_Linux = -lGL -lGLU -lglut -lGLEW
    2.20 +libgl_Darwin = -framework OpenGL -framework GLUT -lGLEW
    2.21 +
    2.22 +libs = -limago -lgmath
    2.23 +
    2.24 +$(bin): $(obj)
    2.25 +	$(CXX) -o $@ $(obj) $(LDFLAGS)
    2.26 +
    2.27 +-include $(dep)
    2.28 +
    2.29 +%.d: %.c
    2.30 +	@$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@
    2.31 +
    2.32 +%.d: %.cc
    2.33 +	@$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@
    2.34 +
    2.35 +.PHONY: clean
    2.36 +clean:
    2.37 +	rm -f $(obj) $(bin)
    2.38 +
    2.39 +.PHONY: cleandep
    2.40 +cleandep:
    2.41 +	rm -f $(dep)
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/src/app.cc	Thu Jul 27 20:36:12 2017 +0300
     3.3 @@ -0,0 +1,243 @@
     3.4 +#include <stdio.h>
     3.5 +#include <stdlib.h>
     3.6 +#include <string.h>
     3.7 +#include <math.h>
     3.8 +#include <assert.h>
     3.9 +#include <imago2.h>
    3.10 +#include "app.h"
    3.11 +#include "opengl.h"
    3.12 +#include "texture.h"
    3.13 +#include "mesh.h"
    3.14 +#include "meshgen.h"
    3.15 +
    3.16 +static void draw_scene();	// both near and infinite parts
    3.17 +static void draw_scene_near();	// near scene: regular objects affected by parallax shift and translation
    3.18 +// infinity scene: objects conceptually at infinity, not affected by parallax shift and translation
    3.19 +static void draw_scene_inf();
    3.20 +static bool parse_args(int argc, char **argv);
    3.21 +
    3.22 +static const char *img_fname;
    3.23 +static float cam_theta, cam_phi;
    3.24 +
    3.25 +static Texture *pano_tex;
    3.26 +static Mesh *pano_mesh;
    3.27 +
    3.28 +static int win_width, win_height;
    3.29 +
    3.30 +
    3.31 +bool app_init(int argc, char **argv)
    3.32 +{
    3.33 +	if(!parse_args(argc, argv)) {
    3.34 +		return false;
    3.35 +	}
    3.36 +	if(!img_fname) {
    3.37 +		fprintf(stderr, "please specify an equilateral panoramic image\n");
    3.38 +		return false;
    3.39 +	}
    3.40 +
    3.41 +	if(!init_opengl()) {
    3.42 +		return false;
    3.43 +	}
    3.44 +
    3.45 +	glEnable(GL_CULL_FACE);
    3.46 +
    3.47 +	if(GLEW_ARB_framebuffer_sRGB) {
    3.48 +		glGetError();	// discard previous errors
    3.49 +		glEnable(GL_FRAMEBUFFER_SRGB);
    3.50 +		if(glGetError() != GL_NO_ERROR) {
    3.51 +			fprintf(stderr, "failed to enable sRGB framebuffer\n");
    3.52 +		}
    3.53 +	}
    3.54 +
    3.55 +	Mesh::use_custom_sdr_attr = false;
    3.56 +	pano_mesh = new Mesh;
    3.57 +	gen_sphere(pano_mesh, 1.0, 80, 40);
    3.58 +	pano_mesh->flip();
    3.59 +	Mat4 xform;
    3.60 +	xform.rotation_y(-M_PI / 2.0);	// rotate the sphere to face the "front" part of the image
    3.61 +	pano_mesh->apply_xform(xform, xform);
    3.62 +
    3.63 +	xform.scaling(-1, 1, 1);		// flip horizontal texcoord since we're inside the sphere
    3.64 +	pano_mesh->texcoord_apply_xform(xform);
    3.65 +
    3.66 +	pano_tex = new Texture;
    3.67 +	if(!pano_tex->load(img_fname)) {
    3.68 +		return false;
    3.69 +	}
    3.70 +	return true;
    3.71 +}
    3.72 +
    3.73 +void app_cleanup()
    3.74 +{
    3.75 +	delete pano_mesh;
    3.76 +	delete pano_tex;
    3.77 +}
    3.78 +
    3.79 +void app_draw()
    3.80 +{
    3.81 +	glClear(GL_COLOR_BUFFER_BIT);
    3.82 +
    3.83 +	Mat4 view_matrix;
    3.84 +	view_matrix.pre_rotate_x(deg_to_rad(cam_phi));
    3.85 +	view_matrix.pre_rotate_y(deg_to_rad(cam_theta));
    3.86 +
    3.87 +	glMatrixMode(GL_MODELVIEW);
    3.88 +	glLoadMatrixf(view_matrix[0]);
    3.89 +
    3.90 +	draw_scene();
    3.91 +
    3.92 +	app_swap_buffers();
    3.93 +	assert(glGetError() == GL_NO_ERROR);
    3.94 +}
    3.95 +
    3.96 +void render_cubemap()
    3.97 +{
    3.98 +	int fbsize = win_width < win_height ? win_width : win_height;
    3.99 +	float *pixels = new float[fbsize * fbsize * 3];
   3.100 +
   3.101 +	glViewport(0, 0, fbsize, fbsize);
   3.102 +
   3.103 +	Mat4 viewmat[6];
   3.104 +	viewmat[0].rotation_y(deg_to_rad(90));	// +X
   3.105 +	viewmat[1].rotation_x(deg_to_rad(-90));	// +Y
   3.106 +	viewmat[2].rotation_y(deg_to_rad(180));	// +Z
   3.107 +	viewmat[3].rotation_y(deg_to_rad(-90));	// -X
   3.108 +	viewmat[4].rotation_x(deg_to_rad(90));	// -Y
   3.109 +
   3.110 +	static const char *fname[] = {
   3.111 +		"cubemap_px.jpg",
   3.112 +		"cubemap_py.jpg",
   3.113 +		"cubemap_pz.jpg",
   3.114 +		"cubemap_nx.jpg",
   3.115 +		"cubemap_ny.jpg",
   3.116 +		"cubemap_nz.jpg"
   3.117 +	};
   3.118 +
   3.119 +	glMatrixMode(GL_PROJECTION);
   3.120 +	glLoadIdentity();
   3.121 +	gluPerspective(45, 1.0, 0.5, 500.0);
   3.122 +
   3.123 +	for(int i=0; i<6; i++) {
   3.124 +		glClear(GL_COLOR_BUFFER_BIT);
   3.125 +
   3.126 +		glMatrixMode(GL_MODELVIEW);
   3.127 +		glLoadMatrixf(viewmat[i][0]);
   3.128 +
   3.129 +		draw_scene();
   3.130 +
   3.131 +		glReadPixels(0, 0, fbsize, fbsize, GL_RGB, GL_FLOAT, pixels);
   3.132 +		if(img_save_pixels(fname[i], pixels, fbsize, fbsize, IMG_FMT_RGBF) == -1) {
   3.133 +			fprintf(stderr, "failed to save %dx%d image: %s\n", fbsize, fbsize, fname[i]);
   3.134 +			break;
   3.135 +		}
   3.136 +	}
   3.137 +
   3.138 +	glViewport(0, 0, win_width, win_height);
   3.139 +
   3.140 +	delete [] pixels;
   3.141 +}
   3.142 +
   3.143 +// both near and infinite parts (see below)
   3.144 +static void draw_scene()
   3.145 +{
   3.146 +	draw_scene_inf();
   3.147 +	draw_scene_near();
   3.148 +}
   3.149 +
   3.150 +// infinity scene: objects conceptually at infinity, not affected by parallax shift and translation
   3.151 +static void draw_scene_inf()
   3.152 +{
   3.153 +	pano_tex->bind();
   3.154 +	glEnable(GL_TEXTURE_2D);
   3.155 +	pano_mesh->draw();
   3.156 +	glDisable(GL_TEXTURE_2D);
   3.157 +}
   3.158 +
   3.159 +// near scene: regular objects affected by parallax shift and translation
   3.160 +static void draw_scene_near()
   3.161 +{
   3.162 +}
   3.163 +
   3.164 +void app_reshape(int x, int y)
   3.165 +{
   3.166 +	glViewport(0, 0, x, y);
   3.167 +
   3.168 +	glMatrixMode(GL_PROJECTION);
   3.169 +	glLoadIdentity();
   3.170 +	gluPerspective(50.0, (float)x / (float)y, 0.5, 500.0);
   3.171 +
   3.172 +	win_width = x;
   3.173 +	win_height = y;
   3.174 +}
   3.175 +
   3.176 +void app_keyboard(int key, bool press)
   3.177 +{
   3.178 +	if(press) {
   3.179 +		switch(key) {
   3.180 +		case 27:
   3.181 +			app_quit();
   3.182 +			break;
   3.183 +
   3.184 +		case 's':
   3.185 +			printf("rendering cubemap\n");
   3.186 +			render_cubemap();
   3.187 +			break;
   3.188 +		}
   3.189 +	}
   3.190 +}
   3.191 +
   3.192 +static float prev_x, prev_y;
   3.193 +static bool bnstate[16];
   3.194 +
   3.195 +void app_mouse_button(int bn, bool press, int x, int y)
   3.196 +{
   3.197 +	if(bn < (int)(sizeof bnstate / sizeof *bnstate)) {
   3.198 +		bnstate[bn] = press;
   3.199 +	}
   3.200 +	prev_x = x;
   3.201 +	prev_y = y;
   3.202 +}
   3.203 +
   3.204 +void app_mouse_motion(int x, int y)
   3.205 +{
   3.206 +	float dx = x - prev_x;
   3.207 +	float dy = y - prev_y;
   3.208 +	prev_x = x;
   3.209 +	prev_y = y;
   3.210 +
   3.211 +	if(!dx && !dy) return;
   3.212 +
   3.213 +	if(bnstate[0]) {
   3.214 +		cam_theta += dx * 0.5;
   3.215 +		cam_phi += dy * 0.5;
   3.216 +
   3.217 +		if(cam_phi < -90) cam_phi = -90;
   3.218 +		if(cam_phi > 90) cam_phi = 90;
   3.219 +		app_redisplay();
   3.220 +	}
   3.221 +}
   3.222 +
   3.223 +static bool parse_args(int argc, char **argv)
   3.224 +{
   3.225 +	for(int i=1; i<argc; i++) {
   3.226 +		if(argv[i][0] == '-') {
   3.227 +			/*
   3.228 +			} else if(strcmp(argv[i], "-help") == 0) {
   3.229 +				printf("usage: %s [options]\noptions:\n", argv[0]);
   3.230 +				printf(" -help: print usage information and exit\n");
   3.231 +				exit(0);
   3.232 +			} else {*/
   3.233 +				fprintf(stderr, "invalid option: %s\n", argv[i]);
   3.234 +				return false;
   3.235 +			//}
   3.236 +		} else {
   3.237 +			if(img_fname) {
   3.238 +				fprintf(stderr, "unexpected option: %s\n", argv[i]);
   3.239 +				return false;
   3.240 +			}
   3.241 +			img_fname = argv[i];
   3.242 +		}
   3.243 +	}
   3.244 +
   3.245 +	return true;
   3.246 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/app.h	Thu Jul 27 20:36:12 2017 +0300
     4.3 @@ -0,0 +1,19 @@
     4.4 +#ifndef APP_H_
     4.5 +#define APP_H_
     4.6 +
     4.7 +bool app_init(int argc, char **argv);
     4.8 +void app_cleanup();
     4.9 +
    4.10 +void app_draw();
    4.11 +
    4.12 +void app_reshape(int x, int y);
    4.13 +void app_keyboard(int key, bool press);
    4.14 +void app_mouse_button(int bn, bool press, int x, int y);
    4.15 +void app_mouse_motion(int x, int y);
    4.16 +
    4.17 +// functions implemented in main.cc
    4.18 +void app_quit();
    4.19 +void app_redisplay();
    4.20 +void app_swap_buffers();
    4.21 +
    4.22 +#endif	// APP_H_
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/geom.cc	Thu Jul 27 20:36:12 2017 +0300
     5.3 @@ -0,0 +1,334 @@
     5.4 +#include <assert.h>
     5.5 +#include <float.h>
     5.6 +#include <algorithm>
     5.7 +#include "geom.h"
     5.8 +
     5.9 +GeomObject::~GeomObject()
    5.10 +{
    5.11 +}
    5.12 +
    5.13 +
    5.14 +Sphere::Sphere()
    5.15 +{
    5.16 +	radius = 1.0;
    5.17 +}
    5.18 +
    5.19 +Sphere::Sphere(const Vec3 &cent, float radius)
    5.20 +	: center(cent)
    5.21 +{
    5.22 +	this->radius = radius;
    5.23 +}
    5.24 +
    5.25 +void Sphere::set_union(const GeomObject *obj1, const GeomObject *obj2)
    5.26 +{
    5.27 +	const Sphere *sph1 = dynamic_cast<const Sphere*>(obj1);
    5.28 +	const Sphere *sph2 = dynamic_cast<const Sphere*>(obj2);
    5.29 +
    5.30 +	if(!sph1 || !sph2) {
    5.31 +		fprintf(stderr, "Sphere::set_union: arguments must be spheres");
    5.32 +		return;
    5.33 +	}
    5.34 +
    5.35 +	float dist = length(sph1->center - sph2->center);
    5.36 +	float surf_dist = dist - (sph1->radius + sph2->radius);
    5.37 +	float d1 = sph1->radius + surf_dist / 2.0;
    5.38 +	float d2 = sph2->radius + surf_dist / 2.0;
    5.39 +	float t = d1 / (d1 + d2);
    5.40 +
    5.41 +	if(t < 0.0) t = 0.0;
    5.42 +	if(t > 1.0) t = 1.0;
    5.43 +
    5.44 +	center = sph1->center * t + sph2->center * (1.0 - t);
    5.45 +	radius = std::max(dist * t + sph2->radius, dist * (1.0f - t) + sph1->radius);
    5.46 +}
    5.47 +
    5.48 +void Sphere::set_intersection(const GeomObject *obj1, const GeomObject *obj2)
    5.49 +{
    5.50 +	fprintf(stderr, "Sphere::intersection undefined\n");
    5.51 +}
    5.52 +
    5.53 +bool Sphere::intersect(const Ray &ray, HitPoint *hit) const
    5.54 +{
    5.55 +	float a = dot(ray.dir, ray.dir);
    5.56 +	float b = 2.0 * ray.dir.x * (ray.origin.x - center.x) +
    5.57 +		2.0 * ray.dir.y * (ray.origin.y - center.y) +
    5.58 +		2.0 * ray.dir.z * (ray.origin.z - center.z);
    5.59 +	float c = dot(ray.origin, ray.origin) + dot(center, center) -
    5.60 +		2.0 * dot(ray.origin, center) - radius * radius;
    5.61 +
    5.62 +	float discr = b * b - 4.0 * a * c;
    5.63 +	if(discr < 1e-4) {
    5.64 +		return false;
    5.65 +	}
    5.66 +
    5.67 +	float sqrt_discr = sqrt(discr);
    5.68 +	float t0 = (-b + sqrt_discr) / (2.0 * a);
    5.69 +	float t1 = (-b - sqrt_discr) / (2.0 * a);
    5.70 +
    5.71 +	if(t0 < 1e-4)
    5.72 +		t0 = t1;
    5.73 +	if(t1 < 1e-4)
    5.74 +		t1 = t0;
    5.75 +
    5.76 +	float t = t0 < t1 ? t0 : t1;
    5.77 +	if(t < 1e-4) {
    5.78 +		return false;
    5.79 +	}
    5.80 +
    5.81 +	// fill the HitPoint structure
    5.82 +	if(hit) {
    5.83 +		hit->obj = this;
    5.84 +		hit->dist = t;
    5.85 +		hit->pos = ray.origin + ray.dir * t;
    5.86 +		hit->normal = (hit->pos - center) / radius;
    5.87 +	}
    5.88 +	return true;
    5.89 +}
    5.90 +
    5.91 +
    5.92 +AABox::AABox()
    5.93 +{
    5.94 +}
    5.95 +
    5.96 +AABox::AABox(const Vec3 &vmin, const Vec3 &vmax)
    5.97 +	: min(vmin), max(vmax)
    5.98 +{
    5.99 +}
   5.100 +
   5.101 +void AABox::set_union(const GeomObject *obj1, const GeomObject *obj2)
   5.102 +{
   5.103 +	const AABox *box1 = dynamic_cast<const AABox*>(obj1);
   5.104 +	const AABox *box2 = dynamic_cast<const AABox*>(obj2);
   5.105 +
   5.106 +	if(!box1 || !box2) {
   5.107 +		fprintf(stderr, "AABox::set_union: arguments must be AABoxes too\n");
   5.108 +		return;
   5.109 +	}
   5.110 +
   5.111 +	min.x = std::min(box1->min.x, box2->min.x);
   5.112 +	min.y = std::min(box1->min.y, box2->min.y);
   5.113 +	min.z = std::min(box1->min.z, box2->min.z);
   5.114 +
   5.115 +	max.x = std::max(box1->max.x, box2->max.x);
   5.116 +	max.y = std::max(box1->max.y, box2->max.y);
   5.117 +	max.z = std::max(box1->max.z, box2->max.z);
   5.118 +}
   5.119 +
   5.120 +void AABox::set_intersection(const GeomObject *obj1, const GeomObject *obj2)
   5.121 +{
   5.122 +	const AABox *box1 = dynamic_cast<const AABox*>(obj1);
   5.123 +	const AABox *box2 = dynamic_cast<const AABox*>(obj2);
   5.124 +
   5.125 +	if(!box1 || !box2) {
   5.126 +		fprintf(stderr, "AABox::set_intersection: arguments must be AABoxes too\n");
   5.127 +		return;
   5.128 +	}
   5.129 +
   5.130 +	for(int i=0; i<3; i++) {
   5.131 +		min[i] = std::max(box1->min[i], box2->min[i]);
   5.132 +		max[i] = std::min(box1->max[i], box2->max[i]);
   5.133 +
   5.134 +		if(max[i] < min[i]) {
   5.135 +			max[i] = min[i];
   5.136 +		}
   5.137 +	}
   5.138 +}
   5.139 +
   5.140 +bool AABox::intersect(const Ray &ray, HitPoint *hit) const
   5.141 +{
   5.142 +	Vec3 param[2] = {min, max};
   5.143 +	Vec3 inv_dir(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z);
   5.144 +	int sign[3] = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0};
   5.145 +
   5.146 +	float tmin = (param[sign[0]].x - ray.origin.x) * inv_dir.x;
   5.147 +	float tmax = (param[1 - sign[0]].x - ray.origin.x) * inv_dir.x;
   5.148 +	float tymin = (param[sign[1]].y - ray.origin.y) * inv_dir.y;
   5.149 +	float tymax = (param[1 - sign[1]].y - ray.origin.y) * inv_dir.y;
   5.150 +
   5.151 +	if(tmin > tymax || tymin > tmax) {
   5.152 +		return false;
   5.153 +	}
   5.154 +	if(tymin > tmin) {
   5.155 +		tmin = tymin;
   5.156 +	}
   5.157 +	if(tymax < tmax) {
   5.158 +		tmax = tymax;
   5.159 +	}
   5.160 +
   5.161 +	float tzmin = (param[sign[2]].z - ray.origin.z) * inv_dir.z;
   5.162 +	float tzmax = (param[1 - sign[2]].z - ray.origin.z) * inv_dir.z;
   5.163 +
   5.164 +	if(tmin > tzmax || tzmin > tmax) {
   5.165 +		return false;
   5.166 +	}
   5.167 +	if(tzmin > tmin) {
   5.168 +		tmin = tzmin;
   5.169 +	}
   5.170 +	if(tzmax < tmax) {
   5.171 +		tmax = tzmax;
   5.172 +	}
   5.173 +
   5.174 +	float t = tmin < 1e-4 ? tmax : tmin;
   5.175 +	if(t >= 1e-4) {
   5.176 +
   5.177 +		if(hit) {
   5.178 +			hit->obj = this;
   5.179 +			hit->dist = t;
   5.180 +			hit->pos = ray.origin + ray.dir * t;
   5.181 +
   5.182 +			float min_dist = FLT_MAX;
   5.183 +			Vec3 offs = min + (max - min) / 2.0;
   5.184 +			Vec3 local_hit = hit->pos - offs;
   5.185 +
   5.186 +			static const Vec3 axis[] = {
   5.187 +				Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)
   5.188 +			};
   5.189 +			//int tcidx[][2] = {{2, 1}, {0, 2}, {0, 1}};
   5.190 +
   5.191 +			for(int i=0; i<3; i++) {
   5.192 +				float dist = fabs((max[i] - offs[i]) - fabs(local_hit[i]));
   5.193 +				if(dist < min_dist) {
   5.194 +					min_dist = dist;
   5.195 +					hit->normal = axis[i] * (local_hit[i] < 0.0 ? 1.0 : -1.0);
   5.196 +					//hit->texcoord = Vec2(hit->pos[tcidx[i][0]], hit->pos[tcidx[i][1]]);
   5.197 +				}
   5.198 +			}
   5.199 +		}
   5.200 +		return true;
   5.201 +	}
   5.202 +	return false;
   5.203 +
   5.204 +}
   5.205 +
   5.206 +Plane::Plane()
   5.207 +	: normal(0.0, 1.0, 0.0)
   5.208 +{
   5.209 +}
   5.210 +
   5.211 +Plane::Plane(const Vec3 &p, const Vec3 &norm)
   5.212 +	: pt(p)
   5.213 +{
   5.214 +	normal = normalize(norm);
   5.215 +}
   5.216 +
   5.217 +Plane::Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3)
   5.218 +	: pt(p1)
   5.219 +{
   5.220 +	normal = normalize(cross(p2 - p1, p3 - p1));
   5.221 +}
   5.222 +
   5.223 +Plane::Plane(const Vec3 &normal, float dist)
   5.224 +{
   5.225 +	this->normal = normalize(normal);
   5.226 +	pt = this->normal * dist;
   5.227 +}
   5.228 +
   5.229 +void Plane::set_union(const GeomObject *obj1, const GeomObject *obj2)
   5.230 +{
   5.231 +	fprintf(stderr, "Plane::set_union undefined\n");
   5.232 +}
   5.233 +
   5.234 +void Plane::set_intersection(const GeomObject *obj1, const GeomObject *obj2)
   5.235 +{
   5.236 +	fprintf(stderr, "Plane::set_intersection undefined\n");
   5.237 +}
   5.238 +
   5.239 +bool Plane::intersect(const Ray &ray, HitPoint *hit) const
   5.240 +{
   5.241 +	float ndotdir = dot(normal, ray.dir);
   5.242 +	if(fabs(ndotdir) < 1e-4) {
   5.243 +		return false;
   5.244 +	}
   5.245 +
   5.246 +	if(hit) {
   5.247 +		Vec3 ptdir = pt - ray.origin;
   5.248 +		float t = dot(normal, ptdir) / ndotdir;
   5.249 +
   5.250 +		hit->pos = ray.origin + ray.dir * t;
   5.251 +		hit->normal = normal;
   5.252 +		hit->obj = this;
   5.253 +	}
   5.254 +	return true;
   5.255 +}
   5.256 +
   5.257 +float sphere_distance(const Vec3 &cent, float rad, const Vec3 &pt)
   5.258 +{
   5.259 +	return length(pt - cent) - rad;
   5.260 +}
   5.261 +
   5.262 +// TODO version which takes both radii into account
   5.263 +float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt)
   5.264 +{
   5.265 +	Vec3 ab_dir = b - a;
   5.266 +	float ab_len_sq = length_sq(ab_dir);
   5.267 +
   5.268 +	if(fabs(ab_len_sq) < 1e-5) {
   5.269 +		// if a == b, the capsule is a sphere with radius the maximum of the capsule radii
   5.270 +		return sphere_distance(a, std::max(ra, rb), pt);
   5.271 +	}
   5.272 +	float ab_len = sqrt(ab_len_sq);
   5.273 +
   5.274 +	Vec3 ap_dir = pt - a;
   5.275 +
   5.276 +	float t = dot(ap_dir, ab_dir / ab_len) / ab_len;
   5.277 +	if(t < 0.0) {
   5.278 +		return sphere_distance(a, ra, pt);
   5.279 +	}
   5.280 +	if(t >= 1.0) {
   5.281 +		return sphere_distance(b, rb, pt);
   5.282 +	}
   5.283 +
   5.284 +	Vec3 pproj = a + ab_dir * t;
   5.285 +	return length(pproj - pt) - ra;
   5.286 +}
   5.287 +
   5.288 +#if 0
   5.289 +float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt)
   5.290 +{
   5.291 +	Vec3 ab_dir = b - a;
   5.292 +
   5.293 +	if(fabs(length_sq(ab_dir)) < 1e-5) {
   5.294 +		// if a == b, the capsule is a sphere with radius the maximum of the capsule radii
   5.295 +		return sphere_distance(a, std::max(ra, rb), pt);
   5.296 +	}
   5.297 +	float ab_len = length(ab_dir);
   5.298 +
   5.299 +	Vec3 ap_dir = pt - a;
   5.300 +	Vec3 rotaxis = normalize(cross(ab_dir, ap_dir));
   5.301 +
   5.302 +	Mat4 rmat;
   5.303 +	rmat.set_rotation(rotaxis, M_PI / 2.0);
   5.304 +	Vec3 right = rmat * ab_dir / ab_len;
   5.305 +
   5.306 +	// XXX I think this check is redundant, always false, due to the cross product order
   5.307 +	//assert(dot(right, ab_dir) >= 0.0);
   5.308 +	if(dot(right, ab_dir) < 0.0) {
   5.309 +		right = -right;
   5.310 +	}
   5.311 +	Vec3 aa = a + right * ra;
   5.312 +	Vec3 bb = b + right * rb;
   5.313 +
   5.314 +	// project pt to the line segment bb-aa, see if the projection lies within the interval [0, 1)
   5.315 +	Vec3 aabb_dir = bb - aa;
   5.316 +	float aabb_len = length(aabb_dir);
   5.317 +	Vec3 aap_dir = pt - aa;
   5.318 +
   5.319 +	float t = dot(aap_dir, aabb_dir / aabb_len) / aabb_len;
   5.320 +	if(t < 0.0) {
   5.321 +		return sphere_distance(a, ra, pt);
   5.322 +	}
   5.323 +	if(t >= 1.0) {
   5.324 +		return sphere_distance(b, rb, pt);
   5.325 +	}
   5.326 +
   5.327 +	Vec3 ppt = aa + aabb_dir * t;
   5.328 +	Vec3 norm = ppt - pt;
   5.329 +	float dist = length(norm);
   5.330 +
   5.331 +	if(dot(norm, right) < 0.0) {
   5.332 +		// inside the cone
   5.333 +		dist = -dist;
   5.334 +	}
   5.335 +	return dist;
   5.336 +}
   5.337 +#endif
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/geom.h	Thu Jul 27 20:36:12 2017 +0300
     6.3 @@ -0,0 +1,73 @@
     6.4 +#ifndef GEOMOBJ_H_
     6.5 +#define GEOMOBJ_H_
     6.6 +
     6.7 +#include <gmath/gmath.h>
     6.8 +
     6.9 +class GeomObject;
    6.10 +class SceneNode;
    6.11 +
    6.12 +struct HitPoint {
    6.13 +	float dist;				//< parametric distance along the ray
    6.14 +	Vec3 pos;			//< position of intersection (orig + dir * dist)
    6.15 +	Vec3 normal;			//< normal at the point of intersection
    6.16 +	const void *obj;		//< pointer to the intersected object
    6.17 +	const SceneNode *node;
    6.18 +	Ray ray;
    6.19 +};
    6.20 +
    6.21 +class GeomObject {
    6.22 +public:
    6.23 +	virtual ~GeomObject();
    6.24 +
    6.25 +	virtual void set_union(const GeomObject *obj1, const GeomObject *obj2) = 0;
    6.26 +	virtual void set_intersection(const GeomObject *obj1, const GeomObject *obj2) = 0;
    6.27 +
    6.28 +	virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const = 0;
    6.29 +};
    6.30 +
    6.31 +class Sphere : public GeomObject {
    6.32 +public:
    6.33 +	Vec3 center;
    6.34 +	float radius;
    6.35 +
    6.36 +	Sphere();
    6.37 +	Sphere(const Vec3 &center, float radius);
    6.38 +
    6.39 +	void set_union(const GeomObject *obj1, const GeomObject *obj2);
    6.40 +	void set_intersection(const GeomObject *obj1, const GeomObject *obj2);
    6.41 +
    6.42 +	bool intersect(const Ray &ray, HitPoint *hit = 0) const;
    6.43 +};
    6.44 +
    6.45 +class AABox : public GeomObject {
    6.46 +public:
    6.47 +	Vec3 min, max;
    6.48 +
    6.49 +	AABox();
    6.50 +	AABox(const Vec3 &min, const Vec3 &max);
    6.51 +
    6.52 +	void set_union(const GeomObject *obj1, const GeomObject *obj2);
    6.53 +	void set_intersection(const GeomObject *obj1, const GeomObject *obj2);
    6.54 +
    6.55 +	bool intersect(const Ray &ray, HitPoint *hit = 0) const;
    6.56 +};
    6.57 +
    6.58 +class Plane : public GeomObject {
    6.59 +public:
    6.60 +	Vec3 pt, normal;
    6.61 +
    6.62 +	Plane();
    6.63 +	Plane(const Vec3 &pt, const Vec3 &normal);
    6.64 +	Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3);
    6.65 +	Plane(const Vec3 &normal, float dist);
    6.66 +
    6.67 +	void set_union(const GeomObject *obj1, const GeomObject *obj2);
    6.68 +	void set_intersection(const GeomObject *obj1, const GeomObject *obj2);
    6.69 +
    6.70 +	bool intersect(const Ray &ray, HitPoint *hit = 0) const;
    6.71 +};
    6.72 +
    6.73 +float sphere_distance(const Vec3 &cent, float rad, const Vec3 &pt);
    6.74 +float capsule_distance(const Vec3 &a, float ra, const Vec3 &b, float rb, const Vec3 &pt);
    6.75 +
    6.76 +#endif	// GEOMOBJ_H_
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/main.cc	Thu Jul 27 20:36:12 2017 +0300
     7.3 @@ -0,0 +1,75 @@
     7.4 +#include <stdlib.h>
     7.5 +#ifdef __APPLE__
     7.6 +#include <GLUT/glut.h>
     7.7 +#else
     7.8 +#include <GL/glut.h>
     7.9 +#endif
    7.10 +#include "app.h"
    7.11 +
    7.12 +static void display();
    7.13 +static void reshape(int x, int y);
    7.14 +static void keydown(unsigned char key, int x, int y);
    7.15 +static void mouse(int bn, int st, int x, int y);
    7.16 +static void motion(int x, int y);
    7.17 +
    7.18 +int main(int argc, char **argv)
    7.19 +{
    7.20 +	glutInit(&argc, argv);
    7.21 +	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
    7.22 +	glutInitWindowSize(1024, 768);
    7.23 +	glutCreateWindow("cubemapper");
    7.24 +
    7.25 +	glutDisplayFunc(display);
    7.26 +	glutReshapeFunc(reshape);
    7.27 +	glutKeyboardFunc(keydown);
    7.28 +	glutMouseFunc(mouse);
    7.29 +	glutMotionFunc(motion);
    7.30 +
    7.31 +	if(!app_init(argc, argv)) {
    7.32 +		return 1;
    7.33 +	}
    7.34 +
    7.35 +	glutMainLoop();
    7.36 +	return 0;
    7.37 +}
    7.38 +
    7.39 +void app_quit()
    7.40 +{
    7.41 +	app_cleanup();
    7.42 +	exit(0);
    7.43 +}
    7.44 +
    7.45 +void app_redisplay()
    7.46 +{
    7.47 +	glutPostRedisplay();
    7.48 +}
    7.49 +
    7.50 +void app_swap_buffers()
    7.51 +{
    7.52 +	glutSwapBuffers();
    7.53 +}
    7.54 +
    7.55 +static void display()
    7.56 +{
    7.57 +	app_draw();
    7.58 +}
    7.59 +
    7.60 +static void reshape(int x, int y)
    7.61 +{
    7.62 +	app_reshape(x, y);
    7.63 +}
    7.64 +
    7.65 +static void keydown(unsigned char key, int x, int y)
    7.66 +{
    7.67 +	app_keyboard(key, true);
    7.68 +}
    7.69 +
    7.70 +static void mouse(int bn, int st, int x, int y)
    7.71 +{
    7.72 +	app_mouse_button(bn - GLUT_LEFT_BUTTON, st == GLUT_DOWN, x, y);
    7.73 +}
    7.74 +
    7.75 +static void motion(int x, int y)
    7.76 +{
    7.77 +	app_mouse_motion(x, y);
    7.78 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/mesh.cc	Thu Jul 27 20:36:12 2017 +0300
     8.3 @@ -0,0 +1,1362 @@
     8.4 +#include <stdio.h>
     8.5 +#include <stdlib.h>
     8.6 +#include <float.h>
     8.7 +#include <assert.h>
     8.8 +#include "opengl.h"
     8.9 +#include "mesh.h"
    8.10 +//#include "xform_node.h"
    8.11 +
    8.12 +#define USE_OLDGL
    8.13 +
    8.14 +bool Mesh::use_custom_sdr_attr = true;
    8.15 +int Mesh::global_sdr_loc[NUM_MESH_ATTR] = { 0, 1, 2, 3, 4, 5, 6 };
    8.16 +/*
    8.17 +	(int)SDR_ATTR_VERTEX,
    8.18 +	(int)SDR_ATTR_NORMAL,
    8.19 +	(int)SDR_ATTR_TANGENT,
    8.20 +	(int)SDR_ATTR_TEXCOORD,
    8.21 +	(int)SDR_ATTR_COLOR,
    8.22 +	-1, -1};
    8.23 +*/
    8.24 +unsigned int Mesh::intersect_mode = ISECT_DEFAULT;
    8.25 +float Mesh::vertex_sel_dist = 0.01;
    8.26 +float Mesh::vis_vecsize = 1.0;
    8.27 +
    8.28 +Mesh::Mesh()
    8.29 +{
    8.30 +	clear();
    8.31 +
    8.32 +	glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects);
    8.33 +
    8.34 +	for(int i=0; i<NUM_MESH_ATTR; i++) {
    8.35 +		vattr[i].vbo = buffer_objects[i];
    8.36 +	}
    8.37 +	ibo = buffer_objects[NUM_MESH_ATTR];
    8.38 +	wire_ibo = 0;
    8.39 +}
    8.40 +
    8.41 +Mesh::~Mesh()
    8.42 +{
    8.43 +	glDeleteBuffers(NUM_MESH_ATTR + 1, buffer_objects);
    8.44 +
    8.45 +	if(wire_ibo) {
    8.46 +		glDeleteBuffers(1, &wire_ibo);
    8.47 +	}
    8.48 +}
    8.49 +
    8.50 +Mesh::Mesh(const Mesh &rhs)
    8.51 +{
    8.52 +	clear();
    8.53 +
    8.54 +	glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects);
    8.55 +
    8.56 +	for(int i=0; i<NUM_MESH_ATTR; i++) {
    8.57 +		vattr[i].vbo = buffer_objects[i];
    8.58 +	}
    8.59 +	ibo = buffer_objects[NUM_MESH_ATTR];
    8.60 +	wire_ibo = 0;
    8.61 +
    8.62 +	clone(rhs);
    8.63 +}
    8.64 +
    8.65 +Mesh &Mesh::operator =(const Mesh &rhs)
    8.66 +{
    8.67 +	if(&rhs != this) {
    8.68 +		clone(rhs);
    8.69 +	}
    8.70 +	return *this;
    8.71 +}
    8.72 +
    8.73 +bool Mesh::clone(const Mesh &m)
    8.74 +{
    8.75 +	clear();
    8.76 +
    8.77 +	for(int i=0; i<NUM_MESH_ATTR; i++) {
    8.78 +		if(m.has_attrib(i)) {
    8.79 +			m.get_attrib_data(i);	// force validation of the actual data on the source mesh
    8.80 +
    8.81 +			vattr[i].nelem = m.vattr[i].nelem;
    8.82 +			vattr[i].data = m.vattr[i].data;	// copy the actual data
    8.83 +			vattr[i].data_valid = true;
    8.84 +		}
    8.85 +	}
    8.86 +
    8.87 +	if(m.is_indexed()) {
    8.88 +		m.get_index_data();		// again, force validation
    8.89 +
    8.90 +		// copy the index data
    8.91 +		idata = m.idata;
    8.92 +		idata_valid = true;
    8.93 +	}
    8.94 +
    8.95 +	name = m.name;
    8.96 +	nverts = m.nverts;
    8.97 +	nfaces = m.nfaces;
    8.98 +
    8.99 +	//bones = m.bones;
   8.100 +
   8.101 +	memcpy(cur_val, m.cur_val, sizeof cur_val);
   8.102 +
   8.103 +	aabb = m.aabb;
   8.104 +	aabb_valid = m.aabb_valid;
   8.105 +	bsph = m.bsph;
   8.106 +	bsph_valid = m.bsph_valid;
   8.107 +
   8.108 +	hitface = m.hitface;
   8.109 +	hitvert = m.hitvert;
   8.110 +
   8.111 +	intersect_mode = m.intersect_mode;
   8.112 +	vertex_sel_dist = m.vertex_sel_dist;
   8.113 +	vis_vecsize = m.vis_vecsize;
   8.114 +
   8.115 +	return true;
   8.116 +}
   8.117 +
   8.118 +void Mesh::set_name(const char *name)
   8.119 +{
   8.120 +	this->name = name;
   8.121 +}
   8.122 +
   8.123 +const char *Mesh::get_name() const
   8.124 +{
   8.125 +	return name.c_str();
   8.126 +}
   8.127 +
   8.128 +bool Mesh::has_attrib(int attr) const
   8.129 +{
   8.130 +	if(attr < 0 || attr >= NUM_MESH_ATTR) {
   8.131 +		return false;
   8.132 +	}
   8.133 +
   8.134 +	// if neither of these is valid, then nobody has set this attribute
   8.135 +	return vattr[attr].vbo_valid || vattr[attr].data_valid;
   8.136 +}
   8.137 +
   8.138 +bool Mesh::is_indexed() const
   8.139 +{
   8.140 +	return ibo_valid || idata_valid;
   8.141 +}
   8.142 +
   8.143 +void Mesh::clear()
   8.144 +{
   8.145 +	//bones.clear();
   8.146 +
   8.147 +	for(int i=0; i<NUM_MESH_ATTR; i++) {
   8.148 +		vattr[i].nelem = 0;
   8.149 +		vattr[i].vbo_valid = false;
   8.150 +		vattr[i].data_valid = false;
   8.151 +		//vattr[i].sdr_loc = -1;
   8.152 +		vattr[i].data.clear();
   8.153 +	}
   8.154 +	ibo_valid = idata_valid = false;
   8.155 +	idata.clear();
   8.156 +
   8.157 +	wire_ibo_valid = false;
   8.158 +
   8.159 +	nverts = nfaces = 0;
   8.160 +
   8.161 +	bsph_valid = false;
   8.162 +	aabb_valid = false;
   8.163 +}
   8.164 +
   8.165 +float *Mesh::set_attrib_data(int attrib, int nelem, unsigned int num, const float *data)
   8.166 +{
   8.167 +	if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
   8.168 +		fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
   8.169 +		return 0;
   8.170 +	}
   8.171 +
   8.172 +	if(nverts && num != nverts) {
   8.173 +		fprintf(stderr, "%s: attribute count missmatch (%d instead of %d)\n", __FUNCTION__, num, nverts);
   8.174 +		return 0;
   8.175 +	}
   8.176 +	nverts = num;
   8.177 +
   8.178 +	vattr[attrib].data.clear();
   8.179 +	vattr[attrib].nelem = nelem;
   8.180 +	vattr[attrib].data.resize(num * nelem);
   8.181 +
   8.182 +	if(data) {
   8.183 +		memcpy(&vattr[attrib].data[0], data, num * nelem * sizeof *data);
   8.184 +	}
   8.185 +
   8.186 +	vattr[attrib].data_valid = true;
   8.187 +	vattr[attrib].vbo_valid = false;
   8.188 +	return &vattr[attrib].data[0];
   8.189 +}
   8.190 +
   8.191 +float *Mesh::get_attrib_data(int attrib)
   8.192 +{
   8.193 +	if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
   8.194 +		fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
   8.195 +		return 0;
   8.196 +	}
   8.197 +
   8.198 +	vattr[attrib].vbo_valid = false;
   8.199 +	return (float*)((const Mesh*)this)->get_attrib_data(attrib);
   8.200 +}
   8.201 +
   8.202 +const float *Mesh::get_attrib_data(int attrib) const
   8.203 +{
   8.204 +	if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
   8.205 +		fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
   8.206 +		return 0;
   8.207 +	}
   8.208 +
   8.209 +	if(!vattr[attrib].data_valid) {
   8.210 +#if GL_ES_VERSION_2_0
   8.211 +		fprintf(stderr, "%s: can't read back attrib data on CrippledGL ES\n", __FUNCTION__);
   8.212 +		return 0;
   8.213 +#else
   8.214 +		if(!vattr[attrib].vbo_valid) {
   8.215 +			fprintf(stderr, "%s: unavailable attrib: %d\n", __FUNCTION__, attrib);
   8.216 +			return 0;
   8.217 +		}
   8.218 +
   8.219 +		// local data copy is unavailable, grab the data from the vbo
   8.220 +		Mesh *m = (Mesh*)this;
   8.221 +		m->vattr[attrib].data.resize(nverts * vattr[attrib].nelem);
   8.222 +
   8.223 +		glBindBuffer(GL_ARRAY_BUFFER, vattr[attrib].vbo);
   8.224 +		void *data = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
   8.225 +		memcpy(&m->vattr[attrib].data[0], data, nverts * vattr[attrib].nelem * sizeof(float));
   8.226 +		glUnmapBuffer(GL_ARRAY_BUFFER);
   8.227 +
   8.228 +		vattr[attrib].data_valid = true;
   8.229 +#endif
   8.230 +	}
   8.231 +
   8.232 +	return &vattr[attrib].data[0];
   8.233 +}
   8.234 +
   8.235 +void Mesh::set_attrib(int attrib, int idx, const Vec4 &v)
   8.236 +{
   8.237 +	float *data = get_attrib_data(attrib);
   8.238 +	if(data) {
   8.239 +		data += idx * vattr[attrib].nelem;
   8.240 +		for(int i=0; i<vattr[attrib].nelem; i++) {
   8.241 +			data[i] = v[i];
   8.242 +		}
   8.243 +	}
   8.244 +}
   8.245 +
   8.246 +Vec4 Mesh::get_attrib(int attrib, int idx) const
   8.247 +{
   8.248 +	Vec4 v(0.0, 0.0, 0.0, 1.0);
   8.249 +	const float *data = get_attrib_data(attrib);
   8.250 +	if(data) {
   8.251 +		data += idx * vattr[attrib].nelem;
   8.252 +		for(int i=0; i<vattr[attrib].nelem; i++) {
   8.253 +			v[i] = data[i];
   8.254 +		}
   8.255 +	}
   8.256 +	return v;
   8.257 +}
   8.258 +
   8.259 +int Mesh::get_attrib_count(int attrib) const
   8.260 +{
   8.261 +	return has_attrib(attrib) ? nverts : 0;
   8.262 +}
   8.263 +
   8.264 +
   8.265 +unsigned int *Mesh::set_index_data(int num, const unsigned int *indices)
   8.266 +{
   8.267 +	int nidx = nfaces * 3;
   8.268 +	if(nidx && num != nidx) {
   8.269 +		fprintf(stderr, "%s: index count missmatch (%d instead of %d)\n", __FUNCTION__, num, nidx);
   8.270 +		return 0;
   8.271 +	}
   8.272 +	nfaces = num / 3;
   8.273 +
   8.274 +	idata.clear();
   8.275 +	idata.resize(num);
   8.276 +
   8.277 +	if(indices) {
   8.278 +		memcpy(&idata[0], indices, num * sizeof *indices);
   8.279 +	}
   8.280 +
   8.281 +	idata_valid = true;
   8.282 +	ibo_valid = false;
   8.283 +
   8.284 +	return &idata[0];
   8.285 +}
   8.286 +
   8.287 +unsigned int *Mesh::get_index_data()
   8.288 +{
   8.289 +	ibo_valid = false;
   8.290 +	return (unsigned int*)((const Mesh*)this)->get_index_data();
   8.291 +}
   8.292 +
   8.293 +const unsigned int *Mesh::get_index_data() const
   8.294 +{
   8.295 +	if(!idata_valid) {
   8.296 +#if GL_ES_VERSION_2_0
   8.297 +		fprintf(stderr, "%s: can't read back index data in CrippledGL ES\n", __FUNCTION__);
   8.298 +		return 0;
   8.299 +#else
   8.300 +		if(!ibo_valid) {
   8.301 +			fprintf(stderr, "%s: indices unavailable\n", __FUNCTION__);
   8.302 +			return 0;
   8.303 +		}
   8.304 +
   8.305 +		// local data copy is unavailable, gram the data from the ibo
   8.306 +		Mesh *m = (Mesh*)this;
   8.307 +		int nidx = nfaces * 3;
   8.308 +		m->idata.resize(nidx);
   8.309 +
   8.310 +		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
   8.311 +		void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
   8.312 +		memcpy(&m->idata[0], data, nidx * sizeof(unsigned int));
   8.313 +		glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
   8.314 +
   8.315 +		idata_valid = true;
   8.316 +#endif
   8.317 +	}
   8.318 +
   8.319 +	return &idata[0];
   8.320 +}
   8.321 +
   8.322 +int Mesh::get_index_count() const
   8.323 +{
   8.324 +	return nfaces * 3;
   8.325 +}
   8.326 +
   8.327 +void Mesh::append(const Mesh &mesh)
   8.328 +{
   8.329 +	unsigned int idxoffs = nverts;
   8.330 +
   8.331 +	if(!nverts) {
   8.332 +		clone(mesh);
   8.333 +		return;
   8.334 +	}
   8.335 +
   8.336 +	nverts += mesh.nverts;
   8.337 +	nfaces += mesh.nfaces;
   8.338 +
   8.339 +	for(int i=0; i<NUM_MESH_ATTR; i++) {
   8.340 +		if(has_attrib(i) && mesh.has_attrib(i)) {
   8.341 +			// force validating the data arrays
   8.342 +			get_attrib_data(i);
   8.343 +			mesh.get_attrib_data(i);
   8.344 +
   8.345 +			// append the mesh data
   8.346 +			vattr[i].data.insert(vattr[i].data.end(), mesh.vattr[i].data.begin(), mesh.vattr[i].data.end());
   8.347 +		}
   8.348 +	}
   8.349 +
   8.350 +	if(ibo_valid || idata_valid) {
   8.351 +		// make index arrays valid
   8.352 +		get_index_data();
   8.353 +		mesh.get_index_data();
   8.354 +
   8.355 +		size_t orig_sz = idata.size();
   8.356 +
   8.357 +		idata.insert(idata.end(), mesh.idata.begin(), mesh.idata.end());
   8.358 +
   8.359 +		// fixup all the new indices
   8.360 +		for(size_t i=orig_sz; i<idata.size(); i++) {
   8.361 +			idata[i] += idxoffs;
   8.362 +		}
   8.363 +	}
   8.364 +
   8.365 +	// fuck everything
   8.366 +	wire_ibo_valid = false;
   8.367 +	aabb_valid = false;
   8.368 +	bsph_valid = false;
   8.369 +}
   8.370 +
   8.371 +// assemble a complete vertex by adding all the useful attributes
   8.372 +void Mesh::vertex(float x, float y, float z)
   8.373 +{
   8.374 +	cur_val[MESH_ATTR_VERTEX] = Vec4(x, y, z, 1.0f);
   8.375 +	vattr[MESH_ATTR_VERTEX].data_valid = true;
   8.376 +	vattr[MESH_ATTR_VERTEX].nelem = 3;
   8.377 +
   8.378 +	for(int i=0; i<NUM_MESH_ATTR; i++) {
   8.379 +		if(vattr[i].data_valid) {
   8.380 +			for(int j=0; j<vattr[MESH_ATTR_VERTEX].nelem; j++) {
   8.381 +				vattr[i].data.push_back(cur_val[i][j]);
   8.382 +			}
   8.383 +		}
   8.384 +		vattr[i].vbo_valid = false;
   8.385 +	}
   8.386 +
   8.387 +	if(idata_valid) {
   8.388 +		idata.clear();
   8.389 +	}
   8.390 +	ibo_valid = idata_valid = false;
   8.391 +}
   8.392 +
   8.393 +void Mesh::normal(float nx, float ny, float nz)
   8.394 +{
   8.395 +	cur_val[MESH_ATTR_NORMAL] = Vec4(nx, ny, nz, 1.0f);
   8.396 +	vattr[MESH_ATTR_NORMAL].data_valid = true;
   8.397 +	vattr[MESH_ATTR_NORMAL].nelem = 3;
   8.398 +}
   8.399 +
   8.400 +void Mesh::tangent(float tx, float ty, float tz)
   8.401 +{
   8.402 +	cur_val[MESH_ATTR_TANGENT] = Vec4(tx, ty, tz, 1.0f);
   8.403 +	vattr[MESH_ATTR_TANGENT].data_valid = true;
   8.404 +	vattr[MESH_ATTR_TANGENT].nelem = 3;
   8.405 +}
   8.406 +
   8.407 +void Mesh::texcoord(float u, float v, float w)
   8.408 +{
   8.409 +	cur_val[MESH_ATTR_TEXCOORD] = Vec4(u, v, w, 1.0f);
   8.410 +	vattr[MESH_ATTR_TEXCOORD].data_valid = true;
   8.411 +	vattr[MESH_ATTR_TEXCOORD].nelem = 3;
   8.412 +}
   8.413 +
   8.414 +void Mesh::boneweights(float w1, float w2, float w3, float w4)
   8.415 +{
   8.416 +	cur_val[MESH_ATTR_BONEWEIGHTS] = Vec4(w1, w2, w3, w4);
   8.417 +	vattr[MESH_ATTR_BONEWEIGHTS].data_valid = true;
   8.418 +	vattr[MESH_ATTR_BONEWEIGHTS].nelem = 4;
   8.419 +}
   8.420 +
   8.421 +void Mesh::boneidx(int idx1, int idx2, int idx3, int idx4)
   8.422 +{
   8.423 +	cur_val[MESH_ATTR_BONEIDX] = Vec4(idx1, idx2, idx3, idx4);
   8.424 +	vattr[MESH_ATTR_BONEIDX].data_valid = true;
   8.425 +	vattr[MESH_ATTR_BONEIDX].nelem = 4;
   8.426 +}
   8.427 +
   8.428 +int Mesh::get_poly_count() const
   8.429 +{
   8.430 +	if(nfaces) {
   8.431 +		return nfaces;
   8.432 +	}
   8.433 +	if(nverts) {
   8.434 +		return nverts / 3;
   8.435 +	}
   8.436 +	return 0;
   8.437 +}
   8.438 +
   8.439 +/// static function
   8.440 +void Mesh::set_attrib_location(int attr, int loc)
   8.441 +{
   8.442 +	if(attr < 0 || attr >= NUM_MESH_ATTR) {
   8.443 +		return;
   8.444 +	}
   8.445 +	Mesh::global_sdr_loc[attr] = loc;
   8.446 +}
   8.447 +
   8.448 +/// static function
   8.449 +int Mesh::get_attrib_location(int attr)
   8.450 +{
   8.451 +	if(attr < 0 || attr >= NUM_MESH_ATTR) {
   8.452 +		return -1;
   8.453 +	}
   8.454 +	return Mesh::global_sdr_loc[attr];
   8.455 +}
   8.456 +
   8.457 +/// static function
   8.458 +void Mesh::clear_attrib_locations()
   8.459 +{
   8.460 +	for(int i=0; i<NUM_MESH_ATTR; i++) {
   8.461 +		Mesh::global_sdr_loc[i] = -1;
   8.462 +	}
   8.463 +}
   8.464 +
   8.465 +/// static function
   8.466 +void Mesh::set_vis_vecsize(float sz)
   8.467 +{
   8.468 +	Mesh::vis_vecsize = sz;
   8.469 +}
   8.470 +
   8.471 +float Mesh::get_vis_vecsize()
   8.472 +{
   8.473 +	return Mesh::vis_vecsize;
   8.474 +}
   8.475 +
   8.476 +void Mesh::apply_xform(const Mat4 &xform)
   8.477 +{
   8.478 +	Mat4 dir_xform = xform.upper3x3();
   8.479 +	apply_xform(xform, dir_xform);
   8.480 +}
   8.481 +
   8.482 +void Mesh::apply_xform(const Mat4 &xform, const Mat4 &dir_xform)
   8.483 +{
   8.484 +	for(unsigned int i=0; i<nverts; i++) {
   8.485 +		Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
   8.486 +		set_attrib(MESH_ATTR_VERTEX, i, xform * v);
   8.487 +
   8.488 +		if(has_attrib(MESH_ATTR_NORMAL)) {
   8.489 +			Vec3 n = get_attrib(MESH_ATTR_NORMAL, i).xyz();
   8.490 +			set_attrib(MESH_ATTR_NORMAL, i, Vec4(dir_xform * n));
   8.491 +		}
   8.492 +		if(has_attrib(MESH_ATTR_TANGENT)) {
   8.493 +			Vec3 t = get_attrib(MESH_ATTR_TANGENT, i).xyz();
   8.494 +			set_attrib(MESH_ATTR_TANGENT, i, Vec4(dir_xform * t));
   8.495 +		}
   8.496 +	}
   8.497 +}
   8.498 +
   8.499 +void Mesh::flip()
   8.500 +{
   8.501 +	flip_faces();
   8.502 +	flip_normals();
   8.503 +}
   8.504 +
   8.505 +void Mesh::flip_faces()
   8.506 +{
   8.507 +	if(is_indexed()) {
   8.508 +		unsigned int *indices = get_index_data();
   8.509 +		if(!indices) return;
   8.510 +
   8.511 +		int idxnum = get_index_count();
   8.512 +		for(int i=0; i<idxnum; i+=3) {
   8.513 +			unsigned int tmp = indices[i + 2];
   8.514 +			indices[i + 2] = indices[i + 1];
   8.515 +			indices[i + 1] = tmp;
   8.516 +		}
   8.517 +
   8.518 +	} else {
   8.519 +		Vec3 *verts = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
   8.520 +		if(!verts) return;
   8.521 +
   8.522 +		int vnum = get_attrib_count(MESH_ATTR_VERTEX);
   8.523 +		for(int i=0; i<vnum; i+=3) {
   8.524 +			Vec3 tmp = verts[i + 2];
   8.525 +			verts[i + 2] = verts[i + 1];
   8.526 +			verts[i + 1] = tmp;
   8.527 +		}
   8.528 +	}
   8.529 +}
   8.530 +
   8.531 +void Mesh::flip_normals()
   8.532 +{
   8.533 +	Vec3 *normals = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
   8.534 +	if(!normals) return;
   8.535 +
   8.536 +	int num = get_attrib_count(MESH_ATTR_NORMAL);
   8.537 +	for(int i=0; i<num; i++) {
   8.538 +		normals[i] = -normals[i];
   8.539 +	}
   8.540 +}
   8.541 +
   8.542 +/*
   8.543 +int Mesh::add_bone(XFormNode *bone)
   8.544 +{
   8.545 +	int idx = bones.size();
   8.546 +	bones.push_back(bone);
   8.547 +	return idx;
   8.548 +}
   8.549 +
   8.550 +const XFormNode *Mesh::get_bone(int idx) const
   8.551 +{
   8.552 +	if(idx < 0 || idx >= (int)bones.size()) {
   8.553 +		return 0;
   8.554 +	}
   8.555 +	return bones[idx];
   8.556 +}
   8.557 +
   8.558 +int Mesh::get_bones_count() const
   8.559 +{
   8.560 +	return (int)bones.size();
   8.561 +}
   8.562 +*/
   8.563 +
   8.564 +bool Mesh::pre_draw() const
   8.565 +{
   8.566 +	cur_sdr = 0;
   8.567 +	glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
   8.568 +
   8.569 +	((Mesh*)this)->update_buffers();
   8.570 +
   8.571 +	if(!vattr[MESH_ATTR_VERTEX].vbo_valid) {
   8.572 +		fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__);
   8.573 +		return false;
   8.574 +	}
   8.575 +
   8.576 +	if(cur_sdr && use_custom_sdr_attr) {
   8.577 +		// rendering with shaders
   8.578 +		if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) {
   8.579 +			fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__);
   8.580 +			return false;
   8.581 +		}
   8.582 +
   8.583 +		for(int i=0; i<NUM_MESH_ATTR; i++) {
   8.584 +			int loc = global_sdr_loc[i];
   8.585 +			if(loc >= 0 && vattr[i].vbo_valid) {
   8.586 +				glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
   8.587 +				glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0);
   8.588 +				glEnableVertexAttribArray(loc);
   8.589 +			}
   8.590 +		}
   8.591 +	} else {
   8.592 +#ifndef GL_ES_VERSION_2_0
   8.593 +		// rendering with fixed-function (not available in GLES2)
   8.594 +		glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_VERTEX].vbo);
   8.595 +		glVertexPointer(vattr[MESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0);
   8.596 +		glEnableClientState(GL_VERTEX_ARRAY);
   8.597 +
   8.598 +		if(vattr[MESH_ATTR_NORMAL].vbo_valid) {
   8.599 +			glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_NORMAL].vbo);
   8.600 +			glNormalPointer(GL_FLOAT, 0, 0);
   8.601 +			glEnableClientState(GL_NORMAL_ARRAY);
   8.602 +		}
   8.603 +		if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) {
   8.604 +			glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_TEXCOORD].vbo);
   8.605 +			glTexCoordPointer(vattr[MESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0);
   8.606 +			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   8.607 +		}
   8.608 +		if(vattr[MESH_ATTR_COLOR].vbo_valid) {
   8.609 +			glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_COLOR].vbo);
   8.610 +			glColorPointer(vattr[MESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0);
   8.611 +			glEnableClientState(GL_COLOR_ARRAY);
   8.612 +		}
   8.613 +#endif
   8.614 +	}
   8.615 +	glBindBuffer(GL_ARRAY_BUFFER, 0);
   8.616 +
   8.617 +	return true;
   8.618 +}
   8.619 +
   8.620 +void Mesh::draw() const
   8.621 +{
   8.622 +	if(!pre_draw()) return;
   8.623 +
   8.624 +	if(ibo_valid) {
   8.625 +		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
   8.626 +		glDrawElements(GL_TRIANGLES, nfaces * 3, GL_UNSIGNED_INT, 0);
   8.627 +		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
   8.628 +	} else {
   8.629 +		glDrawArrays(GL_TRIANGLES, 0, nverts);
   8.630 +	}
   8.631 +
   8.632 +	post_draw();
   8.633 +}
   8.634 +
   8.635 +void Mesh::post_draw() const
   8.636 +{
   8.637 +	if(cur_sdr && use_custom_sdr_attr) {
   8.638 +		// rendered with shaders
   8.639 +		for(int i=0; i<NUM_MESH_ATTR; i++) {
   8.640 +			int loc = global_sdr_loc[i];
   8.641 +			if(loc >= 0 && vattr[i].vbo_valid) {
   8.642 +				glDisableVertexAttribArray(loc);
   8.643 +			}
   8.644 +		}
   8.645 +	} else {
   8.646 +#ifndef GL_ES_VERSION_2_0
   8.647 +		// rendered with fixed-function
   8.648 +		glDisableClientState(GL_VERTEX_ARRAY);
   8.649 +		if(vattr[MESH_ATTR_NORMAL].vbo_valid) {
   8.650 +			glDisableClientState(GL_NORMAL_ARRAY);
   8.651 +		}
   8.652 +		if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) {
   8.653 +			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
   8.654 +		}
   8.655 +		if(vattr[MESH_ATTR_COLOR].vbo_valid) {
   8.656 +			glDisableClientState(GL_COLOR_ARRAY);
   8.657 +		}
   8.658 +#endif
   8.659 +	}
   8.660 +}
   8.661 +
   8.662 +void Mesh::draw_wire() const
   8.663 +{
   8.664 +	if(!pre_draw()) return;
   8.665 +
   8.666 +	((Mesh*)this)->update_wire_ibo();
   8.667 +
   8.668 +	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo);
   8.669 +	glDrawElements(GL_LINES, nfaces * 6, GL_UNSIGNED_INT, 0);
   8.670 +	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
   8.671 +
   8.672 +	post_draw();
   8.673 +}
   8.674 +
   8.675 +void Mesh::draw_vertices() const
   8.676 +{
   8.677 +	if(!pre_draw()) return;
   8.678 +
   8.679 +	glDrawArrays(GL_POINTS, 0, nverts);
   8.680 +
   8.681 +	post_draw();
   8.682 +}
   8.683 +
   8.684 +void Mesh::draw_normals() const
   8.685 +{
   8.686 +#ifdef USE_OLDGL
   8.687 +	int cur_sdr = 0;
   8.688 +	glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
   8.689 +
   8.690 +	Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
   8.691 +	Vec3 *norm = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
   8.692 +	if(!varr || !norm) {
   8.693 +		return;
   8.694 +	}
   8.695 +
   8.696 +	glBegin(GL_LINES);
   8.697 +	if(cur_sdr && use_custom_sdr_attr) {
   8.698 +		int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX];
   8.699 +		if(vert_loc < 0) {
   8.700 +			glEnd();
   8.701 +			return;
   8.702 +		}
   8.703 +
   8.704 +		for(size_t i=0; i<nverts; i++) {
   8.705 +			glVertexAttrib3f(vert_loc, varr[i].x, varr[i].y, varr[i].z);
   8.706 +			Vec3 end = varr[i] + norm[i] * vis_vecsize;
   8.707 +			glVertexAttrib3f(vert_loc, end.x, end.y, end.z);
   8.708 +		}
   8.709 +	} else {
   8.710 +		for(size_t i=0; i<nverts; i++) {
   8.711 +			glVertex3f(varr[i].x, varr[i].y, varr[i].z);
   8.712 +			Vec3 end = varr[i] + norm[i] * vis_vecsize;
   8.713 +			glVertex3f(end.x, end.y, end.z);
   8.714 +		}
   8.715 +	}
   8.716 +	glEnd();
   8.717 +#endif	// USE_OLDGL
   8.718 +}
   8.719 +
   8.720 +void Mesh::draw_tangents() const
   8.721 +{
   8.722 +#ifdef USE_OLDGL
   8.723 +	int cur_sdr = 0;
   8.724 +	glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
   8.725 +
   8.726 +	Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
   8.727 +	Vec3 *tang = (Vec3*)get_attrib_data(MESH_ATTR_TANGENT);
   8.728 +	if(!varr || !tang) {
   8.729 +		return;
   8.730 +	}
   8.731 +
   8.732 +	glBegin(GL_LINES);
   8.733 +	if(cur_sdr && use_custom_sdr_attr) {
   8.734 +		int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX];
   8.735 +		if(vert_loc < 0) {
   8.736 +			glEnd();
   8.737 +			return;
   8.738 +		}
   8.739 +
   8.740 +		for(size_t i=0; i<nverts; i++) {
   8.741 +			glVertexAttrib3f(vert_loc, varr[i].x, varr[i].y, varr[i].z);
   8.742 +			Vec3 end = varr[i] + tang[i] * vis_vecsize;
   8.743 +			glVertexAttrib3f(vert_loc, end.x, end.y, end.z);
   8.744 +		}
   8.745 +	} else {
   8.746 +		for(size_t i=0; i<nverts; i++) {
   8.747 +			glVertex3f(varr[i].x, varr[i].y, varr[i].z);
   8.748 +			Vec3 end = varr[i] + tang[i] * vis_vecsize;
   8.749 +			glVertex3f(end.x, end.y, end.z);
   8.750 +		}
   8.751 +	}
   8.752 +	glEnd();
   8.753 +#endif	// USE_OLDGL
   8.754 +}
   8.755 +
   8.756 +void Mesh::get_aabbox(Vec3 *vmin, Vec3 *vmax) const
   8.757 +{
   8.758 +	if(!aabb_valid) {
   8.759 +		((Mesh*)this)->calc_aabb();
   8.760 +	}
   8.761 +	*vmin = aabb.min;
   8.762 +	*vmax = aabb.max;
   8.763 +}
   8.764 +
   8.765 +const AABox &Mesh::get_aabbox() const
   8.766 +{
   8.767 +	if(!aabb_valid) {
   8.768 +		((Mesh*)this)->calc_aabb();
   8.769 +	}
   8.770 +	return aabb;
   8.771 +}
   8.772 +
   8.773 +float Mesh::get_bsphere(Vec3 *center, float *rad) const
   8.774 +{
   8.775 +	if(!bsph_valid) {
   8.776 +		((Mesh*)this)->calc_bsph();
   8.777 +	}
   8.778 +	*center = bsph.center;
   8.779 +	*rad = bsph.radius;
   8.780 +	return bsph.radius;
   8.781 +}
   8.782 +
   8.783 +const Sphere &Mesh::get_bsphere() const
   8.784 +{
   8.785 +	if(!bsph_valid) {
   8.786 +		((Mesh*)this)->calc_bsph();
   8.787 +	}
   8.788 +	return bsph;
   8.789 +}
   8.790 +
   8.791 +/// static function
   8.792 +void Mesh::set_intersect_mode(unsigned int mode)
   8.793 +{
   8.794 +	Mesh::intersect_mode = mode;
   8.795 +}
   8.796 +
   8.797 +/// static function
   8.798 +unsigned int Mesh::get_intersect_mode()
   8.799 +{
   8.800 +	return Mesh::intersect_mode;
   8.801 +}
   8.802 +
   8.803 +/// static function
   8.804 +void Mesh::set_vertex_select_distance(float dist)
   8.805 +{
   8.806 +	Mesh::vertex_sel_dist = dist;
   8.807 +}
   8.808 +
   8.809 +/// static function
   8.810 +float Mesh::get_vertex_select_distance()
   8.811 +{
   8.812 +	return Mesh::vertex_sel_dist;
   8.813 +}
   8.814 +
   8.815 +bool Mesh::intersect(const Ray &ray, HitPoint *hit) const
   8.816 +{
   8.817 +	assert((Mesh::intersect_mode & (ISECT_VERTICES | ISECT_FACE)) != (ISECT_VERTICES | ISECT_FACE));
   8.818 +
   8.819 +	const Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
   8.820 +	const Vec3 *narr = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
   8.821 +	if(!varr) {
   8.822 +		return false;
   8.823 +	}
   8.824 +	const unsigned int *idxarr = get_index_data();
   8.825 +
   8.826 +	// first test with the bounding box
   8.827 +	AABox box;
   8.828 +	get_aabbox(&box.min, &box.max);
   8.829 +	if(!box.intersect(ray)) {
   8.830 +		return false;
   8.831 +	}
   8.832 +
   8.833 +	HitPoint nearest_hit;
   8.834 +	nearest_hit.dist = FLT_MAX;
   8.835 +	nearest_hit.obj = 0;
   8.836 +
   8.837 +	if(Mesh::intersect_mode & ISECT_VERTICES) {
   8.838 +		// we asked for "intersections" with the vertices of the mesh
   8.839 +		long nearest_vidx = -1;
   8.840 +		float thres_sq = Mesh::vertex_sel_dist * Mesh::vertex_sel_dist;
   8.841 +
   8.842 +		for(unsigned int i=0; i<nverts; i++) {
   8.843 +
   8.844 +			if((Mesh::intersect_mode & ISECT_FRONT) && dot(narr[i], ray.dir) > 0) {
   8.845 +				continue;
   8.846 +			}
   8.847 +
   8.848 +			// project the vertex onto the ray line
   8.849 +			float t = dot(varr[i] - ray.origin, ray.dir);
   8.850 +			Vec3 vproj = ray.origin + ray.dir * t;
   8.851 +
   8.852 +			float dist_sq = length_sq(vproj - varr[i]);
   8.853 +			if(dist_sq < thres_sq) {
   8.854 +				if(!hit) {
   8.855 +					return true;
   8.856 +				}
   8.857 +				if(t < nearest_hit.dist) {
   8.858 +					nearest_hit.dist = t;
   8.859 +					nearest_vidx = i;
   8.860 +				}
   8.861 +			}
   8.862 +		}
   8.863 +
   8.864 +		if(nearest_vidx != -1) {
   8.865 +			hitvert = varr[nearest_vidx];
   8.866 +			nearest_hit.obj = &hitvert;
   8.867 +		}
   8.868 +
   8.869 +	} else {
   8.870 +		// regular intersection test with polygons
   8.871 +
   8.872 +		for(unsigned int i=0; i<nfaces; i++) {
   8.873 +			Triangle face(i, varr, idxarr);
   8.874 +
   8.875 +			// ignore back-facing polygons if the mode flags include ISECT_FRONT
   8.876 +			if((Mesh::intersect_mode & ISECT_FRONT) && dot(face.get_normal(), ray.dir) > 0) {
   8.877 +				continue;
   8.878 +			}
   8.879 +
   8.880 +			HitPoint fhit;
   8.881 +			if(face.intersect(ray, hit ? &fhit : 0)) {
   8.882 +				if(!hit) {
   8.883 +					return true;
   8.884 +				}
   8.885 +				if(fhit.dist < nearest_hit.dist) {
   8.886 +					nearest_hit = fhit;
   8.887 +					hitface = face;
   8.888 +				}
   8.889 +			}
   8.890 +		}
   8.891 +	}
   8.892 +
   8.893 +	if(nearest_hit.obj) {
   8.894 +		if(hit) {
   8.895 +			*hit = nearest_hit;
   8.896 +
   8.897 +			// if we are interested in the mesh and not the faces set obj to this
   8.898 +			if(Mesh::intersect_mode & ISECT_FACE) {
   8.899 +				hit->obj = &hitface;
   8.900 +			} else if(Mesh::intersect_mode & ISECT_VERTICES) {
   8.901 +				hit->obj = &hitvert;
   8.902 +			} else {
   8.903 +				hit->obj = this;
   8.904 +			}
   8.905 +		}
   8.906 +		return true;
   8.907 +	}
   8.908 +	return false;
   8.909 +}
   8.910 +
   8.911 +
   8.912 +// texture coordinate manipulation
   8.913 +void Mesh::texcoord_apply_xform(const Mat4 &xform)
   8.914 +{
   8.915 +	if(!has_attrib(MESH_ATTR_TEXCOORD)) {
   8.916 +		return;
   8.917 +	}
   8.918 +
   8.919 +	for(unsigned int i=0; i<nverts; i++) {
   8.920 +		Vec4 tc = get_attrib(MESH_ATTR_TEXCOORD, i);
   8.921 +		set_attrib(MESH_ATTR_TEXCOORD, i, xform * tc);
   8.922 +	}
   8.923 +}
   8.924 +
   8.925 +void Mesh::texcoord_gen_plane(const Vec3 &norm, const Vec3 &tang)
   8.926 +{
   8.927 +	if(!nverts) return;
   8.928 +
   8.929 +	if(!has_attrib(MESH_ATTR_TEXCOORD)) {
   8.930 +		// allocate texture coordinate attribute array
   8.931 +		set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
   8.932 +	}
   8.933 +
   8.934 +	Vec3 n = normalize(norm);
   8.935 +	Vec3 b = normalize(cross(n, tang));
   8.936 +	Vec3 t = cross(b, n);
   8.937 +
   8.938 +	for(unsigned int i=0; i<nverts; i++) {
   8.939 +		Vec3 pos = get_attrib(MESH_ATTR_VERTEX, i).xyz();
   8.940 +
   8.941 +		// distance along the tangent direction
   8.942 +		float u = dot(pos, t);
   8.943 +		// distance along the bitangent direction
   8.944 +		float v = dot(pos, b);
   8.945 +
   8.946 +		set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(u, v, 0, 1));
   8.947 +	}
   8.948 +}
   8.949 +
   8.950 +void Mesh::texcoord_gen_box()
   8.951 +{
   8.952 +	if(!nverts || !has_attrib(MESH_ATTR_NORMAL)) return;
   8.953 +
   8.954 +	if(!has_attrib(MESH_ATTR_TEXCOORD)) {
   8.955 +		// allocate texture coordinate attribute array
   8.956 +		set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
   8.957 +	}
   8.958 +
   8.959 +	for(unsigned int i=0; i<nverts; i++) {
   8.960 +		Vec3 pos = Vec3(get_attrib(MESH_ATTR_VERTEX, i)) * 0.5 + Vec3(0.5, 0.5, 0.5);
   8.961 +		Vec3 norm = get_attrib(MESH_ATTR_NORMAL, i).xyz();
   8.962 +
   8.963 +		float abs_nx = fabs(norm.x);
   8.964 +		float abs_ny = fabs(norm.y);
   8.965 +		float abs_nz = fabs(norm.z);
   8.966 +		int dom = abs_nx > abs_ny && abs_nx > abs_nz ? 0 : (abs_ny > abs_nz ? 1 : 2);
   8.967 +
   8.968 +		float uv[2], *uvptr = uv;
   8.969 +		for(int j=0; j<3; j++) {
   8.970 +			if(j == dom) continue;	// skip dominant axis
   8.971 +
   8.972 +			*uvptr++ = pos[j];
   8.973 +		}
   8.974 +		set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(uv[0], uv[1], 0, 1));
   8.975 +	}
   8.976 +}
   8.977 +
   8.978 +void Mesh::texcoord_gen_cylinder()
   8.979 +{
   8.980 +	if(!nverts) return;
   8.981 +
   8.982 +	if(!has_attrib(MESH_ATTR_TEXCOORD)) {
   8.983 +		// allocate texture coordinate attribute array
   8.984 +		set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
   8.985 +	}
   8.986 +
   8.987 +	for(unsigned int i=0; i<nverts; i++) {
   8.988 +		Vec3 pos = get_attrib(MESH_ATTR_VERTEX, i).xyz();
   8.989 +
   8.990 +		float rho = sqrt(pos.x * pos.x + pos.z * pos.z);
   8.991 +		float theta = rho == 0.0 ? 0.0 : atan2(pos.z / rho, pos.x / rho);
   8.992 +
   8.993 +		float u = theta / (2.0 * M_PI) + 0.5;
   8.994 +		float v = pos.y;
   8.995 +
   8.996 +		set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(u, v, 0, 1));
   8.997 +	}
   8.998 +}
   8.999 +
  8.1000 +
  8.1001 +bool Mesh::dump(const char *fname) const
  8.1002 +{
  8.1003 +	FILE *fp = fopen(fname, "wb");
  8.1004 +	if(fp) {
  8.1005 +		bool res = dump(fp);
  8.1006 +		fclose(fp);
  8.1007 +		return res;
  8.1008 +	}
  8.1009 +	return false;
  8.1010 +}
  8.1011 +
  8.1012 +bool Mesh::dump(FILE *fp) const
  8.1013 +{
  8.1014 +	if(!has_attrib(MESH_ATTR_VERTEX)) {
  8.1015 +		return false;
  8.1016 +	}
  8.1017 +
  8.1018 +	fprintf(fp, "VERTEX ATTRIBUTES\n");
  8.1019 +	static const char *label[] = { "pos", "nor", "tan", "tex", "col", "bw", "bid" };
  8.1020 +	static const char *elemfmt[] = { 0, " %s(%g)", " %s(%g, %g)", " %s(%g, %g, %g)", " %s(%g, %g, %g, %g)", 0 };
  8.1021 +
  8.1022 +	for(int i=0; i<(int)nverts; i++) {
  8.1023 +		fprintf(fp, "%5u:", i);
  8.1024 +		for(int j=0; j<NUM_MESH_ATTR; j++) {
  8.1025 +			if(has_attrib(j)) {
  8.1026 +				Vec4 v = get_attrib(j, i);
  8.1027 +				int nelem = vattr[j].nelem;
  8.1028 +				fprintf(fp, elemfmt[nelem], label[j], v.x, v.y, v.z, v.w);
  8.1029 +			}
  8.1030 +		}
  8.1031 +		fputc('\n', fp);
  8.1032 +	}
  8.1033 +
  8.1034 +	if(is_indexed()) {
  8.1035 +		const unsigned int *idx = get_index_data();
  8.1036 +		int numidx = get_index_count();
  8.1037 +		int numtri = numidx / 3;
  8.1038 +		assert(numidx % 3 == 0);
  8.1039 +
  8.1040 +		fprintf(fp, "FACES\n");
  8.1041 +
  8.1042 +		for(int i=0; i<numtri; i++) {
  8.1043 +			fprintf(fp, "%5d: %d %d %d\n", i, idx[0], idx[1], idx[2]);
  8.1044 +			idx += 3;
  8.1045 +		}
  8.1046 +	}
  8.1047 +	return true;
  8.1048 +}
  8.1049 +
  8.1050 +bool Mesh::dump_obj(const char *fname) const
  8.1051 +{
  8.1052 +	FILE *fp = fopen(fname, "wb");
  8.1053 +	if(fp) {
  8.1054 +		bool res = dump_obj(fp);
  8.1055 +		fclose(fp);
  8.1056 +		return res;
  8.1057 +	}
  8.1058 +	return false;
  8.1059 +}
  8.1060 +
  8.1061 +bool Mesh::dump_obj(FILE *fp) const
  8.1062 +{
  8.1063 +	if(!has_attrib(MESH_ATTR_VERTEX)) {
  8.1064 +		return false;
  8.1065 +	}
  8.1066 +
  8.1067 +	for(int i=0; i<(int)nverts; i++) {
  8.1068 +		Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
  8.1069 +		fprintf(fp, "v %g %g %g\n", v.x, v.y, v.z);
  8.1070 +	}
  8.1071 +
  8.1072 +	if(has_attrib(MESH_ATTR_NORMAL)) {
  8.1073 +		for(int i=0; i<(int)nverts; i++) {
  8.1074 +			Vec4 v = get_attrib(MESH_ATTR_NORMAL, i);
  8.1075 +			fprintf(fp, "vn %g %g %g\n", v.x, v.y, v.z);
  8.1076 +		}
  8.1077 +	}
  8.1078 +
  8.1079 +	if(has_attrib(MESH_ATTR_TEXCOORD)) {
  8.1080 +		for(int i=0; i<(int)nverts; i++) {
  8.1081 +			Vec4 v = get_attrib(MESH_ATTR_TEXCOORD, i);
  8.1082 +			fprintf(fp, "vt %g %g\n", v.x, v.y);
  8.1083 +		}
  8.1084 +	}
  8.1085 +
  8.1086 +	if(is_indexed()) {
  8.1087 +		const unsigned int *idxptr = get_index_data();
  8.1088 +		int numidx = get_index_count();
  8.1089 +		int numtri = numidx / 3;
  8.1090 +		assert(numidx % 3 == 0);
  8.1091 +
  8.1092 +		for(int i=0; i<numtri; i++) {
  8.1093 +			fputc('f', fp);
  8.1094 +			for(int j=0; j<3; j++) {
  8.1095 +				unsigned int idx = *idxptr++ + 1;
  8.1096 +				fprintf(fp, " %u/%u/%u", idx, idx, idx);
  8.1097 +			}
  8.1098 +			fputc('\n', fp);
  8.1099 +		}
  8.1100 +	} else {
  8.1101 +		int numtri = nverts / 3;
  8.1102 +		unsigned int idx = 1;
  8.1103 +		for(int i=0; i<numtri; i++) {
  8.1104 +			fputc('f', fp);
  8.1105 +			for(int j=0; j<3; j++) {
  8.1106 +				fprintf(fp, " %u/%u/%u", idx, idx, idx);
  8.1107 +				++idx;
  8.1108 +			}
  8.1109 +			fputc('\n', fp);
  8.1110 +		}
  8.1111 +	}
  8.1112 +	return true;
  8.1113 +}
  8.1114 +
  8.1115 +// ------ private member functions ------
  8.1116 +
  8.1117 +void Mesh::calc_aabb()
  8.1118 +{
  8.1119 +	// the cast is to force calling the const version which doesn't invalidate
  8.1120 +	if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) {
  8.1121 +		return;
  8.1122 +	}
  8.1123 +
  8.1124 +	aabb.min = Vec3(FLT_MAX, FLT_MAX, FLT_MAX);
  8.1125 +	aabb.max = -aabb.min;
  8.1126 +
  8.1127 +	for(unsigned int i=0; i<nverts; i++) {
  8.1128 +		Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
  8.1129 +		for(int j=0; j<3; j++) {
  8.1130 +			if(v[j] < aabb.min[j]) {
  8.1131 +				aabb.min[j] = v[j];
  8.1132 +			}
  8.1133 +			if(v[j] > aabb.max[j]) {
  8.1134 +				aabb.max[j] = v[j];
  8.1135 +			}
  8.1136 +		}
  8.1137 +	}
  8.1138 +	aabb_valid = true;
  8.1139 +}
  8.1140 +
  8.1141 +void Mesh::calc_bsph()
  8.1142 +{
  8.1143 +	// the cast is to force calling the const version which doesn't invalidate
  8.1144 +	if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) {
  8.1145 +		return;
  8.1146 +	}
  8.1147 +
  8.1148 +	Vec3 v;
  8.1149 +	bsph.center = Vec3(0, 0, 0);
  8.1150 +
  8.1151 +	// first find the center
  8.1152 +	for(unsigned int i=0; i<nverts; i++) {
  8.1153 +		v = get_attrib(MESH_ATTR_VERTEX, i).xyz();
  8.1154 +		bsph.center += v;
  8.1155 +	}
  8.1156 +	bsph.center /= (float)nverts;
  8.1157 +
  8.1158 +	bsph.radius = 0.0f;
  8.1159 +	for(unsigned int i=0; i<nverts; i++) {
  8.1160 +		v = get_attrib(MESH_ATTR_VERTEX, i).xyz();
  8.1161 +		float dist_sq = length_sq(v - bsph.center);
  8.1162 +		if(dist_sq > bsph.radius) {
  8.1163 +			bsph.radius = dist_sq;
  8.1164 +		}
  8.1165 +	}
  8.1166 +	bsph.radius = sqrt(bsph.radius);
  8.1167 +
  8.1168 +	bsph_valid = true;
  8.1169 +}
  8.1170 +
  8.1171 +void Mesh::update_buffers()
  8.1172 +{
  8.1173 +	for(int i=0; i<NUM_MESH_ATTR; i++) {
  8.1174 +		if(has_attrib(i) && !vattr[i].vbo_valid) {
  8.1175 +			glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
  8.1176 +			glBufferData(GL_ARRAY_BUFFER, nverts * vattr[i].nelem * sizeof(float), &vattr[i].data[0], GL_STATIC_DRAW);
  8.1177 +			vattr[i].vbo_valid = true;
  8.1178 +		}
  8.1179 +	}
  8.1180 +	glBindBuffer(GL_ARRAY_BUFFER, 0);
  8.1181 +
  8.1182 +	if(idata_valid && !ibo_valid) {
  8.1183 +		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
  8.1184 +		glBufferData(GL_ELEMENT_ARRAY_BUFFER, nfaces * 3 * sizeof(unsigned int), &idata[0], GL_STATIC_DRAW);
  8.1185 +		ibo_valid = true;
  8.1186 +	}
  8.1187 +	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  8.1188 +}
  8.1189 +
  8.1190 +void Mesh::update_wire_ibo()
  8.1191 +{
  8.1192 +	update_buffers();
  8.1193 +
  8.1194 +	if(wire_ibo_valid) {
  8.1195 +		return;
  8.1196 +	}
  8.1197 +
  8.1198 +	if(!wire_ibo) {
  8.1199 +		glGenBuffers(1, &wire_ibo);
  8.1200 +	}
  8.1201 +
  8.1202 +	unsigned int *wire_idxarr = new unsigned int[nfaces * 6];
  8.1203 +	unsigned int *dest = wire_idxarr;
  8.1204 +
  8.1205 +	if(ibo_valid) {
  8.1206 +		// we're dealing with an indexed mesh
  8.1207 +		const unsigned int *idxarr = ((const Mesh*)this)->get_index_data();
  8.1208 +
  8.1209 +		for(unsigned int i=0; i<nfaces; i++) {
  8.1210 +			*dest++ = idxarr[0];
  8.1211 +			*dest++ = idxarr[1];
  8.1212 +			*dest++ = idxarr[1];
  8.1213 +			*dest++ = idxarr[2];
  8.1214 +			*dest++ = idxarr[2];
  8.1215 +			*dest++ = idxarr[0];
  8.1216 +			idxarr += 3;
  8.1217 +		}
  8.1218 +	} else {
  8.1219 +		// not an indexed mesh ...
  8.1220 +		for(unsigned int i=0; i<nfaces; i++) {
  8.1221 +			int vidx = i * 3;
  8.1222 +			*dest++ = vidx;
  8.1223 +			*dest++ = vidx + 1;
  8.1224 +			*dest++ = vidx + 1;
  8.1225 +			*dest++ = vidx + 2;
  8.1226 +			*dest++ = vidx + 2;
  8.1227 +			*dest++ = vidx;
  8.1228 +		}
  8.1229 +	}
  8.1230 +
  8.1231 +	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo);
  8.1232 +	glBufferData(GL_ELEMENT_ARRAY_BUFFER, nfaces * 6 * sizeof(unsigned int), wire_idxarr, GL_STATIC_DRAW);
  8.1233 +	delete [] wire_idxarr;
  8.1234 +	wire_ibo_valid = true;
  8.1235 +	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  8.1236 +}
  8.1237 +
  8.1238 +
  8.1239 +// ------ class Triangle ------
  8.1240 +Triangle::Triangle()
  8.1241 +{
  8.1242 +	normal_valid = false;
  8.1243 +	id = -1;
  8.1244 +}
  8.1245 +
  8.1246 +Triangle::Triangle(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2)
  8.1247 +{
  8.1248 +	v[0] = v0;
  8.1249 +	v[1] = v1;
  8.1250 +	v[2] = v2;
  8.1251 +	normal_valid = false;
  8.1252 +	id = -1;
  8.1253 +}
  8.1254 +
  8.1255 +Triangle::Triangle(int n, const Vec3 *varr, const unsigned int *idxarr)
  8.1256 +{
  8.1257 +	if(idxarr) {
  8.1258 +		v[0] = varr[idxarr[n * 3]];
  8.1259 +		v[1] = varr[idxarr[n * 3 + 1]];
  8.1260 +		v[2] = varr[idxarr[n * 3 + 2]];
  8.1261 +	} else {
  8.1262 +		v[0] = varr[n * 3];
  8.1263 +		v[1] = varr[n * 3 + 1];
  8.1264 +		v[2] = varr[n * 3 + 2];
  8.1265 +	}
  8.1266 +	normal_valid = false;
  8.1267 +	id = n;
  8.1268 +}
  8.1269 +
  8.1270 +void Triangle::calc_normal()
  8.1271 +{
  8.1272 +	normal = normalize(cross(v[1] - v[0], v[2] - v[0]));
  8.1273 +	normal_valid = true;
  8.1274 +}
  8.1275 +
  8.1276 +const Vec3 &Triangle::get_normal() const
  8.1277 +{
  8.1278 +	if(!normal_valid) {
  8.1279 +		((Triangle*)this)->calc_normal();
  8.1280 +	}
  8.1281 +	return normal;
  8.1282 +}
  8.1283 +
  8.1284 +void Triangle::transform(const Mat4 &xform)
  8.1285 +{
  8.1286 +	v[0] = xform * v[0];
  8.1287 +	v[1] = xform * v[1];
  8.1288 +	v[2] = xform * v[2];
  8.1289 +	normal_valid = false;
  8.1290 +}
  8.1291 +
  8.1292 +void Triangle::draw() const
  8.1293 +{
  8.1294 +	Vec3 n[3];
  8.1295 +	n[0] = n[1] = n[2] = get_normal();
  8.1296 +
  8.1297 +	int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX);
  8.1298 +	int nloc = Mesh::get_attrib_location(MESH_ATTR_NORMAL);
  8.1299 +
  8.1300 +	glEnableVertexAttribArray(vloc);
  8.1301 +	glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x);
  8.1302 +	glVertexAttribPointer(nloc, 3, GL_FLOAT, GL_FALSE, 0, &n[0].x);
  8.1303 +
  8.1304 +	glDrawArrays(GL_TRIANGLES, 0, 3);
  8.1305 +
  8.1306 +	glDisableVertexAttribArray(vloc);
  8.1307 +	glDisableVertexAttribArray(nloc);
  8.1308 +}
  8.1309 +
  8.1310 +void Triangle::draw_wire() const
  8.1311 +{
  8.1312 +	static const int idxarr[] = {0, 1, 1, 2, 2, 0};
  8.1313 +	int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX);
  8.1314 +
  8.1315 +	glEnableVertexAttribArray(vloc);
  8.1316 +	glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x);
  8.1317 +
  8.1318 +	glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, idxarr);
  8.1319 +
  8.1320 +	glDisableVertexAttribArray(vloc);
  8.1321 +}
  8.1322 +
  8.1323 +Vec3 Triangle::calc_barycentric(const Vec3 &pos) const
  8.1324 +{
  8.1325 +	Vec3 norm = get_normal();
  8.1326 +
  8.1327 +	float area_sq = fabs(dot(cross(v[1] - v[0], v[2] - v[0]), norm));
  8.1328 +	if(area_sq < 1e-5) {
  8.1329 +		return Vec3(0, 0, 0);
  8.1330 +	}
  8.1331 +
  8.1332 +	float asq0 = fabs(dot(cross(v[1] - pos, v[2] - pos), norm));
  8.1333 +	float asq1 = fabs(dot(cross(v[2] - pos, v[0] - pos), norm));
  8.1334 +	float asq2 = fabs(dot(cross(v[0] - pos, v[1] - pos), norm));
  8.1335 +
  8.1336 +	return Vec3(asq0 / area_sq, asq1 / area_sq, asq2 / area_sq);
  8.1337 +}
  8.1338 +
  8.1339 +bool Triangle::intersect(const Ray &ray, HitPoint *hit) const
  8.1340 +{
  8.1341 +	Vec3 normal = get_normal();
  8.1342 +
  8.1343 +	float ndotdir = dot(ray.dir, normal);
  8.1344 +	if(fabs(ndotdir) < 1e-4) {
  8.1345 +		return false;
  8.1346 +	}
  8.1347 +
  8.1348 +	Vec3 vertdir = v[0] - ray.origin;
  8.1349 +	float t = dot(normal, vertdir) / ndotdir;
  8.1350 +
  8.1351 +	Vec3 pos = ray.origin + ray.dir * t;
  8.1352 +	Vec3 bary = calc_barycentric(pos);
  8.1353 +
  8.1354 +	if(bary.x + bary.y + bary.z > 1.00001) {
  8.1355 +		return false;
  8.1356 +	}
  8.1357 +
  8.1358 +	if(hit) {
  8.1359 +		hit->dist = t;
  8.1360 +		hit->pos = ray.origin + ray.dir * t;
  8.1361 +		hit->normal = normal;
  8.1362 +		hit->obj = this;
  8.1363 +	}
  8.1364 +	return true;
  8.1365 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/mesh.h	Thu Jul 27 20:36:12 2017 +0300
     9.3 @@ -0,0 +1,243 @@
     9.4 +#ifndef MESH_H_
     9.5 +#define MESH_H_
     9.6 +
     9.7 +#include <stdio.h>
     9.8 +#include <string>
     9.9 +#include <vector>
    9.10 +#include <gmath/gmath.h>
    9.11 +#include "geom.h"
    9.12 +
    9.13 +enum {
    9.14 +	MESH_ATTR_VERTEX,
    9.15 +	MESH_ATTR_NORMAL,
    9.16 +	MESH_ATTR_TANGENT,
    9.17 +	MESH_ATTR_TEXCOORD,
    9.18 +	MESH_ATTR_COLOR,
    9.19 +	MESH_ATTR_BONEWEIGHTS,
    9.20 +	MESH_ATTR_BONEIDX,
    9.21 +
    9.22 +	NUM_MESH_ATTR
    9.23 +};
    9.24 +
    9.25 +// intersection mode flags
    9.26 +enum {
    9.27 +	ISECT_DEFAULT	= 0,	// default (whole mesh, all intersections)
    9.28 +	ISECT_FRONT		= 1,	// front-faces only
    9.29 +	ISECT_FACE		= 2,	// return intersected face pointer instead of mesh
    9.30 +	ISECT_VERTICES	= 4		// return (?) TODO
    9.31 +};
    9.32 +
    9.33 +//class XFormNode;
    9.34 +
    9.35 +
    9.36 +class Triangle {
    9.37 +public:
    9.38 +	Vec3 v[3];
    9.39 +	Vec3 normal;
    9.40 +	bool normal_valid;
    9.41 +	int id;
    9.42 +
    9.43 +	Triangle();
    9.44 +	Triangle(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2);
    9.45 +	Triangle(int n, const Vec3 *varr, const unsigned int *idxarr = 0);
    9.46 +
    9.47 +	/// calculate normal (quite expensive)
    9.48 +	void calc_normal();
    9.49 +	const Vec3 &get_normal() const;
    9.50 +
    9.51 +	void transform(const Mat4 &xform);
    9.52 +
    9.53 +	void draw() const;
    9.54 +	void draw_wire() const;
    9.55 +
    9.56 +	/// calculate barycentric coordinates of a point
    9.57 +	Vec3 calc_barycentric(const Vec3 &pos) const;
    9.58 +
    9.59 +	bool intersect(const Ray &ray, HitPoint *hit = 0) const;
    9.60 +};
    9.61 +
    9.62 +
    9.63 +class Mesh {
    9.64 +private:
    9.65 +	std::string name;
    9.66 +	unsigned int nverts, nfaces;
    9.67 +
    9.68 +	// current value for each attribute for the immedate mode
    9.69 +	// interface.
    9.70 +	Vec4 cur_val[NUM_MESH_ATTR];
    9.71 +
    9.72 +	unsigned int buffer_objects[NUM_MESH_ATTR + 1];
    9.73 +
    9.74 +	// vertex attribute data and buffer objects
    9.75 +	struct {
    9.76 +		int nelem;					// number of elements per attribute range: [1, 4]
    9.77 +		std::vector<float> data;
    9.78 +		unsigned int vbo;
    9.79 +		mutable bool vbo_valid;		// if this is false, the vbo needs updating from the data
    9.80 +		mutable bool data_valid;	// if this is false, the data needs to be pulled from the vbo
    9.81 +		//int sdr_loc;
    9.82 +	} vattr[NUM_MESH_ATTR];
    9.83 +
    9.84 +	static int global_sdr_loc[NUM_MESH_ATTR];
    9.85 +
    9.86 +	//std::vector<XFormNode*> bones;	// bones affecting this mesh
    9.87 +
    9.88 +	// index data and buffer object
    9.89 +	std::vector<unsigned int> idata;
    9.90 +	unsigned int ibo;
    9.91 +	mutable bool ibo_valid;
    9.92 +	mutable bool idata_valid;
    9.93 +
    9.94 +	// index buffer object for wireframe rendering (constructed on demand)
    9.95 +	unsigned int wire_ibo;
    9.96 +	mutable bool wire_ibo_valid;
    9.97 +
    9.98 +	// axis-aligned bounding box
    9.99 +	mutable AABox aabb;
   9.100 +	mutable bool aabb_valid;
   9.101 +
   9.102 +	// bounding sphere
   9.103 +	mutable Sphere bsph;
   9.104 +	mutable bool bsph_valid;
   9.105 +
   9.106 +	// keeps the last intersected face
   9.107 +	mutable Triangle hitface;
   9.108 +	// keeps the last intersected vertex position
   9.109 +	mutable Vec3 hitvert;
   9.110 +
   9.111 +	void calc_aabb();
   9.112 +	void calc_bsph();
   9.113 +
   9.114 +	static unsigned int intersect_mode;
   9.115 +	static float vertex_sel_dist;
   9.116 +
   9.117 +	static float vis_vecsize;
   9.118 +
   9.119 +	/// update the VBOs after data has changed (invalid vbo/ibo)
   9.120 +	void update_buffers();
   9.121 +	/// construct/update the wireframe index buffer (called from draw_wire).
   9.122 +	void update_wire_ibo();
   9.123 +
   9.124 +	mutable int cur_sdr;
   9.125 +	bool pre_draw() const;
   9.126 +	void post_draw() const;
   9.127 +
   9.128 +
   9.129 +public:
   9.130 +	static bool use_custom_sdr_attr;
   9.131 +
   9.132 +	Mesh();
   9.133 +	~Mesh();
   9.134 +
   9.135 +	Mesh(const Mesh &rhs);
   9.136 +	Mesh &operator =(const Mesh &rhs);
   9.137 +	bool clone(const Mesh &m);
   9.138 +
   9.139 +	void set_name(const char *name);
   9.140 +	const char *get_name() const;
   9.141 +
   9.142 +	bool has_attrib(int attr) const;
   9.143 +	bool is_indexed() const;
   9.144 +
   9.145 +	// clears everything about this mesh, and returns to the newly constructed state
   9.146 +	void clear();
   9.147 +
   9.148 +	// access the vertex attribute data
   9.149 +	// if vdata == 0, space is just allocated
   9.150 +	float *set_attrib_data(int attrib, int nelem, unsigned int num, const float *vdata = 0); // invalidates vbo
   9.151 +	float *get_attrib_data(int attrib);	// invalidates vbo
   9.152 +	const float *get_attrib_data(int attrib) const;
   9.153 +
   9.154 +	// simple access to any particular attribute
   9.155 +	void set_attrib(int attrib, int idx, const Vec4 &v); // invalidates vbo
   9.156 +	Vec4 get_attrib(int attrib, int idx) const;
   9.157 +
   9.158 +	int get_attrib_count(int attrib) const;
   9.159 +
   9.160 +	// ... same for index data
   9.161 +	unsigned int *set_index_data(int num, const unsigned int *indices = 0); // invalidates ibo
   9.162 +	unsigned int *get_index_data();	// invalidates ibo
   9.163 +	const unsigned int *get_index_data() const;
   9.164 +
   9.165 +	int get_index_count() const;
   9.166 +
   9.167 +	void append(const Mesh &mesh);
   9.168 +
   9.169 +	// immediate-mode style mesh construction interface
   9.170 +	void vertex(float x, float y, float z);
   9.171 +	void normal(float nx, float ny, float nz);
   9.172 +	void tangent(float tx, float ty, float tz);
   9.173 +	void texcoord(float u, float v, float w);
   9.174 +	void boneweights(float w1, float w2, float w3, float w4);
   9.175 +	void boneidx(int idx1, int idx2, int idx3, int idx4);
   9.176 +
   9.177 +	int get_poly_count() const;
   9.178 +
   9.179 +	/* apply a transformation to the vertices and its inverse-transpose
   9.180 +	 * to the normals and tangents.
   9.181 +	 */
   9.182 +	void apply_xform(const Mat4 &xform);
   9.183 +	void apply_xform(const Mat4 &xform, const Mat4 &dir_xform);
   9.184 +
   9.185 +	void flip();	// both faces and normals
   9.186 +	void flip_faces();
   9.187 +	void flip_normals();
   9.188 +
   9.189 +	// adds a bone and returns its index
   9.190 +	/*int add_bone(XFormNode *bone);
   9.191 +	const XFormNode *get_bone(int idx) const;
   9.192 +	int get_bones_count() const;*/
   9.193 +
   9.194 +	// access the shader attribute locations
   9.195 +	static void set_attrib_location(int attr, int loc);
   9.196 +	static int get_attrib_location(int attr);
   9.197 +	static void clear_attrib_locations();
   9.198 +
   9.199 +	static void set_vis_vecsize(float sz);
   9.200 +	static float get_vis_vecsize();
   9.201 +
   9.202 +	void draw() const;
   9.203 +	void draw_wire() const;
   9.204 +	void draw_vertices() const;
   9.205 +	void draw_normals() const;
   9.206 +	void draw_tangents() const;
   9.207 +
   9.208 +	/** get the bounding box in local space. The result will be cached, and subsequent
   9.209 +	 * calls will return the same box. The cache gets invalidated by any functions that can affect
   9.210 +	 * the vertex data (non-const variant of get_attrib_data(MESH_ATTR_VERTEX, ...) included).
   9.211 +	 * @{ */
   9.212 +	void get_aabbox(Vec3 *vmin, Vec3 *vmax) const;
   9.213 +	const AABox &get_aabbox() const;
   9.214 +	/// @}
   9.215 +
   9.216 +	/** get the bounding sphere in local space. The result will be cached, and subsequent
   9.217 +	 * calls will return the same box. The cache gets invalidated by any functions that can affect
   9.218 +	 * the vertex data (non-const variant of get_attrib_data(MESH_ATTR_VERTEX, ...) included).
   9.219 +	 * @{ */
   9.220 +	float get_bsphere(Vec3 *center, float *rad) const;
   9.221 +	const Sphere &get_bsphere() const;
   9.222 +
   9.223 +	static void set_intersect_mode(unsigned int mode);
   9.224 +	static unsigned int get_intersect_mode();
   9.225 +	static void set_vertex_select_distance(float dist);
   9.226 +	static float get_vertex_select_distance();
   9.227 +
   9.228 +	/** Find the intersection between the mesh and a ray.
   9.229 +	 * XXX Brute force at the moment, not intended to be used for anything other than picking in tools.
   9.230 +	 *     If you intend to use it in a speed-critical part of the code, you'll *have* to optimize it!
   9.231 +	 */
   9.232 +	bool intersect(const Ray &ray, HitPoint *hit = 0) const;
   9.233 +
   9.234 +	// texture coordinate manipulation
   9.235 +	void texcoord_apply_xform(const Mat4 &xform);
   9.236 +	void texcoord_gen_plane(const Vec3 &norm, const Vec3 &tang);
   9.237 +	void texcoord_gen_box();
   9.238 +	void texcoord_gen_cylinder();
   9.239 +
   9.240 +	bool dump(const char *fname) const;
   9.241 +	bool dump(FILE *fp) const;
   9.242 +	bool dump_obj(const char *fname) const;
   9.243 +	bool dump_obj(FILE *fp) const;
   9.244 +};
   9.245 +
   9.246 +#endif	// MESH_H_
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/src/meshgen.cc	Thu Jul 27 20:36:12 2017 +0300
    10.3 @@ -0,0 +1,881 @@
    10.4 +#include <stdio.h>
    10.5 +#include "meshgen.h"
    10.6 +#include "mesh.h"
    10.7 +
    10.8 +// -------- sphere --------
    10.9 +
   10.10 +#define SURAD(u)	((u) * 2.0 * M_PI)
   10.11 +#define SVRAD(v)	((v) * M_PI)
   10.12 +
   10.13 +static Vec3 sphvec(float theta, float phi)
   10.14 +{
   10.15 +	return Vec3(sin(theta) * sin(phi),
   10.16 +			cos(phi),
   10.17 +			cos(theta) * sin(phi));
   10.18 +}
   10.19 +
   10.20 +void gen_sphere(Mesh *mesh, float rad, int usub, int vsub, float urange, float vrange)
   10.21 +{
   10.22 +	if(usub < 4) usub = 4;
   10.23 +	if(vsub < 2) vsub = 2;
   10.24 +
   10.25 +	int uverts = usub + 1;
   10.26 +	int vverts = vsub + 1;
   10.27 +
   10.28 +	int num_verts = uverts * vverts;
   10.29 +	int num_quads = usub * vsub;
   10.30 +	int num_tri = num_quads * 2;
   10.31 +
   10.32 +	mesh->clear();
   10.33 +	Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
   10.34 +	Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
   10.35 +	Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
   10.36 +	Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
   10.37 +	unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
   10.38 +
   10.39 +	float du = urange / (float)(uverts - 1);
   10.40 +	float dv = vrange / (float)(vverts - 1);
   10.41 +
   10.42 +	float u = 0.0;
   10.43 +	for(int i=0; i<uverts; i++) {
   10.44 +		float theta = u * 2.0 * M_PI;
   10.45 +
   10.46 +		float v = 0.0;
   10.47 +		for(int j=0; j<vverts; j++) {
   10.48 +			float phi = v * M_PI;
   10.49 +
   10.50 +			Vec3 pos = sphvec(theta, phi);
   10.51 +
   10.52 +			*varr++ = pos * rad;
   10.53 +			*narr++ = pos;
   10.54 +			*tarr++ = normalize(sphvec(theta + 0.1f, (float)M_PI / 2.0f) - sphvec(theta - 0.1f, (float)M_PI / 2.0f));
   10.55 +			*uvarr++ = Vec2(u * urange, v * vrange);
   10.56 +
   10.57 +			if(i < usub && j < vsub) {
   10.58 +				int idx = i * vverts + j;
   10.59 +				*idxarr++ = idx;
   10.60 +				*idxarr++ = idx + 1;
   10.61 +				*idxarr++ = idx + vverts + 1;
   10.62 +
   10.63 +				*idxarr++ = idx;
   10.64 +				*idxarr++ = idx + vverts + 1;
   10.65 +				*idxarr++ = idx + vverts;
   10.66 +			}
   10.67 +
   10.68 +			v += dv;
   10.69 +		}
   10.70 +		u += du;
   10.71 +	}
   10.72 +}
   10.73 +
   10.74 +// ------ geosphere ------
   10.75 +#define PHI		1.618034
   10.76 +
   10.77 +static Vec3 icosa_pt[] = {
   10.78 +	Vec3(PHI, 1, 0),
   10.79 +	Vec3(-PHI, 1, 0),
   10.80 +	Vec3(PHI, -1, 0),
   10.81 +	Vec3(-PHI, -1, 0),
   10.82 +	Vec3(1, 0, PHI),
   10.83 +	Vec3(1, 0, -PHI),
   10.84 +	Vec3(-1, 0, PHI),
   10.85 +	Vec3(-1, 0, -PHI),
   10.86 +	Vec3(0, PHI, 1),
   10.87 +	Vec3(0, -PHI, 1),
   10.88 +	Vec3(0, PHI, -1),
   10.89 +	Vec3(0, -PHI, -1)
   10.90 +};
   10.91 +enum { P11, P12, P13, P14, P21, P22, P23, P24, P31, P32, P33, P34 };
   10.92 +static int icosa_idx[] = {
   10.93 +	P11, P31, P21,
   10.94 +	P11, P22, P33,
   10.95 +	P13, P21, P32,
   10.96 +	P13, P34, P22,
   10.97 +	P12, P23, P31,
   10.98 +	P12, P33, P24,
   10.99 +	P14, P32, P23,
  10.100 +	P14, P24, P34,
  10.101 +
  10.102 +	P11, P33, P31,
  10.103 +	P12, P31, P33,
  10.104 +	P13, P32, P34,
  10.105 +	P14, P34, P32,
  10.106 +
  10.107 +	P21, P13, P11,
  10.108 +	P22, P11, P13,
  10.109 +	P23, P12, P14,
  10.110 +	P24, P14, P12,
  10.111 +
  10.112 +	P31, P23, P21,
  10.113 +	P32, P21, P23,
  10.114 +	P33, P22, P24,
  10.115 +	P34, P24, P22
  10.116 +};
  10.117 +
  10.118 +static void geosphere(std::vector<Vec3> *verts, const Vec3 &v1, const Vec3 &v2, const Vec3 &v3, int iter)
  10.119 +{
  10.120 +	if(!iter) {
  10.121 +		verts->push_back(v1);
  10.122 +		verts->push_back(v2);
  10.123 +		verts->push_back(v3);
  10.124 +		return;
  10.125 +	}
  10.126 +
  10.127 +	Vec3 v12 = normalize(v1 + v2);
  10.128 +	Vec3 v23 = normalize(v2 + v3);
  10.129 +	Vec3 v31 = normalize(v3 + v1);
  10.130 +
  10.131 +	geosphere(verts, v1, v12, v31, iter - 1);
  10.132 +	geosphere(verts, v2, v23, v12, iter - 1);
  10.133 +	geosphere(verts, v3, v31, v23, iter - 1);
  10.134 +	geosphere(verts, v12, v23, v31, iter - 1);
  10.135 +}
  10.136 +
  10.137 +void gen_geosphere(Mesh *mesh, float rad, int subdiv, bool hemi)
  10.138 +{
  10.139 +	int num_tri = (sizeof icosa_idx / sizeof *icosa_idx) / 3;
  10.140 +
  10.141 +	std::vector<Vec3> verts;
  10.142 +	for(int i=0; i<num_tri; i++) {
  10.143 +		Vec3 v[3];
  10.144 +
  10.145 +		for(int j=0; j<3; j++) {
  10.146 +			int vidx = icosa_idx[i * 3 + j];
  10.147 +			v[j] = normalize(icosa_pt[vidx]);
  10.148 +		}
  10.149 +
  10.150 +		if(hemi && (v[0].y < 0.0 || v[1].y < 0.0 || v[2].y < 0.0)) {
  10.151 +			continue;
  10.152 +		}
  10.153 +
  10.154 +		geosphere(&verts, v[0], v[1], v[2], subdiv);
  10.155 +	}
  10.156 +
  10.157 +	int num_verts = (int)verts.size();
  10.158 +
  10.159 +	mesh->clear();
  10.160 +	Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
  10.161 +	Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
  10.162 +	Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
  10.163 +	Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
  10.164 +
  10.165 +	for(int i=0; i<num_verts; i++) {
  10.166 +		*varr++ = verts[i] * rad;
  10.167 +		*narr++ = verts[i];
  10.168 +
  10.169 +		float theta = atan2(verts[i].z, verts[i].x);
  10.170 +		float phi = acos(verts[i].y);
  10.171 +
  10.172 +		*tarr++ = normalize(sphvec(theta + 0.1f, (float)M_PI / 2.0f) - sphvec(theta - 0.1f, (float)M_PI / 2.0f));
  10.173 +
  10.174 +		float u = 0.5 * theta / M_PI + 0.5;
  10.175 +		float v = phi / M_PI;
  10.176 +		*uvarr++ = Vec2(u, v);
  10.177 +	}
  10.178 +}
  10.179 +
  10.180 +// -------- torus -----------
  10.181 +static Vec3 torusvec(float theta, float phi, float mr, float rr)
  10.182 +{
  10.183 +	theta = -theta;
  10.184 +
  10.185 +	float rx = -cos(phi) * rr + mr;
  10.186 +	float ry = sin(phi) * rr;
  10.187 +	float rz = 0.0;
  10.188 +
  10.189 +	float x = rx * sin(theta) + rz * cos(theta);
  10.190 +	float y = ry;
  10.191 +	float z = -rx * cos(theta) + rz * sin(theta);
  10.192 +
  10.193 +	return Vec3(x, y, z);
  10.194 +}
  10.195 +
  10.196 +void gen_torus(Mesh *mesh, float mainrad, float ringrad, int usub, int vsub, float urange, float vrange)
  10.197 +{
  10.198 +	if(usub < 4) usub = 4;
  10.199 +	if(vsub < 2) vsub = 2;
  10.200 +
  10.201 +	int uverts = usub + 1;
  10.202 +	int vverts = vsub + 1;
  10.203 +
  10.204 +	int num_verts = uverts * vverts;
  10.205 +	int num_quads = usub * vsub;
  10.206 +	int num_tri = num_quads * 2;
  10.207 +
  10.208 +	mesh->clear();
  10.209 +	Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
  10.210 +	Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
  10.211 +	Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
  10.212 +	Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
  10.213 +	unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
  10.214 +
  10.215 +	float du = urange / (float)(uverts - 1);
  10.216 +	float dv = vrange / (float)(vverts - 1);
  10.217 +
  10.218 +	float u = 0.0;
  10.219 +	for(int i=0; i<uverts; i++) {
  10.220 +		float theta = u * 2.0 * M_PI;
  10.221 +
  10.222 +		float v = 0.0;
  10.223 +		for(int j=0; j<vverts; j++) {
  10.224 +			float phi = v * 2.0 * M_PI;
  10.225 +
  10.226 +			Vec3 pos = torusvec(theta, phi, mainrad, ringrad);
  10.227 +			Vec3 cent = torusvec(theta, phi, mainrad, 0.0);
  10.228 +
  10.229 +			*varr++ = pos;
  10.230 +			*narr++ = (pos - cent) / ringrad;
  10.231 +
  10.232 +			Vec3 pprev = torusvec(theta - 0.1f, phi, mainrad, ringrad);
  10.233 +			Vec3 pnext = torusvec(theta + 0.1f, phi, mainrad, ringrad);
  10.234 +
  10.235 +			*tarr++ = normalize(pnext - pprev);
  10.236 +			*uvarr++ = Vec2(u * urange, v * vrange);
  10.237 +
  10.238 +			if(i < usub && j < vsub) {
  10.239 +				int idx = i * vverts + j;
  10.240 +				*idxarr++ = idx;
  10.241 +				*idxarr++ = idx + 1;
  10.242 +				*idxarr++ = idx + vverts + 1;
  10.243 +
  10.244 +				*idxarr++ = idx;
  10.245 +				*idxarr++ = idx + vverts + 1;
  10.246 +				*idxarr++ = idx + vverts;
  10.247 +			}
  10.248 +
  10.249 +			v += dv;
  10.250 +		}
  10.251 +		u += du;
  10.252 +	}
  10.253 +}
  10.254 +
  10.255 +
  10.256 +// -------- cylinder --------
  10.257 +
  10.258 +static Vec3 cylvec(float theta, float height)
  10.259 +{
  10.260 +	return Vec3(sin(theta), height, cos(theta));
  10.261 +}
  10.262 +
  10.263 +void gen_cylinder(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub, float urange, float vrange)
  10.264 +{
  10.265 +	if(usub < 4) usub = 4;
  10.266 +	if(vsub < 1) vsub = 1;
  10.267 +
  10.268 +	int uverts = usub + 1;
  10.269 +	int vverts = vsub + 1;
  10.270 +
  10.271 +	int num_body_verts = uverts * vverts;
  10.272 +	int num_body_quads = usub * vsub;
  10.273 +	int num_body_tri = num_body_quads * 2;
  10.274 +
  10.275 +	int capvverts = capsub ? capsub + 1 : 0;
  10.276 +	int num_cap_verts = uverts * capvverts;
  10.277 +	int num_cap_quads = usub * capsub;
  10.278 +	int num_cap_tri = num_cap_quads * 2;
  10.279 +
  10.280 +	int num_verts = num_body_verts + num_cap_verts * 2;
  10.281 +	int num_tri = num_body_tri + num_cap_tri * 2;
  10.282 +
  10.283 +	mesh->clear();
  10.284 +	Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
  10.285 +	Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
  10.286 +	Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
  10.287 +	Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
  10.288 +	unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
  10.289 +
  10.290 +	float du = urange / (float)(uverts - 1);
  10.291 +	float dv = vrange / (float)(vverts - 1);
  10.292 +
  10.293 +	float u = 0.0;
  10.294 +	for(int i=0; i<uverts; i++) {
  10.295 +		float theta = SURAD(u);
  10.296 +
  10.297 +		float v = 0.0;
  10.298 +		for(int j=0; j<vverts; j++) {
  10.299 +			float y = (v - 0.5) * height;
  10.300 +			Vec3 pos = cylvec(theta, y);
  10.301 +
  10.302 +			*varr++ = Vec3(pos.x * rad, pos.y, pos.z * rad);
  10.303 +			*narr++ = Vec3(pos.x, 0.0, pos.z);
  10.304 +			*tarr++ = normalize(cylvec(theta + 0.1, 0.0) - cylvec(theta - 0.1, 0.0));
  10.305 +			*uvarr++ = Vec2(u * urange, v * vrange);
  10.306 +
  10.307 +			if(i < usub && j < vsub) {
  10.308 +				int idx = i * vverts + j;
  10.309 +
  10.310 +				*idxarr++ = idx;
  10.311 +				*idxarr++ = idx + vverts + 1;
  10.312 +				*idxarr++ = idx + 1;
  10.313 +
  10.314 +				*idxarr++ = idx;
  10.315 +				*idxarr++ = idx + vverts;
  10.316 +				*idxarr++ = idx + vverts + 1;
  10.317 +			}
  10.318 +
  10.319 +			v += dv;
  10.320 +		}
  10.321 +		u += du;
  10.322 +	}
  10.323 +
  10.324 +
  10.325 +	// now the cap!
  10.326 +	if(!capsub) {
  10.327 +		return;
  10.328 +	}
  10.329 +
  10.330 +	dv = 1.0 / (float)(capvverts - 1);
  10.331 +
  10.332 +	u = 0.0;
  10.333 +	for(int i=0; i<uverts; i++) {
  10.334 +		float theta = SURAD(u);
  10.335 +
  10.336 +		float v = 0.0;
  10.337 +		for(int j=0; j<capvverts; j++) {
  10.338 +			float r = v * rad;
  10.339 +
  10.340 +			Vec3 pos = cylvec(theta, height / 2.0) * r;
  10.341 +			pos.y = height / 2.0;
  10.342 +			Vec3 tang = normalize(cylvec(theta + 0.1, 0.0) - cylvec(theta - 0.1, 0.0));
  10.343 +
  10.344 +			*varr++ = pos;
  10.345 +			*narr++ = Vec3(0, 1, 0);
  10.346 +			*tarr++ = tang;
  10.347 +			*uvarr++ = Vec2(u * urange, v);
  10.348 +
  10.349 +			pos.y = -height / 2.0;
  10.350 +			*varr++ = pos;
  10.351 +			*narr++ = Vec3(0, -1, 0);
  10.352 +			*tarr++ = -tang;
  10.353 +			*uvarr++ = Vec2(u * urange, v);
  10.354 +
  10.355 +			if(i < usub && j < capsub) {
  10.356 +				unsigned int idx = num_body_verts + (i * capvverts + j) * 2;
  10.357 +
  10.358 +				unsigned int vidx[4] = {
  10.359 +					idx,
  10.360 +					idx + capvverts * 2,
  10.361 +					idx + (capvverts + 1) * 2,
  10.362 +					idx + 2
  10.363 +				};
  10.364 +
  10.365 +				*idxarr++ = vidx[0];
  10.366 +				*idxarr++ = vidx[2];
  10.367 +				*idxarr++ = vidx[1];
  10.368 +				*idxarr++ = vidx[0];
  10.369 +				*idxarr++ = vidx[3];
  10.370 +				*idxarr++ = vidx[2];
  10.371 +
  10.372 +				*idxarr++ = vidx[0] + 1;
  10.373 +				*idxarr++ = vidx[1] + 1;
  10.374 +				*idxarr++ = vidx[2] + 1;
  10.375 +				*idxarr++ = vidx[0] + 1;
  10.376 +				*idxarr++ = vidx[2] + 1;
  10.377 +				*idxarr++ = vidx[3] + 1;
  10.378 +			}
  10.379 +
  10.380 +			v += dv;
  10.381 +		}
  10.382 +		u += du;
  10.383 +	}
  10.384 +}
  10.385 +
  10.386 +// -------- cone --------
  10.387 +
  10.388 +static Vec3 conevec(float theta, float y, float height)
  10.389 +{
  10.390 +	float scale = 1.0 - y / height;
  10.391 +	return Vec3(sin(theta) * scale, y, cos(theta) * scale);
  10.392 +}
  10.393 +
  10.394 +void gen_cone(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub, float urange, float vrange)
  10.395 +{
  10.396 +	if(usub < 4) usub = 4;
  10.397 +	if(vsub < 1) vsub = 1;
  10.398 +
  10.399 +	int uverts = usub + 1;
  10.400 +	int vverts = vsub + 1;
  10.401 +
  10.402 +	int num_body_verts = uverts * vverts;
  10.403 +	int num_body_quads = usub * vsub;
  10.404 +	int num_body_tri = num_body_quads * 2;
  10.405 +
  10.406 +	int capvverts = capsub ? capsub + 1 : 0;
  10.407 +	int num_cap_verts = uverts * capvverts;
  10.408 +	int num_cap_quads = usub * capsub;
  10.409 +	int num_cap_tri = num_cap_quads * 2;
  10.410 +
  10.411 +	int num_verts = num_body_verts + num_cap_verts;
  10.412 +	int num_tri = num_body_tri + num_cap_tri;
  10.413 +
  10.414 +	mesh->clear();
  10.415 +	Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
  10.416 +	Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
  10.417 +	Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
  10.418 +	Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
  10.419 +	unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
  10.420 +
  10.421 +	float du = urange / (float)(uverts - 1);
  10.422 +	float dv = vrange / (float)(vverts - 1);
  10.423 +
  10.424 +	float u = 0.0;
  10.425 +	for(int i=0; i<uverts; i++) {
  10.426 +		float theta = SURAD(u);
  10.427 +
  10.428 +		float v = 0.0;
  10.429 +		for(int j=0; j<vverts; j++) {
  10.430 +			float y = v * height;
  10.431 +			Vec3 pos = conevec(theta, y, height);
  10.432 +
  10.433 +			Vec3 tang = normalize(conevec(theta + 0.1, 0.0, height) - conevec(theta - 0.1, 0.0, height));
  10.434 +			Vec3 bitang = normalize(conevec(theta, y + 0.1, height) - pos);
  10.435 +
  10.436 +			*varr++ = Vec3(pos.x * rad, pos.y, pos.z * rad);
  10.437 +			*narr++ = cross(tang, bitang);
  10.438 +			*tarr++ = tang;
  10.439 +			*uvarr++ = Vec2(u * urange, v * vrange);
  10.440 +
  10.441 +			if(i < usub && j < vsub) {
  10.442 +				int idx = i * vverts + j;
  10.443 +
  10.444 +				*idxarr++ = idx;
  10.445 +				*idxarr++ = idx + vverts + 1;
  10.446 +				*idxarr++ = idx + 1;
  10.447 +
  10.448 +				*idxarr++ = idx;
  10.449 +				*idxarr++ = idx + vverts;
  10.450 +				*idxarr++ = idx + vverts + 1;
  10.451 +			}
  10.452 +
  10.453 +			v += dv;
  10.454 +		}
  10.455 +		u += du;
  10.456 +	}
  10.457 +
  10.458 +
  10.459 +	// now the bottom cap!
  10.460 +	if(!capsub) {
  10.461 +		return;
  10.462 +	}
  10.463 +
  10.464 +	dv = 1.0 / (float)(capvverts - 1);
  10.465 +
  10.466 +	u = 0.0;
  10.467 +	for(int i=0; i<uverts; i++) {
  10.468 +		float theta = SURAD(u);
  10.469 +
  10.470 +		float v = 0.0;
  10.471 +		for(int j=0; j<capvverts; j++) {
  10.472 +			float r = v * rad;
  10.473 +
  10.474 +			Vec3 pos = conevec(theta, 0.0, height) * r;
  10.475 +			Vec3 tang = normalize(cylvec(theta + 0.1, 0.0) - cylvec(theta - 0.1, 0.0));
  10.476 +
  10.477 +			*varr++ = pos;
  10.478 +			*narr++ = Vec3(0, -1, 0);
  10.479 +			*tarr++ = tang;
  10.480 +			*uvarr++ = Vec2(u * urange, v);
  10.481 +
  10.482 +			if(i < usub && j < capsub) {
  10.483 +				unsigned int idx = num_body_verts + i * capvverts + j;
  10.484 +
  10.485 +				unsigned int vidx[4] = {
  10.486 +					idx,
  10.487 +					idx + capvverts,
  10.488 +					idx + (capvverts + 1),
  10.489 +					idx + 1
  10.490 +				};
  10.491 +
  10.492 +				*idxarr++ = vidx[0];
  10.493 +				*idxarr++ = vidx[1];
  10.494 +				*idxarr++ = vidx[2];
  10.495 +				*idxarr++ = vidx[0];
  10.496 +				*idxarr++ = vidx[2];
  10.497 +				*idxarr++ = vidx[3];
  10.498 +			}
  10.499 +
  10.500 +			v += dv;
  10.501 +		}
  10.502 +		u += du;
  10.503 +	}
  10.504 +}
  10.505 +
  10.506 +
  10.507 +// -------- plane --------
  10.508 +
  10.509 +void gen_plane(Mesh *mesh, float width, float height, int usub, int vsub)
  10.510 +{
  10.511 +	gen_heightmap(mesh, width, height, usub, vsub, 0);
  10.512 +}
  10.513 +
  10.514 +
  10.515 +// ----- heightmap ------
  10.516 +
  10.517 +void gen_heightmap(Mesh *mesh, float width, float height, int usub, int vsub, float (*hf)(float, float, void*), void *hfdata)
  10.518 +{
  10.519 +	if(usub < 1) usub = 1;
  10.520 +	if(vsub < 1) vsub = 1;
  10.521 +
  10.522 +	mesh->clear();
  10.523 +
  10.524 +	int uverts = usub + 1;
  10.525 +	int vverts = vsub + 1;
  10.526 +	int num_verts = uverts * vverts;
  10.527 +
  10.528 +	int num_quads = usub * vsub;
  10.529 +	int num_tri = num_quads * 2;
  10.530 +
  10.531 +	Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
  10.532 +	Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
  10.533 +	Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
  10.534 +	Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
  10.535 +	unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
  10.536 +
  10.537 +	float du = 1.0 / (float)usub;
  10.538 +	float dv = 1.0 / (float)vsub;
  10.539 +
  10.540 +	float u = 0.0;
  10.541 +	for(int i=0; i<uverts; i++) {
  10.542 +		float v = 0.0;
  10.543 +		for(int j=0; j<vverts; j++) {
  10.544 +			float x = (u - 0.5) * width;
  10.545 +			float y = (v - 0.5) * height;
  10.546 +			float z = hf ? hf(u, v, hfdata) : 0.0;
  10.547 +
  10.548 +			Vec3 normal = Vec3(0, 0, 1);
  10.549 +			if(hf) {
  10.550 +				float u1z = hf(u + du, v, hfdata);
  10.551 +				float v1z = hf(u, v + dv, hfdata);
  10.552 +
  10.553 +				Vec3 tang = Vec3(du * width, 0, u1z - z);
  10.554 +				Vec3 bitan = Vec3(0, dv * height, v1z - z);
  10.555 +				normal = normalize(cross(tang, bitan));
  10.556 +			}
  10.557 +
  10.558 +			*varr++ = Vec3(x, y, z);
  10.559 +			*narr++ = normal;
  10.560 +			*tarr++ = Vec3(1, 0, 0);
  10.561 +			*uvarr++ = Vec2(u, v);
  10.562 +
  10.563 +			if(i < usub && j < vsub) {
  10.564 +				int idx = i * vverts + j;
  10.565 +
  10.566 +				*idxarr++ = idx;
  10.567 +				*idxarr++ = idx + vverts + 1;
  10.568 +				*idxarr++ = idx + 1;
  10.569 +
  10.570 +				*idxarr++ = idx;
  10.571 +				*idxarr++ = idx + vverts;
  10.572 +				*idxarr++ = idx + vverts + 1;
  10.573 +			}
  10.574 +
  10.575 +			v += dv;
  10.576 +		}
  10.577 +		u += du;
  10.578 +	}
  10.579 +}
  10.580 +
  10.581 +// ----- box ------
  10.582 +void gen_box(Mesh *mesh, float xsz, float ysz, float zsz, int usub, int vsub)
  10.583 +{
  10.584 +	static const float face_angles[][2] = {
  10.585 +		{0, 0},
  10.586 +		{M_PI / 2.0, 0},
  10.587 +		{M_PI, 0},
  10.588 +		{3.0 * M_PI / 2.0, 0},
  10.589 +		{0, M_PI / 2.0},
  10.590 +		{0, -M_PI / 2.0}
  10.591 +	};
  10.592 +
  10.593 +	if(usub < 1) usub = 1;
  10.594 +	if(vsub < 1) vsub = 1;
  10.595 +
  10.596 +	mesh->clear();
  10.597 +
  10.598 +	for(int i=0; i<6; i++) {
  10.599 +		Mat4 xform, dir_xform;
  10.600 +		Mesh m;
  10.601 +
  10.602 +		gen_plane(&m, 1, 1, usub, vsub);
  10.603 +		xform.rotate(Vec3(face_angles[i][1], face_angles[i][0], 0));
  10.604 +		dir_xform = xform;
  10.605 +		xform.translate(Vec3(0, 0, 0.5));
  10.606 +		m.apply_xform(xform, dir_xform);
  10.607 +
  10.608 +		mesh->append(m);
  10.609 +	}
  10.610 +
  10.611 +	Mat4 scale;
  10.612 +	scale.scaling(xsz, ysz, zsz);
  10.613 +	mesh->apply_xform(scale, Mat4::identity);
  10.614 +}
  10.615 +
  10.616 +/*
  10.617 +void gen_box(Mesh *mesh, float xsz, float ysz, float zsz)
  10.618 +{
  10.619 +	mesh->clear();
  10.620 +
  10.621 +	const int num_faces = 6;
  10.622 +	int num_verts = num_faces * 4;
  10.623 +	int num_tri = num_faces * 2;
  10.624 +
  10.625 +	float x = xsz / 2.0;
  10.626 +	float y = ysz / 2.0;
  10.627 +	float z = zsz / 2.0;
  10.628 +
  10.629 +	Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
  10.630 +	Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
  10.631 +	Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
  10.632 +	Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
  10.633 +	unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
  10.634 +
  10.635 +	static const Vec2 uv[] = { Vec2(0, 0), Vec2(1, 0), Vec2(1, 1), Vec2(0, 1) };
  10.636 +
  10.637 +	// front
  10.638 +	for(int i=0; i<4; i++) {
  10.639 +		*narr++ = Vec3(0, 0, 1);
  10.640 +		*tarr++ = Vec3(1, 0, 0);
  10.641 +		*uvarr++ = uv[i];
  10.642 +	}
  10.643 +	*varr++ = Vec3(-x, -y, z);
  10.644 +	*varr++ = Vec3(x, -y, z);
  10.645 +	*varr++ = Vec3(x, y, z);
  10.646 +	*varr++ = Vec3(-x, y, z);
  10.647 +	// right
  10.648 +	for(int i=0; i<4; i++) {
  10.649 +		*narr++ = Vec3(1, 0, 0);
  10.650 +		*tarr++ = Vec3(0, 0, -1);
  10.651 +		*uvarr++ = uv[i];
  10.652 +	}
  10.653 +	*varr++ = Vec3(x, -y, z);
  10.654 +	*varr++ = Vec3(x, -y, -z);
  10.655 +	*varr++ = Vec3(x, y, -z);
  10.656 +	*varr++ = Vec3(x, y, z);
  10.657 +	// back
  10.658 +	for(int i=0; i<4; i++) {
  10.659 +		*narr++ = Vec3(0, 0, -1);
  10.660 +		*tarr++ = Vec3(-1, 0, 0);
  10.661 +		*uvarr++ = uv[i];
  10.662 +	}
  10.663 +	*varr++ = Vec3(x, -y, -z);
  10.664 +	*varr++ = Vec3(-x, -y, -z);
  10.665 +	*varr++ = Vec3(-x, y, -z);
  10.666 +	*varr++ = Vec3(x, y, -z);
  10.667 +	// left
  10.668 +	for(int i=0; i<4; i++) {
  10.669 +		*narr++ = Vec3(-1, 0, 0);
  10.670 +		*tarr++ = Vec3(0, 0, 1);
  10.671 +		*uvarr++ = uv[i];
  10.672 +	}
  10.673 +	*varr++ = Vec3(-x, -y, -z);
  10.674 +	*varr++ = Vec3(-x, -y, z);
  10.675 +	*varr++ = Vec3(-x, y, z);
  10.676 +	*varr++ = Vec3(-x, y, -z);
  10.677 +	// top
  10.678 +	for(int i=0; i<4; i++) {
  10.679 +		*narr++ = Vec3(0, 1, 0);
  10.680 +		*tarr++ = Vec3(1, 0, 0);
  10.681 +		*uvarr++ = uv[i];
  10.682 +	}
  10.683 +	*varr++ = Vec3(-x, y, z);
  10.684 +	*varr++ = Vec3(x, y, z);
  10.685 +	*varr++ = Vec3(x, y, -z);
  10.686 +	*varr++ = Vec3(-x, y, -z);
  10.687 +	// bottom
  10.688 +	for(int i=0; i<4; i++) {
  10.689 +		*narr++ = Vec3(0, -1, 0);
  10.690 +		*tarr++ = Vec3(1, 0, 0);
  10.691 +		*uvarr++ = uv[i];
  10.692 +	}
  10.693 +	*varr++ = Vec3(-x, -y, -z);
  10.694 +	*varr++ = Vec3(x, -y, -z);
  10.695 +	*varr++ = Vec3(x, -y, z);
  10.696 +	*varr++ = Vec3(-x, -y, z);
  10.697 +
  10.698 +	// index array
  10.699 +	static const int faceidx[] = {0, 1, 2, 0, 2, 3};
  10.700 +	for(int i=0; i<num_faces; i++) {
  10.701 +		for(int j=0; j<6; j++) {
  10.702 +			*idxarr++ = faceidx[j] + i * 4;
  10.703 +		}
  10.704 +	}
  10.705 +}
  10.706 +*/
  10.707 +
  10.708 +static inline Vec3 rev_vert(float u, float v, Vec2 (*rf)(float, float, void*), void *cls)
  10.709 +{
  10.710 +	Vec2 pos = rf(u, v, cls);
  10.711 +
  10.712 +	float angle = u * 2.0 * M_PI;
  10.713 +	float x = pos.x * cos(angle);
  10.714 +	float y = pos.y;
  10.715 +	float z = pos.x * sin(angle);
  10.716 +
  10.717 +	return Vec3(x, y, z);
  10.718 +}
  10.719 +
  10.720 +// ------ surface of revolution -------
  10.721 +void gen_revol(Mesh *mesh, int usub, int vsub, Vec2 (*rfunc)(float, float, void*), void *cls)
  10.722 +{
  10.723 +	gen_revol(mesh, usub, vsub, rfunc, 0, cls);
  10.724 +}
  10.725 +
  10.726 +void gen_revol(Mesh *mesh, int usub, int vsub, Vec2 (*rfunc)(float, float, void*),
  10.727 +		Vec2 (*nfunc)(float, float, void*), void *cls)
  10.728 +{
  10.729 +	if(!rfunc) return;
  10.730 +	if(usub < 3) usub = 3;
  10.731 +	if(vsub < 1) vsub = 1;
  10.732 +
  10.733 +	mesh->clear();
  10.734 +
  10.735 +	int uverts = usub + 1;
  10.736 +	int vverts = vsub + 1;
  10.737 +	int num_verts = uverts * vverts;
  10.738 +
  10.739 +	int num_quads = usub * vsub;
  10.740 +	int num_tri = num_quads * 2;
  10.741 +
  10.742 +	Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
  10.743 +	Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
  10.744 +	Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
  10.745 +	Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
  10.746 +	unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
  10.747 +
  10.748 +	float du = 1.0 / (float)(uverts - 1);
  10.749 +	float dv = 1.0 / (float)(vverts - 1);
  10.750 +
  10.751 +	float u = 0.0;
  10.752 +	for(int i=0; i<uverts; i++) {
  10.753 +		float v = 0.0;
  10.754 +		for(int j=0; j<vverts; j++) {
  10.755 +			Vec3 pos = rev_vert(u, v, rfunc, cls);
  10.756 +
  10.757 +			Vec3 nextu = rev_vert(fmod(u + du, 1.0), v, rfunc, cls);
  10.758 +			Vec3 tang = nextu - pos;
  10.759 +			if(length_sq(tang) < 1e-6) {
  10.760 +				float new_v = v > 0.5 ? v - dv * 0.25 : v + dv * 0.25;
  10.761 +				nextu = rev_vert(fmod(u + du, 1.0), new_v, rfunc, cls);
  10.762 +				tang = nextu - pos;
  10.763 +			}
  10.764 +
  10.765 +			Vec3 normal;
  10.766 +			if(nfunc) {
  10.767 +				normal = rev_vert(u, v, nfunc, cls);
  10.768 +			} else {
  10.769 +				Vec3 nextv = rev_vert(u, v + dv, rfunc, cls);
  10.770 +				Vec3 bitan = nextv - pos;
  10.771 +				if(length_sq(bitan) < 1e-6) {
  10.772 +					nextv = rev_vert(u, v - dv, rfunc, cls);
  10.773 +					bitan = pos - nextv;
  10.774 +				}
  10.775 +
  10.776 +				normal = cross(tang, bitan);
  10.777 +			}
  10.778 +
  10.779 +			*varr++ = pos;
  10.780 +			*narr++ = normalize(normal);
  10.781 +			*tarr++ = normalize(tang);
  10.782 +			*uvarr++ = Vec2(u, v);
  10.783 +
  10.784 +			if(i < usub && j < vsub) {
  10.785 +				int idx = i * vverts + j;
  10.786 +
  10.787 +				*idxarr++ = idx;
  10.788 +				*idxarr++ = idx + vverts + 1;
  10.789 +				*idxarr++ = idx + 1;
  10.790 +
  10.791 +				*idxarr++ = idx;
  10.792 +				*idxarr++ = idx + vverts;
  10.793 +				*idxarr++ = idx + vverts + 1;
  10.794 +			}
  10.795 +
  10.796 +			v += dv;
  10.797 +		}
  10.798 +		u += du;
  10.799 +	}
  10.800 +}
  10.801 +
  10.802 +
  10.803 +static inline Vec3 sweep_vert(float u, float v, float height, Vec2 (*sf)(float, float, void*), void *cls)
  10.804 +{
  10.805 +	Vec2 pos = sf(u, v, cls);
  10.806 +
  10.807 +	float x = pos.x;
  10.808 +	float y = v * height;
  10.809 +	float z = pos.y;
  10.810 +
  10.811 +	return Vec3(x, y, z);
  10.812 +}
  10.813 +
  10.814 +// ---- sweep shape along a path ----
  10.815 +void gen_sweep(Mesh *mesh, float height, int usub, int vsub, Vec2 (*sfunc)(float, float, void*), void *cls)
  10.816 +{
  10.817 +	if(!sfunc) return;
  10.818 +	if(usub < 3) usub = 3;
  10.819 +	if(vsub < 1) vsub = 1;
  10.820 +
  10.821 +	mesh->clear();
  10.822 +
  10.823 +	int uverts = usub + 1;
  10.824 +	int vverts = vsub + 1;
  10.825 +	int num_verts = uverts * vverts;
  10.826 +
  10.827 +	int num_quads = usub * vsub;
  10.828 +	int num_tri = num_quads * 2;
  10.829 +
  10.830 +	Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0);
  10.831 +	Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0);
  10.832 +	Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0);
  10.833 +	Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0);
  10.834 +	unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0);
  10.835 +
  10.836 +	float du = 1.0 / (float)(uverts - 1);
  10.837 +	float dv = 1.0 / (float)(vverts - 1);
  10.838 +
  10.839 +	float u = 0.0;
  10.840 +	for(int i=0; i<uverts; i++) {
  10.841 +		float v = 0.0;
  10.842 +		for(int j=0; j<vverts; j++) {
  10.843 +			Vec3 pos = sweep_vert(u, v, height, sfunc, cls);
  10.844 +
  10.845 +			Vec3 nextu = sweep_vert(fmod(u + du, 1.0), v, height, sfunc, cls);
  10.846 +			Vec3 tang = nextu - pos;
  10.847 +			if(length_sq(tang) < 1e-6) {
  10.848 +				float new_v = v > 0.5 ? v - dv * 0.25 : v + dv * 0.25;
  10.849 +				nextu = sweep_vert(fmod(u + du, 1.0), new_v, height, sfunc, cls);
  10.850 +				tang = nextu - pos;
  10.851 +			}
  10.852 +
  10.853 +			Vec3 normal;
  10.854 +			Vec3 nextv = sweep_vert(u, v + dv, height, sfunc, cls);
  10.855 +			Vec3 bitan = nextv - pos;
  10.856 +			if(length_sq(bitan) < 1e-6) {
  10.857 +				nextv = sweep_vert(u, v - dv, height, sfunc, cls);
  10.858 +				bitan = pos - nextv;
  10.859 +			}
  10.860 +
  10.861 +			normal = cross(tang, bitan);
  10.862 +
  10.863 +			*varr++ = pos;
  10.864 +			*narr++ = normalize(normal);
  10.865 +			*tarr++ = normalize(tang);
  10.866 +			*uvarr++ = Vec2(u, v);
  10.867 +
  10.868 +			if(i < usub && j < vsub) {
  10.869 +				int idx = i * vverts + j;
  10.870 +
  10.871 +				*idxarr++ = idx;
  10.872 +				*idxarr++ = idx + vverts + 1;
  10.873 +				*idxarr++ = idx + 1;
  10.874 +
  10.875 +				*idxarr++ = idx;
  10.876 +				*idxarr++ = idx + vverts;
  10.877 +				*idxarr++ = idx + vverts + 1;
  10.878 +			}
  10.879 +
  10.880 +			v += dv;
  10.881 +		}
  10.882 +		u += du;
  10.883 +	}
  10.884 +}
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/src/meshgen.h	Thu Jul 27 20:36:12 2017 +0300
    11.3 @@ -0,0 +1,23 @@
    11.4 +#ifndef MESHGEN_H_
    11.5 +#define MESHGEN_H_
    11.6 +
    11.7 +#include <gmath/gmath.h>
    11.8 +
    11.9 +class Mesh;
   11.10 +
   11.11 +void gen_sphere(Mesh *mesh, float rad, int usub, int vsub, float urange = 1.0, float vrange = 1.0);
   11.12 +void gen_geosphere(Mesh *mesh, float rad, int subdiv, bool hemi = false);
   11.13 +void gen_torus(Mesh *mesh, float mainrad, float ringrad, int usub, int vsub, float urange = 1.0, float vrange = 1.0);
   11.14 +void gen_cylinder(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub = 0, float urange = 1.0, float vrange = 1.0);
   11.15 +void gen_cone(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub = 0, float urange = 1.0, float vrange = 1.0);
   11.16 +void gen_plane(Mesh *mesh, float width, float height, int usub = 1, int vsub = 1);
   11.17 +void gen_heightmap(Mesh *mesh, float width, float height, int usub, int vsub, float (*hf)(float, float, void*), void *hfdata = 0);
   11.18 +void gen_box(Mesh *mesh, float xsz, float ysz, float zsz, int usub = 1, int vsub = 1);
   11.19 +
   11.20 +void gen_revol(Mesh *mesh, int usub, int vsub, Vec2 (*rfunc)(float, float, void*), void *cls = 0);
   11.21 +void gen_revol(Mesh *mesh, int usub, int vsub, Vec2 (*rfunc)(float, float, void*), Vec2 (*nfunc)(float, float, void*), void *cls);
   11.22 +
   11.23 +/* callback args: (float u, float v, void *cls) -> Vec2 XZ offset u,v in [0, 1] */
   11.24 +void gen_sweep(Mesh *mesh, float height, int usub, int vsub, Vec2 (*sfunc)(float, float, void*), void *cls = 0);
   11.25 +
   11.26 +#endif	// MESHGEN_H_
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/src/opengl.cc	Thu Jul 27 20:36:12 2017 +0300
    12.3 @@ -0,0 +1,8 @@
    12.4 +#include <string.h>
    12.5 +#include "opengl.h"
    12.6 +
    12.7 +bool init_opengl()
    12.8 +{
    12.9 +	glewInit();
   12.10 +	return true;
   12.11 +}
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/src/opengl.h	Thu Jul 27 20:36:12 2017 +0300
    13.3 @@ -0,0 +1,20 @@
    13.4 +#ifndef OPENGL_H_
    13.5 +#define OPENGL_H_
    13.6 +
    13.7 +#ifdef WIN32
    13.8 +#include <windows.h>
    13.9 +#endif
   13.10 +
   13.11 +#include <GL/glew.h>
   13.12 +
   13.13 +#ifdef __APPLE__
   13.14 +#include <OpenGL/gl.h>
   13.15 +#else
   13.16 +#define GL_GLEXT_PROTOTYPES 1
   13.17 +#include <GL/gl.h>
   13.18 +#include <GL/glu.h>
   13.19 +#endif
   13.20 +
   13.21 +bool init_opengl();
   13.22 +
   13.23 +#endif	// OPENGL_H_
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/src/texture.cc	Thu Jul 27 20:36:12 2017 +0300
    14.3 @@ -0,0 +1,103 @@
    14.4 +#include <stdio.h>
    14.5 +#include <imago2.h>
    14.6 +#include "texture.h"
    14.7 +#include "opengl.h"
    14.8 +
    14.9 +Texture::Texture()
   14.10 +{
   14.11 +	width = height = tex_width = tex_height = 0;
   14.12 +	tex = 0;
   14.13 +}
   14.14 +
   14.15 +Texture::~Texture()
   14.16 +{
   14.17 +	if(tex) {
   14.18 +		glDeleteTextures(1, &tex);
   14.19 +	}
   14.20 +}
   14.21 +
   14.22 +static unsigned int next_pow2(unsigned int x)
   14.23 +{
   14.24 +	--x;
   14.25 +	x |= x >> 1;
   14.26 +	x |= x >> 2;
   14.27 +	x |= x >> 4;
   14.28 +	x |= x >> 8;
   14.29 +	x |= x >> 16;
   14.30 +	return x + 1;
   14.31 +}
   14.32 +
   14.33 +bool Texture::load(const char *fname)
   14.34 +{
   14.35 +	img_pixmap img;
   14.36 +	img_init(&img);
   14.37 +	if(img_load(&img, fname) == -1) {
   14.38 +		fprintf(stderr, "failed to load texture: %s\n", fname);
   14.39 +		img_destroy(&img);
   14.40 +		return false;
   14.41 +	}
   14.42 +
   14.43 +	unsigned int intfmt = img_glintfmt(&img);
   14.44 +	unsigned int pixfmt = img_glfmt(&img);
   14.45 +	unsigned int pixtype = img_gltype(&img);
   14.46 +
   14.47 +	// if we have the sRGB extension, change the internal formats to sRGB
   14.48 +	if(GLEW_EXT_texture_sRGB) {
   14.49 +		switch(intfmt) {
   14.50 +		case 3:
   14.51 +		case GL_RGB:
   14.52 +			intfmt = GL_SRGB_EXT;
   14.53 +			break;
   14.54 +		case 4:
   14.55 +		case GL_RGBA:
   14.56 +			intfmt = GL_SRGB_ALPHA;
   14.57 +			break;
   14.58 +		case 1:
   14.59 +		case GL_LUMINANCE:
   14.60 +			intfmt = GL_SLUMINANCE;
   14.61 +			break;
   14.62 +		default:
   14.63 +			break;
   14.64 +		}
   14.65 +	}
   14.66 +
   14.67 +	width = img.width;
   14.68 +	height = img.height;
   14.69 +	tex_width = next_pow2(width);
   14.70 +	tex_height = next_pow2(height);
   14.71 +
   14.72 +	if(!tex) {
   14.73 +		glGenTextures(1, &tex);
   14.74 +	}
   14.75 +	glBindTexture(GL_TEXTURE_2D, tex);
   14.76 +
   14.77 +	if(GLEW_SGIS_generate_mipmap) {
   14.78 +		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
   14.79 +	} else {
   14.80 +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
   14.81 +	}
   14.82 +
   14.83 +	glTexImage2D(GL_TEXTURE_2D, 0, intfmt, tex_width, tex_height, 0, pixfmt, pixtype, 0);
   14.84 +	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, pixfmt, pixtype, img.pixels);
   14.85 +
   14.86 +	tmat.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1);
   14.87 +	return true;
   14.88 +}
   14.89 +
   14.90 +const Mat4 &Texture::texture_matrix() const
   14.91 +{
   14.92 +	return tmat;
   14.93 +}
   14.94 +
   14.95 +void Texture::bind(bool loadmat) const
   14.96 +{
   14.97 +	if(!tex) return;
   14.98 +
   14.99 +	if(loadmat) {
  14.100 +		glMatrixMode(GL_TEXTURE);
  14.101 +		glLoadMatrixf(tmat[0]);
  14.102 +		glMatrixMode(GL_MODELVIEW);
  14.103 +	}
  14.104 +
  14.105 +	glBindTexture(GL_TEXTURE_2D, tex);
  14.106 +}
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/src/texture.h	Thu Jul 27 20:36:12 2017 +0300
    15.3 @@ -0,0 +1,23 @@
    15.4 +#ifndef TEXTURE_H_
    15.5 +#define TEXTURE_H_
    15.6 +
    15.7 +#include <gmath/gmath.h>
    15.8 +
    15.9 +class Texture {
   15.10 +private:
   15.11 +	int width, height;
   15.12 +	int tex_width, tex_height;
   15.13 +	unsigned int tex;
   15.14 +	Mat4 tmat;
   15.15 +
   15.16 +public:
   15.17 +	Texture();
   15.18 +	~Texture();
   15.19 +
   15.20 +	bool load(const char *fname);
   15.21 +
   15.22 +	const Mat4 &texture_matrix() const;
   15.23 +	void bind(bool loadmat = true) const;
   15.24 +};
   15.25 +
   15.26 +#endif	// TEXTURE_H_