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  */
111 set_target(TextureStage *stage) {
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  */
165 flatten(GraphicsOutput *window) {
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  case TextureStage::M_emission:
718  // Don't know what to do with these funny modes. We should probably raise
719  // an exception or something. Fall through for now.
720 
721  case TextureStage::M_modulate_glow:
722  case TextureStage::M_modulate_gloss:
723  case TextureStage::M_modulate:
724  cba = ColorBlendAttrib::make
725  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_fbuffer_color,
726  ColorBlendAttrib::O_zero);
727  break;
728 
729  case TextureStage::M_decal:
730  if(transparent_base) {
731  cba = AlphaTestAttrib::make
732  (AlphaTestAttrib::M_greater, 0.0f);
733  } else {
734  cba = ColorBlendAttrib::make
735  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_incoming_alpha,
736  ColorBlendAttrib::O_one_minus_incoming_alpha);
737  }
738  break;
739 
740  case TextureStage::M_blend:
741  cba = ColorBlendAttrib::make
742  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
743  ColorBlendAttrib::O_one_minus_incoming_color,
744  stage_info._stage->get_color());
745  break;
746 
747  case TextureStage::M_replace:
748  cba = ColorBlendAttrib::make_off();
749  break;
750 
751  case TextureStage::M_add:
752  cba = ColorBlendAttrib::make
753  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_one,
754  ColorBlendAttrib::O_one);
755  break;
756 
757  case TextureStage::M_combine:
758  // We only support certain modes of M_combine.
759  switch (stage_info._stage->get_combine_rgb_mode()) {
760  case TextureStage::CM_modulate:
761  {
762  TextureStage::CombineSource source0 = stage_info._stage->get_combine_rgb_source0();
763  TextureStage::CombineSource source1 = stage_info._stage->get_combine_rgb_source1();
764  // Since modulate doesn't care about order, let's establish the
765  // convention that the lowest-numbered source operand is in slot 0
766  // (just for purposes of comparison).
767  if (source1 < source0) {
768  source0 = stage_info._stage->get_combine_rgb_source1();
769  source1 = stage_info._stage->get_combine_rgb_source0();
770  }
771 
772  if (source0 == TextureStage::CS_primary_color &&
773  source1 == TextureStage::CS_previous) {
774  // This is just a trick to re-apply the vertex (lighting) color on
775  // the top of the texture stack. We can ignore it, since the
776  // flattened texture will do this anyway.
777  return;
778 
779  } else if (source0 == TextureStage::CS_texture &&
780  source1 == TextureStage::CS_constant) {
781  // Scaling the texture by a flat color.
782  cba = ColorBlendAttrib::make
783  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
784  ColorBlendAttrib::O_zero, stage_info._stage->get_color());
785 
786  } else if (source0 == TextureStage::CS_texture &&
787  source1 == TextureStage::CS_previous) {
788  // Just an ordinary modulate.
789  cba = ColorBlendAttrib::make
790  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_fbuffer_color,
791  ColorBlendAttrib::O_zero);
792 
793  } else {
794  // Some other kind of modulate; we don't support it.
795  return;
796  }
797  }
798  break;
799 
800  default:
801  // Ignore this stage; we don't support it.
802  return;
803  }
804  break;
805 
806  case TextureStage::M_blend_color_scale:
807  // TODO: make a distinction between this and M_blend.
808  cba = ColorBlendAttrib::make
809  (ColorBlendAttrib::M_add, ColorBlendAttrib::O_constant_color,
810  ColorBlendAttrib::O_one_minus_incoming_color,
811  stage_info._stage->get_color());
812  break;
813  }
814 
815  NodePath geom;
816 
817  if (!force_use_geom && stage_info._stage->get_texcoord_name() == _target_stage->get_texcoord_name()) {
818  // If this TextureStage uses the target texcoords, we can just generate a
819  // simple card the fills the entire buffer.
820  CardMaker cm(stage_info._tex->get_name());
821  cm.set_uv_range(min_uv, max_uv);
822  cm.set_has_uvs(true);
823  cm.set_frame(min_uv[0], max_uv[0], min_uv[1], max_uv[1]);
824 
825  geom = render.attach_new_node(cm.generate());
826 
827  } else {
828  // If this TextureStage uses some other texcoords (or if use_geom is
829  // true), we have to generate geometry that maps the texcoords to the
830  // target space. This will work only for very simple cases where the
831  // geometry is not too extensive and doesn't repeat over the same UV's.
832  PT(GeomNode) geom_node = new GeomNode(stage_info._tex->get_name());
833  transfer_geom(geom_node, stage_info._stage->get_texcoord_name(),
834  geom_list, false);
835 
836  geom = render.attach_new_node(geom_node);
837 
838  geom.set_color(LColor(1.0f, 1.0f, 1.0f, 1.0f));
839  }
840 
841  if (!stage_info._tex_mat->is_identity()) {
842  geom.set_tex_transform(TextureStage::get_default(), stage_info._tex_mat);
843  }
844 
845  geom.set_texture(stage_info._tex);
846  geom.node()->set_attrib(cba);
847 }
848 
849 /**
850  * Copy the vertices from the indicated geom_list, mapping the vertex
851  * coordinates so that the geometry will render the appropriate distortion on
852  * the texture to map UV's from the specified set of texture coordinates to
853  * the target set.
854  */
855 void MultitexReducer::
856 transfer_geom(GeomNode *geom_node, const InternalName *texcoord_name,
857  const MultitexReducer::GeomList &geom_list,
858  bool preserve_color) {
859  Thread *current_thread = Thread::get_current_thread();
860  GeomList::const_iterator gi;
861  for (gi = geom_list.begin(); gi != geom_list.end(); ++gi) {
862  const GeomInfo &geom_info = (*gi);
863  const Geom *orig_geom = geom_info._geom_node->get_geom(geom_info._index);
864 
865  // Copy the Geom. This actually performs just a pointer copy of the
866  // original GeomVertexData and other associated structures.
867  PT(Geom) geom = orig_geom->make_copy();
868 
869  // Ensure that any vertex animation has been applied.
870  geom->set_vertex_data(geom->get_animated_vertex_data(true, current_thread));
871 
872  // Now get a modifiable pointer to the vertex data in the new Geom. This
873  // will actually perform a deep copy of the vertex data.
874  PT(GeomVertexData) vdata = geom->modify_vertex_data();
875  vdata->set_usage_hint(Geom::UH_stream);
876 
877  if (vdata->has_column(_target_stage->get_texcoord_name())) {
878  GeomVertexWriter vertex(vdata, InternalName::get_vertex(), current_thread);
879  GeomVertexReader texcoord(vdata, _target_stage->get_texcoord_name(), current_thread);
880 
881  while (!texcoord.is_at_end()) {
882  const LVecBase2 &tc = texcoord.get_data2();
883  vertex.set_data3(tc[0], 0.0f, tc[1]);
884  }
885  }
886 
887  if (texcoord_name != nullptr &&
888  texcoord_name != InternalName::get_texcoord()) {
889  // Copy the texture coordinates from the indicated name over to the
890  // default name.
891  const GeomVertexColumn *column =
892  vdata->get_format()->get_column(texcoord_name);
893  if (column != nullptr) {
894  vdata = vdata->replace_column
895  (InternalName::get_texcoord(), column->get_num_components(),
896  column->get_numeric_type(), column->get_contents());
897  geom->set_vertex_data(vdata);
898 
899  GeomVertexReader from(vdata, texcoord_name, current_thread);
900  GeomVertexWriter to(vdata, InternalName::get_texcoord(), current_thread);
901  while (!from.is_at_end()) {
902  to.add_data2(from.get_data2());
903  }
904  }
905  }
906 
907  CPT(RenderState) geom_state = RenderState::make_empty();
908  if (preserve_color) {
909  // Be sure to preserve whatever colors are on the geom.
910  const RenderAttrib *ca = geom_info._geom_net_state->get_attrib(ColorAttrib::get_class_slot());
911  if (ca != nullptr) {
912  geom_state = geom_state->add_attrib(ca);
913  }
914  const RenderAttrib *csa = geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
915  if (csa != nullptr) {
916  geom_state = geom_state->add_attrib(csa);
917  }
918  }
919 
920  geom_node->add_geom(geom, geom_state);
921  }
922 }
923 
924 /**
925  * Checks all the geoms in the list to see if they all use flat color, or if
926  * there is per-vertex color in use.
927  *
928  * Assumption: num_colors = 0 on entry. On exit, num_colors = 1 if there is
929  * exactly one color in use, or 2 if there is more than one color in use. If
930  * num_colors = 1, then geom_color is filled in with the color in use.
931  */
932 void MultitexReducer::
933 scan_color(const MultitexReducer::GeomList &geom_list, LColor &geom_color,
934  int &num_colors) const {
935  GeomList::const_iterator gi;
936  for (gi = geom_list.begin(); gi != geom_list.end() && num_colors < 2; ++gi) {
937  const GeomInfo &geom_info = (*gi);
938 
939  LColor flat_color;
940  bool has_flat_color = false;
941  bool has_vertex_color = false;
942 
943  LColor color_scale(1.0f, 1.0f, 1.0f, 1.0f);
944  const RenderAttrib *csa = geom_info._geom_net_state->get_attrib(ColorScaleAttrib::get_class_slot());
945  if (csa != nullptr) {
946  const ColorScaleAttrib *a = DCAST(ColorScaleAttrib, csa);
947  if (a->has_scale()) {
948  color_scale = a->get_scale();
949  }
950  }
951 
952  ColorAttrib::Type color_type = ColorAttrib::T_vertex;
953  const RenderAttrib *ca = geom_info._geom_net_state->get_attrib(ColorAttrib::get_class_slot());
954  if (ca != nullptr) {
955  color_type = DCAST(ColorAttrib, ca)->get_color_type();
956  }
957 
958  if (color_type == ColorAttrib::T_flat) {
959  // This geom has a flat color attrib, which overrides the vertices.
960  flat_color = DCAST(ColorAttrib, ca)->get_color();
961  has_flat_color = true;
962 
963  } else if (color_type == ColorAttrib::T_vertex) {
964  // This geom gets its color from its vertices.
965  const Geom *geom = geom_info._geom_node->get_geom(geom_info._index);
966  if (geom->get_vertex_data()->has_column(InternalName::get_color())) {
967  // This geom has per-vertex color. Assume the colors in the table are
968  // actually different from each other.
969  has_vertex_color = true;
970  }
971  }
972 
973  if (has_vertex_color) {
974  num_colors = 2;
975 
976  } else if (has_flat_color) {
977  flat_color.set(flat_color[0] * color_scale[0],
978  flat_color[1] * color_scale[1],
979  flat_color[2] * color_scale[2],
980  flat_color[3] * color_scale[3]);
981 
982  if (num_colors == 0) {
983  num_colors = 1;
984  geom_color = flat_color;
985 
986  } else if (!flat_color.almost_equal(geom_color)) {
987  // Too bad; there are multiple colors.
988  num_colors = 2;
989  }
990  }
991  }
992 }
993 
994 /**
995  * Checks all the stages in the list to see if any of them apply a texture via
996  * M_decal. Returns true if so, false otherwise.
997  */
998 bool MultitexReducer::
999 scan_decal(const MultitexReducer::StageList &stage_list) const {
1000  StageList::const_iterator si;
1001  for (si = stage_list.begin(); si != stage_list.end(); ++si) {
1002  const StageInfo &stage_info = (*si);
1003 
1004  if (stage_info._stage->get_mode() == TextureStage::M_decal) {
1005  return true;
1006  }
1007  }
1008 
1009  return false;
1010 }
1011 
1012 
1013 /**
1014  *
1015  */
1016 MultitexReducer::StageInfo::
1017 StageInfo(TextureStage *stage, const TextureAttrib *ta,
1018  const TexMatrixAttrib *tma) :
1019  _stage(stage),
1020  _tex_mat(TransformState::make_identity())
1021 {
1022  _tex = ta->get_on_texture(_stage);
1023  if (tma->has_stage(stage)) {
1024  _tex_mat = tma->get_transform(stage);
1025  }
1026 }
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.
This class is used by the SceneGraphReducer to maintain and accumulate the set of attributes we have ...
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition: camera.h:35
This class generates 2-d "cards", that is, rectangular polygons, particularly useful for showing text...
Definition: cardMaker.h:29
Indicates what color should be applied to renderable geometry.
Definition: colorAttrib.h:27
Applies a scale to colors in the scene graph and on vertices.
has_scale
Returns true if the ColorScaleAttrib has a non-identity scale, false otherwise (in which case it migh...
get_scale
Returns the scale to be applied to colors.
A rectangular subregion within a window for rendering into.
Definition: displayRegion.h:57
set_camera
Sets the camera that is associated with this DisplayRegion.
Definition: displayRegion.h:94
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition: geomNode.cxx:586
get_num_geoms
Returns the number of geoms in the node.
Definition: geomNode.h:71
get_geom_state
Returns the RenderState associated with the nth geom of the node.
Definition: geomNode.h:75
void set_geom_state(int n, const RenderState *state)
Changes the RenderState associated with the nth geom of the node.
Definition: geomNode.I:102
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...
This defines how a single column is interleaved within a vertex array stored within a Geom.
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
Contents get_contents() const
Returns the token representing the semantic meaning of the stored value.
int get_num_components() const
Returns the number of components of the column: the number of instances of the NumericType in each el...
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This class defines the physical layout of the vertex data stored within a Geom.
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A container for geometry primitives.
Definition: geom.h:54
virtual Geom * make_copy() const
Returns a newly-allocated Geom that is a shallow copy of this one.
Definition: geom.cxx:100
This is a base class for the various different classes that represent the result of a frame of render...
set_one_shot
Changes the current setting of the one-shot flag.
DisplayRegion * make_display_region()
Creates a new DisplayRegion that covers the entire window.
int get_y_size() const
Returns the visible height of the window or buffer, if it is known.
int get_x_size() const
Returns the visible width of the window or buffer, if it is known.
virtual Texture * get_texture(int i=0) const
Returns the nth texture into which the GraphicsOutput renders.
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:41
void clear()
Removes the record of nodes that were previously discovered by scan().
void scan(const NodePath &node)
Starts scanning the hierarchy beginning at the indicated node.
void set_use_geom(bool use_geom)
Indicates whether the actual geometry will be used to generate the textures.
void set_target(TextureStage *stage)
Specifies the target TextureStage (and InternalName) that will be left on each multitexture node afte...
void flatten(GraphicsOutput *window)
Actually performs the reducing operations on the nodes that were previously scanned.
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...
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
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:4632
void set_texture(Texture *tex, int priority=0)
Adds the indicated texture to the list of textures that will be rendered on the default texture stage...
Definition: nodePath.cxx:2938
void set_tex_transform(TextureStage *stage, const TransformState *transform)
Sets the texture matrix on the current node to the indicated transform for the given stage.
Definition: nodePath.cxx:3469
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
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:2019
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:4575
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:600
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:2868
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:4514
An orthographic lens.
PandaNode * get_child(size_t n) const
Returns the nth child of the node.
Definition: pandaNode.I:962
size_t get_num_children() const
Returns the number of children of the node.
Definition: pandaNode.I:953
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2062
set_state
Sets the complete RenderState that will be applied to all nodes at this level and below.
Definition: pandaNode.h:173
void set_attrib(const RenderAttrib *attrib, int override=0)
Adds the indicated render attribute to the scene graph on this node.
Definition: pandaNode.cxx:938
get_children
Returns an object that can be used to walk through the list of children of the node.
Definition: pandaNode.h:782
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:51
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
Applies a transform matrix to UV's before they are rendered.
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:31
get_num_on_stages
Returns the number of stages that are turned on by the attribute.
Definition: textureAttrib.h:55
get_on_texture
Returns the texture associated with the indicated stage, or NULL if no texture is associated.
Definition: textureAttrib.h:69
get_on_stage
Returns the nth stage turned on by the attribute, sorted in render order.
Definition: textureAttrib.h:55
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:35
get_default
Returns the default TextureStage that will be used for all texturing that does not name a particular ...
Definition: textureStage.h:207
get_name
Returns the name of this texture stage.
Definition: textureStage.h:190
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
get_minfilter
Returns the filter mode of the texture for minification.
Definition: texture.h:391
set_anisotropic_degree
Specifies the level of anisotropic filtering to apply to the texture.
Definition: texture.h:403
get_anisotropic_degree
Returns the degree of anisotropic filtering that should be applied to the texture.
Definition: texture.h:403
get_y_size
Returns the height of the texture image in texels.
Definition: texture.h:346
get_magfilter
Returns the filter mode of the texture for magnification.
Definition: texture.h:397
set_minfilter
Sets the filtering method that should be used when viewing the texture from a distance.
Definition: texture.h:391
set_magfilter
Sets the filtering method that should be used when viewing the texture up close.
Definition: texture.h:397
get_x_size
Returns the width of the texture image in texels.
Definition: texture.h:342
A thread; that is, a lightweight process.
Definition: thread.h:46
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
Indicates a coordinate-system transform on vertices.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
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.
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.
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.