GraphicalObject.cpp

00001 #include "GraphicalObject.h"
00002 #include "Utility.h"
00003 
00004 
00005 GraphicalObject::GraphicalObject (RawObject * raw_obj)
00006 {
00007 
00008         // Initialize variables
00009         first_child = NULL;
00010         raw_object = raw_obj;
00011         position = new PositionPath ();
00012 
00013         // Set default position
00014         position->SetConstantPosition (0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
00015 
00016         // initialize bounding radius
00017         if (raw_object != NULL)
00018         {bounding_radius = raw_object->GetBoundingRadius ();}
00019         else
00020         {bounding_radius = 0.0;}
00021 
00022         // initialize bounding box
00023         if (raw_object != NULL)
00024         {bounding_box = raw_object->GetBoundingBox ();}
00025         else
00026         {
00027                 bounding_box.xmax = 0.0f;
00028                 bounding_box.xmin = 0.0f;
00029                 bounding_box.ymax = 0.0f;
00030                 bounding_box.ymin = 0.0f;
00031                 bounding_box.zmax = 0.0f;
00032                 bounding_box.zmin = 0.0f;
00033         }
00034 
00035         // initialize extended culling
00036         UpdateBoxCorners ();
00037         last_outside = 0;
00038 }
00039 
00040 GraphicalObject::~GraphicalObject ()
00041 {
00042         // do nothing
00043 }
00044 
00045 void GraphicalObject::ApplyTransformation (const Position & position, bool rotation)
00046 {
00047         // translate and rotate according to the actual position (don't use glPushMatrix () because may want to allow
00048         // more than 32 consecutive calls of this routine).
00049         glTranslatef (position.x, position.y, position.z);
00050         if (rotation)
00051         {
00052                 glRotatef (position.yaw, 0.0f, 0.0f, 1.0f);
00053                 glRotatef (position.pitch, 0.0f, 1.0f, 0.0f);
00054                 glRotatef (position.roll, 1.0f, 0.0f, 0.0f);
00055         }
00056 }
00057 
00058 void GraphicalObject::UndoTransformation (const Position & position, bool rotation)
00059 {
00060         // undo rotation and translation
00061         if (rotation)
00062         {
00063                 glRotatef (-position.roll, 1.0f, 0.0f, 0.0f);
00064                 glRotatef (-position.pitch, 0.0f, 1.0f, 0.0f);
00065                 glRotatef (-position.yaw, 0.0f, 0.0f, 1.0f);
00066         }
00067         glTranslatef (-position.x, -position.y, -position.z);
00068 }
00069 
00070 void GraphicalObject::UpdateBoundingRadius (bool recurse)
00071 {
00072         ChildListElem * current_child;
00073         BoundingBox child_translation;
00074         Point corner;
00075         double temp;
00076         double max;
00077         int index;
00078         
00079         // update children if we are supposed to recurse
00080         if (recurse)
00081         {
00082                 current_child = first_child;
00083                 while (current_child != NULL)
00084                 {
00085                         current_child->object->UpdateBoundingRadius (recurse);
00086                         current_child = current_child->next;
00087                 }
00088         }
00089 
00090         // initialize own radius
00091         ComputeBoundingRadius ();
00092 
00093         // check children
00094         current_child = first_child;
00095         max = 0.0;
00096 
00097         while (current_child != NULL)
00098         {
00099                 child_translation = current_child->object->position->GetTranslationBox ();
00100                 
00101                 // find fartherst point and add the child radius
00102                 for (index = 0; index < 8; index++)
00103                 {
00104                         corner = Utility::GetPointFromBox (child_translation, index);
00105                         temp = sqrt (corner.x * corner.x + corner.y * corner.y + corner.z * corner.z);
00106                         temp += current_child->object->bounding_radius;
00107                         if (temp > max) max = temp;
00108                 }
00109         
00110                 current_child = current_child ->next;
00111         }
00112 
00113         // update only if maximal radius found is indeed larger than the current radius
00114         if (max > bounding_radius) bounding_radius = max;
00115 }
00116 
00117 void GraphicalObject::UpdateBoundingBox (bool recurse)
00118 {
00119         ChildListElem * current_child;
00120         BoundingBox child_box;
00121         BoundingBox child_translation;
00122         Position child_position;
00123 
00124         // update children first if we are supposed to recurse
00125         if (recurse)
00126         {
00127                 current_child = first_child;
00128                 while (current_child != NULL)
00129                 {
00130                         current_child->object->UpdateBoundingBox (recurse);
00131                         current_child = current_child->next;
00132                 }
00133         }
00134 
00135         // initialize own box
00136         ComputeBoundingBox ();
00137 
00138         // update box depending on children
00139         current_child = first_child;
00140         while (current_child != NULL)
00141         {
00142                 // first, we retrieve information about the child
00143                 child_translation = current_child->object->position->GetTranslationBox ();
00144                 child_box = current_child->object->bounding_box;
00145 
00146                 if (current_child->object->position->HasDynamicRotation ())
00147                 {
00148                         // if changing rotation angles are allowed, we have to make a conservative guess...
00149                         double max_dist;
00150                         double x_max, y_max, z_max;
00151 
00152                         // find the maximal distance that a box may extend (for a cube this would be sqrt(3) * side_length / 2)
00153                         abs (child_box.xmax) > abs (child_box.xmin) ? x_max = abs (child_box.xmax): x_max = abs (child_box.xmin);
00154                         abs (child_box.ymax) > abs (child_box.ymin) ? y_max = abs (child_box.ymax): y_max = abs (child_box.ymin);
00155                         abs (child_box.zmax) > abs (child_box.zmin) ? z_max = abs (child_box.zmax): z_max = abs (child_box.zmin);
00156                         max_dist = sqrt (x_max * x_max + y_max * y_max + z_max * z_max);
00157                         
00158                         // update the own box according to this maximal distance
00159                         if (bounding_box.xmax < child_translation.xmax + max_dist) bounding_box.xmax = child_translation.xmax + max_dist;
00160                         if (bounding_box.xmin > child_translation.xmin - max_dist) bounding_box.xmin = child_translation.xmin - max_dist;
00161                         if (bounding_box.ymax < child_translation.ymax + max_dist) bounding_box.ymax = child_translation.ymax + max_dist;
00162                         if (bounding_box.ymin > child_translation.ymin - max_dist) bounding_box.ymin = child_translation.ymin - max_dist;
00163                         if (bounding_box.zmax < child_translation.zmax + max_dist) bounding_box.zmax = child_translation.zmax + max_dist;
00164                         if (bounding_box.zmin > child_translation.zmin - max_dist) bounding_box.zmin = child_translation.zmin - max_dist;
00165 
00166                 }
00167                 else
00168                 {
00169                         // if we know that the rotation angles of the child does not change, we can update our own box in a 
00170                         // much better way by projecting the box corners of the child back into the own coordinate system
00171                         // and update the own box accordingly...
00172                         float matrix[16];
00173                         Point corner;
00174                         Point result;
00175 
00176                         // since rotation is static, we can get the position at an arbitrary time.
00177                         child_position = current_child->object->position->GetPosition (0);
00178                         child_position.x = 0.0f;
00179                         child_position.y = 0.0f;
00180                         child_position.z = 0.0f;
00181 
00182                         // retrieve the transformation matrix (only care about rotations)
00183                         glPushMatrix ();
00184                         glLoadIdentity ();
00185                         
00186                                 current_child->object->ApplyTransformation (child_position, true);
00187                                 glGetFloatv (GL_MODELVIEW_MATRIX, matrix);
00188 
00189                         glPopMatrix ();
00190 
00191                         // project all 8 corners and update box if necessary
00192                         for (int index = 0; index < 8; index++)
00193                         {
00194                                 corner = Utility::GetPointFromBox (child_box, index);
00195                                 Geometry::VectorMultiply (matrix, corner, result);
00196 
00197                                 if (result.x + child_translation.xmax > bounding_box.xmax) bounding_box.xmax = result.x + child_translation.xmax;
00198                                 if (result.x + child_translation.xmin < bounding_box.xmin) bounding_box.xmin = result.x + child_translation.xmin;
00199                                 if (result.y + child_translation.ymax > bounding_box.ymax) bounding_box.ymax = result.y + child_translation.ymax;
00200                                 if (result.y + child_translation.ymin < bounding_box.ymin) bounding_box.ymin = result.y + child_translation.ymin;
00201                                 if (result.z + child_translation.zmax > bounding_box.zmax) bounding_box.zmax = result.z + child_translation.zmax;
00202                                 if (result.z + child_translation.zmin < bounding_box.zmin) bounding_box.zmin = result.z + child_translation.zmin;       
00203                         }
00204                 }
00205 
00206                 current_child = current_child->next;
00207         }
00208 
00209         UpdateBoxCorners ();
00210 }
00211 
00212 void GraphicalObject::DrawBoundingSphere (int culling_result)
00213 {
00214         if (culling_result == C_OUTSIDE)
00215         {glColor3f (1.0f, 0.2f, 0.2f);}
00216         if (culling_result == C_PARTIAL)
00217         {glColor3f (1.0f, 0.7f, 0.4f);}
00218         if (culling_result == C_INSIDE)
00219         {glColor3f (0.0f, 1.0f, 0.2f);}
00220 
00221         glutWireSphere (bounding_radius, 8, 8);
00222 }
00223 
00224 void GraphicalObject::DrawBoundingBox (int culling_result)
00225 {
00226         if (culling_result == C_OUTSIDE)
00227         {Utility::SetColor (1.0f, 0.2f, 0.2f, 1.0f);}
00228         if (culling_result == C_PARTIAL)
00229         {Utility::SetColor (1.0f, 0.7f, 0.4f, 1.0f);}
00230         if (culling_result == C_INSIDE)
00231         {Utility::SetColor (0.0f, 1.0f, 0.2f, 1.0f);}
00232 
00233         Utility::DrawBoundingBox (bounding_box);
00234 }
00235 
00236 int GraphicalObject::CullObject (Camera * camera, Camera * monitor, int culling_mode, bool show)
00237 {
00238         int result;
00239 
00240         glDisable (GL_LIGHTING);
00241         
00242         switch (culling_mode)
00243         {
00244                 case    C_SPHERE:                       if (monitor == NULL)
00245                                                                         {result = SphereCulling2 (camera);}
00246                                                                         else
00247                                                                         {result = SphereCulling (camera, monitor);}
00248                                                                         if (show)
00249                                                                         {DrawBoundingSphere (result);}
00250                                                                         break;
00251                 case    C_BOUNDING_BOX:         if (monitor == NULL)
00252                                                                         {result = BoxCulling2 (camera);}
00253                                                                         else
00254                                                                         {result = BoxCulling (camera, monitor);}        
00255                                                                         if (show)
00256                                                                         {DrawBoundingBox (result);}
00257                                                                         break;
00258                 case    C_MIXED:                        if (monitor == NULL)
00259                                                                         {
00260                                                                                 result = SphereCulling2 (camera);
00261                                                                                 if (show)
00262                                                                                 {DrawBoundingSphere (result);}
00263                                                                                 if (result == C_PARTIAL)
00264                                                                                 {
00265                                                                                         result = BoxCulling2 (camera);
00266                                                                                         if (show)
00267                                                                                         {DrawBoundingBox (result);}
00268                                                                                 }
00269                                                                         }
00270                                                                         else
00271                                                                         {
00272                                                                                 result = SphereCulling (camera, monitor);
00273                                                                                 if (show)
00274                                                                                 {DrawBoundingSphere (result);}
00275                                                                                 if (result == C_PARTIAL)
00276                                                                                 {
00277                                                                                         result = BoxCulling (camera, monitor);
00278                                                                                         if (show)
00279                                                                                         {DrawBoundingBox (result);}
00280                                                                                 }
00281                                                                         }
00282                                                                         break;
00283                 case    C_NONE:                         result = C_INSIDE;
00284                                                                         break;
00285                 default:                                        break;
00286         }
00287 
00288         glEnable (GL_LIGHTING);
00289 
00290         return result;
00291 }
00292 
00293 void GraphicalObject::UpdateBoxCorners ()
00294 {
00295         for (int index = 0; index < 8; index++)
00296         {
00297                 box_corners.p[index] = Utility::GetPointFromBox (bounding_box, index);
00298         }
00299         last_outside = 0;
00300 }
00301 
00302 
00303 void GraphicalObject::CalculateRelativePositions ()
00304 {
00305         ChildListElem * current_object;
00306         Position child_pos;
00307         Point average = {0.0f, 0.0f, 0.0f};
00308         int num_children = 0;
00309 
00310         // first calculate for all the children
00311         current_object = first_child;
00312         while (current_object != NULL)
00313         {
00314                 num_children++;
00315                 current_object->object->CalculateRelativePositions ();
00316                 current_object = current_object->next;
00317         }
00318 
00319         // reinitialize bounding volumes
00320         ComputeBoundingRadius ();
00321         ComputeBoundingBox ();
00322 
00323         if (num_children == 0) return;
00324 
00325         // now calculate the average position of the objects...
00326         current_object = first_child;
00327         while (current_object != NULL)
00328         {
00329                 child_pos = current_object->object->position->GetPosition (0);
00330                 average.x += child_pos.x;
00331                 average.y += child_pos.y;
00332                 average.z += child_pos.z;
00333                 current_object = current_object->next;
00334         }
00335         average.x /= num_children;
00336         average.y /= num_children;
00337         average.z /= num_children;
00338 
00339         // finally, shift all positions
00340         current_object = first_child;
00341         while (current_object != NULL)
00342         {
00343                 current_object->object->position->Shift (-average.x, -average.y, -average.z);
00344                 current_object = current_object->next;
00345         }
00346         position->Shift (average.x, average.y, average.z);
00347         
00348         UpdateBoundingRadius (true);
00349         UpdateBoundingBox (true);
00350 }
00351 
00352 int GraphicalObject::SphereCulling (Camera * camera, Camera * monitor)
00353 {
00354         Point pos;
00355         Point origin;
00356         float temp_left, temp_right, temp_front, temp_back, temp_bottom, temp_top;
00357         float mod[16];
00358         float * inv_view;
00359 
00360         glGetFloatv (GL_MODELVIEW_MATRIX, mod);
00361         inv_view = monitor->GetInverseView ();
00362         
00363         /* since hierarchical objects are defined in local coordinate systems, we have to retrieve the absolute coordinates
00364            of our object */
00365         origin.x = mod[12];
00366         origin.y = mod[13];
00367         origin.z = mod[14];
00368 
00369         pos.x = origin.x * inv_view[0] + origin.y * inv_view[4] + origin.z * inv_view[8] + inv_view[12];
00370         pos.y = origin.x * inv_view[1] + origin.y * inv_view[5] + origin.z * inv_view[9] + inv_view[13];
00371         pos.z = origin.x * inv_view[2] + origin.y * inv_view[6] + origin.z * inv_view[10] + inv_view[14];
00372 
00373         // test left side
00374         temp_left = camera->left.n.x * pos.x + camera->left.n.y * pos.y + camera->left.n.z * pos.z + camera->left.d;
00375         if (temp_left < - bounding_radius)
00376         {return C_OUTSIDE;}
00377 
00378         // test right side
00379         temp_right = camera->right.n.x * pos.x + camera->right.n.y * pos.y + camera->right.n.z * pos.z + camera->right.d;
00380         if (temp_right < - bounding_radius)
00381         {return C_OUTSIDE;}
00382 
00383         // test top side
00384         temp_top = camera->top.n.x * pos.x + camera->top.n.y * pos.y + camera->top.n.z * pos.z + camera->top.d;
00385         if (temp_top < - bounding_radius)
00386         {return C_OUTSIDE;}
00387 
00388         // test bottom side
00389         temp_bottom = camera->bottom.n.x * pos.x + camera->bottom.n.y * pos.y + camera->bottom.n.z * pos.z + camera->bottom.d;
00390         if (temp_bottom < - bounding_radius)
00391         {return C_OUTSIDE;}
00392 
00393         // test front side
00394         temp_front = camera->front.n.x * pos.x + camera->front.n.y * pos.y + camera->front.n.z * pos.z + camera->front.d;
00395         if (temp_front < - bounding_radius)
00396         {return C_OUTSIDE;}
00397 
00398         // test back side
00399         temp_back = camera->back.n.x * pos.x + camera->back.n.y * pos.y + camera->back.n.z * pos.z + camera->back.d;
00400         if (temp_back < - bounding_radius)
00401         {return C_OUTSIDE;}
00402 
00403         // test partial stuff
00404         if (temp_left < bounding_radius || temp_right < bounding_radius || temp_top < bounding_radius || 
00405                 temp_bottom < bounding_radius || temp_front < bounding_radius || temp_back < bounding_radius)
00406         {return C_PARTIAL;}
00407 
00408         return C_INSIDE;
00409 }
00410 
00411 int GraphicalObject::SphereCulling2 (Camera * camera)
00412 {
00413         float mod[16];
00414         float res[6];
00415         float x, y, z;
00416 
00417         glGetFloatv (GL_MODELVIEW_MATRIX, mod);
00418 
00419         // since the center of the sphere is at (0, 0, 0) it is sufficient to look at the translation-part of the
00420         // matrix
00421         x = mod[12];
00422         y = mod[13];
00423         z = mod[14];
00424 
00425         for (int index = 0; index < 6; index++)
00426         {       
00427                 res[index] = x * camera->planes[index].n.x + y * camera->planes[index].n.y + z * camera->planes[index].n.z + camera->planes[index].d;
00428                 if (res[index] < - bounding_radius) return C_OUTSIDE;
00429         }
00430 
00431         if (res[0] < bounding_radius || res[1] < bounding_radius || res[2] < bounding_radius ||
00432                 res[3] < bounding_radius || res[4] < bounding_radius || res[5] < bounding_radius )
00433         {
00434                 return C_PARTIAL;
00435         }
00436 
00437         return C_INSIDE;
00438 
00439 }
00440 
00441 int GraphicalObject::BoxCulling (Camera * camera, Camera * monitor)
00442 {
00443         int index;
00444         float mod[16];
00445         float * inv_view;
00446         Point temp_point[8];
00447         Point corner[8];
00448         bool inside;
00449         bool outside;
00450 
00451         // retrieve inverse view matrix of the monitor
00452         glGetFloatv (GL_MODELVIEW_MATRIX, mod);
00453         inv_view = monitor->GetInverseView ();
00454 
00455         // transform into global coordinates
00456         for (index = 0; index < 8; index++)
00457         {
00458                 Geometry::VectorMultiply (mod, box_corners.p[index], temp_point[index]);
00459                 Geometry::VectorMultiply (inv_view, temp_point[index], corner[index]);
00460         }
00461 
00462         // just test all points against all planes (non optimized)
00463         inside = true;
00464         outside = true;
00465         for (index = 0; index < 8; index++)
00466         {
00467                 if (corner[index].x * camera->left.n.x + corner[index].y * camera->left.n.y + 
00468                         corner[index].z * camera->left.n.z + camera->left.d >= 0) 
00469                 {outside = false;}
00470                 else
00471                 {inside = false;}
00472         }
00473         if (outside) return C_OUTSIDE;
00474 
00475         outside = true;
00476         for (index = 0; index < 8; index++)
00477         {
00478                 if (corner[index].x * camera->right.n.x + corner[index].y * camera->right.n.y + 
00479                         corner[index].z * camera->right.n.z + camera->right.d >= 0) 
00480                 {outside = false;}
00481                 else
00482                 {inside = false;}
00483         }
00484         if (outside) return C_OUTSIDE;
00485 
00486         outside = true;
00487         for (index = 0; index < 8; index++)
00488         {
00489                 if (corner[index].x * camera->top.n.x + corner[index].y * camera->top.n.y + 
00490                         corner[index].z * camera->top.n.z + camera->top.d >= 0) 
00491                 {outside = false;}
00492                 else
00493                 {inside = false;}
00494         }
00495         if (outside) return C_OUTSIDE;
00496 
00497         outside = true;
00498         for (index = 0; index < 8; index++)
00499         {
00500                 if (corner[index].x * camera->bottom.n.x + corner[index].y * camera->bottom.n.y + 
00501                         corner[index].z * camera->bottom.n.z + camera->bottom.d >= 0) 
00502                 {outside = false;}
00503                 else
00504                 {inside = false;}
00505         }
00506         if (outside) return C_OUTSIDE;
00507 
00508         outside = true;
00509         for (index = 0; index < 8; index++)
00510         {
00511                 if (corner[index].x * camera->front.n.x + corner[index].y * camera->front.n.y + 
00512                         corner[index].z * camera->front.n.z + camera->front.d >= 0) 
00513                 {outside = false;}
00514                 else
00515                 {inside = false;}
00516         }
00517         if (outside) return C_OUTSIDE;
00518 
00519         outside = true;
00520         for (index = 0; index < 8; index++)
00521         {
00522                 if (corner[index].x * camera->back.n.x + corner[index].y * camera->back.n.y + 
00523                         corner[index].z * camera->back.n.z + camera->back.d >= 0) 
00524                 {outside = false;}
00525                 else
00526                 {inside = false;}
00527         }
00528         if (outside) return C_OUTSIDE;
00529 
00530         if (inside) return C_INSIDE;
00531         
00532         return C_PARTIAL;
00533 }
00534 
00535 int GraphicalObject::BoxCulling2 (Camera * camera)
00536 {
00537         int point_index, plane_index, counter;
00538         float mod[16];
00539         Point corner[8];
00540         bool inside;
00541         bool outside;
00542 
00543         // retrieve stuff
00544         glGetFloatv (GL_MODELVIEW_MATRIX, mod);
00545 
00546         // transform into global coordinates
00547         for (point_index = 0; point_index < 8; point_index++)
00548         {Geometry::VectorMultiply (mod, box_corners.p[point_index], corner[point_index]);}
00549 
00550         // test the plane where the test failed the last time first.
00551         inside = true;
00552         for (counter = 0, plane_index = last_outside; counter < 6; counter++, plane_index < 5 ? plane_index++ : plane_index = 0)
00553         {
00554                 outside = true;
00555                 for (point_index = 0; point_index < 8; point_index++)
00556                 {
00557                         if (corner[point_index].x * camera->planes[plane_index].n.x + corner[point_index].y * camera->planes[plane_index].n.y + 
00558                         corner[point_index].z * camera->planes[plane_index].n.z + camera->planes[plane_index].d >= 0) 
00559                         {outside = false;}
00560                         else
00561                         {inside = false;}
00562                 }
00563                 if (outside)
00564                 {
00565                         last_outside = plane_index;
00566                         return C_OUTSIDE;
00567                 }
00568         }
00569         if (inside) return C_INSIDE;
00570 
00571         return C_PARTIAL;
00572 }

Generated on Sun Jul 2 13:20:39 2006 for Demo by  doxygen 1.4.6-NO