Dynamically adjust camera clip range in pview

This commit is contained in:
Ed Swartz 2015-05-11 12:10:18 -05:00
parent 07ea84a1a0
commit e9fa116d17
1 changed files with 111 additions and 0 deletions

View File

@ -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);