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