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 }
int string_to_int(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
void report_frame_rate(std::ostream &out) const
Reports the currently measured average frame rate to the indicated ostream.
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void enable_default_keys()
Sets callbacks on the event handler to handle all of the normal viewer keys, like t to toggle texture...
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.
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:23
This encapsulates the data that is normally associated with a single window, or with a single display...
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
void open_framework()
Should be called once at the beginning of the application to initialize Panda (and the framework) for...
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:471
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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:391
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
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
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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.
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.
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().
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
A thread; that is, a lightweight process.
Definition: thread.h:46
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
void set_window_title(const std::string &title)
Specifies the title that is set for all subsequently created windows.
This class serves to provide a high-level framework for basic applications that use Panda in simple w...
void setup_trackball()
Sets up the mouse to trackball around the camera.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
virtual bool do_frame(Thread *current_thread)
Renders one frame and performs all associated processing.