goat3dgfx
diff src/curve.cc @ 0:1873dfd13f2d
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 14 Nov 2013 05:27:09 +0200 |
parents | |
children | 7d6b667821cf |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/curve.cc Thu Nov 14 05:27:09 2013 +0200 1.3 @@ -0,0 +1,316 @@ 1.4 +#include <float.h> 1.5 +#include <assert.h> 1.6 +#include "curve.h" 1.7 +#include "opengl.h" 1.8 +#include "shader.h" 1.9 +#include "logger.h" 1.10 + 1.11 +#define DEF_THICKNESS 0.075 1.12 +#define DEF_SEGM_SUB 3 1.13 +#define DEF_RING_SUB 6 1.14 + 1.15 +Curve::Curve() 1.16 +{ 1.17 + thickness = DEF_THICKNESS; 1.18 + mesh_valid = false; 1.19 + lengths_valid = false; 1.20 + 1.21 + bbox_valid = false; 1.22 + 1.23 + segm_subdiv = DEF_SEGM_SUB; 1.24 + ring_subdiv = DEF_RING_SUB; 1.25 +} 1.26 + 1.27 +Curve::Curve(const Vector3 *points, int num_points) 1.28 +{ 1.29 + thickness = DEF_THICKNESS; 1.30 + mesh_valid = false; 1.31 + lengths_valid = false; 1.32 + 1.33 + bbox_valid = false; 1.34 + 1.35 + segm_subdiv = DEF_SEGM_SUB; 1.36 + ring_subdiv = DEF_RING_SUB; 1.37 + 1.38 + for(int i=0; i<num_points; i++) { 1.39 + add_point(points[i]); 1.40 + } 1.41 +} 1.42 + 1.43 +Curve::Curve(const Vector2 *points, int num_points) 1.44 +{ 1.45 + thickness = DEF_THICKNESS; 1.46 + mesh_valid = false; 1.47 + lengths_valid = false; 1.48 + 1.49 + bbox_valid = false; 1.50 + 1.51 + segm_subdiv = DEF_SEGM_SUB; 1.52 + ring_subdiv = DEF_RING_SUB; 1.53 + 1.54 + for(int i=0; i<num_points; i++) { 1.55 + add_point(Vector3(points[i].x, points[i].y, 0.0)); 1.56 + } 1.57 +} 1.58 + 1.59 +void Curve::set_name(const char *name) 1.60 +{ 1.61 + this->name = name; 1.62 +} 1.63 + 1.64 +const char *Curve::get_name() const 1.65 +{ 1.66 + return name.c_str(); 1.67 +} 1.68 + 1.69 +bool Curve::empty() const 1.70 +{ 1.71 + return cv.empty(); 1.72 +} 1.73 + 1.74 +void Curve::set_thickness(float thickness) 1.75 +{ 1.76 + this->thickness = thickness; 1.77 +} 1.78 + 1.79 +void Curve::set_subdiv(int seg, int ring) 1.80 +{ 1.81 + if(seg < 1) seg = 1; 1.82 + if(ring < 3) ring = 3; 1.83 + 1.84 + segm_subdiv = seg; 1.85 + ring_subdiv = ring; 1.86 +} 1.87 + 1.88 +void Curve::clear() 1.89 +{ 1.90 + mesh_valid = false; 1.91 + lengths_valid = false; 1.92 + bbox_valid = false; 1.93 + cv.clear(); 1.94 +} 1.95 + 1.96 +void Curve::add_point(const Vector3 &pt) 1.97 +{ 1.98 + cv.push_back(pt); 1.99 + mesh_valid = false; 1.100 + lengths_valid = false; 1.101 + bbox_valid = false; 1.102 +} 1.103 + 1.104 +Vector3 &Curve::get_point(int idx) 1.105 +{ 1.106 + mesh_valid = false; 1.107 + lengths_valid = false; 1.108 + bbox_valid = false; 1.109 + return cv[idx]; 1.110 +} 1.111 + 1.112 +const Vector3 &Curve::get_point(int idx) const 1.113 +{ 1.114 + return cv[idx]; 1.115 +} 1.116 + 1.117 +int Curve::get_count() const 1.118 +{ 1.119 + return (int)cv.size(); 1.120 +} 1.121 + 1.122 +Vector3 &Curve::operator[] (int idx) 1.123 +{ 1.124 + return get_point(idx); 1.125 +} 1.126 + 1.127 +const Vector3 &Curve::operator[] (int idx) const 1.128 +{ 1.129 + return get_point(idx); 1.130 +} 1.131 + 1.132 +void Curve::get_bbox(Vector3 *bbmin, Vector3 *bbmax) const 1.133 +{ 1.134 + if(!bbox_valid) { 1.135 + this->bbmin = Vector3(FLT_MAX, FLT_MAX, FLT_MAX); 1.136 + this->bbmax = -this->bbmin; 1.137 + 1.138 + for(size_t i=0; i<cv.size(); i++) { 1.139 + for(int j=0; j<3; j++) { 1.140 + if(cv[i][j] < this->bbmin[j]) { 1.141 + this->bbmin[j] = cv[i][j]; 1.142 + } 1.143 + if(cv[i][j] > this->bbmax[j]) { 1.144 + this->bbmax[j] = cv[i][j]; 1.145 + } 1.146 + } 1.147 + } 1.148 + bbox_valid = true; 1.149 + } 1.150 + 1.151 + if(bbmin) *bbmin = this->bbmin; 1.152 + if(bbmax) *bbmax = this->bbmax; 1.153 +} 1.154 + 1.155 +void Curve::normalize() 1.156 +{ 1.157 + get_bbox(0, 0); // force validation of the bounding box 1.158 + 1.159 + float len = (bbmax - bbmin).length() * 0.5; 1.160 + if(len == 0.0) { 1.161 + return; 1.162 + } 1.163 + 1.164 + for(size_t i=0; i<cv.size(); i++) { 1.165 + get_point(i) /= len; 1.166 + } 1.167 +} 1.168 + 1.169 +Vector3 Curve::get_pos(float t) const 1.170 +{ 1.171 + if(cv.empty()) { 1.172 + return Vector3(0, 0, 0); 1.173 + } 1.174 + if(cv.size() == 1 || t <= 0.0) { 1.175 + return cv[0]; 1.176 + } 1.177 + if(t >= 1.0) { 1.178 + return cv.back(); 1.179 + } 1.180 + 1.181 + t = reparametrize(t); 1.182 + 1.183 + int numcv = (int)cv.size(); 1.184 + int idx0 = t * (numcv - 1); 1.185 + int idx1 = idx0 + 1; 1.186 + 1.187 + int idx_prev = idx0 <= 0 ? idx0 : idx0 - 1; 1.188 + int idx_next = idx1 >= numcv - 1 ? idx1 : idx1 + 1; 1.189 + 1.190 + float dt = 1.0 / (float)(numcv - 1); 1.191 + 1.192 + float t0 = (float)idx0 * dt; 1.193 + float t1 = (float)idx1 * dt; 1.194 + 1.195 + t = (t - t0) / (t1 - t0); 1.196 + if(t < 0.0) t = 0.0; 1.197 + if(t > 1.0) t = 1.0; 1.198 + 1.199 + //return catmull_rom_spline(cv[idx_prev], cv[idx0], cv[idx1], cv[idx_next], t); 1.200 + return bspline(cv[idx_prev], cv[idx0], cv[idx1], cv[idx_next], t); 1.201 +} 1.202 + 1.203 +Vector3 Curve::operator() (float t) const 1.204 +{ 1.205 + return get_pos(t); 1.206 +} 1.207 + 1.208 +void Curve::draw() const 1.209 +{ 1.210 + update_mesh(); 1.211 + if(!mesh_valid) { 1.212 + return; 1.213 + } 1.214 + 1.215 + mesh.draw(); 1.216 +} 1.217 + 1.218 + 1.219 +float Curve::reparametrize(float t) const 1.220 +{ 1.221 + calc_cvlengths(); 1.222 + return t; // TODO 1.223 +} 1.224 + 1.225 +void Curve::calc_cvlengths() const 1.226 +{ 1.227 + if(lengths_valid || cv.empty()) { 1.228 + return; 1.229 + } 1.230 + 1.231 + length.clear(); 1.232 + length.resize(cv.size()); 1.233 + 1.234 + length[0] = 0; 1.235 + for(size_t i=1; i<cv.size(); i++) { 1.236 + length[i] = length[i - 1] + (cv[i] - cv[i - 1]).length(); 1.237 + } 1.238 + 1.239 + lengths_valid = true; 1.240 +} 1.241 + 1.242 +void Curve::update_mesh() const 1.243 +{ 1.244 + if(mesh_valid) return; 1.245 + 1.246 + if(cv.size() < 2) { 1.247 + return; 1.248 + } 1.249 + 1.250 + mesh.clear(); 1.251 + 1.252 + int nsub = segm_subdiv * (cv.size() - 1); 1.253 + int num_rings = nsub + 1; 1.254 + 1.255 + int num_verts = ring_subdiv * num_rings; 1.256 + int num_quads = ring_subdiv * nsub; 1.257 + int num_tri = num_quads * 2; 1.258 + int num_idx = num_tri * 3; 1.259 + 1.260 + float *varr = mesh.set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts); 1.261 + float *narr = mesh.set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts); 1.262 + float *tcarr = mesh.set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts); 1.263 + unsigned int *idxarr = mesh.set_index_data(num_idx); 1.264 + 1.265 + float t = 0.0; 1.266 + float dt = 1.0 / (float)(num_rings - 1); 1.267 + 1.268 + for(int i=0; i<num_rings; i++) { 1.269 + Vector3 p = get_pos(t); 1.270 + Vector3 dir = (get_pos(t + dt) - p).normalized(); 1.271 + 1.272 + Vector3 up = Vector3(0, 0, 1); 1.273 + float updotdir = dot_product(up, dir); 1.274 + if(1.0 - fabs(updotdir) < 1e-4) { 1.275 + up = Vector3(0, 1, 0); 1.276 + } 1.277 + Vector3 right = cross_product(up, dir).normalized(); 1.278 + up = cross_product(dir, right); 1.279 + 1.280 + for(int j=0; j<ring_subdiv; j++) { 1.281 + float u = (float)j / (float)ring_subdiv * M_PI * 2.0; 1.282 + Quaternion qrot(dir, u); 1.283 + Vector3 v = p + right.transformed(qrot) * thickness; 1.284 + 1.285 + *varr++ = v.x; 1.286 + *varr++ = v.y; 1.287 + *varr++ = v.z; 1.288 + 1.289 + Vector3 norm = (v - p).normalized(); 1.290 + *narr++ = norm.x; 1.291 + *narr++ = norm.y; 1.292 + *narr++ = norm.z; 1.293 + 1.294 + *tcarr++ = u; 1.295 + *tcarr++ = t; 1.296 + 1.297 + if(i < nsub) { 1.298 + int quad = i * ring_subdiv + j; 1.299 + 1.300 + int v0 = quad; 1.301 + int v1 = i * ring_subdiv + ((j + 1) % ring_subdiv); 1.302 + int v2 = (i + 1) * ring_subdiv + ((j + 1) % ring_subdiv); 1.303 + int v3 = (i + 1) * ring_subdiv + j; 1.304 + 1.305 + idxarr[quad * 6] = v0; 1.306 + idxarr[quad * 6 + 1] = v1; 1.307 + idxarr[quad * 6 + 2] = v2; 1.308 + 1.309 + idxarr[quad * 6 + 3] = v0; 1.310 + idxarr[quad * 6 + 4] = v2; 1.311 + idxarr[quad * 6 + 5] = v3; 1.312 + } 1.313 + } 1.314 + 1.315 + t += dt; 1.316 + } 1.317 + 1.318 + mesh_valid = true; 1.319 +}