Panda3D

projectionScreen.cxx

00001 // Filename: projectionScreen.cxx
00002 // Created by:  drose (11Dec01)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "projectionScreen.h"
00016 #include "geomNode.h"
00017 #include "transformState.h"
00018 #include "workingNodePath.h"
00019 #include "switchNode.h"
00020 #include "geom.h"
00021 #include "geomTristrips.h"
00022 #include "geomVertexWriter.h"
00023 #include "geomVertexReader.h"
00024 #include "geomVertexRewriter.h"
00025 #include "config_distort.h"
00026 #include "cullTraverserData.h"
00027 
00028 TypeHandle ProjectionScreen::_type_handle;
00029 
00030 ////////////////////////////////////////////////////////////////////
00031 //     Function: ProjectionScreen::Constructor
00032 //       Access: Published
00033 //  Description: 
00034 ////////////////////////////////////////////////////////////////////
00035 ProjectionScreen::
00036 ProjectionScreen(const string &name) : PandaNode(name)
00037 {
00038   set_cull_callback();
00039 
00040   _texcoord_name = InternalName::get_texcoord();
00041 
00042   _invert_uvs = project_invert_uvs;
00043   _vignette_on = false;
00044   _vignette_color.set(0.0f, 0.0f, 0.0f, 1.0f);
00045   _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
00046   _computed_rel_top_mat = false;
00047   _stale = true;
00048 }
00049 
00050 ////////////////////////////////////////////////////////////////////
00051 //     Function: ProjectionScreen::Destructor
00052 //       Access: Public, Virtual
00053 //  Description: 
00054 ////////////////////////////////////////////////////////////////////
00055 ProjectionScreen::
00056 ~ProjectionScreen() {
00057 }
00058 
00059 ////////////////////////////////////////////////////////////////////
00060 //     Function: ProjectionScreen::Copy Constructor
00061 //       Access: Protected
00062 //  Description: 
00063 ////////////////////////////////////////////////////////////////////
00064 ProjectionScreen::
00065 ProjectionScreen(const ProjectionScreen &copy) :
00066   PandaNode(copy),
00067   _projector(copy._projector),
00068   _projector_node(copy._projector_node),
00069   _texcoord_name(copy._texcoord_name),
00070   _vignette_on(copy._vignette_on),
00071   _vignette_color(copy._vignette_color),
00072   _frame_color(copy._frame_color)
00073 {
00074   _computed_rel_top_mat = false;
00075   _stale = true;
00076 }
00077 
00078 ////////////////////////////////////////////////////////////////////
00079 //     Function: ProjectionScreen::make_copy
00080 //       Access: Public, Virtual
00081 //  Description: Returns a newly-allocated Node that is a shallow copy
00082 //               of this one.  It will be a different Node pointer,
00083 //               but its internal data may or may not be shared with
00084 //               that of the original Node.
00085 ////////////////////////////////////////////////////////////////////
00086 PandaNode *ProjectionScreen::
00087 make_copy() const {
00088   return new ProjectionScreen(*this);
00089 }
00090 
00091 ////////////////////////////////////////////////////////////////////
00092 //     Function: ProjectionScreen::cull_callback
00093 //       Access: Public, Virtual
00094 //  Description: This function will be called during the cull
00095 //               traversal to perform any additional operations that
00096 //               should be performed at cull time.  This may include
00097 //               additional manipulation of render state or additional
00098 //               visible/invisible decisions, or any other arbitrary
00099 //               operation.
00100 //
00101 //               Note that this function will *not* be called unless
00102 //               set_cull_callback() is called in the constructor of
00103 //               the derived class.  It is necessary to call
00104 //               set_cull_callback() to indicated that we require
00105 //               cull_callback() to be called.
00106 //
00107 //               By the time this function is called, the node has
00108 //               already passed the bounding-volume test for the
00109 //               viewing frustum, and the node's transform and state
00110 //               have already been applied to the indicated
00111 //               CullTraverserData object.
00112 //
00113 //               The return value is true if this node should be
00114 //               visible, or false if it should be culled.
00115 ////////////////////////////////////////////////////////////////////
00116 bool ProjectionScreen::
00117 cull_callback(CullTraverser *, CullTraverserData &data) {
00118   recompute_if_stale(data._node_path.get_node_path());
00119   return true;
00120 }
00121 
00122 ////////////////////////////////////////////////////////////////////
00123 //     Function: ProjectionScreen::set_projector
00124 //       Access: Published
00125 //  Description: Specifies the LensNode that is to serve as the
00126 //               projector for this screen.  The relative position of
00127 //               the LensNode to the ProjectionScreen, as well as the
00128 //               properties of the lens associated with the LensNode,
00129 //               determines the UV's that will be assigned to the
00130 //               geometry within the ProjectionScreen.
00131 //
00132 //               The NodePath must refer to a LensNode (or a Camera).
00133 ////////////////////////////////////////////////////////////////////
00134 void ProjectionScreen::
00135 set_projector(const NodePath &projector) {
00136   _projector_node = (LensNode *)NULL;
00137   _projector = projector;
00138   if (!projector.is_empty()) {
00139     nassertv(projector.node()->is_of_type(LensNode::get_class_type()));
00140     _projector_node = DCAST(LensNode, projector.node());
00141     _stale = true;
00142   }
00143 }
00144 
00145 ////////////////////////////////////////////////////////////////////
00146 //     Function: ProjectionScreen::generate_screen
00147 //       Access: Published
00148 //  Description: Synthesizes a polygon mesh based on the projection
00149 //               area of the indicated projector.  This generates and
00150 //               returns a new GeomNode but does not automatically
00151 //               parent it to the ProjectionScreen node; see
00152 //               regenerate_screen().
00153 //
00154 //               The specified projector need not be the same as the
00155 //               projector given to the ProjectionScreen with
00156 //               set_projector() (although this is often what you
00157 //               want).
00158 //
00159 //               num_x_verts and num_y_verts specify the number of
00160 //               vertices to make in the grid across the horizontal
00161 //               and vertical dimension of the projector,
00162 //               respectively; distance represents the approximate
00163 //               distance of the screen from the lens center.
00164 //
00165 //               The fill_ratio parameter specifies the fraction of
00166 //               the image to cover.  If it is 1.0, the entire image
00167 //               is shown full-size; if it is 0.9, 10% of the image
00168 //               around the edges is not part of the grid (and the
00169 //               grid is drawn smaller by the same 10%).  This is
00170 //               intended to work around graphics drivers that tend to
00171 //               show dark edges or other unsatisfactory artifacts
00172 //               around the edges of textures: render the texture
00173 //               larger than necessary by a certain fraction, and make
00174 //               the screen smaller by the inverse fraction.
00175 ////////////////////////////////////////////////////////////////////
00176 PT(GeomNode) ProjectionScreen::
00177 generate_screen(const NodePath &projector, const string &screen_name,
00178                 int num_x_verts, int num_y_verts, float distance,
00179                 float fill_ratio) {
00180   nassertr(!projector.is_empty() && 
00181            projector.node()->is_of_type(LensNode::get_class_type()),
00182            NULL);
00183   LensNode *projector_node = DCAST(LensNode, projector.node());
00184   nassertr(projector_node->get_lens() != NULL, NULL);
00185 
00186   // First, get the relative coordinate space of the projector.
00187   LMatrix4f rel_mat;
00188   NodePath this_np(this);
00189   rel_mat = projector.get_transform(this_np)->get_mat();
00190 
00191   // Create a GeomNode to hold this mesh.
00192   PT(GeomNode) geom_node = new GeomNode(screen_name);
00193 
00194   // Now compute all the vertices for the screen.  These are arranged
00195   // in order from left to right and bottom to top.
00196   int num_verts = num_x_verts * num_y_verts;
00197   Lens *lens = projector_node->get_lens();
00198   float t = (distance - lens->get_near()) / (lens->get_far() - lens->get_near());
00199 
00200   float x_scale = 2.0f / (num_x_verts - 1);
00201   float y_scale = 2.0f / (num_y_verts - 1);
00202 
00203   PT(GeomVertexData) vdata = new GeomVertexData
00204     ("projectionScreen", GeomVertexFormat::get_v3n3(),
00205      Geom::UH_dynamic);
00206   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00207   GeomVertexWriter normal(vdata, InternalName::get_normal());
00208   
00209   for (int yi = 0; yi < num_y_verts; yi++) {
00210     for (int xi = 0; xi < num_x_verts; xi++) {
00211       LPoint2f film = LPoint2f((float)xi * x_scale - 1.0f,
00212                                (float)yi * y_scale - 1.0f);
00213       
00214       // Reduce the image by the fill ratio.
00215       film *= fill_ratio;
00216       
00217       LPoint3f near_point, far_point;
00218       lens->extrude(film, near_point, far_point);
00219       LPoint3f point = near_point + t * (far_point - near_point);
00220       
00221       // Normals aren't often needed on projection screens, but you
00222       // never know.
00223       LVector3f norm;
00224       lens->extrude_vec(film, norm);
00225       
00226       vertex.add_data3f(point * rel_mat);
00227       normal.add_data3f(-normalize(norm * rel_mat));
00228     }
00229   }
00230   nassertr(vdata->get_num_rows() == num_verts, NULL);
00231 
00232   // Now synthesize a triangle mesh.  We run triangle strips
00233   // horizontally across the grid.
00234   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
00235   // Fill up the index array into the vertices.  This lays out the
00236   // order of the vertices in each tristrip.
00237   int ti, si;
00238   for (ti = 1; ti < num_y_verts; ti++) {
00239     strip->add_vertex(ti * num_x_verts);
00240     for (si = 1; si < num_x_verts; si++) {
00241       strip->add_vertex((ti - 1) * num_x_verts + (si-1));
00242       strip->add_vertex(ti * num_x_verts + si);
00243     }
00244     strip->add_vertex((ti - 1) * num_x_verts + (num_x_verts-1));
00245     strip->close_primitive();
00246   }
00247 
00248   PT(Geom) geom = new Geom(vdata);
00249   geom->add_primitive(strip);
00250   
00251   geom_node->add_geom(geom);
00252 
00253   _stale = true;
00254   ++_last_screen;
00255   return geom_node;
00256 }
00257 
00258 ////////////////////////////////////////////////////////////////////
00259 //     Function: ProjectionScreen::regenerate_screen
00260 //       Access: Published
00261 //  Description: Removes all the children from the ProjectionScreen
00262 //               node, and adds the newly generated child returned by
00263 //               generate_screen().
00264 ////////////////////////////////////////////////////////////////////
00265 void ProjectionScreen::
00266 regenerate_screen(const NodePath &projector, const string &screen_name,
00267                   int num_x_verts, int num_y_verts, float distance,
00268                   float fill_ratio) {
00269   // First, remove all existing children.
00270   remove_all_children();
00271 
00272   // And attach a new child.
00273   PT(GeomNode) geom_node = 
00274     generate_screen(projector, screen_name, num_x_verts, num_y_verts, 
00275                     distance, fill_ratio);
00276   add_child(geom_node);
00277 }
00278 
00279 ////////////////////////////////////////////////////////////////////
00280 //     Function: ProjectionScreen::make_flat_mesh
00281 //       Access: Published
00282 //  Description: Generates a deep copy of the hierarchy at the
00283 //               ProjectionScreen node and below, with vertices
00284 //               flattened into two dimensions as if they were seen by
00285 //               the indicated camera node.
00286 //
00287 //               This is useful for rendering an image as seen through
00288 //               a non-linear lens.  The resulting mesh will have
00289 //               vertices in the range [-1, 1] in both x and y, and
00290 //               may be then rendered with an ordinary orthographic
00291 //               lens, to generate the effect of seeing the image
00292 //               through the specified non-linear lens.
00293 //
00294 //               The returned node has no parent; it is up to the
00295 //               caller to parent it somewhere or store it so that it
00296 //               does not get dereferenced and deleted.
00297 ////////////////////////////////////////////////////////////////////
00298 PT(PandaNode) ProjectionScreen::
00299 make_flat_mesh(const NodePath &this_np, const NodePath &camera) {
00300   nassertr(!this_np.is_empty() && this_np.node() == this, NULL);
00301   nassertr(!camera.is_empty() && 
00302            camera.node()->is_of_type(LensNode::get_class_type()),
00303            NULL);
00304   LensNode *camera_node = DCAST(LensNode, camera.node());
00305   nassertr(camera_node->get_lens() != (Lens *)NULL, NULL);
00306 
00307   // First, ensure the UV's are up-to-date.
00308   recompute_if_stale(this_np);
00309 
00310   PT(PandaNode) top = new PandaNode(get_name());
00311 
00312   LMatrix4f rel_mat;
00313   bool computed_rel_mat = false;
00314   make_mesh_children(top, this_np, camera, rel_mat, computed_rel_mat);
00315 
00316   return top;
00317 }
00318 
00319 ////////////////////////////////////////////////////////////////////
00320 //     Function: ProjectionScreen::recompute
00321 //       Access: Published
00322 //  Description: Recomputes all the UV's for geometry below the
00323 //               ProjectionScreen node, as if the texture were
00324 //               projected from the associated projector.
00325 //
00326 //               This function is normally called automatically
00327 //               whenever the relevant properties change, so it should
00328 //               not normally need to be called directly by the user.
00329 //               However, it does no harm to call this if there is any
00330 //               doubt.
00331 ////////////////////////////////////////////////////////////////////
00332 void ProjectionScreen::
00333 recompute() {
00334   NodePath this_np(NodePath::any_path(this));
00335   do_recompute(this_np);
00336 }
00337 
00338 ////////////////////////////////////////////////////////////////////
00339 //     Function: ProjectionScreen::recompute_if_stale
00340 //       Access: Public
00341 //  Description: Calls recompute() only if the relative transform
00342 //               between the ProjectionScreen and the projector has
00343 //               changed, or if any other relevant property has
00344 //               changed.
00345 ////////////////////////////////////////////////////////////////////
00346 void ProjectionScreen::
00347 recompute_if_stale(const NodePath &this_np) {
00348   nassertv(!this_np.is_empty() && this_np.node() == this);
00349 
00350   if (_projector_node != (LensNode *)NULL && 
00351       _projector_node->get_lens() != (Lens *)NULL) {
00352     UpdateSeq lens_change = _projector_node->get_lens()->get_last_change();
00353     if (_stale || lens_change != _projector_lens_change) {
00354       recompute();
00355 
00356     } else {
00357       // Get the relative transform to ensure it hasn't changed.
00358       CPT(TransformState) transform = this_np.get_transform(_projector);
00359       const LMatrix4f &top_mat = transform->get_mat();
00360       if (!_rel_top_mat.almost_equal(top_mat)) {
00361         _rel_top_mat = top_mat;
00362         _computed_rel_top_mat = true;
00363         do_recompute(this_np);
00364       }
00365     }
00366   }
00367 }
00368 
00369 ////////////////////////////////////////////////////////////////////
00370 //     Function: ProjectionScreen::do_recompute
00371 //       Access: Private
00372 //  Description: Starts the recomputation process.
00373 ////////////////////////////////////////////////////////////////////
00374 void ProjectionScreen::
00375 do_recompute(const NodePath &this_np) {
00376   if (_projector_node != (LensNode *)NULL && 
00377       _projector_node->get_lens() != (Lens *)NULL) {
00378 
00379     recompute_node(this_np, _rel_top_mat, _computed_rel_top_mat);
00380     // Make sure this flag is set to false for next time.
00381     _computed_rel_top_mat = false;
00382 
00383     _projector_lens_change = _projector_node->get_lens()->get_last_change();
00384     _stale = false;
00385   }
00386 }
00387 
00388 ////////////////////////////////////////////////////////////////////
00389 //     Function: ProjectionScreen::recompute_node
00390 //       Access: Private
00391 //  Description: Recurses over all geometry at the indicated node and
00392 //               below, looking for GeomNodes that want to have new
00393 //               UV's computed.  When a new transform space is
00394 //               encountered, a new relative matrix is computed.
00395 ////////////////////////////////////////////////////////////////////
00396 void ProjectionScreen::
00397 recompute_node(const WorkingNodePath &np, LMatrix4f &rel_mat,
00398                bool &computed_rel_mat) {
00399   PandaNode *node = np.node();
00400   if (node->is_geom_node()) {
00401     recompute_geom_node(np, rel_mat, computed_rel_mat);
00402   }
00403 
00404   if (node->is_exact_type(SwitchNode::get_class_type())) {
00405     // We make a special case for switch nodes only.  Other kinds of
00406     // selective child nodes, like LOD's and sequence nodes, will get
00407     // all of their children traversed; switch nodes will only
00408     // traverse the currently active child.
00409     int i = DCAST(SwitchNode, node)->get_visible_child();
00410     if (i >= 0 && i < node->get_num_children()) {
00411       PandaNode *child = node->get_child(i);
00412       recompute_child(WorkingNodePath(np, child), rel_mat, computed_rel_mat);
00413     }
00414 
00415   } else {
00416     // A non-switch node.  Recurse on all children.
00417     int num_children = node->get_num_children();
00418     for (int i = 0; i < num_children; i++) {
00419       PandaNode *child = node->get_child(i);
00420       recompute_child(WorkingNodePath(np, child), rel_mat, computed_rel_mat);
00421     }
00422   }
00423 }
00424 
00425 ////////////////////////////////////////////////////////////////////
00426 //     Function: ProjectionScreen::recompute_child
00427 //       Access: Private
00428 //  Description: Works in conjunction with recompute_node() to recurse
00429 //               over the whole graph.  This is called on each child
00430 //               of a given node.
00431 ////////////////////////////////////////////////////////////////////
00432 void ProjectionScreen::
00433 recompute_child(const WorkingNodePath &np, LMatrix4f &rel_mat,
00434                 bool &computed_rel_mat) {
00435   PandaNode *child = np.node();
00436 
00437   const TransformState *transform = child->get_transform();
00438   if (!transform->is_identity()) {
00439     // This child node has a transform; therefore, we must recompute
00440     // the relative matrix from this point.
00441     LMatrix4f new_rel_mat;
00442     bool computed_new_rel_mat = false;
00443 
00444     if (distort_cat.is_spam()) {
00445       distort_cat.spam()
00446         << "Saving rel_mat " << (void *)&new_rel_mat << " at " << np << "\n";
00447     }
00448 
00449     recompute_node(np, new_rel_mat, computed_new_rel_mat);
00450     
00451   } else {
00452     // This child has no transform, so we can use the same transform
00453     // space from before.
00454     recompute_node(np, rel_mat, computed_rel_mat);
00455   }
00456 }
00457 
00458 ////////////////////////////////////////////////////////////////////
00459 //     Function: ProjectionScreen::recompute_geom_node
00460 //       Access: Private
00461 //  Description: Recomputes the UV's just for the indicated GeomNode.
00462 ////////////////////////////////////////////////////////////////////
00463 void ProjectionScreen::
00464 recompute_geom_node(const WorkingNodePath &np, LMatrix4f &rel_mat, 
00465                     bool &computed_rel_mat) {
00466   GeomNode *node = DCAST(GeomNode, np.node());
00467   if (!computed_rel_mat) {
00468     // All right, time to compute the matrix.
00469     NodePath true_np = np.get_node_path();
00470     rel_mat = true_np.get_transform(_projector)->get_mat();
00471     computed_rel_mat = true;
00472 
00473     if (distort_cat.is_spam()) {
00474       distort_cat.spam()
00475         << "Computing rel_mat " << (void *)&rel_mat << " at " << np << "\n";
00476       distort_cat.spam()
00477         << "  " << rel_mat << "\n";
00478     }
00479   } else {
00480     if (distort_cat.is_spam()) {
00481       distort_cat.spam()
00482         << "Applying rel_mat " << (void *)&rel_mat << " to " << np << "\n";
00483     }
00484   }
00485 
00486   int num_geoms = node->get_num_geoms();
00487   for (int i = 0; i < num_geoms; i++) {
00488     Geom *geom = node->modify_geom(i);
00489     recompute_geom(geom, rel_mat);
00490   }
00491 }
00492 
00493 ////////////////////////////////////////////////////////////////////
00494 //     Function: ProjectionScreen::recompute_geom
00495 //       Access: Private
00496 //  Description: Recomputes the UV's just for the indicated Geom.
00497 ////////////////////////////////////////////////////////////////////
00498 void ProjectionScreen::
00499 recompute_geom(Geom *geom, const LMatrix4f &rel_mat) {
00500   static const LMatrix4f lens_to_uv
00501     (0.5f, 0.0f, 0.0f, 0.0f,
00502      0.0f, 0.5f, 0.0f, 0.0f, 
00503      0.0f, 0.0f, 0.5f, 0.0f, 
00504      0.5f, 0.5f, 0.5f, 1.0f);
00505 
00506   static const LMatrix4f lens_to_uv_inverted
00507     (0.5f, 0.0f, 0.0f, 0.0f,
00508      0.0f,-0.5f, 0.0f, 0.0f, 
00509      0.0f, 0.0f, 0.5f, 0.0f, 
00510      0.5f, 0.5f, 0.5f, 1.0f);
00511 
00512   Thread *current_thread = Thread::get_current_thread();
00513 
00514   Lens *lens = _projector_node->get_lens();
00515   nassertv(lens != (Lens *)NULL);
00516 
00517   const LMatrix4f &to_uv = _invert_uvs ? lens_to_uv_inverted : lens_to_uv;
00518 
00519   // Iterate through all the vertices in the Geom.
00520 
00521   CPT(GeomVertexData) vdata = geom->get_vertex_data();
00522   if (!vdata->has_column(_texcoord_name)) {
00523     // We need to add a new column for the new texcoords.
00524     vdata = vdata->replace_column
00525       (_texcoord_name, 3, Geom::NT_float32, Geom::C_texcoord);
00526     geom->set_vertex_data(vdata);
00527   }
00528   if (_vignette_on && !vdata->has_column(InternalName::get_color())) {
00529     // We need to add a column for color.
00530     vdata = vdata->replace_column
00531       (InternalName::get_color(), 1, Geom::NT_packed_dabc, Geom::C_color);
00532     geom->set_vertex_data(vdata);
00533   }
00534 
00535   // Clear the vdata pointer so we don't force a copy in the below.
00536   vdata.clear();
00537 
00538   PT(GeomVertexData) modify_vdata = geom->modify_vertex_data();
00539 
00540   // Maybe the vdata has animation that we should consider.
00541   CPT(GeomVertexData) animated_vdata = geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread);
00542 
00543   GeomVertexWriter texcoord(modify_vdata, _texcoord_name, current_thread);
00544   GeomVertexWriter color(modify_vdata, current_thread);
00545   GeomVertexReader vertex(animated_vdata, InternalName::get_vertex(), current_thread);
00546   
00547   if (_vignette_on) {
00548     color.set_column(InternalName::get_color());
00549   }
00550   
00551   while (!vertex.is_at_end()) {
00552     Vertexf vert = vertex.get_data3f();
00553     
00554     // For each vertex, project to the film plane.
00555     LPoint3f film(0.0f, 0.0f, 0.0f);
00556     bool good = lens->project(vert * rel_mat, film);
00557     
00558     // Now the lens gives us coordinates in the range [-1, 1].
00559     // Rescale these to [0, 1].
00560     texcoord.set_data3f(film * to_uv);
00561     
00562     // If we have vignette color in effect, color the vertex according
00563     // to whether it fell in front of the lens or not.
00564     if (_vignette_on) {
00565       if (good) {
00566         color.set_data4f(_frame_color);
00567       } else {
00568         color.set_data4f(_vignette_color);
00569       }
00570     }
00571   }
00572 }
00573 
00574 ////////////////////////////////////////////////////////////////////
00575 //     Function: ProjectionScreen::make_mesh_node
00576 //       Access: Private
00577 //  Description: Recurses over all geometry at the indicated node and
00578 //               below, and generates a corresponding node hierarchy
00579 //               with all the geometry copied, but flattened into 2-d,
00580 //               as seen from the indicated camera.  Returns the newly
00581 //               created node, or NULL if no node was created.
00582 ////////////////////////////////////////////////////////////////////
00583 PandaNode *ProjectionScreen::
00584 make_mesh_node(PandaNode *result_parent, const WorkingNodePath &np,
00585                const NodePath &camera,
00586                LMatrix4f &rel_mat, bool &computed_rel_mat) {
00587   PandaNode *node = np.node();
00588   if (!node->safe_to_flatten()) {
00589     // If we can't safely flatten this node, ignore it (and all of its
00590     // children) completely.  It's got no business being here anyway.
00591     return NULL;
00592   }
00593 
00594   PT(PandaNode) new_node;
00595   if (node->is_geom_node()) {
00596     new_node = make_mesh_geom_node(np, camera, rel_mat, computed_rel_mat);
00597   } else {
00598     new_node = node->make_copy();
00599   }
00600 
00601   // Now attach the new node to the result.
00602   result_parent->add_child(new_node);
00603   make_mesh_children(new_node, np, camera, rel_mat, computed_rel_mat);
00604   return new_node;
00605 }
00606 
00607 ////////////////////////////////////////////////////////////////////
00608 //     Function: ProjectionScreen::make_mesh_children
00609 //       Access: Private
00610 //  Description: Walks over the list of children for the indicated
00611 //               node, calling make_mesh_node() on each one.
00612 ////////////////////////////////////////////////////////////////////
00613 void ProjectionScreen::
00614 make_mesh_children(PandaNode *new_node, const WorkingNodePath &np,
00615                    const NodePath &camera,
00616                    LMatrix4f &rel_mat, bool &computed_rel_mat) {
00617   PandaNode *node = np.node();
00618   int num_children = node->get_num_children();
00619   for (int i = 0; i < num_children; i++) {
00620     PandaNode *child = node->get_child(i);
00621     PandaNode *new_child;
00622 
00623     const TransformState *transform = child->get_transform();
00624     if (!transform->is_identity()) {
00625       // This child node has a transform; therefore, we must recompute
00626       // the relative matrix from this point.
00627       LMatrix4f new_rel_mat;
00628       bool computed_new_rel_mat = false;
00629       new_child = make_mesh_node(new_node, WorkingNodePath(np, child), camera,
00630                                  new_rel_mat, computed_new_rel_mat);
00631 
00632     } else {
00633       // This child has no transform, so we can use the same transform
00634       // space from before.
00635       new_child = make_mesh_node(new_node, WorkingNodePath(np, child), camera,
00636                                  rel_mat, computed_rel_mat);
00637     }
00638 
00639     // Copy all of the render state (except TransformState) to the
00640     // new arc.
00641     new_child->set_state(child->get_state());
00642   }
00643 }
00644 
00645 ////////////////////////////////////////////////////////////////////
00646 //     Function: ProjectionScreen::make_mesh_geom_node
00647 //       Access: Private
00648 //  Description: Makes a new GeomNode, just like the given one, except
00649 //               flattened into two dimensions as seen by the
00650 //               indicated camera.
00651 ////////////////////////////////////////////////////////////////////
00652 PT(GeomNode) ProjectionScreen::
00653 make_mesh_geom_node(const WorkingNodePath &np, const NodePath &camera,
00654                     LMatrix4f &rel_mat, bool &computed_rel_mat) {
00655   GeomNode *node = DCAST(GeomNode, np.node());
00656   PT(GeomNode) new_node = new GeomNode(node->get_name());
00657   LensNode *lens_node = DCAST(LensNode, camera.node());
00658 
00659   if (!computed_rel_mat) {
00660     // All right, time to compute the matrix.
00661     NodePath true_np = np.get_node_path();
00662     rel_mat = true_np.get_transform(camera)->get_mat();
00663     computed_rel_mat = true;
00664   }
00665 
00666   int num_geoms = node->get_num_geoms();
00667   for (int i = 0; i < num_geoms; i++) {
00668     const Geom *geom = node->get_geom(i);
00669     PT(Geom) new_geom = 
00670       make_mesh_geom(geom, lens_node->get_lens(), rel_mat);
00671     if (new_geom != (Geom *)NULL) {
00672       new_node->add_geom(new_geom, node->get_geom_state(i));
00673     }
00674   }
00675 
00676   return new_node;
00677 }
00678 
00679 ////////////////////////////////////////////////////////////////////
00680 //     Function: ProjectionScreen::make_mesh_geom
00681 //       Access: Private
00682 //  Description: Makes a new Geom, just like the given one, except
00683 //               flattened into two dimensions as seen by the
00684 //               indicated lens.  Any triangle in the original mesh
00685 //               that involves an unprojectable vertex is eliminated.
00686 ////////////////////////////////////////////////////////////////////
00687 PT(Geom) ProjectionScreen::
00688 make_mesh_geom(const Geom *geom, Lens *lens, LMatrix4f &rel_mat) {
00689   PT(Geom) new_geom = geom->make_copy();
00690 
00691   GeomVertexRewriter vertex(new_geom->modify_vertex_data(), 
00692                               InternalName::get_vertex());
00693   while (!vertex.is_at_end()) {
00694     Vertexf vert = vertex.get_data3f();
00695     
00696     // Project each vertex into the film plane, but use three
00697     // dimensions so the Z coordinate remains meaningful.
00698     LPoint3f film(0.0f, 0.0f, 0.0f);
00699     lens->project(vert * rel_mat, film);
00700     
00701     vertex.set_data3f(film);
00702   }      
00703   
00704   return new_geom;
00705 }
 All Classes Functions Variables Enumerations