Panda3D
Loading...
Searching...
No Matches
nurbsSurfaceResult.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file nurbsSurfaceResult.cxx
10 * @author drose
11 * @date 2003-10-10
12 */
13
14#include "nurbsSurfaceResult.h"
15#include "nurbsVertex.h"
16
17
18/**
19 * The constructor automatically builds up the result as the product of the
20 * indicated set of basis matrices and the indicated table of control vertex
21 * positions.
22 */
25 const NurbsBasisVector &v_basis,
26 const LVecBase4 vecs[], const NurbsVertex *verts,
27 int num_u_vertices, int num_v_vertices) :
28 _u_basis(u_basis),
29 _v_basis(v_basis),
30 _verts(verts),
31 _num_u_vertices(num_u_vertices),
32 _num_v_vertices(num_v_vertices)
33{
34 // The V basis matrices will always be transposed.
35 _v_basis.transpose();
36
37 _last_u_segment = -1;
38 _last_v_segment = -1;
39 int u_order = _u_basis.get_order();
40 int v_order = _v_basis.get_order();
41 int num_u_segments = _u_basis.get_num_segments();
42 int num_v_segments = _v_basis.get_num_segments();
43 int num_segments = num_u_segments * num_v_segments;
44
45 _composed.reserve(num_segments);
46 for (int i = 0; i < num_segments; i++) {
47 _composed.push_back(ComposedMats());
48 }
49
50 for (int vi = 0; vi < num_v_segments; vi++) {
51 const LMatrix4 &v_basis_transpose = _v_basis.get_basis(vi);
52
53 int vn = _v_basis.get_vertex_index(vi);
54 nassertv(vn >= 0 && vn + v_order - 1 < _num_v_vertices);
55
56 for (int ui = 0; ui < num_u_segments; ui++) {
57 const LMatrix4 &u_basis_mat = _u_basis.get_basis(ui);
58
59 int un = _u_basis.get_vertex_index(ui);
60 nassertv(un >= 0 && un + u_order - 1 < _num_u_vertices);
61
62 // Create four geometry matrices from our (up to) sixteen involved
63 // vertices.
64 LMatrix4 geom_x, geom_y, geom_z, geom_w;
65 geom_x.fill(0);
66 geom_y.fill(0);
67 geom_z.fill(0);
68 geom_w.fill(0);
69
70 for (int uni = 0; uni < 4; uni++) {
71 for (int vni = 0; vni < 4; vni++) {
72 if (uni < u_order && vni < v_order) {
73 const LVecBase4 &vec = vecs[verti(un + uni, vn + vni)];
74 geom_x(uni, vni) = vec[0];
75 geom_y(uni, vni) = vec[1];
76 geom_z(uni, vni) = vec[2];
77 geom_w(uni, vni) = vec[3];
78 }
79 }
80 }
81
82 // And compose these geometry matrices with the basis matrices to
83 // produce a new set of matrices, which will be used to evaluate the
84 // surface.
85 int i = segi(ui, vi);
86 nassertv(i >= 0 && i < (int)_composed.size());
87 ComposedMats &result = _composed[i];
88 result._x = u_basis_mat * geom_x * v_basis_transpose;
89 result._y = u_basis_mat * geom_y * v_basis_transpose;
90 result._z = u_basis_mat * geom_z * v_basis_transpose;
91 result._w = u_basis_mat * geom_w * v_basis_transpose;
92 }
93 }
94}
95
96/**
97 * Evaluates the point on the surface corresponding to the indicated value in
98 * parametric time within the indicated surface segment. u and v should be in
99 * the range [0, 1].
100 *
101 * The surface is internally represented as a number of connected (or possibly
102 * unconnected) piecewise continuous segments. The exact number of segments
103 * for a particular surface depends on the knot vector, and is returned by
104 * get_num_segments(). Normally, eval_point() is used to evaluate a point
105 * along the continuous surface, but when you care more about local
106 * continuity, you can use eval_segment_point() to evaluate the points along
107 * each segment.
108 */
110eval_segment_point(int ui, int vi, PN_stdfloat u, PN_stdfloat v, LVecBase3 &point) const {
111 int i = segi(ui, vi);
112 nassertv(i >= 0 && i < (int)_composed.size());
113
114 PN_stdfloat u2 = u*u;
115 LVecBase4 uvec(u*u2, u2, u, 1.0f);
116 PN_stdfloat v2 = v*v;
117 LVecBase4 vvec(v*v2, v2, v, 1.0f);
118
119 PN_stdfloat weight = vvec.dot(uvec * _composed[i]._w);
120
121 point.set(vvec.dot(uvec * _composed[i]._x) / weight,
122 vvec.dot(uvec * _composed[i]._y) / weight,
123 vvec.dot(uvec * _composed[i]._z) / weight);
124}
125
126/**
127 * As eval_segment_point, but computes the normal to the surface at the
128 * indicated point. The normal vector will not necessarily be normalized, and
129 * could be zero.
130 */
132eval_segment_normal(int ui, int vi, PN_stdfloat u, PN_stdfloat v, LVecBase3 &normal) const {
133 int i = segi(ui, vi);
134 nassertv(i >= 0 && i < (int)_composed.size());
135
136 PN_stdfloat u2 = u*u;
137 LVecBase4 uvec(u*u2, u2, u, 1.0f);
138 LVecBase4 duvec(3.0f * u2, 2.0f * u, 1.0f, 0.0f);
139 PN_stdfloat v2 = v*v;
140 LVecBase4 vvec(v*v2, v2, v, 1.0f);
141 LVecBase4 dvvec(3.0f * v2, 2.0f * v, 1.0f, 0.0f);
142
143 LVector3 utan(vvec.dot(duvec * _composed[i]._x),
144 vvec.dot(duvec * _composed[i]._y),
145 vvec.dot(duvec * _composed[i]._z));
146
147 LVector3 vtan(dvvec.dot(uvec * _composed[i]._x),
148 dvvec.dot(uvec * _composed[i]._y),
149 dvvec.dot(uvec * _composed[i]._z));
150
151 normal = utan.cross(vtan);
152}
153
154/**
155 * Evaluates the surface in n-dimensional space according to the extended
156 * vertices associated with the surface in the indicated dimension.
157 */
159eval_segment_extended_point(int ui, int vi, PN_stdfloat u, PN_stdfloat v, int d) const {
160 int i = segi(ui, vi);
161 nassertr(i >= 0 && i < (int)_composed.size(), 0.0f);
162
163 PN_stdfloat u2 = u*u;
164 LVecBase4 uvec(u*u2, u2, u, 1.0f);
165 PN_stdfloat v2 = v*v;
166 LVecBase4 vvec(v*v2, v2, v, 1.0f);
167
168 PN_stdfloat weight = vvec.dot(uvec * _composed[i]._w);
169
170 // Calculate the composition of the basis matrices and the geometry matrix
171 // on-the-fly.
172 const LMatrix4 &v_basis_transpose = _v_basis.get_basis(vi);
173 const LMatrix4 &u_basis_mat = _u_basis.get_basis(ui);
174 int u_order = _u_basis.get_order();
175 int v_order = _v_basis.get_order();
176
177 int un = _u_basis.get_vertex_index(ui);
178 int vn = _v_basis.get_vertex_index(vi);
179
180 LMatrix4 geom;
181 geom.fill(0);
182
183 for (int uni = 0; uni < 4; uni++) {
184 for (int vni = 0; vni < 4; vni++) {
185 if (uni < u_order && vni < v_order) {
186 geom(uni, vni) = _verts[verti(un + uni, vn + vni)].get_extended_vertex(d);
187 }
188 }
189 }
190
191 LMatrix4 composed = u_basis_mat * geom * v_basis_transpose;
192 return vvec.dot(uvec * composed) / weight;
193}
194
195/**
196 * Simultaneously performs eval_extended_point on a contiguous sequence of
197 * dimensions. The dimensions evaluated are d through (d + num_values - 1);
198 * the results are filled into the num_values elements in the indicated result
199 * array.
200 */
202eval_segment_extended_points(int ui, int vi, PN_stdfloat u, PN_stdfloat v, int d,
203 PN_stdfloat result[], int num_values) const {
204 int i = segi(ui, vi);
205 nassertv(i >= 0 && i < (int)_composed.size());
206
207 PN_stdfloat u2 = u*u;
208 LVecBase4 uvec(u*u2, u2, u, 1.0f);
209 PN_stdfloat v2 = v*v;
210 LVecBase4 vvec(v*v2, v2, v, 1.0f);
211
212 PN_stdfloat weight = vvec.dot(uvec * _composed[i]._w);
213
214 // Calculate the composition of the basis matrices and the geometry matrix
215 // on-the-fly.
216 const LMatrix4 &v_basis_transpose = _v_basis.get_basis(vi);
217 const LMatrix4 &u_basis_mat = _u_basis.get_basis(ui);
218 int u_order = _u_basis.get_order();
219 int v_order = _v_basis.get_order();
220
221 int un = _u_basis.get_vertex_index(ui);
222 int vn = _v_basis.get_vertex_index(vi);
223
224 for (int n = 0; n < num_values; n++) {
225 LMatrix4 geom;
226 geom.fill(0);
227
228 for (int uni = 0; uni < 4; uni++) {
229 for (int vni = 0; vni < 4; vni++) {
230 if (uni < u_order && vni < v_order) {
231 geom(uni, vni) =
232 _verts[verti(un + uni, vn + vni)].get_extended_vertex(d + n);
233 }
234 }
235 }
236
237 LMatrix4 composed = u_basis_mat * geom * v_basis_transpose;
238 result[n] = vvec.dot(uvec * composed) / weight;
239 }
240}
241
242/**
243 * Returns the index of the segment that contains the indicated value of t, or
244 * -1 if no segment contains this value.
245 */
246int NurbsSurfaceResult::
247find_u_segment(PN_stdfloat u) {
248 // Trivially check the endpoints of the surface.
249 if (u >= get_end_u()) {
250 return _u_basis.get_num_segments() - 1;
251 } else if (u <= get_start_u()) {
252 return 0;
253 }
254
255 // Check the last segment we searched for. Often, two consecutive requests
256 // are for the same segment.
257 if (_last_u_segment != -1 && (u >= _last_u_from && u < _last_u_to)) {
258 return _last_u_segment;
259 }
260
261 // Look for the segment the hard way.
262 int segment = r_find_u_segment(u, 0, _u_basis.get_num_segments() - 1);
263 if (segment != -1) {
264 _last_u_segment = segment;
265 _last_u_from = _u_basis.get_from(segment);
266 _last_u_to = _u_basis.get_to(segment);
267 }
268 return segment;
269}
270
271/**
272 * Recursively searches for the segment that contains the indicated value of t
273 * by performing a binary search. This assumes the segments are stored in
274 * increasing order of t, and they don't overlap.
275 */
276int NurbsSurfaceResult::
277r_find_u_segment(PN_stdfloat u, int top, int bot) const {
278 if (bot < top) {
279 // Not found.
280 return -1;
281 }
282 int mid = (top + bot) / 2;
283 nassertr(mid >= 0 && mid < _u_basis.get_num_segments(), -1);
284
285 PN_stdfloat from = _u_basis.get_from(mid);
286 PN_stdfloat to = _u_basis.get_to(mid);
287 if (from > u) {
288 // Too high, try lower.
289 return r_find_u_segment(u, top, mid - 1);
290
291 } else if (to <= u) {
292 // Too low, try higher.
293 return r_find_u_segment(u, mid + 1, bot);
294
295 } else {
296 // Here we are!
297 return mid;
298 }
299}
300
301
302/**
303 * Returns the index of the segment that contains the indicated value of t, or
304 * -1 if no segment contains this value.
305 */
306int NurbsSurfaceResult::
307find_v_segment(PN_stdfloat v) {
308 // Trivially check the endpoints of the surface.
309 if (v >= get_end_v()) {
310 return _v_basis.get_num_segments() - 1;
311 } else if (v <= get_start_v()) {
312 return 0;
313 }
314
315 // Check the last segment we searched for. Often, two consecutive requests
316 // are for the same segment.
317 if (_last_v_segment != -1 && (v >= _last_v_from && v < _last_v_to)) {
318 return _last_v_segment;
319 }
320
321 // Look for the segment the hard way.
322 int segment = r_find_v_segment(v, 0, _v_basis.get_num_segments() - 1);
323 if (segment != -1) {
324 _last_v_segment = segment;
325 _last_v_from = _v_basis.get_from(segment);
326 _last_v_to = _v_basis.get_to(segment);
327 }
328 return segment;
329}
330
331/**
332 * Recursively searches for the segment that contains the indicated value of t
333 * by performing a binary search. This assumes the segments are stored in
334 * increasing order of t, and they don't overlap.
335 */
336int NurbsSurfaceResult::
337r_find_v_segment(PN_stdfloat v, int top, int bot) const {
338 if (bot < top) {
339 // Not found.
340 return -1;
341 }
342 int mid = (top + bot) / 2;
343 nassertr(mid >= 0 && mid < _v_basis.get_num_segments(), -1);
344
345 PN_stdfloat from = _v_basis.get_from(mid);
346 PN_stdfloat to = _v_basis.get_to(mid);
347 if (from > v) {
348 // Too high, try lower.
349 return r_find_v_segment(v, top, mid - 1);
350
351 } else if (to <= v) {
352 // Too low, try higher.
353 return r_find_v_segment(v, mid + 1, bot);
354
355 } else {
356 // Here we are!
357 return mid;
358 }
359}
This encapsulates a series of matrices that are used to represent the sequential segments of a NurbsC...
void transpose()
Transposes the basis matrices stored in the vector.
int get_num_segments() const
Returns the number of piecewise continuous segments in the curve.
int get_order() const
Returns the order of the segments in the curve.
const LMatrix4 & get_basis(int segment) const
Returns the basis matrix associated with the nth segment.
PN_stdfloat get_to(int segment) const
Returns the t value of the end of this segment.
int get_vertex_index(int segment) const
Returns the vertex index of the nth segment.
PN_stdfloat get_from(int segment) const
Returns the t value of the beginning of this segment.
void eval_segment_point(int ui, int vi, PN_stdfloat u, PN_stdfloat v, LVecBase3 &point) const
Evaluates the point on the surface corresponding to the indicated value in parametric time within the...
PN_stdfloat get_start_u() const
Returns the first legal value of u on the surface.
PN_stdfloat eval_segment_extended_point(int ui, int vi, PN_stdfloat u, PN_stdfloat v, int d) const
Evaluates the surface in n-dimensional space according to the extended vertices associated with the s...
PN_stdfloat get_start_v() const
Returns the first legal value of v on the surface.
void eval_segment_normal(int ui, int vi, PN_stdfloat u, PN_stdfloat v, LVecBase3 &normal) const
As eval_segment_point, but computes the normal to the surface at the indicated point.
PN_stdfloat get_end_u() const
Returns the last legal value of u on the surface.
PN_stdfloat get_end_v() const
Returns the last legal value of v on the surface.
NurbsSurfaceResult(const NurbsBasisVector &u_basis, const NurbsBasisVector &v_basis, const LVecBase4 vecs[], const NurbsVertex *verts, int num_u_vertices, int num_v_vertices)
The constructor automatically builds up the result as the product of the indicated set of basis matri...
void eval_segment_extended_points(int ui, int vi, PN_stdfloat u, PN_stdfloat v, int d, PN_stdfloat result[], int num_values) const
Simultaneously performs eval_extended_point on a contiguous sequence of dimensions.
This represents a single control vertex in a NurbsEvaluator.
Definition nurbsVertex.h:32
PN_stdfloat get_extended_vertex(int d) const
Returns an n-dimensional vertex value.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.