00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00032
00033
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
00053
00054
00055
00056 ProjectionScreen::
00057 ~ProjectionScreen() {
00058 }
00059
00060
00061
00062
00063
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
00082
00083
00084
00085
00086
00087
00088 PandaNode *ProjectionScreen::
00089 make_copy() const {
00090 return new ProjectionScreen(*this);
00091 }
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
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
00128
00129
00130
00131
00132
00133
00134
00135
00136
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
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
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
00191 LMatrix4 rel_mat;
00192 NodePath this_np(this);
00193 rel_mat = projector.get_transform(this_np)->get_mat();
00194
00195
00196 PT(GeomNode) geom_node = new GeomNode(screen_name);
00197
00198
00199
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
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
00226
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
00237
00238 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_static);
00239
00240
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
00264
00265
00266
00267
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
00274 remove_all_children();
00275
00276
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
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
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
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
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336 void ProjectionScreen::
00337 recompute() {
00338 NodePath this_np(NodePath::any_path(this));
00339 do_recompute(this_np);
00340 }
00341
00342
00343
00344
00345
00346
00347
00348
00349
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
00359
00360
00361
00362
00363
00364
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
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
00395
00396
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
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
00414
00415
00416
00417
00418
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
00430
00431
00432
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
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
00451
00452
00453
00454
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
00464
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
00477
00478 recompute_node(np, rel_mat, computed_rel_mat);
00479 }
00480 }
00481
00482
00483
00484
00485
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
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
00519
00520
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
00544
00545 CPT(GeomVertexData) vdata = geom->get_vertex_data();
00546 if (!vdata->has_column(_texcoord_name)) {
00547
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
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
00560 vdata.clear();
00561
00562 PT(GeomVertexData) modify_vdata = geom->modify_vertex_data();
00563
00564
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
00579 LPoint3 film(0.0f, 0.0f, 0.0f);
00580 bool good = lens->project(vert * rel_mat, film);
00581
00582
00583
00584 texcoord.set_data3(film * to_uv);
00585
00586
00587
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
00600
00601
00602
00603
00604
00605
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
00614
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
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
00633
00634
00635
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
00650
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
00658
00659 new_child = make_mesh_node(new_node, WorkingNodePath(np, child), camera,
00660 rel_mat, computed_rel_mat);
00661 }
00662
00663
00664
00665 new_child->set_state(child->get_state());
00666 }
00667 }
00668
00669
00670
00671
00672
00673
00674
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
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
00705
00706
00707
00708
00709
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
00721
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 }