Panda3D
|
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 ©) : 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 }