Panda3D
multitexReducer.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 multitexReducer.cxx
10  * @author drose
11  * @date 2004-11-30
12  */
13 
14 #include "multitexReducer.h"
15 #include "pandaNode.h"
16 #include "geomNode.h"
17 #include "geom.h"
18 #include "geomTransformer.h"
19 #include "accumulatedAttribs.h"
20 #include "sceneGraphReducer.h"
21 #include "renderState.h"
22 #include "transformState.h"
23 #include "graphicsOutput.h"
24 #include "displayRegion.h"
25 #include "camera.h"
26 #include "orthographicLens.h"
27 #include "cardMaker.h"
28 #include "colorAttrib.h"
29 #include "colorScaleAttrib.h"
30 #include "colorBlendAttrib.h"
31 #include "alphaTestAttrib.h"
32 #include "textureAttrib.h"
33 #include "config_grutil.h"
34 #include "config_gobj.h"
35 #include "dcast.h"
36 #include "geom.h"
37 #include "geomVertexWriter.h"
38 #include "geomVertexReader.h"
39 
40 using std::max;
41 using std::min;
42 
43 /**
44  *
45  */
46 MultitexReducer::
47 MultitexReducer() {
48  _target_stage = TextureStage::get_default();
49  _use_geom = false;
50  _allow_tex_mat = false;
51 }
52 
53 /**
54  *
55  */
56 MultitexReducer::
57 ~MultitexReducer() {
58 }
59 
60 /**
61  * Removes the record of nodes that were previously discovered by scan().
62  */
64 clear() {
65  _stages.clear();
66  _geom_node_list.clear();
67 }
68 
69 /**
70  * Starts scanning the hierarchy beginning at the indicated node. Any
71  * GeomNodes discovered in the hierarchy with multitexture will be added to
72  * internal structures in the MultitexReducer so that a future call to
73  * flatten() will operate on all of these at once.
74  *
75  * The indicated transform and state are the state inherited from the node's
76  * ancestors; any multitexture operations will be accumulated from the
77  * indicated starting state.
78  */
80 scan(PandaNode *node, const RenderState *state, const TransformState *transform) {
81  if (grutil_cat.is_debug()) {
82  grutil_cat.debug()
83  << "scan(" << *node << ", " << *state << ", " << *transform << ")\n";
84  }
85 
86  CPT(RenderState) next_state = state->compose(node->get_state());
87  CPT(TransformState) next_transform = transform->compose(node->get_transform());
88 
89  // We must turn off any textures we come across in the scan() operation,
90  // since the flattened texture will be applied to the Geoms after the
91  // flatten() operation, and we don't want to still have a multitexture
92  // specified.
93  node->set_state(node->get_state()->remove_attrib(TextureAttrib::get_class_slot()));
94 
95  if (node->is_geom_node()) {
96  scan_geom_node(DCAST(GeomNode, node), next_state, next_transform);
97  }
98 
99  PandaNode::Children cr = node->get_children();
100  int num_children = cr.get_num_children();
101  for (int i = 0; i < num_children; i++) {
102  scan(cr.get_child(i), next_state, next_transform);
103  }
104 }
105 
106 /**
107  * Specifies the target TextureStage (and InternalName) that will be left on
108  * each multitexture node after the flatten operation has completed.
109  */
112  _target_stage = stage;
113 }
114 
115 /**
116  * Indicates whether the actual geometry will be used to generate the
117  * textures.
118  *
119  * If this is set to true, the geometry discovered by scan() will be used to
120  * generate the textures, which allows for the vertex and polygon colors to be
121  * made part of the texture itself (and makes the M_decal multitexture mode
122  * more reliable). However, this only works if the geometry does not contain
123  * multiple different polygons that map to the same UV range.
124  *
125  * If this is set to false (the default), a plain flat card will be used to
126  * generate the textures, which is more robust in general, but the resulting
127  * texture will not include vertex colors and M_decal won't work properly.
128  *
129  * Note that in case multiple sets of texture coordinates are in effect, then
130  * the additional sets will always use the geometry anyway regardless of the
131  * setting of this flag (but this will not affect vertex color).
132  */
134 set_use_geom(bool use_geom) {
135  _use_geom = use_geom;
136 }
137 
138 /**
139  * Indicates whether the resulting texture should be expected to be animated
140  * beyond its current range via a texture matrix (true), or whether the
141  * current range of texture coordinates will be sufficient forever (false).
142  *
143  * If this is set to true, then the entire texture image must be generated, in
144  * the assumption that the user may animate the texture around on the surface
145  * after it has been composed.
146  *
147  * If this is set to false (the default), then only the portion of the texture
148  * image which is actually in use must be generated, which may be a
149  * significant savings in texture memory.
150  */
152 set_allow_tex_mat(bool allow_tex_mat) {
153  _allow_tex_mat = allow_tex_mat;
154 }
155 
156 /**
157  * Actually performs the reducing operations on the nodes that were previously
158  * scanned.
159  *
160  * A window that can be used to create texture buffers suitable for rendering
161  * this geometry must be supplied. This specifies the particular GSG that
162  * will be used to composite the textures.
163  */
166  if (grutil_cat.is_debug()) {
167  grutil_cat.debug()
168  << "Beginning flatten operation\n";
169  Stages::const_iterator mi;
170  for (mi = _stages.begin(); mi != _stages.end(); ++mi) {
171  const StageList &stage_list = (*mi).first;
172  const GeomList &geom_list = (*mi).second;
173  grutil_cat.debug(false)
174  << "stage_list for:";
175  for (GeomList::const_iterator gi = geom_list.begin();
176  gi != geom_list.end();
177  ++gi) {
178  const GeomInfo &geom_info = (*gi);
179  grutil_cat.debug(false)
180  << " (" << geom_info._geom_node->get_name() << " g"
181  << geom_info._index << ")";
182  }
183  grutil_cat.debug(false) << ":\n";
184 
185  StageList::const_iterator si;
186  for (si = stage_list.begin(); si != stage_list.end(); ++si) {
187  const StageInfo &stage_info = (*si);
188  grutil_cat.debug(false)
189  << " " << *stage_info._stage << " " << *stage_info._tex
190  << " " << *stage_info._tex_mat << "\n";
191  }
192  }
193  }
194  Stages::const_iterator mi;
195  for (mi = _stages.begin(); mi != _stages.end(); ++mi) {
196  const StageList &stage_list = (*mi).first;
197  const GeomList &geom_list = (*mi).second;
198 
199  // determine whether this texture needs a white or transparent background
200  bool use_transparent_bg = false;
201  if(stage_list.size() > 0) {
202  if(stage_list[0]._stage->get_mode() == TextureStage::M_decal)
203  use_transparent_bg = true;
204  else
205  use_transparent_bg = false;
206  }
207  grutil_cat.debug(false) << "use transparent bg = " << use_transparent_bg << "\n";
208 
209 
210 
211  // Create an offscreen buffer in which to render the new texture.
212 
213  // Start by choosing a model TextureStage to determine the new texture's
214  // properties.
215  const StageInfo &model_stage = stage_list[choose_model_stage(stage_list)];
216 
217  Texture *model_tex = model_stage._tex;
218  int aniso_degree = model_tex->get_anisotropic_degree();
219  SamplerState::FilterType minfilter = model_tex->get_minfilter();
220  SamplerState::FilterType magfilter = model_tex->get_magfilter();
221 
222  // What is the UV range of the model stage?
223  LTexCoord min_uv, max_uv;
224  determine_uv_range(min_uv, max_uv, model_stage, geom_list);
225 
226  // Maybe we only use a small portion of the texture, or maybe we need to
227  // repeat the texture several times.
228  LVecBase2 uv_scale;
229  LVecBase2 uv_trans;
230  get_uv_scale(uv_scale, uv_trans, min_uv, max_uv);
231 
232  // Also, if there is now a scale on the UV's (in conjunction with whatever
233  // texture matrix might be applied on the model stage), we may be able to
234  // adjust the image size accordingly, to keep the pixels at about the same
235  // scale--but we have to keep it to a power of 2.
236  int x_size;
237  int y_size;
238  choose_texture_size(x_size, y_size, model_stage, uv_scale,
239  window);
240 
241  static int multitex_id = 1;
242  std::ostringstream multitex_name_strm;
243  multitex_name_strm << "multitex" << multitex_id;
244  multitex_id++;
245 
246  GraphicsOutput *buffer = window->make_texture_buffer
247  (multitex_name_strm.str(), x_size, y_size, nullptr, false);
248 
249  // TODO: this no longer automatically deletes the buffer. We need to take
250  // care of this explicitly now.
251  buffer->set_one_shot(true);
252 
253  Texture *tex = buffer->get_texture();
254  tex->set_anisotropic_degree(aniso_degree);
255  tex->set_minfilter(minfilter);
256  tex->set_magfilter(magfilter);
257 
258  // Set up the offscreen buffer to render 0,0 to 1,1. This will be the
259  // whole texture, but nothing outside the texture.
260  DisplayRegion *dr = buffer->make_display_region();
261  PT(Camera) cam_node = new Camera("multitexCam");
262  PT(Lens) lens = new OrthographicLens();
263  lens->set_film_size(1.0f, 1.0f);
264  lens->set_film_offset(0.5f, 0.5f);
265  lens->set_near_far(-1000.0f, 1000.0f);
266  lens->set_view_mat(LMatrix4(uv_scale[0], 0.0f, 0.0, 0.0f,
267  0.0f, 1.0f, 0.0, 0.0f,
268  0.0f, 0.0f, uv_scale[1], 0.0f,
269  uv_trans[0], 0.0f, uv_trans[1], 1.0f));
270  cam_node->set_lens(lens);
271 
272  // Create a root node for the buffer's scene graph, and set up some
273  // appropriate properties for it.
274  NodePath render("buffer");
275  render.set_bin("unsorted", 0);
276  render.set_depth_test(false);
277  render.set_depth_write(false);
278  render.set_two_sided(1);
279 
280  NodePath cam = render.attach_new_node(cam_node);
281  dr->set_camera(cam);
282 
283  // If the geometry has vertex color and M_decal is in use, we must render
284  // with use_geom in effect. Otherwise we need not (and we might prefer
285  // not to).
286  bool force_use_geom = _use_geom;
287  bool bake_in_color = _use_geom;
288  LColor geom_color(1.0f, 1.0f, 1.0f, 1.0f);
289 
290  // override the base color in the transparent pass down case.
291  if(use_transparent_bg)
292  geom_color = LColor(0.0f,0.0f,0.0f,0.0f);
293 
294  if (!force_use_geom) {
295  bool uses_decal = scan_decal(stage_list);
296  if (uses_decal) {
297  // If we have M_decal, we need to bake in the flat color even if there
298  // is no vertex color.
299  bake_in_color = true;
300 
301  /*
302  int num_colors = 0;
303  scan_color(geom_list, geom_color, num_colors);
304 
305  if (num_colors > 1) {
306  // But if there is also vertex color, then we need to render with
307  // the geometry.
308  force_use_geom = true;
309  }*/
310  }
311  }
312 
313  if (!force_use_geom) {
314  // Put one plain white (or flat-colored) card in the background for the
315  // first texture layer to apply onto.
316 
317  CardMaker cm("background");
318  cm.set_frame(min_uv[0], max_uv[0], min_uv[1], max_uv[1]);
319  if (bake_in_color) {
320  cm.set_color(geom_color);
321  }
322  render.attach_new_node(cm.generate());
323 
324  } else {
325  // Put a vertex-colored model of the geometry in the background for the
326  // first texture layer to apply only.
327  nassertv(bake_in_color);
328  PT(GeomNode) geom_node = new GeomNode("background");
329  transfer_geom(geom_node, nullptr, geom_list, true);
330 
331  render.attach_new_node(geom_node);
332  }
333 
334  StageList::const_iterator si;
335  for (si = stage_list.begin(); si != stage_list.end(); ++si) {
336  const StageInfo &stage_info = (*si);
337 
338  make_texture_layer(render, stage_info, geom_list,
339  min_uv, max_uv, force_use_geom, use_transparent_bg);
340  }
341 
342  // Now modify the geometry to apply the new texture, instead of the old
343  // multitexture.
344  CPT(RenderAttrib) new_ta = DCAST(TextureAttrib, TextureAttrib::make())->
345  add_on_stage(_target_stage, tex);
346 
347  GeomList::const_iterator gi;
348  for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
349  const GeomInfo &geom_info = (*gi);
350 
351  CPT(RenderState) geom_state =
352  geom_info._geom_node->get_geom_state(geom_info._index);
353  int override = geom_info._geom_net_state->get_override(TextureAttrib::get_class_slot());
354  geom_state = geom_state->add_attrib(new_ta, override);
355 
356  if (bake_in_color) {
357  // If we have baked the color into the texture, we have to be sure to
358  // disable coloring on the new fragment.
359  geom_state = geom_state->add_attrib(ColorAttrib::make_flat(LColor(1.0f, 1.0f, 1.0f, 1.0f)));
360 
361  // And we invent a ColorScaleAttrib to undo the effect of any color
362  // scale we're getting from above. This is not the same thing as a
363  // ColorScaleAttrib::make_off(), since that would prohibit any future
364  // changes to the color scale.
365  const RenderAttrib *attrib =
366  geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
367 
368  if (attrib != nullptr) {
369  geom_state = geom_state->add_attrib
370  (attrib->invert_compose(ColorScaleAttrib::make_identity()));
371  }
372  }
373 
374  // Determine what tex matrix should be on the Geom.
375  CPT(TransformState) tex_mat = TransformState::make_identity();
376 
377  const RenderAttrib *ra = geom_info._state->get_attrib(TexMatrixAttrib::get_class_slot());
378  if (ra != nullptr) {
379  // There is a texture matrix inherited from above; put an inverse
380  // matrix on the Geom to compensate.
381  const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, ra);
382  CPT(TransformState) tex_mat = tma->get_transform(_target_stage);
383  }
384 
385  tex_mat = tex_mat->compose(TransformState::make_pos_hpr_scale
386  (LVecBase3(uv_trans[0], uv_trans[1], 0.0f),
387  LVecBase3(0.0f, 0.0f, 0.0f),
388  LVecBase3(uv_scale[0], uv_scale[1], 1.0f)));
389 
390  if (tex_mat->is_identity()) {
391  // There should be no texture matrix on the Geom.
392  geom_state = geom_state->remove_attrib(TexMatrixAttrib::get_class_slot());
393  } else {
394  // The texture matrix should be as computed.
395  CPT(RenderAttrib) new_tma = TexMatrixAttrib::make
396  (_target_stage, tex_mat->invert_compose(TransformState::make_identity()));
397  geom_state = geom_state->add_attrib(new_tma);
398  }
399 
400 
401  geom_info._geom_node->set_geom_state(geom_info._index, geom_state);
402  }
403  }
404 
405  // Now that we've copied all of the geometry and applied texture matrices,
406  // flatten out those texture matrices where possible.
407  GeomTransformer transformer;
408 
409  GeomNodeList::const_iterator gni;
410  for (gni = _geom_node_list.begin(); gni != _geom_node_list.end(); ++gni) {
411  const GeomNodeInfo &geom_node_info = (*gni);
412  AccumulatedAttribs attribs;
413  attribs._texture =
414  geom_node_info._state->get_attrib(TextureAttrib::get_class_slot());
415  geom_node_info._geom_node->apply_attribs_to_vertices
416  (attribs, SceneGraphReducer::TT_tex_matrix, transformer);
417  }
418 }
419 
420 /**
421  * Adds the Geoms in the indicated GeomNode to the internal database of
422  * multitexture elements.
423  */
424 void MultitexReducer::
425 scan_geom_node(GeomNode *node, const RenderState *state,
426  const TransformState *transform) {
427  if (grutil_cat.is_debug()) {
428  grutil_cat.debug()
429  << "scan_geom_node(" << *node << ", " << *state << ", "
430  << *transform << ")\n";
431  }
432 
433  _geom_node_list.push_back(GeomNodeInfo(state, node));
434 
435  int num_geoms = node->get_num_geoms();
436  for (int gi = 0; gi < num_geoms; gi++) {
437  CPT(RenderState) geom_net_state =
438  state->compose(node->get_geom_state(gi));
439 
440  if (grutil_cat.is_debug()) {
441  grutil_cat.debug()
442  << "geom " << gi << " net_state =\n";
443  geom_net_state->write(std::cerr, 2);
444  }
445 
446  // Get out the net TextureAttrib and TexMatrixAttrib from the state.
447  const RenderAttrib *attrib;
448  const TextureAttrib *ta = nullptr;
449 
450  attrib = geom_net_state->get_attrib(TextureAttrib::get_class_slot());
451  if (attrib != nullptr) {
452  ta = DCAST(TextureAttrib, attrib);
453  }
454 
455  if (ta == nullptr) {
456  // No texture should be on the Geom.
457  CPT(RenderState) geom_state = node->get_geom_state(gi);
458  geom_state = geom_state->remove_attrib(TextureAttrib::get_class_slot());
459  node->set_geom_state(gi, geom_state);
460 
461  } else if (ta->get_num_on_stages() < 2) {
462  // Just a single texture on the Geom; we don't really need to do
463  // anything to flatten the textures, then. But we should ensure that
464  // the correct TextureAttrib is applied to the Geom.
465  int override = geom_net_state->get_override(TextureAttrib::get_class_slot());
466  CPT(RenderState) geom_state = node->get_geom_state(gi);
467  geom_state = geom_state->add_attrib(ta, override);
468  node->set_geom_state(gi, geom_state);
469 
470  } else {
471  // Ok, we have multitexture. Record the Geom.
472  CPT(TexMatrixAttrib) tma = DCAST(TexMatrixAttrib, TexMatrixAttrib::make());
473  attrib = geom_net_state->get_attrib(TexMatrixAttrib::get_class_slot());
474  if (attrib != nullptr) {
475  tma = DCAST(TexMatrixAttrib, attrib);
476  }
477 
478  StageList stage_list;
479 
480  int num_stages = ta->get_num_on_stages();
481  for (int si = 0; si < num_stages; si++) {
482  TextureStage *stage = ta->get_on_stage(si);
483  Texture *tex = ta->get_on_texture(stage);
484  if (tex->get_x_size() != 0 && tex->get_y_size() != 0) {
485  stage_list.push_back(StageInfo(stage, ta, tma));
486 
487  } else {
488  grutil_cat.info()
489  << "Ignoring invalid texture stage " << stage->get_name() << "\n";
490  }
491  }
492 
493  if (stage_list.size() >= 2) {
494  record_stage_list(stage_list, GeomInfo(state, geom_net_state, node, gi));
495  }
496  }
497  }
498 }
499 
500 /**
501  * Adds the record of this one Geom and its associated StageList.
502  */
503 void MultitexReducer::
504 record_stage_list(const MultitexReducer::StageList &stage_list,
505  const MultitexReducer::GeomInfo &geom_info) {
506  if (grutil_cat.is_debug()) {
507  grutil_cat.debug()
508  << "record_stage_list for " << geom_info._geom_node->get_name() << " g"
509  << geom_info._index << ":\n";
510  StageList::const_iterator si;
511  for (si = stage_list.begin(); si != stage_list.end(); ++si) {
512  const StageInfo &stage_info = (*si);
513  grutil_cat.debug(false)
514  << " " << *stage_info._stage << " " << *stage_info._tex
515  << " " << *stage_info._tex_mat << "\n";
516  }
517  }
518 
519  _stages[stage_list].push_back(geom_info);
520 }
521 
522 /**
523  * Chooses one of the TextureStages in the stage_list to serve as the model to
524  * determine the size and properties of the resulting texture.
525  */
526 size_t MultitexReducer::
527 choose_model_stage(const MultitexReducer::StageList &stage_list) const {
528  for (size_t si = 0; si < stage_list.size(); si++) {
529  const StageInfo &stage_info = stage_list[si];
530  if (stage_info._stage == _target_stage) {
531  // If we find the target stage, use that.
532  return si;
533  }
534  }
535 
536  // If none of the stages are the target stage, use the bottom image.
537  return 0;
538 }
539 
540 /**
541  * Determines what the effective UV range for the indicated texture is across
542  * its geoms. Returns true if any UV's are found, false otherwise.
543  */
544 bool MultitexReducer::
545 determine_uv_range(LTexCoord &min_uv, LTexCoord &max_uv,
546  const MultitexReducer::StageInfo &model_stage,
547  const MultitexReducer::GeomList &geom_list) const {
548  const InternalName *model_name = model_stage._stage->get_texcoord_name();
549  bool got_any = false;
550 
551  GeomList::const_iterator gi;
552  for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
553  const GeomInfo &geom_info = (*gi);
554 
555  PT(Geom) geom =
556  geom_info._geom_node->get_geom(geom_info._index)->make_copy();
557 
558  CPT(GeomVertexData) vdata = geom->get_vertex_data();
559  CPT(GeomVertexFormat) format = vdata->get_format();
560  if (format->has_column(model_name)) {
561  GeomVertexReader texcoord(vdata, model_name);
562 
563  if (!texcoord.is_at_end()) {
564  const LVecBase2 &uv = texcoord.get_data2();
565  if (!got_any) {
566  min_uv = max_uv = uv;
567  got_any = true;
568 
569  } else {
570  min_uv.set(min(min_uv[0], uv[0]), min(min_uv[1], uv[1]));
571  max_uv.set(max(max_uv[0], uv[0]), max(max_uv[1], uv[1]));
572  }
573 
574  while (!texcoord.is_at_end()) {
575  const LVecBase2 &uv = texcoord.get_data2();
576  min_uv.set(min(min_uv[0], uv[0]), min(min_uv[1], uv[1]));
577  max_uv.set(max(max_uv[0], uv[0]), max(max_uv[1], uv[1]));
578  }
579  }
580  }
581  }
582 
583  if (!got_any) {
584  min_uv.set(0.0f, 0.0f);
585  max_uv.set(1.0f, 1.0f);
586  }
587 
588  return got_any;
589 }
590 
591 /**
592  * Chooses an appropriate transform to apply to all of the UV's on the
593  * generated texture, based on the coverage of the model stage. If only a
594  * portion of the model stage is used, we scale the UV's up to zoom into that
595  * one portion; on the other hand, if the texture repeats many times, we scale
596  * the UV's down to to include all of the repeating image.
597  */
598 void MultitexReducer::
599 get_uv_scale(LVecBase2 &uv_scale, LVecBase2 &uv_trans,
600  const LTexCoord &min_uv, const LTexCoord &max_uv) const {
601  if (max_uv[0] != min_uv[0]) {
602  uv_scale[0] = (max_uv[0] - min_uv[0]);
603  } else {
604  uv_scale[0] = 1.0f;
605  }
606 
607  if (max_uv[1] != min_uv[1]) {
608  uv_scale[1] = (max_uv[1] - min_uv[1]);
609  } else {
610  uv_scale[1] = 1.0f;
611  }
612 
613  uv_trans[0] = (min_uv[0] + max_uv[0]) / 2.0f - uv_scale[0] * 0.5f;
614  uv_trans[1] = (min_uv[1] + max_uv[1]) / 2.0f - uv_scale[1] * 0.5f;
615 }
616 
617 /**
618  * Chooses an appropriate size to make the new texture, based on the size of
619  * the original model stage's texture, and the scale applied to the UV's.
620  */
621 void MultitexReducer::
622 choose_texture_size(int &x_size, int &y_size,
623  const MultitexReducer::StageInfo &model_stage,
624  const LVecBase2 &uv_scale,
625  GraphicsOutput *window) const {
626  Texture *model_tex = model_stage._tex;
627 
628  // Start with the same size as the model texture.
629  x_size = model_tex->get_x_size();
630  y_size = model_tex->get_y_size();
631 
632  // But we might be looking at just a subset of that texture (|scale| < 1) or
633  // a superset of the texture (|scale| > 1). In this case, we should adjust
634  // the pixel size accordingly, although we have to keep it to a power of 2.
635 
636  LVecBase3 inherited_scale = model_stage._tex_mat->get_scale();
637 
638  PN_stdfloat u_scale = cabs(inherited_scale[0]) * uv_scale[0];
639  if (u_scale != 0.0f) {
640  while (u_scale >= 2.0f) {
641  x_size *= 2;
642  u_scale *= 0.5f;
643  }
644  while (u_scale <= 0.5f && x_size > 0) {
645  x_size /= 2;
646  u_scale *= 2.0f;
647  }
648  }
649 
650  PN_stdfloat v_scale = cabs(inherited_scale[1]) * uv_scale[1];
651  if (v_scale != 0.0f) {
652  while (v_scale >= 2.0f) {
653  y_size *= 2;
654  v_scale *= 0.5f;
655  }
656  while (v_scale <= 0.5f && y_size > 0) {
657  y_size /= 2;
658  v_scale *= 2.0f;
659  }
660  }
661 
662  if (x_size == 0 || y_size == 0) {
663  grutil_cat.warning()
664  << "Texture size " << model_tex->get_x_size() << " "
665  << model_tex->get_y_size() << " with scale "
666  << model_stage._tex_mat->get_scale() << ", reduced to size "
667  << x_size << " " << y_size << "; constraining to 1 1.\n";
668  x_size = 1;
669  y_size = 1;
670  }
671 
672  // Constrain the x_size and y_size to the max_texture_dimension.
673  if (max_texture_dimension > 0) {
674  x_size = min(x_size, (int)max_texture_dimension);
675  y_size = min(y_size, (int)max_texture_dimension);
676  }
677 
678  // Finally, make sure the new sizes fit within the window, so we can use a
679  // parasite buffer.
680  int win_x_size = window->get_x_size();
681  if (win_x_size != 0 && x_size > win_x_size) {
682  x_size /= 2;
683  while (x_size > win_x_size) {
684  x_size /= 2;
685  }
686  }
687 
688  int win_y_size = window->get_y_size();
689  if (win_y_size != 0 && y_size > win_y_size) {
690  y_size /= 2;
691  while (y_size > win_y_size) {
692  y_size /= 2;
693  }
694  }
695 }
696 
697 /**
698  * Creates geometry to render the texture into the offscreen buffer using the
699  * same effects that were requested by its multitexture specification.
700  */
701 void MultitexReducer::
702 make_texture_layer(const NodePath &render,
703  const MultitexReducer::StageInfo &stage_info,
704  const MultitexReducer::GeomList &geom_list,
705  const LTexCoord &min_uv, const LTexCoord &max_uv,
706  bool force_use_geom, bool transparent_base) {
707  CPT(RenderAttrib) cba;
708 
709  switch (stage_info._stage->get_mode()) {
710  case TextureStage::M_normal:
711  case TextureStage::M_normal_height:
712  case TextureStage::M_glow:
713  case TextureStage::M_gloss:
714  case TextureStage::M_height:
715  case TextureStage::M_selector:
716  case TextureStage::M_normal_gloss:
717  // Don't know what to do with these funny modes. We should probably raise
718  // an exception or something. Fall through for now.
719 
720  case TextureStage::M_modulate_glow:
721  case TextureStage::M_modulate_gloss:
722  case TextureStage::M_modulate:
723  cba = ColorBlendAttrib::make
724  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_fbuffer_color,
725  ColorBlendAttrib::O_zero);
726  break;
727 
728  case TextureStage::M_decal:
729  if(transparent_base) {
730  cba = AlphaTestAttrib::make
731  (AlphaTestAttrib::M_greater, 0.0f);
732  } else {
733  cba = ColorBlendAttrib::make
734  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_incoming_alpha,
735  ColorBlendAttrib::O_one_minus_incoming_alpha);
736  }
737  break;
738 
739  case TextureStage::M_blend:
740  cba = ColorBlendAttrib::make
741  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
742  ColorBlendAttrib::O_one_minus_incoming_color,
743  stage_info._stage->get_color());
744  break;
745 
746  case TextureStage::M_replace:
747  cba = ColorBlendAttrib::make_off();
748  break;
749 
750  case TextureStage::M_add:
751  cba = ColorBlendAttrib::make
752  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_one,
753  ColorBlendAttrib::O_one);
754  break;
755 
756  case TextureStage::M_combine:
757  // We only support certain modes of M_combine.
758  switch (stage_info._stage->get_combine_rgb_mode()) {
759  case TextureStage::CM_modulate:
760  {
761  TextureStage::CombineSource source0 = stage_info._stage->get_combine_rgb_source0();
762  TextureStage::CombineSource source1 = stage_info._stage->get_combine_rgb_source1();
763  // Since modulate doesn't care about order, let's establish the
764  // convention that the lowest-numbered source operand is in slot 0
765  // (just for purposes of comparison).
766  if (source1 < source0) {
767  source0 = stage_info._stage->get_combine_rgb_source1();
768  source1 = stage_info._stage->get_combine_rgb_source0();
769  }
770 
771  if (source0 == TextureStage::CS_primary_color &&
772  source1 == TextureStage::CS_previous) {
773  // This is just a trick to re-apply the vertex (lighting) color on
774  // the top of the texture stack. We can ignore it, since the
775  // flattened texture will do this anyway.
776  return;
777 
778  } else if (source0 == TextureStage::CS_texture &&
779  source1 == TextureStage::CS_constant) {
780  // Scaling the texture by a flat color.
781  cba = ColorBlendAttrib::make
782  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
783  ColorBlendAttrib::O_zero, stage_info._stage->get_color());
784 
785  } else if (source0 == TextureStage::CS_texture &&
786  source1 == TextureStage::CS_previous) {
787  // Just an ordinary modulate.
788  cba = ColorBlendAttrib::make
789  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_fbuffer_color,
790  ColorBlendAttrib::O_zero);
791 
792  } else {
793  // Some other kind of modulate; we don't support it.
794  return;
795  }
796  }
797  break;
798 
799  default:
800  // Ignore this stage; we don't support it.
801  return;
802  }
803  break;
804 
805  case TextureStage::M_blend_color_scale:
806  // TODO: make a distinction between this and M_blend.
807  cba = ColorBlendAttrib::make
808  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
809  ColorBlendAttrib::O_one_minus_incoming_color,
810  stage_info._stage->get_color());
811  break;
812  }
813 
814  NodePath geom;
815 
816  if (!force_use_geom && stage_info._stage->get_texcoord_name() == _target_stage->get_texcoord_name()) {
817  // If this TextureStage uses the target texcoords, we can just generate a
818  // simple card the fills the entire buffer.
819  CardMaker cm(stage_info._tex->get_name());
820  cm.set_uv_range(min_uv, max_uv);
821  cm.set_has_uvs(true);
822  cm.set_frame(min_uv[0], max_uv[0], min_uv[1], max_uv[1]);
823 
824  geom = render.attach_new_node(cm.generate());
825 
826  } else {
827  // If this TextureStage uses some other texcoords (or if use_geom is
828  // true), we have to generate geometry that maps the texcoords to the
829  // target space. This will work only for very simple cases where the
830  // geometry is not too extensive and doesn't repeat over the same UV's.
831  PT(GeomNode) geom_node = new GeomNode(stage_info._tex->get_name());
832  transfer_geom(geom_node, stage_info._stage->get_texcoord_name(),
833  geom_list, false);
834 
835  geom = render.attach_new_node(geom_node);
836 
837  geom.set_color(LColor(1.0f, 1.0f, 1.0f, 1.0f));
838  }
839 
840  if (!stage_info._tex_mat->is_identity()) {
841  geom.set_tex_transform(TextureStage::get_default(), stage_info._tex_mat);
842  }
843 
844  geom.set_texture(stage_info._tex);
845  geom.node()->set_attrib(cba);
846 }
847 
848 /**
849  * Copy the vertices from the indicated geom_list, mapping the vertex
850  * coordinates so that the geometry will render the appropriate distortion on
851  * the texture to map UV's from the specified set of texture coordinates to
852  * the target set.
853  */
854 void MultitexReducer::
855 transfer_geom(GeomNode *geom_node, const InternalName *texcoord_name,
856  const MultitexReducer::GeomList &geom_list,
857  bool preserve_color) {
858  Thread *current_thread = Thread::get_current_thread();
859  GeomList::const_iterator gi;
860  for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
861  const GeomInfo &geom_info = (*gi);
862  const Geom *orig_geom = geom_info._geom_node->get_geom(geom_info._index);
863 
864  // Copy the Geom. This actually performs just a pointer copy of the
865  // original GeomVertexData and other associated structures.
866  PT(Geom) geom = orig_geom->make_copy();
867 
868  // Ensure that any vertex animation has been applied.
869  geom->set_vertex_data(geom->get_animated_vertex_data(true, current_thread));
870 
871  // Now get a modifiable pointer to the vertex data in the new Geom. This
872  // will actually perform a deep copy of the vertex data.
873  PT(GeomVertexData) vdata = geom->modify_vertex_data();
874  vdata->set_usage_hint(Geom::UH_stream);
875 
876  if (vdata->has_column(_target_stage->get_texcoord_name())) {
877  GeomVertexWriter vertex(vdata, InternalName::get_vertex(), current_thread);
878  GeomVertexReader texcoord(vdata, _target_stage->get_texcoord_name(), current_thread);
879 
880  while (!texcoord.is_at_end()) {
881  const LVecBase2 &tc = texcoord.get_data2();
882  vertex.set_data3(tc[0], 0.0f, tc[1]);
883  }
884  }
885 
886  if (texcoord_name != nullptr &&
887  texcoord_name != InternalName::get_texcoord()) {
888  // Copy the texture coordinates from the indicated name over to the
889  // default name.
890  const GeomVertexColumn *column =
891  vdata->get_format()->get_column(texcoord_name);
892  if (column != nullptr) {
893  vdata = vdata->replace_column
894  (InternalName::get_texcoord(), column->get_num_components(),
895  column->get_numeric_type(), column->get_contents());
896  geom->set_vertex_data(vdata);
897 
898  GeomVertexReader from(vdata, texcoord_name, current_thread);
899  GeomVertexWriter to(vdata, InternalName::get_texcoord(), current_thread);
900  while (!from.is_at_end()) {
901  to.add_data2(from.get_data2());
902  }
903  }
904  }
905 
906  CPT(RenderState) geom_state = RenderState::make_empty();
907  if (preserve_color) {
908  // Be sure to preserve whatever colors are on the geom.
909  const RenderAttrib *ca = geom_info._geom_net_state->get_attrib(ColorAttrib::get_class_slot());
910  if (ca != nullptr) {
911  geom_state = geom_state->add_attrib(ca);
912  }
913  const RenderAttrib *csa = geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
914  if (csa != nullptr) {
915  geom_state = geom_state->add_attrib(csa);
916  }
917  }
918 
919  geom_node->add_geom(geom, geom_state);
920  }
921 }
922 
923 /**
924  * Checks all the geoms in the list to see if they all use flat color, or if
925  * there is per-vertex color in use.
926  *
927  * Assumption: num_colors = 0 on entry. On exit, num_colors = 1 if there is
928  * exactly one color in use, or 2 if there is more than one color in use. If
929  * num_colors = 1, then geom_color is filled in with the color in use.
930  */
931 void MultitexReducer::
932 scan_color(const MultitexReducer::GeomList &geom_list, LColor &geom_color,
933  int &num_colors) const {
934  GeomList::const_iterator gi;
935  for (gi = geom_list.begin(); gi != geom_list.end() && num_colors < 2; ++gi) {
936  const GeomInfo &geom_info = (*gi);
937 
938  LColor flat_color;
939  bool has_flat_color = false;
940  bool has_vertex_color = false;
941 
942  LColor color_scale(1.0f, 1.0f, 1.0f, 1.0f);
943  const RenderAttrib *csa = geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
944  if (csa != nullptr) {
945  const ColorScaleAttrib *a = DCAST(ColorScaleAttrib, csa);
946  if (a->has_scale()) {
947  color_scale = a->get_scale();
948  }
949  }
950 
951  ColorAttrib::Type color_type = ColorAttrib::T_vertex;
952  const RenderAttrib *ca = geom_info._geom_net_state->get_attrib(ColorAttrib::get_class_slot());
953  if (ca != nullptr) {
954  color_type = DCAST(ColorAttrib, ca)->get_color_type();
955  }
956 
957  if (color_type == ColorAttrib::T_flat) {
958  // This geom has a flat color attrib, which overrides the vertices.
959  flat_color = DCAST(ColorAttrib, ca)->get_color();
960  has_flat_color = true;
961 
962  } else if (color_type == ColorAttrib::T_vertex) {
963  // This geom gets its color from its vertices.
964  const Geom *geom = geom_info._geom_node->get_geom(geom_info._index);
965  if (geom->get_vertex_data()->has_column(InternalName::get_color())) {
966  // This geom has per-vertex color. Assume the colors in the table are
967  // actually different from each other.
968  has_vertex_color = true;
969  }
970  }
971 
972  if (has_vertex_color) {
973  num_colors = 2;
974 
975  } else if (has_flat_color) {
976  flat_color.set(flat_color[0] * color_scale[0],
977  flat_color[1] * color_scale[1],
978  flat_color[2] * color_scale[2],
979  flat_color[3] * color_scale[3]);
980 
981  if (num_colors == 0) {
982  num_colors = 1;
983  geom_color = flat_color;
984 
985  } else if (!flat_color.almost_equal(geom_color)) {
986  // Too bad; there are multiple colors.
987  num_colors = 2;
988  }
989  }
990  }
991 }
992 
993 /**
994  * Checks all the stages in the list to see if any of them apply a texture via
995  * M_decal. Returns true if so, false otherwise.
996  */
997 bool MultitexReducer::
998 scan_decal(const MultitexReducer::StageList &stage_list) const {
999  StageList::const_iterator si;
1000  for (si = stage_list.begin(); si != stage_list.end(); ++si) {
1001  const StageInfo &stage_info = (*si);
1002 
1003  if (stage_info._stage->get_mode() == TextureStage::M_decal) {
1004  return true;
1005  }
1006  }
1007 
1008  return false;
1009 }
1010 
1011 
1012 /**
1013  *
1014  */
1015 MultitexReducer::StageInfo::
1016 StageInfo(TextureStage *stage, const TextureAttrib *ta,
1017  const TexMatrixAttrib *tma) :
1018  _stage(stage),
1019  _tex_mat(TransformState::make_identity())
1020 {
1021  _tex = ta->get_on_texture(_stage);
1022  if (tma->has_stage(stage)) {
1023  _tex_mat = tma->get_transform(stage);
1024  }
1025 }
get_geom_state
Returns the RenderState associated with the nth geom of the node.
Definition: geomNode.h:75
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_y_size
Returns the height of the texture image in texels.
Definition: texture.h:338
void set_depth_test(bool depth_test, int priority=0)
Specifically sets or disables the testing of the depth buffer on this particular node.
Definition: nodePath.cxx:4491
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_magfilter
Returns the filter mode of the texture for magnification.
Definition: texture.h:389
Indicates a coordinate-system transform on vertices.
has_scale
Returns true if the ColorScaleAttrib has a non-identity scale, false otherwise (in which case it migh...
Contents get_contents() const
Returns the token representing the semantic meaning of the stored value.
get_name
Returns the name of this texture stage.
Definition: textureStage.h:188
virtual Texture * get_texture(int i=0) const
Returns the nth texture into which the GraphicsOutput renders.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:51
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:41
void set_depth_write(bool depth_write, int priority=0)
Specifically sets or disables the writing to the depth buffer on this particular node.
Definition: nodePath.cxx:4548
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_state
Sets the complete RenderState that will be applied to all nodes at this level and below.
Definition: pandaNode.h:173
void scan(const NodePath &node)
Starts scanning the hierarchy beginning at the indicated node.
PandaNode * get_child(size_t n) const
Returns the nth child of the node.
Definition: pandaNode.I:962
set_magfilter
Sets the filtering method that should be used when viewing the texture up close.
Definition: texture.h:389
get_num_on_stages
Returns the number of stages that are turned on by the attribute.
Definition: textureAttrib.h:55
void set_target(TextureStage *stage)
Specifies the target TextureStage (and InternalName) that will be left on each multitexture node afte...
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_on_stage
Returns the nth stage turned on by the attribute, sorted in render order.
Definition: textureAttrib.h:55
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const LVecBase2 & get_data2()
Returns the data associated with the read row, expressed as a 2-component value, and advances the rea...
void set_bin(const std::string &bin_name, int draw_order, int priority=0)
Assigns the geometry at this level and below to the named rendering bin.
Definition: nodePath.cxx:2801
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
int get_y_size() const
Returns the visible height of the window or buffer, if it is known.
get_anisotropic_degree
Returns the degree of anisotropic filtering that should be applied to the texture.
Definition: texture.h:395
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a=1.0, int priority=0)
Applies a scene-graph color to the referenced node.
Definition: nodePath.cxx:1952
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DisplayRegion * make_display_region()
Creates a new DisplayRegion that covers the entire window.
This defines how a single column is interleaved within a vertex array stored within a Geom.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_minfilter
Returns the filter mode of the texture for minification.
Definition: texture.h:383
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:31
void flatten(GraphicsOutput *window)
Actually performs the reducing operations on the nodes that were previously scanned.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
get_scale
Returns the scale to be applied to colors.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
set_one_shot
Changes the current setting of the one-shot flag.
virtual Geom * make_copy() const
Returns a newly-allocated Geom that is a shallow copy of this one.
Definition: geom.cxx:100
This class is used by the SceneGraphReducer to maintain and accumulate the set of attributes we have ...
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition: nodePath.cxx:563
size_t get_num_children() const
Returns the number of children of the node.
Definition: pandaNode.I:953
set_minfilter
Sets the filtering method that should be used when viewing the texture from a distance.
Definition: texture.h:383
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
set_anisotropic_degree
Specifies the level of anisotropic filtering to apply to the texture.
Definition: texture.h:395
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for geometry primitives.
Definition: geom.h:54
get_children
Returns an object that can be used to walk through the list of children of the node.
Definition: pandaNode.h:784
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void clear()
Removes the record of nodes that were previously discovered by scan().
This class generates 2-d "cards", that is, rectangular polygons, particularly useful for showing text...
Definition: cardMaker.h:29
This is a base class for the various different classes that represent the result of a frame of render...
int get_x_size() const
Returns the visible width of the window or buffer, if it is known.
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
Applies a scale to colors in the scene graph and on vertices.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
set_camera
Sets the camera that is associated with this DisplayRegion.
Definition: displayRegion.h:94
An orthographic lens.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class defines the physical layout of the vertex data stored within a Geom.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Applies a transform matrix to UV's before they are rendered.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_vertex_data(const GeomVertexData *data)
Replaces the Geom's underlying vertex data table with a completely new table.
Definition: geom.cxx:171
get_num_geoms
Returns the number of geoms in the node.
Definition: geomNode.h:71
A thread; that is, a lightweight process.
Definition: thread.h:46
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A rectangular subregion within a window for rendering into.
Definition: displayRegion.h:57
Indicates what color should be applied to renderable geometry.
Definition: colorAttrib.h:27
void set_allow_tex_mat(bool allow_tex_mat)
Indicates whether the resulting texture should be expected to be animated beyond its current range vi...
void set_geom_state(int n, const RenderState *state)
Changes the RenderState associated with the nth geom of the node.
Definition: geomNode.I:102
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition: camera.h:35
int get_num_components() const
Returns the number of components of the column: the number of instances of the NumericType in each el...
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2068
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:35
void set_two_sided(bool two_sided, int priority=0)
Specifically sets or disables two-sided rendering mode on this particular node.
Definition: nodePath.cxx:4430
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
get_x_size
Returns the width of the texture image in texels.
Definition: texture.h:334
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition: geomNode.cxx:584
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_on_texture
Returns the texture associated with the indicated stage, or NULL if no texture is associated.
Definition: textureAttrib.h:69
get_default
Returns the default TextureStage that will be used for all texturing that does not name a particular ...
Definition: textureStage.h:205
void set_use_geom(bool use_geom)
Indicates whether the actual geometry will be used to generate the textures.
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.