No project download is available. This is a code snippet and not a complete project.
This code snippet can be used to determine if a sphere at a location in the current OpenGL coordinates is visible.
- Call setup_cull_info once per drawing callback…this queries OpenGL to get the visibility info from OpenGL’s state. Between callbacks the camera may have moved.
- Call sphere_is_visible once for each visibility check you want to make – r = radius of the sphere in meters; if the sphere whose center is XYZ and radius is r is completely off screen, the function returns false, otherwise is returns true.
- Be careful about tuning cull checks vs. drawing; you only want to cull large chunks of geometry, so that the fps boost from not drawing justifies the CPU time spent culling.
- You don’t need to cull individual OBJs drawn with the XPLMDrawObject API – this API culls for you. But you might want to cull many objects at once, by culling a sphere that contains a whole set of objects.
- This code works in all projection matrices, ortho and frustum, including oblique frustums.
struct cull_info_t { // This struct has everything we need to cull fast! float model_view[16]; // The model view matrix, to get from local OpenGL to eye coordinates. float nea_clip[4]; // Four clip planes in the form of Ax + By + Cz + D = 0 (ABCD are in the array.) float far_clip[4]; // They are oriented so the positive side of the clip plane is INSIDE the view volume. float lft_clip[4]; float rgt_clip[4]; float bot_clip[4]; float top_clip[4]; }; static void setup_cull_info(cull_info_t * i) { float m[16]; // First, just read out the current OpenGL matrices...do this once at setup because it's not the fastest thing to do. glGetFloatv(GL_MODELVIEW_MATRIX ,i->model_view); glGetFloatv(GL_PROJECTION_MATRIX,m); // Now...what the heck is this? Here's the deal: the clip planes have values in "clip" coordinates of: Left = (1,0,0,1) // Right = (-1,0,0,1), Bottom = (0,1,0,1), etc. (Clip coordinates are coordinates from -1 to 1 in XYZ that the driver // uses. The projection matrix converts from eye to clip coordinates.) // // How do we convert a plane backward from clip to eye coordinates? Well, we need the transpose of the inverse of the // inverse of the projection matrix. (Transpose of the inverse is needed to transform a plane, and the inverse of the // projection is the matrix that goes clip -> eye.) Well, that cancels out to the transpose of the projection matrix, // which is nice because it means we don't need a matrix inversion in this bit of sample code. // So this nightmare down here is simply: // clip plane * transpose (proj_matrix) // worked out for all six clip planes. If you squint you can see the patterns: // L: 1 0 0 1 // R: -1 0 0 1 // B: 0 1 0 1 // T: 0 -1 0 1 // etc. i->lft_clip[0] = m[0]+m[3]; i->lft_clip[1] = m[4]+m[7]; i->lft_clip[2] = m[8]+m[11]; i->lft_clip[3] = m[12]+m[15]; i->rgt_clip[0] =-m[0]+m[3]; i->rgt_clip[1] =-m[4]+m[7]; i->rgt_clip[2] =-m[8]+m[11]; i->rgt_clip[3] =-m[12]+m[15]; i->bot_clip[0] = m[1]+m[3]; i->bot_clip[1] = m[5]+m[7]; i->bot_clip[2] = m[9]+m[11]; i->bot_clip[3] = m[13]+m[15]; i->top_clip[0] =-m[1]+m[3]; i->top_clip[1] =-m[5]+m[7]; i->top_clip[2] =-m[9]+m[11]; i->top_clip[3] =-m[13]+m[15]; i->nea_clip[0] = m[2]+m[3]; i->nea_clip[1] = m[6]+m[7]; i->nea_clip[2] = m[10]+m[11]; i->nea_clip[3] = m[14]+m[15]; i->far_clip[0] =-m[2]+m[3]; i->far_clip[1] =-m[6]+m[7]; i->far_clip[2] =-m[10]+m[11]; i->far_clip[3] =-m[14]+m[15]; } static int sphere_is_visible(const cull_info_t * i, float x, float y, float z, float r) { // First: we transform our coordinate into eye coordinates from model-view. float xp = x * i->model_view[0] + y * i->model_view[4] + z * i->model_view[ 8] + i->model_view[12]; float yp = x * i->model_view[1] + y * i->model_view[5] + z * i->model_view[ 9] + i->model_view[13]; float zp = x * i->model_view[2] + y * i->model_view[6] + z * i->model_view[10] + i->model_view[14]; // Now - we apply the "plane equation" of each clip plane to see how far from the clip plane our point is. // The clip planes are directed: positive number distances mean we are INSIDE our viewing area by some distance; // negative means outside. So ... if we are outside by less than -r, the ENTIRE sphere is out of bounds. // We are not visible! We do the near clip plane, then sides, then far, in an attempt to try the planes // that will eliminate the most geometry first...half the world is behind the near clip plane, but not much is // behind the far clip plane on sunny day. if ((xp * i->nea_clip[0] + yp * i->nea_clip[1] + zp * i->nea_clip[2] + i->nea_clip[3] + r) < 0) return false; if ((xp * i->bot_clip[0] + yp * i->bot_clip[1] + zp * i->bot_clip[2] + i->bot_clip[3] + r) < 0) return false; if ((xp * i->top_clip[0] + yp * i->top_clip[1] + zp * i->top_clip[2] + i->top_clip[3] + r) < 0) return false; if ((xp * i->lft_clip[0] + yp * i->lft_clip[1] + zp * i->lft_clip[2] + i->lft_clip[3] + r) < 0) return false; if ((xp * i->rgt_clip[0] + yp * i->rgt_clip[1] + zp * i->rgt_clip[2] + i->rgt_clip[3] + r) < 0) return false; if ((xp * i->far_clip[0] + yp * i->far_clip[1] + zp * i->far_clip[2] + i->far_clip[3] + r) < 0) return false; return true; }
Is fast culling applied by default?
This is sample code for your plugin. XPLMDrawObject is culled by us, so you don’t need this, but for other kinds of drawing or for a SET of objects this can be useful.
Can you show what we can do with the Boolean result of the culling test?
You decide to skip drawing or draw based on whether the sphere is in view.