Panda3D
 All Classes Functions Variables Enumerations
pgrid.cxx
1 // Filename: pgrid.cxx
2 // Created by: drose (03Apr02)
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 "pystub.h"
17 #include "pandaNode.h"
18 #include "transformState.h"
19 #include "clockObject.h"
20 #include "string_utils.h"
21 #include "pvector.h"
22 #include "panda_getopt.h"
23 #include "preprocess_argv.h"
24 
25 #define RANDFRAC (rand()/(PN_stdfloat)(RAND_MAX))
26 
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;
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 how
85 
86  if(gridmotiontype==Rotation) {
87 
88  InfoArr[i].ang1=time_delta*InfoArr[i].ang1_vel;
89  DO_FP_MODULUS(InfoArr[i].ang1,360.0);
90  InfoArr[i].ang2=time_delta*InfoArr[i].ang2_vel;
91  DO_FP_MODULUS(InfoArr[i].ang2,360.0);
92 
93  // xforms happen left to right
94  LVector2 new_center = LVector2(InfoArr[i].radius,0.0) *
95  LMatrix3::rotate_mat(InfoArr[i].ang1);
96 
97  LVector3 translate_vec(InfoArr[i].xcenter+new_center._v.v._0,
98  InfoArr[i].ycenter+new_center._v.v._1,
99  0.0);
100 
101  const LVector3 rotation_axis(0.0, 0.0, 1.0);
102 
103  tmat1 = LMatrix4::rotate_mat_normaxis(InfoArr[i].ang2,rotation_axis);
104  tmat2 = LMatrix4::translate_mat(translate_vec);
105  xfm_mat = tmat1 * tmat2;
106  } else {
107 
108  PN_stdfloat xpos,ypos;
109 
110  if(now>InfoArr[i].endtime) {
111  InfoArr[i].starttime = now;
112 
113  xpos = InfoArr[i].xstart = InfoArr[i].xend;
114  ypos = InfoArr[i].ystart = InfoArr[i].yend;
115 
116  InfoArr[i].xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
117  InfoArr[i].yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
118 
119  PN_stdfloat xdel = InfoArr[i].xdel = InfoArr[i].xend-InfoArr[i].xstart;
120  PN_stdfloat ydel = InfoArr[i].ydel = InfoArr[i].yend-InfoArr[i].ystart;
121 
122  InfoArr[i].endtime = now + csqrt(xdel*xdel+ydel*ydel)/InfoArr[i].vel;
123  InfoArr[i].timedel = InfoArr[i].endtime - InfoArr[i].starttime;
124 
125  const LVector3 rotate_axis(0.0, 0.0, 1.0);
126 
127  PN_stdfloat ang = rad_2_deg(atan2(-xdel,ydel));
128 
129  InfoArr[i].rotmat= LMatrix4::rotate_mat_normaxis(ang,rotate_axis);
130  } else {
131  PN_stdfloat timefrac= time_delta/InfoArr[i].timedel;
132 
133  xpos = InfoArr[i].xdel*timefrac+InfoArr[i].xstart;
134  ypos = InfoArr[i].ydel*timefrac+InfoArr[i].ystart;
135  }
136 
137  LVector3 translate_vec(xpos, ypos, 0.0);
138  LMatrix4 tmat2 = LMatrix4::translate_mat(translate_vec);
139 
140  xfm_mat = InfoArr[i].rotmat * tmat2;
141  }
142  InfoArr[i].node->set_transform(TransformState::make_mat(xfm_mat));
143  }
144 }
145 
146 bool
147 get_command_line_opts(int &argc, char **&argv) {
148  // Use getopt() to decode the optional command-line parameters.
149  // extern char *optarg;
150  extern int optind;
151  const char *options = "rm";
152  int flag = getopt(argc, argv, options);
153  while (flag != EOF) {
154  switch (flag) {
155  case 'r':
156  gridmotiontype = Rotation;
157  break;
158 
159  case 'm':
160  gridmotiontype = LinearMotion;
161  break;
162 
163  case '?':
164  nout << "Invalid parameter.\n";
165  return false;
166  }
167 
168  flag = getopt(argc, argv, options);
169  }
170 
171  argv += (optind - 1);
172  argc -= (optind - 1);
173 
174  return true;
175 }
176 
177 void
178 get_command_line_filenames(int argc, char *argv[],
179  pvector<Filename> &static_filenames,
180  GriddedFilenames &gridded_filenames) {
181  for (int i = 1; i < argc && argv[i] != (char *)NULL; i++) {
182  const string &arg = argv[i];
183  size_t comma = arg.find(',');
184  if (comma == string::npos) {
185  // No comma in the filename, so it must be an ordinary static file.
186  static_filenames.push_back(Filename::from_os_specific(arg));
187 
188  } else {
189  // A comma in the filename indicates a gridded file. The syntax
190  // is filename,count where count represents the number of times
191  // the file is repeated.
192  string name = arg.substr(0, comma);
193  string count_str = arg.substr(comma + 1);
194  int count;
195  if (!string_to_int(count_str, count)) {
196  nout << "Ignoring invalid number: " << count_str << "\n";
197  count = 1;
198  } else if (count <= 0) {
199  nout << "Ignoring inappropriate number: " << count << "\n";
200  count = 1;
201  }
202 
203  GriddedFilename gf;
204  gf._filename = Filename::from_os_specific(name);
205  gf._count = count;
206  gridded_filenames.push_back(gf);
207  }
208  }
209 }
210 
211 void
212 load_gridded_models(WindowFramework *window,
213  GriddedFilenames &filenames,
214  GriddedInfoArray &info_arr) {
215  // Load up all the files indicated in the list of gridded filenames
216  // and store them in the given vector.
217 
218  Loader loader;
219  LoaderOptions options;
220  // options.set_flags(options.get_flags() | LoaderOptions::LF_no_ram_cache);
221 
222  // First, load up each model from disk once, and store them all
223  // separate from the scene graph. Also count up the total number of
224  // models we'll be putting in the grid.
225  int grid_count = 0;
226  GriddedFilenames::iterator fi;
227  for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
228  GriddedFilename &gf = (*fi);
229  PT(PandaNode) node = loader.load_sync(gf._filename, options);
230  if (node != (PandaNode *)NULL) {
231  gf._model = NodePath(node);
232  grid_count += gf._count;
233  }
234  }
235 
236  info_arr.clear();
237  info_arr.reserve(grid_count);
238 
239  // Compute the integer square root of grid_count, so that we put our
240  // models in a nice square grid.
241 
242  gridwidth=1;
243  while(gridwidth*gridwidth < grid_count) {
244  gridwidth++;
245  }
246 
247  grid_pos_offset = -gridwidth*GRIDCELLSIZE/2.0;
248  wander_area_pos_offset = -max((PN_stdfloat)fabs(grid_pos_offset), MIN_WANDERAREA_DIMENSION/2.0f);
249 
250  // Now walk through the list again, copying models into the scene
251  // graph as we go.
252 
253  PN_stdfloat xpos = grid_pos_offset;
254  PN_stdfloat ypos = grid_pos_offset;
255 
256  srand( (unsigned)time( NULL ) );
258 
259  int model_count = 0;
260  int passnum = 0;
261  bool loaded_any;
262 
263  NodePath root = window->get_panda_framework()->get_models();
264  do {
265  loaded_any = false;
266 
267  for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
268  const GriddedFilename &gf = (*fi);
269  if (!gf._model.is_empty() && gf._count > passnum) {
270  loaded_any = true;
271  // Copy this model into the scene graph, and assign it a
272  // position on the grid.
273 
274  ++model_count;
275  PT(PandaNode) node = loader.load_sync(gf._filename, options);
276  NodePath model;
277  if (node == (PandaNode *)NULL) {
278  model = gf._model.copy_to(NodePath());
279  } else {
280  model = NodePath(node);
281  }
282  model.reparent_to(root);
283 
284  gridded_file_info info;
285  info.node = model.node();
286 
287  LMatrix4 xfm_mat,tmat1,tmat2;
288 
289  if(gridmotiontype==Rotation) {
290 
291 #define MIN_REVOLUTION_ANGVEL 30
292 #define MAX_REVOLUTION_ANGVEL 60
293 
294 #define MIN_ROTATION_ANGVEL 30
295 #define MAX_ROTATION_ANGVEL 600
296 
297 #define MAX_RADIUS 4.0*GRIDCELLSIZE
298 #define MIN_RADIUS 0.1*GRIDCELLSIZE
299 
300  info.starttime = now;
301 
302  info.xcenter=xpos;
303  info.ycenter=ypos;
304  info.ang1=RANDFRAC * 360.0;
305  info.ang1_vel=((MAX_REVOLUTION_ANGVEL-MIN_REVOLUTION_ANGVEL) * RANDFRAC) + MIN_REVOLUTION_ANGVEL;
306 
307  info.ang2=RANDFRAC * 360.0;
308  info.ang2_vel=((MAX_ROTATION_ANGVEL-MIN_ROTATION_ANGVEL) * RANDFRAC) + MIN_ROTATION_ANGVEL;
309 
310  info.radius = (RANDFRAC * (MAX_RADIUS-MIN_RADIUS)) + MIN_RADIUS;
311 
312  if(RANDFRAC>0.5) {
313  info.ang1_vel=-info.ang1_vel;
314  }
315 
316  if(RANDFRAC>0.5) {
317  info.ang2_vel=-info.ang2_vel;
318  }
319 
320  // xforms happen left to right
321  LVector2 new_center = LVector2(info.radius,0.0) *
322  LMatrix3::rotate_mat(info.ang1);
323 
324  const LVector3 rotate_axis(0.0, 0.0, 1.0);
325 
326  LVector3 translate_vec(xpos+new_center._v.v._0,
327  ypos+new_center._v.v._1,
328  0.0);
329 
330  tmat1.set_rotate_mat_normaxis(info.ang2,rotate_axis);
331  tmat2 = LMatrix4::translate_mat(translate_vec);
332  xfm_mat = tmat1 * tmat2;
333  } else if(gridmotiontype==LinearMotion) {
334 
335 #define MIN_VEL 2.0
336 #define MAX_VEL (fabs(wander_area_pos_offset))
337 
338  info.vel=((MAX_VEL-MIN_VEL) * RANDFRAC) + MIN_VEL;
339 
340  info.xstart=xpos;
341  info.ystart=ypos;
342 
343  info.xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
344  info.yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset;
345 
346  info.starttime = now;
347 
348  PN_stdfloat xdel = info.xdel = info.xend-info.xstart;
349  PN_stdfloat ydel = info.ydel = info.yend-info.ystart;
350 
351  info.endtime = csqrt(xdel*xdel+ydel*ydel)/info.vel;
352 
353  info.timedel = info.endtime - info.starttime;
354 
355  const LVector3 rotate_axis(0.0, 0.0, 1.0);
356  PN_stdfloat ang = rad_2_deg(atan2(-xdel,ydel));
357 
358  info.rotmat.set_rotate_mat_normaxis(ang,rotate_axis);
359 
360  LVector3 translate_vec(xpos, ypos, 0.0);
361  LMatrix4 tmat2 = LMatrix4::translate_mat(translate_vec);
362 
363  xfm_mat = info.rotmat * tmat2;
364  } else {
365  LVector3 translate_vec(xpos, ypos, 0.0);
366  xfm_mat = LMatrix4::translate_mat(translate_vec);
367  }
368 
369  info.node->set_transform(TransformState::make_mat(xfm_mat));
370 
371  info_arr.push_back(info);
372 
373  if((model_count % gridwidth) == 0) {
374  xpos= -gridwidth*GRIDCELLSIZE/2.0;
375  ypos+=GRIDCELLSIZE;
376  } else {
377  xpos+=GRIDCELLSIZE;
378  }
379  }
380  }
381 
382  passnum++;
383  } while (loaded_any);
384 }
385 
386 int
387 main(int argc, char **argv) {
388  // A call to pystub() to force libpystub.so to be linked in.
389  pystub();
390 
391  preprocess_argv(argc, argv);
392  PandaFramework framework;
393  framework.open_framework(argc, argv);
394  framework.set_window_title("Gridded Object Viewer");
395 
396  if (!get_command_line_opts(argc, argv)) {
397  return (1);
398  }
399 
400  // Extract the remaining arguments into two lists of files: those
401  // with a grid parameter, and those without.
402  pvector<Filename> static_filenames;
403  GriddedFilenames gridded_filenames;
404  get_command_line_filenames(argc, argv, static_filenames, gridded_filenames);
405 
406  WindowFramework *window = framework.open_window();
407  if (window != (WindowFramework *)NULL) {
408  // We've successfully opened a window.
409 
410  window->enable_keyboard();
411  window->setup_trackball();
412  window->load_models(framework.get_models(), static_filenames);
413  framework.get_models().instance_to(window->get_render());
414 
415  GriddedInfoArray info_arr;
416  load_gridded_models(window, gridded_filenames, info_arr);
417 
418  window->loop_animations();
419  window->stagger_animations();
420  window->center_trackball(framework.get_models());
421 
422  framework.enable_default_keys();
423 
424  Thread *current_thread = Thread::get_current_thread();
425  while (framework.do_frame(current_thread)) {
426  if (!info_arr.empty() && gridmotiontype) {
427  move_gridded_stuff(gridmotiontype, &info_arr[0], info_arr.size());
428  }
429  }
430  }
431 
432  framework.report_frame_rate(nout);
433  return (0);
434 }
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:271
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
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...
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:691
void enable_default_keys()
Sets callbacks on the event handler to handle all of the normal viewer keys, like t to toggle texture...
static LMatrix4f translate_mat(const LVecBase3f &trans)
Returns a matrix that applies the indicated translation.
Definition: lmatrix.h:2397
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:26
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:47
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
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
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
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:523
PandaFramework * get_panda_framework() const
Returns a pointer to the associated PandaFramework object.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
double get_frame_time(Thread *current_thread=Thread::get_current_thread()) const
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.I:48
void center_trackball(const NodePath &object)
Centers the trackball on the indicated object, and scales the trackball motion suitably.
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.
void open_framework(int &argc, char **&argv)
Should be called once at the beginning of the application to initialize Panda (and the framework) for...
This is a two-component vector offset.
Definition: lvector2.h:91
static LMatrix4f rotate_mat_normaxis(float angle, const LVecBase3f &axis, CoordinateSystem cs=CS_default)
Returns a matrix that rotates by the given angle in degrees counterclockwise about the indicated vect...
Definition: lmatrix.h:2440
A thread; that is, a lightweight process.
Definition: thread.h:51
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.
static LMatrix3f rotate_mat(float angle)
Returns a matrix that rotates by the given angle in degrees counterclockwise.
Definition: lmatrix.h:4081
void set_window_title(const string &title)
Specifies the title that is set for all subsequently created windows.
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
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.
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
void set_transform(const TransformState *transform, Thread *current_thread=Thread::get_current_thread())
Sets the transform that will be applied to this node and below.
Definition: pandaNode.cxx:1267
void set_rotate_mat_normaxis(float angle, const LVecBase3f &axis, CoordinateSystem cs=CS_default)
Fills mat with a matrix that rotates by the given angle in degrees counterclockwise about the indicat...
Definition: lmatrix.cxx:817
virtual bool do_frame(Thread *current_thread)
Renders one frame and performs all associated processing.
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