Panda3D
eggCompositePrimitive.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 eggCompositePrimitive.cxx
10  * @author drose
11  * @date 2005-03-13
12  */
13 
14 #include "eggCompositePrimitive.h"
15 #include "eggGroupNode.h"
16 #include "eggVertexPool.h"
17 
18 TypeHandle EggCompositePrimitive::_type_handle;
19 
20 
21 /**
22  *
23  */
24 EggCompositePrimitive::
25 ~EggCompositePrimitive() {
26  // Every derived class of EggCompositePrimitive must call clear() in its
27  // destructor.
28  nassertv(_components.empty());
29 }
30 
31 /**
32  * Returns the shading properties apparent on this particular primitive. This
33  * returns S_per_vertex if the vertices have colors or normals (and they are
34  * not all the same values), or for a simple primitive, S_overall otherwise.
35  * A composite primitive may also return S_per_face if the individual
36  * component primitives have colors or normals that are not all the same
37  * values.
38  *
39  * To get the most accurate results, you should call clear_shading() on all
40  * connected primitives (or on all primitives in the egg file), followed by
41  * get_shading() on each primitive. You may find it easiest to call these
42  * methods on the EggData root node (they are defined on EggGroupNode).
43  */
44 EggPrimitive::Shading EggCompositePrimitive::
45 get_shading() const {
46  Shading basic_shading = EggPrimitive::get_shading();
47  if (basic_shading == S_per_vertex) {
48  return basic_shading;
49  }
50  if (_components.empty()) {
51  return S_overall;
52  }
53 
54  // Check if the components all have the same normal.
55  {
56  const EggAttributes *first_component = get_component(0);
57  if (!first_component->has_normal()) {
58  first_component = this;
59  }
60  for (size_t i = 1; i < get_num_components(); ++i) {
61  const EggAttributes *component = get_component(i);
62  if (!component->has_normal()) {
63  component = this;
64  }
65  if (!component->matches_normal(*first_component)) {
66  return S_per_face;
67  }
68  }
69  }
70 
71  // Check if the components all have the same color.
72  {
73  const EggAttributes *first_component = get_component(0);
74  if (!first_component->has_color()) {
75  first_component = this;
76  }
77  for (size_t i = 1; i < get_num_components(); ++i) {
78  const EggAttributes *component = get_component(i);
79  if (!component->has_color()) {
80  component = this;
81  }
82  if (!component->matches_color(*first_component)) {
83  return S_per_face;
84  }
85  }
86  }
87 
88  return S_overall;
89 }
90 
91 /**
92  * Subdivides the composite primitive into triangles and adds those triangles
93  * to the parent group node in place of the original primitive. Returns a
94  * pointer to the original primitive, which is likely about to be destructed.
95  *
96  * If convex_also is true, both concave and convex polygons will be subdivided
97  * into triangles; otherwise, only concave polygons will be subdivided, and
98  * convex polygons will be copied unchanged into the container.
99  */
100 PT(EggCompositePrimitive) EggCompositePrimitive::
101 triangulate_in_place() {
102  EggGroupNode *parent = get_parent();
103  nassertr(parent != nullptr, this);
104 
105  PT(EggCompositePrimitive) save_me = this;
106  parent->remove_child(this);
107  do_triangulate(parent);
108 
109  return save_me;
110 }
111 
112 /**
113  * If the shading property is S_per_vertex, ensures that all vertices have a
114  * normal and a color, and the overall primitive does not.
115  *
116  * If the shading property is S_per_face, and this is a composite primitive,
117  * ensures that all components have a normal and a color, and the vertices and
118  * overall primitive do not. (If this is a simple primitive, S_per_face works
119  * the same as S_overall, below).
120  *
121  * If the shading property is S_overall, ensures that no vertices or
122  * components have a normal or a color, and the overall primitive does (if any
123  * exists at all).
124  *
125  * After this call, either the primitive will have normals or its vertices
126  * will, but not both. Ditto for colors.
127  *
128  * This may create redundant vertices in the vertex pool.
129  */
131 unify_attributes(EggPrimitive::Shading shading) {
132  if (shading == S_unknown) {
133  shading = get_shading();
134  }
135 
136  switch (shading) {
137  case S_per_vertex:
138  // Propagate everything to the vertices.
139  {
140  Components::iterator ci;
141  for (ci = _components.begin(); ci != _components.end(); ++ci) {
142  EggAttributes *component = (*ci);
143  if (component->has_normal()) {
144  if (!has_normal()) {
145  copy_normal(*component);
146  }
147  component->clear_normal();
148  }
149  if (component->has_color()) {
150  if (!has_color()) {
151  copy_color(*component);
152  }
153  component->clear_color();
154  }
155  }
156 
157  // Not having a color is implicitly white.
158  if (!has_color()) {
159  set_color(LColor(1.0f, 1.0f, 1.0f, 1.0f));
160  }
161 
162  iterator pi;
163  for (pi = begin(); pi != end(); ++pi) {
164  EggVertex *orig_vertex = (*pi);
165  PT(EggVertex) vertex = new EggVertex(*orig_vertex);
166  if (!vertex->has_normal() && has_normal()) {
167  vertex->copy_normal(*this);
168  }
169  if (!vertex->has_color() && has_color()) {
170  vertex->copy_color(*this);
171  }
172 
173  EggVertexPool *vertex_pool = orig_vertex->get_pool();
174  nassertv(vertex_pool != nullptr);
175  vertex = vertex_pool->create_unique_vertex(*vertex);
176  vertex->copy_grefs_from(*orig_vertex);
177  replace(pi, vertex);
178  }
179  clear_normal();
180  clear_color();
181  }
182  break;
183 
184  case S_per_face:
185  // Propagate everything to the components.
186  {
187  iterator pi;
188  for (pi = begin(); pi != end(); ++pi) {
189  EggVertex *orig_vertex = (*pi);
190  if (orig_vertex->has_normal() || orig_vertex->has_color()) {
191  if (orig_vertex->has_normal() && !has_normal()) {
192  copy_normal(*orig_vertex);
193  }
194  if (orig_vertex->has_color() && !has_color()) {
195  copy_color(*orig_vertex);
196  }
197 
198  PT(EggVertex) vertex = new EggVertex(*orig_vertex);
199  vertex->clear_normal();
200  vertex->clear_color();
201 
202  EggVertexPool *vertex_pool = orig_vertex->get_pool();
203  nassertv(vertex_pool != nullptr);
204  vertex = vertex_pool->create_unique_vertex(*vertex);
205  vertex->copy_grefs_from(*orig_vertex);
206  replace(pi, vertex);
207  }
208  }
209 
210  // Not having a color is implicitly white.
211  if (!has_color()) {
212  set_color(LColor(1.0f, 1.0f, 1.0f, 1.0f));
213  }
214 
215  Components::iterator ci;
216  for (ci = _components.begin(); ci != _components.end(); ++ci) {
217  EggAttributes *component = (*ci);
218  if (!component->has_normal() && has_normal()) {
219  component->copy_normal(*this);
220  }
221  if (!component->has_color() && has_color()) {
222  component->copy_color(*this);
223  }
224  }
225  clear_normal();
226  clear_color();
227  }
228  break;
229 
230  case S_overall:
231  // Remove everything from the vertices and components.
232  {
233  iterator pi;
234  for (pi = begin(); pi != end(); ++pi) {
235  EggVertex *orig_vertex = (*pi);
236  PT(EggVertex) vertex = new EggVertex(*orig_vertex);
237  if (vertex->has_normal()) {
238  if (!has_normal()) {
239  copy_normal(*vertex);
240  }
241  vertex->clear_normal();
242  }
243  if (vertex->has_color()) {
244  if (!has_color()) {
245  copy_color(*vertex);
246  }
247  vertex->clear_color();
248  }
249 
250  EggVertexPool *vertex_pool = orig_vertex->get_pool();
251  nassertv(vertex_pool != nullptr);
252  vertex = vertex_pool->create_unique_vertex(*vertex);
253  vertex->copy_grefs_from(*orig_vertex);
254  replace(pi, vertex);
255  }
256  Components::iterator ci;
257  for (ci = _components.begin(); ci != _components.end(); ++ci) {
258  EggAttributes *component = (*ci);
259  if (component->has_normal()) {
260  if (!has_normal()) {
261  copy_normal(*component);
262  }
263  component->clear_normal();
264  }
265  if (component->has_color()) {
266  if (!has_color()) {
267  copy_color(*component);
268  }
269  component->clear_color();
270  }
271  }
272 
273  // Not having a color is implicitly white.
274  if (!has_color()) {
275  set_color(LColor(1.0f, 1.0f, 1.0f, 1.0f));
276  }
277  }
278  break;
279 
280  case S_unknown:
281  break;
282  }
283 }
284 
285 /**
286  * Sets the last vertex of the triangle (or each component) to the primitive
287  * normal and/or color, if the primitive is flat-shaded. This reflects the
288  * OpenGL convention of storing flat-shaded properties on the last vertex,
289  * although it is not usually a convention in Egg.
290  *
291  * This may introduce redundant vertices to the vertex pool.
292  */
295  // The first component gets applied to the third vertex, and so on from
296  // there.
297  int num_lead_vertices = get_num_lead_vertices();
298  for (size_t i = 0; i < get_num_components(); ++i) {
299  EggAttributes *component = get_component(i);
300  do_apply_flat_attribute(i + num_lead_vertices, component);
301  }
302 }
303 
304 /**
305  * Sets the first vertex of the triangle (or each component) to the primitive
306  * normal and/or color, if the primitive is flat-shaded. This reflects the
307  * DirectX convention of storing flat-shaded properties on the first vertex,
308  * although it is not usually a convention in Egg.
309  *
310  * This may introduce redundant vertices to the vertex pool.
311  */
314  // The first component gets applied to the first vertex, and so on from
315  // there.
316  for (size_t i = 0; i < get_num_components(); ++i) {
317  EggAttributes *component = get_component(i);
318  do_apply_flat_attribute(i, component);
319  }
320 }
321 
322 /**
323  * Intended as a followup to apply_last_attribute(), this also sets an
324  * attribute on the first vertices of the primitive, if they don't already
325  * have an attribute set, just so they end up with *something*.
326  */
329  if (!empty()) {
330  int num_lead_vertices = get_num_lead_vertices();
331  for (int i = 0; i < (int)size(); i++) {
332  EggVertex *vertex = get_vertex(i);
333  EggAttributes *component = get_component(std::max(i - num_lead_vertices, 0));
334 
335  // Use set_normal() instead of copy_normal(), to avoid getting the
336  // morphs--we don't want them here, since we're just putting a bogus
337  // value on the normal anyway.
338 
339  if (component->has_normal() && !vertex->has_normal()) {
340  vertex->set_normal(component->get_normal());
341  } else if (has_normal() && !vertex->has_normal()) {
342  vertex->set_normal(get_normal());
343  }
344 
345  if (component->has_color() && !vertex->has_color()) {
346  vertex->set_color(component->get_color());
347  } else if (has_color() && !vertex->has_color()) {
348  vertex->set_color(get_color());
349  }
350  }
351  }
352 }
353 
354 /**
355  * Cleans up modeling errors in whatever context this makes sense. For
356  * instance, for a polygon, this calls remove_doubled_verts(true). For a
357  * point, it calls remove_nonunique_verts(). Returns true if the primitive is
358  * valid, or false if it is degenerate.
359  */
362  return (int)size() >= get_num_lead_vertices() + 1;
363 }
364 
365 /**
366  * Marks the vertex as belonging to the primitive. This is an internal
367  * function called by the STL-like functions push_back() and insert(), in
368  * preparation for actually adding the vertex.
369  *
370  * i indicates the new position of the vertex in the list; n indicates the new
371  * number of vertices after the operation has completed.
372  */
373 void EggCompositePrimitive::
374 prepare_add_vertex(EggVertex *vertex, int i, int n) {
375  EggPrimitive::prepare_add_vertex(vertex, i, n);
376 
377  int num_lead_vertices = get_num_lead_vertices();
378  if (n >= num_lead_vertices + 1) {
379  i = std::max(i - num_lead_vertices, 0);
380  nassertv(i <= (int)_components.size());
381  _components.insert(_components.begin() + i, new EggAttributes(*this));
382  }
383 }
384 
385 
386 /**
387  * Marks the vertex as removed from the primitive. This is an internal
388  * function called by the STL-like functions pop_back() and erase(), in
389  * preparation for actually doing the removal.
390  *
391  * i indicates the former position of the vertex in the list; n indicates the
392  * current number of vertices before the operation has completed.
393  *
394  * It is an error to attempt to remove a vertex that is not already a vertex
395  * of this primitive.
396  */
397 void EggCompositePrimitive::
398 prepare_remove_vertex(EggVertex *vertex, int i, int n) {
399  EggPrimitive::prepare_remove_vertex(vertex, i, n);
400 
401  int num_lead_vertices = get_num_lead_vertices();
402  if (n >= num_lead_vertices + 1) {
403  i = std::max(i - num_lead_vertices, 0);
404  nassertv(i < (int)_components.size());
405  delete _components[i];
406  _components.erase(_components.begin() + i);
407  }
408 }
409 
410 /**
411  * Fills the container up with EggPolygons that represent the component
412  * triangles of this triangle strip.
413  *
414  * It is assumed that the EggCompositePrimitive is not already a child of any
415  * other group when this function is called.
416  *
417  * Returns true if the triangulation is successful, or false if there was some
418  * error (in which case the container may contain some partial triangulation).
419  */
420 bool EggCompositePrimitive::
421 do_triangulate(EggGroupNode *container) const {
422  container->add_child((EggCompositePrimitive *)this);
423  return true;
424 }
425 
426 /**
427  * Writes the attributes and the vertices referenced by the primitive to the
428  * indicated output stream in Egg format.
429  */
430 void EggCompositePrimitive::
431 write_body(std::ostream &out, int indent_level) const {
432  EggPrimitive::write_body(out, indent_level);
433 
434  for (size_t i = 0; i < get_num_components(); ++i) {
435  const EggAttributes *attrib = get_component(i);
436  if (attrib->compare_to(*this) != 0 &&
437  (attrib->has_color() || attrib->has_normal())) {
438  indent(out, indent_level)
439  << "<Component> " << i << " {\n";
440  attrib->write(out, indent_level + 2);
441  indent(out, indent_level) << "}\n";
442  }
443  }
444 }
The base class for primitives such as triangle strips and triangle fans, which include several compon...
virtual void unify_attributes(Shading shading)
If the shading property is S_per_vertex, ensures that all vertices have a normal and a color,...
EggVertexPool * get_pool() const
Returns the vertex pool this vertex belongs in.
Definition: eggVertex.I:19
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
get_component
Returns the attributes for the nth component triangle.
void copy_normal(const EggAttributes &other)
Sets this normal to be the same as the other's, include morphs.
Definition: eggAttributes.I:69
PT(EggCompositePrimitive) EggCompositePrimitive
Subdivides the composite primitive into triangles and adds those triangles to the parent group node i...
get_shading
Returns the shading properties apparent on this particular primitive.
Definition: eggPrimitive.h:111
LColor get_color() const
Returns the color set on this particular attribute.
Definition: eggAttributes.I:91
bool matches_color(const EggAttributes &other) const
Returns true if this color matches that of the other EggAttributes object, include the morph list.
get_vertex
Returns a particular index based on its index number.
Definition: eggPrimitive.h:187
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual Shading get_shading() const
Returns the shading properties apparent on this particular primitive.
bool matches_normal(const EggAttributes &other) const
Returns true if this normal matches that of the other EggAttributes object, include the morph list.
Definition: eggAttributes.I:53
void write(std::ostream &out, int indent_level) const
Writes the attributes to the indicated output stream in Egg format.
The set of attributes that may be applied to vertices as well as polygons, such as surface normal and...
Definition: eggAttributes.h:33
void copy_color(const EggAttributes &other)
Sets this color to be the same as the other's, include morphs.
virtual void apply_last_attribute()
Sets the last vertex of the triangle (or each component) to the primitive normal and/or color,...
virtual bool cleanup()
Cleans up modeling errors in whatever context this makes sense.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition: eggVertex.h:39
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
void replace(iterator position, EggVertex *vertex)
Replaces the vertex at the indicated position with the indicated vertex.
Definition: eggPrimitive.I:335
void copy_grefs_from(const EggVertex &other)
Copies all the group references from the other vertex onto this one.
Definition: eggVertex.cxx:747
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
virtual void apply_first_attribute()
Sets the first vertex of the triangle (or each component) to the primitive normal and/or color,...
virtual void post_apply_flat_attribute()
Intended as a followup to apply_last_attribute(), this also sets an attribute on the first vertices o...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggVertex * create_unique_vertex(const EggVertex &copy)
Creates a new vertex in the pool that is a copy of the indicated one and returns it.
A collection of vertices.
Definition: eggVertexPool.h:41
int compare_to(const EggAttributes &other) const
An ordering operator to compare two vertices for sorting order.
get_num_components
Returns the number of individual component triangles within the composite.