Panda3D
 All Classes Functions Variables Enumerations
pview.cxx
1 // Filename: pview.cxx
2 // Created by: drose (25Feb02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pandaFramework.h"
16 #include "pandaSystem.h"
17 #include "pystub.h"
18 #include "textNode.h"
19 #include "configVariableBool.h"
20 #include "texturePool.h"
21 #include "multitexReducer.h"
22 #include "sceneGraphReducer.h"
23 #include "partGroup.h"
24 #include "cardMaker.h"
25 #include "bamCache.h"
26 #include "virtualFileSystem.h"
27 #include "panda_getopt.h"
28 #include "preprocess_argv.h"
29 #include "graphicsPipeSelection.h"
30 
31 // By including checkPandaVersion.h, we guarantee that runtime
32 // attempts to run pview will fail if it inadvertently links with the
33 // wrong version of libdtool.so/.dll.
34 
35 #include "checkPandaVersion.h"
36 
37 PandaFramework framework;
38 
39 ConfigVariableBool pview_test_hack
40 ("pview-test-hack", false,
41  "Enable the '0' key in pview to run whatever hacky test happens to be in "
42  "there right now.");
43 
44 bool
45 output_screenshot(Filename &fn)
46 {
47  Thread *current_thread = Thread::get_current_thread();
48 
49  // Only one frame crashes.
50  framework.do_frame(current_thread);
51  framework.do_frame(current_thread);
52 
53  WindowFramework *wf = framework.get_window(0);
54  bool ok = wf->get_graphics_output()->save_screenshot(fn, "from pview");
55  if (!ok) {
56  cerr << "Could not generate screenshot " << fn << "\n";
57  }
58  return ok;
59 }
60 
61 void
62 event_W(const Event *, void *) {
63  // shift-W: open a new window on the same scene.
64 
65  // If we already have a window, use the same GSG.
66  GraphicsPipe *pipe = (GraphicsPipe *)NULL;
68 
69  if (framework.get_num_windows() > 0) {
70  WindowFramework *old_window = framework.get_window(0);
71  GraphicsOutput *win = old_window->get_graphics_output();
72  pipe = win->get_pipe();
73  // gsg = win->get_gsg();
74  }
75 
76  WindowFramework *window = framework.open_window(pipe, gsg);
77  if (window != (WindowFramework *)NULL) {
78  window->enable_keyboard();
79  window->setup_trackball();
80  framework.get_models().instance_to(window->get_render());
81  }
82 }
83 
84 void
85 event_F(const Event *, void *) {
86  // shift-F: flatten the model hierarchy.
87  framework.get_models().flatten_strong();
88 }
89 
90 void
91 event_Enter(const Event *, void *) {
92  // alt-enter: toggle between window/fullscreen in the same scene.
93 
94  // If we already have a window, use the same GSG.
95  GraphicsPipe *pipe = (GraphicsPipe *)NULL;
97 
98  WindowProperties props;
99 
100  for (int i = 0; i < framework.get_num_windows(); ++i) {
101  WindowFramework *old_window = framework.get_window(i);
102  GraphicsWindow *win = old_window->get_graphics_window();
103  if (win != (GraphicsWindow *)NULL) {
104  pipe = win->get_pipe();
105  gsg = win->get_gsg();
106  props = win->get_properties();
107  framework.close_window(old_window);
108  break;
109  }
110  }
111 
112  // set the toggle
113  props.set_fullscreen(!props.get_fullscreen());
114  int flags = GraphicsPipe::BF_require_window;
115 
116  WindowFramework *window = framework.open_window(props, flags, pipe, gsg);
117  if (window != (WindowFramework *)NULL) {
118  window->enable_keyboard();
119  window->setup_trackball();
120  framework.get_models().instance_to(window->get_render());
121  }
122 }
123 
124 void
125 event_2(const Event *event, void *) {
126  // 2: split the window into two display regions.
127 
128  EventParameter param = event->get_parameter(0);
129  WindowFramework *wf;
130  DCAST_INTO_V(wf, param.get_ptr());
131 
132  WindowFramework *split = wf->split_window();
133  if (split != (WindowFramework *)NULL) {
134  split->enable_keyboard();
135  split->setup_trackball();
136  framework.get_models().instance_to(split->get_render());
137  }
138 }
139 
140 void
141 event_0(const Event *event, void *) {
142  // 0: run hacky test.
143  EventParameter param = event->get_parameter(0);
144  WindowFramework *wf;
145  DCAST_INTO_V(wf, param.get_ptr());
146 
147  // Create a new offscreen buffer.
148  GraphicsOutput *win = wf->get_graphics_output();
149  PT(GraphicsOutput) buffer = win->make_texture_buffer("tex", 256, 256);
150  cerr << buffer->get_type() << "\n";
151 
152  // Set the offscreen buffer to render the same scene as the main camera.
153  DisplayRegion *dr = buffer->make_display_region();
154  dr->set_camera(NodePath(wf->get_camera(0)));
155 
156  // Make the clear color on the buffer be yellow, so it's obviously
157  // different from the main scene's background color.
158  buffer->set_clear_color(LColor(1, 1, 0, 0));
159 
160  // Apply the offscreen buffer's texture to a card in the main
161  // window.
162  CardMaker cm("card");
163  cm.set_frame(0, 1, 0, 1);
164  NodePath card_np(cm.generate());
165 
166  card_np.reparent_to(wf->get_render_2d());
167  card_np.set_texture(buffer->get_texture());
168 }
169 
170 void
171 usage() {
172  cerr <<
173  "\n"
174  "Usage: pview [opts] model [model ...]\n"
175  " pview -h\n\n";
176 }
177 
178 void
179 help() {
180  usage();
181  cerr <<
182  "pview opens a quick Panda window for viewing one or more models and/or\n"
183  "animations.\n\n"
184 
185  "Options:\n\n"
186 
187  " -a\n"
188  " Convert and play animations, if loading an external file type\n"
189  " (like .mb) directly and if the converter supports animations.\n"
190  " Also implicitly enables the animation controls.\n\n"
191 
192  " -c\n"
193  " Automatically center models within the viewing window on startup.\n"
194  " This can also be achieved with the 'c' hotkey at runtime.\n\n"
195 
196  " -l\n"
197  " Open the window before loading any models with the text \"Loading\"\n"
198  " displayed in the window. The default is not to open the window\n"
199  " until all models are loaded.\n\n"
200 
201  " -i\n"
202  " Ignore bundle/group names. Normally, the <group> name must match\n"
203  " the <bundle> name, or the animation will not be used.\n\n"
204 
205  " -s filename\n"
206  " After displaying the models, immediately take a screenshot and\n"
207  " exit.\n\n"
208 
209  " -D\n"
210  " Delete the model files after loading them (presumably this option\n"
211  " will only be used when loading a temporary model file).\n\n"
212 
213  " -L\n"
214  " Enable lighting in the scene. This can also be achieved with\n"
215  " the 'l' hotkey at runtime.\n\n"
216 
217  " -P <pipe>\n"
218  " Select the given graphics pipe for the window, rather than using\n"
219  " the platform default. The allowed values for <pipe> are those\n"
220  " from the Config.prc variables 'load-display' and 'aux-display'.\n\n"
221 
222  " -V\n"
223  " Report the current version of Panda, and exit.\n\n"
224 
225  " -h\n"
226  " Display this help text.\n\n";
227 }
228 
229 void
230 report_version() {
231  nout << "\n";
233  ps->write(nout);
234  nout << "\n";
235 }
236 
237 int
238 main(int argc, char **argv) {
239  // A call to pystub() to force libpystub.so to be linked in.
240  pystub();
241 
242  preprocess_argv(argc, argv);
243  framework.open_framework(argc, argv);
244  framework.set_window_title("Panda Viewer");
245 
246  bool anim_controls = false;
247  bool auto_center = false;
248  bool show_loading = false;
249  bool auto_screenshot = false;
250  int hierarchy_match_flags = PartGroup::HMF_ok_part_extra |
251  PartGroup::HMF_ok_anim_extra;
252  Filename screenshotfn;
253  bool delete_models = false;
254  bool apply_lighting = false;
255  PointerTo<GraphicsPipe> pipe = NULL;
256 
257  extern char *optarg;
258  extern int optind;
259  static const char *optflags = "acls:DVhiLP:";
260  int flag = getopt(argc, argv, optflags);
261 
262  while (flag != EOF) {
263  switch (flag) {
264  case 'a':
265  anim_controls = true;
266  PandaFramework::_loader_options.set_flags(PandaFramework::_loader_options.get_flags() | LoaderOptions::LF_convert_anim);
267  break;
268 
269  case 'c':
270  auto_center = true;
271  break;
272 
273  case 'l':
274  show_loading = true;
275  break;
276 
277  case 'i':
278  hierarchy_match_flags |= PartGroup::HMF_ok_wrong_root_name;
279  break;
280 
281  case 's':
282  auto_screenshot = true;
283  screenshotfn = optarg;
284  break;
285 
286  case 'D':
287  delete_models = true;
288  break;
289 
290  case 'L':
291  apply_lighting = true;
292  break;
293 
294  case 'P': {
295  pipe = GraphicsPipeSelection::get_global_ptr()->make_module_pipe(optarg);
296  if (!pipe) {
297  cerr << "No such pipe '" << optarg << "' available." << endl;
298  return 1;
299  }
300  break;
301  }
302 
303  case 'V':
304  report_version();
305  return 1;
306 
307  case 'h':
308  help();
309  return 1;
310 
311  case '?':
312  usage();
313  return 1;
314 
315  default:
316  cerr << "Unhandled switch: " << flag << endl;
317  break;
318  }
319  flag = getopt(argc, argv, optflags);
320  }
321  argc -= (optind - 1);
322  argv += (optind - 1);
323 
324  WindowFramework *window = framework.open_window(pipe, NULL);
325  if (window != (WindowFramework *)NULL) {
326  // We've successfully opened a window.
327 
328  NodePath loading_np;
329 
330  if (show_loading) {
331  // Put up a "loading" message for the user's benefit.
332  NodePath aspect_2d = window->get_aspect_2d();
333  PT(TextNode) loading = new TextNode("loading");
334  loading_np = aspect_2d.attach_new_node(loading);
335  loading_np.set_scale(0.125f);
336  loading->set_text_color(1.0f, 1.0f, 1.0f, 1.0f);
337  loading->set_shadow_color(0.0f, 0.0f, 0.0f, 1.0f);
338  loading->set_shadow(0.04, 0.04);
339  loading->set_align(TextNode::A_center);
340  loading->set_text("Loading...");
341 
342  // Allow a couple of frames to go by so the window will be fully
343  // created and the text will be visible.
344  Thread *current_thread = Thread::get_current_thread();
345  framework.do_frame(current_thread);
346  framework.do_frame(current_thread);
347  }
348 
349  window->enable_keyboard();
350  window->setup_trackball();
351  framework.get_models().instance_to(window->get_render());
352  if (argc < 2) {
353  // If we have no arguments, get that trusty old triangle out.
354  window->load_default_model(framework.get_models());
355  } else {
356  window->load_models(framework.get_models(), argc, argv);
357 
358  if (delete_models) {
360  for (int i = 1; i < argc && argv[i] != (char *)NULL; i++) {
361  Filename model = Filename::from_os_specific(argv[i]);
362  if (vfs->exists(model)) {
363  nout << "Deleting " << model << "\n";
364  vfs->delete_file(model);
365  }
366  }
367  }
368  }
369  window->loop_animations(hierarchy_match_flags);
370 
371  // Make sure the textures are preloaded.
372  framework.get_models().prepare_scene(window->get_graphics_output()->get_gsg());
373 
374  loading_np.remove_node();
375 
376  if (apply_lighting) {
377  window->set_lighting(true);
378  }
379 
380  if (auto_center) {
381  window->center_trackball(framework.get_models());
382  }
383 
384  if (auto_screenshot) {
385  return(output_screenshot(screenshotfn) ? 0:1);
386  }
387 
388  if (anim_controls) {
389  window->set_anim_controls(true);
390  }
391 
392  framework.enable_default_keys();
393  framework.define_key("shift-w", "open a new window", event_W, NULL);
394  framework.define_key("shift-f", "flatten hierarchy", event_F, NULL);
395  framework.define_key("alt-enter", "toggle between window/fullscreen", event_Enter, NULL);
396  framework.define_key("2", "split the window", event_2, NULL);
397  if (pview_test_hack) {
398  framework.define_key("0", "run quick hacky test", event_0, NULL);
399  }
400  framework.main_loop();
401  framework.report_frame_rate(nout);
402  }
403 
404  framework.close_framework();
405  return (0);
406 }
bool save_screenshot(const Filename &filename, const string &image_comment="")
Saves a screenshot of the region to the indicated filename.
static PandaSystem * get_global_ptr()
Returns the global PandaSystem object.
This class is used as a namespace to group several global properties of Panda.
Definition: pandaSystem.h:29
void loop_animations(int hierarchy_match_flags=PartGroup::HMF_ok_part_extra|PartGroup::HMF_ok_anim_extra)
Looks for characters and their matching animation files in the scene graph; binds and loops any match...
void enable_default_keys()
Sets callbacks on the event handler to handle all of the normal viewer keys, like t to toggle texture...
An optional parameter associated with an event.
NodePath & get_models()
Returns the root of the scene graph normally reserved for parenting models and such.
NodePath get_render()
Returns the root of the 3-d scene graph.
void close_framework()
Should be called at the end of an application to close Panda.
const WindowProperties get_properties() const
Returns the current properties of the window.
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
This is a convenience class to specialize ConfigVariable as a boolean type.
NodePath get_aspect_2d()
Returns the node under the 2-d scene graph that is scaled to suit the window&#39;s aspect ratio...
This encapsulates the data that is normally associated with a single window, or with a single display...
void set_anim_controls(bool enable)
Creates an onscreen animation slider for frame-stepping through the animations.
void set_lighting(bool enable)
Turns lighting on (true) or off (false).
void set_fullscreen(bool fullscreen)
Specifies whether the window should be opened in fullscreen mode (true) or normal windowed mode (fals...
NodePath instance_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Adds the referenced node of the NodePath as a child of the referenced node of the indicated other Nod...
Definition: nodePath.cxx:618
int get_num_windows() const
Returns the number of windows that are currently open.
NodePath load_default_model(const NodePath &parent)
Loads our favorite blue triangle.
bool get_fullscreen() const
Returns true if the window is in fullscreen mode.
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
WindowFramework * get_window(int n) const
Returns the nth window currently open.
GraphicsPipe * get_pipe() const
Returns the GraphicsPipe that this window is associated with.
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
void prepare_scene(GraphicsStateGuardianBase *gsg)
Walks through the scene graph beginning at the bottom node, and does whatever initialization is requi...
Definition: nodePath.cxx:6088
A container for the various kinds of properties we might ask to have on a graphics window before we o...
GraphicsWindow * get_graphics_window() const
Returns a pointer to the underlying GraphicsWindow object, if it is in fact a window; or NULL if it i...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
void center_trackball(const NodePath &object)
Centers the trackball on the indicated object, and scales the trackball motion suitably.
void main_loop()
Called to yield control to the panda framework.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:58
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
void define_key(const string &event_name, const string &description, EventHandler::EventCallbackFunction *function, void *data)
Sets up a handler for the indicated key.
GraphicsStateGuardian * get_gsg() const
Returns the GSG that is associated with this window.
void enable_keyboard()
Creates a ButtonThrower to listen to button presses and throw them as events.
WindowFramework * open_window()
Opens a window on the default graphics pipe.
static GraphicsPipeSelection * get_global_ptr()
Returns a pointer to the one global GraphicsPipeSelection object.
This class generates 2-d &quot;cards&quot;, that is, rectangular polygons, particularly useful for showing text...
Definition: cardMaker.h:32
void close_window(int n)
Closes the nth window and removes it from the list.
This is a base class for the various different classes that represent the result of a frame of render...
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
void open_framework(int &argc, char **&argv)
Should be called once at the beginning of the application to initialize Panda (and the framework) for...
int flatten_strong()
The strongest possible flattening.
Definition: nodePath.cxx:6337
A thread; that is, a lightweight process.
Definition: thread.h:51
A named event, possibly with parameters.
Definition: event.h:36
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition: nodePath.cxx:757
bool load_models(const NodePath &parent, int argc, char *argv[], int first_arg=1)
Loads up all the model files listed in the indicated argument list.
The primary interface to this module.
Definition: textNode.h:52
Encapsulates all the communication with a particular instance of a given rendering backend...
void set_window_title(const string &title)
Specifies the title that is set for all subsequently created windows.
TypedWritableReferenceCount * get_ptr() const
Retrieves a pointer to the actual value stored in the parameter.
WindowFramework * split_window(SplitType split_type=ST_default)
Divides the window into two display regions, each of which gets its own trackball and keyboard events...
A rectangular subregion within a window for rendering into.
Definition: displayRegion.h:61
PointerTo is a template class which implements a smart pointer to an object derived from ReferenceCou...
Definition: pointerTo.h:79
This class serves to provide a high-level framework for basic applications that use Panda in simple w...
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
void setup_trackball()
Sets up the mouse to trackball around the camera.
void report_frame_rate(ostream &out) const
Reports the currently measured average frame rate to the indicated ostream.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
virtual bool do_frame(Thread *current_thread)
Renders one frame and performs all associated processing.
GraphicsOutput * get_graphics_output() const
Returns a pointer to the underlying GraphicsOutput object.
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
Definition: filename.cxx:332