3

As stated in the title: I want to create a 3D cog with 10 teeth which rotates around its center (like a cog does). The cog has square teeth and, for the sake of simplicity, has flat sides between the teeth - no curves on this cog.

Visualization of what one side of the cog should look like. Note that angles are not 100% perfect.

enter image description here

As per the image above, every single cog tooth would have to be an 8 sided polygon, while every single side between the teeth would have to just be a 4 sided polygon. Right now, however, the tooth drawing does the following:

  1. The side tooth which should be facing the camera is rotated into the floor despite no initial rotation being applied.
  2. The tooth is twice as wide as it is tall although scaling and vertex creation is done with uniform numbers (it is scaled by s in the x, y, z directions and uses either 0 or 0.5 as vertex coords).

Fully reproducible example:

import java.awt.Dimension;

import javax.swing.JFrame;

import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLJPanel;
import com.jogamp.opengl.glu.GLU;
import com.jogamp.opengl.util.FPSAnimator;

public class SpinCog3D implements GLEventListener {

    JFrame jf;
    GLJPanel gljpanel;
    Dimension dim = new Dimension(800, 600);
    FPSAnimator animator;

    float rotation;
    float speed;

    // set up the OpenGL Panel within a JFrame
    public SpinCog3D() {
        jf = new JFrame();
        gljpanel = new GLJPanel();
        gljpanel.addGLEventListener(this);
        gljpanel.requestFocusInWindow();
        jf.getContentPane().add(gljpanel);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);
        jf.setPreferredSize(dim);
        jf.pack();
        animator = new FPSAnimator(gljpanel, 20);
        rotation = 0.0f;
        speed = 0.1f;
        animator.start();
    }

    public static void main(String[] args) {
        new SpinCog3D();
    }

    public void init(GLAutoDrawable dr) {
        GL2 gl2 = dr.getGL().getGL2();
        GLU glu = new GLU();
        gl2.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
        gl2.glEnable(GL2.GL_DEPTH_TEST);

        gl2.glMatrixMode(GL2.GL_PROJECTION);
        gl2.glLoadIdentity();
        glu.gluPerspective(60.0, 1.0, 100.0, 800.0);

    }

    public void display(GLAutoDrawable dr) {

        GL2 gl2 = dr.getGL().getGL2();
        GLU glu = new GLU();

        gl2.glMatrixMode(GL2.GL_MODELVIEW);
        gl2.glLoadIdentity();
        glu.gluLookAt(0.0, 200.0, 500.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

        gl2.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

        // Draw 1 Cog Tooth + 1 Side
        drawTooth(gl2, 100.0, 1.0f, 0.0f, 0.0f, 0.0, 0.0, 1.0, 0.0, 0);
        drawSide(gl2, 100.0, 0.0f, 1.0f, 0.0f, 0.0, 0.0, 1.0, 0.0, 10);

        // Draw Floor
        sideRotatedColorScaledFloor(gl2, 300.0, 0.0f, 0.0f, 0.0f, 90.0, 1.0, 0.0, 0.0, 0.0);

        gl2.glFlush();
        rotation += speed;
        if (rotation > 360.9f)
            rotation = 0.0f;
    }

    // draw a single side with a set color and orientation
    private void drawTooth(GL2 gl2, double s, float r, float g, float b, double a, double ax, double ay,
                                        double az, double zoffset) {
        gl2.glPushMatrix();
        gl2.glRotated(a, ax, ay, az);
        gl2.glColor3f(r, g, b);
        gl2.glTranslated(0.0, 0.0, zoffset);
        gl2.glScaled(s, s, s);

        tooth(gl2);
        gl2.glPopMatrix();
    }

    private void drawSide(GL2 gl2, double s, float r, float g, float b, double a, double ax, double ay,
                           double az, double zoffset) {
        gl2.glPushMatrix();
        gl2.glRotated(a, ax, ay, az);
        gl2.glColor3f(r, g, b);
        gl2.glTranslated(0.0, 0.0, zoffset);
        gl2.glScaled(s, s, s);

        side(gl2);
        gl2.glPopMatrix();
    }

    private void sideRotatedColorScaledFloor(GL2 gl2, double s, float r, float g, float b, double a, double ax, double ay,
                                        double az, double zoffset) {
        gl2.glPushMatrix();
        gl2.glRotated(a, ax, ay, az);
        gl2.glColor3f(r, g, b);
        gl2.glTranslated(0.0, 0.0, zoffset);
        gl2.glScaled(s, s, s);

        side(gl2);
        gl2.glPopMatrix();
    }

    private void tooth(GL2 gl2) {
        gl2.glBegin(GL2.GL_POLYGON);
        gl2.glVertex3d(-0.5, -0.5, 0.0);
        gl2.glVertex3d(-0.5, 0.5, 0.0);
        gl2.glVertex3d(0.5, 0.5, 0.0);
        gl2.glVertex3d(0.5, -0.5, 0.0);
        gl2.glVertex3d(-0.5, -0.5, 0.5);
        gl2.glVertex3d(-0.5, 0.5, 0.5);
        gl2.glVertex3d(0.5, 0.5, 0.5);
        gl2.glVertex3d(0.5, -0.5, 0.5);
        gl2.glEnd();
    }

    private void side(GL2 gl2) {
        gl2.glBegin(GL2.GL_POLYGON);
        gl2.glVertex3d(-0.5, -0.5, 0.0);
        gl2.glVertex3d(-0.5, 0.5, 0.0);
        gl2.glVertex3d(0.5, 0.5, 0.0);
        gl2.glVertex3d(0.5, -0.5, 0.0);
        gl2.glEnd();
    }
}

Yes I am aware the glBegin() and glEnd() has been deprecated since forever, but that's not the point. How do I get my teeth to draw properly and have it aligned with the sides properly?

R. Rengold
  • 193
  • 13
  • Could you add a description or, better yet, images of the "weird things"? I think it might make your question even better so that we don't have to run your code ourselves to see what the issue might be. – zero298 Sep 13 '19 at 13:59
  • I added answer without using matrices ... where your bug most likely lies ... – Spektre Sep 13 '19 at 14:17

1 Answers1

3

tooth drawing does all sorts of weird things...

is not a good problem description. My bet is your mesh has many gaps and overlaps as you are using hard-coded shapes without any correction to real sizes of the Cog. Like how big tooth must be to fit n of them to cover full circle exactly ...

how about exploiting parametric circle equation instead?

That would get rid of the matrix mess and would be even simpler I think.

So I would simply divide the cog into triangular slices where each slice has few edge points. So connect them with QUADs and put that into for loop doing all the slices.

I do not code in JAVA but here small C++ example using old GL api and no fancy C++ stuff so you should be able to port this easily:

overview

void glCog(float r0,float r1,float r2,float w,int n)    // shaft/inner/outer radiuses, width, tooths
    {
    int i;
    float a,da,x,y,c,s;
    float p[6][3],q[6][3];  // slice points
    // set z for slice points
    a=-0.5*w; for (i=0;i<3;i++){ p[i][2]=a; q[i][2]=a; }
    a=+0.5*w; for (i=3;i<6;i++){ p[i][2]=a; q[i][2]=a; }
    // init first slice
    q[0][0]= r0; q[5][0]= r0;
    q[0][1]=0.0; q[5][1]=0.0;
    q[1][0]= r1; q[4][0]= r1;
    q[1][1]=0.0; q[4][1]=0.0;
    q[2][0]= r2; q[3][0]= r2;
    q[2][1]=0.0; q[3][1]=0.0;
    // divide circle to 2*n slices
    da=2.0*M_PI/float(4*n);
    glBegin(GL_QUADS);
    for (a=0.0,i=0;i<=n;i++)
        {
        // points on circles at angle a
        c=cos(a); s=sin(a); a+=da;
        x=r0*c; y=r0*s; p[0][0]=x; p[5][0]=x;
                        p[0][1]=y; p[5][1]=y;
        x=r1*c; y=r1*s; p[1][0]=x; p[4][0]=x;
                        p[1][1]=y; p[4][1]=y;
        x=r2*c; y=r2*s; p[2][0]=x; p[3][0]=x;
                        p[2][1]=y; p[3][1]=y;
        // render tooth
        c=cos(a); s=sin(a); a+=da;
        glNormal3f(0.0,0.0,-1.0);   // -Z base
        glVertex3fv(p[0]);
        glVertex3fv(p[2]);
        glVertex3fv(q[2]);
        glVertex3fv(q[0]);
        glNormal3f(0.0,0.0,+1.0);   // +Z base
        glVertex3fv(p[3]);
        glVertex3fv(p[5]);
        glVertex3fv(q[5]);
        glVertex3fv(q[3]);
        glNormal3f(-c,-s,0.0);      // shaft circumference side
        glVertex3fv(p[5]);
        glVertex3fv(p[0]);
        glVertex3fv(q[0]);
        glVertex3fv(q[5]);
        glNormal3f(c,s,0.0);        // outter circumference side
        glVertex3fv(p[2]);
        glVertex3fv(p[3]);
        glVertex3fv(q[3]);
        glVertex3fv(q[2]);
        glNormal3f(-s,c,0.0);
        glVertex3fv(p[4]);
        glVertex3fv(p[3]);
        glVertex3fv(p[2]);
        glVertex3fv(p[1]);
        glNormal3f(s,-c,0.0);
        glVertex3fv(q[1]);
        glVertex3fv(q[2]);
        glVertex3fv(q[3]);
        glVertex3fv(q[4]);

        // points on circles at angle a
        c=cos(a); s=sin(a); a+=da;
        x=r0*c; y=r0*s; q[0][0]=x; q[5][0]=x;
                        q[0][1]=y; q[5][1]=y;
        x=r1*c; y=r1*s; q[1][0]=x; q[4][0]=x;
                        q[1][1]=y; q[4][1]=y;
        x=r2*c; y=r2*s; q[2][0]=x; q[3][0]=x;
                        q[2][1]=y; q[3][1]=y;
        // render gap
        c=cos(a); s=sin(a); a+=da;
        glNormal3f(0.0,0.0,-1.0);   // -Z base
        glVertex3fv(q[0]);
        glVertex3fv(q[1]);
        glVertex3fv(p[1]);
        glVertex3fv(p[0]);
        glNormal3f(0.0,0.0,+1.0);   // +Z base
        glVertex3fv(q[4]);
        glVertex3fv(q[5]);
        glVertex3fv(p[5]);
        glVertex3fv(p[4]);
        glNormal3f(-c,-s,0.0);      // shaft circumference side
        glVertex3fv(q[5]);
        glVertex3fv(q[0]);
        glVertex3fv(p[0]);
        glVertex3fv(p[5]);
        glNormal3f(c,s,0.0);        // outter circumference side
        glVertex3fv(q[1]);
        glVertex3fv(q[4]);
        glVertex3fv(p[4]);
        glVertex3fv(p[1]);
        }
    glEnd();
    }

And here preview for glCog(0.1,0.5,0.6,0.1,10);:

preview1

And here preview for glCog(0.2,0.5,0.52,0.2,50);:

preview2

However beware that the tooth are not rectangular exactly. The less tooth there is the bigger the error is. If you want to have exactly rectangular shape of tooth you need to translate the outer most points instead of rotate (or correct the angle they are computed with)

Using translation to remedy this:

void glCog(float r0,float r1,float r2,float w,int n)    // shaft/inner/outer radiuses, width, tooths
    {
    int i,j;
    float a,da,dr,x,y,c,s;
    float p[6][3],q[6][3];  // slice points
    // divide circle to 2*n slices
    da=2.0*M_PI/float(4*n);
    dr=r2-r1;
    // set z for slice points
    a=-0.5*w; for (i=0;i<3;i++){ p[i][2]=a; q[i][2]=a; }
    a=+0.5*w; for (i=3;i<6;i++){ p[i][2]=a; q[i][2]=a; }
    // init first slice
    q[0][0]= r0; q[5][0]= r0;
    q[0][1]=0.0; q[5][1]=0.0;
    q[1][0]= r1; q[4][0]= r1;
    q[1][1]=0.0; q[4][1]=0.0;
    x=r1+dr*cos(-da); y=dr*sin(-da);
    q[2][0]=  x; q[3][0]=  x;
    q[2][1]=  y; q[3][1]=  y;
    glBegin(GL_QUADS);
    for (a=0.0,i=0;i<=n;i++)
        {
        // points on circles at angle a
        c=cos(a); s=sin(a);
        x=r0*c; y=r0*s; p[0][0]=x; p[5][0]=x;
                        p[0][1]=y; p[5][1]=y;
        x=r1*c; y=r1*s; p[1][0]=x; p[4][0]=x;
                        p[1][1]=y; p[4][1]=y;
        c=cos(a-da); s=sin(a-da); a+=da;
        x+=dr*c;y+=dr*s;p[2][0]=x; p[3][0]=x;
                        p[2][1]=y; p[3][1]=y;
        c=cos(a); s=sin(a); a+=da;
        // render tooth
        glNormal3f(0.0,0.0,-1.0);   // -Z base
        glVertex3fv(p[0]);
        glVertex3fv(p[1]);
        glVertex3fv(q[1]);
        glVertex3fv(q[0]);
        glVertex3fv(p[1]);
        glVertex3fv(p[2]);
        glVertex3fv(q[2]);
        glVertex3fv(q[1]);
        glNormal3f(0.0,0.0,+1.0);   // +Z base
        glVertex3fv(p[3]);
        glVertex3fv(p[4]);
        glVertex3fv(q[4]);
        glVertex3fv(q[3]);
        glVertex3fv(p[4]);
        glVertex3fv(p[5]);
        glVertex3fv(q[5]);
        glVertex3fv(q[4]);
        glNormal3f(-c,-s,0.0);      // shaft circumference side
        glVertex3fv(p[5]);
        glVertex3fv(p[0]);
        glVertex3fv(q[0]);
        glVertex3fv(q[5]);
        glNormal3f(c,s,0.0);        // outter circumference side
        glVertex3fv(p[2]);
        glVertex3fv(p[3]);
        glVertex3fv(q[3]);
        glVertex3fv(q[2]);
        glNormal3f(-s,c,0.0);
        glVertex3fv(p[4]);
        glVertex3fv(p[3]);
        glVertex3fv(p[2]);
        glVertex3fv(p[1]);
        glNormal3f(s,-c,0.0);
        glVertex3fv(q[1]);
        glVertex3fv(q[2]);
        glVertex3fv(q[3]);
        glVertex3fv(q[4]);

        // points on circles at angle a
        c=cos(a); s=sin(a);;
        x=r0*c; y=r0*s; q[0][0]=x; q[5][0]=x;
                        q[0][1]=y; q[5][1]=y;
        x=r1*c; y=r1*s; q[1][0]=x; q[4][0]=x;
                        q[1][1]=y; q[4][1]=y;
        c=cos(a+da); s=sin(a+da); a+=da;
        x+=dr*c;y+=dr*s;q[2][0]=x; q[3][0]=x;
                        q[2][1]=y; q[3][1]=y;
        c=cos(a); s=sin(a); a+=da;
        // render gap
        glNormal3f(0.0,0.0,-1.0);   // -Z base
        glVertex3fv(q[0]);
        glVertex3fv(q[1]);
        glVertex3fv(p[1]);
        glVertex3fv(p[0]);
        glNormal3f(0.0,0.0,+1.0);   // +Z base
        glVertex3fv(q[4]);
        glVertex3fv(q[5]);
        glVertex3fv(p[5]);
        glVertex3fv(p[4]);
        glNormal3f(-c,-s,0.0);      // shaft circumference side
        glVertex3fv(q[5]);
        glVertex3fv(q[0]);
        glVertex3fv(p[0]);
        glVertex3fv(p[5]);
        glNormal3f(c,s,0.0);        // outter circumference side
        glVertex3fv(q[1]);
        glVertex3fv(q[4]);
        glVertex3fv(p[4]);
        glVertex3fv(p[1]);
        }
    glEnd();
    }

so just points p[2],q[2],p[3],q[3] changes slightly and the base side must be done with more QUADS to compensate ...

And here preview for glCog(0.2,0.5,0.6,0.2,10);:

final preview

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Nice illustration (+1) – Rabbid76 Sep 13 '19 at 14:51
  • @Rabbid76 thx ... its captured by my own GIF capture and encoder ... however sometimes when capturing GL rendering white frame come up at the end not sure why (was not doing it when I developed this probably some change in gfx drivers over the years) – Spektre Sep 13 '19 at 17:25
  • I'd give you +2 for this answer if I could. Thank you Sir. – R. Rengold Sep 14 '19 at 02:12