|
|
@@ -27,6 +27,9 @@
|
|
|
#include "panda_getopt.h"
|
|
|
#include "preprocess_argv.h"
|
|
|
#include "graphicsPipeSelection.h"
|
|
|
+#include "asyncTaskManager.h"
|
|
|
+#include "asyncTask.h"
|
|
|
+#include "boundingSphere.h"
|
|
|
|
|
|
// By including checkPandaVersion.h, we guarantee that runtime
|
|
|
// attempts to run pview will fail if it inadvertently links with the
|
|
|
@@ -234,6 +237,111 @@ report_version() {
|
|
|
nout << "\n";
|
|
|
}
|
|
|
|
|
|
+// Task that dynamically adjusts the camera len's near/far clipping
|
|
|
+// planes to ensure the user can zoom in as close as needed to a model.
|
|
|
+//
|
|
|
+// Code adapted from WindowFramework::center_trackball(), but
|
|
|
+// without moving the camera. When the camera is inside the model,
|
|
|
+// the near clip is set to near-zero.
|
|
|
+//
|
|
|
+class AdjustCameraClipPlanesTask : public AsyncTask {
|
|
|
+public:
|
|
|
+ AdjustCameraClipPlanesTask(const string &name, Camera *camera) :
|
|
|
+ AsyncTask(name), _camera(camera), _lens(camera->get_lens(0)), _sphere(NULL)
|
|
|
+ {
|
|
|
+ NodePath np = framework.get_models();
|
|
|
+ PT(BoundingVolume) volume = np.get_bounds();
|
|
|
+
|
|
|
+ // We expect at least a geometric bounding volume around the world.
|
|
|
+ nassertv(volume != (BoundingVolume *)NULL);
|
|
|
+ nassertv(volume->is_of_type(GeometricBoundingVolume::get_class_type()));
|
|
|
+ CPT(GeometricBoundingVolume) gbv = DCAST(GeometricBoundingVolume, volume);
|
|
|
+
|
|
|
+ if (np.has_parent()) {
|
|
|
+ CPT(TransformState) net_transform = np.get_parent().get_net_transform();
|
|
|
+ PT(GeometricBoundingVolume) new_gbv = DCAST(GeometricBoundingVolume, gbv->make_copy());
|
|
|
+ new_gbv->xform(net_transform->get_mat());
|
|
|
+ gbv = new_gbv;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Determine the bounding sphere around the object.
|
|
|
+ if (gbv->is_infinite()) {
|
|
|
+ framework_cat.warning()
|
|
|
+ << "Infinite bounding volume for " << np << "\n";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (gbv->is_empty()) {
|
|
|
+ framework_cat.warning()
|
|
|
+ << "Empty bounding volume for " << np << "\n";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // The BoundingVolume might be a sphere (it's likely), but since it
|
|
|
+ // might not, we'll take no chances and make our own sphere.
|
|
|
+ _sphere = new BoundingSphere(gbv->get_approx_center(), 0.0f);
|
|
|
+ if (!_sphere->extend_by(gbv)) {
|
|
|
+ framework_cat.warning()
|
|
|
+ << "Cannot determine bounding volume of " << np << "\n";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ALLOC_DELETED_CHAIN(AdjustCameraClipPlanesTask);
|
|
|
+
|
|
|
+ virtual DoneStatus do_task() {
|
|
|
+ if (!_sphere) {
|
|
|
+ return DS_done;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (framework.get_num_windows() == 0) {
|
|
|
+ return DS_cont;
|
|
|
+ }
|
|
|
+
|
|
|
+ WindowFramework *wf = framework.get_window(0);
|
|
|
+ if (!wf) {
|
|
|
+ return DS_cont;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get current camera position.
|
|
|
+ NodePath cameraNP = wf->get_camera_group();
|
|
|
+ LPoint3 pos = cameraNP.get_pos();
|
|
|
+
|
|
|
+ // See how far or close the camera is
|
|
|
+ LPoint3 center = _sphere->get_center();
|
|
|
+ PN_stdfloat radius = _sphere->get_radius();
|
|
|
+
|
|
|
+ PN_stdfloat min_distance = 0.001 * radius;
|
|
|
+
|
|
|
+ // Choose a suitable distance to view the whole volume in our frame.
|
|
|
+ // This is based on the camera lens in use.
|
|
|
+ PN_stdfloat distance;
|
|
|
+ CPT(GeometricBoundingVolume) gbv = DCAST(GeometricBoundingVolume, _sphere);
|
|
|
+ if (gbv->contains(pos)) {
|
|
|
+ // See as up-close to the model as possible
|
|
|
+ distance = min_distance;
|
|
|
+ } else {
|
|
|
+ // View from a distance
|
|
|
+ distance = (center - pos).length();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ensure the far plane is far enough back to see the entire object.
|
|
|
+ PN_stdfloat ideal_far_plane = distance + radius * 1.5;
|
|
|
+ _lens->set_far(max(_lens->get_default_far(), ideal_far_plane));
|
|
|
+
|
|
|
+ // And that the near plane is far enough forward, but if inside
|
|
|
+ // the sphere, keep above 0.
|
|
|
+ PN_stdfloat ideal_near_plane = max(min_distance * 10, distance - radius);
|
|
|
+ _lens->set_near(min(_lens->get_default_near(), ideal_near_plane));
|
|
|
+
|
|
|
+ return DS_cont;
|
|
|
+ }
|
|
|
+
|
|
|
+ Camera *_camera;
|
|
|
+ Lens *_lens;
|
|
|
+ PT(BoundingSphere) _sphere;
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
int
|
|
|
main(int argc, char **argv) {
|
|
|
// A call to pystub() to force libpystub.so to be linked in.
|
|
|
@@ -389,6 +497,9 @@ main(int argc, char **argv) {
|
|
|
window->set_anim_controls(true);
|
|
|
}
|
|
|
|
|
|
+ PT(AdjustCameraClipPlanesTask) task = new AdjustCameraClipPlanesTask("Adjust Camera Bounds", window->get_camera(0));
|
|
|
+ framework.get_task_mgr().add(task);
|
|
|
+
|
|
|
framework.enable_default_keys();
|
|
|
framework.define_key("shift-w", "open a new window", event_W, NULL);
|
|
|
framework.define_key("shift-f", "flatten hierarchy", event_F, NULL);
|