nuclear@25: #include nuclear@25: #include nuclear@25: #include nuclear@25: #include nuclear@25: #include "opengl.h" nuclear@25: #include "mesh.h" nuclear@25: //#include "xform_node.h" nuclear@25: #include "shader.h" nuclear@25: nuclear@25: int Mesh::global_sdr_loc[NUM_MESH_ATTR] = { nuclear@25: (int)SDR_ATTR_VERTEX, nuclear@25: (int)SDR_ATTR_NORMAL, nuclear@25: (int)SDR_ATTR_TANGENT, nuclear@25: (int)SDR_ATTR_TEXCOORD, nuclear@25: (int)SDR_ATTR_COLOR, nuclear@25: -1, -1}; nuclear@25: unsigned int Mesh::intersect_mode = ISECT_DEFAULT; nuclear@25: float Mesh::vertex_sel_dist = 0.01; nuclear@25: float Mesh::vis_vecsize = 1.0; nuclear@25: nuclear@25: Mesh::Mesh() nuclear@25: { nuclear@25: clear(); nuclear@25: nuclear@25: glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects); nuclear@25: nuclear@25: for(int i=0; iname = name; nuclear@25: } nuclear@25: nuclear@25: const char *Mesh::get_name() const nuclear@25: { nuclear@25: return name.c_str(); nuclear@25: } nuclear@25: nuclear@25: bool Mesh::has_attrib(int attr) const nuclear@25: { nuclear@25: if(attr < 0 || attr >= NUM_MESH_ATTR) { nuclear@25: return false; nuclear@25: } nuclear@25: nuclear@25: // if neither of these is valid, then nobody has set this attribute nuclear@25: return vattr[attr].vbo_valid || vattr[attr].data_valid; nuclear@25: } nuclear@25: nuclear@25: bool Mesh::is_indexed() const nuclear@25: { nuclear@25: return ibo_valid || idata_valid; nuclear@25: } nuclear@25: nuclear@25: void Mesh::clear() nuclear@25: { nuclear@25: //bones.clear(); nuclear@25: nuclear@25: for(int i=0; i= NUM_MESH_ATTR) { nuclear@25: fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: if(nverts && num != nverts) { nuclear@25: fprintf(stderr, "%s: attribute count missmatch (%d instead of %d)\n", __FUNCTION__, num, nverts); nuclear@25: return 0; nuclear@25: } nuclear@25: nverts = num; nuclear@25: nuclear@25: vattr[attrib].data.clear(); nuclear@25: vattr[attrib].nelem = nelem; nuclear@25: vattr[attrib].data.resize(num * nelem); nuclear@25: nuclear@25: if(data) { nuclear@25: memcpy(&vattr[attrib].data[0], data, num * nelem * sizeof *data); nuclear@25: } nuclear@25: nuclear@25: vattr[attrib].data_valid = true; nuclear@25: vattr[attrib].vbo_valid = false; nuclear@25: return &vattr[attrib].data[0]; nuclear@25: } nuclear@25: nuclear@25: float *Mesh::get_attrib_data(int attrib) nuclear@25: { nuclear@25: if(attrib < 0 || attrib >= NUM_MESH_ATTR) { nuclear@25: fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: vattr[attrib].vbo_valid = false; nuclear@25: return (float*)((const Mesh*)this)->get_attrib_data(attrib); nuclear@25: } nuclear@25: nuclear@25: const float *Mesh::get_attrib_data(int attrib) const nuclear@25: { nuclear@25: if(attrib < 0 || attrib >= NUM_MESH_ATTR) { nuclear@25: fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib); nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: if(!vattr[attrib].data_valid) { nuclear@25: #if GL_ES_VERSION_2_0 nuclear@25: fprintf(stderr, "%s: can't read back attrib data on CrippledGL ES\n", __FUNCTION__); nuclear@25: return 0; nuclear@25: #else nuclear@25: if(!vattr[attrib].vbo_valid) { nuclear@25: fprintf(stderr, "%s: unavailable attrib: %d\n", __FUNCTION__, attrib); nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: // local data copy is unavailable, grab the data from the vbo nuclear@25: Mesh *m = (Mesh*)this; nuclear@25: m->vattr[attrib].data.resize(nverts * vattr[attrib].nelem); nuclear@25: nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, vattr[attrib].vbo); nuclear@25: void *data = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); nuclear@25: memcpy(&m->vattr[attrib].data[0], data, nverts * vattr[attrib].nelem * sizeof(float)); nuclear@25: glUnmapBuffer(GL_ARRAY_BUFFER); nuclear@25: nuclear@25: vattr[attrib].data_valid = true; nuclear@25: #endif nuclear@25: } nuclear@25: nuclear@25: return &vattr[attrib].data[0]; nuclear@25: } nuclear@25: nuclear@25: void Mesh::set_attrib(int attrib, int idx, const Vector4 &v) nuclear@25: { nuclear@25: float *data = get_attrib_data(attrib); nuclear@25: if(data) { nuclear@25: data += idx * vattr[attrib].nelem; nuclear@25: for(int i=0; iget_index_data(); nuclear@25: } nuclear@25: nuclear@25: const unsigned int *Mesh::get_index_data() const nuclear@25: { nuclear@25: if(!idata_valid) { nuclear@25: #if GL_ES_VERSION_2_0 nuclear@25: fprintf(stderr, "%s: can't read back index data in CrippledGL ES\n", __FUNCTION__); nuclear@25: return 0; nuclear@25: #else nuclear@25: if(!ibo_valid) { nuclear@25: fprintf(stderr, "%s: indices unavailable\n", __FUNCTION__); nuclear@25: return 0; nuclear@25: } nuclear@25: nuclear@25: // local data copy is unavailable, gram the data from the ibo nuclear@25: Mesh *m = (Mesh*)this; nuclear@25: int nidx = nfaces * 3; nuclear@25: m->idata.resize(nidx); nuclear@25: nuclear@25: glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); nuclear@25: void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); nuclear@25: memcpy(&m->idata[0], data, nidx * sizeof(unsigned int)); nuclear@25: glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); nuclear@25: nuclear@25: idata_valid = true; nuclear@25: #endif nuclear@25: } nuclear@25: nuclear@25: return &idata[0]; nuclear@25: } nuclear@25: nuclear@25: int Mesh::get_index_count() const nuclear@25: { nuclear@25: return nfaces * 3; nuclear@25: } nuclear@25: nuclear@25: void Mesh::append(const Mesh &mesh) nuclear@25: { nuclear@25: unsigned int idxoffs = nverts; nuclear@25: nuclear@25: nverts += mesh.nverts; nuclear@25: nfaces += mesh.nfaces; nuclear@25: nuclear@25: for(int i=0; i= NUM_MESH_ATTR) { nuclear@25: return; nuclear@25: } nuclear@25: Mesh::global_sdr_loc[attr] = loc; nuclear@25: } nuclear@25: nuclear@25: /// static function nuclear@25: int Mesh::get_attrib_location(int attr) nuclear@25: { nuclear@25: if(attr < 0 || attr >= NUM_MESH_ATTR) { nuclear@25: return -1; nuclear@25: } nuclear@25: return Mesh::global_sdr_loc[attr]; nuclear@25: } nuclear@25: nuclear@25: /// static function nuclear@25: void Mesh::clear_attrib_locations() nuclear@25: { nuclear@25: for(int i=0; i= (int)bones.size()) { nuclear@25: return 0; nuclear@25: } nuclear@25: return bones[idx]; nuclear@25: } nuclear@25: nuclear@25: int Mesh::get_bones_count() const nuclear@25: { nuclear@25: return (int)bones.size(); nuclear@25: } nuclear@25: */ nuclear@25: nuclear@25: void Mesh::draw() const nuclear@25: { nuclear@25: #ifdef GL_ES_VERSION_2_0 nuclear@25: if(!SdrProg::active) { nuclear@25: fprintf(stderr, "%s: CrippledGL ES can't draw without a shader\n", __FUNCTION__); nuclear@25: return; nuclear@25: } nuclear@25: #endif nuclear@25: nuclear@25: ((Mesh*)this)->update_buffers(); nuclear@25: nuclear@25: if(!vattr[MESH_ATTR_VERTEX].vbo_valid) { nuclear@25: fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__); nuclear@25: return; nuclear@25: } nuclear@25: nuclear@25: if(SdrProg::active) { nuclear@25: // rendering with shaders nuclear@25: if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { nuclear@25: fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__); nuclear@25: return; nuclear@25: } nuclear@25: nuclear@25: for(int i=0; i= 0 && vattr[i].vbo_valid) { nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); nuclear@25: glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); nuclear@25: glEnableVertexAttribArray(loc); nuclear@25: } nuclear@25: } nuclear@25: } else { nuclear@25: #ifndef GL_ES_VERSION_2_0 nuclear@25: // rendering with fixed-function (not available in GLES2) nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_VERTEX].vbo); nuclear@25: glVertexPointer(vattr[MESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0); nuclear@25: glEnableClientState(GL_VERTEX_ARRAY); nuclear@25: nuclear@25: if(vattr[MESH_ATTR_NORMAL].vbo_valid) { nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_NORMAL].vbo); nuclear@25: glNormalPointer(GL_FLOAT, 0, 0); nuclear@25: glEnableClientState(GL_NORMAL_ARRAY); nuclear@25: } nuclear@25: if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) { nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_TEXCOORD].vbo); nuclear@25: glTexCoordPointer(vattr[MESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0); nuclear@25: glEnableClientState(GL_TEXTURE_COORD_ARRAY); nuclear@25: } nuclear@25: if(vattr[MESH_ATTR_COLOR].vbo_valid) { nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_COLOR].vbo); nuclear@25: glColorPointer(vattr[MESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0); nuclear@25: glEnableClientState(GL_COLOR_ARRAY); nuclear@25: } nuclear@25: #endif nuclear@25: } nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, 0); nuclear@25: nuclear@25: if(ibo_valid) { nuclear@25: glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); nuclear@25: glDrawElements(GL_TRIANGLES, nfaces * 3, GL_UNSIGNED_INT, 0); nuclear@25: glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); nuclear@25: } else { nuclear@25: glDrawArrays(GL_TRIANGLES, 0, nverts); nuclear@25: } nuclear@25: nuclear@25: if(SdrProg::active) { nuclear@25: // rendered with shaders nuclear@25: for(int i=0; i= 0 && vattr[i].vbo_valid) { nuclear@25: glDisableVertexAttribArray(loc); nuclear@25: } nuclear@25: } nuclear@25: } else { nuclear@25: #ifndef GL_ES_VERSION_2_0 nuclear@25: // rendered with fixed-function nuclear@25: glDisableClientState(GL_VERTEX_ARRAY); nuclear@25: if(vattr[MESH_ATTR_NORMAL].vbo_valid) { nuclear@25: glDisableClientState(GL_NORMAL_ARRAY); nuclear@25: } nuclear@25: if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) { nuclear@25: glDisableClientState(GL_TEXTURE_COORD_ARRAY); nuclear@25: } nuclear@25: if(vattr[MESH_ATTR_COLOR].vbo_valid) { nuclear@25: glDisableClientState(GL_COLOR_ARRAY); nuclear@25: } nuclear@25: #endif nuclear@25: } nuclear@25: } nuclear@25: nuclear@25: void Mesh::draw_wire() const nuclear@25: { nuclear@25: ((Mesh*)this)->update_wire_ibo(); nuclear@25: nuclear@25: if(!vattr[MESH_ATTR_VERTEX].vbo_valid || !wire_ibo_valid) { nuclear@25: fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__); nuclear@25: return; nuclear@25: } nuclear@25: if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { nuclear@25: fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__); nuclear@25: return; nuclear@25: } nuclear@25: nuclear@25: for(int i=0; i= 0 && vattr[i].vbo_valid) { nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); nuclear@25: glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); nuclear@25: glEnableVertexAttribArray(loc); nuclear@25: } nuclear@25: } nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, 0); nuclear@25: nuclear@25: glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo); nuclear@25: glDrawElements(GL_LINES, nfaces * 6, GL_UNSIGNED_INT, 0); nuclear@25: glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); nuclear@25: nuclear@25: for(int i=0; i= 0 && vattr[i].vbo_valid) { nuclear@25: glDisableVertexAttribArray(loc); nuclear@25: } nuclear@25: } nuclear@25: } nuclear@25: nuclear@25: void Mesh::draw_vertices() const nuclear@25: { nuclear@25: ((Mesh*)this)->update_buffers(); nuclear@25: nuclear@25: if(!vattr[MESH_ATTR_VERTEX].vbo_valid) { nuclear@25: fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__); nuclear@25: return; nuclear@25: } nuclear@25: if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) { nuclear@25: fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__); nuclear@25: return; nuclear@25: } nuclear@25: nuclear@25: for(int i=0; i= 0 && vattr[i].vbo_valid) { nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo); nuclear@25: glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0); nuclear@25: glEnableVertexAttribArray(loc); nuclear@25: } nuclear@25: } nuclear@25: glBindBuffer(GL_ARRAY_BUFFER, 0); nuclear@25: nuclear@25: glDrawArrays(GL_POINTS, 0, nverts); nuclear@25: nuclear@25: for(int i=0; i= 0 && vattr[i].vbo_valid) { nuclear@25: glDisableVertexAttribArray(loc); nuclear@25: } nuclear@25: } nuclear@25: } nuclear@25: nuclear@25: void Mesh::draw_normals() const nuclear@25: { nuclear@25: #ifdef USE_OLDGL nuclear@25: Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX); nuclear@25: Vector3 *norm = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL); nuclear@25: if(!varr || !norm) { nuclear@25: return; nuclear@25: } nuclear@25: nuclear@25: glBegin(GL_LINES); nuclear@25: if(get_current_shader()) { nuclear@25: int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX]; nuclear@25: if(vert_loc < 0) { nuclear@25: glEnd(); nuclear@25: return; nuclear@25: } nuclear@25: nuclear@25: for(size_t i=0; icalc_aabb(); nuclear@25: } nuclear@25: *vmin = aabb.min; nuclear@25: *vmax = aabb.max; nuclear@25: } nuclear@25: nuclear@25: const AABox &Mesh::get_aabbox() const nuclear@25: { nuclear@25: if(!aabb_valid) { nuclear@25: ((Mesh*)this)->calc_aabb(); nuclear@25: } nuclear@25: return aabb; nuclear@25: } nuclear@25: nuclear@25: float Mesh::get_bsphere(Vector3 *center, float *rad) const nuclear@25: { nuclear@25: if(!bsph_valid) { nuclear@25: ((Mesh*)this)->calc_bsph(); nuclear@25: } nuclear@25: *center = bsph.center; nuclear@25: *rad = bsph.radius; nuclear@25: return bsph.radius; nuclear@25: } nuclear@25: nuclear@25: const Sphere &Mesh::get_bsphere() const nuclear@25: { nuclear@25: if(!bsph_valid) { nuclear@25: ((Mesh*)this)->calc_bsph(); nuclear@25: } nuclear@25: return bsph; nuclear@25: } nuclear@25: nuclear@25: /// static function nuclear@25: void Mesh::set_intersect_mode(unsigned int mode) nuclear@25: { nuclear@25: Mesh::intersect_mode = mode; nuclear@25: } nuclear@25: nuclear@25: /// static function nuclear@25: unsigned int Mesh::get_intersect_mode() nuclear@25: { nuclear@25: return Mesh::intersect_mode; nuclear@25: } nuclear@25: nuclear@25: /// static function nuclear@25: void Mesh::set_vertex_select_distance(float dist) nuclear@25: { nuclear@25: Mesh::vertex_sel_dist = dist; nuclear@25: } nuclear@25: nuclear@25: /// static function nuclear@25: float Mesh::get_vertex_select_distance() nuclear@25: { nuclear@25: return Mesh::vertex_sel_dist; nuclear@25: } nuclear@25: nuclear@25: bool Mesh::intersect(const Ray &ray, HitPoint *hit) const nuclear@25: { nuclear@25: assert((Mesh::intersect_mode & (ISECT_VERTICES | ISECT_FACE)) != (ISECT_VERTICES | ISECT_FACE)); nuclear@25: nuclear@25: const Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX); nuclear@25: const Vector3 *narr = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL); nuclear@25: if(!varr) { nuclear@25: return false; nuclear@25: } nuclear@25: const unsigned int *idxarr = get_index_data(); nuclear@25: nuclear@25: // first test with the bounding box nuclear@25: AABox box; nuclear@25: get_aabbox(&box.min, &box.max); nuclear@25: if(!box.intersect(ray)) { nuclear@25: return false; nuclear@25: } nuclear@25: nuclear@25: HitPoint nearest_hit; nuclear@25: nearest_hit.dist = FLT_MAX; nuclear@25: nearest_hit.obj = 0; nuclear@25: nuclear@25: if(Mesh::intersect_mode & ISECT_VERTICES) { nuclear@25: // we asked for "intersections" with the vertices of the mesh nuclear@25: long nearest_vidx = -1; nuclear@25: float thres_sq = Mesh::vertex_sel_dist * Mesh::vertex_sel_dist; nuclear@25: nuclear@25: for(unsigned int i=0; i 0) { nuclear@25: continue; nuclear@25: } nuclear@25: nuclear@25: // project the vertex onto the ray line nuclear@25: float t = dot_product(varr[i] - ray.origin, ray.dir); nuclear@25: Vector3 vproj = ray.origin + ray.dir * t; nuclear@25: nuclear@25: float dist_sq = (vproj - varr[i]).length_sq(); nuclear@25: if(dist_sq < thres_sq) { nuclear@25: if(!hit) { nuclear@25: return true; nuclear@25: } nuclear@25: if(t < nearest_hit.dist) { nuclear@25: nearest_hit.dist = t; nuclear@25: nearest_vidx = i; nuclear@25: } nuclear@25: } nuclear@25: } nuclear@25: nuclear@25: if(nearest_vidx != -1) { nuclear@25: hitvert = varr[nearest_vidx]; nuclear@25: nearest_hit.obj = &hitvert; nuclear@25: } nuclear@25: nuclear@25: } else { nuclear@25: // regular intersection test with polygons nuclear@25: nuclear@25: for(unsigned int i=0; i 0) { nuclear@25: continue; nuclear@25: } nuclear@25: nuclear@25: HitPoint fhit; nuclear@25: if(face.intersect(ray, hit ? &fhit : 0)) { nuclear@25: if(!hit) { nuclear@25: return true; nuclear@25: } nuclear@25: if(fhit.dist < nearest_hit.dist) { nuclear@25: nearest_hit = fhit; nuclear@25: hitface = face; nuclear@25: } nuclear@25: } nuclear@25: } nuclear@25: } nuclear@25: nuclear@25: if(nearest_hit.obj) { nuclear@25: if(hit) { nuclear@25: *hit = nearest_hit; nuclear@25: nuclear@25: // if we are interested in the mesh and not the faces set obj to this nuclear@25: if(Mesh::intersect_mode & ISECT_FACE) { nuclear@25: hit->obj = &hitface; nuclear@25: } else if(Mesh::intersect_mode & ISECT_VERTICES) { nuclear@25: hit->obj = &hitvert; nuclear@25: } else { nuclear@25: hit->obj = this; nuclear@25: } nuclear@25: } nuclear@25: return true; nuclear@25: } nuclear@25: return false; nuclear@25: } nuclear@25: nuclear@25: nuclear@25: // ------ private member functions ------ nuclear@25: nuclear@25: void Mesh::calc_aabb() nuclear@25: { nuclear@25: // the cast is to force calling the const version which doesn't invalidate nuclear@25: if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) { nuclear@25: return; nuclear@25: } nuclear@25: nuclear@25: aabb.min = Vector3(FLT_MAX, FLT_MAX, FLT_MAX); nuclear@25: aabb.max = -aabb.min; nuclear@25: nuclear@25: for(unsigned int i=0; i aabb.max[j]) { nuclear@25: aabb.max[j] = v[j]; nuclear@25: } nuclear@25: } nuclear@25: } nuclear@25: aabb_valid = true; nuclear@25: } nuclear@25: nuclear@25: void Mesh::calc_bsph() nuclear@25: { nuclear@25: // the cast is to force calling the const version which doesn't invalidate nuclear@25: if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) { nuclear@25: return; nuclear@25: } nuclear@25: nuclear@25: Vector3 v; nuclear@25: bsph.center = Vector3(0, 0, 0); nuclear@25: nuclear@25: // first find the center nuclear@25: for(unsigned int i=0; i bsph.radius) { nuclear@25: bsph.radius = dist_sq; nuclear@25: } nuclear@25: } nuclear@25: bsph.radius = sqrt(bsph.radius); nuclear@25: nuclear@25: bsph_valid = true; nuclear@25: } nuclear@25: nuclear@25: void Mesh::update_buffers() nuclear@25: { nuclear@25: for(int i=0; iget_index_data(); nuclear@25: nuclear@25: for(unsigned int i=0; icalc_normal(); nuclear@25: } nuclear@25: return normal; nuclear@25: } nuclear@25: nuclear@25: void Triangle::transform(const Matrix4x4 &xform) nuclear@25: { nuclear@25: v[0].transform(xform); nuclear@25: v[1].transform(xform); nuclear@25: v[2].transform(xform); nuclear@25: normal_valid = false; nuclear@25: } nuclear@25: nuclear@25: void Triangle::draw() const nuclear@25: { nuclear@25: Vector3 n[3]; nuclear@25: n[0] = get_normal(); nuclear@25: n[1] = get_normal(); nuclear@25: n[2] = get_normal(); nuclear@25: nuclear@25: int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX); nuclear@25: int nloc = Mesh::get_attrib_location(MESH_ATTR_NORMAL); nuclear@25: nuclear@25: glEnableVertexAttribArray(vloc); nuclear@25: glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x); nuclear@25: glVertexAttribPointer(nloc, 3, GL_FLOAT, GL_FALSE, 0, &n[0].x); nuclear@25: nuclear@25: glDrawArrays(GL_TRIANGLES, 0, 3); nuclear@25: nuclear@25: glDisableVertexAttribArray(vloc); nuclear@25: glDisableVertexAttribArray(nloc); nuclear@25: CHECK_GLERROR; nuclear@25: } nuclear@25: nuclear@25: void Triangle::draw_wire() const nuclear@25: { nuclear@25: static const int idxarr[] = {0, 1, 1, 2, 2, 0}; nuclear@25: int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX); nuclear@25: nuclear@25: glEnableVertexAttribArray(vloc); nuclear@25: glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x); nuclear@25: nuclear@25: glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, idxarr); nuclear@25: nuclear@25: glDisableVertexAttribArray(vloc); nuclear@25: CHECK_GLERROR; nuclear@25: } nuclear@25: nuclear@25: Vector3 Triangle::calc_barycentric(const Vector3 &pos) const nuclear@25: { nuclear@25: Vector3 norm = get_normal(); nuclear@25: nuclear@25: float area_sq = fabs(dot_product(cross_product(v[1] - v[0], v[2] - v[0]), norm)); nuclear@25: if(area_sq < 1e-5) { nuclear@25: return Vector3(0, 0, 0); nuclear@25: } nuclear@25: nuclear@25: float asq0 = fabs(dot_product(cross_product(v[1] - pos, v[2] - pos), norm)); nuclear@25: float asq1 = fabs(dot_product(cross_product(v[2] - pos, v[0] - pos), norm)); nuclear@25: float asq2 = fabs(dot_product(cross_product(v[0] - pos, v[1] - pos), norm)); nuclear@25: nuclear@25: return Vector3(asq0 / area_sq, asq1 / area_sq, asq2 / area_sq); nuclear@25: } nuclear@25: nuclear@25: bool Triangle::intersect(const Ray &ray, HitPoint *hit) const nuclear@25: { nuclear@25: Vector3 normal = get_normal(); nuclear@25: nuclear@25: float ndotdir = dot_product(ray.dir, normal); nuclear@25: if(fabs(ndotdir) < 1e-4) { nuclear@25: return false; nuclear@25: } nuclear@25: nuclear@25: Vector3 vertdir = v[0] - ray.origin; nuclear@25: float t = dot_product(normal, vertdir) / ndotdir; nuclear@25: nuclear@25: Vector3 pos = ray.origin + ray.dir * t; nuclear@25: Vector3 bary = calc_barycentric(pos); nuclear@25: nuclear@25: if(bary.x + bary.y + bary.z > 1.00001) { nuclear@25: return false; nuclear@25: } nuclear@25: nuclear@25: if(hit) { nuclear@25: hit->dist = t; nuclear@25: hit->pos = ray.origin + ray.dir * t; nuclear@25: hit->normal = normal; nuclear@25: hit->obj = this; nuclear@25: } nuclear@25: return true; nuclear@25: }