Skip to content

Instantly share code, notes, and snippets.

@jtsiomb
Last active December 12, 2022 23:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jtsiomb/43b5da0515d4786a80a861369d35097c to your computer and use it in GitHub Desktop.
Save jtsiomb/43b5da0515d4786a80a861369d35097c to your computer and use it in GitHub Desktop.
Example of rendering cross-sections of closed geometry with OpenGL
/* Example of rendering cross-sections of closed non-self-intersecting geometry
* with the stencil buffer. See draw_cross_section for details.
*
* Controls:
* - rotate object by dragging with the left mouse button
* - move cross-section plane back and forth by dragging up/down with the right
* mouse button
*
* Compile with: cc -o xsection xsection.c -lGL -lGLU -lglut
*
* - Nuclear / Mindlapse <nuclear@member.fsf.org>
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <GL/glut.h>
void disp(void);
void draw_regular(void);
void draw_cross_section(void);
void draw_object(void);
void reshape(int x, int y);
void keyb(unsigned char key, int x, int y);
void mouse(int bn, int st, int x, int y);
void motion(int x, int y);
int win_width, win_height;
float theta, phi, clipz;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitWindowSize(1024, 600);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL | GLUT_DOUBLE);
glutCreateWindow("GL cross-section hack");
glutDisplayFunc(disp);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyb);
glutMouseFunc(mouse);
glutMotionFunc(motion);
/* we're going to need backface culling for this */
glEnable(GL_CULL_FACE);
glutMainLoop();
return 0;
}
void disp(void)
{
glViewport(0, 0, win_width, win_height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
/* ----- render the regular view on the left ----- */
glViewport(0, 0, win_width / 2, win_height);
/* set up perspective projection matrix */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, win_width * 0.5 / win_height, 0.5, 50.0);
draw_regular();
/* ----- render the cross-section on the right ----- */
glViewport(win_width / 2, 0, win_width / 2, win_height);
/* set up an orthographic projection matrix */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-2, 2, -2, 2, -10, 10);
draw_cross_section();
/* draw a separator, just to make it clear that we have two viewports */
glViewport(0, 0, win_width, win_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, win_width, 0, win_height, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor3f(0.7, 0.7, 0.7);
glRectf(win_width / 2 - 2, 0, win_width / 2 + 2, win_height);
glutSwapBuffers();
assert(glGetError() == GL_NO_ERROR);
}
void draw_regular(void)
{
int i;
/* set up a view matrix */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -6);
glRotatef(20, 1, 0, 0);
glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_LINE_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
draw_object();
/* also draw a quad to visualize the cross-section plane */
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING);
glDepthMask(0);
glLineWidth(2.0);
for(i=0; i<2; i++) {
glBegin(i ? GL_QUADS : GL_LINE_LOOP);
glColor4f(1, 0, 0, i ? 0.25 : 0.5);
glVertex3f(-1.8, -1.8, clipz);
glVertex3f(1.8, -1.8, clipz);
glVertex3f(1.8, 1.8, clipz);
glVertex3f(-1.8, 1.8, clipz);
glEnd();
}
glPopAttrib();
}
void draw_cross_section(void)
{
double plane[] = {0, 0, -1, 0};
/* just identity for the view matrix */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushAttrib(GL_ENABLE_BIT);
glEnable(GL_STENCIL_TEST); /* enable stencil testing */
/* initially we don't care about the stencil buffer, we always want to draw */
glStencilFunc(GL_ALWAYS, 0, 0);
/* set up a clip plane to cut everything from the plane Z to the user */
plane[3] = clipz;
glClipPlane(GL_CLIP_PLANE0, plane);
glEnable(GL_CLIP_PLANE0);
glColorMask(0, 0, 0, 1); /* disable color writing, we only want to affect the stencil buffer */
/* render back faces and increment stencil where we draw */
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
glCullFace(GL_FRONT);
draw_object();
/* render front faces and decrement stencil where we draw */
glStencilOp(GL_DECR, GL_DECR, GL_DECR);
glCullFace(GL_BACK);
draw_object();
/* now everywhere outside the cross-section the stencil is 0, and inside the
* cross-section the stencil is 1
*/
glColorMask(1, 1, 1, 1); /* re-enable color writing */
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); /* don't modify the stencil buffer further */
glStencilFunc(GL_LEQUAL, 1, 0xffffffff); /* test passes only where stencil >= 1 */
/* just draw a fullscreen quad, will be drawn only inside the cross-section
* (stencil == 1) area
*/
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glDisable(GL_CLIP_PLANE0); /* disable the clip plane, we don't want the quad to be clipped */
glBegin(GL_QUADS);
glColor3f(1, 0.3, 0.2); /* color of the cross-section */
glVertex2f(-1, -1);
glVertex2f(1, -1);
glVertex2f(1, 1);
glVertex2f(-1, 1);
glEnd();
glPopAttrib();
}
void draw_object(void)
{
glPushMatrix();
glRotatef(phi, 1, 0, 0);
glRotatef(theta, 0, 1, 0);
glutSolidTorus(0.25, 1.0, 8, 28);
glutSolidSphere(0.5, 16, 8);
glPopMatrix();
}
void reshape(int x, int y)
{
win_width = x;
win_height = y;
}
void keyb(unsigned char key, int x, int y)
{
switch(key) {
case 27:
exit(0);
}
}
int bnstate[16];
int prev_x, prev_y;
void mouse(int bn, int st, int x, int y)
{
int bidx = bn - GLUT_LEFT_BUTTON;
bnstate[bidx] = st == GLUT_DOWN ? 1 : 0;
prev_x = x;
prev_y = y;
}
void motion(int x, int y)
{
int dx = x - prev_x;
int dy = y - prev_y;
prev_x = x;
prev_y = y;
if(!dx && !dy) return;
if(bnstate[0]) {
theta += dx * 0.5;
phi += dy * 0.5;
if(phi < -90) phi = -90;
if(phi > 90) phi = 90;
glutPostRedisplay();
}
if(bnstate[2]) {
clipz += dy * 0.01;
if(clipz < -2) clipz = -2;
if(clipz > 2) clipz = 2;
glutPostRedisplay();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment