Panda3D
Loading...
Searching...
No Matches
eggMesherStrip.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 eggMesherStrip.cxx
10 * @author drose
11 * @date 2005-03-13
12 */
13
14#include "eggMesherStrip.h"
15#include "eggMesherEdge.h"
16#include "eggPrimitive.h"
17#include "eggTriangleFan.h"
18#include "eggTriangleStrip.h"
19#include "eggPolygon.h"
20#include "dcast.h"
21#include "config_egg.h"
22
23/**
24 *
25 */
26EggMesherStrip::
27EggMesherStrip(PrimType prim_type, MesherOrigin origin) {
28 _origin = origin;
29 _type = prim_type;
30
31 _index = -1;
32 _row_id = 0;
33 _status = MS_alive;
34 _planar = false;
35 _flat_shaded = false;
36}
37
38/**
39 *
40 */
41EggMesherStrip::
42EggMesherStrip(const EggPrimitive *prim, int index,
43 const EggVertexPool *vertex_pool,
44 bool flat_shaded) {
45 _index = index;
46 _row_id = 0;
47 _status = MS_alive;
48 _origin = MO_unknown;
49 _flat_shaded = flat_shaded;
50
51 _type = PT_poly; //prim.get_type();
52
53 // We care only about the prim's attributes in the _prims array. The
54 // vertices get re-added later by EggMesher::add_prim().
55 _prims.push_back(prim);
56
57 if (_type == PT_poly) {
58 switch (prim->size()) {
59 case 3:
60 _type = PT_tri;
61 break;
62
63 case 4:
64 _type = PT_quad;
65 break;
66 }
67 }
68
69 if (_type == PT_quad) {
70 // A quad has two internal triangles; we therefore push the prim
71 // attributes twice.
72 _prims.push_back(prim);
73 }
74
75 _planar = false;
76
77 if (prim->is_of_type(EggPolygon::get_class_type())) {
78 // Although for the most part we ignore the actual value of the vertices,
79 // we will ask the polygon for its plane equation (i.e. its normal).
80 LNormald normal;
81 if (DCAST(EggPolygon, prim)->calculate_normal(normal)) {
82 _plane_normal = normal;
83 _planar = true;
84 LPoint3d p1 = prim->get_vertex(0)->get_pos3();
85 _plane_offset = -dot(_plane_normal, p1);
86 }
87 }
88}
89
90
91/**
92 * Creates an EggPrimitive corresponding to the strip represented by this
93 * node.
94 */
95PT(EggPrimitive) EggMesherStrip::
96make_prim(const EggVertexPool *vertex_pool) {
97 PT(EggPrimitive) prim;
98 PrimType dest_type;
99
100 switch (_type) {
101 case PT_quad:
102 case PT_tristrip:
103 case PT_quadstrip:
104 dest_type = PT_tristrip;
105 break;
106
107 case PT_trifan:
108 dest_type = PT_trifan;
109 break;
110
111 default:
112 dest_type = _type;
113 }
114
115 if (dest_type != PT_tristrip && dest_type != PT_trifan) {
116 // The easy case: a simple primitive, i.e. a polygon.
117 prim = new EggPolygon;
118 prim->copy_attributes(*_prims.front());
119
120 Verts::iterator vi;
121 for (vi = _verts.begin(); vi != _verts.end(); ++vi) {
122 prim->add_vertex(vertex_pool->get_vertex(*vi));
123 }
124
125 } else {
126 // The harder case: a tristrip of some kind.
127 convert_to_type(dest_type);
128 if (dest_type == PT_trifan) {
129 prim = new EggTriangleFan;
130 } else {
131 prim = new EggTriangleStrip;
132 }
133 prim->copy_attributes(*_prims.front());
134
135 // Now store all the vertices. Each individual triangle's attributes, if
136 // any, get applied to the third vertex of each triangle.
137 Verts::iterator vi;
138 Prims::iterator pi;
139 pi = _prims.begin();
140 int count = 0;
141 for (vi = _verts.begin();
142 vi != _verts.end() && pi != _prims.end();
143 ++vi) {
144 PT(EggVertex) vertex = vertex_pool->get_vertex(*vi);
145 prim->add_vertex(vertex);
146
147 ++count;
148 if (count >= 3) {
149 // Beginning with the third vertex, we increment pi. Thus, the first
150 // two vertices stand alone, then each vertex beginning with the third
151 // completes a triangle.
152 const EggAttributes *attrib = (*pi);
153 ++pi;
154 DCAST(EggCompositePrimitive, prim)->set_component(count - 3, attrib);
155 }
156 }
157
158 // If either of these fail, there weren't num_prims + 2 vertices in the
159 // tristrip!
160 nassertr(vi == _verts.end(), prim);
161 nassertr(pi == _prims.end(), prim);
162 }
163
164 return prim;
165}
166
167/**
168 * Determines the extents of the quadsheet that can be derived by starting
169 * with this strip, and searching in the direction indicated by the given
170 * edge.
171 */
173measure_sheet(const EggMesherEdge *edge, int new_row, int &num_prims,
174 int &num_rows, int first_row_id, int this_row_id,
175 int this_row_distance) {
176 if (new_row) {
177 // If we would create a new row by stepping here, we won't stay if there
178 // was any other row already defined here.
179 if (_row_id >= first_row_id) {
180 return;
181 }
182 } else {
183 // On the other hand, if this is a continuation of the current row, we'll
184 // stay if the other row had to travel farther to get here.
185 if (_row_id >= first_row_id && _row_distance <= this_row_distance) {
186 return;
187 }
188 }
189
190 num_prims += _prims.size();
191 if (new_row) {
192 ++num_rows;
193 this_row_id = first_row_id + num_rows - 1;
194 }
195
196 _row_id = this_row_id;
197
198 Edges::iterator ei;
199 EggMesherEdge::Strips::iterator si;
200
201 if (_type == PT_quad) {
202 // If this is a quad, it has four neighbors: two in the direction we are
203 // testing, and two in an orthagonal direction.
204
205 int vi_a = edge->_vi_a;
206 int vi_b = edge->_vi_b;
207
208 // We use these vertices to differentiate the edges that run in our
209 // primary direction from those in the secondary direction. For each
210 // edge, we count the number of vertices that the edge shares with our
211 // starting edge. There are then three cases:
212
213 // (a) The edge shares two vertices. It is the direction we came from;
214 // forget it.
215
216 // (b) The edge shares one vertex. It is at right angles to our starting
217 // edge. This is the primary direction if new_row is true, and the
218 // secondary direction if new_row is false.
219
220 // (c) The edge shares no vertices. It is directly opposite our starting
221 // edge. This is the primary direction if new_row is false, and the
222 // secondary direction if new_row is true.
223
224
225 // Here's a silly little for loop that executes the following code twice:
226 // once with secondary == 0, and once with secondary == 1. This is because
227 // we want to find all the primary edges first, and then all the secondary
228 // edges.
229
230 for (int secondary = 0; secondary <= 1; secondary++) {
231 // How many common vertices are we looking for this pass (see above)?
232
233 int want_count;
234 if (secondary) {
235 want_count = new_row ? 0 : 1;
236 } else {
237 want_count = new_row ? 1 : 0;
238 }
239
240 for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
241 int common_verts =
242 ((*ei)->_vi_a == vi_a || (*ei)->_vi_a == vi_b) +
243 ((*ei)->_vi_b == vi_a || (*ei)->_vi_b == vi_b);
244
245 if (common_verts == want_count) {
246 // Here's the edge. Look at all its connections. Hopefully, there
247 // will only be one besides ourselves, but there may be more. Pick
248 // the best.
249
250 EggMesherEdge::Strips &strips = (*ei)->_strips;
251 EggMesherStrip *mate = nullptr;
252 for (si = strips.begin(); si != strips.end(); ++si) {
253 if ((*si)->_row_id < first_row_id) {
254 if (mate == nullptr || pick_sheet_mate(**si, *mate)) {
255 mate = *si;
256 }
257 }
258 }
259 if (mate!=nullptr) {
260 mate->measure_sheet(*ei, secondary, num_prims, num_rows,
261 first_row_id, this_row_id,
262 this_row_distance + secondary);
263 }
264 }
265 }
266 }
267
268 } else {
269 // Otherwise, this is not a quad. It's certainly not a triangle, because
270 // we've built all the single triangles already.
271 nassertv(_type != PT_tri);
272
273 // Therefore, it must be a tristrip or quadstrip.
274 nassertv(_type == PT_tristrip || _type == PT_quadstrip);
275
276 // Since it's a strip, it only has two neighbors: the one we came from,
277 // and the other one. Find the other one.
278
279 for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
280 if (!(*ei)->matches(*edge)) {
281 // Here's the edge. Same drill as above.
282
283 EggMesherEdge::Strips &strips = (*ei)->_strips;
284 EggMesherStrip *mate = nullptr;
285 for (si = strips.begin(); si != strips.end(); ++si) {
286 if ((*si)->_row_id < first_row_id) {
287 if (mate == nullptr || pick_sheet_mate(**si, *mate)) {
288 mate = *si;
289 }
290 }
291 }
292 if (mate != nullptr) {
293 mate->measure_sheet(*ei, false, num_prims, num_rows,
294 first_row_id, this_row_id, this_row_distance);
295 }
296 }
297 }
298 }
299}
300
301
302/**
303 *
304 */
305void EggMesherStrip::
306cut_sheet(int first_row_id, int do_mate, const EggVertexPool *vertex_pool) {
307 Edges::iterator ei;
308 EggMesherEdge::Strips::iterator si;
309
310 // First, start the process going on any neighbors that belong to a later
311 // row. (We must do these first, because we'll change our neighbor list
312 // when we start to mate.)
313
314 // We need to build a temporary list of neighbors first, because calling
315 // cut_sheet() recursively will start things mating, and could damage our
316 // edge list.
317
318 typedef plist<EggMesherStrip *> StripPtrs;
319 StripPtrs strip_ptrs;
320
321 for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
322 EggMesherEdge::Strips &strips = (*ei)->_strips;
323 for (si = strips.begin(); si != strips.end(); ++si) {
324 if ((*si)->_row_id > _row_id) {
325 // Here's a different row in the sheet!
326 strip_ptrs.push_back(*si);
327 }
328 }
329 }
330
331 // Now walk the temporary list and do some damage. We pass do_mate = true
332 // to each of these neighbors, because as far as we know, they're the first
333 // nodes of a particular row.
334 StripPtrs::iterator spi;
335 for (spi = strip_ptrs.begin(); spi != strip_ptrs.end(); ++spi) {
336 if ((*spi)->_status == MS_alive) {
337 (*spi)->cut_sheet(first_row_id, true, vertex_pool);
338 }
339 }
340
341
342 if (do_mate && _status == MS_alive) {
343 // Now mate like a bunny until we don't have any more eligible mates.
344 int not_any;
345 do {
346 not_any = true;
347
348 ei = _edges.begin();
349 while (not_any && ei != _edges.end()) {
350 EggMesherEdge::Strips &strips = (*ei)->_strips;
351 si = strips.begin();
352 while (not_any && si != strips.end()) {
353 if (*si != this && (*si)->_row_id == _row_id) {
354 // Here's one!
355 not_any = false;
356 EggMesherStrip *mate = *si;
357
358 // We also recurse on these guys so they can spread the word to
359 // their own neighbors. This time we don't need to build a
360 // temporary list, because we'll be restarting from the beginning
361 // of our edge list after we do this. We also pass do_mate =
362 // false to these guys because we're the ones doing the mating
363 // here.
364 mate->cut_sheet(first_row_id, false, vertex_pool);
365
366 if (_status == MS_alive && mate->_status == MS_alive) {
367 // Now mate. This will either succeed or fail. It ought to
368 // succeed, but if it doesn't, no harm done; it will simply
369 // remove the common edge and return. We'll go around again and
370 // not encounter this neighbor next time.
371 mate_pieces(*ei, *this, *mate, vertex_pool);
372 }
373 }
374 if (not_any) {
375 ++si;
376 }
377 }
378 if (not_any) {
379 ++ei;
380 }
381 }
382 } while (!not_any);
383
384 // All done. Mark this one as down for the count.
385 _row_id = -first_row_id;
386 }
387}
388
389
390
391
392/**
393 * Finds a neighboring strip and joins up with it to make a larger strip.
394 * Returns true if mating was successful or at least possible, false if the
395 * strip has no neighbors.
396 */
398mate(const EggVertexPool *vertex_pool) {
399 // We must walk through the list of our neighbors and choose our best mate.
400 nassertr(_status == MS_alive, false);
401
403 EggMesherEdge *common_edge;
404
405 if (!find_ideal_mate(mate, common_edge, vertex_pool)) {
406 // We have no more eligible neighbors. Call us done.
407 _status = MS_done;
408
409 return false;
410 }
411
412 nassertr(!mate->_prims.empty(), false);
413 nassertr(!mate->_verts.empty(), false);
414
415 mate_pieces(common_edge, *this, *mate, vertex_pool);
416
417 // Whether the mate failed or not, the strip still (probably) has other
418 // neighbors to consider. Return true regardless.
419 return true;
420}
421
422/**
423 * Searches our neighbors for the most suitable mate. Returns true if one is
424 * found, false if we have no neighbors.
425 */
427find_ideal_mate(EggMesherStrip *&mate, EggMesherEdge *&common_edge,
428 const EggVertexPool *vertex_pool) {
429 Edges::iterator ei;
430
431 mate = nullptr;
432 common_edge = nullptr;
433
434 for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
435 EggMesherEdge::Strips &strips = (*ei)->_strips;
436 EggMesherEdge::Strips::iterator si;
437 for (si = strips.begin(); si != strips.end(); ++si) {
438 if (*si != this) {
439 if (mate==nullptr || pick_mate(**si, *mate, **ei, *common_edge,
440 vertex_pool)) {
441 mate = *si;
442 common_edge = *ei;
443 }
444 }
445 }
446 }
447
448 return (mate!=nullptr);
449}
450
451
452
453
454/**
455 * Connects two pieces of arbitrary type, if possible. Returns true if
456 * successful, false if failure.
457 */
459mate_pieces(EggMesherEdge *common_edge, EggMesherStrip &front,
460 EggMesherStrip &back, const EggVertexPool *vertex_pool) {
461 nassertr(front._status == MS_alive, false);
462 nassertr(back._status == MS_alive, false);
463 nassertr(&front != &back, false);
464
465 bool success = true;
466 // remove_sides tracks whether we want to remove all but the leading edges
467 // of the newly joined piece if we succeed.
468 bool remove_sides = true;
469
470 bool is_coplanar = front.is_coplanar_with(back, egg_coplanar_threshold);
471
472 if (front._type == PT_tri && back._type == PT_tri) {
473
474 if (is_coplanar && egg_retesselate_coplanar &&
475 front._prims.front() == back._prims.front() &&
476 convex_quad(common_edge, front, back, vertex_pool)) {
477
478 // If we're joining two equivalent coplanar triangles, call it a quad.
479 front._type = PT_quad;
480
481 // We add one additional vertex for the new triangle, the one vertex we
482 // didn't already share.
483 int new_vert = back.find_uncommon_vertex(common_edge);
484
485 // Now we just need to find the right place to insert it. It belongs in
486 // the middle of the common edge, i.e. after the first vertex that is
487 // on the common edge and before the second vertex.
488 Verts::iterator a = front._verts.begin();
489 Verts::iterator b = a;
490 ++b;
491
492 if (common_edge->contains_vertex(*a)) {
493 if (common_edge->contains_vertex(*b)) {
494 // It goes between a and b.
495 front._verts.insert(b, new_vert);
496 } else {
497 // It goes at the end.
498 front._verts.push_back(new_vert);
499 }
500 } else {
501 // It goes between b and c.
502 ++b;
503 front._verts.insert(b, new_vert);
504 }
505
506 front._prims.splice(front._prims.end(), back._prims);
507 back._verts.clear();
508
509 // We leave all four surrounding edges for now, since the quad might
510 // still be joined up in any direction.
511 remove_sides = false;
512
513 } else {
514 // Otherwise, connect the two tris into a tristrip.
515 front._type = PT_tristrip;
516
517 int new_vert = back.find_uncommon_vertex(common_edge);
518 front.rotate_to_back(common_edge);
519
520 front._verts.push_back(new_vert);
521 front._prims.splice(front._prims.end(), back._prims);
522 back._verts.clear();
523 }
524
525 } else if ((front._type == PT_quad || front._type == PT_quadstrip) &&
526 (back._type == PT_quad || back._type == PT_quadstrip)) {
527 // Joining two quads, two quadstrips, or a quad and a quadstrip. This
528 // makes another quadstrip.
529
530 // We expect this to succeed every time with quadstrips.
531 success = mate_strips(common_edge, front, back, PT_quadstrip);
532
533 if (!success) {
534 // Although it might fail in rare circumstances (specifically, if the
535 // two strips we attempted to join were backfacing to each other). If
536 // so, remove the adjoining edge so these two don't get tried again.
537 common_edge->remove(&front);
538 common_edge->remove(&back);
539 }
540
541 } else {
542
543 // Otherwise. This might be two tristrips, a quad and a tristrip, a
544 // triangle and a quad, a triangle and a tristrip, a triangle and a
545 // quadstrip, or a tristrip and a quadstrip. In any case, we'll end up
546 // with a tristrip.
547
548 // This might fail if the tristrips don't match polarity.
549 success = mate_strips(common_edge, front, back, PT_tristrip);
550
551 if (!success) {
552 // If it does fail, we'll try reversing the connection. This makes
553 // sense if we are joining a tri or tristrip to a quad or quadstrip,
554 // which might fail in one direction but succeed in the other.
555 success = mate_strips(common_edge, back, front, PT_tristrip);
556
557 if (success) {
558 // Yay! Now return all the stuff to front.
559 front._verts.splice(front._verts.end(), back._verts);
560 front._prims.splice(front._prims.end(), back._prims);
561
562 } else {
563 // A miserable failure. Never try to join these two again.
564 common_edge->remove(&front);
565 common_edge->remove(&back);
566 }
567 }
568 }
569
570 if (success) {
571 front.combine_edges(back, remove_sides);
572 if (!remove_sides) {
573 // If we didn't want to remove the side edges, at least remove the join
574 // edge, which is now internal.
575 common_edge->remove(&front);
576 }
577
578 nassertr(back._prims.empty(), false);
579 nassertr(back._verts.empty(), false);
580
581 // Strip back is no more.
582 back._status = MS_dead;
583
584 // The result is planar if and only if we joined two coplanar pieces.
585 front._planar = is_coplanar;
586 front._origin = MO_mate;
587 }
588
589 return success;
590}
591
592/**
593 * Stitches two strips together, producing in "front" a new strip of the
594 * indicated type (quadstrip or tristrip). The front strip stores the result,
595 * and the back strip is emptied on success.
596 *
597 * Returns true if successful, false if failure (generally because of
598 * incorrect polarity of tristrips), in which case nothing has changed (or at
599 * least, not much).
600 */
602mate_strips(EggMesherEdge *common_edge, EggMesherStrip &front,
603 EggMesherStrip &back, EggMesherStrip::PrimType type) {
604 // We don't allow making odd-length strips at all. Odd-length strips can't
605 // be rotated if they're flat-shaded, and they can't be joined end-to-end
606 // using degenerate triangles. So forget 'em.
607
608 // This might not be the right place to impose this rule, because it tends
609 // to end up with lots of independent triangles in certain kinds of meshes,
610 // but it's the easiest place to impose it.
611 if ((front._type != PT_tri && back._type == PT_tri) ||
612 (front._type == PT_tri && back._type != PT_tri) ||
613 (front._type == PT_tristrip && back._type == PT_tristrip &&
614 ((front._verts.size() + back._verts.size()) & 1) != 0)) {
615 return false;
616 }
617
618 // If we start with a quad or tri, rotate the vertices around so we start
619 // with the common edge.
620 if (front._type == PT_tri || front._type == PT_quad) {
621 front.rotate_to_back(common_edge);
622 }
623 if (back._type == PT_tri || back._type == PT_quad) {
624 back.rotate_to_front(common_edge);
625 }
626
627 bool reverse_front = common_edge->matches(front.get_head_edge());
628 bool reverse_back = !common_edge->matches(back.get_head_edge());
629
630 bool invert_front = false;
631 bool invert_back = false;
632
633 if (reverse_front && front.is_odd()) {
634 // If we're going to reverse the front strip, we have to be careful. This
635 // will also reverse the facing direction if it has an odd number of
636 // prims.
637 if (!front.can_invert()) {
638 return false;
639 }
640 invert_front = true;
641 }
642
643 if (must_invert(front, back, reverse_back, type)) {
644 if (!back.can_invert()) {
645 return false;
646 }
647 invert_back = true;
648 back.invert();
649 }
650
651 if (invert_front) {
652 front.invert();
653 }
654
655 if (reverse_front) {
656 reverse(front._verts.begin(), front._verts.end());
657 reverse(front._prims.begin(), front._prims.end());
658 }
659
660 if (reverse_back) {
661 reverse(back._verts.begin(), back._verts.end());
662 reverse(back._prims.begin(), back._prims.end());
663 }
664
665 bool will_reverse = front.would_reverse_tail(type);
666 bool is_headtotail = (front.get_tail_edge() == back.get_head_edge());
667 if (will_reverse == is_headtotail) {
668 // Oops, we tried to join two backfacing strips. This really shouldn't
669 // happen, but it occasionally does for some mysterious reason. Maybe one
670 // day I'll understand why. In the meantime, just recover and carry on.
671 if (reverse_back) {
672 reverse(back._verts.begin(), back._verts.end());
673 reverse(back._prims.begin(), back._prims.end());
674 }
675 if (invert_back) {
676 back.invert();
677 }
678 if (reverse_front) {
679 reverse(front._verts.begin(), front._verts.end());
680 reverse(front._prims.begin(), front._prims.end());
681 }
682 if (invert_front) {
683 front.invert();
684 }
685 return false;
686 }
687
688 front.convert_to_type(type);
689 back.convert_to_type(type);
690
691 /*
692 if (! (front.get_tail_edge() == back.get_head_edge()) ) {
693 builder_cat.error()
694 << "\nFailure, trying to connect " << front
695 << "\nto " << back
696 << "\nreverse_front = " << reverse_front
697 << " reverse_back = " << reverse_back
698 << " invert_front = " << invert_front
699 << "\n";
700 Edges::iterator ei;
701
702 nout << "\nFront edges:\n";
703 for (ei = front._edges.begin(); ei != front._edges.end(); ++ei) {
704 nout << **ei << "\n";
705 }
706
707 nout << "\nBack edges:\n";
708 for (ei = back._edges.begin(); ei != back._edges.end(); ++ei) {
709 nout << **ei << "\n";
710 }
711 }
712 */
713
714 // If this assertion fails, we were misinformed about our ability to join
715 // these two strips. Either the must_invert() call returned the incorrect
716 // value, or our edge-detection logic failed and we attempted to join two
717 // oppositely-facing strips. nassertr(front.get_tail_edge() ==
718 // back.get_head_edge(), false);
719
720 front._verts.pop_back();
721 front._verts.pop_back();
722 front._verts.splice(front._verts.end(), back._verts);
723 front._prims.splice(front._prims.end(), back._prims);
724
725 return true;
726}
727
728/**
729 * Returns false if the strips can be mated as they currently are. Returns
730 * true if the back strip must be inverted first.
731 */
733must_invert(const EggMesherStrip &front, const EggMesherStrip &back,
734 bool will_reverse_back, EggMesherStrip::PrimType type) {
735 bool invert = false;
736
737 if ((front._type == PT_quad || front._type == PT_quadstrip) &&
738 type == PT_tristrip) {
739 // If we'll be converting from quads to tris, the tail edge of the front
740 // strip will always be even.
741
742 } else if (front.is_odd()) {
743 // Otherwise, we have to flip if the tail edge is odd.
744 invert = !invert;
745 }
746
747 if (will_reverse_back) {
748 // With the back strip, we don't care about what will happen to its tail
749 // edge when we convert it, but we do care what happens to its front edge
750 // if we reverse it.
751 if (back.is_odd()) {
752 // Specifically, the front edge will be reversed when the strip is
753 // reversed only if the strip is odd.
754 invert = !invert;
755 }
756 }
757
758 return invert;
759}
760
761/**
762 * Returns true if the quad that would be formed by connecting coplanar tris
763 * front and back along common_edge is convex, false otherwise.
764 */
766convex_quad(EggMesherEdge *common_edge, EggMesherStrip &front,
767 EggMesherStrip &back, const EggVertexPool *vertex_pool) {
768 // Find the edge from the apex of one triangle to the apex of the other.
769 // This is the "other" diagonal of the quad-to-be, other than the
770 // common_edge.
771 int vi_a = front.find_uncommon_vertex(common_edge);
772 int vi_b = back.find_uncommon_vertex(common_edge);
773 nassertr(vi_a >= 0 && vi_b >= 0, false);
774
775 LPoint3d a3, b3, c3, d3;
776 a3 = vertex_pool->get_vertex(vi_a)->get_pos3();
777 b3 = vertex_pool->get_vertex(vi_b)->get_pos3();
778
779 c3 = vertex_pool->get_vertex(common_edge->_vi_a)->get_pos3();
780 d3 = vertex_pool->get_vertex(common_edge->_vi_b)->get_pos3();
781
782 // Project both edges into the 2-d axis plane most nearly perpendicular to
783 // the normal. We're assuming both tris have the same normal.
784
785 nassertr(front._planar, false);
786
787 LNormald n(front._plane_normal);
788 int xi, yi;
789
790 // Find the largest dimension of the normal.
791 if (fabs(n[0]) > fabs(n[1])) {
792 if (fabs(n[0]) > fabs(n[2])) {
793 xi = 1;
794 yi = 2;
795 } else {
796 xi = 0;
797 yi = 1;
798 }
799 } else {
800 if (fabs(n[1]) > fabs(n[2])) {
801 xi = 0;
802 yi = 2;
803 } else {
804 xi = 0;
805 yi = 1;
806 }
807 }
808
809 LVecBase2d a2, b2, c2, d2;
810 a2.set(a3[xi], a3[yi]);
811 b2.set(b3[xi], b3[yi]);
812 c2.set(c3[xi], c3[yi]);
813 d2.set(d3[xi], d3[yi]);
814
815 // Now (c2-d2) is the common edge, and (a2-b2) is the new edge. The quad is
816 // convex iff (c2-d2) intersects (a2-b2). We actually only need to test
817 // whether (c2-d2) intersects the infinite line passing through (a2-b2).
818
819 // The equation for the infinite line containing (a2-b2): Ax + By + C = 0
820 double A = (b2[1] - a2[1]);
821 double B = (a2[0] - b2[0]);
822 double C = -(A*b2[0] + B*b2[1]);
823
824 // The parametric equations for the line segment (c2-d2): x = c2[0] +
825 // (d2[0]-c2[0])t y = c2[1] + (d2[1]-c2[1])t
826
827 // Solved for t:
828 double t = - ((A*c2[0] + B*c2[1]) + C) / (A*(d2[0]-c2[0]) + B*(d2[1]-c2[1]));
829
830 // Now the lines intersect if t is in [0, 1].
831 return (0.0 <= t && t <= 1.0);
832}
833
834/**
835 * Returns the number of neighbors the strip shares.
836 */
838count_neighbors() const {
839 int count = 0;
840 Edges::const_iterator ei;
841
842 for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
843 count += (*ei)->_strips.size();
844 }
845 return count;
846}
847
848/**
849 * Writes all the neighbor indexes to the ostream.
850 */
852output_neighbors(std::ostream &out) const {
853 Edges::const_iterator ei;
854 EggMesherEdge::Strips::const_iterator si;
855
856 for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
857 for (si = (*ei)->_strips.begin();
858 si != (*ei)->_strips.end();
859 ++si) {
860 out << " " << (*si)->_index;
861 }
862 }
863}
864
865/**
866 * Returns the first vertex found that is not shared by the given edge.
867 */
869find_uncommon_vertex(const EggMesherEdge *edge) const {
870 int vi_a = edge->_vi_a;
871 int vi_b = edge->_vi_b;
872
873 Edges::const_iterator ei;
874 for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
875 EggMesherEdge *e = (*ei);
876
877 if (e->_vi_a != vi_a && e->_vi_a != vi_b) {
878 return e->_vi_a;
879 } else if (e->_vi_b != vi_a && e->_vi_b != vi_b) {
880 return e->_vi_b;
881 }
882 }
883
884 return -1;
885}
886
887/**
888 * Returns the first edge found that does not contain the given vertex. In a
889 * tri, this will be the edge opposite the given vertex.
890 */
892find_opposite_edge(int vi) const {
893 Edges::const_iterator ei;
894 for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
895 EggMesherEdge *e = (*ei);
896 if (!e->contains_vertex(vi)) {
897 return e;
898 }
899 }
900
901 return nullptr;
902}
903
904/**
905 * Returns the first edge found that shares no vertices with the given edge.
906 * In a quad, this will be the edge opposite the given edge.
907 */
909find_opposite_edge(const EggMesherEdge *edge) const {
910 int vi_a = edge->_vi_a;
911 int vi_b = edge->_vi_b;
912
913 Edges::const_iterator ei;
914 for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
915 EggMesherEdge *e = (*ei);
916 if (!e->contains_vertex(vi_a) && !e->contains_vertex(vi_b)) {
917 return e;
918 }
919 }
920
921 return nullptr;
922}
923
924/**
925 * Returns the first edge found that shares exactly one vertex with the given
926 * edge. In a quad, this will be one of two edges adjacent to the given edge.
927 */
929find_adjacent_edge(const EggMesherEdge *edge) const {
930 int vi_a = edge->_vi_a;
931 int vi_b = edge->_vi_b;
932
933 Edges::const_iterator ei;
934 for (ei = _edges.begin(); ei != _edges.end(); ++ei) {
935 EggMesherEdge *e = (*ei);
936 if (e->contains_vertex(vi_a) != e->contains_vertex(vi_b)) {
937 return e;
938 }
939 }
940
941 return nullptr;
942}
943
944/**
945 * Rotates a triangle or quad so that the given edge is first in the vertex
946 * list.
947 */
949rotate_to_front(const EggMesherEdge *edge) {
950 int vi_a = edge->_vi_a;
951 int vi_b = edge->_vi_b;
952
953 // See if we're already there.
954 if (_verts.front() == vi_a || _verts.front() == vi_b) {
955 Verts::iterator vi = _verts.begin();
956 ++vi;
957 if (*vi == vi_a || *vi == vi_b) {
958 // Yes!
959 return;
960 }
961
962 // No, we must be right on the line. Roll back one.
963 rotate_back();
964
965 } else {
966 // Roll forward until it comes into view.
967 int num_verts = _verts.size();
968 while (_verts.front() != vi_a && _verts.front() != vi_b) {
969 // Make sure either vertex exists.
970 num_verts--;
971 nassertv(num_verts > 0);
973 }
974 }
975
976#ifndef NDEBUG
977 // Now make sure the edge actually exists.
978 Verts::iterator vi = _verts.begin();
979 ++vi;
980 nassertv(*vi == vi_a || *vi == vi_b);
981#endif
982}
983
984/**
985 * Rotates a triangle or quad so that the given edge is last in the vertex
986 * list.
987 */
989rotate_to_back(const EggMesherEdge *edge) {
990 int vi_a = edge->_vi_a;
991 int vi_b = edge->_vi_b;
992
993 // See if we're already there.
994 if (_verts.back() == vi_a || _verts.back() == vi_b) {
995 Verts::reverse_iterator vi = _verts.rbegin();
996 ++vi;
997 if (*vi == vi_a || *vi == vi_b) {
998 // Yes!
999 return;
1000 }
1001
1002 // No, we must be right on the line. Roll forward one.
1004
1005 } else {
1006 // Roll backward until it comes into view.
1007 int num_verts = _verts.size();
1008 while (_verts.back() != vi_a && _verts.back() != vi_b) {
1009 // Make sure either vertex exists.
1010 num_verts--;
1011 nassertv(num_verts > 0);
1012 rotate_back();
1013 }
1014 }
1015
1016#ifndef NDEBUG
1017 // Now make sure the edge actually exists.
1018 Verts::reverse_iterator vi = _verts.rbegin();
1019 ++vi;
1020 nassertv(*vi == vi_a || *vi == vi_b);
1021#endif
1022}
1023
1024
1025/**
1026 * Returns true if the strip can be inverted (reverse its facing direction).
1027 * Generally, this is true for quadstrips and false for tristrips.
1028 */
1030can_invert() const {
1031 return (_type == PT_quadstrip || _type == PT_quad);
1032}
1033
1034/**
1035 * Reverses the facing of a quadstrip by reversing pairs of vertices. Returns
1036 * true if successful, false if failure (for instance, on a tristrip).
1037 */
1039invert() {
1040 if (!can_invert()) {
1041 return false;
1042 }
1043 Verts::iterator vi, vi2;
1044 vi = _verts.begin();
1045 while (vi != _verts.end()) {
1046 vi2 = vi;
1047 ++vi2;
1048 nassertr(vi2 != _verts.end(), false);
1049
1050 // Exchange vi and vi2
1051 int t = *vi2;
1052 *vi2 = *vi;
1053 *vi = t;
1054
1055 // Increment
1056 vi = vi2;
1057 ++vi;
1058 }
1059 return true;
1060}
1061
1062/**
1063 * Returns true if the tristrip or quadstrip contains an odd number of pieces.
1064 */
1066is_odd() const {
1067 if (_type == PT_quadstrip || _type == PT_quad) {
1068 // If a quadstrip has a multiple of four vertices, it has an odd number of
1069 // quads.
1070 return (_verts.size() % 4 == 0);
1071 } else {
1072 // If a tristrip has an odd number of vertices, it has an odd number of
1073 // tris.
1074 return (_verts.size() % 2 == 1);
1075 }
1076}
1077
1078/**
1079 * Returns true if convert_to_type() would reverse the tail edge of the given
1080 * strip, false otherwise.
1081 */
1083would_reverse_tail(EggMesherStrip::PrimType want_type) const {
1084 bool reverse = false;
1085
1086 if (_type == want_type) {
1087 return false;
1088 }
1089 if (want_type == PT_tristrip) {
1090 switch (_type) {
1091 case PT_tri:
1092 case PT_tristrip:
1093 break;
1094
1095 case PT_quad:
1096 case PT_quadstrip:
1097 // When we convert a quadstrip to a tristrip, we reverse the tail edge
1098 // if we have a multiple of four verts.
1099 reverse = (_verts.size() % 4 == 0);
1100 break;
1101
1102 default:
1103 egg_cat.fatal() << "Invalid conversion!\n";
1104 abort();
1105 break;
1106 }
1107
1108 } else if (want_type == PT_quadstrip) {
1109 switch (_type) {
1110 case PT_quad:
1111 case PT_quadstrip:
1112 break;
1113
1114 case PT_tri:
1115 case PT_tristrip:
1116 // We don't convert tristrips to quadstrips; fall through.
1117
1118 default:
1119 egg_cat.fatal() << "Invalid conversion!\n";
1120 abort();
1121 break;
1122 }
1123 }
1124
1125 return reverse;
1126}
1127
1128
1129/**
1130 * Converts the EggMesherStrip from whatever form it is--triangle, quad, or
1131 * quadstrip--into a tristrip or quadstrip.
1132 */
1134convert_to_type(EggMesherStrip::PrimType want_type) {
1135 Verts::iterator vi, vi2;
1136 int even;
1137
1138 if (_type == want_type) {
1139 return;
1140 }
1141 if (want_type == PT_tristrip) {
1142 switch (_type) {
1143 case PT_tri:
1144 case PT_tristrip:
1145 break;
1146
1147 case PT_quad:
1148 case PT_quadstrip:
1149 // To convert from quadquadstrip to tristrip, we reverse every other
1150 // pair of vertices.
1151
1152 vi = _verts.begin();
1153 even = 0;
1154 while (vi != _verts.end()) {
1155 vi2 = vi;
1156 ++vi2;
1157 nassertv(vi2 != _verts.end());
1158
1159 // vi and vi2 are a pair. Should we reverse them?
1160 if (even) {
1161 int t = *vi2;
1162 *vi2 = *vi;
1163 *vi = t;
1164 }
1165
1166 // Increment
1167 vi = vi2;
1168 ++vi;
1169 even = !even;
1170 }
1171 break;
1172
1173 default:
1174 egg_cat.fatal() << "Invalid conversion!\n";
1175 abort();
1176 }
1177
1178 } else if (want_type == PT_quadstrip) {
1179 switch (_type) {
1180 case PT_quad:
1181 case PT_quadstrip:
1182 break;
1183
1184 case PT_tri:
1185 case PT_tristrip:
1186 // We don't convert tristrips to quadstrips; fall through.
1187
1188 default:
1189 egg_cat.fatal() << "Invalid conversion!\n";
1190 abort();
1191 }
1192 }
1193
1194 _type = want_type;
1195}
1196
1197/**
1198 * Removes the edges from the given strip and appends them to our own. If
1199 * remove_sides is true, then removes all the edges except the head and the
1200 * tail.
1201 */
1203combine_edges(EggMesherStrip &other, int remove_sides) {
1204 Edges::iterator ei;
1205 for (ei = other._edges.begin(); ei != other._edges.end(); ++ei) {
1206 (*ei)->change_strip(&other, this);
1207 }
1208
1209 _edges.splice(_edges.end(), other._edges);
1210
1211 if (remove_sides) {
1212 // Identify the head and tail edges so we can remove everything else.
1215
1216 if (!is_odd()) {
1217 // If the strip is odd, its true tail edge is the inverse of its actual
1218 // edge.
1219 tail = EggMesherEdge(tail._vi_b, tail._vi_a);
1220 }
1221
1222 Edges junk_edges;
1223
1224 Edges::iterator next_ei;
1225 ei = _edges.begin();
1226 while (ei != _edges.end()) {
1227 next_ei = ei;
1228 ++next_ei;
1229
1230 // Is this edge to be saved or is it fodder?
1231 if (!(**ei == head) && !(**ei == tail)) {
1232 // Fodder! But we can't remove it right away, because this will upset
1233 // the current list; instead, we'll splice it to junk_edges.
1234 junk_edges.splice(junk_edges.end(), _edges, ei);
1235 }
1236 ei = next_ei;
1237 }
1238
1239 // Now we can safely remove all the to-be-junked edges.
1240 for (ei = junk_edges.begin(); ei != junk_edges.end(); ++ei) {
1241 (*ei)->remove(this);
1242 }
1243 }
1244}
1245
1246
1247/**
1248 * Removes all active edges from the strip. This effectively renders it
1249 * ineligible to mate with anything else.
1250 */
1253 // First, move all the edges to a safe place so we can traverse the list
1254 // without it changing on us.
1255 Edges junk_edges;
1256 junk_edges.splice(junk_edges.end(), _edges);
1257
1258 // Now we can safely remove all the to-be-junked edges.
1259 Edges::iterator ei;
1260 for (ei = junk_edges.begin(); ei != junk_edges.end(); ++ei) {
1261 (*ei)->remove(this);
1262 }
1263}
1264
1265/**
1266 * Defines an ordering to select neighbors to mate with. This compares strip
1267 * a with strip b and returns true if strip a is the preferable choice, false
1268 * if strip b.
1269 */
1271pick_mate(const EggMesherStrip &a_strip, const EggMesherStrip &b_strip,
1272 const EggMesherEdge &a_edge, const EggMesherEdge &b_edge,
1273 const EggVertexPool *vertex_pool) const {
1274 // First, try to avoid polluting quads, quadstrips, and tristrips with
1275 // arbitrary triangles. When we mate a tri or tristrip to a quadstrip, we
1276 // end up with a tristrip that may be less versatile than the original
1277 // quadstrip. Better to avoid this if we can. Try to choose a mate that
1278 // more closely matches our own type.
1279 int a_cat = a_strip.type_category();
1280 int b_cat = b_strip.type_category();
1281 if (a_cat != b_cat) {
1282 int me_cat = type_category();
1283 return abs(a_cat - me_cat) < abs(b_cat - me_cat);
1284 }
1285
1286 // Now, if we're connecting two tris, try to connect them up so they make
1287 // good quads.
1288 if (_type == PT_tri && a_strip._type == PT_tri &&
1289 b_strip._type == PT_tri) {
1290
1291 // This will depend on both coplanarity and edge length. We can't use
1292 // just one or the other, because some tris are nearly isosceles, and some
1293 // have more than one coplanar neighbor. Hopefully the combination of
1294 // both factors will zero us in on the correct neighbor first.
1295
1296 double a_coplanar = coplanarity(a_strip);
1297 double b_coplanar = coplanarity(b_strip);
1298 double coplanar_diff = a_coplanar - b_coplanar;
1299
1300 double a_length = a_edge.compute_length(vertex_pool);
1301 double b_length = b_edge.compute_length(vertex_pool);
1302 double length_diff = (a_length - b_length) / (a_length + b_length);
1303
1304 // These weights were chosen empirically to yield fairly good results.
1305 double sum = 4.0 * coplanar_diff - 1.0 * length_diff;
1306 return sum < 0;
1307 }
1308
1309 // Then, get the smallest strip.
1310 if (a_strip._prims.size() != b_strip._prims.size()) {
1311 return a_strip._prims.size() < b_strip._prims.size();
1312 }
1313
1314 // Finally, get the strip with the fewest neighbors.
1315 return a_strip.count_neighbors() < b_strip.count_neighbors();
1316}
1317
1318/**
1319 * Defines an ordering to select neighbors to follow when measuring out a
1320 * quadsheet. This is only called when three or more prims share a single
1321 * edge, which should be rarely--generally only when coplanar polys are going
1322 * on.
1323 */
1325pick_sheet_mate(const EggMesherStrip &a_strip,
1326 const EggMesherStrip &b_strip) const {
1327 // First, try to get the poly which is closest to our own normal.
1328 if (_planar && a_strip._planar && b_strip._planar) {
1329 double a_diff = dot(LNormald(_plane_normal), LNormald(a_strip._plane_normal));
1330 double b_diff = dot(LNormald(_plane_normal), LNormald(b_strip._plane_normal));
1331
1332 if (fabs(a_diff - b_diff) > 0.0001) {
1333 return a_diff > b_diff;
1334 }
1335 }
1336
1337 // Then, pick the one that's most like our own type.
1338 int a_cat = a_strip.type_category();
1339 int b_cat = b_strip.type_category();
1340 if (a_cat != b_cat) {
1341 int me_cat = type_category();
1342 return abs(a_cat - me_cat) < abs(b_cat - me_cat);
1343 }
1344
1345 // Oh, just pick any old one.
1346 return false;
1347}
1348
1349/**
1350 * Formats the vertex for output in some sensible way.
1351 */
1353output(std::ostream &out) const {
1354 switch (_status) {
1355 case MS_alive:
1356 break;
1357
1358 case MS_dead:
1359 out << "Dead ";
1360 break;
1361
1362 case MS_done:
1363 out << "Done ";
1364 break;
1365
1366 default:
1367 out << "Unknown status ";
1368 }
1369
1370 switch (_type) {
1371 case PT_tri:
1372 out << "Tri";
1373 break;
1374
1375 case PT_quad:
1376 out << "Quad";
1377 break;
1378
1379 case PT_tristrip:
1380 out << "TriStrip";
1381 break;
1382
1383 case PT_trifan:
1384 out << "TriFan";
1385 break;
1386
1387 case PT_quadstrip:
1388 out << "QuadStrip";
1389 break;
1390
1391 default:
1392 out << "Unknown";
1393 }
1394
1395 if (_planar) {
1396 out << " (planar)";
1397 }
1398
1399 out << " " << _index << " [";
1400
1401 Verts::const_iterator vi;
1402 for (vi = _verts.begin(); vi != _verts.end(); vi++) {
1403 out << " " << *vi;
1404 }
1405 out << " ]: " << _prims.size()
1406 << " prims, " << count_neighbors() << " neighbors";
1407
1408 output_neighbors(out);
1409
1410 out << " edges";
1411 Edges::const_iterator ei;
1412 for (ei = _edges.begin(); ei != _edges.end(); ei++) {
1413 out << " " << (void *)(*ei);
1414 }
1415
1416 out << ".";
1417}
The set of attributes that may be applied to vertices as well as polygons, such as surface normal and...
The base class for primitives such as triangle strips and triangle fans, which include several compon...
Represents one edge of a triangle, as used by the EggMesher to discover connected triangles.
void remove(EggMesherStrip *strip)
Removes an edge from a particular strip.
double compute_length(const EggVertexPool *vertex_pool) const
Returns the length of the edge in model units.
bool matches(const EggMesherEdge &other) const
Returns true if this edge represents the same line segment as the other edge, in either direction.
bool contains_vertex(int vi) const
Returns true if the edge contains the indicated vertex index, false otherwise.
Represents a triangle strip or quad strip in progress, as assembled by the mesher.
EggMesherEdge get_tail_edge() const
Returns an EggMesherEdge which represents the trailing edge in the quadstrip or tristrip.
bool invert()
Reverses the facing of a quadstrip by reversing pairs of vertices.
void rotate_forward()
Rotates a triangle or quad by bringing its first vertex to the back.
bool is_coplanar_with(const EggMesherStrip &other, PN_stdfloat threshold) const
Returns true if the strip and the other strip are coplanar, within the indicated threshold.
bool find_ideal_mate(EggMesherStrip *&mate, EggMesherEdge *&common_edge, const EggVertexPool *vertex_pool)
Searches our neighbors for the most suitable mate.
EggMesherEdge get_head_edge() const
Returns an EggMesherEdge which represents the leading edge in the quadstrip or tristrip.
void output_neighbors(std::ostream &out) const
Writes all the neighbor indexes to the ostream.
bool mate(const EggVertexPool *vertex_pool)
Finds a neighboring strip and joins up with it to make a larger strip.
static bool must_invert(const EggMesherStrip &front, const EggMesherStrip &back, bool will_reverse_back, PrimType type)
Returns false if the strips can be mated as they currently are.
int count_neighbors() const
Returns the number of neighbors the strip shares.
int type_category() const
Returns an integer which gives a heuristic about the similarity of different strip types.
static bool mate_strips(EggMesherEdge *common_edge, EggMesherStrip &front, EggMesherStrip &back, PrimType type)
Stitches two strips together, producing in "front" a new strip of the indicated type (quadstrip or tr...
void combine_edges(EggMesherStrip &other, int remove_sides)
Removes the edges from the given strip and appends them to our own.
void rotate_back()
Rotates a triangle or quad by bringing its last vertex to the front.
bool pick_mate(const EggMesherStrip &a_strip, const EggMesherStrip &b_strip, const EggMesherEdge &a_edge, const EggMesherEdge &b_edge, const EggVertexPool *vertex_pool) const
Defines an ordering to select neighbors to mate with.
bool pick_sheet_mate(const EggMesherStrip &a_strip, const EggMesherStrip &b_strip) const
Defines an ordering to select neighbors to follow when measuring out a quadsheet.
static bool mate_pieces(EggMesherEdge *common_edge, EggMesherStrip &front, EggMesherStrip &back, const EggVertexPool *vertex_pool)
Connects two pieces of arbitrary type, if possible.
bool can_invert() const
Returns true if the strip can be inverted (reverse its facing direction).
PN_stdfloat coplanarity(const EggMesherStrip &other) const
Returns the degree to which the two strips are coplanar.
bool is_odd() const
Returns true if the tristrip or quadstrip contains an odd number of pieces.
const EggMesherEdge * find_opposite_edge(int vi) const
Returns the first edge found that does not contain the given vertex.
void measure_sheet(const EggMesherEdge *edge, int new_row, int &num_prims, int &num_rows, int first_row_id, int this_row_id, int this_row_distance)
Determines the extents of the quadsheet that can be derived by starting with this strip,...
int find_uncommon_vertex(const EggMesherEdge *edge) const
Returns the first vertex found that is not shared by the given edge.
void output(std::ostream &out) const
Formats the vertex for output in some sensible way.
void rotate_to_front(const EggMesherEdge *edge)
Rotates a triangle or quad so that the given edge is first in the vertex list.
bool would_reverse_tail(PrimType want_type) const
Returns true if convert_to_type() would reverse the tail edge of the given strip, false otherwise.
void remove_all_edges()
Removes all active edges from the strip.
static bool convex_quad(EggMesherEdge *common_edge, EggMesherStrip &front, EggMesherStrip &back, const EggVertexPool *vertex_pool)
Returns true if the quad that would be formed by connecting coplanar tris front and back along common...
void rotate_to_back(const EggMesherEdge *edge)
Rotates a triangle or quad so that the given edge is last in the vertex list.
const EggMesherEdge * find_adjacent_edge(const EggMesherEdge *edge) const
Returns the first edge found that shares exactly one vertex with the given edge.
void convert_to_type(PrimType want_type)
Converts the EggMesherStrip from whatever form it is–triangle, quad, or quadstrip–into a tristrip or ...
A single polygon.
Definition eggPolygon.h:24
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
get_vertex
Returns a particular index based on its index number.
void copy_attributes(const EggAttributes &other)
Copies the rendering attributes from the indicated primitive.
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive's list of vertices, and returns it.
A connected fan of triangles.
A connected strip of triangles.
A collection of vertices.
EggVertex * get_vertex(int index) const
Returns the vertex in the pool with the indicated index number, or NULL if no vertices have that inde...
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition eggVertex.h:39
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition eggVertex.I:131
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition typedObject.I:28
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.