PDA

View Full Version : Clipping a sphere



shariq
04-29-2015, 02:16 AM
Tried several times posting here, sending whatever i can, can't even post a link, don't know if this is going to work as it denies posting for unknown reasons.


My Q.)
I have simply overridden/copied functions, from a base library, that are drawing the hemisphere and altering the GL part to basically INSERT clipping. I am able to draw the hemisphere centred at a location (latitude,longitude) and radius may be 2000. But when i cut it using a plane nothing happens. Please check the equation of the plane(its a plane parallel to the surface of the globe at height may be 1000, so (0,0,+1,1000))

The base class has drawUnitSphere so that might be causing some problems so trying to use GL's gluSphere(). But can't even see the sphere on globe. Used translate to shift it to my location(lat/lon) but still can't see it. Might be some issues with lat/lon and cartesian coordinates, or placing of my clipping code.

Here is the code snippet:
GL gl = dc.getGL(); // GL initialization checks for GL2 compatibility.
gl.glPushAttrib(GL.GL_POLYGON_BIT | GL.GL_TRANSFORM_BIT);
try
{

gl.glEnable(GL.GL_CULL_FACE);
gl.glFrontFace(GL.GL_CCW);

// Were applying a scale transform on the modelview matrix, so the normal vectors must be re-normalized
// before lighting is computed. In this case we're scaling by a constant factor, so GL_RESCALE_NORMAL
// is sufficient and potentially less expensive than GL_NORMALIZE (or computing unique normal vectors
// for each value of radius). GL_RESCALE_NORMAL was introduced in OpenGL version 1.2.
gl.glEnable(GL.GL_RESCALE_NORMAL);

gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glPushMatrix();

//clipping
DoubleBuffer eqn1 = BufferUtils.createDoubleBuffer(8).put(new double[] {0, 0, 1, 100});
eqn1.flip();
gl.glClipPlane(GL.GL_CLIP_PLANE0, eqn1);
gl.glEnable(GL.GL_CLIP_PLANE0);
try
{
gl.glLoadMatrixd(matrixArray, 0);
//this.drawUnitSphere(dc, subdivisions);
gl.glLoadIdentity();
gl.glTranslatef(75.2f, 32.5f, 0.0f); // Latitude is 32.5 and Longitude is 75.2
gl.glColor3f(1.0f, 0.0f, 0.0f);
GLU glu = dc.getGLU();
GLUquadric qd=glu.gluNewQuadric();
glu.gluSphere(qd,3.0f,20,20);
}
finally
{
gl.glPopMatrix();
}
}
finally
{
gl.glPopAttrib();
}

Carmine
04-29-2015, 11:09 AM
gl.glTranslatef(75.2f, 32.5f, 0.0f); // [B]Latitude is 32.5 and Longitude is 75.2This will not put your object at 75.2 degs lat and 32.5 degs lon. Lat and lon are angles (i.e. rotations), not translations. For simplicity, forget about the second sphere, and simply try to place a big point at 0.0 degs lat and 0.0 degs lon. You wouldn't translate it to (0.0, 0.0, 0.0), right? That would put it in the middle of the earth (i.e. not visible). What you should do is translate it to (earth_radius, 0.0, 0.0). That would put the point on the surface of the earth at 0.0 degs lat and 0.0 degs lon. Now if you want to move it to 20 degs lon, you would rotate that point by 20 degs around the Z axis (earth's polar axis). To rotate the point to 32.5 degs lat, you would rotate it by -32.5 degs about the Y axis. The order of rotations is important. The code would look something like -


glPushMatrix ();
glRotatef ( 75.2, 0,0,1); // Lon rotation.
glRotatef (-32.5, 0,1,0); // Lat rotation.
glPointSize (15.0); // Makes the point BIG.
glBegin (GL_POINTS);
glVertex3f (earth_radius, 0.0, 0.0); // Point on earth's surface at 0 degs lat, 0 degs lon.
glEnd ();
glPopMatrix ();
When you get it working for a big point, then move on to using a sphere.

shariq
04-29-2015, 11:59 PM
Hi Carmine,

Thanks for the response. I am new to OpenGL so still facing two basic issues :
1.) use of OpenGL commands and functions like pushMatrix(), Enable(), Matrixmode() etc. so trying to use a working code from somewhere and customizing to my needs as have some time constraints.
2.) The relation between globe and cartesian, that might be causing this problem. On a normal 640x480 framewindow, glusphere() draws.

Understood what you told. drawing a basic point as you suggested. It seems right except the extra GL functions, tried removing them aslo, still not drawing on that location.

Providing the code that I am calling on a mouse click, please see if something is wrong:

MouseClicked(MouseEvent me)
int x= me.getX(); //not using this at the moment since using globe
int y = me.getY();

DrawContext dc = canvas1.getSceneController().getDrawContext();
GL gl = dc.getGL();
gl.glPushAttrib(GL.GL_POLYGON_BIT | GL.GL_TRANSFORM_BIT); //not sure about these functions
try
{
gl.glEnable(GL.GL_CULL_FACE);// tried removing these still didn't work
gl.glFrontFace(GL.GL_CCW);
gl.glEnable(GL.GL_RESCALE_NORMAL);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glPushMatrix();
try
{
gl.glLoadIdentity();
double eRad = canvas1.getModel().getGlobe().getRadiusAt(new LatLon(0,0)); //in debug shows a value of 6378137.0
gl.glTranslated(eRad, 0, 0); //added this to move from earth centre to the surface, when just rotation didn't work
gl.glColor3f(1.0f, 0.0f, 0.0f);//red color
gl.glRotatef ( 75.2f, 0,0,1); // Lon rotation.
gl.glRotatef (-32.5f, 0,1,0); // Lat rotation.
gl.glPointSize (150.0f); // Makes the point BIG. 15 didn't work so 150
gl.glBegin (GL.GL_POINTS);
gl.glVertex3d (rad, 0.0, 0.0); // Point on earth's surface at 0 degs lat, 0 degs lon.
gl.glEnd ();
}
finally
{
gl.glPopMatrix();
}
}
finally
{
gl.glPopAttrib();
}
}

Carmine
04-30-2015, 10:06 AM
... On a normal 640x480 framewindow, glusphere() draws. ... This sounds promising. Are you saying that you can draw a sphere successfully using 'glusphere'? I assume this means that somewhere in your program gluSphere is called? The radius of the sphere is specified in that call. You shouldn't have to use a debugger to find the radius. Also, glTranslated(eRad, 0, 0) should not be necessary. What is 'rad' in the glVertex3d call? It should be erad, not rad. A point size of 15.0 should be plenty big enough if everything else is right. Make sure the point is a different color from the sphere.

shariq
05-03-2015, 10:25 PM
Hi Carmine,

No I can't draw a sphere. So drawing a point. Correct its eRad, still it doesn't draw the point.

Please check the code and also please let me know if the following functions in this code which I took from other examples are required/correct and have the correct parameters:
GlPushAttrib(),
glEnable(),
GlMatrixMode(),
glPushMatrix(),
glLoadIdentity(),
glPopMatrix(),
glPopAttrib()

Code:

public void mouseClicked(MouseEvent me){
DrawContext dc = canvas1.getSceneController().getDrawContext();
GL gl = dc.getGL();
gl.glPushAttrib(GL.GL_POLYGON_BIT | GL.GL_TRANSFORM_BIT);
try
{
gl.glEnable(GL.GL_CULL_FACE);
gl.glFrontFace(GL.GL_CCW);
gl.glEnable(GL.GL_RESCALE_NORMAL);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glPushMatrix();
try
{
gl.glLoadIdentity();
gl.glColor3f(1.0f, 0.0f, 0.0f);
double eRad = canvas1.getModel().getGlobe().getRadiusAt(new LatLon(0,0));
gl.glColor3f(1.0f, 0.0f, 0.0f);//red color
gl.glRotatef ( 75.2f, 0,0,1); // Lon rotation.
gl.glRotatef (-32.5f, 0,1,0); // Lat rotation.
gl.glPointSize (15.0f); // Makes the point BIG.
gl.glBegin (GL.GL_POINTS);
gl.glVertex3d (eRad, 0.0, 0.0); // Point on earth's surface at 0 degs lat, 0 degs lon.
gl.glEnd ();
}
finally
gl.glPopMatrix();
}
}
finally
{
gl.glPopAttrib();
}

shariq
05-03-2015, 11:21 PM
Now coming to original question:

I am trying to use WorldWind's SphereAirspace to construct a particular 3D airspace. The 3D shape I am trying to construct is a hemisphere cut parallel to the ground at two places, suppose at height h1 and h2 from the ground (ref the attached picture in post 1). The base of hemisphere is parallel to the ground.

I am trying to clip a portion of the hemisphere that has been drawn by SphereAirspace using openGl glClipPlane():
DoubleBuffer eqn1 = BufferUtils.createDoubleBuffer(8).put(new double[] {0, 0, 1, 500});
gl.glClipPlane(GL.GL_CLIP_PLANE0, eqn1);
gl.glEnable(GL.GL_CLIP_PLANE0);

I override the three functions in my class derived from WorldWind's SphereAirspace drawSphere(), drawUnitSphere() and makeSphere() and tried to modify the drawing to get the cut portion. I insert the above code during drawing

You can refer the SphereAirspace code here: http://worldwind31.arc.nasa.gov/svn/trunk/WorldWind/src/gov/nasa/worldwind/render/airspaces/SphereAirspace.java


This draws the hemisphere but does not clip it, if it clips the sphere as shown in the attached picture, I am done.

Please check the code below and let me know if clipping part is correct and where it should be placed.

My code is as follows:


@Override
public void drawSphere(DrawContext dc)
{
double[] altitudes = this.getAltitudes(dc.getVerticalExaggeration());
boolean[] terrainConformant = this.isTerrainConforming();
int subdivisions = this.getSubdivisions();

if (this.isEnableLevelOfDetail())
{
DetailLevel level = this.computeDetailLevel(dc);

Object o = level.getValue(SUBDIVISIONS);
if (o != null && o instanceof Integer)
subdivisions = (Integer) o;
}

Vec4 centerPoint = this.computePointFromPosition(dc,
this.location.getLatitude(), this.location.getLongitude(), altitudes[0], terrainConformant[0]);

Matrix modelview = dc.getView().getModelviewMatrix();
modelview = modelview.multiply(Matrix.fromTranslation(centerPo int));
modelview = modelview.multiply(Matrix.fromScale(this.getRadius ()));
double[] matrixArray = new double[16];
modelview.toArray(matrixArray, 0, false);

this.setExpiryTime(-1L); // Sphere geometry never expires.


GL gl = dc.getGL(); // GL initialization checks for GL2 compatibility.
gl.glPushAttrib(GL.GL_POLYGON_BIT | GL.GL_TRANSFORM_BIT);
try
{

gl.glEnable(GL.GL_CULL_FACE);
gl.glFrontFace(GL.GL_CCW);

// Were applying a scale transform on the modelview matrix, so the normal vectors must be re-normalized
// before lighting is computed. In this case we're scaling by a constant factor, so GL_RESCALE_NORMAL
// is sufficient and potentially less expensive than GL_NORMALIZE (or computing unique normal vectors
// for each value of radius). GL_RESCALE_NORMAL was introduced in OpenGL version 1.2.
gl.glEnable(GL.GL_RESCALE_NORMAL);

gl.glMatrixMode(GL.GL_MODELVIEW);

//clipping
double eRad = canvas.getModel().getGlobe().getRadiusAt(location) ;//added this to clipping height to come to earth's surface from core, please check this
DoubleBuffer eqn1 = BufferUtils.createDoubleBuffer(8).put(new double[] {0, 0, 1, eRad+ 1000}); //Please check if this equation is correct
eqn1.flip();
gl.glClipPlane(GL.GL_CLIP_PLANE0, eqn1);
gl.glEnable(GL.GL_CLIP_PLANE0);

gl.glPushMatrix();


try
{
gl.glLoadMatrixd(matrixArray, 0);
this.drawUnitSphere(dc, subdivisions);
}
finally
{
gl.glPopMatrix();
}
}
finally
{
gl.glPopAttrib();
}
}

@Override
public void drawUnitSphere(DrawContext dc, int subdivisions)
{
Object cacheKey = new Geometry.CacheKey(this.getClass(), "Sphere", subdivisions);
Geometry geom = (Geometry) this.getGeometryCache().getObject(cacheKey);
if (geom == null || this.isExpired(dc, geom))
{
if (geom == null)
geom = new Geometry();
this.makeSphere(1.0, subdivisions, geom);
this.updateExpiryCriteria(dc, geom);
this.getGeometryCache().add(cacheKey, geom);
}

this.getRenderer().drawGeometry(dc, geom);
}

@Override
public void makeSphere(double radius, int subdivisions, Geometry dest)
{
GeometryBuilder gb = this.getGeometryBuilder();
gb.setOrientation(GeometryBuilder.OUTSIDE);

GeometryBuilder.IndexedTriangleArray ita = gb.tessellateSphere((float) radius, subdivisions);
float[] normalArray = new float[3 * ita.getVertexCount()];
gb.makeIndexedTriangleArrayNormals(ita, normalArray);

dest.setElementData(GL.GL_TRIANGLES, ita.getIndexCount(), ita.getIndices());
dest.setVertexData(ita.getVertexCount(), ita.getVertices());
dest.setNormalData(ita.getVertexCount(), normalArray);
}

Dark Photon
05-04-2015, 06:00 AM
Please use
... or ... blocks around your source code. It makes it much more readable, increasing the likelihood that folks will want to sift through your posts to give you helpful suggestions. Thanks. (To illustrate, I've done this on your most recent post.)

shariq
05-04-2015, 10:59 PM
@Dark Photon: Thanks, will keep that in mind.