X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=ld42_outofspace;a=blobdiff_plain;f=src%2Fmesh.cc;fp=src%2Fmesh.cc;h=798e8d197a2f8be7bf15a84f3d2645f1e12666fc;hp=0000000000000000000000000000000000000000;hb=066942396f5fbac07e608bc5b23bb6d3cdccc42a;hpb=89df915930177f3bc5f71095562c6e15074be220 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; +}