Panda3D
cubicCurveseg.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 cubicCurveseg.cxx
10  * @author drose
11  * @date 2001-03-04
12  */
13 
14 #include "piecewiseCurve.h"
15 
16 #include "config_parametrics.h"
17 #include "hermiteCurve.h"
18 
19 #include "datagram.h"
20 #include "datagramIterator.h"
21 #include "bamWriter.h"
22 #include "bamReader.h"
23 
24 TypeHandle CubicCurveseg::_type_handle;
25 
26 /**
27  *
28  */
29 CubicCurveseg::
30 CubicCurveseg() {
31 }
32 
33 /**
34  * Creates the curveseg given the four basis vectors (the columns of the
35  * matrix) explicitly.
36  */
37 CubicCurveseg::
38 CubicCurveseg(const LMatrix4 &basis) {
39  Bx = basis.get_col(0);
40  By = basis.get_col(1);
41  Bz = basis.get_col(2);
42  Bw = basis.get_col(3);
43  rational = true;
44 }
45 
46 
47 /**
48  * Creates the curveseg as a Bezier segment.
49  */
50 CubicCurveseg::
51 CubicCurveseg(const BezierSeg &seg) {
52  bezier_basis(seg);
53 }
54 
55 
56 /**
57  * Creates the curveseg as a NURBS segment. See nurbs_basis for a description
58  * of the parameters.
59  */
60 CubicCurveseg::
61 CubicCurveseg(int order, const PN_stdfloat knots[], const LVecBase4 cvs[]) {
62  nurbs_basis(order, knots, cvs);
63 }
64 
65 /**
66  *
67  */
68 CubicCurveseg::
69 ~CubicCurveseg() {
70 }
71 
72 
73 
74 /**
75  * Computes the surface point at a given parametric point t.
76  */
78 get_point(PN_stdfloat t, LVecBase3 &point) const {
79  PN_stdfloat t_sqrd = t*t;
80  evaluate_point(LVecBase4(t*t_sqrd, t_sqrd, t, 1.0f), point);
81  return true;
82 }
83 
84 /**
85  * Computes the surface tangent at a given parametric point t.
86  */
88 get_tangent(PN_stdfloat t, LVecBase3 &tangent) const {
89  evaluate_vector(LVecBase4(3.0f*t*t, 2.0f*t, 1.0f, 0.0f), tangent);
90  return true;
91 }
92 
93 /**
94  * Simultaneously computes the point and the tangent at the given parametric
95  * point.
96  */
98 get_pt(PN_stdfloat t, LVecBase3 &point, LVecBase3 &tangent) const {
99  PN_stdfloat t_sqrd=t*t;
100  evaluate_point(LVecBase4(t*t_sqrd, t_sqrd, t, 1.0f), point);
101  evaluate_vector(LVecBase4(3.0f*t_sqrd, /*2.0f*t*/t+t, 1.0f, 0.0f), tangent);
102  return true;
103 }
104 
105 /**
106  * Computes the surface 2nd-order tangent at a given parametric point t.
107  */
109 get_2ndtangent(PN_stdfloat t, LVecBase3 &tangent2) const {
110  evaluate_vector(LVecBase4(6.0f*t, 2.0f, 0.0f, 0.0f), tangent2);
111  return true;
112 }
113 
114 
115 /**
116  * Defines the curve segment as a Hermite. This only sets up the basis
117  * vectors, so the curve will be computed correctly; it does not retain the
118  * CV's.
119  */
121 hermite_basis(const HermiteCurveCV &cv0,
122  const HermiteCurveCV &cv1,
123  PN_stdfloat tlength) {
124  static LMatrix4
125  Mh( 2.0f, -3.0f, 0.0f, 1.0f,
126  -2.0f, 3.0f, 0.0f, 0.0f,
127  1.0f, -2.0f, 1.0f, 0.0f,
128  1.0f, -1.0f, 0.0f, 0.0f);
129 
130  LVecBase4 Gx(cv0._p[0], cv1._p[0],
131  cv0._out[0]*tlength, cv1._in[0]*tlength);
132  LVecBase4 Gy(cv0._p[1], cv1._p[1],
133  cv0._out[1]*tlength, cv1._in[1]*tlength);
134  LVecBase4 Gz(cv0._p[2], cv1._p[2],
135  cv0._out[2]*tlength, cv1._in[2]*tlength);
136 
137  Bx = Gx * Mh;
138  By = Gy * Mh;
139  Bz = Gz * Mh;
140  rational = false;
141 }
142 
143 /**
144  * Defines the curve segment as a Bezier. This only sets up the basis
145  * vectors, so the curve will be computed correctly; it does not retain the
146  * CV's.
147  */
149 bezier_basis(const BezierSeg &seg) {
150  static LMatrix4
151  Mb(-1.0f, 3.0f, -3.0f, 1.0f,
152  3.0f, -6.0f, 3.0f, 0.0f,
153  -3.0f, 3.0f, 0.0f, 0.0f,
154  1.0f, 0.0f, 0.0f, 0.0f);
155 
156  LVecBase4 Gx(seg._v[0][0], seg._v[1][0], seg._v[2][0], seg._v[3][0]);
157  LVecBase4 Gy(seg._v[0][1], seg._v[1][1], seg._v[2][1], seg._v[3][1]);
158  LVecBase4 Gz(seg._v[0][2], seg._v[1][2], seg._v[2][2], seg._v[3][2]);
159 
160  Bx = Gx * Mb;
161  By = Gy * Mb;
162  Bz = Gz * Mb;
163  rational = false;
164 }
165 
166 static LVecBase4
167 nurbs_blending_function(int order, int i, int j,
168  const PN_stdfloat knots[]) {
169  // This is doubly recursive. Ick.
170  LVecBase4 r;
171 
172  if (j==1) {
173  if (i==order-1 && knots[i] < knots[i+1]) {
174  r.set(0.0f, 0.0f, 0.0f, 1.0f);
175  } else {
176  r.set(0.0f, 0.0f, 0.0f, 0.0f);
177  }
178 
179  } else {
180  LVecBase4 bi0 = nurbs_blending_function(order, i, j-1, knots);
181  LVecBase4 bi1 = nurbs_blending_function(order, i+1, j-1, knots);
182 
183  PN_stdfloat d0 = knots[i+j-1] - knots[i];
184  PN_stdfloat d1 = knots[i+j] - knots[i+1];
185 
186  // First term. Division by zero is defined to equal zero.
187  if (d0 != 0.0f) {
188  if (d1 != 0.0f) {
189  r = bi0 / d0 - bi1 / d1;
190  } else {
191  r = bi0 / d0;
192  }
193 
194  } else if (d1 != 0.0f) {
195  r = - bi1 / d1;
196 
197  } else {
198  r.set(0.0f, 0.0f, 0.0f, 0.0f);
199  }
200 
201  // scale by t.
202  r[0] = r[1];
203  r[1] = r[2];
204  r[2] = r[3];
205  r[3] = 0.0f;
206 
207  // Second term.
208  if (d0 != 0.0f) {
209  if (d1 != 0.0f) {
210  r += bi0 * (- knots[i] / d0) + bi1 * (knots[i+j] / d1);
211  } else {
212  r += bi0 * (- knots[i] / d0);
213  }
214 
215  } else if (d1 != 0.0f) {
216  r += bi1 * (knots[i+j] / d1);
217  }
218  }
219 
220  return r;
221 }
222 
223 void
224 compute_nurbs_basis(int order,
225  const PN_stdfloat knots_in[],
226  LMatrix4 &basis) {
227  int i;
228 
229  // Scale the supplied knots to the range 0..1.
230  PN_stdfloat knots[8];
231  PN_stdfloat mink = knots_in[order-1];
232  PN_stdfloat maxk = knots_in[order];
233 
234  if (mink==maxk) {
235  // Huh. What were you thinking? This is a trivial NURBS.
236  parametrics_cat->warning()
237  << "Trivial NURBS curve specified." << std::endl;
238  memset((void *)&basis, 0, sizeof(LMatrix4));
239  return;
240  }
241 
242  for (i = 0; i<2*order; i++) {
243  knots[i] = (knots_in[i] - mink) / (maxk-mink);
244  }
245 
246 
247  LVecBase4 b[4];
248  for (i = 0; i<order; i++) {
249  b[i] = nurbs_blending_function(order, i, order, knots);
250  }
251 
252  for (i = 0; i<order; i++) {
253  basis.set_row(i, b[i]);
254  }
255 
256  for (i=order; i<4; i++) {
257  basis.set_row(i, LVecBase4::zero());
258  }
259 }
260 
261 
262 
263 /**
264  * Defines the curve segment as a NURBS. Order is one more than the degree,
265  * and must be 1, 2, 3, or 4; knots is an array of order*2 values, and cvs is
266  * an array of order values.
267  */
269 nurbs_basis(int order, const PN_stdfloat knots[], const LVecBase4 cvs[]) {
270  assert(order>=1 && order<=4);
271 
272  LMatrix4 B;
273  compute_nurbs_basis(order, knots, B);
274 
275  // Create a local copy of our CV's, so we can zero out the unused elements.
276  LVecBase4 c[4];
277  for (int i = 0; i < 4; i++) {
278  c[i] = (i<order) ? cvs[i] : LVecBase4(0.0f, 0.0f, 0.0f, 0.0f);
279  }
280 
281  Bx = LVecBase4(c[0][0], c[1][0], c[2][0], c[3][0]) * B;
282  By = LVecBase4(c[0][1], c[1][1], c[2][1], c[3][1]) * B;
283  Bz = LVecBase4(c[0][2], c[1][2], c[2][2], c[3][2]) * B;
284  Bw = LVecBase4(c[0][3], c[1][3], c[2][3], c[3][3]) * B;
285 
286  rational = true;
287 }
288 
289 /**
290  * Fills the BezierSeg structure with a description of the curve segment as a
291  * Bezier, if possible, but does not change the _t member of the structure.
292  * Returns true if successful, false otherwise.
293  */
295 get_bezier_seg(BezierSeg &seg) const {
296  static LMatrix4
297  Mbi(0.0f, 0.0f, 0.0f, 1.0f,
298  0.0f, 0.0f, 1.0f/3.0f, 1.0f,
299  0.0f, 1.0f/3.0f, 2.0f/3.0f, 1.0f,
300  1.0f, 1.0f, 1.0f, 1.0f);
301 
302  LVecBase4 Gx = Bx * Mbi;
303  LVecBase4 Gy = By * Mbi;
304  LVecBase4 Gz = Bz * Mbi;
305 
306  if (rational) {
307  LVecBase4 Gw = Bw * Mbi;
308  seg._v[0].set(Gx[0]/Gw[0], Gy[0]/Gw[0], Gz[0]/Gw[0]);
309  seg._v[1].set(Gx[1]/Gw[1], Gy[1]/Gw[1], Gz[1]/Gw[1]);
310  seg._v[2].set(Gx[2]/Gw[2], Gy[2]/Gw[2], Gz[2]/Gw[2]);
311  seg._v[3].set(Gx[3]/Gw[3], Gy[3]/Gw[3], Gz[3]/Gw[3]);
312  } else {
313  seg._v[0].set(Gx[0], Gy[0], Gz[0]);
314  seg._v[1].set(Gx[1], Gy[1], Gz[1]);
315  seg._v[2].set(Gx[2], Gy[2], Gz[2]);
316  seg._v[3].set(Gx[3], Gy[3], Gz[3]);
317  }
318 
319  return true;
320 }
321 
322 // We need this operator since Performer didn't supply it.
323 inline LVecBase4
324 col_mult(const LMatrix4 &M, const LVecBase4 &v) {
325  return LVecBase4(M(0,0)*v[0] + M(0,1)*v[1] + M(0,2)*v[2] + M(0,3)*v[3],
326  M(1,0)*v[0] + M(1,1)*v[1] + M(1,2)*v[2] + M(1,3)*v[3],
327  M(2,0)*v[0] + M(2,1)*v[1] + M(2,2)*v[2] + M(2,3)*v[3],
328  M(3,0)*v[0] + M(3,1)*v[1] + M(3,2)*v[2] + M(3,3)*v[3]);
329 }
330 
331 /**
332  * Interprets the parameters for a particular column of compute_seg. Builds
333  * the indicated column of T and P.
334  */
335 static bool
336 compute_seg_col(int c,
337  int rtype, PN_stdfloat t, const LVecBase4 &v,
338  const LMatrix4 &B,
339  const LMatrix4 &Bi,
340  const LMatrix4 &G,
341  const LMatrix4 &GB,
342  LMatrix4 &T, LMatrix4 &P) {
343  bool keep_orig = ((rtype & RT_KEEP_ORIG) != 0);
344 
345  if (parametrics_cat.is_debug()) {
346  parametrics_cat.debug()
347  << "Computing col " << c << " type " << (rtype & RT_BASE_TYPE)
348  << " at " << t << " keep_orig = " << keep_orig
349  << " v = " << v << "\n";
350  }
351 
352  switch (rtype & RT_BASE_TYPE) {
353  // RT_point defines the point on the curve at t. This is the vector [ t^3
354  // t^2 t^1 t^0 ].
355  PN_stdfloat t_sqrd,t_cubed;
356 
357  case RT_POINT:
358  t_sqrd = t*t;
359  t_cubed = t_sqrd*t;
360  T.set_col(c, LVecBase4(t_cubed, t_sqrd, t, 1.0f));
361  if (keep_orig) {
362  LVecBase4 vec(t_cubed, t_sqrd, t, 1.0f);
363  LVecBase4 ov = col_mult(GB, vec);
364  if (parametrics_cat.is_debug()) {
365  parametrics_cat.debug()
366  << "orig point = " << ov << "\n";
367  }
368  P.set_col(c, ov);
369  } else {
370  P.set_col(c, v);
371  }
372  break;
373 
374  // RT_tangent defines the tangent to the curve at t. This is the vector [
375  // 3t^2 2t 1 0 ].
376  case RT_TANGENT:
377  t_sqrd = t*t;
378  T.set_col(c, LVecBase4(3.0f*t_sqrd, t+t, 1.0f, 0.0f));
379  if (keep_orig) {
380  LVecBase4 vec(3.0f*t_sqrd, /*2.0f*t*/t+t, 1.0f, 0.0f);
381  LVecBase4 ov = col_mult(GB, vec);
382  if (parametrics_cat.is_debug()) {
383  parametrics_cat.debug()
384  << "Matrix is:\n";
385  GB.write(parametrics_cat.debug(false), 2);
386  parametrics_cat.debug(false)
387  << "vector is " << vec << "\n"
388  << "orig tangent = " << ov << "\n";
389  }
390  P.set_col(c, ov);
391  } else {
392  P.set_col(c, v);
393  }
394  break;
395 
396  // RT_cv defines the cth control point. This is the cth column vector
397  // from Bi.
398  case RT_CV:
399  T.set_col(c, Bi.get_col(c));
400  if (keep_orig) {
401  if (parametrics_cat.is_debug()) {
402  parametrics_cat.debug()
403  << "orig CV = " << G.get_col(c) << "\n";
404  }
405  P.set_col(c, G.get_col(c));
406  } else {
407  P.set_col(c, v);
408  }
409  break;
410 
411  default:
412  std::cerr << "Invalid rebuild type in compute_seg\n";
413  return false;
414  }
415 
416  return true;
417 }
418 
419 /**
420  * Given a set of four properties of a curve segment (e.g. four points, four
421  * tangent values, four control points, or any combination), and a basis
422  * matrix, computes the corresponding geometry matrix that (together with the
423  * basis matrix) represents the curve that satisfies the four properties.
424  *
425  * The basis matrix is passed in as B, and its inverse must be precomputed and
426  * passed in as Bi.
427  *
428  * The result is returned in the matrix G, each column of which represents the
429  * cth control vertex. If any of the four properties has RT_KEEP_ORIG set
430  * (see below), G's input value is used to define the original shape of the
431  * curve; otherwise, G's input value is ignored.
432  *
433  * Each property is defined by an rtype, which may be any of RT_POINT,
434  * RT_TANGENT, or RT_CV, and may or may not be or'ed with RT_KEEP_ORIG. The
435  * meanings of the types are as follows:
436  *
437  * RT_POINT defines a specific point which the curve segment must pass
438  * through. t is in the range [0,1] and represents the parametric value at
439  * which the curve segment will intersect the given point. If RT_KEEP_ORIG is
440  * not set, v defines the point; otherwise, v is ignored and the original
441  * curve at point t defines the point.
442  *
443  * RT_TANGENT defines a specific tangent value which the curve segment must
444  * have at point t. As with RT_POINT, if RT_KEEP_ORIG is not set, v defines
445  * the tangent; otherwise, v is ignored and the original curve defines the
446  * tangent.
447  *
448  * RT_CV defines a specific control vertex which the curve segment must have.
449  * In this case, t is ignored. The position within the argument list
450  * determines which control vertex is applicable; e.g. rtype0 = RT_CV defines
451  * control vertex 0, and rtype2 = RT_CV defines control vertex 2. If
452  * RT_KEEP_ORIG is not set, v defines the new control vertex; otherwise, the
453  * control vertex is taken from G.
454  *
455  * The return value is true if all the parameters are sensible, or false if
456  * there is some error.
457  */
459 compute_seg(int rtype0, PN_stdfloat t0, const LVecBase4 &v0,
460  int rtype1, PN_stdfloat t1, const LVecBase4 &v1,
461  int rtype2, PN_stdfloat t2, const LVecBase4 &v2,
462  int rtype3, PN_stdfloat t3, const LVecBase4 &v3,
463  const LMatrix4 &B,
464  const LMatrix4 &Bi,
465  LMatrix4 &G) {
466 
467  // We can define a cubic curve segment given four arbitrary properties of
468  // the segment: any point along the curve, any tangent along the curve, any
469  // control point. Given any four such properties, a single cubic curve
470  // segment is defined.
471 
472  // For a given cubic curve segment so defined, and given a basis matrix B,
473  // we can define the four control vertices that represent the segment with
474  // the basis matrix. That is, we can define the matrix G such that G * B *
475  // tc, where tc is [ t^3 t^2 t^1 t^0 ] for t in [ 0..1 ], represents the
476  // point on the curve segment corresponding to t.
477 
478  // First, we build a matrix T, such that each of the four columns of T
479  // contains the vector that would compute the corresponding property. We
480  // also build a corresponding matrix P, such that each of its columns
481  // contains the vector that is the solution of the corresponding column in
482  // T.
483 
484  LMatrix4 T, P, GB;
485 
486  // GB is G * B, but we only need to compute this if any of the columns wants
487  // the value from the original G.
488  if ((rtype0 | rtype1 | rtype2 | rtype3) & RT_KEEP_ORIG) {
489  GB = G * B;
490  }
491 
492  if (! (compute_seg_col(0, rtype0, t0, v0, B, Bi, G, GB, T, P) &&
493  compute_seg_col(1, rtype1, t1, v1, B, Bi, G, GB, T, P) &&
494  compute_seg_col(2, rtype2, t2, v2, B, Bi, G, GB, T, P) &&
495  compute_seg_col(3, rtype3, t3, v3, B, Bi, G, GB, T, P))) {
496  return false;
497  }
498 
499  LMatrix4 Ti;
500  Ti = invert(T);
501 
502  // Now we have T and P such that P represents the solution of T, when T is
503  // applied to the geometry and basis matrices. That is, each column of P
504  // represents the solution computed by the corresponding column of T. P = G
505  // * B * T.
506 
507  // We simply solve for G and get G = P * T^(-1) * B^(-1).
508 
509  G = P * Ti * Bi;
510 
511  return true;
512 }
513 
514 /**
515  * Initializes the factory for reading these things from Bam files.
516  */
519  BamReader::get_factory()->register_factory(get_class_type(), make_CubicCurveseg);
520 }
521 
522 /**
523  * Factory method to generate an object of this type.
524  */
525 TypedWritable *CubicCurveseg::
526 make_CubicCurveseg(const FactoryParams &params) {
527  CubicCurveseg *me = new CubicCurveseg;
528  DatagramIterator scan;
529  BamReader *manager;
530 
531  parse_params(params, scan, manager);
532  me->fillin(scan, manager);
533  return me;
534 }
535 
536 /**
537  * Function to write the important information in the particular object to a
538  * Datagram
539  */
540 void CubicCurveseg::
541 write_datagram(BamWriter *manager, Datagram &me) {
542  ParametricCurve::write_datagram(manager, me);
543 
544  Bx.write_datagram(me);
545  By.write_datagram(me);
546  Bz.write_datagram(me);
547  Bw.write_datagram(me);
548  me.add_bool(rational);
549 }
550 
551 /**
552  * Function that reads out of the datagram (or asks manager to read) all of
553  * the data that is needed to re-create this object and stores it in the
554  * appropiate place
555  */
556 void CubicCurveseg::
557 fillin(DatagramIterator &scan, BamReader *manager) {
558  ParametricCurve::fillin(scan, manager);
559 
560  Bx.read_datagram(scan);
561  By.read_datagram(scan);
562  Bz.read_datagram(scan);
563  Bw.read_datagram(scan);
564  rational = scan.get_bool();
565 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
A CubicCurveseg is any curve that can be completely described by four 4-valued basis vectors,...
Definition: cubicCurveseg.h:50
void bezier_basis(const BezierSeg &seg)
Defines the curve segment as a Bezier.
virtual bool get_point(PN_stdfloat t, LVecBase3 &point) const
Computes the surface point at a given parametric point t.
virtual bool get_pt(PN_stdfloat t, LVecBase3 &point, LVecBase3 &tangent) const
Simultaneously computes the point and the tangent at the given parametric point.
virtual bool get_tangent(PN_stdfloat t, LVecBase3 &tangent) const
Computes the surface tangent at a given parametric point t.
void hermite_basis(const HermiteCurveCV &cv0, const HermiteCurveCV &cv1, PN_stdfloat tlength=1.0f)
Defines the curve segment as a Hermite.
static void register_with_read_factory()
Initializes the factory for reading these things from Bam files.
virtual bool get_bezier_seg(BezierSeg &seg) const
Fills the BezierSeg structure with a description of the curve segment as a Bezier,...
void nurbs_basis(int order, const PN_stdfloat knots[], const LVecBase4 cvs[])
Defines the curve segment as a NURBS.
static bool compute_seg(int rtype0, PN_stdfloat t0, const LVecBase4 &v0, int rtype1, PN_stdfloat t1, const LVecBase4 &v1, int rtype2, PN_stdfloat t2, const LVecBase4 &v2, int rtype3, PN_stdfloat t3, const LVecBase4 &v3, const LMatrix4 &B, const LMatrix4 &Bi, LMatrix4 &G)
Given a set of four properties of a curve segment (e.g.
virtual bool get_2ndtangent(PN_stdfloat t, LVecBase3 &tangent2) const
Computes the surface 2nd-order tangent at a given parametric point t.
A class to retrieve the individual data elements previously stored in a Datagram.
bool get_bool()
Extracts a boolean value.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
A single CV of a Hermite curve.
Definition: hermiteCurve.h:50
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.