WorldWindGLAutoDrawable继承GLEventListener,按照jogl的模式调用init,display,reshape,dispose进行渲染。
先看display
public void display(GLAutoDrawable glAutoDrawable)
{
// Performing shutdown here in order to do so with a current GL context for GL resource disposal.
if (this.shuttingDown)
{
try
{
this.doShutdown();
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE, Logging.getMessage(
"WorldWindowGLCanvas.ExceptionWhileShuttingDownWorldWindow"), e);
}
return;
}
try
{
SceneController sc = this.getSceneController();
if (sc == null)
{
String msg = Logging.getMessage("WorldWindowGLCanvas.ScnCntrllerNullOnRepaint");
Logging.logger().severe(msg);
throw new IllegalStateException(msg);
}
// Determine if the view has changed since the last frame.
this.checkForViewChange();
Position positionAtStart = this.getCurrentPosition();
PickedObject selectionAtStart = this.getCurrentSelection();
PickedObjectList boxSelectionAtStart = this.getCurrentBoxSelection();
try
{
this.callRenderingListeners(new RenderingEvent(this.drawable, RenderingEvent.BEFORE_RENDERING));
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE,
Logging.getMessage("WorldWindowGLAutoDrawable.ExceptionDuringGLEventListenerDisplay"), e);
}
int redrawDelay = this.doDisplay();
if (redrawDelay > 0)
{
if (this.redrawTimer == null)
{
this.redrawTimer = new Timer(redrawDelay, new ActionListener()
{
public void actionPerformed(ActionEvent actionEvent)
{
redraw();
redrawTimer = null;
}
});
redrawTimer.setRepeats(false);
redrawTimer.start();
}
}
try
{
this.callRenderingListeners(new RenderingEvent(this.drawable, RenderingEvent.BEFORE_BUFFER_SWAP));
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE,
Logging.getMessage("WorldWindowGLAutoDrawable.ExceptionDuringGLEventListenerDisplay"), e);
}
this.doSwapBuffers(this.drawable);
Double frameTime = sc.getFrameTime();
if (frameTime != null)
this.setValue(PerformanceStatistic.FRAME_TIME, frameTime);
Double frameRate = sc.getFramesPerSecond();
if (frameRate != null)
this.setValue(PerformanceStatistic.FRAME_RATE, frameRate);
// Dispatch the rendering exceptions accumulated by the SceneController during this frame to our
// RenderingExceptionListeners.
Iterable<Throwable> renderingExceptions = sc.getRenderingExceptions();
if (renderingExceptions != null)
{
for (Throwable t : renderingExceptions)
{
if (t != null)
this.callRenderingExceptionListeners(t);
}
}
this.callRenderingListeners(new RenderingEvent(this.drawable, RenderingEvent.AFTER_BUFFER_SWAP));
// Position and selection notification occurs only on triggering conditions, not same-state conditions:
// start == null, end == null: nothing selected -- don't notify
// start == null, end != null: something now selected -- notify
// start != null, end == null: something was selected but no longer is -- notify
// start != null, end != null, start != end: something new was selected -- notify
// start != null, end != null, start == end: same thing is selected -- don't notify
Position positionAtEnd = this.getCurrentPosition();
if (positionAtStart != null || positionAtEnd != null)
{
// call the listener if both are not null or positions are the same
if (positionAtStart != null && positionAtEnd != null)
{
if (!positionAtStart.equals(positionAtEnd))
this.callPositionListeners(new PositionEvent(this.drawable, sc.getPickPoint(),
positionAtStart, positionAtEnd));
}
else
{
this.callPositionListeners(new PositionEvent(this.drawable, sc.getPickPoint(),
positionAtStart, positionAtEnd));
}
}
PickedObject selectionAtEnd = this.getCurrentSelection();
if (selectionAtStart != null || selectionAtEnd != null)
{
this.callSelectListeners(new SelectEvent(this.drawable, SelectEvent.ROLLOVER,
sc.getPickPoint(), sc.getPickedObjectList()));
}
PickedObjectList boxSelectionAtEnd = this.getCurrentBoxSelection();
if (boxSelectionAtStart != null || boxSelectionAtEnd != null)
{
this.callSelectListeners(new SelectEvent(this.drawable, SelectEvent.BOX_ROLLOVER,
sc.getPickRectangle(), sc.getObjectsInPickRectangle()));
}
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE, Logging.getMessage(
"WorldWindowGLCanvas.ExceptionAttemptingRepaintWorldWindow"), e);
}
}
再找到关键的方法doDiaplay
protected int doDisplay()
{
return this.getSceneController().repaint();
}
/**
* Performs the actual buffer swap. Provided so that subclasses may override the swap steps.
*
* @param drawable the window's associated drawable.
*/
protected void doSwapBuffers(GLAutoDrawable drawable)
{
drawable.swapBuffers();
}
/**
* See {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int)}.
*
* @param glAutoDrawable the drawable
*/
public void reshape(GLAutoDrawable glAutoDrawable, int x, int y, int w, int h)
{
// This is apparently necessary to enable the WWJ canvas to resize correctly with JSplitPane.
((Component) glAutoDrawable).setMinimumSize(new Dimension(0, 0));
}
@Override
public void redraw()
{
if (this.drawable != null)
((AWTGLAutoDrawable) this.drawable).repaint();
}
public void redrawNow()
{
if (this.drawable != null)
this.drawable.display();
}
其中repaint是关键方法,repaint是借口SceneController的方法,在WorldWindGLAutoDrawable中调用的SceneController具体实现类为StereoOptionSceneController
public WorldWindowImpl()
{
this.sceneController = (SceneController) WorldWind.createConfigurationComponent(
AVKey.SCENE_CONTROLLER_CLASS_NAME);
// Set up to initiate a repaint whenever a file is retrieved and added to the local file store.
WorldWind.getDataFileStore().addPropertyChangeListener(this);
}
<Property name="gov.nasa.worldwind.avkey.SceneControllerClassName"
value="gov.nasa.worldwind.StereoOptionSceneController"/>
然而,repaint是在父类AbstractScenceController中实现的,核心在于调用doRepaint
public int repaint()
{
this.frameTime = System.currentTimeMillis();
this.perFrameStatistics.clear();
this.renderingExceptions.clear(); // Clear the rendering exceptions accumulated during the last frame.
this.glRuntimeCaps.initialize(GLContext.getCurrent());
this.initializeDrawContext(this.dc);
this.doRepaint(this.dc);
++this.frame;
long time = System.currentTimeMillis();
this.frameTime = System.currentTimeMillis() - this.frameTime;
if (time - this.timebase > 2000) // recalculate every two seconds
{
this.framesPerSecond = frame * 1000d / (time - timebase);
this.timebase = time;
this.frame = 0;
}
this.dc.setPerFrameStatistic(PerformanceStatistic.FRAME_TIME, "Frame Time (ms)", (int) this.frameTime);
this.dc.setPerFrameStatistic(PerformanceStatistic.FRAME_RATE, "Frame Rate (fps)", (int) this.framesPerSecond);
this.dc.setPerFrameStatistic(PerformanceStatistic.PICK_TIME, "Pick Time (ms)", (int) this.pickTime);
Set<String> perfKeys = dc.getPerFrameStatisticsKeys();
if (perfKeys == null)
return dc.getRedrawRequested();
if (perfKeys.contains(PerformanceStatistic.MEMORY_CACHE) || perfKeys.contains(PerformanceStatistic.ALL))
{
this.dc.setPerFrameStatistics(WorldWind.getMemoryCacheSet().getPerformanceStatistics());
}
if (perfKeys.contains(PerformanceStatistic.TEXTURE_CACHE) || perfKeys.contains(PerformanceStatistic.ALL))
{
if (dc.getTextureCache() != null)
this.dc.setPerFrameStatistic(PerformanceStatistic.TEXTURE_CACHE,
"Texture Cache size (Kb)", this.dc.getTextureCache().getUsedCapacity() / 1000);
}
if (perfKeys.contains(PerformanceStatistic.JVM_HEAP) || perfKeys.contains(PerformanceStatistic.ALL))
{
long totalMemory = Runtime.getRuntime().totalMemory();
this.dc.setPerFrameStatistic(PerformanceStatistic.JVM_HEAP,
"JVM total memory (Kb)", totalMemory / 1000);
this.dc.setPerFrameStatistic(PerformanceStatistic.JVM_HEAP_USED,
"JVM used memory (Kb)", (totalMemory - Runtime.getRuntime().freeMemory()) / 1000);
}
return dc.getRedrawRequested();
}
doRepaint是在StereoOptionSceneController的父类BasicSceneController中实现的
public class BasicSceneController extends AbstractSceneController
{
SectorGeometryList sglC, sglL, sglR;
Sector visibleSectorC, visibleSectorL, visibleSectorR;
public void doRepaint(DrawContext dc)
{
this.initializeFrame(dc);
try
{
if (dc.getGlobe() instanceof Globe2D && ((Globe2D)dc.getGlobe()).isContinuous())
this.do2DContiguousRepaint(dc);
else
this.doNormalRepaint(dc);
}
finally
{
this.finalizeFrame(dc);
}
}
protected void doNormalRepaint(DrawContext dc)
{
this.applyView(dc);
this.createPickFrustum(dc);
this.createTerrain(dc);
this.preRender(dc);
this.clearFrame(dc);
this.pick(dc);
this.clearFrame(dc);
this.draw(dc);
}
绘制图层的方法就在draw这个方法里面了,它在AbstractSceneController中实现
protected void draw(DrawContext dc)
{
try
{
// Draw the layers.
if (dc.getLayers() != null)
{
for (Layer layer : dc.getLayers())
{
try
{
if (layer != null)
{
dc.setCurrentLayer(layer);
layer.render(dc);
}
}
catch (Exception e)
{
String message = Logging.getMessage("SceneController.ExceptionWhileRenderingLayer",
(layer != null ? layer.getClass().getName() : Logging.getMessage("term.unknown")));
Logging.logger().log(Level.SEVERE, message, e);
// Don't abort; continue on to the next layer.
}
}
dc.setCurrentLayer(null);
}
// Draw the deferred/ordered surface renderables.
this.drawOrderedSurfaceRenderables(dc);
if (this.isDeferOrderedRendering())
return;
if (this.screenCreditController != null)
this.screenCreditController.render(dc);
// Draw the deferred/ordered renderables.
dc.setOrderedRenderingMode(true);
// dc.applyGroupingFilters();
dc.applyClutterFilter();
while (dc.peekOrderedRenderables() != null)
{
try
{
dc.pollOrderedRenderables().render(dc);
}
catch (Exception e)
{
Logging.logger().log(Level.WARNING,
Logging.getMessage("BasicSceneController.ExceptionDuringRendering"), e);
}
}
dc.setOrderedRenderingMode(false);
// Draw the diagnostic displays.
if (dc.getSurfaceGeometry() != null && dc.getModel() != null && (dc.getModel().isShowWireframeExterior() ||
dc.getModel().isShowWireframeInterior() || dc.getModel().isShowTessellationBoundingVolumes()))
{
Model model = dc.getModel();
float[] previousColor = new float[4];
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
gl.glGetFloatv(GL2.GL_CURRENT_COLOR, previousColor, 0);
for (SectorGeometry sg : dc.getSurfaceGeometry())
{
if (model.isShowWireframeInterior() || model.isShowWireframeExterior())
sg.renderWireframe(dc, model.isShowWireframeInterior(), model.isShowWireframeExterior());
if (model.isShowTessellationBoundingVolumes())
{
gl.glColor3d(1, 0, 0);
sg.renderBoundingVolume(dc);
}
}
gl.glColor4fv(previousColor, 0);
}
}
catch (Throwable e)
{
Logging.logger().log(Level.SEVERE, Logging.getMessage("BasicSceneController.ExceptionDuringRendering"), e);
}
}
layer.render()就是在进行绘制渲染的过程,因为所有的图层类都继承了AbstractLayer,render()在AbstractLayer就做了实现
public void render(DrawContext dc)
{
if (!this.enabled)
return; // Don't check for arg errors if we're disabled
if (null == dc)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
if (null == dc.getGlobe())
{
String message = Logging.getMessage("layers.AbstractLayer.NoGlobeSpecifiedInDrawingContext");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
if (null == dc.getView())
{
String message = Logging.getMessage("layers.AbstractLayer.NoViewSpecifiedInDrawingContext");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
if (!this.isLayerActive(dc))
return;
if (!this.isLayerInView(dc))
return;
this.doRender(dc);
}
具体不同的图层渲染的区别主要在于对Render()的重载实现不同!
所以,自定RenderableLayer渲染图层时,就是重载render()方法,利用opengl命令进行绘制即可。
参考示例:https://github.com/upcdxlq/WorldWindCampus/blob/master/RenderableLayer/DrawCubeLayer.java