Panda3D
projectionScreen.cxx
1 // Filename: projectionScreen.cxx
2 // Created by: drose (11Dec01)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "projectionScreen.h"
16 #include "geomNode.h"
17 #include "transformState.h"
18 #include "workingNodePath.h"
19 #include "switchNode.h"
20 #include "geom.h"
21 #include "geomTristrips.h"
22 #include "geomVertexWriter.h"
23 #include "geomVertexReader.h"
24 #include "geomVertexRewriter.h"
25 #include "config_distort.h"
26 #include "cullTraverserData.h"
27 
28 TypeHandle ProjectionScreen::_type_handle;
29 
30 ////////////////////////////////////////////////////////////////////
31 // Function: ProjectionScreen::Constructor
32 // Access: Published
33 // Description:
34 ////////////////////////////////////////////////////////////////////
35 ProjectionScreen::
36 ProjectionScreen(const string &name) : PandaNode(name)
37 {
38  set_cull_callback();
39 
40  _texcoord_name = InternalName::get_texcoord();
41 
42  _has_undist_lut = false;
43  _invert_uvs = project_invert_uvs;
44  _texcoord_3d = false;
45  _vignette_on = false;
46  _vignette_color.set(0.0f, 0.0f, 0.0f, 1.0f);
47  _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
48  _computed_rel_top_mat = false;
49  _stale = true;
50  _auto_recompute = true;
51 }
52 
53 ////////////////////////////////////////////////////////////////////
54 // Function: ProjectionScreen::Destructor
55 // Access: Public, Virtual
56 // Description:
57 ////////////////////////////////////////////////////////////////////
58 ProjectionScreen::
59 ~ProjectionScreen() {
60 }
61 
62 ////////////////////////////////////////////////////////////////////
63 // Function: ProjectionScreen::Copy Constructor
64 // Access: Protected
65 // Description:
66 ////////////////////////////////////////////////////////////////////
67 ProjectionScreen::
68 ProjectionScreen(const ProjectionScreen &copy) :
69  PandaNode(copy),
70  _projector(copy._projector),
71  _projector_node(copy._projector_node),
72  _texcoord_name(copy._texcoord_name),
73  _vignette_on(copy._vignette_on),
74  _vignette_color(copy._vignette_color),
75  _frame_color(copy._frame_color),
76  _auto_recompute(copy._auto_recompute)
77 {
78  _computed_rel_top_mat = false;
79  _stale = true;
80 }
81 
82 ////////////////////////////////////////////////////////////////////
83 // Function: ProjectionScreen::make_copy
84 // Access: Public, Virtual
85 // Description: Returns a newly-allocated Node that is a shallow copy
86 // of this one. It will be a different Node pointer,
87 // but its internal data may or may not be shared with
88 // that of the original Node.
89 ////////////////////////////////////////////////////////////////////
91 make_copy() const {
92  return new ProjectionScreen(*this);
93 }
94 
95 ////////////////////////////////////////////////////////////////////
96 // Function: ProjectionScreen::cull_callback
97 // Access: Public, Virtual
98 // Description: This function will be called during the cull
99 // traversal to perform any additional operations that
100 // should be performed at cull time. This may include
101 // additional manipulation of render state or additional
102 // visible/invisible decisions, or any other arbitrary
103 // operation.
104 //
105 // Note that this function will *not* be called unless
106 // set_cull_callback() is called in the constructor of
107 // the derived class. It is necessary to call
108 // set_cull_callback() to indicated that we require
109 // cull_callback() to be called.
110 //
111 // By the time this function is called, the node has
112 // already passed the bounding-volume test for the
113 // viewing frustum, and the node's transform and state
114 // have already been applied to the indicated
115 // CullTraverserData object.
116 //
117 // The return value is true if this node should be
118 // visible, or false if it should be culled.
119 ////////////////////////////////////////////////////////////////////
122  if (_auto_recompute) {
123  recompute_if_stale(data._node_path.get_node_path());
124  }
125  return true;
126 }
127 
128 ////////////////////////////////////////////////////////////////////
129 // Function: ProjectionScreen::set_projector
130 // Access: Published
131 // Description: Specifies the LensNode that is to serve as the
132 // projector for this screen. The relative position of
133 // the LensNode to the ProjectionScreen, as well as the
134 // properties of the lens associated with the LensNode,
135 // determines the UV's that will be assigned to the
136 // geometry within the ProjectionScreen.
137 //
138 // The NodePath must refer to a LensNode (or a Camera).
139 ////////////////////////////////////////////////////////////////////
141 set_projector(const NodePath &projector) {
142  _projector_node = (LensNode *)NULL;
143  _projector = projector;
144  if (!projector.is_empty()) {
145  nassertv(projector.node()->is_of_type(LensNode::get_class_type()));
146  _projector_node = DCAST(LensNode, projector.node());
147  _stale = true;
148  }
149 }
150 
151 ////////////////////////////////////////////////////////////////////
152 // Function: ProjectionScreen::generate_screen
153 // Access: Published
154 // Description: Synthesizes a polygon mesh based on the projection
155 // area of the indicated projector. This generates and
156 // returns a new GeomNode but does not automatically
157 // parent it to the ProjectionScreen node; see
158 // regenerate_screen().
159 //
160 // The specified projector need not be the same as the
161 // projector given to the ProjectionScreen with
162 // set_projector() (although this is often what you
163 // want).
164 //
165 // num_x_verts and num_y_verts specify the number of
166 // vertices to make in the grid across the horizontal
167 // and vertical dimension of the projector,
168 // respectively; distance represents the approximate
169 // distance of the screen from the lens center.
170 //
171 // The fill_ratio parameter specifies the fraction of
172 // the image to cover. If it is 1.0, the entire image
173 // is shown full-size; if it is 0.9, 10% of the image
174 // around the edges is not part of the grid (and the
175 // grid is drawn smaller by the same 10%). This is
176 // intended to work around graphics drivers that tend to
177 // show dark edges or other unsatisfactory artifacts
178 // around the edges of textures: render the texture
179 // larger than necessary by a certain fraction, and make
180 // the screen smaller by the inverse fraction.
181 ////////////////////////////////////////////////////////////////////
182 PT(GeomNode) ProjectionScreen::
183 generate_screen(const NodePath &projector, const string &screen_name,
184  int num_x_verts, int num_y_verts, PN_stdfloat distance,
185  PN_stdfloat fill_ratio) {
186  nassertr(!projector.is_empty() &&
187  projector.node()->is_of_type(LensNode::get_class_type()),
188  NULL);
189  LensNode *projector_node = DCAST(LensNode, projector.node());
190  nassertr(projector_node->get_lens() != NULL, NULL);
191 
192  // First, get the relative coordinate space of the projector.
193  LMatrix4 rel_mat;
194  NodePath this_np(this);
195  rel_mat = projector.get_transform(this_np)->get_mat();
196 
197  // Create a GeomNode to hold this mesh.
198  PT(GeomNode) geom_node = new GeomNode(screen_name);
199 
200  // Now compute all the vertices for the screen. These are arranged
201  // in order from left to right and bottom to top.
202  int num_verts = num_x_verts * num_y_verts;
203  Lens *lens = projector_node->get_lens();
204  PN_stdfloat t = (distance - lens->get_near()) / (lens->get_far() - lens->get_near());
205 
206  PN_stdfloat x_scale = 2.0f / (num_x_verts - 1);
207  PN_stdfloat y_scale = 2.0f / (num_y_verts - 1);
208 
209  PT(GeomVertexData) vdata = new GeomVertexData
210  ("projectionScreen", GeomVertexFormat::get_v3n3(),
211  Geom::UH_dynamic);
212  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
213  GeomVertexWriter normal(vdata, InternalName::get_normal());
214 
215  for (int yi = 0; yi < num_y_verts; yi++) {
216  for (int xi = 0; xi < num_x_verts; xi++) {
217  LPoint2 film = LPoint2((PN_stdfloat)xi * x_scale - 1.0f,
218  (PN_stdfloat)yi * y_scale - 1.0f);
219 
220  // Reduce the image by the fill ratio.
221  film *= fill_ratio;
222 
223  LPoint3 near_point, far_point;
224  lens->extrude(film, near_point, far_point);
225  LPoint3 point = near_point + t * (far_point - near_point);
226 
227  // Normals aren't often needed on projection screens, but you
228  // never know.
229  LVector3 norm;
230  lens->extrude_vec(film, norm);
231 
232  vertex.add_data3(point * rel_mat);
233  normal.add_data3(-normalize(norm * rel_mat));
234  }
235  }
236  nassertr(vdata->get_num_rows() == num_verts, NULL);
237 
238  // Now synthesize a triangle mesh. We run triangle strips
239  // horizontally across the grid.
240  PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
241  // Fill up the index array into the vertices. This lays out the
242  // order of the vertices in each tristrip.
243  int ti, si;
244  for (ti = 1; ti < num_y_verts; ti++) {
245  strip->add_vertex(ti * num_x_verts);
246  for (si = 1; si < num_x_verts; si++) {
247  strip->add_vertex((ti - 1) * num_x_verts + (si-1));
248  strip->add_vertex(ti * num_x_verts + si);
249  }
250  strip->add_vertex((ti - 1) * num_x_verts + (num_x_verts-1));
251  strip->close_primitive();
252  }
253 
254  PT(Geom) geom = new Geom(vdata);
255  geom->add_primitive(strip);
256 
257  geom_node->add_geom(geom);
258 
259  _stale = true;
260  ++_last_screen;
261  return geom_node;
262 }
263 
264 ////////////////////////////////////////////////////////////////////
265 // Function: ProjectionScreen::regenerate_screen
266 // Access: Published
267 // Description: Removes all the children from the ProjectionScreen
268 // node, and adds the newly generated child returned by
269 // generate_screen().
270 ////////////////////////////////////////////////////////////////////
272 regenerate_screen(const NodePath &projector, const string &screen_name,
273  int num_x_verts, int num_y_verts, PN_stdfloat distance,
274  PN_stdfloat fill_ratio) {
275  // First, remove all existing children.
277 
278  // And attach a new child.
279  PT(GeomNode) geom_node =
280  generate_screen(projector, screen_name, num_x_verts, num_y_verts,
281  distance, fill_ratio);
282  add_child(geom_node);
283 }
284 
285 ////////////////////////////////////////////////////////////////////
286 // Function: ProjectionScreen::make_flat_mesh
287 // Access: Published
288 // Description: Generates a deep copy of the hierarchy at the
289 // ProjectionScreen node and below, with vertices
290 // flattened into two dimensions as if they were seen by
291 // the indicated camera node.
292 //
293 // This is useful for rendering an image as seen through
294 // a non-linear lens. The resulting mesh will have
295 // vertices in the range [-1, 1] in both x and y, and
296 // may be then rendered with an ordinary orthographic
297 // lens, to generate the effect of seeing the image
298 // through the specified non-linear lens.
299 //
300 // The returned node has no parent; it is up to the
301 // caller to parent it somewhere or store it so that it
302 // does not get dereferenced and deleted.
303 ////////////////////////////////////////////////////////////////////
304 PT(PandaNode) ProjectionScreen::
305 make_flat_mesh(const NodePath &this_np, const NodePath &camera) {
306  nassertr(!this_np.is_empty() && this_np.node() == this, NULL);
307  nassertr(!camera.is_empty() &&
308  camera.node()->is_of_type(LensNode::get_class_type()),
309  NULL);
310  LensNode *camera_node = DCAST(LensNode, camera.node());
311  nassertr(camera_node->get_lens() != (Lens *)NULL, NULL);
312 
313  // First, ensure the UV's are up-to-date.
314  recompute_if_stale(this_np);
315 
316  PT(PandaNode) top = new PandaNode(get_name());
317 
318  LMatrix4 rel_mat;
319  bool computed_rel_mat = false;
320  make_mesh_children(top, this_np, camera, rel_mat, computed_rel_mat);
321 
322  return top;
323 }
324 
325 ////////////////////////////////////////////////////////////////////
326 // Function: ProjectionScreen::recompute
327 // Access: Published
328 // Description: Recomputes all the UV's for geometry below the
329 // ProjectionScreen node, as if the texture were
330 // projected from the associated projector.
331 //
332 // This function is normally called automatically
333 // whenever the relevant properties change, so it should
334 // not normally need to be called directly by the user.
335 // However, it does no harm to call this if there is any
336 // doubt.
337 ////////////////////////////////////////////////////////////////////
340  NodePath this_np(NodePath::any_path(this));
341  do_recompute(this_np);
342 }
343 
344 ////////////////////////////////////////////////////////////////////
345 // Function: ProjectionScreen::recompute_if_stale
346 // Access: Published
347 // Description: Calls recompute() only if the relative transform
348 // between the ProjectionScreen and the projector has
349 // changed, or if any other relevant property has
350 // changed. Returns true if recomputed, false
351 // otherwise.
352 ////////////////////////////////////////////////////////////////////
355  NodePath this_np(NodePath::any_path(this));
356  return recompute_if_stale(this_np);
357 }
358 
359 ////////////////////////////////////////////////////////////////////
360 // Function: ProjectionScreen::recompute_if_stale
361 // Access: Published
362 // Description: Calls recompute() only if the relative transform
363 // between the ProjectionScreen and the projector has
364 // changed, or if any other relevant property has
365 // changed. Returns true if recomputed, false
366 // otherwise.
367 ////////////////////////////////////////////////////////////////////
369 recompute_if_stale(const NodePath &this_np) {
370  nassertr(!this_np.is_empty() && this_np.node() == this, false);
371 
372  if (_projector_node != (LensNode *)NULL &&
373  _projector_node->get_lens() != (Lens *)NULL) {
374  UpdateSeq lens_change = _projector_node->get_lens()->get_last_change();
375  if (_stale || lens_change != _projector_lens_change) {
376  recompute();
377  return true;
378 
379  } else {
380  // Get the relative transform to ensure it hasn't changed.
381  CPT(TransformState) transform = this_np.get_transform(_projector);
382  const LMatrix4 &top_mat = transform->get_mat();
383  if (!_rel_top_mat.almost_equal(top_mat)) {
384  _rel_top_mat = top_mat;
385  _computed_rel_top_mat = true;
386  do_recompute(this_np);
387  return true;
388  }
389  }
390  }
391 
392  return false;
393 }
394 
395 ////////////////////////////////////////////////////////////////////
396 // Function: ProjectionScreen::do_recompute
397 // Access: Private
398 // Description: Starts the recomputation process.
399 ////////////////////////////////////////////////////////////////////
400 void ProjectionScreen::
401 do_recompute(const NodePath &this_np) {
402  if (_projector_node != (LensNode *)NULL &&
403  _projector_node->get_lens() != (Lens *)NULL) {
404 
405  recompute_node(this_np, _rel_top_mat, _computed_rel_top_mat);
406  // Make sure this flag is set to false for next time.
407  _computed_rel_top_mat = false;
408 
409  _projector_lens_change = _projector_node->get_lens()->get_last_change();
410  _stale = false;
411  }
412 }
413 
414 ////////////////////////////////////////////////////////////////////
415 // Function: ProjectionScreen::recompute_node
416 // Access: Private
417 // Description: Recurses over all geometry at the indicated node and
418 // below, looking for GeomNodes that want to have new
419 // UV's computed. When a new transform space is
420 // encountered, a new relative matrix is computed.
421 ////////////////////////////////////////////////////////////////////
422 void ProjectionScreen::
423 recompute_node(const WorkingNodePath &np, LMatrix4 &rel_mat,
424  bool &computed_rel_mat) {
425  PandaNode *node = np.node();
426  if (node->is_geom_node()) {
427  recompute_geom_node(np, rel_mat, computed_rel_mat);
428  }
429 
430  if (node->is_exact_type(SwitchNode::get_class_type())) {
431  // We make a special case for switch nodes only. Other kinds of
432  // selective child nodes, like LOD's and sequence nodes, will get
433  // all of their children traversed; switch nodes will only
434  // traverse the currently active child.
435  int i = DCAST(SwitchNode, node)->get_visible_child();
436  if (i >= 0 && i < node->get_num_children()) {
437  PandaNode *child = node->get_child(i);
438  recompute_child(WorkingNodePath(np, child), rel_mat, computed_rel_mat);
439  }
440 
441  } else {
442  // A non-switch node. Recurse on all children.
443  int num_children = node->get_num_children();
444  for (int i = 0; i < num_children; i++) {
445  PandaNode *child = node->get_child(i);
446  recompute_child(WorkingNodePath(np, child), rel_mat, computed_rel_mat);
447  }
448  }
449 }
450 
451 ////////////////////////////////////////////////////////////////////
452 // Function: ProjectionScreen::recompute_child
453 // Access: Private
454 // Description: Works in conjunction with recompute_node() to recurse
455 // over the whole graph. This is called on each child
456 // of a given node.
457 ////////////////////////////////////////////////////////////////////
458 void ProjectionScreen::
459 recompute_child(const WorkingNodePath &np, LMatrix4 &rel_mat,
460  bool &computed_rel_mat) {
461  PandaNode *child = np.node();
462 
463  const TransformState *transform = child->get_transform();
464  if (!transform->is_identity()) {
465  // This child node has a transform; therefore, we must recompute
466  // the relative matrix from this point.
467  LMatrix4 new_rel_mat;
468  bool computed_new_rel_mat = false;
469 
470  if (distort_cat.is_spam()) {
471  distort_cat.spam()
472  << "Saving rel_mat " << (void *)&new_rel_mat << " at " << np << "\n";
473  }
474 
475  recompute_node(np, new_rel_mat, computed_new_rel_mat);
476 
477  } else {
478  // This child has no transform, so we can use the same transform
479  // space from before.
480  recompute_node(np, rel_mat, computed_rel_mat);
481  }
482 }
483 
484 ////////////////////////////////////////////////////////////////////
485 // Function: ProjectionScreen::recompute_geom_node
486 // Access: Private
487 // Description: Recomputes the UV's just for the indicated GeomNode.
488 ////////////////////////////////////////////////////////////////////
489 void ProjectionScreen::
490 recompute_geom_node(const WorkingNodePath &np, LMatrix4 &rel_mat,
491  bool &computed_rel_mat) {
492  GeomNode *node = DCAST(GeomNode, np.node());
493  if (!computed_rel_mat) {
494  // All right, time to compute the matrix.
495  NodePath true_np = np.get_node_path();
496  rel_mat = true_np.get_transform(_projector)->get_mat();
497  computed_rel_mat = true;
498 
499  if (distort_cat.is_spam()) {
500  distort_cat.spam()
501  << "Computing rel_mat " << (void *)&rel_mat << " at " << np << "\n";
502  distort_cat.spam()
503  << " " << rel_mat << "\n";
504  }
505  } else {
506  if (distort_cat.is_spam()) {
507  distort_cat.spam()
508  << "Applying rel_mat " << (void *)&rel_mat << " to " << np << "\n";
509  }
510  }
511 
512  int num_geoms = node->get_num_geoms();
513  for (int i = 0; i < num_geoms; i++) {
514  PT(Geom) geom = node->modify_geom(i);
515  distort_cat.debug()
516  << " " << *node << " got geom " << geom
517  << ", cache_ref = " << geom->get_cache_ref_count() << "\n";
518  geom->test_ref_count_integrity();
519  recompute_geom(geom, rel_mat);
520  }
521 }
522 
523 ////////////////////////////////////////////////////////////////////
524 // Function: ProjectionScreen::recompute_geom
525 // Access: Private
526 // Description: Recomputes the UV's just for the indicated Geom.
527 ////////////////////////////////////////////////////////////////////
528 void ProjectionScreen::
529 recompute_geom(Geom *geom, const LMatrix4 &rel_mat) {
530  static const LMatrix4 lens_to_uv
531  (0.5f, 0.0f, 0.0f, 0.0f,
532  0.0f, 0.5f, 0.0f, 0.0f,
533  0.0f, 0.0f, 1.0f, 0.0f,
534  0.5f, 0.5f, 0.0f, 1.0f);
535 
536  static const LMatrix4 lens_to_uv_inverted
537  (0.5f, 0.0f, 0.0f, 0.0f,
538  0.0f,-0.5f, 0.0f, 0.0f,
539  0.0f, 0.0f, 1.0f, 0.0f,
540  0.5f, 0.5f, 0.0f, 1.0f);
541 
542  Thread *current_thread = Thread::get_current_thread();
543 
544  Lens *lens = _projector_node->get_lens();
545  nassertv(lens != (Lens *)NULL);
546 
547  const LMatrix4 &to_uv = _invert_uvs ? lens_to_uv_inverted : lens_to_uv;
548 
549  // Iterate through all the vertices in the Geom.
550  CPT(GeomVertexData) vdata = geom->get_vertex_data(current_thread);
551  vdata = vdata->animate_vertices(true, current_thread);
552 
553  CPT(GeomVertexFormat) vformat = vdata->get_format();
554  if (!vformat->has_column(_texcoord_name) || (_texcoord_3d && vformat->get_column(_texcoord_name)->get_num_components() < 3)) {
555  // We need to add a new column for the new texcoords.
556  vdata = vdata->replace_column
557  (_texcoord_name, 3, Geom::NT_stdfloat, Geom::C_texcoord);
558  geom->set_vertex_data(vdata);
559  }
560  if (_vignette_on && !vdata->has_column(InternalName::get_color())) {
561  // We need to add a column for color.
562  vdata = vdata->replace_column
563  (InternalName::get_color(), 1, Geom::NT_packed_dabc, Geom::C_color);
564  geom->set_vertex_data(vdata);
565  }
566 
567  // Clear the vdata pointer so we don't force a copy in the below.
568  vdata.clear();
569 
570  PT(GeomVertexData) modify_vdata = geom->modify_vertex_data();
571 
572  // Maybe the vdata has animation that we should consider.
573  CPT(GeomVertexData) animated_vdata = geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread);
574 
575  GeomVertexWriter texcoord(modify_vdata, _texcoord_name, current_thread);
576  GeomVertexWriter color(modify_vdata, current_thread);
577  GeomVertexReader vertex(animated_vdata, InternalName::get_vertex(), current_thread);
578 
579  if (_vignette_on) {
580  color.set_column(InternalName::get_color());
581  }
582 
583  while (!vertex.is_at_end()) {
584  LVertex vert = vertex.get_data3();
585 
586  // For each vertex, project to the film plane.
587  LPoint3 vert3d = vert * rel_mat;
588  LPoint3 film(0.0f, 0.0f, 0.0f);
589  bool good = lens->project(vert3d, film);
590 
591  // Now the lens gives us coordinates in the range [-1, 1].
592  // Rescale these to [0, 1].
593  LPoint3 uvw = film * to_uv;
594 
595  if (good && _has_undist_lut) {
596  LPoint3f p;
597  if (!_undist_lut.calc_bilinear_point(p, uvw[0], 1.0 - uvw[1])) {
598  // Point is missing.
599 
600  // We're better off keeping the point where it is,
601  // undistorted--it's probably close to where it should
602  // be--than we are changing it arbitrarily to (0, 0), which
603  // might be far away from where it should be.
604  //uvw.set(0, 0, 0);
605  good = false;
606 
607  } else {
608  uvw = LCAST(PN_stdfloat, p);
609  uvw[1] = 1.0 - uvw[1];
610  }
611  }
612  texcoord.set_data3(uvw);
613 
614  // If we have vignette color in effect, color the vertex according
615  // to whether it fell in front of the lens or not.
616  if (_vignette_on) {
617  if (good) {
618  color.set_data4(_frame_color);
619  } else {
620  color.set_data4(_vignette_color);
621  }
622  }
623  }
624 }
625 
626 ////////////////////////////////////////////////////////////////////
627 // Function: ProjectionScreen::make_mesh_node
628 // Access: Private
629 // Description: Recurses over all geometry at the indicated node and
630 // below, and generates a corresponding node hierarchy
631 // with all the geometry copied, but flattened into 2-d,
632 // as seen from the indicated camera. Returns the newly
633 // created node, or NULL if no node was created.
634 ////////////////////////////////////////////////////////////////////
635 PandaNode *ProjectionScreen::
636 make_mesh_node(PandaNode *result_parent, const WorkingNodePath &np,
637  const NodePath &camera,
638  LMatrix4 &rel_mat, bool &computed_rel_mat) {
639  PandaNode *node = np.node();
640 
641  PT(PandaNode) new_node;
642  if (node->is_geom_node()) {
643  new_node = make_mesh_geom_node(np, camera, rel_mat, computed_rel_mat);
644  } else if (node->safe_to_flatten()) {
645  new_node = node->make_copy();
646  new_node->clear_transform();
647  } else {
648  // If we can't safely flatten the node, just make a plain node in
649  // its place.
650  new_node = new PandaNode(node->get_name());
651  new_node->set_state(node->get_state());
652  }
653 
654  // Now attach the new node to the result.
655  result_parent->add_child(new_node);
656  make_mesh_children(new_node, np, camera, rel_mat, computed_rel_mat);
657  return new_node;
658 }
659 
660 ////////////////////////////////////////////////////////////////////
661 // Function: ProjectionScreen::make_mesh_children
662 // Access: Private
663 // Description: Walks over the list of children for the indicated
664 // node, calling make_mesh_node() on each one.
665 ////////////////////////////////////////////////////////////////////
666 void ProjectionScreen::
667 make_mesh_children(PandaNode *new_node, const WorkingNodePath &np,
668  const NodePath &camera,
669  LMatrix4 &rel_mat, bool &computed_rel_mat) {
670  PandaNode *node = np.node();
671  int num_children = node->get_num_children();
672  for (int i = 0; i < num_children; i++) {
673  PandaNode *child = node->get_child(i);
674  PandaNode *new_child;
675 
676  const TransformState *transform = child->get_transform();
677  if (!transform->is_identity()) {
678  // This child node has a transform; therefore, we must recompute
679  // the relative matrix from this point.
680  LMatrix4 new_rel_mat;
681  bool computed_new_rel_mat = false;
682  new_child = make_mesh_node(new_node, WorkingNodePath(np, child), camera,
683  new_rel_mat, computed_new_rel_mat);
684 
685  } else {
686  // This child has no transform, so we can use the same transform
687  // space from before.
688  new_child = make_mesh_node(new_node, WorkingNodePath(np, child), camera,
689  rel_mat, computed_rel_mat);
690  }
691 
692  if (new_child != NULL) {
693  // Copy all of the render state (except TransformState) to the
694  // new arc.
695  new_child->set_state(child->get_state());
696  }
697  }
698 }
699 
700 ////////////////////////////////////////////////////////////////////
701 // Function: ProjectionScreen::make_mesh_geom_node
702 // Access: Private
703 // Description: Makes a new GeomNode, just like the given one, except
704 // flattened into two dimensions as seen by the
705 // indicated camera.
706 ////////////////////////////////////////////////////////////////////
707 PT(GeomNode) ProjectionScreen::
708 make_mesh_geom_node(const WorkingNodePath &np, const NodePath &camera,
709  LMatrix4 &rel_mat, bool &computed_rel_mat) {
710  GeomNode *node = DCAST(GeomNode, np.node());
711  PT(GeomNode) new_node = new GeomNode(node->get_name());
712  LensNode *lens_node = DCAST(LensNode, camera.node());
713 
714  if (!computed_rel_mat) {
715  // All right, time to compute the matrix.
716  NodePath true_np = np.get_node_path();
717  rel_mat = true_np.get_transform(camera)->get_mat();
718  computed_rel_mat = true;
719  }
720 
721  int num_geoms = node->get_num_geoms();
722  for (int i = 0; i < num_geoms; i++) {
723  const Geom *geom = node->get_geom(i);
724  PT(Geom) new_geom =
725  make_mesh_geom(geom, lens_node->get_lens(), rel_mat);
726  if (new_geom != (Geom *)NULL) {
727  new_node->add_geom(new_geom, node->get_geom_state(i));
728  }
729  }
730 
731  return new_node;
732 }
733 
734 ////////////////////////////////////////////////////////////////////
735 // Function: ProjectionScreen::make_mesh_geom
736 // Access: Private
737 // Description: Makes a new Geom, just like the given one, except
738 // flattened into two dimensions as seen by the
739 // indicated lens. Any triangle in the original mesh
740 // that involves an unprojectable vertex is eliminated.
741 ////////////////////////////////////////////////////////////////////
742 PT(Geom) ProjectionScreen::
743 make_mesh_geom(const Geom *geom, Lens *lens, LMatrix4 &rel_mat) {
744  static const LMatrix4 lens_to_uv
745  (0.5f, 0.0f, 0.0f, 0.0f,
746  0.0f, 0.5f, 0.0f, 0.0f,
747  0.0f, 0.0f, 1.0f, 0.0f,
748  0.5f, 0.5f, 0.0f, 1.0f);
749  static const LMatrix4 uv_to_lens = invert(lens_to_uv);
750 
751  Thread *current_thread = Thread::get_current_thread();
752  PT(Geom) new_geom = geom->make_copy();
753  PT(GeomVertexData) vdata = new_geom->modify_vertex_data();
754  new_geom->set_vertex_data(vdata->animate_vertices(false, current_thread));
755  vdata = new_geom->modify_vertex_data();
756  GeomVertexRewriter vertex(vdata, InternalName::get_vertex());
757  while (!vertex.is_at_end()) {
758  LVertex vert = vertex.get_data3();
759 
760  // Project each vertex into the film plane, but use three
761  // dimensions so the Z coordinate remains meaningful.
762  LPoint3 vert3d = vert * rel_mat;
763  LPoint3 film(0.0f, 0.0f, 0.0f);
764  bool good = lens->project(vert3d, film);
765 
766  if (good && _has_undist_lut) {
767 
768  // Now the lens gives us coordinates in the range [-1, 1].
769  // Rescale these to [0, 1].
770  LPoint3 uvw = film * lens_to_uv;
771 
772  LPoint3f p;
773  if (!_undist_lut.calc_bilinear_point(p, uvw[0], 1.0 - uvw[1])) {
774  // Point is missing.
775  uvw.set(0, 0, 0);
776  good = false;
777  } else {
778  uvw = LCAST(PN_stdfloat, p);
779  uvw[1] = 1.0 - uvw[1];
780  }
781 
782  film = uvw * uv_to_lens;
783  }
784 
785  vertex.set_data3(film);
786  }
787 
788  return new_geom;
789 }
const RenderState * get_geom_state(int n) const
Returns the RenderState associated with the nth geom of the node.
Definition: geomNode.I:106
bool is_at_end() const
Returns true if the reader or writer is currently at the end of the list of vertices, false otherwise.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
PandaNode * node() const
Returns the node traversed to so far.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:74
bool test_ref_count_integrity() const
Does some easy checks to make sure that the reference count isn&#39;t completely bogus.
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:45
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
void set_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row. ...
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
bool almost_equal(const LMatrix4f &other, float threshold) const
Returns true if two matrices are memberwise equal within a specified tolerance.
Definition: lmatrix.cxx:879
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of child nodes this node has.
Definition: pandaNode.I:68
A node that contains a Lens.
Definition: lensNode.h:32
bool calc_bilinear_point(LPoint3f &result, PN_float32 x, PN_float32 y) const
Computes the weighted average of the four nearest points to the floating-point index (x...
Definition: pfmFile.cxx:708
PandaNode * get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns the nth child node of this node.
Definition: pandaNode.I:82
void set_projector(const NodePath &projector)
Specifies the LensNode that is to serve as the projector for this screen.
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
Defines a series of triangle strips.
Definition: geomTristrips.h:25
void add_primitive(const GeomPrimitive *primitive)
Adds a new GeomPrimitive structure to the Geom object.
Definition: geom.cxx:366
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
int get_num_geoms() const
Returns the number of geoms in the node.
Definition: geomNode.I:46
int get_cache_ref_count() const
Returns the current reference count.
This is a class designed to support low-overhead traversals of the complete scene graph...
void regenerate_screen(const NodePath &projector, const string &screen_name, int num_x_verts, int num_y_verts, PN_stdfloat distance, PN_stdfloat fill_ratio)
Removes all the children from the ProjectionScreen node, and adds the newly generated child returned ...
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
PN_stdfloat get_near() const
Returns the position of the near plane (or cylinder, sphere, whatever).
Definition: lens.I:497
static NodePath any_path(PandaNode *node, Thread *current_thread=Thread::get_current_thread())
Returns a new NodePath that represents any arbitrary path from the root to the indicated node...
Definition: nodePath.I:77
bool project(const LPoint3 &point3d, LPoint3 &point2d) const
Given a 3-d point in space, determine the 2-d point this maps to, in the range (-1,1) in both dimensions, where (0,0) is the center of the lens and (-1,-1) is the lower-left corner.
Definition: lens.I:168
NodePath get_node_path() const
Constructs and returns an actual NodePath that represents the same path we have just traversed...
virtual Geom * make_copy() const
Returns a newly-allocated Geom that is a shallow copy of this one.
Definition: geom.cxx:118
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
void remove_all_children(Thread *current_thread=Thread::get_current_thread())
Removes all the children from the node at once, including stashed children.
Definition: pandaNode.cxx:990
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
Definition: geom.h:58
void set_state(const RenderState *state, Thread *current_thread=Thread::get_current_thread())
Sets the complete RenderState that will be applied to all nodes at this level and below...
Definition: pandaNode.cxx:1216
const LVecBase3 & get_data3()
Returns the data associated with the read row, expressed as a 3-component value, and advances the rea...
void add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row. ...
bool extrude_vec(const LPoint2 &point2d, LVector3 &vec3d) const
Given a 2-d point in the range (-1,1) in both dimensions, where (0,0) is the center of the lens and (...
Definition: lens.I:95
void set_data4(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat w)
Sets the write row to a particular 4-component value, and advances the write row. ...
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
A ProjectionScreen implements a simple system for projective texturing.
virtual PandaNode * make_copy() const
Returns a newly-allocated PandaNode that is a shallow copy of this one.
Definition: pandaNode.cxx:604
void recompute()
Recomputes all the UV&#39;s for geometry below the ProjectionScreen node, as if the texture were projecte...
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
void set_vertex_data(const GeomVertexData *data)
Replaces the Geom&#39;s underlying vertex data table with a completely new table.
Definition: geom.cxx:183
A thread; that is, a lightweight process.
Definition: thread.h:51
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
bool is_at_end() const
Returns true if the reader is currently at the end of the list of vertices, false otherwise...
bool extrude(const LPoint2 &point2d, LPoint3 &near_point, LPoint3 &far_point) const
Given a 2-d point in the range (-1,1) in both dimensions, where (0,0) is the center of the lens and (...
Definition: lens.I:32
bool set_column(int column)
Sets up the writer to use the nth data type of the GeomVertexFormat, numbering from 0...
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
This is a two-component point in space.
Definition: lpoint2.h:92
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:925
This is a sequence number that increments monotonically.
Definition: updateSeq.h:43
bool recompute_if_stale()
Calls recompute() only if the relative transform between the ProjectionScreen and the projector has c...
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2486
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48
PN_stdfloat get_far() const
Returns the position of the far plane (or cylinder, sphere, whatever).
Definition: lens.I:522
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:37
A node that renders only one of its children, according to the user&#39;s indication. ...
Definition: switchNode.h:27
This object provides the functionality of both a GeomVertexReader and a GeomVertexWriter, combined together into one convenient package.
virtual bool safe_to_flatten() const
Returns true if it is generally safe to flatten out this particular kind of PandaNode by duplicating ...
Definition: pandaNode.cxx:237