A Table Lamp
Structure
The lamp consist of cylinders (the foot, the two arms and the lamp shade). Each joint is marked with a sphere.
|
|
oneLamp
The lamps are placed on a round "table" in the xy-plane. A lamp is implemented in class oneLamp:
package tablelamp;
import javax.media.opengl.GL;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.glu.GLUquadric;
/**
*
* @author borres
*/
public class oneLamp
{
// nice to have
private final float G2R=0.01745277777778f;
private final float R2G=57.29577951308f;
//id for lamp
int m_LampID;
// the geometry of the lamp
// length of the three arms
int[] m_Arm={40,50,10};
// angles for armposition:
// thickness of arm
int m_ArmW=3;
// radius for spheres in elbows
int m_ElbowRadius=6;
// base board
int m_BaseRadius=40;
int m_BaseHeight=2;
// lamps head
float m_TopR=10;
float m_OpenR=30;
float m_HeadCyl=30;
float m_HeadSpread=30;;
// angles for each rotation
float m_v1=0.0f;
float m_v2=0.0f;
float m_v3=10.0f;
float m_v4=30.0f;
// spot smoothness
float m_SpotExp=0.0f;
// quadric drawing precision
int nLong=10;
int nRound=20;
// which lightsource
int m_MyLight;
boolean m_LampIsOn=false;
public oneLamp(int LampID)
{
m_MyLight=GL.GL_LIGHT0+LampID+1;
m_LampID=LampID;
}
public void draw(GL gl)
{
GLU glu=new GLU();
// preserve modelview
gl.glPushMatrix();
if(!m_LampIsOn)
gl.glDisable(m_MyLight);
// use a quadric to draw parts of lamp
GLUquadric glpQ=glu.gluNewQuadric();
// material
int baseMaterial=stdMaterials.MAT_GREEN_PLASTIC;
int elbowMaterial=stdMaterials.MAT_GREEN_PLASTIC;
int armMaterial=stdMaterials.MAT_WARM_WHITE;
// scale the integer coordinates down to unit box
gl.glScalef(0.01f,0.01f,0.01f);
// display the lamp
// use lightsource as ID for lamp
gl.glLoadName(m_LampID*10+0);
// base, foot
stdMaterials.setMaterial(gl,baseMaterial,GL.GL_FRONT);
gl.glTranslatef(0.0f,0.0f,(float)-m_BaseHeight);
glu.gluDisk(glpQ,0.0f,(float)m_BaseRadius,20,20);
glu.gluCylinder(glpQ,(float)m_BaseRadius,(float)m_BaseRadius,
(float)m_BaseHeight,nRound,nLong);
gl.glTranslatef(0.0f,0.0f,(float)m_BaseHeight);
glu.gluDisk(glpQ,0,(float)m_BaseRadius,nRound,nRound);
stdMaterials.setMaterial(gl,elbowMaterial,GL.GL_FRONT);
glu.gluSphere(glpQ,(float)m_ElbowRadius,20,20);
// first arm, basic orientation around z-axis
gl.glLoadName(m_LampID*10+1);
gl.glRotatef(m_v1,0.0f,0.0f,1.0f);
// from now on all arms are rotated around y-axis
gl.glRotatef(m_v2,0.0f,1.0f,0.0f);
stdMaterials.setMaterial(gl,armMaterial,GL.GL_FRONT);
glu.gluCylinder(glpQ,m_ArmW,m_ArmW,m_Arm[0],nRound,nLong);
gl.glTranslatef(0.0f,0.0f,(float)m_Arm[0]);
stdMaterials.setMaterial(gl,elbowMaterial,GL.GL_FRONT);
glu.gluSphere(glpQ,m_ElbowRadius,nRound,nRound);
// second arm
gl.glLoadName(m_LampID*10+2);
gl.glRotatef(m_v3,0.0f,1.0f,0.0f);
stdMaterials.setMaterial(gl,armMaterial,GL.GL_FRONT);
glu.gluCylinder(glpQ,m_ArmW,m_ArmW,m_Arm[1],nRound,nLong);
gl.glTranslatef(0.0f,0.0f,(float)m_Arm[1]);
stdMaterials.setMaterial(gl,elbowMaterial,GL.GL_FRONT);
glu.gluSphere(glpQ,m_ElbowRadius,nRound,nRound);
// head
gl.glLoadName(m_LampID*10+3);
gl.glTranslatef(0.0f,0.0f,(float)m_ElbowRadius);
gl.glRotatef(m_v4,0.0f,1.0f,0.0f);
gl.glTranslatef(0.0f,0.0f,(float)-m_HeadCyl/2.0f);
stdMaterials.setMaterial(gl,elbowMaterial,GL.GL_FRONT);
glu.gluDisk(glpQ,0.0,m_TopR,nRound,nRound);
glu.gluCylinder(glpQ,m_TopR,m_TopR,m_HeadCyl,nRound,nLong);
gl.glTranslatef(0.0f,0.0f,(float)m_HeadCyl);
glu.gluCylinder(glpQ,m_TopR,m_OpenR,m_HeadSpread,nRound,nLong);
//lighton
// lightbulb and inner screen
if(m_LampIsOn)
{
// light inside of head
stdMaterials.setMaterial(gl,stdMaterials.MAT_BRIGHT_WHITE,GL.GL_FRONT);
glu.gluCylinder(glpQ,m_TopR-0.1f,m_OpenR-0.1f,m_HeadSpread,nRound,nLong);
// specify and set the spotlight
float spambient[] = {0.2f,0.2f,0.2f,1.0f };
float spdiffuse[] = {0.8f,0.8f,0.8f,1.0f };
float spspecular[] = {0.8f,0.8f,0.8f,1.0f };
float spposition[] = {0.0f,0.0f,0.0f,1.0f};
float spdirection[] = {0.0f,0.0f,1.0f};
float spotangle= R2G*(float)Math.atan((m_OpenR-m_TopR)/m_HeadSpread)+10;
gl.glLightfv(m_MyLight, GL.GL_AMBIENT, spambient,0);
gl.glLightfv(m_MyLight, GL.GL_DIFFUSE, spdiffuse,0);
gl.glLightfv(m_MyLight, GL.GL_SPECULAR, spspecular,0);
gl.glLightfv(m_MyLight, GL.GL_POSITION, spposition,0);
gl.glLightfv(m_MyLight, GL.GL_SPOT_DIRECTION, spdirection,0);
gl.glLightf (m_MyLight, GL.GL_SPOT_CUTOFF, spotangle);
gl.glLightf(m_MyLight, GL.GL_SPOT_EXPONENT, m_SpotExp);
gl.glEnable(GL.GL_LIGHTING);
gl.glEnable(m_MyLight);
// light the bulb
float[] ev={1.0f,1.0f,1.0f};
float[] nev={0.0f,0.0f,0.0f};
gl.glMaterialfv(GL.GL_FRONT,GL.GL_EMISSION,ev,0);
gl.glTranslatef(0.0f,0.0f,m_HeadSpread/5.0f);
glu.gluSphere(glpQ,m_TopR,nRound,nRound);
gl.glMaterialfv(GL.GL_FRONT,GL.GL_EMISSION,nev,0);
}
glu.gluSphere(glpQ,m_TopR,nRound,nRound);
//eoflighton
// finished with quadric, kill it
glu.gluDeleteQuadric(glpQ);
// reset matrix
gl.glPopMatrix();
}
public float GetAngle(int ix)
{
switch(ix)
{
case 0: return m_v1;
case 1:return m_v2;
case 2:return m_v3;
case 3:return m_v4;
default: return 0.0f;
}
}
public void SetAngle(int x,float v)
{
switch(x)
{
case 0:m_v1=v; break;
case 1:m_v2=v; break;
case 2:m_v3=v; break;
case 3:m_v4=v; break;
default: ;
}
}
public void SetLightOn(boolean on){m_LampIsOn=on;}
public boolean GetLightOn() {return m_LampIsOn;}
public void setSpotExponent(int v) {m_SpotExp=v/10.0f;}
}
Rendering
The class GLrenderer controls the rendering of the scene and selection of lamp or lamp part. The lamps are kept in :
private Vector<oneLamp> pLamp
The method display administrates the rendering:
public void display(GLAutoDrawable drawable)
{
GL gl = drawable.getGL();
// Clear the color and depth buffers.
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
if(m_pressed)// we have pressed right mousebutton
{
m_pressed=false;
int n=pickSelected(m_thePanel.getGL(),m_lastX,m_lastY);
if(n!=NO_OBJECT)
{
m_hilitedPart=n%10; // lamp part
m_hilitedLamp=n/10; // lampindex
m_bIsManipulating=true;
}
else
{
m_hilitedLamp=NO_OBJECT; // to make sure
}
}
DrawScene(gl);
}
The method DrawScene goes like this:
private void DrawScene(GL gl)
{
GLU glu=new GLU();
// Set up for scene
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
glu.gluLookAt(m_zx,m_zy,m_zz, // eye
0.0f,0.0f,0.0f, // VRF
0.0f,0.0f,1.0f); // VUP
// interactive movement
// rotate around z-axis
gl.glRotatef(m_zv,0.0f,0.0f,1.0f);
// rotate around x-axis
gl.glRotatef(m_xv,1.0f,0.0f,0.0f);
// set a name;NO_OBJECT on anything irrelevant we draw
gl.glLoadName(NO_OBJECT);
GLUquadric glpQ=glu.gluNewQuadric();
//----------------
// draw table
gl.glPushMatrix();
gl.glTranslatef(0.0f, 0.0f, -0.01f);
stdMaterials.setMaterial(gl, stdMaterials.MAT_EMERALD, GL.GL_FRONT_AND_BACK);
glu.gluDisk(glpQ,0.0,2.0,table_res,table_res);
gl.glPopMatrix();
//-----------------
// draw lamps
// lamp 0
gl.glPushMatrix();
gl.glTranslatef(-1.0f,1.0f,0.0f);
gl.glLoadName(0);
pLamp.elementAt(0).draw(gl);
gl.glPopMatrix();
// lamp 1
gl.glPushMatrix();
gl.glTranslatef(1.0f,1.0f,0.0f);
gl.glLoadName(1);
pLamp.elementAt(1).draw(gl);
gl.glPopMatrix();
// lamp 2
gl.glPushMatrix();
gl.glTranslatef(1.0f,-1.0f,0.0f);
gl.glLoadName(2);
pLamp.elementAt(2).draw(gl);
gl.glPopMatrix();
// lamp 3
gl.glPushMatrix();
gl.glTranslatef(-1.0f,-1.0f,0.0f);
gl.glLoadName(2);
pLamp.elementAt(3).draw(gl);
gl.glPopMatrix();
// set a name on anything irrelevant we draw
gl.glLoadName(NO_OBJECT);
// finnish drawing
gl.glFlush();
}
Naming
we know that we have to set a name for each part of the scene we want to identify by pointing. In this solution this means that we must identify which lamp and which part of the lamp. We use the strategy that the lamps are numbered from 0, and the parts are numbered as 10*lamppnumber + partnumber. In DrawScene:
// lamp 1 gl.glPushMatrix(); gl.glTranslatef(1.0f,1.0f,0.0f); gl.glLoadName(1); pLamp.elementAt(1).draw(gl); gl.glPopMatrix();
and in each lamps draw:
// first arm, basic orientation around z-axis gl.glLoadName(m_LampID*10+1); ...
The hit mechanism is identical to the strategy used in Selection and is implemented in method pickSelected in GLRenderer.
Light Effect
The light is a combination of three effects.
- We want to set up a spotlight that "fits" in the lamp shade
- We want the light bulb to look "lighted"
- We we want to make the inside of the lamp shade to look lighted
// lightbulb and inner screen
if(m_LampIsOn)
{
// light inside of head
stdMaterials.setMaterial(gl,stdMaterials.MAT_BRIGHT_WHITE,GL.GL_FRONT);
glu.gluCylinder(glpQ,m_TopR-0.1f,m_OpenR-0.1f,m_HeadSpread,nRound,nLong);
// specify and set the spotlight
float spambient[] = {0.2f,0.2f,0.2f,1.0f };
float spdiffuse[] = {0.8f,0.8f,0.8f,1.0f };
float spspecular[] = {0.8f,0.8f,0.8f,1.0f };
float spposition[] = {0.0f,0.0f,0.0f,1.0f};
float spdirection[] = {0.0f,0.0f,1.0f};
float spotangle= R2G*(float)Math.atan((m_OpenR-m_TopR)/m_HeadSpread)+10;
gl.glLightfv(m_MyLight, GL.GL_AMBIENT, spambient,0);
gl.glLightfv(m_MyLight, GL.GL_DIFFUSE, spdiffuse,0);
gl.glLightfv(m_MyLight, GL.GL_SPECULAR, spspecular,0);
gl.glLightfv(m_MyLight, GL.GL_POSITION, spposition,0);
gl.glLightfv(m_MyLight, GL.GL_SPOT_DIRECTION, spdirection,0);
gl.glLightf (m_MyLight, GL.GL_SPOT_CUTOFF, spotangle);
gl.glLightf(m_MyLight, GL.GL_SPOT_EXPONENT, m_SpotExp);
gl.glEnable(GL.GL_LIGHTING);
gl.glEnable(m_MyLight);
// light the bulb
float[] ev={1.0f,1.0f,1.0f};
float[] nev={0.0f,0.0f,0.0f};
gl.glMaterialfv(GL.GL_FRONT,GL.GL_EMISSION,ev,0);
gl.glTranslatef(0.0f,0.0f,m_HeadSpread/5.0f);
glu.gluSphere(glpQ,m_TopR,nRound,nRound);
gl.glMaterialfv(GL.GL_FRONT,GL.GL_EMISSION,nev,0);
}
glu.gluSphere(glpQ,m_TopR,nRound,nRound);
Note the use of GL_SPOT_EXPONENT that softens the edges of the spot. Comined with the granularity of the table, we can control the light effect. Both the spot exponent and the granularity can be manipulated in the application. See also Module Shades and smoothness












