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