Football
Geometri
A buckyball has 60 corners, 20 polygons with 6 corners (hexagons) and 12 polygons with 5 corners (pentagons). The illustration is from GoldenNumber.net [5]
The data that is used to draw a buckyball in this module is:
When we draw a figure based on surfaces described by a set of polygons, we will have to calculate normals and we will have to determine what is in (back) and what is out(front). We must index the corners in a consistent way. In OpenGL context this means that we must describe the corners clockwise (GL_CW) or counterclockwise(GL_CW). The direction has only meaning when we decide a point of observation. One way to do this is to decide that we look at a polygon from what we decide to call the front, usually the outside.
gl.glFrontFace(GL.GL_CW) gl.glFrontFace(GL.GL_CCW)
When we hav made these decisions we can calculate normals on all polygons, (or polygon corners) by means of the cross product for vectors.
normal6=new float[20][3]; for (int ix = 0; ix < 20; ix++) { float ax = p6[ix][2][0] - p6[ix][0][0]; float ay = p6[ix][2][1] - p6[ix][0][1]; float az = p6[ix][2][2] - p6[ix][0][2]; float bx = p6[ix][3][0] - p6[ix][0][0]; float by = p6[ix][3][1] - p6[ix][0][1]; float bz = p6[ix][3][2] - p6[ix][0][2]; float x = ay * bz - az * by; float y = az * bx - ax * bz; float z = ax * by - ay * bx; float l=(float)Math.sqrt(x*x+y*y+z*z); normal6[ix][0]=x/l; normal6[ix][1]=y/l; normal6[ix][2]=z/l; } normal5=new float[12][3]; for (int ix = 0; ix < 12; ix++) { float ax = p5[ix][2][0] - p5[ix][0][0]; float ay = p5[ix][2][1] - p5[ix][0][1]; float az = p5[ix][2][2] - p5[ix][0][2]; float bx = p5[ix][3][0] - p5[ix][0][0]; float by = p5[ix][3][1] - p5[ix][0][1]; float bz = p5[ix][3][2] - p5[ix][0][2]; float x = ay * bz - az * by; float y = az * bx - ax * bz; float z = ax * by - ay * bx; float l=(float)Math.sqrt(x*x+y*y+z*z); normal5[ix][0]=x/l; normal5[ix][1]=y/l; normal5[ix][2]=z/l; }
This makes the drawing straight forward:
public void drawMe(GL gl) { int mode = GL.GL_POLYGON;//GL.GL_LINE_STRIP; // 20 polygons with 6 edges stdMaterials.setMaterial(gl, stdMaterials.MAT_WARM_WHITE, GL.GL_FRONT); for (int p = 0; p < 20; p++) { gl.glBegin(mode); gl.glNormal3f(normal6[p][0], normal6[p][1], normal6[p][2]); for (int ix = 0; ix < 6; ix++) gl.glVertex3f(p6[p][ix][0], p6[p][ix][1], p6[p][ix][2]); gl.glEnd(); } // 12 polygons with 5 edges stdMaterials.setMaterial(gl, stdMaterials.MAT_RUBY, GL.GL_FRONT); for (int p = 0; p < 12; p++) { gl.glBegin(mode); gl.glNormal3f(normal5[p][0], normal5[p][1], normal5[p][2]); for (int ix = 0; ix < 5; ix++) gl.glVertex3f(p5[p][ix][0], p5[p][ix][1], p5[p][ix][2]); gl.glEnd(); } }
Texture
We will add texture to all hexagons. We start out with a qudratic image and want to find which coordinates in the image we want to map to the hexagons corners.
p0 | d/3 , 0 |
p1 | 2d/3 , 0 |
p2 | d-(d/3)cos(60) , (d/3)sin(60) |
p3 | d-(2d/3)cos(60) , (2d/3)sin(60) |
p4 | (2d/3)cos(60) , (2d/3)sin(60) |
p5 | (d/3)cos(60) , (d/3)sin(60) |
where d is the side in the triangle, and the square. When we map coordinates in a bitmap, as in the algorithm below, d always has the value 1, independant of the dimension of the bitmap measure in pixels. A texture has a logical dimension of 1 x 1.
Since OpenGL (versions before 2.1) only accepts textures with format: 2n*2m,
we will compose the image such that the part we want to include fits inside the triangle in question.
Texture mapping can be prepared in the array tex6:
// calculate texture points float d=1.0f; float cos60=(float)Math.cos(Math.PI/3.0);//60 degrees float sin60=(float)Math.sin(Math.PI/3.0); tex6=new float[6][2]; tex6[0][0]=d/3.0f; tex6[0][1]=0.0f; tex6[1][0]=2.0f*d/3.0f; tex6[1][1]=0.0f; tex6[2][0]=d-(d/3.0f)*cos60; tex6[2][1]=(d/3.0f)*sin60; tex6[3][0]=d-(2.0f*d/3.0f)*cos60; tex6[3][1]=(2.0f*d/3.0f)*sin60; tex6[4][0]=(2.0f*d/3.0f)*cos60; tex6[4][1]=(2.0f*d/3.0f)*sin60; tex6[5][0]=(d/3.0f)*cos60; tex6[5][1]=(d/3.0f)*sin60; // load textures textures=new TextureReader.Texture[3]; try { textures[0] = TextureReader.readTexture("images/Marrakech.jpg"); textures[1] = TextureReader.readTexture("images/chesspiece.gif"); textures[2] = TextureReader.readTexture("images/ronaldinho.jpg"); } catch (IOException ex) { System.out.println(ex.getMessage()); }
Rendering:
public void drawMeTextured(GL gl,int tIx) { int mode = GL.GL_POLYGON;//GL.GL_LINE_STRIP; if(tIx <0) tIx=0; if(tIx >=textures.length) tIx=textures.length-1; gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S,GL.GL_CLAMP); gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_T,GL.GL_CLAMP); gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_NEAREST); gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_NEAREST); gl.glTexEnvi(GL.GL_TEXTURE_ENV,GL.GL_TEXTURE_ENV_MODE,GL.GL_MODULATE); gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT,GL.GL_NICEST); gl.glDisable(GL.GL_TEXTURE_2D); gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGB, textures[tIx].getWidth(),textures[tIx].getHeight(), 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, textures[tIx].getPixels()); gl.glEnable(GL.GL_TEXTURE_2D); // 20 polygons with 6 edges stdMaterials.setMaterial(gl, stdMaterials.MAT_WARM_WHITE, GL.GL_FRONT); for (int p = 0; p < 20; p++) { gl.glBegin(mode); gl.glNormal3f(normal6[p][0], normal6[p][1], normal6[p][2]); for (int ix = 0; ix < 6; ix++) { gl.glTexCoord2f(tex6[ix][0], tex6[ix][1]); gl.glVertex3f(p6[p][ix][0], p6[p][ix][1], p6[p][ix][2]); } gl.glEnd(); } // 12 polygons with 5 edges stdMaterials.setMaterial(gl, stdMaterials.MAT_RUBY, GL.GL_FRONT); for (int p = 0; p < 12; p++) { gl.glBegin(mode); gl.glNormal3f(normal5[p][0], normal5[p][1], normal5[p][2]); for (int ix = 0; ix < 5; ix++) gl.glVertex3f(p5[p][ix][0], p5[p][ix][1], p5[p][ix][2]); gl.glEnd(); } }
- The program project: https://svnit.hiof.no/svn/psource/JOGL/bucky