/Users/craigcornelius/Projects/SPRING Mac Release 0.2/object.cpp

Go to the documentation of this file.
00001 // $Id: object.cpp,v 1.249 2006/05/30 20:25:19 craig Exp $ 
00002 // $Copyright: (c)2001 National Biocomputation Center, Stanford University $
00003 
00004 
00005 #if defined(_WIN32)                     // SYK 5/09/06 for VC++ 2005
00006 #pragma warning (disable:4355)
00007 #endif // _WIN32
00008 
00009 #include "object.h"
00010 
00011 #include <stdlib.h>     // for malloc, free, realloc, qsort
00012 #include <stdio.h>
00013 #include <assert.h>  // for assert functions...
00014 #include <time.h>
00015 #include <sys/timeb.h>
00016 
00017 #ifdef _WIN32
00018 #else
00019 #include <strings.h>
00020 #endif
00021 
00022 #include "springCore.h"
00023 #include "objectarray.h"
00024 #include "tetraarray.h"
00025 #include "facearray.h"
00026 #include "edgearray.h"
00027 #include "nodearray.h"
00028 
00029 #include "node.h"
00030 #include "edge.h"
00031 #include "face.h"
00032 #include "tetra.h"
00033 
00034 #include "point3d.h"
00035 #include "cyberware.h"
00036 #include "rasterfont.h"
00037 #include "geometryreplicator.h"
00038 #include "perlinnoise.h"        // perlin noise function for fluid dynamics
00039 
00040 #include "space.h"
00041 #include "triangles.h"
00042 #include "void.h"
00043 
00044 #include "haptic_v1.h"
00045 #include "haptic_v2.h"
00046 
00047 #ifndef _WIN32
00048 // If this is not known
00049 #define _fileno fileno
00050 #endif // _WIN32
00051 
00052 #include "glui.h"
00053 
00054 
00055 #ifdef __APPLE__
00056 #include <OpenGL/gl.h>
00057 #else
00058 #include <GL/gl.h>
00059 #endif // __APPLE__
00060 
00061 
00062 // globals
00063 const char* Object::rcsid = "@(#) $Id: object.cpp,v 1.249 2006/05/30 20:25:19 craig Exp $ $Copyright: (c)2001 National Biocomputation Center, Stanford University $";
00064 int Object::debug = 0;
00065 
00066 // default deformable material properties
00067 static GLfloat default_deformable_mat_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
00068 static GLfloat default_deformable_mat_diffuse[] = { 0.9f, 0.35f, 0.2f, 1.0f };
00069 static GLfloat default_deformable_mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
00070 static GLfloat default_deformable_mat_shininess[] = { 128.0f };
00071 
00072 // default deformable material properties
00073 static GLfloat default_articulate_mat_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
00074 static GLfloat default_articulate_mat_diffuse[] = { 0.9f, 0.35f, 0.2f, 1.0f };
00075 static GLfloat default_articulate_mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
00076 static GLfloat default_articulate_mat_shininess[] = { 128.0f };
00077 
00078 // default rigid body (bone) material properties
00079 static GLfloat default_rigid_mat_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
00080 static GLfloat default_rigid_mat_diffuse[] = { 0.8f, 0.65f, 0.5f, 1.0f };
00081 static GLfloat default_rigid_mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
00082 static GLfloat default_rigid_mat_shininess[] = { 128.0f };
00083 
00084 // default rest material properties
00085 static GLfloat default_rest_mat_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
00086 static GLfloat default_rest_mat_diffuse[] = { 0.5f, 0.5f, 0.5f, 1.0f };
00087 static GLfloat default_rest_mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
00088 static GLfloat default_rest_mat_shininess[] = { 100.0 };
00089 
00090 // default button dynamics material properties
00091 static GLfloat default_button_mat_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
00092 static GLfloat default_button_mat_diffuse[] = { 0.5f, 0.5f, 0.5f, 1.0f };
00093 static GLfloat default_button_mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
00094 static GLfloat default_button_mat_shininess[] = { 100.0 };
00095 
00096 Object::Object()
00097         : nodearray(this), 
00098                         edgearray(&nodearray), 
00099                         facearray(&nodearray,&edgearray),
00100                         tetraarray(&nodearray,&edgearray,&facearray)
00101 { 
00102     if (debug) cerr << "Object::constructor()\n";
00103     objectarray_p = NULL;
00104     type = nodes_only; 
00105     dynamics = deformable;
00106     behavior = none;
00107     nummethod = quasi_ordered;
00108     visible = 1;
00109     selected = 0;
00110     texture = NULL;
00111     nodearray.Init(&edgearray);
00112     strncpy(name, "UNINITIALIZED", NAME_LENGTH);
00113     sensor_p = NULL;    // not linked to any sensor
00114     linked_part = 0;    // entire object (class/color 0)
00115     tip_node = 0;
00116     drawover_amount = 0.0;
00117         being_modified = 0;
00118     
00119     IsAScreen   = 0;
00120     group_number = 0;
00121     
00122     closest_node = NULL;
00123     being_grabbed = 0;
00124         do_deform_only_on_grab = 0;
00125         do_preserve_volume = 0;
00126     sensor_offset = Point3D(0,0,0);     
00127     uses_viagra = 0;
00128     
00129 
00130         // Collision: general variables
00131 
00132     collision_extent = no_collisions;
00133 
00134         collision_force = penetration_depth_force;
00135 
00136         boundingBoxRoot = NULL;                 // used in BOUNDING_SPHERE_CD
00137 
00138         boundingSphereRoot = NULL;              // used in AABB_CD
00139 
00140         maxLevelCollisionCheck = (int)1e6;      // how many levels deep to check for collisions 
00141 
00142         ConstructCollisions();
00143 
00144 
00145     // labels stuff
00146     label_offset = 0.0;
00147     
00148     // graphics stuff
00149     dl_needs_refresh = 1;
00150     dl_id = -1;
00151     
00152     // initial params
00153     display_mode.mode = DisplayMode::smooth; 
00154     display_mode.draw_subobjcolormode = 0;
00155     last_display_mode = display_mode;
00156                 
00157         // initialize default fluid height
00158         height = 0;
00159 
00160         // initialize hinges
00161     hinges.Init();
00162 
00163     // initialize rigid object hinges
00164         rigidHinge = NULL;      // default for no hinge!
00165 
00166     // set default material properties, based on dynamics
00167     set_default_material_properties();
00168     timer.Reset();
00169         
00170         // Initialize pointer to GLUI attribute dialog.
00171         current_attribute_menu = NULL;
00172 }
00173 
00174 Object::~Object()
00175 { 
00176     if (debug) cerr << "Object[" << name << "]::destructor()\n";
00177     strncpy(name, "DESTROYED", NAME_LENGTH);
00178     
00179 
00180         // deinit collision stuff
00181     DestructCollisions();
00182 
00183 
00184 
00185         // destroy bounding boxes if any
00186 
00187 
00188     // if we're linked to a sensor, unlink us
00189     if (sensor_p) sensor_p->LinkToObject(NULL);
00190 }
00191 
00192 void Object::Init(ObjectArray* objectarray_in, char* name_in)
00193 {
00194     objectarray_p = objectarray_in;
00195     if (!name_in) name_in = "UNNAMED";
00196     strncpy(name, name_in, NAME_LENGTH);
00197         strncpy(path, "", PATH_LENGTH);
00198     objextrusions = 0;
00199 }
00200 
00201 void Object::set_default_material_properties()
00202 // set default material properties based on the value of dynamics
00203 {
00204     if (debug) 
00205         cerr << "Object[" << getName() << "]:set_default_material_properties()\n";
00206     
00207     switch (dynamics) {
00208                 case rest:
00209                         memcpy(mat_ambient, default_rest_mat_ambient, 4*sizeof(float));
00210                         memcpy(mat_diffuse, default_rest_mat_diffuse, 4*sizeof(float));
00211                         memcpy(mat_specular, default_rest_mat_specular, 4*sizeof(float));
00212                         memcpy(mat_shininess, default_rest_mat_shininess, 1*sizeof(float));
00213                         break;
00214                 case rigid:
00215                         memcpy(mat_ambient, default_rigid_mat_ambient, 4*sizeof(float));
00216                         memcpy(mat_diffuse, default_rigid_mat_diffuse, 4*sizeof(float));
00217                         memcpy(mat_specular, default_rigid_mat_specular, 4*sizeof(float));
00218                         memcpy(mat_shininess, default_rigid_mat_shininess, 1*sizeof(float));
00219                         break;
00220                 case deformable:
00221                         memcpy(mat_ambient, default_deformable_mat_ambient, 4*sizeof(float));
00222                         memcpy(mat_diffuse, default_deformable_mat_diffuse, 4*sizeof(float));
00223                         memcpy(mat_specular, default_deformable_mat_specular, 4*sizeof(float));
00224                         memcpy(mat_shininess, default_deformable_mat_shininess, 1*sizeof(float));
00225                         break;
00226                 case articulate:
00227                         memcpy(mat_ambient, default_articulate_mat_ambient, 4*sizeof(float));
00228                         memcpy(mat_diffuse, default_articulate_mat_diffuse, 4*sizeof(float));
00229                         memcpy(mat_specular, default_articulate_mat_specular, 4*sizeof(float));
00230                         memcpy(mat_shininess, default_articulate_mat_shininess, 1*sizeof(float));
00231                         break;
00232                 case button:
00233                         memcpy(mat_ambient, default_button_mat_ambient, 4*sizeof(float));
00234                         memcpy(mat_diffuse, default_button_mat_diffuse, 4*sizeof(float));
00235                         memcpy(mat_specular, default_button_mat_specular, 4*sizeof(float));
00236                         memcpy(mat_shininess, default_button_mat_shininess, 1*sizeof(float));
00237                         break;
00238     } // end switch
00239     
00240     // by default, make the back faces the same as the front
00241     memcpy(back_mat_ambient, mat_ambient, 4*sizeof(float));
00242     memcpy(back_mat_diffuse, mat_diffuse, 4*sizeof(float));
00243     memcpy(back_mat_specular, mat_specular, 4*sizeof(float));
00244     memcpy(back_mat_shininess, mat_shininess, 1*sizeof(float));
00245 }
00246 
00247 // Amira format
00248 int Object::ReadAmira(FILE* fp)
00249 {
00250  if (debug) cerr << "Object[" << name << "]::ReadAmira(" 
00251                                                                  << int(fp) << ")\n";
00252 
00253         dynamics = rigid;
00254         set_default_material_properties();
00255         type = tetras_only;
00256         
00257         int num_nodes = 0;
00258         int num_tetra = 0;
00259         
00260         char s[MAXLINE];
00261   myfgets(s, MAXLINE, fp);
00262   int num_args = sscanf(s, "%d %d", &num_nodes, &num_tetra);
00263          
00264         if (num_args < 2) {
00265                 cerr << "bad file" << endl;
00266                 return 0;
00267         }
00268   // process the node lines (will also compute texture coords if needed)
00269   int status;
00270   if ((status = nodearray.getAmiraNodeData(fp,num_nodes)) == 0) 
00271     return(status);
00272 
00273   // process the face lines, this creates both faces and edges
00274   status = tetraarray.getAmiraTetraData(fp,num_tetra);
00275 
00276   // recompute normals (since surface is changing)
00277 //  tetraarray.computeNormals();
00278 //  nodearray.interpolateTetraNormals();
00279         
00280   // try to read in a texture (use name of object.rgb)
00281   setTexture(NULL);
00282 
00283   // return value
00284   return(status);
00285 }
00286 
00287 int Object::ReadMesh(FILE* fp)
00288 {
00289     if (debug) cerr << "Object[" << name << "]::ReadMesh(" << int(fp) << ")\n";
00290     
00291     { // deal with header lines
00292         char s[MAXLINE];
00293         myfgets(s, MAXLINE, fp);    // obj# grp#
00294         int obj_num, group_num, dummy;
00295         int num_args = sscanf(s, "%d %d %d", &obj_num, &group_num, &dummy);
00296         if (num_args == 2) { // newer style mesh format
00297             if (debug) cerr << "newer style mesh file\n";
00298             if (group_num == 3) dynamics = rest;    // bone group number/color
00299             else dynamics = deformable; 
00300             
00301             myfgets(s, MAXLINE, fp);    // obj center
00302             myfgets(s, MAXLINE, fp);    // crap crap more_crap
00303         } else {        // very old style mesh format
00304             if (debug) cerr << "older style mesh file\n";
00305             dynamics = deformable; 
00306         }
00307         
00308         // setup material properties appropriately
00309         set_default_material_properties();
00310     }
00311     
00312     // set initial type to be just nodes
00313     type = nodes_only;
00314     
00315     // process the node lines (will also compute texture coords if needed)
00316     int status;
00317     if ((status = nodearray.getMeshNodeData(fp)) == 0) 
00318         return(status);
00319     
00320     // process the face lines, this creates both faces and edges
00321     status = facearray.getMeshFaceData(fp);
00322     
00323     // if we made it this far, assume we have faces
00324     type = faces_only;
00325     
00326 #define RECALC_NORMALS_ON_READ
00327 #ifdef RECALC_NORMALS_ON_READ
00328     // recompute normals (since surface is changing)
00329     facearray.computeNormals();
00330     
00331     // average face normals to get per-vertex normals
00332     nodearray.interpolateNormals();
00333 #endif
00334     
00335     // try to read in a texture (use name of object.rgb)
00336     setTexture(NULL);
00337     
00338     // set some better defaults
00339     if (dynamics == deformable) {
00340         setNumMethod(Object::numerical_method(5));  // ordered quasi
00341         getEdgeArray()->setSpringConstants(100.0);  // stiff
00342         getNodeArray()->setVelocityDampConstants(100.0);
00343     }
00344     
00345     // return value
00346     return(status);
00347 }
00348 
00349 int Object::ReadSMF(FILE* fp)
00350 {
00351     if (debug) cerr << "Object[" << name << "]::ReadSMF(" << int(fp) << ")\n";
00352     
00353     // assume bone group number/color
00354     dynamics = rigid;    
00355     
00356     // setup material properties appropriately
00357     set_default_material_properties();
00358     
00359     // set initial type to be just nodes
00360     type = faces_only;
00361     
00362     // process the node lines
00363     int status;
00364     if ((status = nodearray.getSMFNodeData(fp)) == 0) 
00365         return(status);
00366     
00367     // process the face lines, this creates both faces and edges
00368     status = facearray.getSMFFaceData(fp);
00369     
00370     // compute normals
00371     facearray.computeNormals();
00372     
00373     // average face normals to get per-vertex normals
00374     nodearray.interpolateNormals();
00375     
00376     // and compute the texture coords (assume cylindrical)
00377     nodearray.computeTextureCoords(Node::cylindrical);
00378     
00379     // try to read in a texture (use name of object.rgb)
00380     setTexture(NULL);
00381     
00382     // return value
00383     return(status);
00384 }
00385 
00386 // Alias/Wavefront OBJ format
00387 int Object::ReadOBJ(FILE* fp)
00388 {
00389     if (debug) cerr << "Object[" << name << "]::ReadOBJ(" << int(fp) << ")\n";
00390     
00391     // assume bone group number/color
00392     dynamics = rest;    
00393     
00394     // setup material properties appropriately
00395     set_default_material_properties();
00396     
00397     // set initial type to be just nodes
00398     type = faces_only;
00399     
00400     // process the node lines
00401     int status = nodearray.getOBJNodeData(fp);
00402     if (status  == 0) {
00403 
00404                 // error condition
00405         return(status);
00406 
00407         }
00408 
00409         else if (status == 1) {
00410 
00411                 // no texture coords defined, calculate cylindrical
00412 
00413                 nodearray.computeTextureCoords(Node::cylindrical);
00414 
00415         }
00416     
00417     // process the face lines, this creates both faces and edges
00418     status = facearray.getOBJFaceData(fp);
00419     
00420     // compute normals
00421     facearray.computeNormals();
00422     
00423     // average face normals to get per-vertex normals
00424     nodearray.interpolateNormals();
00425     
00426     // try to read in a texture (use name of object.rgb)
00427     setTexture(NULL);
00428     
00429     // return value
00430     return(status);
00431 }
00432 
00433 // Reads an SOBJ file (=Spring OBJ) - The file contains commands
00434 // to create the basic objects which are already defined in
00435 // spring such as sphere and others.
00436 int Object::ReadSOBJ(FILE* fp)
00437 {
00438         // the status which is returned later
00439         int status = 0;
00440 
00441     // make a local buffer for reading/parsing
00442     char buffer[256];
00443         char object_name[80]; 
00444     
00445     // get the first line
00446     fgets(buffer, 256, fp);
00447         sscanf(buffer, "%s", object_name);
00448 
00449         // check what we should create
00450         if (!strcmp(object_name, "sphere")) {
00451                 // parse attributes
00452                 float layers, radius, inner;
00453         sscanf(buffer, "%*s %f %f %f", &layers, &radius, &inner);
00454                 
00455                 // and create sphere
00456                 status = CreateSphere((int)layers, radius, inner);
00457         }
00458         else if (!strcmp(object_name, "plane")) {
00459                 // parse attributes
00460                 float x_length, y_length;
00461                 int x_steps, y_steps;
00462                 float x_offset, y_offset, z_offset;
00463                 char orientation;
00464         sscanf(buffer, "%*s %f %f %d %d %f %f %f %c", &x_length, &y_length, &x_steps, &y_steps, 
00465                                                                   &x_offset, &y_offset, &z_offset, &orientation);
00466                 // and create plane
00467                 CreatePlane(x_length, y_length, x_steps, y_steps, 
00468                             Point3D(x_offset, y_offset, z_offset), orientation);
00469         }
00470 
00471         // return value
00472         return(status);
00473 }
00474 
00475 int Object::ReadFRD(FILE* fp)
00476 {
00477     if (debug) cerr << "Object[" << name << "]::ReadFRD(" << int(fp) << ")\n";
00478     
00479     char s[MAXLINE];
00480     int dyn;
00481     float Rred, Ggreen, Bblue, Aalpha;
00482     
00483     // setup material properties appropriately
00484     set_default_material_properties();
00485     
00486     // set initial type to be just nodes
00487     type = faces_only;
00488     
00489     // process the node lines
00490     int status;
00491     if ((status = nodearray.getSMFNodeData(fp)) == 0) 
00492         return(status);
00493     
00494     // process the edge lines
00495     status = edgearray.getFRDEdgeData(fp);
00496     
00497     // process the face lines, this creates faces (and edges if needed)
00498     status = facearray.getSMFFaceData(fp);
00499     
00500     // process extrusion, dynamics and color data
00501     myfgets(s, MAXLINE, fp);
00502     if (s[0] == 'o')
00503         sscanf(s, "%*c %d", &objextrusions);
00504     else {
00505         printf("Bad read on objextrusions. objextrusions will be set to 0\n");  
00506         objextrusions = 0;;
00507     }
00508     
00509     myfgets(s, MAXLINE, fp);
00510     if (s[0] == 'd'){    
00511         sscanf(s, "%*c %d", &dyn);
00512         setDynamics(dynamics_type(dyn));
00513     }
00514     else {
00515         printf("Bad read on dynamics. Dynamics will be set to rest\n");  
00516         dynamics = rest;
00517     }
00518     
00519     myfgets(s, MAXLINE, fp);
00520     if (s[0] == 'r')
00521         sscanf(s, "%*c %e", &Rred);
00522     else {
00523         printf("Bad read on red color. Red color will be set to 0.5\n");  
00524         Rred = 0.5;
00525     }
00526     myfgets(s, MAXLINE, fp);
00527     if (s[0] == 'g')
00528         sscanf(s, "%*c %e", &Ggreen);
00529     else {
00530         printf("Bad read on green color. Green color will be set to 0.5\n");  
00531         Ggreen = 0.5;
00532     }
00533     myfgets(s, MAXLINE, fp);
00534     if (s[0] == 'b')
00535         sscanf(s, "%*c %e", &Bblue);
00536     else {
00537         printf("Bad read on blue color. Blue color will be set to 0.5\n");  
00538         Bblue = 0.5;
00539     }
00540     myfgets(s, MAXLINE, fp);
00541     if (s[0] == 'a')
00542         sscanf(s, "%*c %e", &Aalpha);
00543     else {
00544         printf("Bad read on alpha. Alpha will be set to 0.5\n");  
00545         Aalpha = 0.5;
00546     }
00547     
00548     setDiffuseColor(Rred, Ggreen, Bblue, Aalpha);
00549     
00550     myfgets(s, MAXLINE, fp);
00551     if (s[0] == 'n')
00552         sscanf(s, "%*c %d", &Nb_face_surf);
00553     else {
00554         printf("Bad read on the # of faces on the original mesh.\nIt will be set to the actual # of faces of this object\n");  
00555         Nb_face_surf = facearray.getNumFaces();;
00556     }
00557     
00558     double tempmass, tempspring, tempdamp;
00559     myfgets(s, MAXLINE, fp);
00560     if (s[0] == 'm')
00561         sscanf(s, "%*c %lf", &tempmass);
00562     else {
00563         printf("Bad read on the mass constant on the original mesh.\nIt will be set to 1\n");
00564         nodearray.setMasses(1.0);
00565     }
00566     nodearray.setMasses(tempmass);
00567     
00568     myfgets(s, MAXLINE, fp);
00569     if (s[0] == 's')
00570         sscanf(s, "%*c %lf", &tempspring);
00571     else {
00572         printf("Bad read on the spring constant on the original mesh.\nIt will be set to 1\n");
00573         edgearray.setSpringConstants(1.0);
00574     }
00575     edgearray.setSpringConstants(tempspring);
00576     
00577     myfgets(s, MAXLINE, fp);
00578     if (s[0] == 'p')
00579         sscanf(s, "%*c %lf", &tempdamp);
00580     else {
00581         printf("Bad read on the damp constant on the original mesh.\nIt will be set to 1\n");
00582         nodearray.setVelocityDampConstants(1.0);
00583     }
00584     nodearray.setVelocityDampConstants(tempdamp);
00585     
00586     
00587     // compute normals
00588     facearray.computeNormals();
00589     
00590     // average face normals to get per-vertex normals
00591     nodearray.interpolateNormals();
00592     
00593     // and compute the texture coords (assume cylindrical)
00594     nodearray.computeTextureCoords(Node::cylindrical);
00595     
00596     // try to read in a texture (use name of object.rgb)
00597     setTexture(NULL);
00598     
00599     // return value
00600     return(status);
00601 }
00602 
00603 char* get_next_token(char* buffer, int bufsize, FILE* fp)
00604 {
00605     char* delimiters = " \t\n\r,";
00606     char* ptr = strtok(NULL, delimiters);
00607     
00608     while (!ptr && !feof(fp)) { // ran out of stuff on this line
00609         char* b = myfgets(buffer, bufsize, fp); //  get a new line
00610         ptr = strtok(b, delimiters);
00611     }
00612     return(ptr);
00613 }
00614 
00615 // VRML format
00616 // Note: VRML files may have many subobjects inside, so this function may
00617 // recursively create many objects if create_subobjects is set to true!
00618 // (otherwise it will create one large object, but will only have one texture
00619 // defined)
00620 int Object::ReadVRML(FILE* fp, int create_subobjects)
00621 {
00622   if (debug) cerr << "Object[" << name << "]::ReadVRML(" << int(fp) << ")\n";
00623 
00624   // make a line buffer
00625   const int bufsize = 256;
00626   static char buffer[bufsize]; 
00627   strtok(buffer, "\0"); // set up for later parsing
00628         
00629   // read the initial line of the file
00630   fgets(buffer, bufsize, fp);   //  get a raw first line
00631   strtok(buffer, "\n");         // prime strtok for later parsing
00632   int is_vrml2 = 0;
00633   if (strstr(buffer, "2.")) is_vrml2 = 1;
00634   if (debug) {
00635     cerr << "file type buffer is [" << buffer << "]\n";
00636     cerr << "File format is : ";
00637     if (is_vrml2) cerr << "VRML2.0\n";
00638     else cerr << "VRML1.0\n";
00639   }
00640 
00641   // create an initial transformation matrix
00642   Matrix initial_xform; // initialized to identity
00643 
00644   // read (maybe recursively) the objects in the VRML file
00645   int status = ReadVRMLObject(fp, initial_xform, create_subobjects, is_vrml2);
00646 
00647   // and return
00648   return(status);
00649 }
00650 
00651 // function to (perhaps recursively) read a VRML object
00652 int Object::ReadVRMLObject(FILE* fp, Matrix original_xform, int create_subobjects, int is_vrml2)
00653 {
00654   if (debug) cerr << "Object[" << name << "]::ReadVRMLObject(" << int(fp) << ", xform, " << create_subobjects << ", " << is_vrml2 << ")\n";
00655     
00656   int status = 1;       // assume okay
00657     
00658   // assume bone group number/color
00659   dynamics = rest;    
00660     
00661   // setup material properties appropriately
00662   set_default_material_properties();
00663     
00664   // set initial type to be faces
00665   type = faces_only;
00666     
00667   // note: we used to do allocateNodes, Edges, and Faces here, but now we read the items into ReallocableArrays
00668   // first, then allocate the right amount of space and copy them over- saves a ton of space and is much smarter...
00669     
00670   // make a line buffer
00671   const int bufsize = 256;
00672   static char buffer[bufsize]; 
00673   strtok(buffer, "\0"); // set up for later parsing
00674     
00675   // get some locals for parsing
00676   int nesting = 1;
00677   int last_block_of_coords_index = 0;
00678   int cur_part_id = -1;
00679   int num_defs = 0;
00680   ReallocableArray<float> texture_u, texture_v;
00681     
00682   // transform values
00683   Matrix local_xform;
00684   int local_trans_nest_level = 0, local_rot_nest_level = 0, 
00685     local_scale_nest_level = 0;
00686     
00687   // start parsing
00688   while (!feof(fp) && (nesting > 0)) {
00689 
00690     // get the next token
00691     char* ptr = get_next_token(buffer, bufsize, fp);
00692     if (!ptr) break;    // hit eof
00693         
00694     { // parse it
00695       //cerr << getName() << "-token: " << ptr << ", nesting = " << nesting << ", recurse=" << create_subobjects << endl;
00696       //getchar();
00697             
00698       // process that token
00699       if (strstr(ptr, "{") || strstr(ptr,"[")) nesting++;
00700       else if (strstr(ptr, "}") || strstr(ptr, "]")) nesting--;
00701       else if (!strcmp(ptr, "DEF")) {   // defines a new object
00702         ptr = get_next_token(buffer, bufsize, fp);      // prime for name
00703         setName(ptr);
00704         num_defs++;
00705         cerr << num_defs << ": DEF of [" << ptr << "]\n";
00706         if (create_subobjects) {
00707                                         
00708           // create a composite matrix of the original and current transforms
00709           Matrix composite_xform;
00710           composite_xform.Multiply(original_xform);
00711           composite_xform.Multiply(local_xform);
00712           //cerr << "original matrix: " << endl; original_xform.Print();
00713           //cerr << "local matrix: " << endl; local_xform.Print();
00714           //cerr << "composite matrix: " << endl; composite_xform.Print();
00715 
00716           // spawn a new object and tell it to go read itself
00717           Object* new_obj = objectarray_p->addObject(ptr);
00718           new_obj->ReadVRMLObject(fp, composite_xform, ++create_subobjects, is_vrml2);
00719         }
00720       }
00721       else if (!strcmp(ptr, "Coordinate3")) {
00722                                 
00723         // now doing a new part/block of vertices
00724         cur_part_id++;
00725                 
00726         // loop, reading in the vertices
00727         last_block_of_coords_index = nodearray.getNumNodes();
00728         ReallocableArray<Point3D> local_pts;
00729         int subnesting = 0;
00730         do {
00731           ptr = get_next_token(buffer, bufsize, fp);    // prime for token
00732           if (!ptr) break;
00733                                         
00734           if (strstr(ptr, "{") || strstr(ptr, "[")) subnesting++;
00735           else if (strstr(ptr, "}") || strstr(ptr, "]")) subnesting--;
00736           else if (!strcmp(ptr, "point")) continue;
00737           else {        // must be points
00738             float x,y,z;
00739             x = atof(ptr);
00740             ptr = get_next_token(buffer, bufsize, fp);  // get y
00741             y = atof(ptr);
00742             ptr = get_next_token(buffer, bufsize, fp);  // get z
00743             z = atof(ptr);
00744             Point3D p(x,y,z);
00745                                                 
00746             // and save it
00747             local_pts.Append(p);
00748                                                 
00749             // if last point had a } or ] at the end, have to catch that, too
00750             if (strstr(ptr, "}") || strstr(ptr, "]")) subnesting--;
00751           }
00752                     
00753           //cerr << "node is " << p << endl;
00754         } while (subnesting > 0);
00755 
00756                                 // allocate space in this object for nodes and edges
00757         int num_read = local_pts.NumElements();
00758         cerr << "...read " << num_read << " nodes\n";
00759         nodearray.allocateNodes((int)(num_read * 1.10));
00760         edgearray.allocateEdges((int)(num_read * 2 * 1.10));
00761 
00762                                 // create a composite matrix of the original and current transforms
00763         Matrix composite_xform;
00764         composite_xform.Multiply(original_xform);
00765         composite_xform.Multiply(local_xform);
00766                                 //cerr << "original matrix: " << endl; original_xform.Print();
00767                                 //cerr << "local matrix: " << endl; local_xform.Print();
00768 
00769                                 // and copy them over into the object, transforming as we go
00770         for (int i=0; i<local_pts.NumElements(); i++) {
00771           Point3D p = local_pts[i];
00772           p.Xform(composite_xform.m);
00773 
00774           // and add it
00775           int index = nodearray.addNode(p);
00776           Node* node = nodearray.getNode(index);
00777           node->setPartId(cur_part_id);
00778         }
00779       }
00780       else if (!strcmp(ptr, "coordIndex")) {
00781         ReallocableArray<iPoint3D> local_faces;
00782         int subnesting = 0, num_read = 0;
00783         do {
00784           ptr = get_next_token(buffer, bufsize, fp);    // prime for token
00785                                         
00786           if (strstr(ptr, "{") || strstr(ptr, "[")) { subnesting++; continue; }
00787           if (strstr(ptr, "}") || strstr(ptr, "]")) { subnesting--; continue; }
00788                                         
00789           int i1, i2, i3;
00790           i1 = atoi(ptr);
00791           ptr = get_next_token(buffer, bufsize, fp);    // get i2
00792           i2 = atoi(ptr);
00793           ptr = get_next_token(buffer, bufsize, fp);    // get i3
00794           i3 = atoi(ptr);
00795                                         
00796           if ((i1 < 0) || (i2 < 0)) {
00797             cerr << " face[ " << facearray.getNumFaces() << "] has indices " << i1 << ", " << i2 << endl;
00798           }
00799           else { // okay values
00800             int is_two_params = (i3 == -1);     // if only two params, set flag
00801                                                 
00802             // make relative to current block of vertices for this subobject
00803             i1 += last_block_of_coords_index;
00804             i2 += last_block_of_coords_index;
00805             i3 += last_block_of_coords_index;
00806                                                 
00807             int nn = nodearray.getNumNodes();
00808             if ((i1 >= nn) || (i2 >= nn) || (i3 >= nn)) {
00809               cerr << " face[ " << facearray.getNumFaces() << "] has indices beyond bounds (" << nn << "): " 
00810                    << i1 << ", " << i2 << ", " << i3 << endl;
00811             }
00812             else if (!is_two_params) {                                                                  
00813               ptr = get_next_token(buffer, bufsize, fp);        // flush -1
00814               iPoint3D temp(i1, i2, i3);
00815               local_faces.Append(temp);
00816               num_read++;
00817             }
00818             else {  // only 2 indices- is edge-based object
00819               type = edges_only;
00820               display_mode.mode = DisplayMode::wireframe;
00821               edgearray.addEdge(i1,i2);
00822               cerr << "edge is " << i1 << " " << i2 << endl;
00823               num_read++;
00824             }
00825           }             
00826           //cerr << "face is " << i1 << " " << i2 << " " << i3 << endl;
00827         } while (subnesting > 0);
00828         cerr << "...read " << num_read << " faces\n";
00829 
00830                                 // allocate space for faces
00831         facearray.allocateFaces((int)(num_read * 1.10));
00832 
00833                                 // and copy over
00834         for (int i=0; i<local_faces.NumElements(); i++) {
00835           iPoint3D temp = local_faces[i];
00836           facearray.addFace(temp.x, temp.y, temp.z);    // actually i1, i2, i3
00837         }
00838       }
00839       else if (!strcmp(ptr, "TextureCoordinate2")) {
00840                                               
00841         // loop, reading in the vertices
00842         int subnesting = 0;
00843         do {
00844           ptr = get_next_token(buffer, bufsize, fp);    // prime for token
00845           if (!ptr) break;
00846 
00847           if (strstr(ptr, "{") || strstr(ptr, "[")) subnesting++;
00848           else if (strstr(ptr, "}") || strstr(ptr, "]")) subnesting--;
00849           else if (!strcmp(ptr, "point")) continue;
00850           else {        // must be the points
00851             float u, v;
00852             u = atof(ptr);
00853             ptr = get_next_token(buffer, bufsize, fp);  // get u
00854             v = atof(ptr);
00855 
00856             // save it
00857             texture_u.Append(u); texture_v.Append(v);
00858             //cerr << "texindex[" << texture_u.NumElements()-1 << "] = (" << u << ", " << v << ")\n";
00859                                                 
00860             // if last point had a } or ] at the end, have to catch that, too
00861             if (strstr(ptr, "}") || strstr(ptr, "]")) subnesting--;
00862           }
00863                     
00864           //cerr << "node is " << p << endl;
00865         } while (subnesting > 0);
00866         cerr << "...read " << texture_u.NumElements() << " texture coords\n";
00867       }            
00868       else if (!strcmp(ptr, "textureIndex")) {  // directly gives the u,v for each vertex
00869         ptr = get_next_token(buffer, bufsize, fp);      // flush the '['
00870         // loop, reading in the vertices
00871         int index = last_block_of_coords_index;
00872         ptr = get_next_token(buffer, bufsize, fp);      // prime for u
00873         while (!strstr(ptr, "}") && !strstr(ptr, "]")) {
00874           float u, v;
00875           u = atof(ptr);
00876           ptr = get_next_token(buffer, bufsize, fp);    // get v
00877           v = atof(ptr);
00878           ptr = get_next_token(buffer, bufsize, fp);    // get -1
00879                     
00880           // set the texture coord here
00881           Node* n = nodearray.getNode(index);
00882           if (n) {
00883             n->setTextureCoords(u, v);
00884             cerr << "setting node " << index << " texture coords to " << u << ", " << v << endl;
00885           }
00886 
00887           // prime for next time
00888           index++;
00889           ptr = get_next_token(buffer, bufsize, fp);    // prime for u again
00890         }
00891       }
00892       else if (!strcmp(ptr, "textureCoordIndex")) {     // indirectly gives texture u,v- must index through face
00893         // loop, reading in the texture indices for each face
00894         int face_index = 0, subnesting = 0;
00895         do {
00896           ptr = get_next_token(buffer, bufsize, fp);    // get a token
00897 
00898           if (strstr(ptr, "{") || strstr(ptr, "[")) { subnesting++; continue; }
00899           if (strstr(ptr, "}") || strstr(ptr, "]")) { subnesting--; continue; }
00900  
00901           // get index to face
00902           Face* f = facearray.getFace(face_index);
00903           if (!f) { cerr << "textureCoordIndex: bad face index=" << face_index << endl; continue; }
00904                                         
00905           // loop for each vertex
00906           for (int i=0; i<3; i++) {
00907             int index = atoi(ptr);      // parse the texture array index for this node
00908             if ((index < 0) || (index >= texture_u.NumElements())) { 
00909               cerr << "textureCoordIndex: index " << index << " out of bounds\n"; 
00910               continue; 
00911             }
00912             float u = texture_u[index], v = texture_v[index];   // look up the values
00913             Node* n = f->getNodeLink(i);
00914             if (!n) continue;
00915             n->setTextureCoords(u, v);
00916             //cerr << "setting face#" << face_index << ".node" << i << " (node#" << nodearray.getIndex(n)
00917             //<< ") texture coords to texindex[" << index << "] = (" << u << ", " << v << ")\n";;
00918             ptr = get_next_token(buffer, bufsize, fp);  // get next index (or flush the -1 at end)
00919             if (strstr(ptr, "}") || strstr(ptr, "]")) { subnesting--; continue; }       // for if concatenated at end
00920           }
00921 
00922           face_index++;
00923         } while (subnesting > 0);
00924       }
00925       else if (!strcmp(ptr, "Normal")) {
00926         int subnesting = 0;
00927         do {
00928           ptr = get_next_token(buffer, bufsize, fp);    // prime for token
00929           if (strstr(ptr, "{") || strstr(ptr, "[")) subnesting++;
00930           else if (strstr(ptr, "}") || strstr(ptr, "]")) subnesting--;
00931           // pretty much ignore it- we'll calculate our own anyway
00932         } while (subnesting > 0);
00933       }
00934       else if (!strcmp(ptr, "normalIndex")) {
00935         do {
00936           ptr = get_next_token(buffer, bufsize, fp);    // get a token
00937         } while (!strstr(ptr, "]"));
00938       }
00939       else if (!strcmp(ptr, "MaterialBinding")) {
00940         do {
00941           ptr = get_next_token(buffer, bufsize, fp);    // get a token
00942         } while (!strstr(ptr, "}"));
00943       }
00944       else if (!strcmp(ptr, "Material")) {
00945         ptr = get_next_token(buffer, bufsize, fp);      // flush the '{'
00946                                 
00947         // loop, reading in the attributes
00948         do {
00949           ptr = get_next_token(buffer, bufsize, fp);    // prime for attribute
00950                                         
00951           //cerr << "Material: " << ptr << endl;
00952           if (!strcmp(ptr, "ambientColor")) {
00953             double r,g,b;
00954             ptr = get_next_token(buffer, bufsize, fp);  r = atof(ptr);
00955             ptr = get_next_token(buffer, bufsize, fp);  g = atof(ptr);
00956             ptr = get_next_token(buffer, bufsize, fp);  b = atof(ptr);
00957             setAmbientColor(r,g,b,1.0);
00958           }
00959           else if (!strcmp(ptr, "diffuseColor")) {
00960             double r,g,b;
00961             ptr = get_next_token(buffer, bufsize, fp);  r = atof(ptr);
00962             ptr = get_next_token(buffer, bufsize, fp);  g = atof(ptr);
00963             ptr = get_next_token(buffer, bufsize, fp);  b = atof(ptr);
00964             setDiffuseColor(r,g,b,1.0);
00965           }
00966           else if (!strcmp(ptr, "emissiveColor")) {
00967             double r,g,b;
00968             ptr = get_next_token(buffer, bufsize, fp);  r = atof(ptr);
00969             ptr = get_next_token(buffer, bufsize, fp);  g = atof(ptr);
00970             ptr = get_next_token(buffer, bufsize, fp);  b = atof(ptr);
00971             // we don't really have emissive, so call it diffuse
00972             setDiffuseColor(r,g,b,1.0);
00973           }
00974           else if (!strcmp(ptr, "specularColor")) {
00975             double r,g,b;
00976             ptr = get_next_token(buffer, bufsize, fp);  r = atof(ptr);
00977             ptr = get_next_token(buffer, bufsize, fp);  g = atof(ptr);
00978             ptr = get_next_token(buffer, bufsize, fp);  b = atof(ptr);
00979             setSpecularColor(r,g,b,1.0);
00980           }
00981           else if (!strcmp(ptr, "shininess")) {
00982             double s;
00983             ptr = get_next_token(buffer, bufsize, fp);  s = atof(ptr);
00984             if (s <= 1.0) s *= 128;
00985             setShininess(s);
00986           }
00987           else if (!strcmp(ptr, "transparency")) {      // 1 means fully opaque
00988             // in VRML1.0, a value of 1 meant fully opaque
00989             // in VRML2.0, a value of 1 meant fully transparent
00990             // go figure
00991             double a;
00992             ptr = get_next_token(buffer, bufsize, fp);  a = atof(ptr);
00993             //if (is_vrml2) a = 1.0 - a;
00994             a = 1.0 - a;        // seems to be reversed in VRML1.0 as well?
00995             float* cur_diffuse = getDiffuseColor();
00996             setDiffuseColor(cur_diffuse[0], cur_diffuse[1], cur_diffuse[2],a);
00997           }
00998           else if (debug)
00999             cerr << "Material: UNKNOWN ATTRIBUTE: " << ptr << endl;
01000         } while (!strstr(ptr, "}"));
01001                                         
01002         // already got the '}'
01003       }  // end Material
01004       else if (!strcmp(ptr, "Texture2")) {
01005         ptr = get_next_token(buffer, bufsize, fp);      // flush the '{'
01006         ptr = get_next_token(buffer, bufsize, fp);      // flush the "filename" token
01007 
01008         ptr = get_next_token(buffer, bufsize, fp);      // get the filename
01009         if (*ptr == '\"') { ptr++; ptr[strlen(ptr)-1] = '\0'; } // get rid of the '"' chars
01010                                 
01011                                 // open the texture file
01012         if (!getTexture()) {
01013           cerr << getName() << ": reading texture from file [" << ptr << "]\n";
01014           debug = 1;
01015           setTexture(ptr);
01016           debug = 0;
01017         }
01018 
01019         if (!getTexture())
01020           cerr << getName() << ": could not read texture\n";
01021 
01022         ptr = get_next_token(buffer, bufsize, fp);      // flush the '}'
01023       }  // end Texture2
01024       else if (strstr(ptr, "Texture2Transform")) {
01025         do {
01026           ptr = get_next_token(buffer, bufsize, fp);    // get a token
01027         } while (!strstr(ptr, "}"));
01028       }
01029       else if (!strcmp(ptr, "Translation")) {
01030         local_trans_nest_level = nesting;
01031         ptr = get_next_token(buffer, bufsize, fp);      // flush the '{'
01032                 
01033         // loop, reading in the attributes
01034         ptr = get_next_token(buffer, bufsize, fp);      // prime for attribute
01035         while (!strstr(ptr, "}") && !strstr(ptr, "]")) {
01036           if (!strcmp(ptr, "translation")) {
01037             double x,y,z;
01038             ptr = get_next_token(buffer, bufsize, fp);  x = atof(ptr);
01039             ptr = get_next_token(buffer, bufsize, fp);  y = atof(ptr);
01040             ptr = get_next_token(buffer, bufsize, fp);  z = atof(ptr);
01041             local_xform.m[3][0] = x; local_xform.m[3][1] = y; local_xform.m[3][2] = z;
01042           }
01043           ptr = get_next_token(buffer, bufsize, fp);  // prime for token again
01044         }
01045 
01046                                 //ptr = get_next_token(buffer, bufsize, fp);    // flush the '}'
01047       }  // end Translation
01048       else if (!strcmp(ptr, "Rotation")) {
01049         local_rot_nest_level = nesting;
01050         ptr = get_next_token(buffer, bufsize, fp);      // flush the '{'
01051                 
01052         // loop, reading in the attributes
01053         ptr = get_next_token(buffer, bufsize, fp);      // prime for attribute
01054         while (!strstr(ptr, "}") && !strstr(ptr, "]")) {
01055           if (!strcmp(ptr, "rotation")) {
01056             double x,y,z,t;
01057             ptr = get_next_token(buffer, bufsize, fp);  x = atof(ptr);
01058             ptr = get_next_token(buffer, bufsize, fp);  y = atof(ptr);
01059             ptr = get_next_token(buffer, bufsize, fp);  z = atof(ptr);
01060             ptr = get_next_token(buffer, bufsize, fp);  t = atof(ptr);
01061             // heck if I know what to do about twist
01062             //local_xform = Point3D(x,y,z); 
01063             // PUT IN THE CODE TO CONVERT THE DIRECTION+TWIST
01064             // TO A ROTATION MATRIX AND STORE IT IN local_xform
01065           }
01066           ptr = get_next_token(buffer, bufsize, fp);  // prime for token again
01067         }
01068 
01069                                 //ptr = get_next_token(buffer, bufsize, fp);    // flush the '}'
01070       }  // end Rotation
01071       else if (!strcmp(ptr, "Scaling")) {
01072         local_scale_nest_level = nesting;
01073         ptr = get_next_token(buffer, bufsize, fp);      // flush the '{'
01074                 
01075         // loop, reading in the attributes
01076         ptr = get_next_token(buffer, bufsize, fp);      // prime for attribute
01077         while (!strstr(ptr, "}") && !strstr(ptr, "]")) {
01078           if (!strcmp(ptr, "scaling")) {
01079             double x,y,z;
01080             ptr = get_next_token(buffer, bufsize, fp);  x = atof(ptr);
01081             ptr = get_next_token(buffer, bufsize, fp);  y = atof(ptr);
01082             ptr = get_next_token(buffer, bufsize, fp);  z = atof(ptr);
01083             local_xform.m[0][0] = x; local_xform.m[1][1] = y; local_xform.m[2][2] = z; 
01084           }
01085           ptr = get_next_token(buffer, bufsize, fp);  // prime for token again
01086         }
01087 
01088         ptr = get_next_token(buffer, bufsize, fp);      // flush the '}'
01089       }  // end Scaling
01090       else if (!strcmp(ptr, "MatrixTransform")) {
01091         local_rot_nest_level = nesting;
01092         ptr = get_next_token(buffer, bufsize, fp);      // flush the '{'
01093                 
01094         // loop, reading in the attributes
01095         ptr = get_next_token(buffer, bufsize, fp);      // prime for attribute
01096         while (!strstr(ptr, "}")) {
01097           if (!strcmp(ptr, "matrix")) {
01098             double m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44;
01099                         
01100             ptr = get_next_token(buffer, bufsize, fp);  m11 = atof(ptr);
01101             ptr = get_next_token(buffer, bufsize, fp);  m12 = atof(ptr);
01102             ptr = get_next_token(buffer, bufsize, fp);  m13 = atof(ptr);
01103             ptr = get_next_token(buffer, bufsize, fp);  m14 = atof(ptr);
01104             ptr = get_next_token(buffer, bufsize, fp);  m21 = atof(ptr);
01105             ptr = get_next_token(buffer, bufsize, fp);  m22 = atof(ptr);
01106             ptr = get_next_token(buffer, bufsize, fp);  m23 = atof(ptr);
01107             ptr = get_next_token(buffer, bufsize, fp);  m24 = atof(ptr);
01108             ptr = get_next_token(buffer, bufsize, fp);  m31 = atof(ptr);
01109             ptr = get_next_token(buffer, bufsize, fp);  m32 = atof(ptr);
01110             ptr = get_next_token(buffer, bufsize, fp);  m33 = atof(ptr);
01111             ptr = get_next_token(buffer, bufsize, fp);  m34 = atof(ptr);
01112             ptr = get_next_token(buffer, bufsize, fp);  m41 = atof(ptr);
01113             ptr = get_next_token(buffer, bufsize, fp);  m42 = atof(ptr);
01114             ptr = get_next_token(buffer, bufsize, fp);  m43 = atof(ptr);
01115             ptr = get_next_token(buffer, bufsize, fp);  m44 = atof(ptr);
01116                                                 
01117             // assign it
01118             local_xform.m[0][0] = m11; local_xform.m[0][1] = m12; local_xform.m[0][2] = m13;
01119             local_xform.m[1][0] = m21; local_xform.m[1][1] = m22; local_xform.m[1][2] = m23;
01120             local_xform.m[2][0] = m31; local_xform.m[2][1] = m32; local_xform.m[2][2] = m33;
01121             local_xform.m[3][0] = m41; local_xform.m[3][1] = m42; local_xform.m[3][2] = m43; 
01122           }
01123           ptr = get_next_token(buffer, bufsize, fp);  // prime for token again
01124         }
01125 
01126                                 // already got the '}'
01127       }  // end Matrix
01128       else {    // unknown token
01129         if (debug) cerr << "UNKNOWN TOKEN: " << ptr << endl;
01130         // flush to next one?
01131       }
01132             
01133       // reset local transformations if go out of scope
01134       if (nesting < local_rot_nest_level) local_xform.Init();
01135             
01136     } // parse it
01137   }  // end while
01138   
01139   // debugging
01140   cerr << *this << ", " << cur_part_id << " subparts\n";
01141   cerr << "final nesting level was " << nesting << endl;
01142   
01143   // compute stats
01144   nodearray.computeStats();
01145   
01146   // compute normals
01147   facearray.computeNormals();
01148   
01149   // average face normals to get per-vertex normals
01150   nodearray.interpolateNormals();
01151 
01152   // assume they have read in their own texture coords and texture if they wanted it
01153   
01154   // return value
01155   return(status);
01156 }
01157 
01158 GSPEC *gs = NULL;                       /* file header buffer base pointer */
01159 int vertex_is_void(Point3D p)
01160 {
01161     if ((p.x == -1) || (p.y == -1) || (p.z == -1)) return(1);
01162     else return(0);
01163 }
01164 
01165 // for now, essentially a mesh file, but later will do convert itself
01166 // Note: Must currently convert texture image file to power of 2 dimensions
01167 //              (assuming 512x512)
01168 int Object::ReadCyberware(FILE* fp)
01169 {
01170     if (debug) 
01171         cerr << "Object[" << name << "]::ReadCyberware(" << fp << ")\n";
01172     
01173     // blow away any old data in this object
01174     nodearray.reset();
01175     edgearray.reset();
01176     facearray.reset();
01177     
01178     // read the cyberware header
01179     if ((gs = cyread(NULL, _fileno(fp))) == NULL) 
01180         return(0);
01181     
01182     // save important info from the data
01183     int is_cartesian = ((gs->flags & FLAG_CARTESIAN) > 0); // else cylindrical
01184     int lg_size = gs->lgmax - gs->lgmin + 1;
01185     int lt_size = gs->ltmax - gs->ltmin + 1;
01186     
01187     // display image info for verification
01188     {
01189         fprintf(stderr, "image: %s ", gs->name);
01190         fprintf(stderr, "hdrsiz=%d ltincr=%ld\n", sizeof(GSPEC), gs->ltincr);
01191         fprintf(stderr, "ltrange=%d:%d lgrange=%d:%d\n",
01192             gs->ltmin, gs->ltmax, gs->lgmin, gs->lgmax);
01193         fprintf(stderr, "coord type=%d (0=cyl, 1=cartesian)\n", is_cartesian);
01194     }
01195     
01196     // allocate space
01197     int projected_num_nodes = lg_size * lt_size;
01198     int projected_num_edges = 3 * projected_num_nodes;
01199     int projected_num_faces = 2 * projected_num_nodes;
01200     nodearray.allocateNodes(projected_num_nodes);
01201     edgearray.allocateEdges(projected_num_edges);
01202     facearray.allocateFaces(projected_num_faces);
01203     
01204     // for each longitude
01205     long theta = gs->lgmin * gs->lgincr;
01206     int num_good_nodes = 0, num_void_nodes = 0;
01207     for (int lg = gs->lgmin; lg <= gs->lgmax; lg++, theta += gs->lgincr) {
01208         long y = gs->ltmin * gs->ltincr;
01209         
01210         // get some handy locals
01211         float cos_theta = cos(URTOR(theta));
01212         float sin_theta = sin(URTOR(theta));
01213         
01214         // for each lattitude
01215         for (int lt = gs->ltmin; lt <= gs->ltmax; lt++, y += gs->ltincr) {
01216             long radius = GETR(gs, lt, lg);
01217             
01218             // quick exit path
01219             if (radius == long(VOIDGS(gs))) {
01220                 num_void_nodes++;
01221                 nodearray.addNode( Point3D(-1,-1,-1) );
01222                 continue;
01223             }
01224             
01225             // must be good- compute and save the value
01226             Point3D pt;
01227             if (is_cartesian) { // cartesian coords
01228                 pt = Point3D(theta,y,radius);   // actually has the x,y,z coords
01229             } else {    // cylindrical coords
01230                 long x, z;                      
01231                 x = long(sin_theta * radius);
01232                 z = -long(cos_theta * radius);  // neg Z is toward user
01233                 pt = Point3D(x,y,z);
01234             }
01235             num_good_nodes++;
01236             
01237             // convert to millimeters and save it in the nodearray
01238             const double microns_to_millimeters = 1000.0;
01239             int index = nodearray.addNode( pt/microns_to_millimeters );
01240             
01241             // get the node pointer so can set texture coords
01242             Node* new_node = nodearray.getNode(index);
01243             new_node->setTextureCoords(
01244                 double(lt-gs->ltmin)/lt_size,
01245                 double(lg-gs->lgmin)/lg_size);
01246         }
01247     }
01248     cyfree(gs);
01249     
01250     // more debugging
01251     cerr << "Had " << num_good_nodes << " good nodes and "
01252         << num_void_nodes << " void nodes\n";
01253     
01254     // now center the object (otherwise is who knows where)
01255     cerr << "Centering object\n";
01256     CenterObject();
01257     cerr << "done Centering object\n";
01258     
01259     // now do the tessellation
01260     cerr << "Generating mesh\n";
01261     int num_polys = 0, num_void_polys = 0;
01262     { // create a simple polygon list
01263         const int ypass = (gs->ltmax - gs->ltmin + 1);
01264         const int xpass = (gs->lgmax - gs->lgmin + 1);
01265         int num_verts = nodearray.getNumNodes();
01266         for (int x=0; x<(num_verts/xpass)-1; x++) 
01267             for (int y=0; y<ypass-1; y++) {
01268                 int index = x * ypass + y;
01269                 int index2 = (x+1) * ypass + y;
01270                 
01271                 // check vertices- if any are VOID, bail
01272                 Node* n_a = nodearray.getNode(index);
01273                 Node* n_b = nodearray.getNode(index+1);
01274                 Node* n_c = nodearray.getNode(index2);
01275                 Node* n_d = nodearray.getNode(index2+1);
01276                 if (vertex_is_void(n_a->p) || vertex_is_void(n_b->p) ||
01277                     vertex_is_void(n_c->p) || vertex_is_void(n_d->p)) {
01278                     num_void_polys++;
01279                     continue;
01280                 }
01281                 
01282                 // First half of square
01283                 facearray.addFace(index, index2, index+1);
01284                 num_polys++;
01285                 
01286                 // Other half of square
01287                 facearray.addFace(index+1, index2, index2+1);
01288                 num_polys++;
01289             }
01290     }
01291     cerr << "done Generating mesh\n";
01292     
01293     // more debugging
01294     cerr << "Had " << num_polys << " good polys and " 
01295         << num_void_polys << " void polygons\n";
01296     
01297     // set other parameters
01298     dynamics = deformable;
01299     set_default_material_properties();
01300     setDiffuseColor(1.0, 1.0, 1.0, 1.0);        // make the mesh white, with texture
01301     type = faces_only;
01302     
01303     // read in the texture
01304     //setTexture(NULL);
01305     
01306 #ifdef TESTING_NEW_TEXTURE_COORDS
01307     // compute texture coords
01308     if (is_cartesian)
01309         nodearray.computeTextureCoords(Node::linear);
01310     else nodearray.computeTextureCoords(Node::cylindrical);
01311 #endif
01312     
01313     // recompute normals (since surface is new)
01314     facearray.computeNormals();
01315     
01316     // average face normals to get per-vertex normals
01317     nodearray.interpolateNormals();
01318     
01319     // return value
01320     return(1);
01321 }
01322 
01323 int Object::Read(char* dirname_part, char* filename_part)
01324 {
01325         // construct the full pathname w.r.t. the world.desc file
01326         char filename[256];
01327         sprintf(filename, "%s/%s", dirname_part, filename_part);
01328 
01329         // store path
01330     setPath(dirname_part);
01331 
01332         // read now
01333         return Read(filename);
01334 }
01335 
01336 int Object::Read(char* filename) {
01337     if (debug) cerr << "Object[" << name << "]::Read(" << filename << ")\n";
01338     
01339     // open the mesh data file
01340     FILE* meshfile;
01341     if ((meshfile = fopen(filename, "r")) == NULL) {
01342         printf("Could not open node data file\n");
01343         return(0);
01344     }
01345     
01346     // read `em in
01347     char* ext = myextension(filename);
01348     if (!strcmp(ext, ".mesh")) ReadMesh(meshfile);
01349     else if (!strcmp(ext, ".smf")) ReadSMF(meshfile);
01350     else if (!strcmp(ext, ".obj")) ReadOBJ(meshfile);
01351     else if (!strcmp(ext, ".cy")) ReadCyberware(meshfile);
01352     else if (!strcmp(ext, ".frd")) ReadFRD(meshfile);
01353     else if (!strcmp(ext, ".wrl")) ReadVRML(meshfile);
01354     else if (!strcmp(ext, ".iv")) ReadVRML(meshfile);
01355     else if (!strcmp(ext, ".inp")) ReadAmira(meshfile);
01356         else if (!strcmp(ext, ".sobj")) ReadSOBJ(meshfile);
01357 
01358     // close the file
01359     fclose(meshfile);
01360     
01361     // setup other vars
01362     dl_needs_refresh = 1;
01363     
01364     // and return success
01365     return(0);
01366 }
01367 
01368 void Object::ParseAttribute(char* buffer)
01369 {
01370     // locals
01371     int had_bambient = 0;
01372     int had_bdiffuse = 0;
01373     int had_bspecular = 0;
01374     
01375     // parse the attribute
01376     char attr_name[80]; sscanf(buffer, "%s", attr_name);
01377     cerr << " " << attr_name << " [";
01378     
01379     // parse values based on attribute keyword
01380     if (!strcmp(attr_name, "ambient:")) {
01381         float r,g,b,a;
01382         sscanf(buffer, "%*s %f %f %f %f", &r, &g, &b, &a);
01383         setAmbientColor(r,g,b,a);
01384         if (!had_bambient) setBackAmbientColor(r,g,b,a);
01385         cerr << r << "," << g << "," << b << "," << a;
01386     }
01387     else if (!strcmp(attr_name, "bambient:")) {
01388         float r,g,b,a;
01389         sscanf(buffer, "%*s %f %f %f %f", &r, &g, &b, &a);
01390         setBackAmbientColor(r,g,b,a);
01391         cerr << r << "," << g << "," << b << "," << a;
01392         had_bambient = 1;
01393     }
01394     else if (!strcmp(attr_name, "diffuse:")) {
01395         float r,g,b,a;
01396         sscanf(buffer, "%*s %f %f %f %f", &r, &g, &b, &a);
01397         setDiffuseColor(r,g,b,a);
01398         if (!had_bdiffuse) setBackDiffuseColor(r,g,b,a);
01399         cerr << r << "," << g << "," << b << "," << a;
01400     }
01401     else if (!strcmp(attr_name, "bdiffuse:")) {
01402         float r,g,b,a;
01403         sscanf(buffer, "%*s %f %f %f %f", &r, &g, &b, &a);
01404         setBackDiffuseColor(r,g,b,a);
01405         cerr << r << "," << g << "," << b << "," << a;
01406         had_bdiffuse = 1;
01407     }
01408     else if (!strcmp(attr_name, "specular:")) {
01409         float r,g,b,a;
01410         sscanf(buffer, "%*s %f %f %f %f", &r, &g, &b, &a);
01411         setSpecularColor(r,g,b,a);
01412         if (!had_bspecular) setBackSpecularColor(r,g,b,a);
01413         cerr << r << "," << g << "," << b << "," << a;
01414     }
01415     else if (!strcmp(attr_name, "bspecular:")) {
01416         float r,g,b,a;
01417         sscanf(buffer, "%*s %f %f %f %f", &r, &g, &b, &a);
01418         setBackSpecularColor(r,g,b,a);
01419         cerr << r << "," << g << "," << b << "," << a;
01420         had_bspecular = 1;
01421     }
01422     else if (!strcmp(attr_name, "shininess:")) {
01423         float s;
01424         sscanf(buffer, "%*s %f", &s);
01425         setShininess(s);
01426         cerr << s;
01427     }
01428     else if (!strcmp(attr_name, "bshininess:")) {
01429         float s;
01430         sscanf(buffer, "%*s %f", &s);
01431         setBackShininess(s);
01432         cerr << s;
01433     }
01434     else if (!strcmp(attr_name, "texture:")) {
01435         char texturefilename[256];
01436         sscanf(buffer, "%*s %s", texturefilename);
01437         setTexture(texturefilename);
01438         cerr << texturefilename;
01439     }
01440     else if (!strcmp(attr_name, "texturecoords:")) {
01441         int model;
01442         sscanf(buffer, "%*s %d", &model);
01443         setTextureCoords(Node::texture_coord_type(model));
01444         cerr << model;
01445     }
01446     else if (!strcmp(attr_name, "behavior:")) {
01447         char str[80];
01448         sscanf(buffer, "%*s %s", str);
01449         behavior_type b = none;
01450         if (!strcmp(str, "none")) b = none;
01451         else if (!strcmp(str, "grabbing")) b = grabbing;
01452         else if (!strcmp(str, "cutting")) b = cutting;
01453         else if (!strcmp(str, "deleting")) b = deleting;
01454         else if (!strcmp(str, "poking")) b = poking;
01455         else if (!strcmp(str, "ablating")) b = ablating;
01456         else if (!strcmp(str, "cauterizing")) b = cauterizing;
01457         else if (!strcmp(str, "dilating")) b = dilating;
01458         else if (!strcmp(str, "coloring")) b = coloring;
01459         else if (!strcmp(str, "quisar_test")) b = quisar_test;
01460         else if (!strcmp(str, "beating")) b = beating;
01461         else if (!strcmp(str, "fluid")) b = fluid;
01462         else {  // old format-uses a number, so use old numbering
01463             int val = atoi(str);
01464             switch (val) {
01465             case int(none): b = none; break;
01466             case int(grabbing): b = grabbing; break;
01467             case int(cutting): b = cutting; break;
01468             case int(deleting): b = deleting; break;
01469             case int(poking): b = poking; break;
01470             case int(ablating): b = ablating; break;
01471             case int(cauterizing): b = cauterizing; break;
01472             case int(dilating): b = dilating; break;
01473             case int(coloring): b = coloring; break;
01474             case int(quisar_test): b = quisar_test; break;
01475             case int(beating): b = beating; break;
01476             case int(fluid): b = fluid; break;
01477             }
01478         }
01479         setBehavior(b);
01480         cerr << b;
01481     }
01482     else if (!strcmp(attr_name, "dynamics:")) {
01483         char str[80];
01484         sscanf(buffer, "%*s %s", str);
01485         dynamics_type d = rest;
01486         if (!strcmp(str, "rest")) d = rest;
01487         else if (!strcmp(str, "rigid")) d = rigid;
01488         else if (!strcmp(str, "deformable")) d = deformable;
01489         else if (!strcmp(str, "articulate")) d = articulate;
01490         else if (!strcmp(str, "button")) d = button;
01491         else {  // old format-uses a number, so use old numbering
01492             int val = atoi(str);
01493             switch (val) {
01494                                 case 0: d = rest; break;
01495                                 case 1: d = rigid; break;
01496                                 case 2: d = deformable; break;
01497                                 case 3: d = articulate; break;
01498                                 case 4: d = button; break;
01499             }
01500         }
01501         setDynamics(d);
01502         cerr << d;
01503     }
01504     else if (!strcmp(attr_name, "mass:")) {
01505         float m;
01506         sscanf(buffer, "%*s %f", &m);
01507         getNodeArray()->setMasses(m);
01508         cerr << m;
01509         }
01510     else if (!strcmp(attr_name, "deformonlyongrab:")) {
01511         int i;
01512         sscanf(buffer, "%*s %d", &i);
01513                 do_deform_only_on_grab = i;
01514         cerr << i;
01515     }
01516     else if (!strcmp(attr_name, "springconstant:")) {
01517         float sc;
01518         sscanf(buffer, "%*s %f", &sc);
01519         getEdgeArray()->setSpringConstants(sc);
01520         cerr << sc;
01521     }
01522     else if (!strcmp(attr_name, "dampconstant:")) {
01523         float dc;
01524         sscanf(buffer, "%*s %f", &dc);
01525         getNodeArray()->setVelocityDampConstants(dc);
01526         cerr << dc;
01527     }
01528     else if (!strcmp(attr_name, "nummethod:")) {
01529         int nm;
01530         sscanf(buffer, "%*s %d", &nm);
01531         setNumMethod(Object::numerical_method(nm));
01532         cerr << nm;
01533     }
01534     else if (!strcmp(attr_name, "visible:")) {
01535         int on;
01536         sscanf(buffer, "%*s %d", &on);
01537         SetVisible(on);
01538         cerr << on;
01539     }
01540         else if (!strcmp(attr_name, "translate:")) {
01541         float x, y, z;
01542         sscanf(buffer, "%*s %f %f %f", &x, &y, &z);
01543                 Point3D vector(x,y,z);
01544         Move(vector);
01545         cerr << x << " " << y << " " << z;
01546     }
01547         else if (!strcmp(attr_name, "scale:")) {
01548         float scale_factor;
01549         sscanf(buffer, "%*s %f", &scale_factor);
01550         Scale(scale_factor);
01551         cerr << scale_factor;
01552     }
01553         else if (!strcmp(attr_name, "rotate:")) {
01554         char axis; double angle;
01555         sscanf(buffer, "%*s %c %lf", &axis, &angle);
01556         Rotate(axis, angle);
01557         cerr << axis << " " << angle;
01558     }
01559     else if (!strcmp(attr_name, "collisionextent:")) {
01560         int ce;
01561         sscanf(buffer, "%*s %d", &ce);
01562         setCollisionExtent(Object::collision_extent_type(ce));
01563         cerr << ce;
01564     }
01565     else if (!strcmp(attr_name, "linkedpart:")) {
01566         int lp;
01567         sscanf(buffer, "%*s %d", &lp);
01568         setLinkedPart(lp);
01569         cerr << lp;
01570     }
01571     else if (!strcmp(attr_name, "hingenode:")) {
01572                         int i; 
01573                         int si;
01574                         float x,y,z;
01575                         double maxopen, maxclosed;
01576                         int col0 = -1;
01577                         int col1 = -1;
01578                         int col2 = -1;
01579 
01580                         sscanf(buffer, "%*s %d %d %f %f %f %lf %lf %d %d %d", 
01581                                 &si, &i, &x, &y, &z, &maxopen, &maxclosed, 
01582                                 &col0, &col1, &col2);
01583                         Point3D r(x,y,z);
01584                         this->addToHinges(si,i,r,maxopen,maxclosed,col0,col1,col2);
01585 
01586                         cerr << si << " " << i << " " << r << " " << maxopen << " " 
01587                                  << maxclosed << " " << col0 << " " << col1 << " " << col2;
01588         }
01589     else if (!strcmp(attr_name, "sensoroffset:")) {
01590         float x,y,z;
01591         sscanf(buffer, "%*s %f %f %f", &x, &y, &z);
01592         Point3D p(x,y,z);
01593         setSensorOffset(p);
01594         cerr << p;
01595     }
01596     else if (!strcmp(attr_name, "tipnode:")) {
01597         int tn;
01598         sscanf(buffer, "%*s %d", &tn);
01599         setTip(tn);
01600         cerr << tn;
01601     }
01602     else if (!strcmp(attr_name, "collisionfaces:")) {
01603 
01604         // make some locals
01605 
01606         char* delimiters = " \t";
01607 
01608         char* ptr = strtok(buffer, delimiters); // flush attr_name
01609 
01610         
01611 
01612         // parse the face indices on the line and add them to the collisionfaces list
01613 
01614         while ((ptr = strtok(NULL, delimiters)) != NULL ) {
01615 
01616             int face_index = atoi(ptr);
01617 
01618             Face* face = getFaceArray()->getFace(face_index);
01619 
01620             if (!face) {
01621 
01622                 cerr << "Bad collision face index: " << face_index << endl;
01623 
01624                         }
01625 
01626             else {
01627 
01628                 // add to collision face list
01629 
01630                 collision_faces.Append(face);
01631 
01632             }
01633 
01634         }  
01635 
01636     }   
01637         else if (!strcmp(attr_name, "cuttingfaces:")) {
01638                 char* delimiters = " \t\n";
01639                 char* ptr = strtok(buffer, delimiters);
01640                 while(ptr) {
01641                         ptr = strtok(NULL,delimiters);
01642                         if (ptr == NULL) break;
01643                         int f = atoi(ptr);
01644                         cerr << f << " " ;
01645                         addToCuttingFaces(getFaceArray()->getFace(f));
01646                 }
01647         }
01648         else if (!strcmp(attr_name, "cuttingedges:")) {
01649                 char* delimiters  = " \t\n";
01650                 char* ptr = strtok(buffer, delimiters);
01651                 while(ptr) {
01652                         ptr = strtok(NULL,delimiters);
01653                         if (ptr == NULL) break;
01654                         int e = atoi(ptr);
01655                         cerr << e << " " ;
01656                         addToCuttingEdges(getEdgeArray()->getEdge(e));
01657                 }
01658         }
01659     else {    // unknown
01660         cerr << "UNKNOWN ATTRIBUTE";
01661     }
01662     
01663     // end the line
01664     cerr << "]\n";
01665 }
01666 
01667 void Object::addToHinges(int si, int ai, Point3D r, double maxopen, double maxclosed, 
01668                                                                                                  int col0, int col1, int col2)
01669 { 
01670         hinge h;
01671   h.sensor_index = si;
01672         h.node_index = ai; 
01673         h.rotation_vector = r;
01674         h.max_open = maxopen;
01675         h.max_closed = maxclosed;
01676 
01677         int posnum = 0;
01678         int negnum = 0;
01679         
01680         for (int i=0; i<3; i++) {
01681                 h.colors_pos[i] = -1;
01682                 h.colors_neg[i] = -1;
01683         }
01684 
01685 
01686         if (col0 > -1) {
01687                 h.colors_pos[posnum] = col0;
01688                 posnum++;
01689         }
01690         else {
01691                 h.colors_neg[negnum] = col0;
01692                 negnum++;
01693         }
01694 
01695         if (col1 > -1) {
01696                 h.colors_pos[posnum] = col1;
01697                 posnum++;
01698         }
01699         else {
01700                 h.colors_neg[negnum] = col1;
01701                 negnum++;
01702         }
01703 
01704         if (col2 > -1) {
01705                 h.colors_pos[posnum] = col2;
01706                 posnum++;
01707         }
01708         else {
01709                 h.colors_neg[negnum] = col2;
01710                 negnum++;
01711         }
01712 
01713         hinges.Append(h);
01714         return;
01715 }
01716   
01717 void Object::setAmbientColor(float red, float green, float blue, float alpha)
01718 { 
01719     // set the values
01720     mat_ambient[0] = red; 
01721     mat_ambient[1] = green; 
01722     mat_ambient[2] = blue;
01723     mat_ambient[3] = alpha;
01724     
01725     // update display list
01726     dl_needs_refresh = 1; 
01727 }
01728 
01729 void Object::setDiffuseColor(float red, float green, float blue, float alpha)
01730 { 
01731 #ifdef SUN
01732     // Hack: There is a bug in the Solaris version of OpenGL where 
01733     // alpha-blended textures aren't handled correctly and produce an
01734     // artifact.  The workaround is to have the r,g,b values the same
01735     // as the alpha if you're doing this
01736     if (texture) {
01737         red = green = blue = alpha;
01738     }
01739 #endif
01740     
01741     // set the values
01742     mat_diffuse[0] = red; 
01743     mat_diffuse[1] = green; 
01744     mat_diffuse[2] = blue;
01745     mat_diffuse[3] = alpha;
01746     
01747     // update display list
01748     dl_needs_refresh = 1; 
01749 }
01750 
01751 void Object::setSpecularColor(float red, float green, float blue, float alpha)
01752 { 
01753     // set the values
01754     mat_specular[0] = red; 
01755     mat_specular[1] = green; 
01756     mat_specular[2] = blue;
01757     mat_specular[3] = alpha;
01758     
01759     // update display list
01760     dl_needs_refresh = 1; 
01761 }
01762 
01763 void Object::setShininess(float s)
01764 { 
01765     // set the values
01766     mat_shininess[0] = s; 
01767     
01768     // update display list
01769     dl_needs_refresh = 1; 
01770 }
01771 
01772 void Object::setBackAmbientColor(float red, float green, float blue, float alpha)
01773 { 
01774     // set the values
01775     back_mat_ambient[0] = red; 
01776     back_mat_ambient[1] = green; 
01777     back_mat_ambient[2] = blue;
01778     back_mat_ambient[3] = alpha;
01779     
01780     // update display list
01781     dl_needs_refresh = 1; 
01782 }
01783 
01784 void Object::setBackDiffuseColor(float red, float green, float blue, float alpha)
01785 { 
01786     // Hack: There is a bug in the Solaris version of OpenGL where 
01787     // alpha-blended textures aren't handled correctly and produce an
01788     // artifact.  The workaround is to have the r,g,b values the same
01789     // as the alpha if you're doing this
01790     if (texture) {
01791         red = green = blue = alpha;
01792     }
01793     
01794     // set the values
01795     back_mat_diffuse[0] = red; 
01796     back_mat_diffuse[1] = green; 
01797     back_mat_diffuse[2] = blue;
01798     back_mat_diffuse[3] = alpha;
01799     
01800     // update display list
01801     dl_needs_refresh = 1; 
01802 }
01803 
01804 void Object::setBackSpecularColor(float red, float green, float blue, float alpha)
01805 { 
01806     // set the values
01807     back_mat_specular[0] = red; 
01808     back_mat_specular[1] = green; 
01809     back_mat_specular[2] = blue;
01810     back_mat_specular[3] = alpha;
01811     
01812     // update display list
01813     dl_needs_refresh = 1; 
01814 }
01815 
01816 void Object::setBackShininess(float s)
01817 { 
01818     // set the values
01819     back_mat_shininess[0] = s; 
01820     
01821     // update display list
01822     dl_needs_refresh = 1; 
01823 }
01824 
01825 
01826 // Note: caller should also do a nodearray.computeTextureCoords()
01827 void Object::setTexture(char* filename)
01828 {
01829     char texture_filename[256];
01830     if (filename)     // use the name we were given
01831         strncpy(texture_filename, filename, 256);
01832     else {    // create name from name of object
01833                 char regular_filename[256];
01834                 char* filePath = getPath();
01835                 
01836                 if (strcmp(getPath(), "")) {
01837                         sprintf(regular_filename, "%s/%s", getPath(), getName());
01838                 }
01839                 else {
01840                         sprintf(regular_filename, "%s", getName());
01841                 }
01842         strncpy(texture_filename, regular_filename, 256);        // copy over name
01843         char* ptr = myrindex(texture_filename, '.');      // strip off extension
01844         if (ptr) {
01845                         sprintf(ptr, ".rgb");                    // add on a .rgb
01846                 }
01847         else {
01848                         sprintf(texture_filename, "%s.rgb", texture_filename);
01849                 }
01850     }
01851 
01852     // create the texture
01853     // NOTE: width and height must be powers of 2 for texture! 
01854     // (maybe next release of OGL, but was still case in 1.2!)
01855     if (debug) 
01856         cerr << "Reading texture from file [" << texture_filename << "]\n";
01857     
01858     // read the texture
01859     texture = read_texture(texture_filename, 
01860         &texture_size.x, &texture_size.y, &texture_size.z);
01861     
01862     // debugging
01863     if (debug) {
01864         if (texture) 
01865             cerr << " size = " << texture_size.x << "x" << texture_size.y 
01866             << " @ " << texture_size.z << endl;
01867         else cerr << " texture [" << texture_filename << "] not found!\n";
01868     }
01869     
01870     // setup other vars
01871     dl_needs_refresh = 1;
01872 }
01873 
01874 void Object::setTextureCoords(Node::texture_coord_type t)
01875 {
01876     nodearray.computeTextureCoords(t);
01877     dl_needs_refresh = 1;
01878 }
01879 
01880 void Object::moveTexture(Point3D translation, Point3D scaling)
01881 {
01882     nodearray.moveTexture(translation, scaling);
01883     dl_needs_refresh = 1;
01884 }
01885 
01886 void Object::tipUpdate()
01887 {
01888         if (nodearray.getNumNodes() <= 0) return;
01889 
01890     tipdelta = getTip() - oldtip;
01891     oldtip = getTip();
01892 }
01893 
01894 void Object::Update()
01895 {
01896     if (debug) cerr << "Object[" << name << "]::Update()\n";
01897 
01898         // tell other threads that we're modifying this one now
01899         // (poor man's mutex- just want to indicate, not lock)
01900         being_modified = 1;
01901 
01902 
01903 
01904 
01905     // handle sensors/motion links here
01906     if (sensor_p) {    
01907         
01908         // Note: I'd ifdefed this out again to *quickly* provide support for 
01909         // multihinged objects for Cynthia's articulating hand- the right thing 
01910         // to do is to put it into the more general xformObject framework.  But 
01911         // either way, either method needs to provide support for xforms of 
01912         // "rest'(static) objects, hinged objects, multihinged objects, 
01913         // "plunger" objects (syringes), etc
01914 #define NEW_XFORM_OBJECT_WAY
01915 #ifdef NEW_XFORM_OBJECT_WAY
01916                 // xform hinged object (hinged objects are still a special case 
01917                 // until we generalize it into the xformObject() method
01918         if (linked_part == -1)
01919            xformHingedObject();
01920         else // update us (handles parts, everything, etc)
01921            xformObject();
01922 #else
01923         // if we're linked to a sensor, update us
01924         // this is kinda like an external force acting on the
01925         // whole object (maybe it should be a force, now it's
01926         // just a 'move here' kinda thing)
01927         if (linked_part == -1) {                        // xform hinged object
01928            xformHingedObject();
01929         }
01930         else if (linked_part == 0) {            // xform whole thing
01931             nodearray.setToInitialPosition();   // xform from initial position
01932             nodearray.xformNodesOfPart(-1, sensor_p->m, sensor_offset, 
01933                 sensor_p->pos);
01934         }
01935         else {  // just a part of object moved- do translation only
01936             // get closest node in our mesh
01937             //Node* closest_node = findClosestNode(sensor_p->pos);
01938             //Point3D motion_vector = sensor_p->pos - closest_node->p;
01939             Point3D motion_vector = sensor_p->pos - sensor_p->init_pos;
01940             nodearray.moveNodesOfPart(linked_part, motion_vector, 1);
01941         }
01942 #endif
01943         
01944 
01945                 // indication that nodes were modified
01946         addEverythingToUpdateList();
01947 
01948 
01949         // no matter what, we've moved
01950         dl_needs_refresh = 1;
01951     }
01952     
01953     // Note: we don't reset the nodearray's collision flags here because we only
01954     //        do collision testing on the first update cycle now
01955     switch (dynamics) {
01956                 case rest: {    
01957                         // for objects that have no dynamics of their own
01958                         // Note: they CANNOT be picked up and DON'T deform (deformable) and 
01959                         //        DON'T undergo rigid body dynamics
01960                         break;
01961                 }
01962 
01963                 case button: {
01964                         // for objects that have no dynamics of their own
01965                         // Note: they CAN be picked up and DON'T deform (deformable) and 
01966                         //        DON'T undergo rigid body dynamics
01967                         break;
01968                 }
01969 
01970                 case rigid: {
01971                         // rigid bodies don't spontaneously move (sez Newton)
01972                         // but maybe they should handle a per-node velocity, etc
01973         
01974                         // if we're not attached to a sensor, then process rigid body dynamics
01975                         if (!sensor_p) {
01976             
01977                                 // Note: There is a problem if we do the current code, then grab the 
01978                                 // object with a sensor.  In the current updateNodesRigid() code, it has
01979                                 // to reset the init_pos of the nodes as it goes.  The problem is
01980                                 // that then, later, if we grab the tool, our sense of the initial
01981                                 // position is wrong, as is the sensor_offset, hinge_point, etc
01982 
01983 
01984                                 // update node positions- rigid method
01985                                 int nodes_moved = nodearray.updateNodesRigid();
01986 
01987 
01988                                 // if have updated, assume we need to refresh graphics
01989                                 if (nodes_moved) {
01990                                         // indication that nodes were modified
01991 
01992                                         addEverythingToUpdateList();
01993 
01994 
01995                                         dl_needs_refresh = 1;
01996                                 }
01997 
01998 
01999                                 // this is for our pole and ring edge objects, so that the quads
02000                                 // redraw as front-facing as the camera moves
02001                                 if (type == edges_only) {
02002                                         dl_needs_refresh = 1;
02003 
02004                                 }
02005 
02006                         }
02007                         break;
02008                 }
02009 
02010                 case deformable: {
02011                         // useful mode for some applications
02012                         if (do_deform_only_on_grab && !being_grabbed) {
02013                                 if (nodearray.orderedNodesFlag)  {
02014                                         edgearray.computeNewRestLengths();
02015                                         nodearray.orderedNodesFlag = 0;
02016                                 }
02017                                 break;
02018                         }
02019 
02020                         // update node positions
02021                         int nodes_moved = nodearray.updateNodes();
02022         
02023                         // if have updated and moved something, we need to update graphics
02024                         if (nodes_moved) {
02025                                 // indication that nodes were modified
02026 
02027                                 addEverythingToUpdateList();
02028 
02029 
02030                                 if (do_preserve_volume) { //attempt to maintain volume
02031                                         PreserveVolume();
02032                                 }
02033 
02034                                 dl_needs_refresh = 1;
02035                         }               
02036                         // cbnote and add moved faces to bs tree
02037 
02038         
02039                         break;
02040                 }
02041         
02042                 case articulate:  {
02043                         // update myself
02044                         extern game_enum game_mode;
02045                         if (game_mode == knot_game || game_mode == vessel_knot_game) {
02046                                 nodearray.updateThread();
02047 
02048                         }
02049                         else {
02050 
02051                                 // for vessel_game or others
02052                                 nodearray.updateArticulateEdge();
02053                         }
02054 
02055 
02056                         // indication that nodes were modified
02057 
02058                         addEverythingToUpdateList();
02059 
02060 
02061 
02062                         // if have updated, assume we need to refresh graphics
02063                         dl_needs_refresh = 1;
02064         
02065                         break;
02066                 }
02067     }  // end switch
02068 
02069 
02070 
02071 
02072         // no longer in use
02073         being_modified = 0;
02074 }
02075 
02076 // print out position and angles of the given plane tool
02077 void Object::getPosAngle(Point3D* pos_p, Point3D* rot_p)
02078 {
02079     if (strncmp(name, "plane #", 7))
02080         return;
02081     // node 0 is the center, normal of node 0 is the direction
02082     Node *node0 = nodearray.getNode(0);
02083     Point3D pos = node0->p;
02084     Point3D normal = node0->getNormal();
02085     double dotx = normal.Dot(Point3D(1,0,0));
02086     double doty = normal.Dot(Point3D(0,1,0));
02087     double dotz = normal.Dot(Point3D(0,0,1));
02088     double thetax = (acos(dotx))*(180.0/PI);
02089     double thetay = (acos(doty))*(180.0/PI);
02090     double thetaz = (acos(dotz))*(180.0/PI);
02091     
02092     *pos_p = pos;
02093     rot_p->x = thetax;
02094     rot_p->y = thetay;
02095     rot_p->z = thetaz;
02096 }
02097 
02098 // split an object in two with respect to the given planar tool
02099 void Object::SplitColor(Object* tool)
02100 {
02101     if (debug) cerr << "Object::SplitColor(" << tool << ")\n";
02102     
02103     if (!strstr(tool->name, "grabby") && !strstr(tool->name, "plane"))
02104         return;    // only split about the planar tools
02105     
02106     // the 0th face of the tool is the correct plane
02107     Face *splitting_face = tool->facearray.getFace(0);
02108     nodearray.SplitColor(splitting_face);
02109     
02110     // update graphics required in case viewing
02111     dl_needs_refresh = 1;
02112 }
02113 
02114 void Object::DeleteNodesOfColor(int color)
02115 {
02116     if (debug) cerr << "Object::DeleteNodeOfColor(" << color << ")\n";
02117     facearray.EraseFacesOfColor(color); // get rid of the faces...
02118     edgearray.DeleteFacelessEdges();
02119     //nodearray.DeleteNodesOfColor(color);      // and the nodes that love them
02120     dl_needs_refresh = 1;
02121 }
02122 
02123 double Object::distanceToBoundingBox(Point3D pt)
02124 {
02125     // get bounding box of object
02126     Point3D cur_min, cur_max;
02127     getBoundingBox(&cur_min, &cur_max);
02128     
02129     // if we're inside, forget it
02130     if ((pt.x > cur_min.x) && (pt.y > cur_min.y) && (pt.z > cur_min.z) &&
02131         (pt.x < cur_max.x) && (pt.y < cur_max.y) && (pt.z < cur_max.z))
02132         return(0);      // are inside the bbox
02133     
02134     // otherwise, calc distance to it
02135     // this is totally wrongo:
02136     //Point3D dist_vect(MIN(cur_min.x-pt.x,pt.x-cur_max.x),
02137     //    MIN(cur_min.y-pt.y,pt.y-cur_max.y),
02138     //    MIN(cur_min.z-pt.z,pt.z-cur_max.z));
02139     
02140     // let's try again:
02141     double xdist, ydist, zdist;
02142     xdist = ydist = zdist = 0.0;
02143     if (pt.x > cur_max.x)
02144       xdist = pt.x - cur_max.x;
02145     else if (pt.x < cur_min.x)
02146       xdist = cur_min.x - pt.x;
02147     if (pt.y > cur_max.y)
02148       ydist = pt.y - cur_max.y;
02149     else if (pt.y < cur_min.y)
02150       ydist = cur_min.y - pt.y;
02151     if (pt.z > cur_max.z)
02152       zdist = pt.z - cur_max.z;
02153     else if (pt.z < cur_min.z)
02154       zdist = cur_min.z - pt.z;
02155 
02156     // and return the distance
02157     return(sqrt(xdist*xdist + ydist*ydist + zdist*zdist));
02158 }
02159 
02160 // this object is a tool (ie has a behavior)
02161 void Object::HandleBehavior()
02162 {
02163   if (debug) cerr << "Object[" << getName() << "]::HandleBehavior()\n";
02164 
02165   // update the notion of the tip delta movement
02166   tipUpdate();
02167     
02168   // handle both interactive and spontaneous behaviors
02169   HandleInteractiveBehavior();
02170   HandleSpontaneousBehavior();
02171 }
02172 
02173 // Handles interaction type behavior, i.e. a tool interacting with the object. (Chris, HI)
02174 void Object::HandleInteractiveBehavior() 
02175 {
02176   // Check if sensor is available, otherwise there is no interactive behavior to process 
02177   if (!sensor_p) {    
02178     closest_node = NULL;    // reset things
02179     return;
02180   }   
02181 
02182   // Local constants
02183   // Note: setting this value this high certainly seems odd, but if it is
02184   //             set lower, then grabbing is somewhat problematic.  It seems
02185   //             that we all too eaily loose our grasp on our closest_node and
02186   //             break our grab and do it in such a way that it's still fixed 
02187   //             in position.  So, for now, we put it big- later, let's solve this.
02188   const double max_distance_to_affect = 250.5;    // mm
02189     
02190   // Vectors for force calculation
02191   Point3D haptic_force(0,0,0);
02192   float twist_force = 0, grip_force = 0;
02193         
02194   //
02195   // INTERACTION BEHAVIORS
02196   //
02197   // The rationale is this- a tool affects an object that it's in contact
02198   // (i.e., colliding) with.  Therefore, this is a special case of a
02199   // collision (for example, a drill collides with a skull and, in so
02200   // doing, makes a hole).
02201   // 
02202   // However, once in contact (meaning once the closest_node is set),
02203   // we remain in contact.  This takes care of a few things: sometimes
02204   // when in contact, the tool can barely move out of the bbox of the
02205   // affected object and it would release.  Also, this way is faster
02206   // because we don't do a search for what object we're in contact with
02207   // if we've already got one.
02208   //
02209   // Also note- the way this is structured right now, on first contact,
02210   // we can only affect one object at a time (the first one we find that 
02211   // intersects our bounding sphere/box).
02212   //
02213   switch (behavior) {          
02214   case quisar_test: {
02215     // if not the v2 sensor, forget it
02216     if (sensor_p->type != Sensor::haptic_v2_sensor) break;
02217       
02218     // cast away!
02219     Haptic_v2* haptic_p = (Haptic_v2*)sensor_p;
02220       
02221     // This thing is supposed to handle the first contact: that's all.
02222     Object* other_obj = NULL;
02223       
02224     // we drop a grabbed node if sensor becomes inactive
02225     if (closest_node && (haptic_p->active[0] < 0.5)) {
02226       closest_node->setFixedType(Node::NOT);  
02227       being_grabbed = 0;
02228       closest_node->getNodeArray()->getObject()->being_grabbed = 0;
02229       closest_node = NULL;
02230     }
02231       
02232     // now we find a closest node if we don't have one from prior grab
02233     if (!closest_node)
02234       closest_node = objectarray_p->FindClosestUnlinkedNode(getTip());
02235       
02236     // and find the object it belongs to
02237     if (closest_node)
02238       other_obj = closest_node->getNodeArray()->getObject();
02239       
02240     // we may not have found any closest node if we're outside bboxes
02241     if (!closest_node) {
02242       haptic_p->my_space = haptic_p->my_space_v;
02243       haptic_p->points_to_save->erase_all();
02244       break;
02245     }
02246       
02247     Point3D vect = getTip() - closest_node->p;
02248       
02249     if ((haptic_p->active[0] < 0.5) || 
02250         (vect.Length() > max_distance_to_affect)) {
02251       // We handle only the triangles right now.
02252       haptic_p->my_space = haptic_p->my_space_t;
02253       haptic_p->points_to_save->erase_all();
02254       haptic_p->my_space_t->erase_all();
02255       triangle *t;
02256       Face *f;
02257       for(int i=0; i< closest_node->numFaceLinks(); i++) {
02258         f  = closest_node->getFaceLink(i);
02259         t = new triangle(f->getNodeLink(0)->p, f->getNodeLink(1)->p, 
02260                          f->getNodeLink(2)->p, f->getNodeLink(0)->getNormal(),
02261                          f->getNodeLink(1)->getNormal(), 
02262                          f->getNodeLink(2)->getNormal(), (char) 127);
02263         haptic_p->my_space_t->add_new_triangle(t);
02264       }
02265       break;
02266     }
02267       
02268     // now we have a closest node, and he is close enough => we
02269     // need to send the matrix of points
02270     if (!being_grabbed) { // this is our first grab, maybe do special
02271 
02272                 if (other_obj->getNumMethod() == quasi_ordered ||
02273 
02274                         other_obj->getNumMethod() == euler_ordered) {      
02275                         // order nodes
02276                         other_obj->getNodeArray()->OrderNodes(closest_node);
02277                 }
02278                 being_grabbed = 1;
02279                 other_obj->being_grabbed = 1;
02280     }
02281       
02282     // set the position of the node to our tip
02283     // if (use_fext) closest_node->setFext(50.0*vect); // using forces
02284     closest_node->p = getTip();
02285       
02286     // make it stop moving, since we've got it
02287     closest_node->v = Point3D(0,0,0);
02288       
02289     // we need to fix a node, otherwise it can snap back if we have
02290     // multiple Update calls between calls to HandleBehavior
02291     closest_node->setFixedType(Node::POS); 
02292             
02293     // Now we must find which points we don't have the force.
02294     // Finding the force for those, putting what we need in the
02295     // space_i we have, and it will be sent later.
02296     Point3D pttmp;
02297     point *mypoint;
02298     double xmin = ceil((closest_node->p.x - RAYON) / MATRIX) * MATRIX;
02299     double ymin = ceil((closest_node->p.y - RAYON) / MATRIX) * MATRIX;
02300     double zmin = ceil((closest_node->p.z - RAYON) / MATRIX) * MATRIX;
02301             
02302     haptic_p->my_space_i->erase_all();
02303             
02304     for(double x=xmin; x <= (closest_node->p.x + RAYON); x+=MATRIX) {
02305       for(double y=ymin; y <= (closest_node->p.y + RAYON); y+=MATRIX) {
02306         for(double z=zmin; z <= (closest_node->p.z + RAYON); z+=MATRIX) {
02307           //             cerr << x << ":" << y << ":" << z << endl;
02308           pttmp.x = x; pttmp.y = y; pttmp.z = z;
02309           if((closest_node->p - pttmp).Squared() <= RAYON * RAYON) {
02310             mypoint = haptic_p->points_to_save->isthere(&pttmp);
02311             if(mypoint) {
02312               point *pointcopy;
02313               pointcopy = new point(mypoint);
02314               haptic_p->my_space_i->add_new_point(pointcopy);
02315             }
02316             else {
02317               point *pointcopy;
02318               mypoint = new point(&pttmp, what_force_if(closest_node, &pttmp));
02319               pointcopy = new point(mypoint);
02320               haptic_p->points_to_save->add_new_point(pointcopy);
02321               haptic_p->my_space_i->add_new_point(mypoint);
02322             }
02323           }
02324         } // for z
02325       } // for y
02326     } // for x
02327     haptic_p->my_space = haptic_p->my_space_i;
02328   }  // case quisar test
02329     
02330   case grabbing: {
02331     // need to find closest node, its object, and grab it:
02332     Object* other_obj = NULL;
02333     float sensor_activation = sensor_p->active[0];      // activation status of sensor
02334     float prev_sensor_activation = prev_grabbing_activation;
02335     prev_grabbing_activation = sensor_activation;
02336 
02337     // check if we were already grabbing before, but didn't catch anything
02338     // -> if so, we won't try grabbing again, because the tool has to be opened 
02339     //    again first, to allow grabbing again
02340     if ((prev_sensor_activation >= 0.5) && (being_grabbed == 0)) {
02341                                 // let's just stop right here!
02342       break;
02343     }
02344 
02345     // we drop a grabbed node if sensor becomes inactive
02346     // this also sets the closes_node to NULL even if we didn't grab previously!  
02347     if (closest_node && (sensor_activation < 0.5)) {
02348       closest_node->setFixedType(Node::NOT);  
02349             
02350 #ifdef JOEL
02351       int hack_index = closest_node->getIndex();
02352                                 //------------------------- HACK here ------------------------------
02353       Object *other_vessel = objectarray_p->findObject("other vessel");
02354       if (other_vessel != NULL) {
02355         Object *left_vessel = objectarray_p->findObject("left vessel");
02356         Object *right_vessel = objectarray_p->findObject("right vessel");
02357                 
02358         Node* n = left_vessel->nodearray.getNode(hack_index);
02359         n->setFixedType(Node::NOT);
02360         n = right_vessel->nodearray.getNode(hack_index);
02361         n->setFixedType(Node::NOT);
02362         n = other_vessel->nodearray.getNode(hack_index);
02363         n->setFixedType(Node::NOT);
02364       }
02365                                 // -----------------------------------------------------------------
02366 #endif
02367                                 // Only resets grabbing, if we were really grabbing before
02368                                 // Chris: This fixes a bug if a second laparascope comes close to the
02369                                 //      object. The moving in of the second laparoscope made the object
02370                                 //      automatically get detached/ungrabbed from the first laparoscope!
02371       if (being_grabbed == 1) {
02372         being_grabbed = 0;
02373         closest_node->getNodeArray()->getObject()->being_grabbed = 0;
02374       }
02375 
02376       closest_node = NULL;
02377     }    // if sensor inactive
02378         
02379     // now we find a closest node if we don't have one from prior grab
02380     if (!closest_node)
02381       closest_node = objectarray_p->FindClosestUnlinkedNode(getTip());
02382         
02383     // and find the object it belongs to
02384     if (closest_node)
02385       other_obj = closest_node->getNodeArray()->getObject();
02386         
02387     // we may not have found any closest node if we're outside bboxes
02388     if (!closest_node) break;
02389         
02390     // don't grab tied nodes
02391     if (closest_node->getTieNode())  {
02392       closest_node = NULL;
02393       break;
02394     }
02395         
02396     // we may be inactive
02397     if (sensor_activation < 0.5) break; // keep the closest node for fun
02398         
02399     // if it's too far away, forget it
02400     Point3D vect = getTip() - closest_node->p;
02401     if (vect.Length() > max_distance_to_affect) {
02402       closest_node = NULL;
02403       if (debug) cerr << "Too far away to grab\n";
02404       break;
02405     }
02406         
02407     // now we actually grab something
02408     if (!being_grabbed) { // this is our first grab, maybe do special
02409 
02410                 if (other_obj->getNumMethod() == quasi_ordered ||
02411 
02412                         other_obj->getNumMethod() == euler_ordered) {
02413                         other_obj->getNodeArray()->OrderNodes(closest_node);
02414                 }
02415                 being_grabbed = 1;
02416                 other_obj->being_grabbed = 1;
02417     }
02418         
02419     // check if button
02420     if (closest_node->getNodeArray()->getObject()->getDynamics() == button) {
02421                                 // break, because there are no forces rendered for button
02422       break;
02423     }
02424 
02425     // set the old position of the grabbed node -- this allows us, for
02426     // example, to break a grab into steps when pulling something too
02427     // far, as in updateThread
02428     closest_node->p_old = closest_node->p;
02429     // set the position of the node to our tip
02430     // if (use_fext) closest_node->setFext(50.0*vect); // using forces
02431     closest_node->p = getTip();
02432         
02433     // make it stop moving, since we've got it
02434     closest_node->v = Point3D(0,0,0);
02435         
02436     // we need to fix a node, otherwise it can snap back if we have
02437     // multiple Update calls between calls to HandleBehavior
02438     closest_node->setFixedType(Node::POS); 
02439         
02440 #ifdef JOEL
02441     // ----------------------------and HACK here ---------------------
02442     Object *other_vessel = objectarray_p->findObject("other vessel");
02443     if (other_vessel != NULL) {
02444       Object *left_vessel = objectarray_p->findObject("left vessel");
02445       Object *right_vessel = objectarray_p->findObject("right vessel");
02446             
02447       Point3D disp =closest_node->p - closest_node->init_pos;
02448       int hack_index = closest_node->getIndex();
02449       Node* n = left_vessel->nodearray.getNode(hack_index);
02450       n->p = n->init_pos + disp;
02451       n->v = Point3D(0.0,0.0,0.0);
02452       n->setFixedType(Node::POS);
02453       n = right_vessel->nodearray.getNode(hack_index);
02454       n->p = n->init_pos + disp;
02455       n->v = Point3D(0.0,0.0,0.0);
02456       n->setFixedType(Node::POS);
02457       n = other_vessel->nodearray.getNode(hack_index);
02458       n->p = n->init_pos + disp;
02459       n->v = Point3D(0.0,0.0,0.0);
02460       n->setFixedType(Node::POS);
02461     }
02462     // ----------------------------------------------------------------
02463 #endif
02464         
02465 
02466         // calculate haptic force
02467     if (closest_node->getNodeArray()->getObject()->getRigidHinge() == NULL) {
02468       // regular haptics force
02469       haptic_force = (closest_node->getF());
02470     }
02471     else {
02472       // Chris: haptics force if we pull on a hinged object.
02473       // The farther away you go, the stronger you are pulled back
02474       // to the hinged object
02475       Point3D tip_pt = getTip();
02476       float distance = closest_node->p_old.Dist(tip_pt);
02477       haptic_force = closest_node->p_old - tip_pt;
02478       haptic_force *= 10 * distance * distance;
02479     }
02480                         
02481     // if we're grabbing something, give us some force = hack
02482     grip_force = (sensor_activation - 0.5)*2 * 100;     
02483     twist_force = 0;    // but no twist for now
02484                         
02485     if (debug) 
02486       cerr << "haptic_force: " << haptic_force 
02487            << ",twist=" << twist_force << ", grip=" << grip_force 
02488            << endl;
02489     break;
02490   } // case grabbing
02491                 
02492   case deleting: {
02493     Object* other_obj = NULL;
02494       
02495 #ifdef DELETE_CURRENT_OBJECT_ONLY
02496     other_obj = current_object;
02497 #else
02498     // find the closest node 
02499     closest_node = objectarray_p->FindClosestUnlinkedNode(getTip());
02500       
02501     // and find the object it belongs to
02502     if (closest_node) {
02503       other_obj = closest_node->getNodeArray()->getObject();
02504     }
02505 #endif
02506       
02507     // call Jeremie's cutting code here
02508     if (sensor_p->active[0] && other_obj) {
02509       other_obj->CuttingByDeleting(this);
02510     }
02511     break;
02512   }  // case deleting
02513       
02514   case coloring: {
02515     if (sensor_p->active[0] < 0.5) break;
02516       
02517     // get the object
02518     closest_node = objectarray_p->FindClosestUnlinkedNode(getTip());
02519     if (!closest_node) break;
02520       
02521     // find the object it belongs to
02522     Object* other_obj = closest_node->getNodeArray()->getObject();
02523     cerr << "Coloring on object [" << other_obj->getName() << "]\n";
02524     int color = 10;
02525       
02526     // get our region
02527     Point3D min, max;
02528     getBoundingBox(&min, &max);
02529       
02530     // and color it in our region
02531     other_obj->ColorNodesWithinBoundingBox(min, max, color);
02532       
02533     break;
02534   } // case coloring 
02535       
02536   case poking: {
02537     // Note: This is handled by the ResolveCollisions() framework now
02538     break;
02539   } // case poking
02540       
02541   case ablating:
02542   case cauterizing:
02543 
02544     //#define SOUND
02545 #if defined(SOUND) && defined(SUN)
02546     // if the tool is on, make a sound
02547     if (sensor_p->num_active > 1) {
02548       if (sensor_p->active[1] > 0.5) {
02549         system("/usr/bin/audioplay fastbusy.au &");
02550       }
02551     }
02552 #endif
02553     break;
02554 
02555   case none: 
02556     // no behavior defined, do nothing
02557     break;
02558                 
02559   default: 
02560     // not handled here
02561     break;
02562                         
02563   } // switch behavior
02564     
02565   // render the haptic force 
02566   // (used to have sensor must be active=kill switch)
02567   if (sensor_p->type == Sensor::haptic_v1_sensor) {
02568     Haptic_v1* haptic_p = (Haptic_v1*)sensor_p;
02569     haptic_p->force = haptic_force;
02570     if (haptic_p->GetGridMode() == false) {
02571       haptic_p->twist_force = twist_force; 
02572       haptic_p->grip_force = grip_force; 
02573     }
02574     else { 
02575       // the haptic controller uses a forcegrid...
02576       if (closest_node && (sensor_p->active[0] > 0.5)) {
02577         haptic_p->SetActive(true);
02578       }
02579       else {
02580         haptic_p->SetActive(false);
02581       }
02582     }     
02583     //if (haptic_force.z != 0)
02584     //cout << "\nForce sent to haptic_v1: " << haptic_force.x << " " 
02585     //<< haptic_force.y << " " << haptic_force.z << endl;
02586     if (debug) {
02587       cerr << "[" << getName() << "]- haptic force = " << haptic_force 
02588            << ", " << haptic_force.Length() << endl;
02589     }
02590   }
02591 }
02592 
02593 // Handles spontaneous behavior such as beating or fluid type dynamics which happen
02594 // without outside influence. (Chris, HI)
02595 void Object::HandleSpontaneousBehavior() 
02596 {  
02597   //
02598   // SPONTANEOUS BEHAVIORS
02599   //
02600   // Spontaneous behaviors move on their own without any outside influence.
02601   //
02602   switch (behavior) {          
02603   case beating: {
02604     const double repeat_interval = 1.0;  // seconds
02605     const double magnitude = 0.2;
02606     extern Timer global_timer;    // global system timer
02607     double t = global_timer.GetElapsedTime();
02608     t /= repeat_interval;   // interval- every n seconds
02609     t = t - int(t);   // get just the fractional part of a second
02610     t *= magnitude;
02611 
02612     // scale it in place
02613     nodearray.setToInitialPosition();
02614     nodearray.ScaleByNormal(t);
02615     //edgearray.scaleRestLengths(value);
02616     dl_needs_refresh = 1;
02617     addEverythingToUpdateList();
02618     break;
02619   } // case beating
02620 
02621   case fluid: {
02622     // use perlin noise function to create fluid effect in y (height) direction (Chris: HI)
02623     extern int iteration;
02624     // factor to reduce and save cpu processing power for fluids 
02625     const int iterations_per_update = 5;     
02626     // bigger value = slower liquid movement = more viscosity 
02627     // -> The spring constant donates the viscosity level!
02628     float viscosity = getEdgeArray()->getEdge(0)->getSpringConstant();
02629     // get perlin noise function
02630     PerlinNoise* noise = PerlinNoise::Instance();
02631     if ((iteration % iterations_per_update) == 0) {
02632       NodeArray* fluid_nodes = getNodeArray();
02633       int numnodes = fluid_nodes->getNumNodes();
02634       for (int i = 0; i < numnodes; i++) {
02635         Node* node = fluid_nodes->getNode(i);
02636         double y = noise->pnoise(node->p.x, node->p.z, 
02637                                  ((float)iteration / iterations_per_update / viscosity));
02638         node->p.y = getHeight() + y / 10;
02639       }
02640                                 // to be called, otherwise nothing gets updated and shown on the screen
02641       addEverythingToUpdateList();
02642       dl_needs_refresh = 1;
02643     }
02644     break;
02645   } // case fluid
02646 
02647   default: 
02648     // not handled here
02649     break;
02650                                 
02651   } // switch behavior
02652 }
02653 
02654 
02655 // top level draw function
02656 void Object::Draw()
02657 {
02658   if (visible) {
02659     DrawGeometry();
02660   }
02661   if (selected)
02662     DrawBoundingBox();
02663 }
02664 
02665 // this draws the edges and/or faces and/or nodes of the actual object
02666 void Object::DrawGeometry()
02667 { 
02668   if (debug) cerr << "Object[" << name << "]::Draw(" << &display_mode << ")\n";
02669     
02670   // find out if we're selecting/picking
02671   GLint rendermode;
02672   glGetIntegerv(GL_RENDER_MODE, &rendermode);
02673     
02674   // if have updated, assume we need to refresh graphics
02675   extern int do_display_lists;
02676   if (do_display_lists && !dl_needs_refresh && 
02677       (display_mode == last_display_mode) && (rendermode == GL_RENDER)) {
02678     glCallList(dl_id);
02679     return;
02680   }
02681     
02682   // if never here before, create a new list
02683   if (dl_id == -1) {
02684 
02685           dl_id = glGenLists(1);
02686 
02687   }
02688   else {
02689 
02690           glDeleteLists(dl_id, 1);
02691 
02692           // dl_id = glGenLists(1); Chris: Is this needed?
02693 
02694   }
02695 
02696 
02697   // setup for DL list refresh
02698 
02699   if (do_display_lists) {
02700         glNewList(dl_id, GL_COMPILE_AND_EXECUTE);
02701 
02702   }
02703   dl_needs_refresh = 0;
02704   last_display_mode = display_mode;
02705   if (debug) cerr << "Object[" << getName() << "- regen display-list\n";
02706     
02707   // since have updated node positions, recalculate our stats here
02708   // (we used to do it on every iteration, but that was overkill)
02709   Point3D mmin, mmax; getBoundingBox(&mmin, &mmax);
02710   nodearray.computeStats();
02711     
02712   // if doing selection, load up the object number on the name stack
02713   if (rendermode == GL_SELECT)
02714     glPushName(objectarray_p->getIndex(this));
02715     
02716   // draw special things- labels, normals, etc
02717   {
02718     glDisable(GL_LIGHTING);
02719     glShadeModel(GL_FLAT);
02720         
02721     // if to draw normals on nodes
02722     if (display_mode.draw_nodenormals) 
02723       { nodearray.drawnormals(); }
02724         
02725     // if to draw normals on faces
02726     if (display_mode.draw_facenormals) 
02727       { facearray.drawnormals(); }
02728         
02729     // if to draw mesh over polys
02730     // if to draw normals on faces
02731     if (display_mode.draw_tetranormals) 
02732       { tetraarray.drawnormals(); }
02733 
02734     // if to draw mesh over polys
02735     if ((type == faces_only) && display_mode.draw_wireover) { 
02736       glColor3d(0,0, 0); 
02737       edgearray.drawover(drawover_amount); 
02738     }
02739                 
02740     // if to draw mesh over polys
02741     if ((type == tetras_only) && display_mode.draw_wireover) { 
02742       glColor3d(0, 0, 0); 
02743                                                 
02744       for (int i=0; i<facearray.getNumFaces(); i++) {
02745         Face* myface = facearray.getFace(i);
02746         if (myface->getTetraLink(1) == NULL) {  
02747           for (int j=0; j<3; j++) {
02748             Edge* myedge = myface->getEdgeLink(j);
02749             if (myedge) myedge->drawover(drawover_amount);
02750           }
02751         }
02752       } 
02753     }        
02754     // if to draw initial position
02755     if (display_mode.draw_initial) 
02756       { edgearray.drawinitial(); nodearray.drawinitial(); }
02757         
02758     // if to draw initial position
02759     if (display_mode.draw_collisions) 
02760       { facearray.drawmarkers(); }
02761         
02762     // if to draw labels for nodes
02763     if (display_mode.draw_nodelabels) 
02764       { glColor3d(0.5, 1.0, 1.0); nodearray.drawlabels(label_offset); }
02765         
02766     // if to draw labels for edges
02767     if (display_mode.draw_edgelabels) 
02768       { glColor3d(1.0, 1.0, 0.5); edgearray.drawlabels(label_offset); }
02769         
02770     // if to draw labels for faces
02771     if (display_mode.draw_facelabels) 
02772       { glColor3d(0.5, 1.0, 0.5); facearray.drawlabels(label_offset); } 
02773     
02774     // if to draw labels for faces
02775     if (display_mode.draw_tetralabels) 
02776       { glColor3d(0.0, 1.0, 1.0); tetraarray.drawlabels(label_offset); } 
02777   
02778     // if to draw labels for objects
02779     if (display_mode.draw_objectlabels) 
02780       { glColor3d(1.0, 0.0, 0.0); DrawLabel(); }
02781   }
02782     
02783   // do textures if supposed to
02784   extern int do_textures;
02785   if (do_textures && texture && (type == faces_only) &&
02786       ((display_mode.mode == DisplayMode::flat) || 
02787        (display_mode.mode == DisplayMode::smooth))){
02788     // if we have a texture, deal with it here
02789     glEnable(GL_TEXTURE_2D);
02790         
02791     // 4-byte-wise alignment (word-align) for speed
02792     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
02793         
02794     // setup texturing parameters
02795     //MODULATE,BLEND,DECAL,REPLACE
02796     if (texture_mode == Object::texture_env_mode(0))
02797       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
02798     else if (texture_mode == texture_env_mode(1))
02799       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
02800     else if (texture_mode == Object::texture_env_mode(2))
02801       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
02802     else if (texture_mode == Object::texture_env_mode(3))
02803       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
02804         
02805     if (texture_wrap == Object::clamp) {        
02806       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
02807       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
02808     }
02809     else {
02810       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
02811       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
02812     }
02813         
02814     if (texture_filter == Object::linear) {
02815       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
02816       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
02817     }
02818     else {
02819       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
02820       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
02821     }
02822         
02823     // debugging if we need it
02824     if (debug) 
02825       cerr << "applying " << texture_size.x << "x" << texture_size.y 
02826            << " texture...\n";
02827         
02828     /*********************************************/
02829     // if texture is not a power of 2, crop out the nearest part that is
02830     int crop_w = 1,crop_h = 1;
02831     int grab_w = 256,grab_h = 256;
02832     if ((texture_size.x == 64) || (texture_size.x == 128) || 
02833                 (texture_size.x == 256) || (texture_size.x == 512) || (texture_size.x == 1024))
02834         {
02835                 grab_w = texture_size.x;
02836                 crop_w = 0;
02837         }
02838     if ((texture_size.y == 64) || (texture_size.y == 128) || 
02839                 (texture_size.y == 256) || (texture_size.y == 512) || (texture_size.y == 1024))
02840         {
02841                 grab_h = texture_size.y;
02842                 crop_h = 0;
02843         }
02844         
02845     unsigned char* texture2 = NULL;
02846     int d = texture_size.z;
02847     if (crop_w*crop_h) {
02848                 cerr << "cropping texture to be power of 2...\n";
02849                 if (!texture2) {
02850                         texture2 = (unsigned char*)malloc(grab_w*grab_h*d*sizeof(unsigned char));
02851                 }
02852                 
02853                 int texWidth = (grab_w > texture_size.x ? texture_size.x : grab_w);
02854                 int texHeight = (grab_h > texture_size.y ? texture_size.y : grab_h);
02855                 
02856                 for (int count = 0; count < texHeight; count++)
02857                         memcpy((char *)texture2 + count*grab_w*d, (char *)texture + (count)*texture_size.x*d, texWidth*d);
02858     }
02859     else 
02860                 texture2 = (unsigned char*)texture;
02861         
02862     switch (d) {
02863     case 1:
02864       glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, grab_w, 
02865                    grab_h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, texture2);
02866       break;
02867     case 3:
02868       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, grab_w, 
02869                    grab_h, 0, GL_RGB, GL_UNSIGNED_BYTE, texture2);
02870       break;
02871     case 4:
02872       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, grab_w, 
02873                    grab_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture2);
02874       break;
02875     default:
02876       break;
02877     }
02878     //  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, grab_w, 
02879     //          grab_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
02880         
02881         
02882   }
02883   /********************************************************************/
02884   // if we should just display nodes, do so
02885   switch (type) {
02886   case nodes_only: {
02887     glDisable(GL_LIGHTING);
02888     nodearray.DrawNodes();
02889     break;
02890   }
02891   case edges_only: {
02892     // here we add texture to the thread object -- must figure out
02893     // why this interferes with plane texture
02894     if (do_textures && texture) {
02895       glEnable(GL_TEXTURE_2D);
02896       glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
02897       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
02898       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
02899       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
02900       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
02901       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_size.x, 
02902                    texture_size.y,0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
02903 
02904       // should think about mipmaps, like the following but use
02905       // glbindtextures ???
02906       //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
02907       //                GL_NEAREST_MIPMAP_NEAREST);
02908       //gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGBA,texture_size.x,
02909       //                  texture_size.y,GL_RGBA,GL_UNSIGNED_BYTE,texture);
02910       glDisable(GL_LIGHTING);
02911     }
02912     else { // do colors/material properties
02913       // set front face properties
02914       glEnable(GL_LIGHTING);
02915       glShadeModel(GL_FLAT);
02916       glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
02917       glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
02918       glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
02919       glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
02920       // set back face properties
02921       glMaterialfv(GL_BACK, GL_AMBIENT, back_mat_ambient);
02922       glMaterialfv(GL_BACK, GL_DIFFUSE, back_mat_diffuse);
02923       glMaterialfv(GL_BACK, GL_SPECULAR, back_mat_specular);
02924       glMaterialfv(GL_BACK, GL_SHININESS, back_mat_shininess);  
02925     }
02926     edgearray.DrawEdges();
02927     glDisable(GL_TEXTURE_2D);
02928     if (display_mode.mode == DisplayMode::wireframe)
02929       nodearray.DrawNodes();
02930     break;
02931   }
02932   case faces_only: {
02933     // recompute normals (since surface is changing)
02934     facearray.computeNormals();
02935             
02936     // average face normals to get per-vertex normals
02937     nodearray.interpolateNormals();
02938             
02939     // depending on the display mode
02940     if (display_mode.mode == DisplayMode::wireframe) {
02941       glDisable(GL_LIGHTING);
02942       edgearray.DrawEdges();
02943       nodearray.DrawNodes();
02944     }
02945             
02946     // for flat or solid shaded modes
02947     if ((display_mode.mode == DisplayMode::flat) || 
02948         (display_mode.mode == DisplayMode::smooth)) {
02949       // turn on lights
02950       glEnable(GL_LIGHTING);
02951                 
02952       // setup shading
02953       if (display_mode.mode == DisplayMode::flat) glShadeModel(GL_FLAT);
02954       else glShadeModel(GL_SMOOTH);
02955                 
02956       { // do colors/material properties
02957         // set front face properties
02958         glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
02959         glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
02960         glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
02961         glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
02962                     
02963         // set back face properties
02964         glMaterialfv(GL_BACK, GL_AMBIENT, back_mat_ambient);
02965         glMaterialfv(GL_BACK, GL_DIFFUSE, back_mat_diffuse);
02966         glMaterialfv(GL_BACK, GL_SPECULAR, back_mat_specular);
02967         glMaterialfv(GL_BACK, GL_SHININESS, back_mat_shininess);        
02968       }
02969                 
02970       // do the draw
02971       facearray.DrawFaces(int(do_textures && texture));
02972     }
02973     break;
02974   }
02975   case tetras_only: {
02976     if (dynamics == deformable) {
02977       // recompute normals (since surface is changing)
02978       facearray.computeNormals();
02979 
02980       // average face normals to get per-vertex normals
02981       nodearray.interpolateTetraNormals();
02982     }
02983 
02984     // depending on the display mode
02985     if (display_mode.mode == DisplayMode::wireframe) {
02986       glDisable(GL_LIGHTING);
02987       edgearray.DrawEdges();
02988       nodearray.DrawNodes();
02989     }
02990 
02991     // for flat or solid shaded modes
02992     if ((display_mode.mode == DisplayMode::flat) || 
02993         (display_mode.mode == DisplayMode::smooth)) {
02994       // turn on lights
02995       glEnable(GL_LIGHTING);
02996 
02997       // setup shading
02998       if (display_mode.mode == DisplayMode::flat) glShadeModel(GL_FLAT);
02999       else glShadeModel(GL_SMOOTH);
03000 
03001       { // do colors/material properties
03002         glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
03003         glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
03004         glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
03005         glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
03006 
03007         glColor4f(mat_ambient[0],mat_ambient[1],mat_ambient[2],mat_ambient[3]);
03008 
03009         glMaterialfv(GL_BACK, GL_AMBIENT, back_mat_ambient);
03010         glMaterialfv(GL_BACK, GL_DIFFUSE, back_mat_diffuse);
03011         glMaterialfv(GL_BACK, GL_SPECULAR, back_mat_specular);
03012         glMaterialfv(GL_BACK, GL_SHININESS, back_mat_shininess);                                                
03013       }
03014 
03015       // do the draw
03016       tetraarray.DrawTetras(int(do_textures && texture));
03017     }
03018     break;
03019   }
03020   } // end switch(type)
03021         
03022   // finish up - other objects may not be textured, so turn off
03023   glDisable(GL_TEXTURE_2D);    
03024         
03025   { 
03026 
03027         // draw tie nodes if set.
03028         if (display_mode.draw_tienodes) {
03029 
03030           nodearray.drawtienodes();
03031 
03032         }
03033 
03034     // if to draw bounding sphere
03035     if (display_mode.draw_boundingspheres) {
03036 
03037                 // check what type of collision bounds to draw
03038 
03039                 extern springCore* springApp;
03040 
03041                 switch (springApp->getCollisionAlgorithm()) {
03042 
03043 
03044 
03045                         // BOUNDING_SPHERE type collision detection
03046 
03047                         case springCore::BOUNDING_SPHERE_CD: {
03048 
03049                                 if (boundingSphereRoot) {
03050                                         boundingSphereRoot->drawSubtree();
03051 
03052                                 }
03053 
03054                                 break;
03055 
03056                         }
03057 
03058 
03059 
03060                         // AABB type collision detection (Axis-Aligned Bounding-Boxes)
03061 
03062                         case springCore::AABB_CD: {
03063 
03064                                 if (boundingBoxRoot) {
03065 
03066                                         boundingBoxRoot->draw();
03067 
03068                                 }
03069 
03070                                 break;
03071 
03072                         }
03073 
03074                 }
03075         }
03076 
03077 
03078     //draw principal axis
03079     if (display_mode.draw_principalaxis) {
03080       getNodeArray()->PrincipalAxes();
03081       DrawPrincipalAxes();
03082     }
03083   }
03084         
03085   // if doing selection, pop the object number off the name stack
03086   if (rendermode == GL_SELECT)
03087     glPopName();
03088         
03089   // end of display list
03090 
03091   if (do_display_lists) {
03092         glEndList();
03093 
03094   }
03095 }
03096 
03097 // this draws a wireframe bounding box around the object
03098 void Object::DrawBoundingBox()
03099 {
03100     Point3D min, max;
03101     getBoundingBox(&min, &max);
03102     glDisable(GL_LIGHTING);
03103     glColor3f(1.0, 0.0, 0.0);  // red for now
03104     // front face
03105     glBegin(GL_LINE_LOOP);
03106     glVertex3d(min.x, min.y, min.z);
03107     glVertex3d(max.x, min.y, min.z);
03108     glVertex3d(max.x, max.y, min.z);
03109     glVertex3d(min.x, max.y, min.z);
03110     glEnd();
03111     // back face
03112     glBegin(GL_LINE_LOOP);
03113     glVertex3d(min.x, min.y, max.z);
03114     glVertex3d(max.x, min.y, max.z);
03115     glVertex3d(max.x, max.y, max.z);
03116     glVertex3d(min.x, max.y, max.z);
03117     glEnd();
03118     // connecting edges
03119     glBegin(GL_LINES);
03120     glVertex3d(min.x, min.y, min.z);
03121     glVertex3d(min.x, min.y, max.z);
03122     glVertex3d(max.x, min.y, min.z);
03123     glVertex3d(max.x, min.y, max.z);
03124     glVertex3d(max.x, max.y, min.z);
03125     glVertex3d(max.x, max.y, max.z);
03126     glVertex3d(min.x, max.y, min.z);
03127     glVertex3d(min.x, max.y, max.z);
03128     glEnd();
03129 }
03130 
03131 void Object::DrawLabel()
03132 {
03133     // draw object name in center of object
03134     extern RasterFont* font_p;
03135     Point3D center = getCenter();
03136     font_p->printString(getName(), center.x,center.y,center.z);
03137 }
03138 
03139 void Object::DrawPrincipalAxes()
03140 {
03141         //draw princial axes.  Note that higher values of sig[] contain the 
03142         //relative importance of principal axis, ie the bigger one. could draw 
03143         //in this order if desired but not set up to do so now
03144   
03145         NodeArray       *na = getNodeArray(); 
03146         Point3D bmin, bmax;
03147         getBoundingBox(&bmin, &bmax);
03148         double temp1 = MAX3(fabs(bmin.x), fabs(bmin.y), fabs(bmin.z));
03149         double temp2 = MAX3(fabs(bmax.x), fabs(bmax.y), fabs(bmax.z));
03150         double max = MAX(temp1, temp2);  // size of axes
03151         double xx, xy, xz, yx, yy, yz, zx, zy, zz;
03152 
03153         double U[3][3];  //matrix contains the eigen vectors that correspond to P.A.
03154         na->getUmatrix(U);
03155 
03156         //assign these vectors to axis directions
03157         xx=U[0][0];
03158         xy=U[1][0];
03159         xz=U[2][0];
03160         yx=U[0][1];
03161         yy=U[1][1];
03162         yz=U[2][1];
03163         zx=U[0][2];
03164         zy=U[1][2];
03165         zz=U[2][2];
03166 
03167   glDisable(GL_LIGHTING);
03168   glBegin(GL_LINES);
03169   glColor3f(1.0, 0.0, 0.0);
03170   glVertex3d(-max*xx, -max*xy, -max*xz);
03171   glVertex3d(max*xx, max*xy, max*xz);
03172   glColor3f(1.0, 1.0, 0.0);
03173   glVertex3d(-max*yx, -max*yy, -max*yz);
03174   glVertex3d(max*yx, max*yy, max*yz);
03175   glColor3f(0.0, 1.0, 0.0);
03176   glVertex3d(-max*zx, -max*zy, -max*zz);
03177   glVertex3d(max*zx, max*zy, max*zz);
03178   glEnd();
03179 }
03180 
03181 void Object::GeometryReplicate(GeometryReplicator* gr)
03182 {
03183   if (debug) 
03184           cerr << "Object[" << getName() << "]::GeometryReplicate(" << gr << ")\n";
03185 
03186   // sanity check
03187   if (!gr) return; 
03188 
03189   // if nothing new, then nothing to update!
03190   if (!dl_needs_refresh) return;
03191 
03192   // if currently being modified, don't send- 
03193   // let them use the geometry they've got
03194   if (being_modified) return;
03195 
03196   // message buffer + locals
03197   const int buf_size = 1024;
03198   char buffer[buf_size];
03199   Timer timer;
03200 
03201   { // first, send our object ID
03202     int s = sprintf(buffer, "%c%c%s", GR_CODE, GR_OBJECT, getName());
03203     gr->SendMessage(buffer, s);
03204   }
03205 
03206   { // next, send the object attributes 
03207     // (visibility, ambient, diffuse, specular, shininess)
03208         unsigned char* ubuffer = (unsigned char*)buffer;
03209     int n = 0;
03210     ubuffer[n++] = GR_CODE; ubuffer[n++] = GR_OBJ_ATTR;
03211         ubuffer[n++] = isVisible();
03212     { for (int i=0; i<4; i++) ubuffer[n++] = int(mat_ambient[i] * 255); }
03213     { for (int i=0; i<4; i++) ubuffer[n++] = int(mat_diffuse[i] * 255); }
03214     { for (int i=0; i<4; i++) ubuffer[n++] = int(mat_specular[i] * 255); }
03215     { for (int i=0; i<4; i++) ubuffer[n++] = int(mat_ambient[i] * 255); }
03216     ubuffer[n++] = int(mat_shininess[0] * 255);
03217 
03218     // (do we need to send the number of nodes/faces as an attribute to make 
03219     // it easier for the other side?)
03220 
03221     gr->SendMessage((char*)ubuffer, n);
03222   }
03223 
03224   // next send our texture (if we have one)
03225   // other things to add- texture modes- clamp/repeat, etc
03226   if (debug) { 
03227           cerr << "\ttime to send attributes = " << timer.GetElapsedTime() << endl;
03228           timer.Reset();
03229   }
03230   if (texture) {
03231         unsigned char* ubuffer = (unsigned char*)buffer;
03232     int n = 0;
03233         // encode the texture header
03234     ubuffer[n++] = GR_CODE; ubuffer[n++] = GR_TEXTURE;
03235         ubuffer[n++] = texture_size.x / 255; ubuffer[n++] = texture_size.x % 255;
03236         ubuffer[n++] = texture_size.y / 255; ubuffer[n++] = texture_size.y % 255;
03237         ubuffer[n++] = texture_size.z / 255; ubuffer[n++] = texture_size.z % 255;
03238 
03239         // and the texture data itself
03240     int size = texture_size.x * texture_size.y * texture_size.z;
03241         memcpy(&(buffer[n]), texture, size);
03242    gr->SendMessage(buffer, n+size);
03243   }
03244   if (debug) {
03245           cerr << "\ttime to send texture = " << timer.GetElapsedTime() << endl;
03246           timer.Reset();
03247   }
03248 
03249   // then send our nodes
03250   nodearray.GeometryReplicate(gr);
03251   if (debug) {
03252           cerr << "\ttime to send nodes = " << timer.GetElapsedTime() << endl;
03253           timer.Reset();
03254   }
03255 
03256   // get the nodearray to cache the index for each node to make it's index
03257   // lookup fast for the facearray function
03258   nodearray.CacheIndices();
03259 
03260   // and send our faces
03261   facearray.GeometryReplicate(gr);
03262   if (debug) {
03263           cerr << "\ttime to send faces = " << timer.GetElapsedTime() << endl;
03264           timer.Reset();
03265   }
03266 }
03267 
03268 // tests if this object and input object have intersecting bounding spheres
03269 // (faster test than bounding box test)
03270 int Object::intersectsBoundingSphere(Object* test_obj)
03271 {
03272     Point3D our_center = getCenter();
03273     Point3D test_obj_center = test_obj->getCenter();
03274     double dist_between_centers = our_center.Dist(test_obj_center);
03275     double dist_between_bspheres = getRadius() + test_obj->getRadius();
03276     int retval = ( (dist_between_centers < dist_between_bspheres) );
03277     
03278     if (debug && retval)
03279         cerr << "[" << getName() << "] is inside bsphere of object ["
03280         << test_obj->getName() << "] dist = "
03281         << dist_between_centers << ", radii = "
03282         << getRadius() << " + " << test_obj->getRadius() 
03283         << "=" << dist_between_bspheres << endl;
03284     
03285     return(retval);
03286 }
03287 
03288 // tests if this object and input object have intersecting bounding boxes
03289 // can grow each bounding box by some error e (defaults to 0)
03290 int Object::intersectsBoundingBox(Object *obj, double e)
03291 {
03292     Point3D thismin, thismax;
03293     Point3D objmin, objmax;
03294     this->getBoundingBox(&thismin, &thismax);
03295     obj->getBoundingBox(&objmin, &objmax);
03296     if (e > 0.0) { // grow the bounding boxes
03297         Point3D p(e, e, e);
03298         thismin -= p;
03299         thismax += p;
03300         objmin -= p;
03301         objmax += p;
03302     }
03303     if (((thismax.x < objmin.x) || (thismin.x > objmax.x)) || 
03304         ((thismax.y < objmin.y) || (thismin.y > objmax.y)) || 
03305         ((thismax.z < objmin.z) || (thismin.z > objmax.z)))
03306         return(0);  // they can't intersect if one of these conditions is true
03307     else
03308         return(1);
03309 }
03310 
03311 Node* Object::findClosestNode(Point3D pt)
03312 {
03313     if (debug) cerr << "Object[" << name << "]::findClosestNode(" << pt << ")\n";
03314     
03315     if (nodearray.getNumNodes() == 0) 
03316         return(NULL);
03317     
03318     // ask the nodearray to do it for us- they have the info
03319 //#define USE_SBBS
03320 #ifdef USE_SBBS
03321     int closest_node_index = nodearray.findCollisionNode(pt);
03322 #else
03323     int closest_node_index = nodearray.findClosestNode(pt);
03324 #endif
03325     if (closest_node_index < 0) return(NULL);
03326     
03327     // return the closest one
03328     return( nodearray.getNode(closest_node_index) );
03329 }
03330 
03331 // Takes another object and returns the closest node in our mesh to them,
03332 // the closest node in their mesh to us, and the distance
03333 // Uses dumb, but correct brute force O(n^2) algorithm
03334 double Object::findClosestNodesBetweenObjects(Object* other_obj, 
03335                                               Node** our_node_p, Node** other_node_p)
03336 {
03337     if (debug) 
03338         cerr << "Object[" << name << "]::findClosestNodesBetweenObjects(" 
03339         << other_obj << "," << our_node_p << "," << other_node_p << ")\n";
03340     
03341     // ask nodearray to do it
03342     double dist = nodearray.findClosestNodesBetweenNodeArrays(
03343         other_obj->getNodeArray(), our_node_p, other_node_p);
03344     
03345     // finally, return the distance
03346     return( dist );
03347 }
03348 
03349 /* test with a 1-D object */
03350 void Object::CreateTestLine(int num_segs, double length, Point3D offset,
03351                             int fix_end)
03352 {
03353     type = edges_only;
03354     dynamics = deformable;
03355     behavior = none;
03356     uses_viagra = 0;
03357     set_default_material_properties();
03358     nodearray.reset();
03359     edgearray.reset();
03360     facearray.reset(); // don't need to allocate faces
03361     nodearray.allocateNodes(num_segs + 1);
03362     edgearray.allocateEdges(num_segs);
03363     
03364     double seg_length = length/num_segs;
03365     for (int i = 0; i < (num_segs + 1); i++) {
03366         Point3D pt(offset.x + (double)i*seg_length, offset.y, offset.z);
03367         nodearray.addNode(pt);
03368         if (i != 0)
03369             edgearray.addEdge(i, i-1);
03370     }
03371     if (fix_end)
03372         nodearray.getNode(num_segs)->setFixedType(Node::POS);
03373     
03374     nodearray.computeStats();
03375 }
03376 
03377 // create a cloud of randomly placed nodes
03378 void Object::CreateNodeCloud(int num_nodes, int radius, Point3D offset)
03379 {
03380     type = nodes_only;
03381     dynamics = deformable;
03382     behavior = none;
03383     uses_viagra = 0;
03384     set_default_material_properties();
03385     nodearray.reset();
03386     edgearray.reset();
03387     facearray.reset(); // don't need to allocate faces
03388     nodearray.allocateNodes(num_nodes);
03389     
03390     for (int i = 0; i < num_nodes; i++) {
03391         Point3D pt(rand() % radius, rand() % radius, rand() % radius);
03392         pt += offset;
03393         nodearray.addNode(pt);
03394     }
03395     
03396     nodearray.computeStats();
03397 }
03398 
03399 // triangular equilateral mesh
03400 void Object::CreateEquiMesh(int numlevels, double edgelength)
03401 {
03402     // set the type, dynamics, and material of object
03403     type = faces_only;
03404     dynamics = deformable;
03405     behavior = none;
03406     uses_viagra = 0;
03407     set_default_material_properties();
03408     
03409     if (numlevels < 1) numlevels = 1;
03410     // allocate enough nodes, edges, and faces
03411     int projected_num_nodes = (numlevels + 1)*(numlevels + 2)/2;
03412     nodearray.reset();
03413     edgearray.reset();
03414     facearray.reset();
03415     projected_num_nodes *= 2; // need safety factor for adding nodes, etc.
03416     nodearray.allocateNodes(projected_num_nodes);
03417     edgearray.allocateEdges(projected_num_nodes * 3);
03418     facearray.allocateFaces(projected_num_nodes * 2);
03419     
03420     double cos30 = cos(PI/6.0);
03421     int node_index = 0;
03422     for (int i = 0; i <= numlevels; i++) {
03423         for (int j = 0; j <= i; j++) {
03424             // add the nodes
03425             Point3D pt(i*edgelength*cos30, i*edgelength*0.5 - j*edgelength, 0.0);
03426             nodearray.addNode(pt);
03427             // fix if in last level
03428             if (i == numlevels)
03429                 nodearray.getNode(node_index)->setFixedType(Node::POS);
03430             // and add faces when their 3 nodes all exist
03431             if (i > 0) {
03432                 if (j > 0 && j < i) {
03433                     facearray.addFace(node_index, node_index - i - 1, node_index - 1);
03434                     facearray.addFace(node_index, node_index - i, node_index - i - 1);
03435                 }
03436                 else if (j == i) {
03437                     facearray.addFace(node_index, node_index - i - 1, node_index - 1);
03438                 }
03439             }
03440             node_index++;
03441         }
03442     }
03443 }
03444 
03445 /*
03446 * orientation: 'x' for length going in direction of x axis, 'y' for y, etc
03447 * minimal tube will have rings of edges, connected with straight edges to 
03448 * adjacent rings.  
03449 * add one diagonal between rings to get triangles (diag1)
03450 * add another to have symmetry (diag2)
03451 * add a smaller tube inside with straight edges to first tube (inner)
03452 * add cross links between the 2 layers in adjacent rings (cross1)
03453 * add cross links between the 2 layers in the same ring (cross2)
03454 * rl = -1, restLength default, sc1 (radial) sc2 (lateral) springConstants 
03455 * rl_out = resting length of the springs on the outer cylinder
03456 * rl_in = resting length of the springs on the inner cylinder
03457 * rl_betw = resting length of the springs linking the 2 cylinders
03458 */
03459 void Object::CreateTube(char orientation, int num_radii, int num_rings, double radius, 
03460                         double length, Point3D offset, int diag1, int diag2,
03461                         int fix_num, double inner_radius, 
03462                         int cross1, int cross2, int capped,
03463                         double sc1, double sc2, double rl_out,
03464                         double rl_in, double rl_betw)
03465 {
03466     if (debug) cerr << "Object[" << name << "]::CreateTube(" << num_radii 
03467         << ", " << radius << ", " << inner_radius << ", "
03468         << length << ", " << offset << ")\n";
03469     
03470     // set the type, dynamics, and material of object
03471     type = faces_only;
03472     dynamics = deformable;
03473     behavior = none;
03474     uses_viagra = 0;
03475     set_default_material_properties();
03476     
03477     // allocate enough nodes, edges, and faces
03478     int projected_num_nodes = num_rings * num_radii * 2; // *2 in case inner hull
03479     nodearray.reset();
03480     edgearray.reset();
03481     facearray.reset();
03482     projected_num_nodes *= 2; // need safety factor for adding nodes, etc.
03483     nodearray.allocateNodes(projected_num_nodes);
03484     edgearray.allocateEdges(projected_num_nodes * 6);
03485     facearray.allocateFaces(projected_num_nodes * 2);
03486     
03487     int node_index = 0;
03488     
03489     // avoid division by 0:
03490     int xdivide = ((num_rings == 1) ? 1 : (num_rings - 1));
03491     // outer hull
03492     {
03493         for (int ring = 0; ring < num_rings; ring++) { // for each ring
03494             for (int spoke = 0; spoke < num_radii; spoke++) { // for each spoke
03495                 // compute a node
03496                 double angle = spoke * 2*PI/num_radii;
03497                 Point3D pt;
03498                 
03499                 // use orientation to figure out how to map the values
03500                 switch (orientation) {
03501                 default:
03502                 case 'x' : pt = Point3D(ring*length/(xdivide),
03503                                radius*cos(angle),-radius*sin(angle));
03504                     break;
03505                 case 'y' : pt = Point3D(radius*cos(angle),
03506                                ring*length/(xdivide),-radius*sin(angle));
03507                     break;
03508                 case 'z' : pt = Point3D(radius*cos(angle),
03509                                -radius*sin(angle),ring*length/(xdivide));
03510                     break;
03511                 } // end switch
03512                 
03513                 // add in the offset and create the node at this point
03514                 pt += offset;
03515                 nodearray.addNode(pt);
03516                 
03517                 // caveat: make sure nodes exist before adding edges 
03518                 // and edges exist before adding faces
03519                 
03520                 // add edges in a ring
03521                 if (spoke != 0)
03522                     edgearray.addEdge(node_index, (node_index - 1), rl_out, sc1);
03523                 if (spoke == (num_radii - 1))
03524                     edgearray.addEdge(node_index, (node_index - num_radii + 1), rl_out, sc1);
03525                 
03526                 // add straight edges between adjacent rings
03527                 if (ring != 0)
03528                     edgearray.addEdge(node_index, (node_index - num_radii), rl_out, sc2);
03529                 
03530                 // add one diagonal between adjacent rings
03531                 if (diag1 && ring != 0) {
03532                     if (spoke != 0)
03533                         edgearray.addEdge(node_index,(node_index - num_radii - 1),rl_out,sc2);
03534                     else
03535                         edgearray.addEdge(node_index, (node_index - 1), rl_out, sc2);
03536                 }
03537                 
03538                 // add other diagonal between adjacent rings
03539                 if (diag2 && ring != 0) {
03540                     if (spoke != (num_radii - 1))
03541                         edgearray.addEdge(node_index,(node_index - num_radii + 1),rl_out,sc2);
03542                     else
03543                         edgearray.addEdge(node_index,(node_index -2*num_radii + 1),rl_out,sc2);
03544                 }
03545                 
03546                 // add faces using the first diagonal, if we have it
03547                 if (diag1 && ring != 0) {
03548                     int other = node_index - num_radii; // neighbor in previous ring
03549                     if (spoke != 0) {
03550                         facearray.addFace(node_index, other, (other - 1));
03551                         facearray.addFace(node_index, (other - 1), (node_index - 1));
03552                     }
03553                     if (spoke == 0)
03554                         facearray.addFace(node_index, other, (node_index - 1));
03555                     if (spoke == (num_radii - 1))
03556                         facearray.addFace(node_index, (other + 1), other);
03557                 }
03558                 
03559                 // fix some rings on the end of the tube
03560                 if ((((num_rings - ring) <= fix_num) && (offset.x > 0)) ||
03561                     ((ring < fix_num) && (offset.x < 0))) {
03562                     nodearray.getNode(node_index)->setFixedType(Node::POS);
03563                 }
03564                 
03565                 node_index++;
03566             }
03567         }
03568     }
03569     
03570     // inner hull
03571     if (inner_radius > 0.0) {
03572         int outer_num = node_index;  // number of nodes in outer hull
03573         radius = inner_radius;
03574         
03575         for (int ring = 0; ring < num_rings; ring++) { // for each ring
03576             for (int spoke = 0; spoke < num_radii; spoke++) { // for each spoke
03577                 // compute a node
03578                 double angle = spoke * 2*PI/num_radii;
03579                 Point3D pt;
03580                 
03581                 // use orientation to figure out how to map the values
03582                 switch (orientation) {
03583                 default:
03584                 case 'x' : pt = Point3D(ring*length/(xdivide),
03585                                radius*cos(angle),-radius*sin(angle));
03586                     break;
03587                 case 'y' : pt = Point3D(radius*cos(angle),
03588                                ring*length/(xdivide),-radius*sin(angle));
03589                     break;
03590                 case 'z' : pt = Point3D(radius*cos(angle),
03591                                -radius*sin(angle),ring*length/(xdivide));
03592                     break;
03593                 } // end switch
03594                 
03595                 // add in the offset and create the node at this point
03596                 pt += offset;
03597                 nodearray.addNode(pt);
03598                 
03599                 // caveat: make sure nodes exist before adding edges 
03600                 // and edges exist before adding faces
03601                 
03602                 // add edges in a ring
03603                 if (spoke != 0)
03604                     edgearray.addEdge(node_index, (node_index - 1), rl_in, sc1);
03605                 if (spoke == (num_radii - 1))
03606                     edgearray.addEdge(node_index, (node_index - num_radii + 1), rl_in, sc1);
03607                 
03608                 // add straight edges between adjacent rings
03609                 if (ring != 0)
03610                     edgearray.addEdge(node_index, (node_index - num_radii), rl_in, sc2);
03611                 
03612                 // add one diagonal between adjacent rings
03613                 if (diag1 && ring != 0) {
03614                     if (spoke != 0)
03615                         edgearray.addEdge(node_index,(node_index - num_radii - 1),rl_in,sc2);
03616                     else
03617                         edgearray.addEdge(node_index, (node_index - 1), rl_in, sc2);
03618                 }
03619                 
03620                 // add other diagonal between adjacent rings
03621                 if (diag2 && ring != 0) {
03622                     if (spoke != (num_radii - 1))
03623                         edgearray.addEdge(node_index,(node_index - num_radii + 1),rl_in,sc2);
03624                     else
03625                         edgearray.addEdge(node_index,(node_index -2*num_radii + 1),rl_in,sc2);
03626                 }
03627                 
03628                 // add radial edge connecting inner and outer hull
03629                 edgearray.addEdge(node_index, node_index - outer_num, rl_betw, sc1);
03630                 
03631                 // add cross members between corresponding inner and outer nodes of
03632                 // adjacent rings
03633                 if (cross1) {
03634                     int other = node_index - outer_num;
03635                     if (ring != (num_rings - 1))
03636                         edgearray.addEdge(node_index, (other + num_radii), rl_betw, sc2);
03637                     if (ring != 0)
03638                         edgearray.addEdge(node_index, (other - num_radii), rl_betw, sc2);
03639                 }
03640                 
03641                 // add cross members between inner and outer nodes of the same ring
03642                 if (cross2) {
03643                     int other = node_index - outer_num;
03644                     if (spoke != 0)
03645                         edgearray.addEdge(node_index, (other - 1), rl_betw, sc1);
03646                     else
03647                         edgearray.addEdge(node_index, (other + num_radii - 1), rl_betw, sc1);
03648                     if (spoke != (num_radii - 1))
03649                         edgearray.addEdge(node_index, (other + 1), rl_betw, sc1);
03650                     else
03651                         edgearray.addEdge(node_index, (other - num_radii + 1), rl_betw, sc1);
03652                 }
03653                 
03654                 // ??? could eliminate faces on inner tube
03655                 // add faces using the first diagonal, if we have it
03656                 /*if (diag1 && ring != 0) {
03657                 int other = node_index - num_radii; // neighbor in previous ring
03658                 if (spoke != 0) {
03659                 facearray.addFace(node_index, other, (other - 1));
03660                 facearray.addFace(node_index, (other - 1), (node_index - 1));
03661                 }
03662                 if (spoke == 0)
03663                 facearray.addFace(node_index, other, (node_index - 1));
03664                 if (spoke == (num_radii - 1))
03665                 facearray.addFace(node_index, (other + 1), other);
03666             }*/
03667                 
03668                 // add faces to cap walls at the ends
03669                 // NB: this will add (or use) one of the cross2 links
03670                 if (capped) {
03671                     int other = node_index - outer_num;
03672                     if (ring == 0) {
03673                         if (spoke != 0) {
03674                             facearray.addFace(node_index, (node_index - 1), (other - 1));
03675                             facearray.addFace(node_index, (other - 1), other);
03676                         }
03677                         if (spoke == 0)
03678                             facearray.addFace(node_index, (other + num_radii - 1), other);
03679                         if (spoke == (num_radii - 1))
03680                             facearray.addFace(node_index, other,(node_index - num_radii +1));
03681                     }
03682                     if (ring == (num_rings -1)) {  // make normals go the other way
03683                         if (spoke != 0) {
03684                             facearray.addFace(node_index, (other - 1), (node_index - 1));
03685                             facearray.addFace(node_index, other, (other - 1));
03686                         }
03687                         if (spoke == 0)
03688                             facearray.addFace(node_index, other, (other + num_radii - 1));
03689                         if (spoke == (num_radii - 1))
03690                             facearray.addFace(node_index,(node_index - num_radii +1),other);
03691                     }
03692                 }
03693                 
03694                 // fix some rings on the end of the tube
03695                 if ((((num_rings - ring) <= fix_num) && (offset.x > 0)) ||
03696                     ((ring < fix_num) && (offset.x < 0))) {
03697                     nodearray.getNode(node_index)->setFixedType(Node::POS);
03698                 }
03699                 
03700                 node_index++;
03701       }
03702     }
03703   }
03704   
03705   // cap the ends (with many triangles all meeting in the center) even 
03706   // without an inner cylinder
03707   if (inner_radius == 0.0 && capped) {
03708       int outer_num = node_index;
03709       Point3D center0 = offset;
03710       Point3D center1 = offset + (nodearray.getNode(outer_num - num_radii)->p - 
03711           nodearray.getNode(0)->p);
03712       
03713       int i;
03714       nodearray.addNode(center0);
03715       for (i = 0; i < num_radii; i++) {
03716           facearray.addFace(node_index, i, (i+1)%num_radii);
03717       }
03718       node_index++;
03719       
03720       if (num_rings > 1) {
03721           nodearray.addNode(center1);
03722           for (i = 0; i < num_radii; i++) {
03723               facearray.addFace(node_index, (i+1)%num_radii + outer_num - num_radii,
03724                   i + outer_num - num_radii);
03725           }
03726           node_index++;
03727       }
03728   }
03729   
03730   // tell nodearray to recalculate bounding boxes, etc
03731   nodearray.computeStats();
03732   
03733   // recompute surface normals (since is a new surface)
03734   facearray.computeNormals();
03735   
03736   // average face normals to get per-vertex normals
03737   nodearray.interpolateNormals();
03738 }
03739 
03740 // Create a triangulated sphere with given radius
03741 int Object::CreateSphere(int numlayers, double radius, double inner)
03742 {
03743     // set the type, dynamics, and material of object
03744     type = faces_only;
03745     dynamics = deformable;
03746     behavior = none;
03747     uses_viagra = 0;
03748     set_default_material_properties();
03749     
03750     if (numlayers < 2) numlayers = 2;
03751     // allocate enough nodes, edges, and faces
03752     int projected_num_nodes = 2000;
03753     nodearray.reset();
03754     edgearray.reset();
03755     facearray.reset();
03756     nodearray.allocateNodes(projected_num_nodes);
03757     edgearray.allocateEdges(projected_num_nodes * 3);
03758     facearray.allocateFaces(projected_num_nodes * 2);
03759     
03760     Point3D pt;
03761     int shells = 1;
03762     int top = 0;
03763     int outer = 0;
03764     if (inner > 0.0)
03765         shells = 2;
03766     for (int k = 0; k < shells; k++) {
03767         if (k == 1)
03768             radius = inner;
03769         //
03770         // first add all the nodes
03771         //
03772         { // northern hemisphere
03773             for (int layer = 0; layer < numlayers; layer++) { 
03774                 if (layer == 0) { // do north pole
03775                     pt = Point3D(0, radius, 0);
03776                     nodearray.addNode(pt);
03777                 }
03778                 else {
03779                     double h = radius*sin((PI/2)*(numlayers-1-layer)/(numlayers-1));
03780                     double r = sqrt(radius*radius - h*h);
03781                     int n = 5*(int)(pow((double) 2, (layer-1)));        // SYK - 5/09/06 for VC2005
03782                     for (int i = 0; i < n; i++) {
03783                         double angle = i*2*PI/n;
03784                         pt = Point3D(r*sin(angle), h, r*cos(angle));
03785                         nodearray.addNode(pt);
03786                     }
03787                 }
03788             }
03789         }
03790         
03791         if (k == 0)
03792             top = nodearray.getNumNodes(); 
03793         
03794         {  // southern hemisphere
03795             for (int layer = 0; layer < (numlayers - 1); layer ++) { 
03796                 if (layer == 0) { // south pole (fix it)
03797                     pt = Point3D(0, -radius, 0);
03798                     nodearray.addNode(pt);
03799                     nodearray.getNode(top)->setFixedType(Node::POS);
03800                 }
03801                 else {
03802                     double h = radius*sin((PI/2)*(numlayers-1-layer)/(numlayers-1));
03803                     double r = sqrt(radius*radius - h*h);
03804                     int n = 5*(int)(pow((double) 2, (layer-1)));  // SYK - 5/09/06 for VC2005
03805                     for (int i = 0; i < n; i++) {
03806                         double angle = i*2*PI/n;
03807                         pt = Point3D(r*sin(angle), -h, r*cos(angle));
03808                         nodearray.addNode(pt);
03809                     }
03810                 }
03811             }
03812         }
03813         
03814         // and add some edges between inner/outer sphere
03815         if (k == 1) {
03816             for (int m = 0; m < outer; m++)
03817                 edgearray.addEdge(m, outer+m);
03818         }
03819         // now add all the faces
03820         // 5 triangles for poles
03821         for (int i = 0; i < 5; i++) {
03822             if (numlayers > 2) {
03823                 facearray.addFace(outer,outer+i+1, outer+(i+1)%5 + 1);
03824                 facearray.addFace(outer+top, outer+(i+1)%5 +1+top, outer+i+1 +top);
03825             }
03826             else { // if this is the final layer
03827                 facearray.addFace(outer, outer+i+1, outer+(i+1)%5 + 1);
03828                 facearray.addFace(outer+top, outer+(i+1)%5 + 1, outer+i+1);
03829             }
03830         }
03831         int min = outer; int min2;
03832         int max = outer; int max2;
03833         int fl;
03834         for (int layer = 1; layer < (numlayers - 1); layer++) {
03835             min = max + 1;                   // min node in this layer
03836             max = max + 5*(int)(pow((double) 2,(layer-1)));  // max node in this layer // SYK - 5/09/06 for VC2005
03837             min2 = max + 1;                  // min node in next layer
03838             max2 = max + 5*(int)(pow((double) 2,layer));     // max node in next layer // SYK - 5/09/06 for VC2005
03839             if (layer == (numlayers-2)) // flag for final layer of south
03840                 fl = 0;
03841             else
03842                 fl = top;
03843             int n = 5*(int)(pow((double) 2, (layer-1)));        // SYK - 5/09/06 for VC2005
03844             for (int i = 0; i < n; i++) {
03845                 // need to add 3 triangles for each node in interior layer
03846                 facearray.addFace(min + i, min2 + 2*i, min2 + 2*i +1);
03847                 if (i == (n - 1))
03848                     facearray.addFace(min + i, min2 + 2*i + 1, min);
03849                 else
03850                     facearray.addFace(min + i, min2 + 2*i + 1, min +i+1);
03851                 if (i == 0)
03852                     facearray.addFace(min + i, max2, min2 + 2*i);
03853                 else
03854                     facearray.addFace(min + i, min2 + 2*i - 1, min2 + 2*i);
03855                 // and again for southern hemisphere
03856                 facearray.addFace(min +i+top, min2 + 2*i +1+fl, min2 + 2*i +fl);
03857                 if (i == (n - 1))
03858                     facearray.addFace(min +i+top, min+top, min2 + 2*i + 1+fl);
03859                 else
03860                     facearray.addFace(min +i+top, min +i+1+top, min2 + 2*i + 1+fl);
03861                 if (i == 0)
03862                     facearray.addFace(min +i+top, min2 + 2*i +fl, max2 +fl);
03863                 else
03864                     facearray.addFace(min +i+top, min2 + 2*i +fl, min2 + 2*i -1+fl);
03865             }
03866         }
03867         outer = nodearray.getNumNodes(); printf("%d %d\n", top, outer);
03868   } // for k -- inner shell
03869   // tell nodearray to recalculate bounding boxes, etc
03870   nodearray.computeStats();
03871   
03872   // recompute surface normals (since is a new surface)
03873   facearray.computeNormals();
03874   
03875   // average face normals to get per-vertex normals
03876   nodearray.interpolateNormals();
03877   return (top); // might want to fix the south pole
03878 }
03879 
03880 // creates an articulate thread of given length (in mm), with nodes spaced
03881 // X mm apart, starting at given offset (orientation: x=0,y=1,z=2)
03882 void Object::CreateThread(double length, double node_spacing, int orientation,
03883                           Point3D offset, double edgethresh_in, int first_seg)
03884 {
03885     if (debug) cerr << "Object[" << name << "]::CreateThread(" << length << 
03886         ", " << node_spacing << ", " << offset << ")\n";
03887     
03888     // set the type of object
03889     type = edges_only;
03890     // set the thickness of the edges
03891     edgethresh = edgethresh_in;
03892     dynamics = articulate;
03893     //dynamics = deformable;
03894     nummethod = quasi;
03895     behavior = none;
03896     uses_viagra = 0;
03897     set_default_material_properties(); // setup material properties appropriately
03898     
03899     // will turn during knot tie (iff we have >=2 objects colliding)
03900     //do_internal_collisions = 1;                
03901     
03902     int num_nodes = (int)(length/node_spacing) + 1;
03903 
03904     // calculate how many nodes and edges there will be and allocate enough
03905     nodearray.reset();
03906     edgearray.reset();
03907     facearray.reset();
03908     nodearray.allocateNodes(2*num_nodes);  // safety factor
03909     edgearray.allocateEdges(2*num_nodes);  // #edges = #nodes (well, -1)
03910     facearray.allocateFaces(10);           // there aren't any faces
03911     
03912     // create the thread
03913     int last_node_index = -1;
03914     for (int i=0; i < num_nodes; i++) {
03915         Point3D pt = offset;
03916         switch (orientation) {
03917         case 0:  pt.x += i*node_spacing; break; // x-direction
03918         case 1:  pt.y += i*node_spacing; break; // y-direction
03919         case 2:  pt.z += i*node_spacing; break; // z-direction
03920         }
03921         int node_index = nodearray.addNode(pt);
03922         //Node* node = nodearray.getNode(node_index);
03923         //node->setMass(100.0);
03924         
03925         if (last_node_index == -1)
03926             last_node_index = node_index;
03927         else {
03928             // add in this edge
03929             int edge_index = 
03930                 edgearray.addEdge(last_node_index, node_index, -1, 20, 40);
03931             edgearray.getEdge(edge_index)->setSpringConstant(2.0);
03932             
03933             // save the back pointer
03934             last_node_index = node_index;
03935         }
03936         if (i == 0)
03937           i += first_seg;
03938         //if (i == 0|| i == 3)
03939         //i += 2; // make the first 2 edges 3 times as long, they're the needle!
03940     }
03941     
03942     // tell nodearray to recalculate bounding boxes, etc
03943     nodearray.computeStats();
03944     
03945     // recompute surface normals (since is a new surface)
03946     facearray.computeNormals();
03947     
03948     // average face normals to get per-vertex normals
03949     nodearray.interpolateNormals();
03950 }
03951 
03952 // creates an edge object in ring shape, for wrapping thread around
03953 void Object::CreateEdgeRing(Point3D center, double radius, int numedges,
03954                             double edgethresh_in)
03955 {
03956     if (debug) cerr << "Object[" << name << "]::CreateEdgeRing(" << center << 
03957         ", " << radius << ", " << numedges << ")\n";
03958     
03959     // set the type of object
03960     type = edges_only;
03961     // set the thickness of the edges
03962     edgethresh = edgethresh_in;
03963     dynamics = rigid;
03964     nummethod = quasi;
03965     behavior = none;
03966     uses_viagra = 0;
03967     set_default_material_properties(); // setup material properties appropriately
03968     
03969     int num_nodes = numedges;
03970 
03971     // calculate how many nodes and edges there will be and allocate enough
03972     nodearray.reset();
03973