Panda3D
|
00001 // Filename: pgrid.cxx 00002 // Created by: drose (03Apr02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "pandaFramework.h" 00016 #include "pandaNode.h" 00017 #include "transformState.h" 00018 #include "clockObject.h" 00019 #include "string_utils.h" 00020 #include "pvector.h" 00021 00022 #ifdef HAVE_GETOPT 00023 #include <getopt.h> 00024 #else 00025 #include "gnu_getopt.h" 00026 #endif 00027 00028 00029 #define RANDFRAC (rand()/(float)(RAND_MAX)) 00030 00031 class GriddedFilename { 00032 public: 00033 Filename _filename; 00034 int _count; 00035 NodePath _model; 00036 }; 00037 typedef pvector<GriddedFilename> GriddedFilenames; 00038 00039 typedef struct { 00040 // for rot moving 00041 float xcenter,ycenter; 00042 float xoffset,yoffset; 00043 float ang1,ang1_vel; 00044 float ang2,ang2_vel; 00045 00046 float radius; 00047 00048 // for moving 00049 float xstart,ystart; 00050 float xend,yend; 00051 float xdel,ydel,timedel; 00052 double starttime,endtime; 00053 double vel; 00054 LMatrix4f rotmat; 00055 00056 PandaNode *node; 00057 } gridded_file_info; 00058 00059 typedef pvector<gridded_file_info> GriddedInfoArray; 00060 00061 typedef enum {None,Rotation,LinearMotion} GriddedMotionType; 00062 00063 #define GRIDCELLSIZE 5.0 00064 static int gridwidth; // cells/side 00065 00066 #define MIN_WANDERAREA_DIMENSION 120.0f 00067 00068 static float grid_pos_offset; // origin of grid 00069 static float wander_area_pos_offset; 00070 00071 static GriddedMotionType gridmotiontype = None; 00072 00073 00074 // making these fns to get around ridiculous VC++ matrix inlining bugs at Opt2 00075 static void 00076 move_gridded_stuff(GriddedMotionType gridmotiontype, 00077 gridded_file_info *InfoArr, int size) { 00078 double now = ClockObject::get_global_clock()->get_frame_time(); 00079 00080 LMatrix4f tmat1,tmat2,xfm_mat; 00081 00082 for(int i = 0; i < size; i++) { 00083 double time_delta = (now-InfoArr[i].starttime); 00084 #define DO_FP_MODULUS(VAL,MAXVAL) \ 00085 {if(VAL > MAXVAL) {int idivresult = (int)(VAL / (float)MAXVAL); VAL=VAL-idivresult*MAXVAL;} else \ 00086 if(VAL < -MAXVAL) {int idivresult = (int)(VAL / (float)MAXVAL); VAL=VAL+idivresult*MAXVAL;}} 00087 00088 // probably should use panda lerps for this stuff, but I dont understand how 00089 00090 if(gridmotiontype==Rotation) { 00091 00092 InfoArr[i].ang1=time_delta*InfoArr[i].ang1_vel; 00093 DO_FP_MODULUS(InfoArr[i].ang1,360.0); 00094 InfoArr[i].ang2=time_delta*InfoArr[i].ang2_vel; 00095 DO_FP_MODULUS(InfoArr[i].ang2,360.0); 00096 00097 // xforms happen left to right 00098 LVector2f new_center = LVector2f(InfoArr[i].radius,0.0) * 00099 LMatrix3f::rotate_mat(InfoArr[i].ang1); 00100 00101 LVector3f translate_vec(InfoArr[i].xcenter+new_center._v.v._0, 00102 InfoArr[i].ycenter+new_center._v.v._1, 00103 0.0); 00104 00105 const LVector3f rotation_axis(0.0, 0.0, 1.0); 00106 00107 tmat1 = LMatrix4f::rotate_mat_normaxis(InfoArr[i].ang2,rotation_axis); 00108 tmat2 = LMatrix4f::translate_mat(translate_vec); 00109 xfm_mat = tmat1 * tmat2; 00110 } else { 00111 00112 float xpos,ypos; 00113 00114 if(now>InfoArr[i].endtime) { 00115 InfoArr[i].starttime = now; 00116 00117 xpos = InfoArr[i].xstart = InfoArr[i].xend; 00118 ypos = InfoArr[i].ystart = InfoArr[i].yend; 00119 00120 InfoArr[i].xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset; 00121 InfoArr[i].yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset; 00122 00123 float xdel = InfoArr[i].xdel = InfoArr[i].xend-InfoArr[i].xstart; 00124 float ydel = InfoArr[i].ydel = InfoArr[i].yend-InfoArr[i].ystart; 00125 00126 InfoArr[i].endtime = now + csqrt(xdel*xdel+ydel*ydel)/InfoArr[i].vel; 00127 InfoArr[i].timedel = InfoArr[i].endtime - InfoArr[i].starttime; 00128 00129 const LVector3f rotate_axis(0.0, 0.0, 1.0); 00130 00131 float ang = rad_2_deg(atan2(-xdel,ydel)); 00132 00133 InfoArr[i].rotmat= LMatrix4f::rotate_mat_normaxis(ang,rotate_axis); 00134 } else { 00135 float timefrac= time_delta/InfoArr[i].timedel; 00136 00137 xpos = InfoArr[i].xdel*timefrac+InfoArr[i].xstart; 00138 ypos = InfoArr[i].ydel*timefrac+InfoArr[i].ystart; 00139 } 00140 00141 LVector3f translate_vec(xpos, ypos, 0.0); 00142 LMatrix4f tmat2 = LMatrix4f::translate_mat(translate_vec); 00143 00144 xfm_mat = InfoArr[i].rotmat * tmat2; 00145 } 00146 InfoArr[i].node->set_transform(TransformState::make_mat(xfm_mat)); 00147 } 00148 } 00149 00150 bool 00151 get_command_line_opts(int &argc, char **&argv) { 00152 // Use getopt() to decode the optional command-line parameters. 00153 // extern char *optarg; 00154 extern int optind; 00155 const char *options = "rm"; 00156 int flag = getopt(argc, argv, options); 00157 while (flag != EOF) { 00158 switch (flag) { 00159 case 'r': 00160 gridmotiontype = Rotation; 00161 break; 00162 00163 case 'm': 00164 gridmotiontype = LinearMotion; 00165 break; 00166 00167 case '?': 00168 nout << "Invalid parameter.\n"; 00169 return false; 00170 } 00171 00172 flag = getopt(argc, argv, options); 00173 } 00174 00175 argv += (optind - 1); 00176 argc -= (optind - 1); 00177 00178 return true; 00179 } 00180 00181 void 00182 get_command_line_filenames(int argc, char *argv[], 00183 pvector<Filename> &static_filenames, 00184 GriddedFilenames &gridded_filenames) { 00185 for (int i = 1; i < argc && argv[i] != (char *)NULL; i++) { 00186 const string &arg = argv[i]; 00187 size_t comma = arg.find(','); 00188 if (comma == string::npos) { 00189 // No comma in the filename, so it must be an ordinary static file. 00190 static_filenames.push_back(Filename::from_os_specific(arg)); 00191 00192 } else { 00193 // A comma in the filename indicates a gridded file. The syntax 00194 // is filename,count where count represents the number of times 00195 // the file is repeated. 00196 string name = arg.substr(0, comma); 00197 string count_str = arg.substr(comma + 1); 00198 int count; 00199 if (!string_to_int(count_str, count)) { 00200 nout << "Ignoring invalid number: " << count_str << "\n"; 00201 count = 1; 00202 } else if (count <= 0) { 00203 nout << "Ignoring inappropriate number: " << count << "\n"; 00204 count = 1; 00205 } 00206 00207 GriddedFilename gf; 00208 gf._filename = Filename::from_os_specific(name); 00209 gf._count = count; 00210 gridded_filenames.push_back(gf); 00211 } 00212 } 00213 } 00214 00215 void 00216 load_gridded_models(WindowFramework *window, 00217 GriddedFilenames &filenames, 00218 GriddedInfoArray &info_arr) { 00219 // Load up all the files indicated in the list of gridded filenames 00220 // and store them in the given vector. 00221 00222 // First, load up each model from disk once, and store them all 00223 // separate from the scene graph. Also count up the total number of 00224 // models we'll be putting in the grid. 00225 int grid_count = 0; 00226 NodePath models("models"); 00227 GriddedFilenames::iterator fi; 00228 for (fi = filenames.begin(); fi != filenames.end(); ++fi) { 00229 GriddedFilename &gf = (*fi); 00230 gf._model = window->load_model(models, gf._filename); 00231 if (!gf._model.is_empty()) { 00232 grid_count += gf._count; 00233 } 00234 } 00235 00236 info_arr.clear(); 00237 info_arr.reserve(grid_count); 00238 00239 // Compute the integer square root of grid_count, so that we put our 00240 // models in a nice square grid. 00241 00242 gridwidth=1; 00243 while(gridwidth*gridwidth < grid_count) { 00244 gridwidth++; 00245 } 00246 00247 grid_pos_offset = -gridwidth*GRIDCELLSIZE/2.0; 00248 wander_area_pos_offset = -max((float)fabs(grid_pos_offset), MIN_WANDERAREA_DIMENSION/2.0f); 00249 00250 // Now walk through the list again, copying models into the scene 00251 // graph as we go. 00252 00253 float xpos = grid_pos_offset; 00254 float ypos = grid_pos_offset; 00255 00256 srand( (unsigned)time( NULL ) ); 00257 double now = ClockObject::get_global_clock()->get_frame_time(); 00258 00259 int model_count = 0; 00260 int passnum = 0; 00261 bool loaded_any; 00262 00263 NodePath render = window->get_render(); 00264 do { 00265 loaded_any = false; 00266 00267 for (fi = filenames.begin(); fi != filenames.end(); ++fi) { 00268 const GriddedFilename &gf = (*fi); 00269 if (!gf._model.is_empty() && gf._count > passnum) { 00270 loaded_any = true; 00271 // Copy this model into the scene graph, and assign it a 00272 // position on the grid. 00273 00274 string model_name = format_string(++model_count); 00275 NodePath model = render.attach_new_node(model_name); 00276 gf._model.copy_to(model); 00277 00278 gridded_file_info info; 00279 info.node = model.node(); 00280 00281 LMatrix4f xfm_mat,tmat1,tmat2; 00282 00283 if(gridmotiontype==Rotation) { 00284 00285 #define MIN_REVOLUTION_ANGVEL 30 00286 #define MAX_REVOLUTION_ANGVEL 60 00287 00288 #define MIN_ROTATION_ANGVEL 30 00289 #define MAX_ROTATION_ANGVEL 600 00290 00291 #define MAX_RADIUS 4.0*GRIDCELLSIZE 00292 #define MIN_RADIUS 0.1*GRIDCELLSIZE 00293 00294 info.starttime = now; 00295 00296 info.xcenter=xpos; 00297 info.ycenter=ypos; 00298 info.ang1=RANDFRAC * 360.0; 00299 info.ang1_vel=((MAX_REVOLUTION_ANGVEL-MIN_REVOLUTION_ANGVEL) * RANDFRAC) + MIN_REVOLUTION_ANGVEL; 00300 00301 info.ang2=RANDFRAC * 360.0; 00302 info.ang2_vel=((MAX_ROTATION_ANGVEL-MIN_ROTATION_ANGVEL) * RANDFRAC) + MIN_ROTATION_ANGVEL; 00303 00304 info.radius = (RANDFRAC * (MAX_RADIUS-MIN_RADIUS)) + MIN_RADIUS; 00305 00306 if(RANDFRAC>0.5) { 00307 info.ang1_vel=-info.ang1_vel; 00308 } 00309 00310 if(RANDFRAC>0.5) { 00311 info.ang2_vel=-info.ang2_vel; 00312 } 00313 00314 // xforms happen left to right 00315 LVector2f new_center = LVector2f(info.radius,0.0) * 00316 LMatrix3f::rotate_mat(info.ang1); 00317 00318 const LVector3f rotate_axis(0.0, 0.0, 1.0); 00319 00320 LVector3f translate_vec(xpos+new_center._v.v._0, 00321 ypos+new_center._v.v._1, 00322 0.0); 00323 00324 tmat1.set_rotate_mat_normaxis(info.ang2,rotate_axis); 00325 tmat2 = LMatrix4f::translate_mat(translate_vec); 00326 xfm_mat = tmat1 * tmat2; 00327 } else if(gridmotiontype==LinearMotion) { 00328 00329 #define MIN_VEL 2.0 00330 #define MAX_VEL (fabs(wander_area_pos_offset)) 00331 00332 info.vel=((MAX_VEL-MIN_VEL) * RANDFRAC) + MIN_VEL; 00333 00334 info.xstart=xpos; 00335 info.ystart=ypos; 00336 00337 info.xend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset; 00338 info.yend = RANDFRAC*fabs(2.0*wander_area_pos_offset) + wander_area_pos_offset; 00339 00340 info.starttime = now; 00341 00342 float xdel = info.xdel = info.xend-info.xstart; 00343 float ydel = info.ydel = info.yend-info.ystart; 00344 00345 info.endtime = csqrt(xdel*xdel+ydel*ydel)/info.vel; 00346 00347 info.timedel = info.endtime - info.starttime; 00348 00349 const LVector3f rotate_axis(0.0, 0.0, 1.0); 00350 float ang = rad_2_deg(atan2(-xdel,ydel)); 00351 00352 info.rotmat.set_rotate_mat_normaxis(ang,rotate_axis); 00353 00354 LVector3f translate_vec(xpos, ypos, 0.0); 00355 LMatrix4f tmat2 = LMatrix4f::translate_mat(translate_vec); 00356 00357 xfm_mat = info.rotmat * tmat2; 00358 } else { 00359 LVector3f translate_vec(xpos, ypos, 0.0); 00360 xfm_mat = LMatrix4f::translate_mat(translate_vec); 00361 } 00362 00363 info.node->set_transform(TransformState::make_mat(xfm_mat)); 00364 00365 info_arr.push_back(info); 00366 00367 if((model_count % gridwidth) == 0) { 00368 xpos= -gridwidth*GRIDCELLSIZE/2.0; 00369 ypos+=GRIDCELLSIZE; 00370 } else { 00371 xpos+=GRIDCELLSIZE; 00372 } 00373 } 00374 } 00375 00376 passnum++; 00377 } while (loaded_any); 00378 00379 // Finally, remove the source models we loaded up. Not a real big deal. 00380 for (fi = filenames.begin(); fi != filenames.end(); ++fi) { 00381 GriddedFilename &gf = (*fi); 00382 if (!gf._model.is_empty()) { 00383 gf._model.remove_node(); 00384 } 00385 } 00386 } 00387 00388 int 00389 main(int argc, char *argv[]) { 00390 PandaFramework framework; 00391 vector_string args; 00392 framework.open_framework(argc, argv); 00393 framework.set_window_title("Gridded Object Viewer"); 00394 00395 if (!get_command_line_opts(argc, argv)) { 00396 return (1); 00397 } 00398 00399 // Extract the remaining arguments into two lists of files: those 00400 // with a grid parameter, and those without. 00401 pvector<Filename> static_filenames; 00402 GriddedFilenames gridded_filenames; 00403 get_command_line_filenames(argc, argv, static_filenames, gridded_filenames); 00404 00405 WindowFramework *window = framework.open_window(); 00406 if (window != (WindowFramework *)NULL) { 00407 // We've successfully opened a window. 00408 00409 window->enable_keyboard(); 00410 window->setup_trackball(); 00411 window->load_models(window->get_render(), static_filenames); 00412 00413 GriddedInfoArray info_arr; 00414 load_gridded_models(window, gridded_filenames, info_arr); 00415 00416 window->loop_animations(); 00417 window->stagger_animations(); 00418 00419 framework.enable_default_keys(); 00420 00421 Thread *current_thread = Thread::get_current_thread(); 00422 while (framework.do_frame(current_thread)) { 00423 if (!info_arr.empty() && gridmotiontype) { 00424 move_gridded_stuff(gridmotiontype, &info_arr[0], info_arr.size()); 00425 } 00426 } 00427 } 00428 00429 framework.report_frame_rate(nout); 00430 return (0); 00431 }