Panda3D
pgrid.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file pgrid.cxx
10  * @author drose
11  * @date 2002-04-03
12  */
13 
14 #include "pandaFramework.h"
15 #include "pandaNode.h"
16 #include "transformState.h"
17 #include "clockObject.h"
18 #include "string_utils.h"
19 #include "pvector.h"
20 #include "panda_getopt.h"
21 #include "preprocess_argv.h"
22 
23 #define RANDFRAC (rand()/(PN_stdfloat)(RAND_MAX))
24 
25 using std::string;
26 
27 class GriddedFilename {
28 public:
29  Filename _filename;
30  int _count;
31  NodePath _model;
32 };
34 
35 typedef struct {
36  // for rot moving
37  PN_stdfloat xcenter,ycenter;
38  PN_stdfloat xoffset,yoffset;
39  PN_stdfloat ang1,ang1_vel;
40  PN_stdfloat ang2,ang2_vel;
41 
42  PN_stdfloat radius;
43 
44  // for moving
45  PN_stdfloat xstart,ystart;
46  PN_stdfloat xend,yend;
47  PN_stdfloat xdel,ydel,timedel;
48  double starttime,endtime;
49  double vel;
50  LMatrix4 rotmat;
51 
52  PandaNode *node;
53 } gridded_file_info;
54 
56 
57 typedef enum {None,Rotation,LinearMotion} GriddedMotionType;
58 
59 #define GRIDCELLSIZE 5.0
60 static int gridwidth; // cells/side
61 
62 #define MIN_WANDERAREA_DIMENSION 120.0f
63 
64 static PN_stdfloat grid_pos_offset; // origin of grid
65 static PN_stdfloat wander_area_pos_offset;
66 
67 static GriddedMotionType gridmotiontype = None;
68 
69 
70 // making these fns to get around ridiculous VC++ matrix inlining bugs at Opt2
71 static void
72 move_gridded_stuff(GriddedMotionType gridmotiontype,
73  gridded_file_info *InfoArr, int size) {
75 
76  LMatrix4 tmat1,tmat2,xfm_mat;
77 
78  for(int i = 0; i < size; i++) {
79  double time_delta = (now-InfoArr[i].starttime);
80 #define DO_FP_MODULUS(VAL,MAXVAL) \
81  {if(VAL > MAXVAL) {int idivresult = (int)(VAL / (PN_stdfloat)MAXVAL); VAL=VAL-idivresult*MAXVAL;} else \
82  if(VAL < -MAXVAL) {int idivresult = (int)(VAL / (PN_stdfloat)MAXVAL); VAL=VAL+idivresult*MAXVAL;}}
83 
84  // probably should use panda lerps for this stuff, but I don't understand
85  // how
86 
87  if(gridmotiontype==Rotation) {
88 
89  InfoArr[i].ang1=time_delta*InfoArr[i].ang1_vel;
90  DO_FP_MODULUS(InfoArr[i].ang1,360.0);
91  InfoArr[i].ang2=time_delta*InfoArr[i].ang2_vel;
92  DO_FP_MODULUS(InfoArr[i].ang2,360.0);
93 
94  // xforms happen left to right
95  LVector2 new_center = LVector2(InfoArr[i].radius,0.0) *
96  LMatrix3::rotate_mat(InfoArr[i].ang1);
97 
98  LVector3 translate_vec(InfoArr[i].xcenter+new_center._v.v._0,
99  InfoArr[i].ycenter+new_center._v.v._1,
100  0.0);
101 
102  const LVector3 rotation_axis(0.0, 0.0, 1.0);
103 
104  tmat1 = LMatrix4::rotate_mat_normaxis(InfoArr[i].ang2,rotation_axis);
105  tmat2 = LMatrix4::translate_mat(translate_vec);
106  xfm_mat = tmat1 * tmat2;
107  } else {
108 
109  PN_stdfloat xpos,ypos;
110 
111  if(now>InfoArr[i].endtime) {
112  InfoArr[i].starttime = now;
113 
114  xpos = InfoArr[i].xstart = InfoArr[i].xend;
115  ypos = InfoArr[i].ystart = InfoArr[i].yend;
116 
117  InfoArr[i].xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
118  InfoArr[i].yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
119 
120  PN_stdfloat xdel = InfoArr[i].xdel = InfoArr[i].xend-InfoArr[i].xstart;
121  PN_stdfloat ydel = InfoArr[i].ydel = InfoArr[i].yend-InfoArr[i].ystart;
122 
123  InfoArr[i].endtime = now + csqrt(xdel*xdel+ydel*ydel)/InfoArr[i].vel;
124  InfoArr[i].timedel = InfoArr[i].endtime - InfoArr[i].starttime;
125 
126  const LVector3 rotate_axis(0.0, 0.0, 1.0);
127 
128  PN_stdfloat ang = rad_2_deg(atan2(-xdel,ydel));
129 
130  InfoArr[i].rotmat= LMatrix4::rotate_mat_normaxis(ang,rotate_axis);
131  } else {
132  PN_stdfloat timefrac= time_delta/InfoArr[i].timedel;
133 
134  xpos = InfoArr[i].xdel*timefrac+InfoArr[i].xstart;
135  ypos = InfoArr[i].ydel*timefrac+InfoArr[i].ystart;
136  }
137 
138  LVector3 translate_vec(xpos, ypos, 0.0);
139  LMatrix4 tmat2 = LMatrix4::translate_mat(translate_vec);
140 
141  xfm_mat = InfoArr[i].rotmat * tmat2;
142  }
143  InfoArr[i].node->set_transform(TransformState::make_mat(xfm_mat));
144  }
145 }
146 
147 bool
148 get_command_line_opts(int &argc, char **&argv) {
149  // Use getopt() to decode the optional command-line parameters. extern char
150  // *optarg;
151  extern int optind;
152  const char *options = "rm";
153  int flag = getopt(argc, argv, options);
154  while (flag != EOF) {
155  switch (flag) {
156  case 'r':
157  gridmotiontype = Rotation;
158  break;
159 
160  case 'm':
161  gridmotiontype = LinearMotion;
162  break;
163 
164  case '?':
165  nout << "Invalid parameter.\n";
166  return false;
167  }
168 
169  flag = getopt(argc, argv, options);
170  }
171 
172  argv += (optind - 1);
173  argc -= (optind - 1);
174 
175  return true;
176 }
177 
178 void
179 get_command_line_filenames(int argc, char *argv[],
180  pvector<Filename> &static_filenames,
181  GriddedFilenames &gridded_filenames) {
182  for (int i = 1; i < argc && argv[i] != nullptr; i++) {
183  const string &arg = argv[i];
184  size_t comma = arg.find(',');
185  if (comma == string::npos) {
186  // No comma in the filename, so it must be an ordinary static file.
187  static_filenames.push_back(Filename::from_os_specific(arg));
188 
189  } else {
190  // A comma in the filename indicates a gridded file. The syntax is
191  // filename,count where count represents the number of times the file is
192  // repeated.
193  string name = arg.substr(0, comma);
194  string count_str = arg.substr(comma + 1);
195  int count;
196  if (!string_to_int(count_str, count)) {
197  nout << "Ignoring invalid number: " << count_str << "\n";
198  count = 1;
199  } else if (count <= 0) {
200  nout << "Ignoring inappropriate number: " << count << "\n";
201  count = 1;
202  }
203 
204  GriddedFilename gf;
205  gf._filename = Filename::from_os_specific(name);
206  gf._count = count;
207  gridded_filenames.push_back(gf);
208  }
209  }
210 }
211 
212 void
213 load_gridded_models(WindowFramework *window,
214  GriddedFilenames &filenames,
215  GriddedInfoArray &info_arr) {
216  // Load up all the files indicated in the list of gridded filenames and
217  // store them in the given vector.
218 
219  Loader loader;
220  LoaderOptions options;
221  // options.set_flags(options.get_flags() | LoaderOptions::LF_no_ram_cache);
222 
223  // First, load up each model from disk once, and store them all separate
224  // from the scene graph. Also count up the total number of models we'll be
225  // putting in the grid.
226  int grid_count = 0;
227  GriddedFilenames::iterator fi;
228  for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
229  GriddedFilename &gf = (*fi);
230  PT(PandaNode) node = loader.load_sync(gf._filename, options);
231  if (node != nullptr) {
232  gf._model = NodePath(node);
233  grid_count += gf._count;
234  }
235  }
236 
237  info_arr.clear();
238  info_arr.reserve(grid_count);
239 
240  // Compute the integer square root of grid_count, so that we put our models
241  // in a nice square grid.
242 
243  gridwidth=1;
244  while(gridwidth*gridwidth < grid_count) {
245  gridwidth++;
246  }
247 
248  grid_pos_offset = -gridwidth*GRIDCELLSIZE/2.0;
249  wander_area_pos_offset = -std::max((PN_stdfloat)fabs(grid_pos_offset), MIN_WANDERAREA_DIMENSION/2.0f);
250 
251  // Now walk through the list again, copying models into the scene graph as
252  // we go.
253 
254  PN_stdfloat xpos = grid_pos_offset;
255  PN_stdfloat ypos = grid_pos_offset;
256 
257  srand( (unsigned)time( nullptr ) );
259 
260  int model_count = 0;
261  int passnum = 0;
262  bool loaded_any;
263 
264  NodePath root = window->get_panda_framework()->get_models();
265  do {
266  loaded_any = false;
267 
268  for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
269  const GriddedFilename &gf = (*fi);
270  if (!gf._model.is_empty() && gf._count > passnum) {
271  loaded_any = true;
272  // Copy this model into the scene graph, and assign it a position on
273  // the grid.
274 
275  ++model_count;
276  PT(PandaNode) node = loader.load_sync(gf._filename, options);
277  NodePath model;
278  if (node == nullptr) {
279  model = gf._model.copy_to(NodePath());
280  } else {
281  model = NodePath(node);
282  }
283  model.reparent_to(root);
284 
285  gridded_file_info info;
286  info.node = model.node();
287 
288  LMatrix4 xfm_mat,tmat1,tmat2;
289 
290  if(gridmotiontype==Rotation) {
291 
292 #define MIN_REVOLUTION_ANGVEL 30
293 #define MAX_REVOLUTION_ANGVEL 60
294 
295 #define MIN_ROTATION_ANGVEL 30
296 #define MAX_ROTATION_ANGVEL 600
297 
298 #define MAX_RADIUS 4.0*GRIDCELLSIZE
299 #define MIN_RADIUS 0.1*GRIDCELLSIZE
300 
301  info.starttime = now;
302 
303  info.xcenter=xpos;
304  info.ycenter=ypos;
305  info.ang1=RANDFRAC * 360.0;
306  info.ang1_vel=((MAX_REVOLUTION_ANGVEL-MIN_REVOLUTION_ANGVEL) * RANDFRAC) + MIN_REVOLUTION_ANGVEL;
307 
308  info.ang2=RANDFRAC * 360.0;
309  info.ang2_vel=((MAX_ROTATION_ANGVEL-MIN_ROTATION_ANGVEL) * RANDFRAC) + MIN_ROTATION_ANGVEL;
310 
311  info.radius = (RANDFRAC * (MAX_RADIUS-MIN_RADIUS)) + MIN_RADIUS;
312 
313  if(RANDFRAC>0.5) {
314  info.ang1_vel=-info.ang1_vel;
315  }
316 
317  if(RANDFRAC>0.5) {
318  info.ang2_vel=-info.ang2_vel;
319  }
320 
321  // xforms happen left to right
322  LVector2 new_center = LVector2(info.radius,0.0) *
323  LMatrix3::rotate_mat(info.ang1);
324 
325  const LVector3 rotate_axis(0.0, 0.0, 1.0);
326 
327  LVector3 translate_vec(xpos+new_center._v.v._0,
328  ypos+new_center._v.v._1,
329  0.0);
330 
331  tmat1.set_rotate_mat_normaxis(info.ang2,rotate_axis);
332  tmat2 = LMatrix4::translate_mat(translate_vec);
333  xfm_mat = tmat1 * tmat2;
334  } else if(gridmotiontype==LinearMotion) {
335 
336 #define MIN_VEL 2.0
337 #define MAX_VEL (fabs(wander_area_pos_offset))
338 
339  info.vel=((MAX_VEL-MIN_VEL) * RANDFRAC) + MIN_VEL;
340 
341  info.xstart=xpos;
342  info.ystart=ypos;
343 
344  info.xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
345  info.yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
346 
347  info.starttime = now;
348 
349  PN_stdfloat xdel = info.xdel = info.xend-info.xstart;
350  PN_stdfloat ydel = info.ydel = info.yend-info.ystart;
351 
352  info.endtime = csqrt(xdel*xdel+ydel*ydel)/info.vel;
353 
354  info.timedel = info.endtime - info.starttime;
355 
356  const LVector3 rotate_axis(0.0, 0.0, 1.0);
357  PN_stdfloat ang = rad_2_deg(atan2(-xdel,ydel));
358 
359  info.rotmat.set_rotate_mat_normaxis(ang,rotate_axis);
360 
361  LVector3 translate_vec(xpos, ypos, 0.0);
362  LMatrix4 tmat2 = LMatrix4::translate_mat(translate_vec);
363 
364  xfm_mat = info.rotmat * tmat2;
365  } else {
366  LVector3 translate_vec(xpos, ypos, 0.0);
367  xfm_mat = LMatrix4::translate_mat(translate_vec);
368  }
369 
370  info.node->set_transform(TransformState::make_mat(xfm_mat));
371 
372  info_arr.push_back(info);
373 
374  if((model_count % gridwidth) == 0) {
375  xpos= -gridwidth*GRIDCELLSIZE/2.0;
376  ypos+=GRIDCELLSIZE;
377  } else {
378  xpos+=GRIDCELLSIZE;
379  }
380  }
381  }
382 
383  passnum++;
384  } while (loaded_any);
385 }
386 
387 int
388 main(int argc, char **argv) {
389  preprocess_argv(argc, argv);
390  PandaFramework framework;
391  framework.open_framework(argc, argv);
392  framework.set_window_title("Gridded Object Viewer");
393 
394  if (!get_command_line_opts(argc, argv)) {
395  return (1);
396  }
397 
398  // Extract the remaining arguments into two lists of files: those with a
399  // grid parameter, and those without.
400  pvector<Filename> static_filenames;
401  GriddedFilenames gridded_filenames;
402  get_command_line_filenames(argc, argv, static_filenames, gridded_filenames);
403 
404  WindowFramework *window = framework.open_window();
405  if (window != nullptr) {
406  // We've successfully opened a window.
407 
408  window->enable_keyboard();
409  window->setup_trackball();
410  window->load_models(framework.get_models(), static_filenames);
411  framework.get_models().instance_to(window->get_render());
412 
413  GriddedInfoArray info_arr;
414  load_gridded_models(window, gridded_filenames, info_arr);
415 
416  window->loop_animations();
417  window->stagger_animations();
418  window->center_trackball(framework.get_models());
419 
420  framework.enable_default_keys();
421 
422  Thread *current_thread = Thread::get_current_thread();
423  while (framework.do_frame(current_thread)) {
424  if (!info_arr.empty() && gridmotiontype) {
425  move_gridded_stuff(gridmotiontype, &info_arr[0], info_arr.size());
426  }
427  }
428  }
429 
430  framework.report_frame_rate(nout);
431  return (0);
432 }
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.h:91
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:23
A convenient class for loading models from disk, in bam or egg format (or any of a number of other fo...
Definition: loader.h:42
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
NodePath copy_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Functions like instance_to(), except a deep copy is made of the referenced node and all of its descen...
Definition: nodePath.cxx:539
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
void reparent_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread())
Removes the referenced node of the NodePath from its current parent and attaches it to the referenced...
Definition: nodePath.cxx:395
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:475
This class serves to provide a high-level framework for basic applications that use Panda in simple w...
WindowFramework * open_window()
Opens a window on the default graphics pipe.
void enable_default_keys()
Sets callbacks on the event handler to handle all of the normal viewer keys, like t to toggle texture...
void open_framework()
Should be called once at the beginning of the application to initialize Panda (and the framework) for...
void report_frame_rate(std::ostream &out) const
Reports the currently measured average frame rate to the indicated ostream.
void set_window_title(const std::string &title)
Specifies the title that is set for all subsequently created windows.
NodePath & get_models()
Returns the root of the scene graph normally reserved for parenting models and such.
virtual bool do_frame(Thread *current_thread)
Renders one frame and performs all associated processing.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
A thread; that is, a lightweight process.
Definition: thread.h:46
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
This encapsulates the data that is normally associated with a single window, or with a single display...
void setup_trackball()
Sets up the mouse to trackball around the camera.
void stagger_animations()
Walks through all the animations that were bound by loop_animations() and staggers their play rate sl...
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_keyboard()
Creates a ButtonThrower to listen to button presses and throw them as events.
NodePath get_render()
Returns the root of the 3-d scene graph.
void center_trackball(const NodePath &object)
Centers the trackball on the indicated object, and scales the trackball motion suitably.
PandaFramework * get_panda_framework() const
Returns a pointer to the associated PandaFramework object.
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.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void preprocess_argv(int &argc, char **&argv)
Processes the argc, argv pair as needed before passing it to getopt().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int string_to_int(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.