From: John Tsiombikas Date: Sun, 12 Aug 2018 15:39:22 +0000 (+0300) Subject: tesselation X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=ld42_outofspace;a=commitdiff_plain;h=066942396f5fbac07e608bc5b23bb6d3cdccc42a tesselation --- diff --git a/sdr/field.p.glsl b/sdr/field.p.glsl index b8888c9..1969702 100644 --- a/sdr/field.p.glsl +++ b/sdr/field.p.glsl @@ -1,12 +1,13 @@ #version 410 compatibility -uniform sampler2D gvis_tex, field_tex; +uniform sampler2D gvis_tex; +uniform float gvis_scale; void main() { - vec4 gridcol = texture2D(gvis_tex, gl_TexCoord[0].st); - float field = -texture2D(field_tex, gl_TexCoord[1].st).x; + vec2 uv = gl_TexCoord[0].st * vec2(gvis_scale, gvis_scale); + vec4 gridcol = texture2D(gvis_tex, uv); - gl_FragColor.rgb = gridcol.rgb + vec3(field, field, field); + gl_FragColor.rgb = gridcol.rgb; gl_FragColor.a = 1.0; } diff --git a/sdr/field.te.glsl b/sdr/field.te.glsl index cb01515..eaf485d 100644 --- a/sdr/field.te.glsl +++ b/sdr/field.te.glsl @@ -2,21 +2,22 @@ layout(quads, ccw) in; +uniform sampler2D field_tex; +uniform float field_scale; + void main() { vec4 v1 = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x); vec4 v2 = mix(gl_in[3].gl_Position, gl_in[2].gl_Position, gl_TessCoord.x); vec4 pos = mix(v1, v2, gl_TessCoord.y); - vec4 top0 = mix(gl_in[0].gl_TexCoord[0], gl_in[1].gl_TexCoord[0], gl_TessCoord.x); - vec4 bot0 = mix(gl_in[3].gl_TexCoord[0], gl_in[2].gl_TexCoord[0], gl_TessCoord.x); - vec4 res0 = mix(top0, bot0, gl_TessCoord.y); + vec4 top = mix(gl_in[0].gl_TexCoord[0], gl_in[1].gl_TexCoord[0], gl_TessCoord.x); + vec4 bot = mix(gl_in[3].gl_TexCoord[0], gl_in[2].gl_TexCoord[0], gl_TessCoord.x); + vec4 uv = mix(top, bot, gl_TessCoord.y); - vec4 top1 = mix(gl_in[0].gl_TexCoord[1], gl_in[1].gl_TexCoord[1], gl_TessCoord.x); - vec4 bot1 = mix(gl_in[3].gl_TexCoord[1], gl_in[2].gl_TexCoord[1], gl_TessCoord.x); - vec4 res1 = mix(top1, bot1, gl_TessCoord.y); + float field = -texture2D(field_tex, uv.st).x; + pos.y = field * field_scale; gl_Position = gl_ModelViewProjectionMatrix * pos; - gl_TexCoord[0] = res0; - gl_TexCoord[1] = res1; + gl_TexCoord[0] = uv; } diff --git a/sdr/field.v.glsl b/sdr/field.v.glsl index 26760af..cecfabb 100644 --- a/sdr/field.v.glsl +++ b/sdr/field.v.glsl @@ -4,5 +4,4 @@ void main() { gl_Position = gl_Vertex; gl_TexCoord[0] = gl_MultiTexCoord0; - gl_TexCoord[1] = gl_MultiTexCoord1; } diff --git a/src/gamescr.cc b/src/gamescr.cc index d51c16a..032b3b9 100644 --- a/src/gamescr.cc +++ b/src/gamescr.cc @@ -32,6 +32,16 @@ struct Rect { int width, height; }; +struct QuadMesh { + Vec3 *v; + Vec2 *uv; + uint16_t *idx; + + int num_verts, num_idx, num_quads; + + unsigned int vbo_v, vbo_uv, ibo; +}; + #define SIM_DT 0.016 #define GRID_SIZE 2048 @@ -52,6 +62,10 @@ struct Rect { static int pos_to_grid(float x, float y); static Vec2 grid_to_pos(int gx, int gy); +static void destroy_quadmesh(QuadMesh *m); +static void draw_quadmesh(const QuadMesh *m, unsigned int prim = GL_QUADS); +static void gen_quad_plane(QuadMesh *mesh, float width, float height, int usub, int vsub); + static float grid[GRID_SIZE * GRID_SIZE]; static Particle grid_part[GRID_SIZE * GRID_SIZE]; static Texture *grid_tex; @@ -60,6 +74,10 @@ static std::vector emitters; static Texture *gvis_tex; // texture tile for visualizing a grid static unsigned int field_sdr; +static int tess_level = 32; +static float field_scale = 16.0f; + +static QuadMesh field_mesh; static float cam_theta; static float cam_dist = 100.0f; @@ -97,18 +115,26 @@ bool GameScreen::init() } set_uniform_int(field_sdr, "gvis_tex", 0); set_uniform_int(field_sdr, "field_tex", 1); - set_uniform_int(field_sdr, "tess_level", 2); + set_uniform_float(field_sdr, "gvis_scale", FIELD_SIZE / 32.0f); + set_uniform_int(field_sdr, "tess_level", tess_level); + set_uniform_float(field_sdr, "field_scale", field_scale); + + gen_quad_plane(&field_mesh, FIELD_SIZE, FIELD_SIZE, 32, 32); // XXX DBG emit_place_pos = Vec2(0, 0); emit_place_pending = true; + assert(glGetError() == GL_NO_ERROR); return true; } void GameScreen::destroy() { + free_program(field_sdr); delete gvis_tex; + delete grid_tex; + destroy_quadmesh(&field_mesh); } static void calc_contrib_bounds(const Emitter *em, Rect *rect) @@ -242,34 +268,16 @@ void GameScreen::draw() bind_texture(grid_tex, 1); glUseProgram(field_sdr); + assert(glGetError() == GL_NO_ERROR); - float maxu = FIELD_SIZE / 32.0f; - float maxv = FIELD_SIZE / 32.0f; - float hsz = FIELD_SIZE * 0.5f; - - glBegin(GL_QUADS); - glColor3f(1, 1, 1); - - glMultiTexCoord2f(0, 0, 0); - glMultiTexCoord2f(1, 0, 0); - glVertex3f(-hsz, 0, -hsz); - - glMultiTexCoord2f(0, maxu, 0); - glMultiTexCoord2f(1, 1, 0); - glVertex3f(hsz, 0, -hsz); - - glMultiTexCoord2f(0, maxu, maxv); - glMultiTexCoord2f(1, 1, 1); - glVertex3f(hsz, 0, hsz); - - glMultiTexCoord2f(0, 0, maxv); - glMultiTexCoord2f(1, 0, 1); - glVertex3f(-hsz, 0, hsz); - glEnd(); + glPatchParameteri(GL_PATCH_VERTICES, 4); + draw_quadmesh(&field_mesh, GL_PATCHES); glUseProgram(0); glPopAttrib(); + + assert(glGetError() == GL_NO_ERROR); } void GameScreen::reshape(int x, int y) @@ -286,6 +294,39 @@ void GameScreen::keyboard(int key, bool pressed) pop_screen(); break; + case ']': + if(tess_level < glcaps.max_tess_level) { + tess_level++; + printf("tessellation level: %d\n", tess_level); + set_uniform_int(field_sdr, "tess_level", tess_level); + glUseProgram(0); + } + break; + + case '[': + if(tess_level > 1) { + tess_level--; + printf("tessellation level: %d\n", tess_level); + set_uniform_int(field_sdr, "tess_level", tess_level); + glUseProgram(0); + } + break; + + case '=': + field_scale += 0.5; + printf("field scale: %f\n", field_scale); + set_uniform_float(field_sdr, "field_scale", field_scale); + break; + + case '-': + field_scale -= 0.5; + if(field_scale < 0.0f) { + field_scale = 0.0f; + } + printf("field scale: %f\n", field_scale); + set_uniform_float(field_sdr, "field_scale", field_scale); + break; + default: break; } @@ -340,3 +381,97 @@ static Vec2 grid_to_pos(int gx, int gy) return Vec2(x, y); } + +static void destroy_quadmesh(QuadMesh *m) +{ + glDeleteBuffers(1, &m->vbo_v); + glDeleteBuffers(1, &m->vbo_uv); + glDeleteBuffers(1, &m->ibo); + + delete [] m->v; + delete [] m->uv; + delete [] m->idx; +} + +static void draw_quadmesh(const QuadMesh *m, unsigned int prim) +{ + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glBindBuffer(GL_ARRAY_BUFFER, m->vbo_v); + glVertexPointer(3, GL_FLOAT, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, m->vbo_uv); + glTexCoordPointer(2, GL_FLOAT, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ibo); + glDrawElements(prim, m->num_idx, GL_UNSIGNED_SHORT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +static void gen_quad_plane(QuadMesh *m, float width, float height, int usub, int vsub) +{ + Vec3 *vptr; + Vec2 *uvptr; + uint16_t *iptr; + + if(usub < 1) usub = 1; + if(vsub < 1) vsub = 1; + + int uverts = usub + 1; + int vverts = vsub + 1; + m->num_verts = uverts * vverts; + m->num_quads = usub * vsub; + m->num_idx = m->num_quads * 4; + + vptr = m->v = new Vec3[m->num_verts]; + uvptr = m->uv = new Vec2[m->num_verts]; + iptr = m->idx = new uint16_t[m->num_idx]; + + float du = 1.0f / (float)usub; + float dv = 1.0f / (float)vsub; + + float u = 0.0f; + for(int i=0; ivbo_v); + glBindBuffer(GL_ARRAY_BUFFER, m->vbo_v); + glBufferData(GL_ARRAY_BUFFER, m->num_verts * 3 * sizeof(float), m->v, GL_STATIC_DRAW); + + glGenBuffers(1, &m->vbo_uv); + glBindBuffer(GL_ARRAY_BUFFER, m->vbo_uv); + glBufferData(GL_ARRAY_BUFFER, m->num_verts * 2 * sizeof(float), m->uv, GL_STATIC_DRAW); + + glGenBuffers(1, &m->ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m->num_idx * sizeof(uint16_t), m->idx, GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} diff --git a/src/geom.cc b/src/geom.cc new file mode 100644 index 0000000..dfbc52f --- /dev/null +++ b/src/geom.cc @@ -0,0 +1,760 @@ +#include +#include +#include +#include "geom.h" + +#define SPHERE(ptr) ((Sphere*)ptr) +#define AABOX(ptr) ((AABox*)ptr) +#define BOX(ptr) ((Box*)ptr) +#define PLANE(ptr) ((Plane*)ptr) + +GeomObject::GeomObject() +{ + type = GOBJ_UNKNOWN; +} + +GeomObject::~GeomObject() +{ +} + +bool GeomObject::valid() const +{ + return true; +} + +void GeomObject::invalidate() +{ +} + +float GeomObject::distance(const Vec3 &v) const +{ + return sqrt(distance_sq(v)); +} + +float GeomObject::signed_distance(const Vec3 &v) const +{ + return sqrt(signed_distance_sq(v)); +} + +Sphere::Sphere() +{ + type = GOBJ_SPHERE; + radius = 1.0; +} + +Sphere::Sphere(const Vec3 ¢, float radius) + : center(cent) +{ + type = GOBJ_SPHERE; + this->radius = radius; +} + +bool Sphere::valid() const +{ + return radius >= 0.0f; +} + +void Sphere::invalidate() +{ + center = Vec3(0, 0, 0); + radius = -1; +} + +bool Sphere::intersect(const Ray &ray, HitPoint *hit) const +{ + float a = dot(ray.dir, ray.dir); + float b = 2.0 * ray.dir.x * (ray.origin.x - center.x) + + 2.0 * ray.dir.y * (ray.origin.y - center.y) + + 2.0 * ray.dir.z * (ray.origin.z - center.z); + float c = dot(ray.origin, ray.origin) + dot(center, center) - + 2.0 * dot(ray.origin, center) - radius * radius; + + float discr = b * b - 4.0 * a * c; + if(discr < 1e-4) { + return false; + } + + float sqrt_discr = sqrt(discr); + float t0 = (-b + sqrt_discr) / (2.0 * a); + float t1 = (-b - sqrt_discr) / (2.0 * a); + + if(t0 < 1e-4) + t0 = t1; + if(t1 < 1e-4) + t1 = t0; + + float t = t0 < t1 ? t0 : t1; + if(t < 1e-4) { + return false; + } + + // fill the HitPoint structure + if(hit) { + hit->obj = this; + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + hit->normal = (hit->pos - center) / radius; + } + return true; +} + +bool Sphere::contains(const Vec3 &pt) const +{ + return length_sq(pt - center) <= radius * radius; +} + +float Sphere::distance_sq(const Vec3 &v) const +{ + return std::max(length_sq(v - center) - radius * radius, 0.0f); +} + +float Sphere::signed_distance_sq(const Vec3 &v) const +{ + return length_sq(v - center) - radius * radius; +} + +AABox::AABox() +{ + type = GOBJ_AABOX; +} + +AABox::AABox(const Vec3 &vmin, const Vec3 &vmax) + : min(vmin), max(vmax) +{ + type = GOBJ_AABOX; +} + +bool AABox::valid() const +{ + return min.x <= max.x && min.y <= max.y && min.z <= max.z; +} + +void AABox::invalidate() +{ + min = Vec3(FLT_MAX, FLT_MAX, FLT_MAX); + max = Vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX); +} + +Vec3 AABox::get_corner(int idx) const +{ + Vec3 v[] = {min, max}; + static const int xidx[] = {0, 1, 1, 0, 0, 1, 1, 0}; + static const int yidx[] = {0, 0, 0, 0, 1, 1, 1, 1}; + static const int zidx[] = {0, 0, 1, 1, 0, 0, 1, 1}; + return Vec3(v[xidx[idx]].x, v[yidx[idx]].y, v[zidx[idx]].z); +} + +Plane AABox::get_plane(int pidx) const +{ + Vec3 c = (max - min) * 0.5f; + switch(pidx) { + case AABOX_PLANE_PX: + return Plane(Vec3(max.x, c.y, c.z), Vec3(1, 0, 0)); + case AABOX_PLANE_NX: + return Plane(Vec3(min.x, c.y, c.z), Vec3(-1, 0, 0)); + case AABOX_PLANE_PY: + return Plane(Vec3(c.x, max.x, c.z), Vec3(0, 1, 0)); + case AABOX_PLANE_NY: + return Plane(Vec3(c.x, min.x, c.z), Vec3(0, -1, 0)); + case AABOX_PLANE_PZ: + return Plane(Vec3(c.x, c.y, max.z), Vec3(0, 0, 1)); + case AABOX_PLANE_NZ: + return Plane(Vec3(c.x, c.y, min.z), Vec3(0, 0, -1)); + default: + break; + } + abort(); + return Plane(); +} + +bool AABox::intersect(const Ray &ray, HitPoint *hit) const +{ + Vec3 param[2] = {min, max}; + Vec3 inv_dir(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z); + int sign[3] = {inv_dir.x < 0, inv_dir.y < 0, inv_dir.z < 0}; + + float tmin = (param[sign[0]].x - ray.origin.x) * inv_dir.x; + float tmax = (param[1 - sign[0]].x - ray.origin.x) * inv_dir.x; + float tymin = (param[sign[1]].y - ray.origin.y) * inv_dir.y; + float tymax = (param[1 - sign[1]].y - ray.origin.y) * inv_dir.y; + + if(tmin > tymax || tymin > tmax) { + return false; + } + if(tymin > tmin) { + tmin = tymin; + } + if(tymax < tmax) { + tmax = tymax; + } + + float tzmin = (param[sign[2]].z - ray.origin.z) * inv_dir.z; + float tzmax = (param[1 - sign[2]].z - ray.origin.z) * inv_dir.z; + + if(tmin > tzmax || tzmin > tmax) { + return false; + } + if(tzmin > tmin) { + tmin = tzmin; + } + if(tzmax < tmax) { + tmax = tzmax; + } + + float t = tmin < 1e-4 ? tmax : tmin; + if(t >= 1e-4) { + + if(hit) { + hit->obj = this; + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + + float min_dist = FLT_MAX; + Vec3 offs = min + (max - min) / 2.0; + Vec3 local_hit = hit->pos - offs; + + static const Vec3 axis[] = { + Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1) + }; + //int tcidx[][2] = {{2, 1}, {0, 2}, {0, 1}}; + + for(int i=0; i<3; i++) { + float dist = fabs((max[i] - offs[i]) - fabs(local_hit[i])); + if(dist < min_dist) { + min_dist = dist; + hit->normal = axis[i] * (local_hit[i] < 0.0 ? 1.0 : -1.0); + //hit->texcoord = Vec2(hit->pos[tcidx[i][0]], hit->pos[tcidx[i][1]]); + } + } + } + return true; + } + return false; +} + +bool AABox::contains(const Vec3 &v) const +{ + return v.x >= min.x && v.y >= min.y && v.z >= min.z && + v.x <= max.x && v.y <= max.y && v.z <= max.z; +} + +#define SQ(x) ((x) * (x)) +float AABox::distance_sq(const Vec3 &v) const +{ + float dsq = 0.0f; + + for(int i=0; i<3; i++) { + if(v[i] < min[i]) dsq += SQ(min[i] - v[i]); + if(v[i] > max[i]) dsq += SQ(v[i] - max[i]); + } + return dsq; +} + +float AABox::signed_distance_sq(const Vec3 &v) const +{ + if(!contains(v)) { + return distance_sq(v); + } + + float dsq = 0.0f; + for(int i=0; i<3; i++) { + float dmin = v[i] - min[i]; + float dmax = max[i] - v[i]; + dsq -= dmin < dmax ? SQ(dmin) : SQ(dmax); + } + return dsq; +} + +Box::Box() +{ + type = GOBJ_BOX; +} + +Box::Box(const AABox &aabox, const Mat4 &xform) + : xform(xform) +{ + type = GOBJ_BOX; + min = aabox.min; + max = aabox.max; +} + +void Box::invalidate() +{ + AABox::invalidate(); + xform = Mat4::identity; +} + +Box::Box(const Vec3 &min, const Vec3 &max) + : AABox(min, max) +{ + type = GOBJ_BOX; +} + +Box::Box(const Vec3 &min, const Vec3 &max, const Mat4 &xform) + : AABox(min, max), xform(xform) +{ + type = GOBJ_BOX; +} + +// XXX all this shit is completely untested +Box::Box(const Vec3 &pos, const Vec3 &vi, const Vec3 &vj, const Vec3 &vk) +{ + type = GOBJ_BOX; + float ilen = length(vi); + float jlen = length(vj); + float klen = length(vk); + + min = Vec3(-ilen, -jlen, -klen); + max = Vec3(ilen, jlen, klen); + + float si = ilen == 0.0 ? 1.0 : 1.0 / ilen; + float sj = jlen == 0.0 ? 1.0 : 1.0 / jlen; + float sk = klen == 0.0 ? 1.0 : 1.0 / klen; + + xform = Mat4(vi * si, vj * sj, vk * sk); + xform.translate(pos); +} + +Box::Box(const Vec3 *varr, int vcount) +{ + type = GOBJ_BOX; + calc_bounding_aabox(this, varr, vcount); +} + +Vec3 Box::get_corner(int idx) const +{ + return xform * AABox::get_corner(idx); +} + +bool Box::intersect(const Ray &ray, HitPoint *hit) const +{ + Mat4 inv_xform = inverse(xform); + Mat4 dir_inv_xform = inv_xform.upper3x3(); + Mat4 dir_xform = transpose(dir_inv_xform); + Ray local_ray = Ray(inv_xform * ray.origin, dir_inv_xform * ray.dir); + + bool res = AABox::intersect(local_ray, hit); + if(!res || !hit) return res; + + hit->pos = xform * hit->pos; + hit->normal = dir_xform * hit->normal; + hit->local_ray = local_ray; + hit->ray = ray; + return true; +} + +bool Box::contains(const Vec3 &pt) const +{ + // XXX is it faster to extract 6 planes and do dot products? sounds marginal + return AABox::contains(inverse(xform) * pt); +} + +float Box::distance_sq(const Vec3 &v) const +{ + return 0.0f; // TODO +} + +float Box::signed_distance_sq(const Vec3 &v) const +{ + return 0.0f; // TODO +} + +Plane::Plane() + : normal(0.0, 1.0, 0.0) +{ + type = GOBJ_PLANE; +} + +Plane::Plane(const Vec3 &p, const Vec3 &norm) + : pt(p) +{ + type = GOBJ_PLANE; + normal = normalize(norm); +} + +Plane::Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3) + : pt(p1) +{ + type = GOBJ_PLANE; + normal = normalize(cross(p2 - p1, p3 - p1)); +} + +Plane::Plane(const Vec3 &normal, float dist) +{ + type = GOBJ_PLANE; + this->normal = normalize(normal); + pt = this->normal * dist; +} + +bool Plane::intersect(const Ray &ray, HitPoint *hit) const +{ + float ndotdir = dot(normal, ray.dir); + if(fabs(ndotdir) < 1e-4) { + return false; + } + + if(hit) { + Vec3 ptdir = pt - ray.origin; + float t = dot(normal, ptdir) / ndotdir; + + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + hit->normal = normal; + hit->obj = this; + } + return true; +} + +bool Plane::contains(const Vec3 &v) const +{ + return dot(v, normal) <= 0.0; +} + +float Plane::distance(const Vec3 &v) const +{ + return std::max(dot(v - pt, normal), 0.0f); +} + +float Plane::signed_distance(const Vec3 &v) const +{ + return dot(v - pt, normal); +} + +float Plane::distance_sq(const Vec3 &v) const +{ + float d = distance(v); + return d * d; +} + +float Plane::signed_distance_sq(const Vec3 &v) const +{ + float d = distance(v); + return dot(v, normal) >= 0.0f ? d * d : -(d * d); +} + + +Disc::Disc() +{ + type = GOBJ_DISC; + radius = 1.0; +} + +Disc::Disc(const Vec3 &pt, const Vec3 &normal, float rad) + : Plane(pt, normal) +{ + type = GOBJ_DISC; + radius = rad; +} + +Disc::Disc(const Vec3 &normal, float dist, float rad) + : Plane(normal, dist) +{ + type = GOBJ_DISC; + radius = rad; +} + +bool Disc::valid() const +{ + return radius >= 0.0f; +} + +void Disc::invalidate() +{ + radius = -1; +} + +bool Disc::intersect(const Ray &ray, HitPoint *hit) const +{ + HitPoint phit; + if(Plane::intersect(ray, &phit)) { + if(length_sq(phit.pos - pt) <= radius * radius) { + *hit = phit; + return true; + } + } + return false; +} + +bool Disc::contains(const Vec3 &pt) const +{ + Vec3 pj = proj_point_plane(pt, *this); + return length_sq(pj - this->pt) <= radius * radius; +} + +float Disc::distance_sq(const Vec3 &v) const +{ + return 0.0; // TODO +} + +float Disc::signed_distance_sq(const Vec3 &v) const +{ + return 0.0; // TODO +} + + +Vec3 proj_point_plane(const Vec3 &pt, const Plane &plane) +{ + float dist = plane.signed_distance(pt); + return pt - plane.normal * dist; +} + +// ---- bounding sphere calculations ---- + +bool calc_bounding_sphere(Sphere *sph, const GeomObject *obj) +{ + if(!obj->valid()) { + sph->invalidate(); + return true; + } + + switch(obj->type) { + case GOBJ_SPHERE: + *sph = *(Sphere*)obj; + break; + + case GOBJ_AABOX: + sph->center = (AABOX(obj)->min + AABOX(obj)->max) * 0.5; + sph->radius = length(AABOX(obj)->max - AABOX(obj)->min) * 0.5; + break; + + case GOBJ_BOX: + sph->center = (BOX(obj)->min + BOX(obj)->max) * 0.5 + BOX(obj)->xform.get_translation(); + sph->radius = length(BOX(obj)->max - BOX(obj)->min) * 0.5; + break; + + case GOBJ_PLANE: + default: + return false; + } + return true; +} + +bool calc_bounding_sphere(Sphere *sph, const GeomObject *a, const GeomObject *b) +{ + Sphere bsa, bsb; + + if(!calc_bounding_sphere(&bsa, a) || !calc_bounding_sphere(&bsb, b)) { + return false; + } + + float dist = length(bsa.center - bsb.center); + float surf_dist = dist - (bsa.radius + bsb.radius); + float d1 = bsa.radius + surf_dist / 2.0; + float d2 = bsb.radius + surf_dist / 2.0; + float t = d1 / (d1 + d2); + + if(t < 0.0) t = 0.0; + if(t > 1.0) t = 1.0; + + sph->center = bsa.center * t + bsb.center * (1.0 - t); + sph->radius = std::max(dist * t + bsb.radius, dist * (1.0f - t) + bsa.radius); + return true; +} + +bool calc_bounding_sphere(Sphere *sph, const GeomObject **objv, int num) +{ + if(num <= 0) return false; + + if(!calc_bounding_sphere(sph, objv[0])) { + return false; + } + + for(int i=1; icenter = Vec3(0.0, 0.0, 0.0); + for(int i=0; icenter += xform * v[i]; + } + sph->center /= (float)num; + + float rad_sq = 0.0f; + for(int i=0; icenter; + rad_sq = std::max(rad_sq, dot(dir, dir)); + } + sph->radius = sqrt(rad_sq); + return true; +} + +bool calc_bounding_aabox(AABox *box, const GeomObject *obj) +{ + if(!obj->valid()) { + box->invalidate(); + return true; + } + + switch(obj->type) { + case GOBJ_AABOX: + *box = *(AABox*)obj; + break; + + case GOBJ_BOX: + { + Vec3 v[8]; + for(int i=0; i<8; i++) { + v[i] = BOX(obj)->get_corner(i); + } + calc_bounding_aabox(box, v, 8); + } + break; + + case GOBJ_SPHERE: + { + float r = SPHERE(obj)->radius; + box->min = SPHERE(obj)->center - Vec3(r, r, r); + box->max = SPHERE(obj)->center + Vec3(r, r, r); + } + break; + + case GOBJ_PLANE: + default: + return false; + } + return true; +} + +bool calc_bounding_aabox(AABox *box, const GeomObject *a, const GeomObject *b) +{ + AABox bba, bbb; + + if(!calc_bounding_aabox(&bba, a) || !calc_bounding_aabox(&bbb, b)) { + return false; + } + + for(int i=0; i<3; i++) { + box->min[i] = std::min(bba.min[i], bbb.min[i]); + box->max[i] = std::max(bba.max[i], bbb.max[i]); + } + return true; +} + +bool calc_bounding_aabox(AABox *box, const GeomObject **objv, int num) +{ + if(num <= 0) return false; + + if(!calc_bounding_aabox(box, objv[0])) { + return false; + } + + for(int i=1; imin = box->max = xform * v[0]; + for(int i=1; imin[j] = std::min(box->min[j], p[j]); + box->max[j] = std::max(box->max[j], p[j]); + } + } + return true; +} + +bool calc_bounding_box(Box *box, const GeomObject *obj) +{ + if(!obj->valid()) { + box->invalidate(); + return true; + } + + switch(obj->type) { + case GOBJ_BOX: + *box = *(Box*)obj; + break; + + case GOBJ_AABOX: + box->min = BOX(obj)->min; + box->max = BOX(obj)->max; + box->xform = Mat4::identity; + break; + + case GOBJ_SPHERE: + { + float r = SPHERE(obj)->radius; + box->min = SPHERE(obj)->center - Vec3(r, r, r); + box->max = SPHERE(obj)->center + Vec3(r, r, r); + box->xform = Mat4::identity; + } + break; + + case GOBJ_PLANE: + default: + return false; + } + return true; +} + +bool intersect_sphere_sphere(Disc *result, const Sphere &a, const Sphere &b) +{ + Vec3 dir = b.center - a.center; + + float dist_sq = length_sq(dir); + if(dist_sq <= 1e-8) return false; + + float rsum = a.radius + b.radius; + float rdif = fabs(a.radius - b.radius); + if(dist_sq > rsum * rsum || dist_sq < rdif * rdif) { + return false; + } + + float dist = sqrt(dist_sq); + float t = (dist_sq + a.radius * a.radius - b.radius * b.radius) / (2.0 * sqrt(dist_sq)); + + result->pt = a.center + dir * t; + result->normal = dir / dist; + result->radius = sin(acos(t)) * a.radius; + return true; +} + +bool intersect_plane_plane(Ray *result, const Plane &a, const Plane &b) +{ + return false; // TODO +} + +bool intersect_sphere_plane(Sphere *result, const Sphere &s, const Plane &p) +{ + return false; // TODO +} + +bool intersect_plane_sphere(Sphere *result, const Plane &p, const Sphere &s) +{ + return false; // TODO +} + +bool intersect_aabox_aabox(AABox *res, const AABox &a, const AABox &b) +{ + for(int i=0; i<3; i++) { + res->min[i] = std::max(a.min[i], b.min[i]); + res->max[i] = std::min(a.max[i], b.max[i]); + + if(res->max[i] < res->min[i]) { + res->max[i] = res->min[i]; + } + } + return res->min.x != res->max.x && res->min.y != res->max.y && res->min.z != res->max.z; +} + +bool collision_sphere_aabox(const Sphere &s, const AABox &b) +{ + return b.distance_sq(s.center) <= s.radius * s.radius; +} diff --git a/src/geom.h b/src/geom.h new file mode 100644 index 0000000..0d8dd00 --- /dev/null +++ b/src/geom.h @@ -0,0 +1,200 @@ +#ifndef GEOMOBJ_H_ +#define GEOMOBJ_H_ + +/* TODO: + * - implement distance functions + */ + +#include + +enum GeomObjectType { + GOBJ_UNKNOWN, + GOBJ_SPHERE, + GOBJ_AABOX, + GOBJ_BOX, + GOBJ_PLANE, + GOBJ_DISC +}; + +enum { + AABOX_PLANE_PX, + AABOX_PLANE_NX, + AABOX_PLANE_PY, + AABOX_PLANE_NY, + AABOX_PLANE_PZ, + AABOX_PLANE_NZ +}; + +class GeomObject; +class Plane; + +struct HitPoint { + float dist; // parametric distance along the ray + Vec3 pos; // position of intersection (orig + dir * dist) + Vec3 normal; // normal at the point of intersection + Ray ray, local_ray; + const GeomObject *obj; // pointer to the intersected geom-object + void *data; // place to hang extra data +}; + +class GeomObject { +public: + GeomObjectType type; + + GeomObject(); + virtual ~GeomObject(); + + virtual bool valid() const; + virtual void invalidate(); + + virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const = 0; + virtual bool contains(const Vec3 &pt) const = 0; + + virtual float distance(const Vec3 &v) const; + virtual float signed_distance(const Vec3 &v) const; + + virtual float distance_sq(const Vec3 &v) const = 0; + virtual float signed_distance_sq(const Vec3 &v) const = 0; +}; + +class Sphere : public GeomObject { +public: + Vec3 center; + float radius; + + Sphere(); + Sphere(const Vec3 ¢er, float radius); + + virtual bool valid() const; + virtual void invalidate(); + + virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const; + virtual bool contains(const Vec3 &pt) const; + + virtual float distance_sq(const Vec3 &v) const; + virtual float signed_distance_sq(const Vec3 &v) const; +}; + +class AABox : public GeomObject { +public: + Vec3 min, max; + + AABox(); + AABox(const Vec3 &min, const Vec3 &max); + + virtual bool valid() const; + virtual void invalidate(); + + virtual Vec3 get_corner(int idx) const; + virtual Plane get_plane(int pidx) const; + + virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const; + virtual bool contains(const Vec3 &pt) const; + + virtual float distance_sq(const Vec3 &v) const; + virtual float signed_distance_sq(const Vec3 &v) const; +}; + +class Box : public AABox { +public: + Mat4 xform; + + Box(); + Box(const AABox &aabox, const Mat4 &xform); + Box(const Vec3 &min, const Vec3 &max); + Box(const Vec3 &min, const Vec3 &max, const Mat4 &xform); + Box(const Vec3 &pos, const Vec3 &vi, const Vec3 &vj, const Vec3 &vk); + Box(const Vec3 *varr, int vcount); + + virtual void invalidate(); + + virtual Vec3 get_corner(int idx) const; + + virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const; + virtual bool contains(const Vec3 &pt) const; + + virtual float distance_sq(const Vec3 &v) const; + virtual float signed_distance_sq(const Vec3 &v) const; +}; + + +class Plane : public GeomObject { +public: + Vec3 pt, normal; + + Plane(); + Plane(const Vec3 &pt, const Vec3 &normal); + Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3); + Plane(const Vec3 &normal, float dist); + + virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const; + virtual bool contains(const Vec3 &pt) const; + + virtual float distance(const Vec3 &v) const; + virtual float signed_distance(const Vec3 &v) const; + + virtual float distance_sq(const Vec3 &v) const; + virtual float signed_distance_sq(const Vec3 &v) const; +}; + +class Disc : public Plane { +public: + float radius; + + Disc(); + Disc(const Vec3 &pt, const Vec3 &normal, float rad); + Disc(const Vec3 &normal, float dist, float rad); + + virtual bool valid() const; + virtual void invalidate(); + + virtual bool intersect(const Ray &ray, HitPoint *hit = 0) const; + //! true if the projection of pt to the plane is contained within the disc radius + virtual bool contains(const Vec3 &pt) const; + + virtual float distance_sq(const Vec3 &v) const; + virtual float signed_distance_sq(const Vec3 &v) const; +}; + +//! project point to plane +Vec3 proj_point_plane(const Vec3 &pt, const Plane &plane); + +//! calculate the bounding sphere of any object +bool calc_bounding_sphere(Sphere *sph, const GeomObject *obj); +//! calculate the bounding sphere of two objects +bool calc_bounding_sphere(Sphere *sph, const GeomObject *a, const GeomObject *b); +//! calculate the bounding sphere of multiple objects +bool calc_bounding_sphere(Sphere *sph, const GeomObject **objv, int num); +//! calculate the bounding sphere of multiple points, optionally transformed by `xform` +bool calc_bounding_sphere(Sphere *sph, const Vec3 *v, int num, const Mat4 &xform = Mat4::identity); + +//! calculate the bounding axis-aligned box of any object +bool calc_bounding_aabox(AABox *box, const GeomObject *obj); +//! calculate the bounding axis-aligned box of two objects +bool calc_bounding_aabox(AABox *box, const GeomObject *a, const GeomObject *b); +//! calculate the bounding axis-aligned box of multiple objects +bool calc_bounding_aabox(AABox *box, const GeomObject **objv, int num); +//! calculate the bounding axis-aligned box of multiple points, optionally transformed by `xform` +bool calc_bounding_aabox(AABox *box, const Vec3 *v, int num, const Mat4 &xform = Mat4::identity); + +//! calculate the bounding box of any object +bool calc_bounding_box(Box *box, const GeomObject *obj); + +//! calculate the intersection plane of two spheres. false if there is no plane or infinite planes. +bool intersect_sphere_sphere(Plane *result, const Sphere &a, const Sphere &b); +//! calculate the intersection line of two planes. returns false if planes are exactly parallel. +bool intersect_plane_plane(Ray *result, const Plane &a, const Plane &b); +/*! calculate the intesection circle of a plane and a sphere. returns false if they don't intersect. + * \{ + */ +bool intersect_sphere_plane(Sphere *result, const Sphere &s, const Plane &p); +bool intersect_plane_sphere(Sphere *result, const Plane &p, const Sphere &s); +//! \} + +//! calculate the intersection of two axis-aligned bounding boxes +bool intersect_aabox_aabox(AABox *res, const AABox &a, const AABox &b); + +//! determine if a sphere and an axis-aligned box collide +bool collision_sphere_aabox(const Sphere &s, const AABox &b); + +#endif // GEOMOBJ_H_ diff --git a/src/main.cc b/src/main.cc index ca23fbc..4c707d6 100644 --- a/src/main.cc +++ b/src/main.cc @@ -30,6 +30,11 @@ int main(int argc, char **argv) glutInit(&argc, argv); glutInitWindowSize(1024, 600); glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_SRGB | GLUT_MULTISAMPLE); + + glutInitContextVersion(3, 0); + glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE); + glutInitContextFlags(GLUT_DEBUG); + glutCreateWindow("ludum dare 42"); glutDisplayFunc(display); diff --git a/src/mesh.cc b/src/mesh.cc new file mode 100644 index 0000000..798e8d1 --- /dev/null +++ b/src/mesh.cc @@ -0,0 +1,1443 @@ +#include +#include +#include +#include +#include "opengl.h" +#include "mesh.h" +//#include "logger.h" +//#include "xform_node.h" + +#define USE_OLDGL + +bool Mesh::use_custom_sdr_attr = true; +int Mesh::global_sdr_loc[NUM_MESH_ATTR] = { 0, 1, 2, 3, 4, 5, 6, 7 }; +unsigned int Mesh::intersect_mode = ISECT_DEFAULT; +float Mesh::vertex_sel_dist = 0.01; +float Mesh::vis_vecsize = 1.0; + +Mesh::Mesh() +{ + clear(); + + glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects); + + for(int i=0; iname = name; +} + +const char *Mesh::get_name() const +{ + return name.c_str(); +} + +bool Mesh::has_attrib(int attr) const +{ + if(attr < 0 || attr >= NUM_MESH_ATTR) { + return false; + } + + // if neither of these is valid, then nobody has set this attribute + return vattr[attr].vbo_valid || vattr[attr].data_valid; +} + +bool Mesh::is_indexed() const +{ + return ibo_valid || idata_valid; +} + +void Mesh::clear() +{ + //bones.clear(); + + for(int i=0; i= NUM_MESH_ATTR) { + fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + if(nverts && num != nverts) { + fprintf(stderr, "%s: attribute count missmatch (%d instead of %d)\n", __FUNCTION__, num, nverts); + return 0; + } + nverts = num; + + vattr[attrib].data.clear(); + vattr[attrib].nelem = nelem; + vattr[attrib].data.resize(num * nelem); + + if(data) { + memcpy(&vattr[attrib].data[0], data, num * nelem * sizeof *data); + } + + vattr[attrib].data_valid = true; + vattr[attrib].vbo_valid = false; + return &vattr[attrib].data[0]; +} + +float *Mesh::get_attrib_data(int attrib) +{ + if(attrib < 0 || attrib >= NUM_MESH_ATTR) { + fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + vattr[attrib].vbo_valid = false; + return (float*)((const Mesh*)this)->get_attrib_data(attrib); +} + +const float *Mesh::get_attrib_data(int attrib) const +{ + if(attrib < 0 || attrib >= NUM_MESH_ATTR) { + fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + if(!vattr[attrib].data_valid) { +#if GL_ES_VERSION_2_0 + fprintf(stderr, "%s: can't read back attrib data on CrippledGL ES\n", __FUNCTION__); + return 0; +#else + if(!vattr[attrib].vbo_valid) { + fprintf(stderr, "%s: unavailable attrib: %d\n", __FUNCTION__, attrib); + return 0; + } + + // local data copy is unavailable, grab the data from the vbo + Mesh *m = (Mesh*)this; + m->vattr[attrib].data.resize(nverts * vattr[attrib].nelem); + + glBindBuffer(GL_ARRAY_BUFFER, vattr[attrib].vbo); + void *data = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + memcpy(&m->vattr[attrib].data[0], data, nverts * vattr[attrib].nelem * sizeof(float)); + glUnmapBuffer(GL_ARRAY_BUFFER); + + vattr[attrib].data_valid = true; +#endif + } + + return &vattr[attrib].data[0]; +} + +void Mesh::set_attrib(int attrib, int idx, const Vec4 &v) +{ + float *data = get_attrib_data(attrib); + if(data) { + data += idx * vattr[attrib].nelem; + for(int i=0; iget_index_data(); +} + +const unsigned int *Mesh::get_index_data() const +{ + if(!idata_valid) { +#if GL_ES_VERSION_2_0 + fprintf(stderr, "%s: can't read back index data in CrippledGL ES\n", __FUNCTION__); + return 0; +#else + if(!ibo_valid) { + fprintf(stderr, "%s: indices unavailable\n", __FUNCTION__); + return 0; + } + + // local data copy is unavailable, gram the data from the ibo + Mesh *m = (Mesh*)this; + int nidx = nfaces * 3; + m->idata.resize(nidx); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); + memcpy(&m->idata[0], data, nidx * sizeof(unsigned int)); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + + idata_valid = true; +#endif + } + + return &idata[0]; +} + +int Mesh::get_index_count() const +{ + return nfaces * 3; +} + +void Mesh::append(const Mesh &mesh) +{ + unsigned int idxoffs = nverts; + + if(!nverts) { + clone(mesh); + return; + } + + nverts += mesh.nverts; + nfaces += mesh.nfaces; + + for(int i=0; i= NUM_MESH_ATTR) { + return; + } + Mesh::global_sdr_loc[attr] = loc; +} + +/// static function +int Mesh::get_attrib_location(int attr) +{ + if(attr < 0 || attr >= NUM_MESH_ATTR) { + return -1; + } + return Mesh::global_sdr_loc[attr]; +} + +/// static function +void Mesh::clear_attrib_locations() +{ + for(int i=0; i= (int)bones.size()) { + return 0; + } + return bones[idx]; +} + +int Mesh::get_bones_count() const +{ + return (int)bones.size(); +} +*/ + +bool Mesh::pre_draw() const +{ + cur_sdr = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr); + + ((Mesh*)this)->update_buffers(); + + if(!vattr[MESH_ATTR_VERTEX].vbo_valid) { + fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__); + return false; + } + + if(cur_sdr && use_custom_sdr_attr) { + // rendering with shaders + if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { + fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__); + return false; + } + + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); + glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(loc); + } + } + } else { +#ifndef GL_ES_VERSION_2_0 + // rendering with fixed-function (not available in GLES2) + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_VERTEX].vbo); + glVertexPointer(vattr[MESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + if(vattr[MESH_ATTR_NORMAL].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_NORMAL].vbo); + glNormalPointer(GL_FLOAT, 0, 0); + glEnableClientState(GL_NORMAL_ARRAY); + } + if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_TEXCOORD].vbo); + glTexCoordPointer(vattr[MESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + if(vattr[MESH_ATTR_COLOR].vbo_valid) { + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_COLOR].vbo); + glColorPointer(vattr[MESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_COLOR_ARRAY); + } + if(vattr[MESH_ATTR_TEXCOORD2].vbo_valid) { + glClientActiveTexture(GL_TEXTURE1); + glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_TEXCOORD2].vbo); + glTexCoordPointer(vattr[MESH_ATTR_TEXCOORD2].nelem, GL_FLOAT, 0, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTexture(GL_TEXTURE0); + } +#endif + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + return true; +} + +void Mesh::draw() const +{ + if(!pre_draw()) return; + + if(ibo_valid) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glDrawElements(GL_TRIANGLES, nfaces * 3, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } else { + glDrawArrays(GL_TRIANGLES, 0, nverts); + } + + post_draw(); +} + +void Mesh::post_draw() const +{ + if(cur_sdr && use_custom_sdr_attr) { + // rendered with shaders + for(int i=0; i= 0 && vattr[i].vbo_valid) { + glDisableVertexAttribArray(loc); + } + } + } else { +#ifndef GL_ES_VERSION_2_0 + // rendered with fixed-function + glDisableClientState(GL_VERTEX_ARRAY); + if(vattr[MESH_ATTR_NORMAL].vbo_valid) { + glDisableClientState(GL_NORMAL_ARRAY); + } + if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + if(vattr[MESH_ATTR_COLOR].vbo_valid) { + glDisableClientState(GL_COLOR_ARRAY); + } + if(vattr[MESH_ATTR_TEXCOORD2].vbo_valid) { + glClientActiveTexture(GL_TEXTURE1); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glClientActiveTexture(GL_TEXTURE0); + } +#endif + } +} + +void Mesh::draw_wire() const +{ + if(!pre_draw()) return; + + ((Mesh*)this)->update_wire_ibo(); + + int num_faces = get_poly_count(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo); + glDrawElements(GL_LINES, num_faces * 6, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + post_draw(); +} + +void Mesh::draw_vertices() const +{ + if(!pre_draw()) return; + + glDrawArrays(GL_POINTS, 0, nverts); + + post_draw(); +} + +void Mesh::draw_normals() const +{ +#ifdef USE_OLDGL + int cur_sdr = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr); + + Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX); + Vec3 *norm = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL); + if(!varr || !norm) { + return; + } + + glBegin(GL_LINES); + if(cur_sdr && use_custom_sdr_attr) { + int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX]; + if(vert_loc < 0) { + glEnd(); + return; + } + + for(size_t i=0; icalc_aabb(); + } + *vmin = aabb.min; + *vmax = aabb.max; +} + +const AABox &Mesh::get_aabbox() const +{ + if(!aabb_valid) { + ((Mesh*)this)->calc_aabb(); + } + return aabb; +} + +float Mesh::get_bsphere(Vec3 *center, float *rad) const +{ + if(!bsph_valid) { + ((Mesh*)this)->calc_bsph(); + } + *center = bsph.center; + *rad = bsph.radius; + return bsph.radius; +} + +const Sphere &Mesh::get_bsphere() const +{ + if(!bsph_valid) { + ((Mesh*)this)->calc_bsph(); + } + return bsph; +} + +/// static function +void Mesh::set_intersect_mode(unsigned int mode) +{ + Mesh::intersect_mode = mode; +} + +/// static function +unsigned int Mesh::get_intersect_mode() +{ + return Mesh::intersect_mode; +} + +/// static function +void Mesh::set_vertex_select_distance(float dist) +{ + Mesh::vertex_sel_dist = dist; +} + +/// static function +float Mesh::get_vertex_select_distance() +{ + return Mesh::vertex_sel_dist; +} + +bool Mesh::intersect(const Ray &ray, HitPoint *hit) const +{ + assert((Mesh::intersect_mode & (ISECT_VERTICES | ISECT_FACE)) != (ISECT_VERTICES | ISECT_FACE)); + + const Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX); + const Vec3 *narr = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL); + if(!varr) { + return false; + } + const unsigned int *idxarr = get_index_data(); + + // first test with the bounding box + AABox box; + get_aabbox(&box.min, &box.max); + if(!box.intersect(ray)) { + return false; + } + + HitPoint nearest_hit; + nearest_hit.dist = FLT_MAX; + nearest_hit.data = 0; + + if(Mesh::intersect_mode & ISECT_VERTICES) { + // we asked for "intersections" with the vertices of the mesh + long nearest_vidx = -1; + float thres_sq = Mesh::vertex_sel_dist * Mesh::vertex_sel_dist; + + for(unsigned int i=0; i 0) { + continue; + } + + // project the vertex onto the ray line + float t = dot(varr[i] - ray.origin, ray.dir); + Vec3 vproj = ray.origin + ray.dir * t; + + float dist_sq = length_sq(vproj - varr[i]); + if(dist_sq < thres_sq) { + if(!hit) { + return true; + } + if(t < nearest_hit.dist) { + nearest_hit.dist = t; + nearest_vidx = i; + } + } + } + + if(nearest_vidx != -1) { + hitvert = varr[nearest_vidx]; + nearest_hit.data = &hitvert; + } + + } else { + // regular intersection test with polygons + + for(unsigned int i=0; i 0) { + continue; + } + + HitPoint fhit; + if(face.intersect(ray, hit ? &fhit : 0)) { + if(!hit) { + return true; + } + if(fhit.dist < nearest_hit.dist) { + nearest_hit = fhit; + hitface = face; + } + } + } + } + + if(nearest_hit.data) { + if(hit) { + *hit = nearest_hit; + + // if we are interested in the mesh and not the faces set obj to this + if(Mesh::intersect_mode & ISECT_FACE) { + hit->data = &hitface; + } else if(Mesh::intersect_mode & ISECT_VERTICES) { + hit->data = &hitvert; + } else { + hit->data = (void*)this; + } + } + return true; + } + return false; +} + + +// texture coordinate manipulation +void Mesh::texcoord_apply_xform(const Mat4 &xform) +{ + if(!has_attrib(MESH_ATTR_TEXCOORD)) { + return; + } + + for(unsigned int i=0; i abs_ny && abs_nx > abs_nz ? 0 : (abs_ny > abs_nz ? 1 : 2); + + float uv[2], *uvptr = uv; + for(int j=0; j<3; j++) { + if(j == dom) continue; // skip dominant axis + + *uvptr++ = pos[j]; + } + set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(uv[0], uv[1], 0, 1)); + } +} + +void Mesh::texcoord_gen_cylinder() +{ + if(!nverts) return; + + if(!has_attrib(MESH_ATTR_TEXCOORD)) { + // allocate texture coordinate attribute array + set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts); + } + + for(unsigned int i=0; iget_attrib_data(MESH_ATTR_VERTEX)) { + return; + } + + aabb.min = Vec3(FLT_MAX, FLT_MAX, FLT_MAX); + aabb.max = -aabb.min; + + for(unsigned int i=0; i aabb.max[j]) { + aabb.max[j] = v[j]; + } + } + } + aabb_valid = true; +} + +void Mesh::calc_bsph() +{ + // the cast is to force calling the const version which doesn't invalidate + if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) { + return; + } + + Vec3 v; + bsph.center = Vec3(0, 0, 0); + + // first find the center + for(unsigned int i=0; i bsph.radius) { + bsph.radius = dist_sq; + } + } + bsph.radius = sqrt(bsph.radius); + + bsph_valid = true; +} + +void Mesh::update_buffers() +{ + for(int i=0; iget_index_data(); + + for(int i=0; icalc_normal(); + } + return normal; +} + +void Triangle::transform(const Mat4 &xform) +{ + v[0] = xform * v[0]; + v[1] = xform * v[1]; + v[2] = xform * v[2]; + normal_valid = false; +} + +void Triangle::draw() const +{ + Vec3 n[3]; + n[0] = n[1] = n[2] = get_normal(); + + int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX); + int nloc = Mesh::get_attrib_location(MESH_ATTR_NORMAL); + + glEnableVertexAttribArray(vloc); + glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x); + glVertexAttribPointer(nloc, 3, GL_FLOAT, GL_FALSE, 0, &n[0].x); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + glDisableVertexAttribArray(vloc); + glDisableVertexAttribArray(nloc); +} + +void Triangle::draw_wire() const +{ + static const int idxarr[] = {0, 1, 1, 2, 2, 0}; + int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX); + + glEnableVertexAttribArray(vloc); + glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x); + + glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, idxarr); + + glDisableVertexAttribArray(vloc); +} + +Vec3 Triangle::calc_barycentric(const Vec3 &pos) const +{ + Vec3 norm = get_normal(); + + float area_sq = fabs(dot(cross(v[1] - v[0], v[2] - v[0]), norm)); + if(area_sq < 1e-5) { + return Vec3(0, 0, 0); + } + + float asq0 = fabs(dot(cross(v[1] - pos, v[2] - pos), norm)); + float asq1 = fabs(dot(cross(v[2] - pos, v[0] - pos), norm)); + float asq2 = fabs(dot(cross(v[0] - pos, v[1] - pos), norm)); + + return Vec3(asq0 / area_sq, asq1 / area_sq, asq2 / area_sq); +} + +bool Triangle::intersect(const Ray &ray, HitPoint *hit) const +{ + Vec3 normal = get_normal(); + + float ndotdir = dot(ray.dir, normal); + if(fabs(ndotdir) < 1e-4) { + return false; + } + + Vec3 vertdir = v[0] - ray.origin; + float t = dot(normal, vertdir) / ndotdir; + + Vec3 pos = ray.origin + ray.dir * t; + Vec3 bary = calc_barycentric(pos); + + if(bary.x + bary.y + bary.z > 1.00001) { + return false; + } + + if(hit) { + hit->dist = t; + hit->pos = ray.origin + ray.dir * t; + hit->normal = normal; + hit->data = (void*)this; + } + return true; +} diff --git a/src/mesh.h b/src/mesh.h new file mode 100644 index 0000000..8291aa7 --- /dev/null +++ b/src/mesh.h @@ -0,0 +1,249 @@ +#ifndef MESH_H_ +#define MESH_H_ + +#include +#include +#include +#include "gmath/gmath.h" +#include "geom.h" + +enum { + MESH_ATTR_VERTEX, + MESH_ATTR_NORMAL, + MESH_ATTR_TANGENT, + MESH_ATTR_TEXCOORD, + MESH_ATTR_COLOR, + MESH_ATTR_BONEWEIGHTS, + MESH_ATTR_BONEIDX, + MESH_ATTR_TEXCOORD2, + + NUM_MESH_ATTR +}; + +// intersection mode flags +enum { + ISECT_DEFAULT = 0, // default (whole mesh, all intersections) + ISECT_FRONT = 1, // front-faces only + ISECT_FACE = 2, // return intersected face pointer instead of mesh + ISECT_VERTICES = 4 // return (?) TODO +}; + +//class XFormNode; + + +class Triangle { +public: + Vec3 v[3]; + Vec3 normal; + bool normal_valid; + int id; + + Triangle(); + Triangle(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2); + Triangle(int n, const Vec3 *varr, const unsigned int *idxarr = 0); + + /// calculate normal (quite expensive) + void calc_normal(); + const Vec3 &get_normal() const; + + void transform(const Mat4 &xform); + + void draw() const; + void draw_wire() const; + + /// calculate barycentric coordinates of a point + Vec3 calc_barycentric(const Vec3 &pos) const; + + bool intersect(const Ray &ray, HitPoint *hit = 0) const; +}; + + +class Mesh { +private: + std::string name; + unsigned int nverts, nfaces; + + // current value for each attribute for the immedate mode + // interface. + Vec4 cur_val[NUM_MESH_ATTR]; + + unsigned int buffer_objects[NUM_MESH_ATTR + 1]; + + // vertex attribute data and buffer objects + struct VertexAttrib { + int nelem; // number of elements per attribute range: [1, 4] + std::vector data; + unsigned int vbo; + mutable bool vbo_valid; // if this is false, the vbo needs updating from the data + mutable bool data_valid; // if this is false, the data needs to be pulled from the vbo + //int sdr_loc; + } vattr[NUM_MESH_ATTR]; + + static int global_sdr_loc[NUM_MESH_ATTR]; + + //std::vector bones; // bones affecting this mesh + + // index data and buffer object + std::vector idata; + unsigned int ibo; + mutable bool ibo_valid; + mutable bool idata_valid; + + // index buffer object for wireframe rendering (constructed on demand) + unsigned int wire_ibo; + mutable bool wire_ibo_valid; + + // axis-aligned bounding box + mutable AABox aabb; + mutable bool aabb_valid; + + // bounding sphere + mutable Sphere bsph; + mutable bool bsph_valid; + + // keeps the last intersected face + mutable Triangle hitface; + // keeps the last intersected vertex position + mutable Vec3 hitvert; + + void calc_aabb(); + void calc_bsph(); + + static unsigned int intersect_mode; + static float vertex_sel_dist; + + static float vis_vecsize; + + /// update the VBOs after data has changed (invalid vbo/ibo) + void update_buffers(); + /// construct/update the wireframe index buffer (called from draw_wire). + void update_wire_ibo(); + + mutable int cur_sdr; + bool pre_draw() const; + void post_draw() const; + + +public: + static bool use_custom_sdr_attr; + + Mesh(); + ~Mesh(); + + Mesh(const Mesh &rhs); + Mesh &operator =(const Mesh &rhs); + bool clone(const Mesh &m); + + void set_name(const char *name); + const char *get_name() const; + + bool has_attrib(int attr) const; + bool is_indexed() const; + + // clears everything about this mesh, and returns to the newly constructed state + void clear(); + + // access the vertex attribute data + // if vdata == 0, space is just allocated + float *set_attrib_data(int attrib, int nelem, unsigned int num, const float *vdata = 0); // invalidates vbo + float *get_attrib_data(int attrib); // invalidates vbo + const float *get_attrib_data(int attrib) const; + + // simple access to any particular attribute + void set_attrib(int attrib, int idx, const Vec4 &v); // invalidates vbo + Vec4 get_attrib(int attrib, int idx) const; + + int get_attrib_count(int attrib) const; + + // ... same for index data + unsigned int *set_index_data(int num, const unsigned int *indices = 0); // invalidates ibo + unsigned int *get_index_data(); // invalidates ibo + const unsigned int *get_index_data() const; + + int get_index_count() const; + + void append(const Mesh &mesh); + + // immediate-mode style mesh construction interface + void vertex(float x, float y, float z); + void normal(float nx, float ny, float nz); + void tangent(float tx, float ty, float tz); + void texcoord(float u, float v, float w); + void boneweights(float w1, float w2, float w3, float w4); + void boneidx(int idx1, int idx2, int idx3, int idx4); + + int get_poly_count() const; + + /* apply a transformation to the vertices and its inverse-transpose + * to the normals and tangents. + */ + void apply_xform(const Mat4 &xform); + void apply_xform(const Mat4 &xform, const Mat4 &dir_xform); + + void flip(); // both faces and normals + void flip_faces(); + void flip_normals(); + + void explode(); // undo all vertex sharing + + void calc_face_normals(); // this is only guaranteed to work on an exploded mesh + + // adds a bone and returns its index + /*int add_bone(XFormNode *bone); + const XFormNode *get_bone(int idx) const; + int get_bones_count() const;*/ + + // access the shader attribute locations + static void set_attrib_location(int attr, int loc); + static int get_attrib_location(int attr); + static void clear_attrib_locations(); + + static void set_vis_vecsize(float sz); + static float get_vis_vecsize(); + + void draw() const; + void draw_wire() const; + void draw_vertices() const; + void draw_normals() const; + void draw_tangents() const; + + /** get the bounding box in local space. The result will be cached, and subsequent + * calls will return the same box. The cache gets invalidated by any functions that can affect + * the vertex data (non-const variant of get_attrib_data(MESH_ATTR_VERTEX, ...) included). + * @{ */ + void get_aabbox(Vec3 *vmin, Vec3 *vmax) const; + const AABox &get_aabbox() const; + /// @} + + /** get the bounding sphere in local space. The result will be cached, and subsequent + * calls will return the same box. The cache gets invalidated by any functions that can affect + * the vertex data (non-const variant of get_attrib_data(MESH_ATTR_VERTEX, ...) included). + * @{ */ + float get_bsphere(Vec3 *center, float *rad) const; + const Sphere &get_bsphere() const; + /// @} + + static void set_intersect_mode(unsigned int mode); + static unsigned int get_intersect_mode(); + static void set_vertex_select_distance(float dist); + static float get_vertex_select_distance(); + + /** Find the intersection between the mesh and a ray. + * XXX Brute force at the moment, not intended to be used for anything other than picking in tools. + * If you intend to use it in a speed-critical part of the code, you'll *have* to optimize it! + */ + bool intersect(const Ray &ray, HitPoint *hit = 0) const; + + // texture coordinate manipulation + void texcoord_apply_xform(const Mat4 &xform); + void texcoord_gen_plane(const Vec3 &norm, const Vec3 &tang); + void texcoord_gen_box(); + void texcoord_gen_cylinder(); + + bool dump(const char *fname) const; + bool dump(FILE *fp) const; + bool dump_obj(const char *fname) const; + bool dump_obj(FILE *fp, int voffs = 0) const; +}; + +#endif // MESH_H_ diff --git a/src/meshgen.cc b/src/meshgen.cc new file mode 100644 index 0000000..7664aea --- /dev/null +++ b/src/meshgen.cc @@ -0,0 +1,883 @@ +#include +#include "meshgen.h" +#include "mesh.h" + +// -------- sphere -------- + +#define SURAD(u) ((u) * 2.0 * M_PI) +#define SVRAD(v) ((v) * M_PI) + +static Vec3 sphvec(float theta, float phi) +{ + return Vec3(sin(theta) * sin(phi), + cos(phi), + cos(theta) * sin(phi)); +} + +void gen_sphere(Mesh *mesh, float rad, int usub, int vsub, float urange, float vrange) +{ + if(urange == 0.0 || vrange == 0.0) return; + + if(usub < 4) usub = 4; + if(vsub < 2) vsub = 2; + + int uverts = usub + 1; + int vverts = vsub + 1; + + int num_verts = uverts * vverts; + int num_quads = usub * vsub; + int num_tri = num_quads * 2; + + mesh->clear(); + Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; i *verts, const Vec3 &v1, const Vec3 &v2, const Vec3 &v3, int iter) +{ + if(!iter) { + verts->push_back(v1); + verts->push_back(v2); + verts->push_back(v3); + return; + } + + Vec3 v12 = normalize(v1 + v2); + Vec3 v23 = normalize(v2 + v3); + Vec3 v31 = normalize(v3 + v1); + + geosphere(verts, v1, v12, v31, iter - 1); + geosphere(verts, v2, v23, v12, iter - 1); + geosphere(verts, v3, v31, v23, iter - 1); + geosphere(verts, v12, v23, v31, iter - 1); +} + +void gen_geosphere(Mesh *mesh, float rad, int subdiv, bool hemi) +{ + int num_tri = (sizeof icosa_idx / sizeof *icosa_idx) / 3; + + std::vector verts; + for(int i=0; iclear(); + Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + + for(int i=0; iclear(); + Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; iclear(); + Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; iclear(); + Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = urange / (float)(uverts - 1); + float dv = vrange / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; iclear(); + + int uverts = usub + 1; + int vverts = vsub + 1; + int num_verts = uverts * vverts; + + int num_quads = usub * vsub; + int num_tri = num_quads * 2; + + Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = 1.0 / (float)usub; + float dv = 1.0 / (float)vsub; + + float u = 0.0; + for(int i=0; iclear(); + + for(int i=0; i<6; i++) { + Mat4 xform, dir_xform; + Mesh m; + + gen_plane(&m, 1, 1, usub, vsub); + xform.translate(Vec3(0, 0, 0.5)); + xform.rotate(Vec3(face_angles[i][1], face_angles[i][0], 0)); + dir_xform = xform; + m.apply_xform(xform, dir_xform); + + mesh->append(m); + } + + Mat4 scale; + scale.scaling(xsz, ysz, zsz); + mesh->apply_xform(scale, Mat4::identity); +} + +/* +void gen_box(Mesh *mesh, float xsz, float ysz, float zsz) +{ + mesh->clear(); + + const int num_faces = 6; + int num_verts = num_faces * 4; + int num_tri = num_faces * 2; + + float x = xsz / 2.0; + float y = ysz / 2.0; + float z = zsz / 2.0; + + Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + static const Vec2 uv[] = { Vec2(0, 0), Vec2(1, 0), Vec2(1, 1), Vec2(0, 1) }; + + // front + for(int i=0; i<4; i++) { + *narr++ = Vec3(0, 0, 1); + *tarr++ = Vec3(1, 0, 0); + *uvarr++ = uv[i]; + } + *varr++ = Vec3(-x, -y, z); + *varr++ = Vec3(x, -y, z); + *varr++ = Vec3(x, y, z); + *varr++ = Vec3(-x, y, z); + // right + for(int i=0; i<4; i++) { + *narr++ = Vec3(1, 0, 0); + *tarr++ = Vec3(0, 0, -1); + *uvarr++ = uv[i]; + } + *varr++ = Vec3(x, -y, z); + *varr++ = Vec3(x, -y, -z); + *varr++ = Vec3(x, y, -z); + *varr++ = Vec3(x, y, z); + // back + for(int i=0; i<4; i++) { + *narr++ = Vec3(0, 0, -1); + *tarr++ = Vec3(-1, 0, 0); + *uvarr++ = uv[i]; + } + *varr++ = Vec3(x, -y, -z); + *varr++ = Vec3(-x, -y, -z); + *varr++ = Vec3(-x, y, -z); + *varr++ = Vec3(x, y, -z); + // left + for(int i=0; i<4; i++) { + *narr++ = Vec3(-1, 0, 0); + *tarr++ = Vec3(0, 0, 1); + *uvarr++ = uv[i]; + } + *varr++ = Vec3(-x, -y, -z); + *varr++ = Vec3(-x, -y, z); + *varr++ = Vec3(-x, y, z); + *varr++ = Vec3(-x, y, -z); + // top + for(int i=0; i<4; i++) { + *narr++ = Vec3(0, 1, 0); + *tarr++ = Vec3(1, 0, 0); + *uvarr++ = uv[i]; + } + *varr++ = Vec3(-x, y, z); + *varr++ = Vec3(x, y, z); + *varr++ = Vec3(x, y, -z); + *varr++ = Vec3(-x, y, -z); + // bottom + for(int i=0; i<4; i++) { + *narr++ = Vec3(0, -1, 0); + *tarr++ = Vec3(1, 0, 0); + *uvarr++ = uv[i]; + } + *varr++ = Vec3(-x, -y, -z); + *varr++ = Vec3(x, -y, -z); + *varr++ = Vec3(x, -y, z); + *varr++ = Vec3(-x, -y, z); + + // index array + static const int faceidx[] = {0, 1, 2, 0, 2, 3}; + for(int i=0; iclear(); + + int uverts = usub + 1; + int vverts = vsub + 1; + int num_verts = uverts * vverts; + + int num_quads = usub * vsub; + int num_tri = num_quads * 2; + + Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = 1.0 / (float)(uverts - 1); + float dv = 1.0 / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; i 0.5 ? v - dv * 0.25 : v + dv * 0.25; + nextu = rev_vert(fmod(u + du, 1.0), new_v, rfunc, cls); + tang = nextu - pos; + } + + Vec3 normal; + if(nfunc) { + normal = rev_vert(u, v, nfunc, cls); + } else { + Vec3 nextv = rev_vert(u, v + dv, rfunc, cls); + Vec3 bitan = nextv - pos; + if(length_sq(bitan) < 1e-6) { + nextv = rev_vert(u, v - dv, rfunc, cls); + bitan = pos - nextv; + } + + normal = cross(tang, bitan); + } + + *varr++ = pos; + *narr++ = normalize(normal); + *tarr++ = normalize(tang); + *uvarr++ = Vec2(u, v); + + if(i < usub && j < vsub) { + int idx = i * vverts + j; + + *idxarr++ = idx; + *idxarr++ = idx + vverts + 1; + *idxarr++ = idx + 1; + + *idxarr++ = idx; + *idxarr++ = idx + vverts; + *idxarr++ = idx + vverts + 1; + } + + v += dv; + } + u += du; + } +} + + +static inline Vec3 sweep_vert(float u, float v, float height, Vec2 (*sf)(float, float, void*), void *cls) +{ + Vec2 pos = sf(u, v, cls); + + float x = pos.x; + float y = v * height; + float z = pos.y; + + return Vec3(x, y, z); +} + +// ---- sweep shape along a path ---- +void gen_sweep(Mesh *mesh, float height, int usub, int vsub, Vec2 (*sfunc)(float, float, void*), void *cls) +{ + if(!sfunc) return; + if(usub < 3) usub = 3; + if(vsub < 1) vsub = 1; + + mesh->clear(); + + int uverts = usub + 1; + int vverts = vsub + 1; + int num_verts = uverts * vverts; + + int num_quads = usub * vsub; + int num_tri = num_quads * 2; + + Vec3 *varr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, 0); + Vec3 *narr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, 0); + Vec3 *tarr = (Vec3*)mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, 0); + Vec2 *uvarr = (Vec2*)mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, 0); + unsigned int *idxarr = mesh->set_index_data(num_tri * 3, 0); + + float du = 1.0 / (float)(uverts - 1); + float dv = 1.0 / (float)(vverts - 1); + + float u = 0.0; + for(int i=0; i 0.5 ? v - dv * 0.25 : v + dv * 0.25; + nextu = sweep_vert(fmod(u + du, 1.0), new_v, height, sfunc, cls); + tang = nextu - pos; + } + + Vec3 normal; + Vec3 nextv = sweep_vert(u, v + dv, height, sfunc, cls); + Vec3 bitan = nextv - pos; + if(length_sq(bitan) < 1e-6) { + nextv = sweep_vert(u, v - dv, height, sfunc, cls); + bitan = pos - nextv; + } + + normal = cross(tang, bitan); + + *varr++ = pos; + *narr++ = normalize(normal); + *tarr++ = normalize(tang); + *uvarr++ = Vec2(u, v); + + if(i < usub && j < vsub) { + int idx = i * vverts + j; + + *idxarr++ = idx; + *idxarr++ = idx + vverts + 1; + *idxarr++ = idx + 1; + + *idxarr++ = idx; + *idxarr++ = idx + vverts; + *idxarr++ = idx + vverts + 1; + } + + v += dv; + } + u += du; + } +} diff --git a/src/meshgen.h b/src/meshgen.h new file mode 100644 index 0000000..f154c24 --- /dev/null +++ b/src/meshgen.h @@ -0,0 +1,23 @@ +#ifndef MESHGEN_H_ +#define MESHGEN_H_ + +#include "gmath/gmath.h" + +class Mesh; + +void gen_sphere(Mesh *mesh, float rad, int usub, int vsub, float urange = 1.0, float vrange = 1.0); +void gen_geosphere(Mesh *mesh, float rad, int subdiv, bool hemi = false); +void gen_torus(Mesh *mesh, float mainrad, float ringrad, int usub, int vsub, float urange = 1.0, float vrange = 1.0); +void gen_cylinder(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub = 0, float urange = 1.0, float vrange = 1.0); +void gen_cone(Mesh *mesh, float rad, float height, int usub, int vsub, int capsub = 0, float urange = 1.0, float vrange = 1.0); +void gen_plane(Mesh *mesh, float width, float height, int usub = 1, int vsub = 1); +void gen_heightmap(Mesh *mesh, float width, float height, int usub, int vsub, float (*hf)(float, float, void*), void *hfdata = 0); +void gen_box(Mesh *mesh, float xsz, float ysz, float zsz, int usub = 1, int vsub = 1); + +void gen_revol(Mesh *mesh, int usub, int vsub, Vec2 (*rfunc)(float, float, void*), void *cls = 0); +void gen_revol(Mesh *mesh, int usub, int vsub, Vec2 (*rfunc)(float, float, void*), Vec2 (*nfunc)(float, float, void*), void *cls); + +/* callback args: (float u, float v, void *cls) -> Vec2 XZ offset u,v in [0, 1] */ +void gen_sweep(Mesh *mesh, float height, int usub, int vsub, Vec2 (*sfunc)(float, float, void*), void *cls = 0); + +#endif // MESHGEN_H_ diff --git a/src/opengl.c b/src/opengl.c index 5c3ff44..06c637b 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -18,6 +18,9 @@ int init_opengl(void) if(GLEW_EXT_texture_filter_anisotropic) { glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glcaps.max_aniso); } + if(GLEW_ARB_tessellation_shader) { + glGetIntegerv(GL_MAX_TESS_GEN_LEVEL, &glcaps.max_tess_level); + } glcaps.debug = GLEW_ARB_debug_output; diff --git a/src/opengl.h b/src/opengl.h index 397cd38..b338cd9 100644 --- a/src/opengl.h +++ b/src/opengl.h @@ -6,6 +6,7 @@ struct GLCaps { int debug; /* ARB_debug_output */ int max_aniso; + int max_tess_level; }; extern struct GLCaps glcaps;