Panda3D
Loading...
Searching...
No Matches
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
40using std::max;
41using std::min;
42
43/**
44 *
45 */
46MultitexReducer::
47MultitexReducer() {
48 _target_stage = TextureStage::get_default();
49 _use_geom = false;
50 _allow_tex_mat = false;
51}
52
53/**
54 *
55 */
56MultitexReducer::
57~MultitexReducer() {
58}
59
60/**
61 * Removes the record of nodes that were previously discovered by scan().
62 */
64clear() {
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 */
80scan(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
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 */
111set_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 */
134set_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 */
152set_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 */
165flatten(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 */
424void MultitexReducer::
425scan_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 */
503void MultitexReducer::
504record_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 */
526size_t MultitexReducer::
527choose_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 */
544bool MultitexReducer::
545determine_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 */
598void MultitexReducer::
599get_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 */
621void MultitexReducer::
622choose_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 */
701void MultitexReducer::
702make_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 */
855void MultitexReducer::
856transfer_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 */
932void MultitexReducer::
933scan_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 */
998bool MultitexReducer::
999scan_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 */
1016MultitexReducer::StageInfo::
1017StageInfo(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.
set_camera
Sets the camera that is associated with this DisplayRegion.
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:612
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.
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.
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...
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.
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.
void set_depth_test(bool depth_test, int priority=0)
Specifically sets or disables the testing of the depth buffer on this particular node.
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:599
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.
void set_two_sided(bool two_sided, int priority=0)
Specifically sets or disables two-sided rendering mode on this particular node.
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.
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.
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...
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...
get_num_on_stages
Returns the number of stages that are turned on by the attribute.
get_on_texture
Returns the texture associated with the indicated stage, or NULL if no texture is associated.
get_on_stage
Returns the nth stage turned on by the attribute, sorted in render order.
Defines the properties of a named stage of the multitexture pipeline.
get_default
Returns the default TextureStage that will be used for all texturing that does not name a particular ...
get_name
Returns the name of this texture stage.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
get_minfilter
Returns the filter mode of the texture for minification.
Definition texture.h:392
set_anisotropic_degree
Specifies the level of anisotropic filtering to apply to the texture.
Definition texture.h:404
get_anisotropic_degree
Returns the degree of anisotropic filtering that should be applied to the texture.
Definition texture.h:404
get_y_size
Returns the height of the texture image in texels.
Definition texture.h:347
get_magfilter
Returns the filter mode of the texture for magnification.
Definition texture.h:398
set_minfilter
Sets the filtering method that should be used when viewing the texture from a distance.
Definition texture.h:392
set_magfilter
Sets the filtering method that should be used when viewing the texture up close.
Definition texture.h:398
get_x_size
Returns the width of the texture image in texels.
Definition texture.h:343
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.