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