rev |
line source |
nuclear@0
|
1 #include <float.h>
|
nuclear@0
|
2 #include <assert.h>
|
nuclear@0
|
3 #include "curve.h"
|
nuclear@0
|
4 #include "opengl.h"
|
nuclear@0
|
5 #include "shader.h"
|
nuclear@0
|
6 #include "logger.h"
|
nuclear@0
|
7
|
nuclear@15
|
8 using namespace goatgfx;
|
nuclear@15
|
9
|
nuclear@0
|
10 #define DEF_THICKNESS 0.075
|
nuclear@0
|
11 #define DEF_SEGM_SUB 3
|
nuclear@0
|
12 #define DEF_RING_SUB 6
|
nuclear@0
|
13
|
nuclear@0
|
14 Curve::Curve()
|
nuclear@0
|
15 {
|
nuclear@0
|
16 thickness = DEF_THICKNESS;
|
nuclear@0
|
17 mesh_valid = false;
|
nuclear@0
|
18 lengths_valid = false;
|
nuclear@0
|
19
|
nuclear@0
|
20 bbox_valid = false;
|
nuclear@0
|
21
|
nuclear@0
|
22 segm_subdiv = DEF_SEGM_SUB;
|
nuclear@0
|
23 ring_subdiv = DEF_RING_SUB;
|
nuclear@0
|
24 }
|
nuclear@0
|
25
|
nuclear@0
|
26 Curve::Curve(const Vector3 *points, int num_points)
|
nuclear@0
|
27 {
|
nuclear@0
|
28 thickness = DEF_THICKNESS;
|
nuclear@0
|
29 mesh_valid = false;
|
nuclear@0
|
30 lengths_valid = false;
|
nuclear@0
|
31
|
nuclear@0
|
32 bbox_valid = false;
|
nuclear@0
|
33
|
nuclear@0
|
34 segm_subdiv = DEF_SEGM_SUB;
|
nuclear@0
|
35 ring_subdiv = DEF_RING_SUB;
|
nuclear@0
|
36
|
nuclear@0
|
37 for(int i=0; i<num_points; i++) {
|
nuclear@0
|
38 add_point(points[i]);
|
nuclear@0
|
39 }
|
nuclear@0
|
40 }
|
nuclear@0
|
41
|
nuclear@0
|
42 Curve::Curve(const Vector2 *points, int num_points)
|
nuclear@0
|
43 {
|
nuclear@0
|
44 thickness = DEF_THICKNESS;
|
nuclear@0
|
45 mesh_valid = false;
|
nuclear@0
|
46 lengths_valid = false;
|
nuclear@0
|
47
|
nuclear@0
|
48 bbox_valid = false;
|
nuclear@0
|
49
|
nuclear@0
|
50 segm_subdiv = DEF_SEGM_SUB;
|
nuclear@0
|
51 ring_subdiv = DEF_RING_SUB;
|
nuclear@0
|
52
|
nuclear@0
|
53 for(int i=0; i<num_points; i++) {
|
nuclear@0
|
54 add_point(Vector3(points[i].x, points[i].y, 0.0));
|
nuclear@0
|
55 }
|
nuclear@0
|
56 }
|
nuclear@0
|
57
|
nuclear@0
|
58 void Curve::set_name(const char *name)
|
nuclear@0
|
59 {
|
nuclear@0
|
60 this->name = name;
|
nuclear@0
|
61 }
|
nuclear@0
|
62
|
nuclear@0
|
63 const char *Curve::get_name() const
|
nuclear@0
|
64 {
|
nuclear@0
|
65 return name.c_str();
|
nuclear@0
|
66 }
|
nuclear@0
|
67
|
nuclear@0
|
68 bool Curve::empty() const
|
nuclear@0
|
69 {
|
nuclear@0
|
70 return cv.empty();
|
nuclear@0
|
71 }
|
nuclear@0
|
72
|
nuclear@0
|
73 void Curve::set_thickness(float thickness)
|
nuclear@0
|
74 {
|
nuclear@0
|
75 this->thickness = thickness;
|
nuclear@0
|
76 }
|
nuclear@0
|
77
|
nuclear@0
|
78 void Curve::set_subdiv(int seg, int ring)
|
nuclear@0
|
79 {
|
nuclear@0
|
80 if(seg < 1) seg = 1;
|
nuclear@0
|
81 if(ring < 3) ring = 3;
|
nuclear@0
|
82
|
nuclear@0
|
83 segm_subdiv = seg;
|
nuclear@0
|
84 ring_subdiv = ring;
|
nuclear@0
|
85 }
|
nuclear@0
|
86
|
nuclear@0
|
87 void Curve::clear()
|
nuclear@0
|
88 {
|
nuclear@0
|
89 mesh_valid = false;
|
nuclear@0
|
90 lengths_valid = false;
|
nuclear@0
|
91 bbox_valid = false;
|
nuclear@0
|
92 cv.clear();
|
nuclear@0
|
93 }
|
nuclear@0
|
94
|
nuclear@0
|
95 void Curve::add_point(const Vector3 &pt)
|
nuclear@0
|
96 {
|
nuclear@0
|
97 cv.push_back(pt);
|
nuclear@0
|
98 mesh_valid = false;
|
nuclear@0
|
99 lengths_valid = false;
|
nuclear@0
|
100 bbox_valid = false;
|
nuclear@0
|
101 }
|
nuclear@0
|
102
|
nuclear@0
|
103 Vector3 &Curve::get_point(int idx)
|
nuclear@0
|
104 {
|
nuclear@0
|
105 mesh_valid = false;
|
nuclear@0
|
106 lengths_valid = false;
|
nuclear@0
|
107 bbox_valid = false;
|
nuclear@0
|
108 return cv[idx];
|
nuclear@0
|
109 }
|
nuclear@0
|
110
|
nuclear@0
|
111 const Vector3 &Curve::get_point(int idx) const
|
nuclear@0
|
112 {
|
nuclear@0
|
113 return cv[idx];
|
nuclear@0
|
114 }
|
nuclear@0
|
115
|
nuclear@0
|
116 int Curve::get_count() const
|
nuclear@0
|
117 {
|
nuclear@0
|
118 return (int)cv.size();
|
nuclear@0
|
119 }
|
nuclear@0
|
120
|
nuclear@0
|
121 Vector3 &Curve::operator[] (int idx)
|
nuclear@0
|
122 {
|
nuclear@0
|
123 return get_point(idx);
|
nuclear@0
|
124 }
|
nuclear@0
|
125
|
nuclear@0
|
126 const Vector3 &Curve::operator[] (int idx) const
|
nuclear@0
|
127 {
|
nuclear@0
|
128 return get_point(idx);
|
nuclear@0
|
129 }
|
nuclear@0
|
130
|
nuclear@0
|
131 void Curve::get_bbox(Vector3 *bbmin, Vector3 *bbmax) const
|
nuclear@0
|
132 {
|
nuclear@0
|
133 if(!bbox_valid) {
|
nuclear@0
|
134 this->bbmin = Vector3(FLT_MAX, FLT_MAX, FLT_MAX);
|
nuclear@0
|
135 this->bbmax = -this->bbmin;
|
nuclear@0
|
136
|
nuclear@0
|
137 for(size_t i=0; i<cv.size(); i++) {
|
nuclear@0
|
138 for(int j=0; j<3; j++) {
|
nuclear@0
|
139 if(cv[i][j] < this->bbmin[j]) {
|
nuclear@0
|
140 this->bbmin[j] = cv[i][j];
|
nuclear@0
|
141 }
|
nuclear@0
|
142 if(cv[i][j] > this->bbmax[j]) {
|
nuclear@0
|
143 this->bbmax[j] = cv[i][j];
|
nuclear@0
|
144 }
|
nuclear@0
|
145 }
|
nuclear@0
|
146 }
|
nuclear@0
|
147 bbox_valid = true;
|
nuclear@0
|
148 }
|
nuclear@0
|
149
|
nuclear@0
|
150 if(bbmin) *bbmin = this->bbmin;
|
nuclear@0
|
151 if(bbmax) *bbmax = this->bbmax;
|
nuclear@0
|
152 }
|
nuclear@0
|
153
|
nuclear@0
|
154 void Curve::normalize()
|
nuclear@0
|
155 {
|
nuclear@0
|
156 get_bbox(0, 0); // force validation of the bounding box
|
nuclear@0
|
157
|
nuclear@0
|
158 float len = (bbmax - bbmin).length() * 0.5;
|
nuclear@0
|
159 if(len == 0.0) {
|
nuclear@0
|
160 return;
|
nuclear@0
|
161 }
|
nuclear@0
|
162
|
nuclear@0
|
163 for(size_t i=0; i<cv.size(); i++) {
|
nuclear@0
|
164 get_point(i) /= len;
|
nuclear@0
|
165 }
|
nuclear@0
|
166 }
|
nuclear@0
|
167
|
nuclear@0
|
168 Vector3 Curve::get_pos(float t) const
|
nuclear@0
|
169 {
|
nuclear@0
|
170 if(cv.empty()) {
|
nuclear@0
|
171 return Vector3(0, 0, 0);
|
nuclear@0
|
172 }
|
nuclear@0
|
173 if(cv.size() == 1 || t <= 0.0) {
|
nuclear@0
|
174 return cv[0];
|
nuclear@0
|
175 }
|
nuclear@0
|
176 if(t >= 1.0) {
|
nuclear@0
|
177 return cv.back();
|
nuclear@0
|
178 }
|
nuclear@0
|
179
|
nuclear@0
|
180 t = reparametrize(t);
|
nuclear@0
|
181
|
nuclear@0
|
182 int numcv = (int)cv.size();
|
nuclear@0
|
183 int idx0 = t * (numcv - 1);
|
nuclear@0
|
184 int idx1 = idx0 + 1;
|
nuclear@0
|
185
|
nuclear@0
|
186 int idx_prev = idx0 <= 0 ? idx0 : idx0 - 1;
|
nuclear@0
|
187 int idx_next = idx1 >= numcv - 1 ? idx1 : idx1 + 1;
|
nuclear@0
|
188
|
nuclear@0
|
189 float dt = 1.0 / (float)(numcv - 1);
|
nuclear@0
|
190
|
nuclear@0
|
191 float t0 = (float)idx0 * dt;
|
nuclear@0
|
192 float t1 = (float)idx1 * dt;
|
nuclear@0
|
193
|
nuclear@0
|
194 t = (t - t0) / (t1 - t0);
|
nuclear@0
|
195 if(t < 0.0) t = 0.0;
|
nuclear@0
|
196 if(t > 1.0) t = 1.0;
|
nuclear@0
|
197
|
nuclear@0
|
198 //return catmull_rom_spline(cv[idx_prev], cv[idx0], cv[idx1], cv[idx_next], t);
|
nuclear@0
|
199 return bspline(cv[idx_prev], cv[idx0], cv[idx1], cv[idx_next], t);
|
nuclear@0
|
200 }
|
nuclear@0
|
201
|
nuclear@0
|
202 Vector3 Curve::operator() (float t) const
|
nuclear@0
|
203 {
|
nuclear@0
|
204 return get_pos(t);
|
nuclear@0
|
205 }
|
nuclear@0
|
206
|
nuclear@0
|
207 void Curve::draw() const
|
nuclear@0
|
208 {
|
nuclear@0
|
209 update_mesh();
|
nuclear@0
|
210 if(!mesh_valid) {
|
nuclear@0
|
211 return;
|
nuclear@0
|
212 }
|
nuclear@0
|
213
|
nuclear@0
|
214 mesh.draw();
|
nuclear@0
|
215 }
|
nuclear@0
|
216
|
nuclear@0
|
217
|
nuclear@0
|
218 float Curve::reparametrize(float t) const
|
nuclear@0
|
219 {
|
nuclear@0
|
220 calc_cvlengths();
|
nuclear@0
|
221 return t; // TODO
|
nuclear@0
|
222 }
|
nuclear@0
|
223
|
nuclear@0
|
224 void Curve::calc_cvlengths() const
|
nuclear@0
|
225 {
|
nuclear@0
|
226 if(lengths_valid || cv.empty()) {
|
nuclear@0
|
227 return;
|
nuclear@0
|
228 }
|
nuclear@0
|
229
|
nuclear@0
|
230 length.clear();
|
nuclear@0
|
231 length.resize(cv.size());
|
nuclear@0
|
232
|
nuclear@0
|
233 length[0] = 0;
|
nuclear@0
|
234 for(size_t i=1; i<cv.size(); i++) {
|
nuclear@0
|
235 length[i] = length[i - 1] + (cv[i] - cv[i - 1]).length();
|
nuclear@0
|
236 }
|
nuclear@0
|
237
|
nuclear@0
|
238 lengths_valid = true;
|
nuclear@0
|
239 }
|
nuclear@0
|
240
|
nuclear@0
|
241 void Curve::update_mesh() const
|
nuclear@0
|
242 {
|
nuclear@0
|
243 if(mesh_valid) return;
|
nuclear@0
|
244
|
nuclear@0
|
245 if(cv.size() < 2) {
|
nuclear@0
|
246 return;
|
nuclear@0
|
247 }
|
nuclear@0
|
248
|
nuclear@0
|
249 mesh.clear();
|
nuclear@0
|
250
|
nuclear@0
|
251 int nsub = segm_subdiv * (cv.size() - 1);
|
nuclear@0
|
252 int num_rings = nsub + 1;
|
nuclear@0
|
253
|
nuclear@0
|
254 int num_verts = ring_subdiv * num_rings;
|
nuclear@0
|
255 int num_quads = ring_subdiv * nsub;
|
nuclear@0
|
256 int num_tri = num_quads * 2;
|
nuclear@0
|
257 int num_idx = num_tri * 3;
|
nuclear@0
|
258
|
nuclear@0
|
259 float *varr = mesh.set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts);
|
nuclear@0
|
260 float *narr = mesh.set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts);
|
nuclear@0
|
261 float *tcarr = mesh.set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts);
|
nuclear@0
|
262 unsigned int *idxarr = mesh.set_index_data(num_idx);
|
nuclear@0
|
263
|
nuclear@0
|
264 float t = 0.0;
|
nuclear@0
|
265 float dt = 1.0 / (float)(num_rings - 1);
|
nuclear@0
|
266
|
nuclear@0
|
267 for(int i=0; i<num_rings; i++) {
|
nuclear@0
|
268 Vector3 p = get_pos(t);
|
nuclear@0
|
269 Vector3 dir = (get_pos(t + dt) - p).normalized();
|
nuclear@0
|
270
|
nuclear@0
|
271 Vector3 up = Vector3(0, 0, 1);
|
nuclear@0
|
272 float updotdir = dot_product(up, dir);
|
nuclear@0
|
273 if(1.0 - fabs(updotdir) < 1e-4) {
|
nuclear@0
|
274 up = Vector3(0, 1, 0);
|
nuclear@0
|
275 }
|
nuclear@0
|
276 Vector3 right = cross_product(up, dir).normalized();
|
nuclear@0
|
277 up = cross_product(dir, right);
|
nuclear@0
|
278
|
nuclear@0
|
279 for(int j=0; j<ring_subdiv; j++) {
|
nuclear@0
|
280 float u = (float)j / (float)ring_subdiv * M_PI * 2.0;
|
nuclear@0
|
281 Quaternion qrot(dir, u);
|
nuclear@0
|
282 Vector3 v = p + right.transformed(qrot) * thickness;
|
nuclear@0
|
283
|
nuclear@0
|
284 *varr++ = v.x;
|
nuclear@0
|
285 *varr++ = v.y;
|
nuclear@0
|
286 *varr++ = v.z;
|
nuclear@0
|
287
|
nuclear@0
|
288 Vector3 norm = (v - p).normalized();
|
nuclear@0
|
289 *narr++ = norm.x;
|
nuclear@0
|
290 *narr++ = norm.y;
|
nuclear@0
|
291 *narr++ = norm.z;
|
nuclear@0
|
292
|
nuclear@0
|
293 *tcarr++ = u;
|
nuclear@0
|
294 *tcarr++ = t;
|
nuclear@0
|
295
|
nuclear@0
|
296 if(i < nsub) {
|
nuclear@0
|
297 int quad = i * ring_subdiv + j;
|
nuclear@0
|
298
|
nuclear@0
|
299 int v0 = quad;
|
nuclear@0
|
300 int v1 = i * ring_subdiv + ((j + 1) % ring_subdiv);
|
nuclear@0
|
301 int v2 = (i + 1) * ring_subdiv + ((j + 1) % ring_subdiv);
|
nuclear@0
|
302 int v3 = (i + 1) * ring_subdiv + j;
|
nuclear@0
|
303
|
nuclear@0
|
304 idxarr[quad * 6] = v0;
|
nuclear@0
|
305 idxarr[quad * 6 + 1] = v1;
|
nuclear@0
|
306 idxarr[quad * 6 + 2] = v2;
|
nuclear@0
|
307
|
nuclear@0
|
308 idxarr[quad * 6 + 3] = v0;
|
nuclear@0
|
309 idxarr[quad * 6 + 4] = v2;
|
nuclear@0
|
310 idxarr[quad * 6 + 5] = v3;
|
nuclear@0
|
311 }
|
nuclear@0
|
312 }
|
nuclear@0
|
313
|
nuclear@0
|
314 t += dt;
|
nuclear@0
|
315 }
|
nuclear@0
|
316
|
nuclear@0
|
317 mesh_valid = true;
|
nuclear@0
|
318 }
|