Integrating jPCT in NASA 3D GIS WorldWind

Started by iamfoolberg, September 12, 2015, 11:29:56 AM

Previous topic - Next topic

iamfoolberg

In order to draw 3d animations in WorldWind, i'm trying to introducing jPCT into it.
The idea is simple: 1.inject WW's GL2 object into jPCT, 2. jPCT draws all of its Object3ds on the GL2, 3. A WW's layer is used to wrap 1 & 2.

Firstly, i write a ww's layer Jpct3DLayer which extends ww's AbstractLayer.

In the method doRender(), which will be called back when the WW is request to update, the following operations are performed:
1. store all WW's GL matrix.
2. inject the WW's GL into jPCT if it is not the same as last injected.
3. set the camera location to WW's eyepoint, the coordinates are adjusted from (x,y,z) to (x,-y,-z).
4. call jPCT to repaint its world.
5. force jPCT  to draw its scene into injected GL.
6. restore all WW's GL matrix.

To test the jPCT layer, i load a .3ds file with a .jpg texture. And rotate the 3d object continously.

The layer looks like:

package org.aoe.det.ww.d3man.ww;

import java.awt.Canvas;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;

import org.aoe.det.ww.d3man.D3Man;
import org.gbzh.util.log.Log;
import org.gbzh.util.resource.Resource;

import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Loader;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.Util;
import com.threed.jpct.World;

import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.geom.Vec4;
import gov.nasa.worldwind.layers.AbstractLayer;
import gov.nasa.worldwind.render.DrawContext;

/**
* A jPCT based three model layer. In jPCT, positive x goes to the right,
* positive z goes INTO the screen and positive y goes DOWN. In worldwind,
* positive x goes to the right, positive z goes OUT the screen and positive
* goes UP. In worldwind,cartesian coordinates's point(0, 0, radius of the
* earth) has the longi/lati-tude position(0, 0, radius of the earth). longitude
* goes RIGHT to +180 and left to -180, latitude goes UP to +90 and down to -90.
*
* @author berg
*
*/
public class Jpct3DLayer extends AbstractLayer {
/** The world of jPCT. */
public World world;
/***/
public FrameBuffer buffer;
public GL2 lastGL;
public Canvas canvas;
public Object3D box;

public Jpct3DLayer() {
this.world = new World();
this.world.setAmbientLight(255, 255, 255);

TextureManager.getInstance().addTexture("box",
new Texture(Resource.getResource("data/red.jpg")));

Object3D[] objs=Loader.load3DS(Resource.getResource("data/spaceship3.3ds"), 1);
    if (objs.length>0) {
    box = Object3D.mergeAll(objs);
    box.scale(0.3f);
    }else{      
    box = Primitives.getBox(13f, 2f);
    }

box.setTexture("box");
box.setEnvmapped(Object3D.ENVMAP_ENABLED);
box.build();
world.addObject(box);
Position boxPos = Position.fromDegrees(0, 20, 16000);
Position camPos = Position.fromDegrees(0, 20, 16000);

setObject3DPosition(box, ww2jpct(boxPos));
this.world.getCamera().setPosition(ww2jpct(camPos));
this.world.getCamera().lookAt(ww2jpct(boxPos));



new Thread(){//rotate the box
@Override
public void run() {
while(true){
box.rotateZ(0.1f);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
/**
* prepare a jPCT FrameBuffer for given width&height.
* @param width
* @param height
*/
private void preparejPCTBuffer(int width, int height) {
method: do {
if(this.buffer!=null){
if(this.buffer.getHeight()!=height || this.buffer.getWidth()!=width){
this.buffer.dispose();
this.buffer = null;
}
}
if(this.buffer == null){
this.buffer = new FrameBuffer(width, height,
FrameBuffer.SAMPLINGMODE_NORMAL);

canvas = buffer.enableGLCanvasRenderer();
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
}
break method;
} while (false);
}

// puts opengl in the correct state for this layer
protected void beginDraw(DrawContext dc) {
GL2 gl = (GL2) dc.getGL();

// gl.glPushAttrib(GL2.GL_TEXTURE_BIT | GL2.GL_ENABLE_BIT
// | GL2.GL_CURRENT_BIT | GL2.GL_TRANSFORM_BIT);
gl.glPushAttrib(GL2.GL_ALL_ATTRIB_BITS);

// if (!dc.isPickingMode()) {
// gl.glEnable(GL.GL_BLEND);
// gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
// }

gl.glMatrixMode(GL2.GL_MODELVIEW);//|GL2.GL_PROJECTION|GL2.GL_TEXTURE);
gl.glPushMatrix();
gl.glLoadIdentity();
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glPushMatrix();
gl.glLoadIdentity();
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glPushMatrix();
gl.glLoadIdentity();

gl.glMatrixPushEXT(GL2.GL_MODELVIEW);
gl.glMatrixPushEXT(GL2.GL_PROJECTION);
gl.glMatrixPushEXT(GL2.GL_TEXTURE);
this.preparejPCTBuffer(dc.getDrawableWidth(), dc.getDrawableHeight());
// inject gl if neccessary
if (this.lastGL != gl) {
Util.injectExternalGL(canvas, gl);
this.lastGL = gl;
}
}
/**
* Called by WorldWind to refresh this layer.
*/
@Override
protected void doRender(DrawContext dc) {
this.beginDraw(dc);

// get the eye point of WW, to set the camera in jPCT
// so that jpct's camera go with ww's eye point
Vec4 eyeLoc = dc.getView().getEyePoint();
this.world.getCamera().setPosition(ww2jpct(eyeLoc));
this.world.getCamera().lookAt(new SimpleVector(0, 0, 0));

// save current gl settings before change it
dc.getView().pushReferenceCenter(dc, eyeLoc);

// jPCT draw its world with the given gl
this.repaint();

// force jPCT to draw on WW's gl.
Util.forceDraw(canvas);

    // restore last gl settings
dc.getView().popReferenceCenter(dc);
this.endDraw(dc);
}

// resets opengl state
protected void endDraw(DrawContext dc) {
GL2 gl = (GL2) dc.getGL();

gl.glMatrixPopEXT(GL2.GL_TEXTURE);
gl.glMatrixPopEXT(GL2.GL_PROJECTION);
gl.glMatrixPopEXT(GL2.GL_MODELVIEW);

gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glPopMatrix();
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glPopMatrix();
gl.glMatrixMode(GL2.GL_MODELVIEW);//|GL2.GL_PROJECTION|GL2.GL_TEXTURE);
gl.glPopMatrix();

gl.glPopAttrib();
}

/**
* convert WorldWind's Vec4 to jPCT's SimpleVector.
*
* @param x
* @param y
* @param z
* @return
*/
public static SimpleVector ww2jpct(double x, double y, double z) {
return new SimpleVector((float) x, (float) -y, (float) -z);
}

/**
* convert WorldWind's Vec4 to jPCT's SimpleVector.
*
* @param x
* @param y
* @param z
* @return
*/
public static SimpleVector ww2jpct(Vec4 vec) {
return new SimpleVector((float) vec.x, (float) -vec.y, (float) -vec.z);
}

/**
* move the object to the given position, not delta.
*
* @param obj
*            the object to me moved
* @param sv
*            the target position
*/
public static void setObject3DPosition(Object3D obj, SimpleVector sv) {
method: do {
if (sv == null || obj == null) {
Log.e("The input param sv or obj is null.");
break method;
}
SimpleVector cur = obj.getTranslation();
obj.translate(sv.calcSub(cur));
} while (false);
}
/**
* move the object to the given position, not delta.
* x,y,z is jPCT coordinate.
* @param obj
* @param x
* @param y
* @param z
*/
public static void setObject3DPosition(Object3D obj, float x, float y,
float z) {
method: do {
if (obj == null) {
Log.e("The input param obj is null.");
break method;
}
SimpleVector sv = new SimpleVector(x, y, z);
SimpleVector cur = obj.getTranslation();
obj.translate(sv.calcSub(cur));
} while (false);
}

public static Vec4 jpct2wwPoint(float x, float y, float z) {
return new Vec4(x, -y, -z);
}

public static Vec4 jpct2wwPoint(SimpleVector sv) {
Vec4 result = null;
method: do {
if (sv == null) {
Log.e("The input param sv is null.");
break method;
}
result = new Vec4(sv.x, -sv.y, -sv.z);
} while (false);
return result;
}

public static SimpleVector ww2jpct(Position position) {
SimpleVector result = null;
method: do {
if (position == null) {
Log.e("The input param position is null.");
break method;
}
Vec4 vec = D3Man.getInstance().position2Point(position);
result = new SimpleVector((float) vec.x, (float) -vec.y,
(float) -vec.z);
} while (false);
return result;
}

public static Position jpct2wwPosition(float x, float y, float z) {
return D3Man.getInstance().point2Position(x, -y, -z);
}

public static Position jpct2wwPosition(SimpleVector sv) {
Position result = null;
method: do {
if (sv == null) {
Log.e("The input param sv is null.");
break method;
}
result = D3Man.getInstance().point2Position(sv.x, -sv.y, -sv.z);
} while (false);
return result;
}



/**
* force this layer to redraw components inside it.
*/
public void repaint() {
// buffer.clear(new Color(0.0f,0.0f,0.0f,1.0f));
world.renderScene(buffer);
world.draw(buffer);
buffer.update();
buffer.displayGLOnly();
canvas.repaint();
}
}


As we can see, the Object3d box(.3ds if loaded, or a primitive box) is placed at (0, 20, 16000) with WW's latitude-longitude-elevation coordinate.

To debug the layer, i add a BeanShell script engine so that i can execute script's lively.
When i execute the following script to locate the 3d object, i can not directly see it.
In fact, it is blocked by other layers. Event when i put the jPCTLayer at the top or last of the layers' list, it is blocked.
When i unselect the blocker layer, make it invisible, i can see the jPCT's 3d object, rotating.

import gov.nasa.worldwind.geom.Position;
import java.util.ArrayList;
import java.util.List;
import org.gbzh.jsapi.tts.AudioData;
import org.gbzh.jsapi.tts.SimplePlayer;
import org.gbzh.jsapi.tts.SpeechEngine;
import org.aoe.det.ww.d3man.ww.KMLLayerMan;


wwFunc(){
  //go to given position
  public void GoTo(double latitude, double longitude, double elevation){
    Position position = Position.fromDegrees(latitude, longitude, elevation);
    worldwindow.getView().goTo(position, elevation);
  }
  return this;
}
ww = wwFunc();

ww.GoTo(0.0, 20.0, 16020.0);


I don't know why the jPCT layer is blocked by others.

And the WW dose not function well with jPCT layer.
When the cursor is on the WW's earth globe, it will display the current lat-lon-elev data.
But when the jPCT layer is loaded, it only displays "Off", which means the cursor is not on the globe.
Another problem is the navigator view display nothing but white square.

The following code shows how i force jPCT to draw on WW's GL. Note that, it is in the package com.threed.jpct, where we can access the JOGLCanvas and it's package-level methods.

package com.threed.jpct;

import java.awt.Canvas;

import javax.media.opengl.GL;

public class Util {
public static void forceDraw(Canvas canvas){
JOGLCanvas jc = (JOGLCanvas)canvas;
jc.display(null);
}

public static void injectExternalGL(Canvas canvas, GL gl){
JOGLCanvas jc = (JOGLCanvas)canvas;
jc.injectExternalGL(gl);
}
}



I'm trying to find the reason, but without jPCT's source, its really painful.

I'll go on until succeed or give up.

PS: The missed classes such as Log and Resource is simple. Log outputs log information and Resource fetches files from a given directory.

iamfoolberg

Well, another bug is:
when the code "this.world.setAmbientLight(255, 255, 255);" is changed to "this.world.setAmbientLight(0, 0, 0);"
the earth will become black.

This means that the light in jPCT affects those in WW.

EgonOlsen


iamfoolberg

Hi, EgonOlsen, let's go on:)
I read an example code in WW.
In the following code, a cube(the same as a primitive box in jPCT) is drawn in WW.


/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/

package gov.nasa.worldwindx.examples.tutorial;

import gov.nasa.worldwind.Configuration;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.pick.PickSupport;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.OGLUtil;
import gov.nasa.worldwindx.examples.ApplicationTemplate;

import javax.media.opengl.*;
import java.awt.*;

/**
* Example of a custom {@link Renderable} that draws a cube at a geographic
* position. This class shows the simplest possible example of a custom
* Renderable, while still following World Wind best practices. See
* http://goworldwind.org/developers-guide/how-to-build-a-custom-renderable/ for
* a complete description of this example.
*
* @author pabercrombie
* @version $Id: Cube.java 691 2012-07-12 19:17:17Z pabercrombie $
*/
public class Cube extends ApplicationTemplate implements Renderable {
/** Geographic position of the cube. */
protected Position position;
/** Length of each face, in meters. */
protected double size;

/** Support object to help with pick resolution. */
protected PickSupport pickSupport = new PickSupport();

// Determined each frame
protected long frameTimestamp = -1L;
protected OrderedCube currentFramesOrderedCube;

/**
* This class holds the Cube's Cartesian coordinates. An instance of it is
* added to the scene controller's ordered renderable queue during picking
* and rendering.
*/
protected class OrderedCube implements OrderedRenderable {
/**
* Cartesian position of the cube, computed from
* {@link gov.nasa.worldwindx.examples.tutorial.Cube#position}.
*/
protected Vec4 placePoint;
/** Distance from the eye point to the cube. */
protected double eyeDistance;
/**
* The cube's Cartesian bounding extent.
*/
protected Extent extent;

public double getDistanceFromEye() {
return this.eyeDistance;
}

public void pick(DrawContext dc, Point pickPoint) {
// Use same code for rendering and picking.
this.render(dc);
}

public void render(DrawContext dc) {
Cube.this.drawOrderedRenderable(dc, Cube.this.pickSupport);
}
}

public Cube(Position position, double sizeInMeters) {
this.position = position;
this.size = sizeInMeters;
}

public void render(DrawContext dc) {
// Render is called twice, once for picking and once for rendering. In
// both cases an OrderedCube is added to
// the ordered renderable queue.

OrderedCube orderedCube = this.makeOrderedRenderable(dc);

if (orderedCube.extent != null) {
if (!this.intersectsFrustum(dc, orderedCube))
return;

// If the shape is less that a pixel in size, don't render it.
if (dc.isSmall(orderedCube.extent, 1))
return;
}

// Add the cube to the ordered renderable queue. The SceneController
// sorts the ordered renderables by eye
// distance, and then renders them back to front.
dc.addOrderedRenderable(orderedCube);
}

/**
* Determines whether the cube intersects the view frustum.
*
* @param dc
*            the current draw context.
*
* @return true if this cube intersects the frustum, otherwise false.
*/
protected boolean intersectsFrustum(DrawContext dc, OrderedCube orderedCube) {
if (dc.isPickingMode())
return dc.getPickFrustums().intersectsAny(orderedCube.extent);

return dc.getView().getFrustumInModelCoordinates()
.intersects(orderedCube.extent);
}

/**
* Compute per-frame attributes, and add the ordered renderable to the
* ordered renderable list.
*
* @param dc
*            Current draw context.
*/
protected OrderedCube makeOrderedRenderable(DrawContext dc) {
// This method is called twice each frame: once during picking and once
// during rendering. We only need to
// compute the placePoint, eye distance and extent once per frame, so
// check the frame timestamp to see if
// this is a new frame. However, we can't use this optimization for 2D
// continuous globes because the
// Cartesian coordinates of the cube are different for each 2D globe
// drawn during the current frame.

if (dc.getFrameTimeStamp() != this.frameTimestamp
|| dc.isContinuous2DGlobe()) {
OrderedCube orderedCube = new OrderedCube();

// Convert the cube's geographic position to a position in Cartesian
// coordinates. If drawing to a 2D
// globe ignore the shape's altitude.
if (dc.is2DGlobe()) {
orderedCube.placePoint = dc.getGlobe()
.computePointFromPosition(this.position.getLatitude(),
this.position.getLongitude(), 0);
} else {
orderedCube.placePoint = dc.getGlobe()
.computePointFromPosition(this.position);
}

// Compute the distance from the eye to the cube's position.
orderedCube.eyeDistance = dc.getView().getEyePoint()
.distanceTo3(orderedCube.placePoint);

// Compute a sphere that encloses the cube. We'll use this sphere
// for intersection calculations to determine
// if the cube is actually visible.
orderedCube.extent = new Sphere(orderedCube.placePoint,
Math.sqrt(3.0) * this.size / 2.0);

// Keep track of the timestamp we used to compute the ordered
// renderable.
this.frameTimestamp = dc.getFrameTimeStamp();
this.currentFramesOrderedCube = orderedCube;

return orderedCube;
} else {
return this.currentFramesOrderedCube;
}
}

/**
* Set up drawing state, and draw the cube. This method is called when the
* cube is rendered in ordered rendering mode.
*
* @param dc
*            Current draw context.
*/
protected void drawOrderedRenderable(DrawContext dc,
PickSupport pickCandidates) {
this.beginDrawing(dc);
try {
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2
// compatibility.
if (dc.isPickingMode()) {
Color pickColor = dc.getUniquePickColor();
pickCandidates.addPickableObject(pickColor.getRGB(), this,
this.position);
gl.glColor3ub((byte) pickColor.getRed(),
(byte) pickColor.getGreen(), (byte) pickColor.getBlue());
}

// Render a unit cube and apply a scaling factor to scale the cube
// to the appropriate size.
gl.glScaled(this.size, this.size, this.size);
this.drawUnitCube(dc);
} finally {
this.endDrawing(dc);
}
}

/**
* Setup drawing state in preparation for drawing the cube. State changed by
* this method must be restored in endDrawing.
*
* @param dc
*            Active draw context.
*/
protected void beginDrawing(DrawContext dc) {
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2
// compatibility.

int attrMask = GL2.GL_CURRENT_BIT | GL2.GL_COLOR_BUFFER_BIT;

gl.glPushAttrib(attrMask);

if (!dc.isPickingMode()) {
dc.beginStandardLighting();
gl.glEnable(GL.GL_BLEND);
OGLUtil.applyBlending(gl, false);

// Were applying a scale transform on the modelview matrix, so the
// normal vectors must be re-normalized
// before lighting is computed.
gl.glEnable(GL2.GL_NORMALIZE);
}

// Multiply the modelview matrix by a surface orientation matrix to set
// up a local coordinate system with the
// origin at the cube's center position, the Y axis pointing North, the
// X axis pointing East, and the Z axis
// normal to the globe.
gl.glMatrixMode(GL2.GL_MODELVIEW);

Matrix matrix = dc.getGlobe().computeSurfaceOrientationAtPosition(
this.position);
matrix = dc.getView().getModelviewMatrix().multiply(matrix);

double[] matrixArray = new double[16];
matrix.toArray(matrixArray, 0, false);
gl.glLoadMatrixd(matrixArray, 0);
}

/**
* Restore drawing state changed in beginDrawing to the default.
*
* @param dc
*            Active draw context.
*/
protected void endDrawing(DrawContext dc) {
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2
// compatibility.

if (!dc.isPickingMode())
dc.endStandardLighting();

gl.glPopAttrib();
}

/**
* Draw a unit cube, using the active modelview matrix to orient the shape.
*
* @param dc
*            Current draw context.
*/
protected void drawUnitCube(DrawContext dc) {
// Vertices of a unit cube, centered on the origin.
float[][] v = { { -0.5f, 0.5f, -0.5f }, { -0.5f, 0.5f, 0.5f },
{ 0.5f, 0.5f, 0.5f }, { 0.5f, 0.5f, -0.5f },
{ -0.5f, -0.5f, 0.5f }, { 0.5f, -0.5f, 0.5f },
{ 0.5f, -0.5f, -0.5f }, { -0.5f, -0.5f, -0.5f } };

// Array to group vertices into faces
int[][] faces = { { 0, 1, 2, 3 }, { 2, 5, 6, 3 }, { 1, 4, 5, 2 },
{ 0, 7, 4, 1 }, { 0, 7, 6, 3 }, { 4, 7, 6, 5 } };

// Normal vectors for each face
float[][] n = { { 0, 1, 0 }, { 1, 0, 0 }, { 0, 0, 1 }, { -1, 0, 0 },
{ 0, 0, -1 }, { 0, -1, 0 } };

// Note: draw the cube in OpenGL immediate mode for simplicity. Real
// applications should use vertex arrays
// or vertex buffer objects to achieve better performance.
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2
// compatibility.
gl.glBegin(GL2.GL_QUADS);
try {
for (int i = 0; i < faces.length; i++) {
gl.glNormal3f(n[i][0], n[i][1], n[i][2]);

for (int j = 0; j < faces[0].length; j++) {
gl.glVertex3f(v[faces[i][j]][0], v[faces[i][j]][1],
v[faces[i][j]][2]);
}
}
} finally {
gl.glEnd();
}
}

protected static class AppFrame extends ApplicationTemplate.AppFrame {
public AppFrame() {
super(true, true, false);

RenderableLayer layer = new RenderableLayer();
Cube cube = new Cube(Position.fromDegrees(35.0, -120.0, 3000), 1000);
layer.addRenderable(cube);

getWwd().getModel().getLayers().add(layer);
}
}

public static void main(String[] args) {
Configuration.setValue(AVKey.INITIAL_LATITUDE, 35.0);
Configuration.setValue(AVKey.INITIAL_LONGITUDE, -120.0);
Configuration.setValue(AVKey.INITIAL_ALTITUDE, 15500);
Configuration.setValue(AVKey.INITIAL_PITCH, 45);
Configuration.setValue(AVKey.INITIAL_HEADING, 45);

ApplicationTemplate.start("World Wind Custom Renderable Tutorial",
AppFrame.class);
}
}


The key steps are:
1. create a WW's renderable layer
RenderableLayer layer = new RenderableLayer();

2. create the cube and put it on the layer
Cube cube = new Cube(Position.fromDegrees(35.0, -120.0, 3000), 1000);
layer.addRenderable(cube);

3. insert the layer into WW's layer list.
getWwd().getModel().getLayers().add(layer);

When the WW want to update the scene, it calls the doRender() method in its layers, here is the definition:

protected abstract void doRender(DrawContext dc);   

the gl and other objects are passed in by the parameter dc.

Then, the RenderableLayer holding the cube will call the cube's drawOrderedRenderable method.
The drawOrderedRenderable method do 3 steps:
1. call beginDrawing() to prepare
2. call drawUnitCube() to draw the cube with gl operations
3. call endDrawing() to clean something

The key is that, in beginDrawing, a local coordinate is prepared:

      // Multiply the modelview matrix by a surface orientation matrix to set
      // up a local coordinate system with the
      // origin at the cube's center position, the Y axis pointing North, the
      // X axis pointing East, and the Z axis
      // normal to the globe.
      gl.glMatrixMode(GL2.GL_MODELVIEW);

      Matrix matrix = dc.getGlobe().computeSurfaceOrientationAtPosition(
            this.position);
      matrix = dc.getView().getModelviewMatrix().multiply(matrix);

      double[] matrixArray = new double[16];
      matrix.toArray(matrixArray, 0, false);
      gl.glLoadMatrixd(matrixArray, 0);

These code lines tell both WW's dc and OpenGL's gl to set a new local coord.
Thus the cube will be drawn and translated to the correct position.

In the former post, i do not make such a translation in the jPCT layer.

How to apply this to jPCT layer?
In the former post, the jPCT's origin point is (0,0,0) at the center of the earth.
Now we have two options: 1) keep the origin at (0, 0, 0), 2)move the origin to the surface of the earth and change the box's coord to the new local coord.
Actually, i think the option 1) do not need a new local coord system, for the local's origin is the same as the old, so no matrix are needed.
Then i tried the option 2).
the matrix is:

Matrix matrix = dc.getGlobe()
            .computeSurfaceOrientationAtPosition(boxPos.latitude, boxPos.longitude, oElv);
            
oElv is a variable, so i can change it in my script Frame.

first, 0, the box is gone.
then, 16000, the original elevation of the box, i see it when the WW's eye point is (0, 20, 16020).
then, 17000, i see the box when the WW's eye point is about (0, 20, 16520).

It seems that, the matrix do change the location of the box, but not the way i thought.

If the matrix has the same effect as in cube demo, the box should be at WW's (0, 40, 16000+16000) or (0, 40, 16000+17000).
The result is that it is at (0, 20, 16000) and (0, 20, 165xx).

Now, i begin to guess, for i have no jPCT code.
1. jPCT draw the Object3ds by gl operations as those in cude demo.
2. jPCT do some coord translations. At lease, jPCT translate visible Object3ds to render them in the camera's vision.
3. when i fore jPCT to draw(in the forceDraw method), jPCT may perform the gl operations directly or indirectly (in a double-buffer and copy the result to the canvas).

According to the cube demo, it will be straightfully for us to apply the translating matrix and gl operations to show objects in WW.
So i wonder, how can i force jPCT to commit the gl operations without any of its internal translations or buffer mechanisms?
That will make things simple and straightful.

EgonOlsen

Quote from: iamfoolberg on September 13, 2015, 07:47:41 AM
According to the cube demo, it will be straightfully for us to apply the translating matrix and gl operations to show objects in WW.
So i wonder, how can i force jPCT to commit the gl operations without any of its internal translations or buffer mechanisms?
That will make things simple and straightful.
The basic design of jPCT's canvas renderes (jogl and lwjgl's AWTGLCanvas) is to queue render commands (on a higher level, not the exact gl calls) and execute them in the corresponding display-method of the choosen canvas/event listener. In your case, this would be GLEventListener's display method:


public void display(GLAutoDrawable tGLAutoDrawable)  { ... }


in JOGLCanvas. jPCT has no control about when this method will get called (or if at all) nor does it care about it. So...maybe there's a way to force jogl into calling that method directly? What's the thread in which ww does it's rendering and how do they ensure that their rendering happens there? I would add some debug output to ww's rendering as well as into an IPaintListener implementation to see the actual order in which the rendering happens. That way, we would be able to see if it's consistent or needs some work.

iamfoolberg

Yes, i think you cache the Object3d rendering and other higher level operations, but they must be explained or compiled as gl operations and committed to the openGL engine(JOGL in this case).

And i get the stacktrace when the "protected void doRender(DrawContext dc)" is called.

Thread [AWT-EventQueue-0] (Suspended (breakpoint at line 205 in Jpct3DLayer))
owns: Object  (id=140)
Jpct3DLayer.doRender(DrawContext) line: 205
Jpct3DLayer(AbstractLayer).render(DrawContext) line: 232
StereoOptionSceneController(AbstractSceneController).draw(DrawContext) line: 864
StereoOptionSceneController.draw(DrawContext) line: 153
StereoOptionSceneController(BasicSceneController).doNormalRepaint(DrawContext) line: 42
StereoOptionSceneController(BasicSceneController).doRepaint(DrawContext) line: 28
StereoOptionSceneController(AbstractSceneController).repaint() line: 381
WorldWindowGLAutoDrawable.doDisplay() line: 472
WorldWindowGLAutoDrawable.display(GLAutoDrawable) line: 343
GLDrawableHelper.displayImpl(GLAutoDrawable) line: 665
GLDrawableHelper.display(GLAutoDrawable) line: 649
GLCanvas$10.run() line: 1289
GLDrawableHelper.invokeGLImpl(GLDrawable, GLContext, Runnable, Runnable) line: 1119
GLDrawableHelper.invokeGL(GLDrawable, GLContext, Runnable, Runnable) line: 994
GLCanvas$11.run() line: 1300
Threading.invoke(boolean, Runnable, Object) line: 193
WorldWindowGLCanvas(GLCanvas).display() line: 541
WorldWindowGLCanvas(GLCanvas).paint(Graphics) line: 595
WorldWindowGLCanvas(GLCanvas).update(Graphics) line: 795
RepaintArea.updateComponent(Component, Graphics) line: 255
RepaintArea.paint(Object, boolean) line: 232
WCanvasPeer(WComponentPeer).handleEvent(AWTEvent) line: 358
WorldWindowGLCanvas(Component).dispatchEventImpl(AWTEvent) line: 4957
WorldWindowGLCanvas(Component).dispatchEvent(AWTEvent) line: 4703
EventQueue.dispatchEventImpl(AWTEvent, Object) line: 758
EventQueue.access$500(EventQueue, AWTEvent, Object) line: 97
EventQueue$3.run() line: 709
EventQueue$3.run() line: 703
AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]
ProtectionDomain$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 75
ProtectionDomain$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext) line: 86
EventQueue$4.run() line: 731
EventQueue$4.run() line: 729
AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]
ProtectionDomain$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 75
EventQueue.dispatchEvent(AWTEvent) line: 728
EventDispatchThread.pumpOneEventForFilters(int) line: 201
EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: 116
EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 105
EventDispatchThread.pumpEvents(int, Conditional) line: 101
EventDispatchThread.pumpEvents(Conditional) line: 93
EventDispatchThread.run() line: 82

The stack trace "EventQueue.dispatchEventImpl(AWTEvent, Object) line: 758   " tells the method is called in AWT EDT.
So we can apply gl and GUI operations.

I think WW has the same mechanism as jPCT in rendering: 1) gl operations are wrapped in some objects such as Layer in WW, 2) update is send to EDT by something like event, 3) EDT calls back to the wrapper layer objects to perform the actual gl operations. Most GUI uses the same mechanism to assure the multi-thread safety.

In my opinion, what we need is to called some method that explains/compiles higher level operations and execute the result gl operations directly.
No camera, no light, nor other extra elements. Only the 3d objects and texture, partical system and animations are needed, that is as simple as in the cube demo.

Would you please give some example code for me to insert the IPaintListener and output the information you want?

EgonOlsen

That interface has two methods. One will be called right before the actual rendering, the other one right after the rendering. Just print out something like "start jPCT" end "end jPCT" and do the same for WW. That way, we can see the actual order in which things happen. BTW: If you want some stuff like lights not to be executed, you already have all the means to do that. because you have these G11, G15...implementations. You can always swallow some of the commands and not pass them to jogl. 

iamfoolberg

Good news is that i tried to integrate the jReality into WorldWind as a layer, Bad news is the method causes error in jPCT.

I downloaded the jReality's source code and compiled them in Eclipse+maven. Although some .jars are missed, it works.
Then, i look into its Viewer and Render(with the same name JOGLRender as jPCT), and comment some camera and projection gl operations, especially those for sky background.

When i write a WW's layer, called jrealityLayer, as JpctLayer, it has the same problem.
I can see the 3d object, but it will be covered by other layers.
Fortunatly, i find the way:
1. comment the camera's gl matrix operations.
2. comment the projection matrix operation
3. comment the background drawing
4. add a coord matrix as that in the former cube demo
5. add a projection matrix before step 4. --this is the key step, i think.
Now, i can see the 3d object as that in the cube demo.

The following is the jrealityLayer and a simple view for the layer.

package org.aoe.det.ww.d3man.ww;

import gov.nasa.worldwind.geom.Matrix;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.geom.Vec4;
import gov.nasa.worldwind.layers.AbstractLayer;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.util.OGLUtil;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;

import org.aoe.det.ww.d3man.D3Man;

import de.jreality.geometry.Primitives;
import de.jreality.jogl4ww.Jogl4WWViewer;
import de.jreality.math.MatrixBuilder;
import de.jreality.scene.Appearance;
import de.jreality.scene.Camera;
import de.jreality.scene.DirectionalLight;
import de.jreality.scene.IndexedFaceSet;
import de.jreality.scene.Light;
import de.jreality.scene.SceneGraphComponent;
import de.jreality.scene.SceneGraphPath;
import de.jreality.shader.Color;
import de.jreality.shader.CommonAttributes;
import de.jreality.tools.RotateTool;

public class JrealityLayer extends AbstractLayer {
public Jogl4WWViewer viewer;
public SceneGraphComponent rootNode;
public SceneGraphComponent cameraNode;
public SceneGraphComponent lightNode;
public SceneGraphComponent geometryNode;

public JrealityLayer() {
// do nothing
}

// puts opengl in the correct state for this layer
protected void beginDraw(DrawContext dc) {
GL2 gl = (GL2) dc.getGL();

// gl.glPushAttrib(GL2.GL_TEXTURE_BIT | GL2.GL_ENABLE_BIT
// | GL2.GL_CURRENT_BIT | GL2.GL_TRANSFORM_BIT);
gl.glPushAttrib(GL2.GL_ALL_ATTRIB_BITS);

// if (!dc.isPickingMode()) {
// gl.glEnable(GL.GL_BLEND);
// gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
// }

if (!dc.isPickingMode()) {
dc.beginStandardLighting();
gl.glEnable(GL.GL_BLEND);
OGLUtil.applyBlending(gl, false);

// Were applying a scale transform on the modelview matrix, so the
// normal vectors must be re-normalized
// before lighting is computed.
gl.glEnable(GL2.GL_NORMALIZE);
}

gl.glMatrixMode(GL2.GL_MODELVIEW);// |GL2.GL_PROJECTION|GL2.GL_TEXTURE);
gl.glPushMatrix();
gl.glLoadIdentity();
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glPushMatrix();
gl.glLoadIdentity();
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glPushMatrix();
gl.glLoadIdentity();

gl.glMatrixPushEXT(GL2.GL_MODELVIEW);
gl.glMatrixPushEXT(GL2.GL_PROJECTION);
gl.glMatrixPushEXT(GL2.GL_TEXTURE);

// Multiply the modelview matrix by a surface orientation matrix to set
// up a local coordinate system with the
// origin at the cube's center position, the Y axis pointing North, the
// X axis pointing East, and the Z axis
// normal to the globe.

// the local coord O--> WW's (0-latitude, 20-longitude,
// 1000-metersElevation)
Position pos = Position.fromDegrees(0, 20);
double[] matrixArray;
Matrix posMatrix, matrix;

posMatrix = dc.getGlobe().computeSurfaceOrientationAtPosition(// boxPos);
pos.latitude, pos.longitude, -D3Man.surfaceElevation);//

matrix = dc.getView().getProjectionMatrix().multiply(posMatrix);
matrixArray = new double[16];
matrix.toArray(matrixArray, 0, false);
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glLoadMatrixd(matrixArray, 0);

matrix = dc.getView().getModelviewMatrix().multiply(posMatrix);
matrixArray = new double[16];
matrix.toArray(matrixArray, 0, false);
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glLoadMatrixd(matrixArray, 0);
}

/**
* Called by WorldWind to refresh this layer.
*/
@Override
protected void doRender(DrawContext dc) {
this.beginDraw(dc);
GL2 gl = dc.getGL().getGL2();
if (this.viewer == null) {
rootNode = new SceneGraphComponent();
cameraNode = new SceneGraphComponent();
geometryNode = new SceneGraphComponent();
lightNode = new SceneGraphComponent();

rootNode.addChild(geometryNode);
rootNode.addChild(cameraNode);
cameraNode.addChild(lightNode);

Light dl = new DirectionalLight();
lightNode.setLight(dl);

Camera camera = new Camera();
cameraNode.setCamera(camera);

IndexedFaceSet ifs = Primitives.icosahedron();
geometryNode.setGeometry(ifs);

RotateTool rotateTool = new RotateTool();
geometryNode.addTool(rotateTool);

MatrixBuilder.euclidean().translate(0, 0, 1).assignTo(cameraNode);
MatrixBuilder.euclidean().translate(0, 0, 0).assignTo(geometryNode);
// MatrixBuilder.euclidean().scale(20.0).assignTo(geometryNode);
// MatrixBuilder.euclidean().rotateZ(20.0).assignTo(geometryNode);

Appearance rootApp = new Appearance();
// rootApp.setAttribute(CommonAttributes.BACKGROUND_COLOR,
// new Color(0f, 1.0f, .1f));
rootApp.setAttribute(CommonAttributes.DIFFUSE_COLOR,
new Color(1f, 1f, 1f));
rootNode.setAppearance(rootApp);

SceneGraphPath camPath = new SceneGraphPath();
camPath.push(rootNode);
camPath.push(cameraNode);
camPath.push(camera);

viewer = new Jogl4WWViewer();
viewer.initialize(gl, camPath, rootNode);
}
viewer.reInitGL(gl, 0, 0, dc.getDrawableWidth(),
dc.getDrawableHeight());

//set camera
// Vec4 eyeLoc = dc.getView().getEyePoint();
// MatrixBuilder.euclidean().translate(eyeLoc.x, eyeLoc.y, eyeLoc.z).assignTo(cameraNode);

viewer.render();
this.endDraw(dc);
}

// resets opengl state
protected void endDraw(DrawContext dc) {
GL2 gl = (GL2) dc.getGL();

gl.glMatrixPopEXT(GL2.GL_TEXTURE);
gl.glMatrixPopEXT(GL2.GL_PROJECTION);
gl.glMatrixPopEXT(GL2.GL_MODELVIEW);

gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glPopMatrix();
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glPopMatrix();
gl.glMatrixMode(GL2.GL_MODELVIEW);// |GL2.GL_PROJECTION|GL2.GL_TEXTURE);
gl.glPopMatrix();

if (!dc.isPickingMode()) {
dc.endStandardLighting();
}
gl.glPopAttrib();
}
}
/**
*
* This file is part of jReality. jReality is open source software, made
* available under a BSD license:
*
* Copyright (c) 2003-2006, jReality Group: Charles Gunn, Tim Hoffmann, Markus
* Schmies, Steffen Weissmann.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
*   list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
*   this list of conditions and the following disclaimer in the documentation
*   and/or other materials provided with the distribution.
*
* - Neither the name of jReality nor the names of its contributors nor the
*   names of their associated organizations may be used to endorse or promote
*   products derived from this software without specific prior written
*   permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/

package de.jreality.jogl4ww;

import java.awt.Dimension;
import java.util.logging.Level;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawableFactory;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.opengl.GLCanvas;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

import de.jreality.jogl.JOGLConfiguration;
import de.jreality.jogl.JOGLRenderer;
import de.jreality.scene.SceneGraphComponent;
import de.jreality.scene.SceneGraphPath;
import de.jreality.util.SceneGraphUtility;

/**
* a viewer for NASA WorldWind, with injected GL2 object.
*
* @author berg
*
*/
public class Jogl4WWViewer implements de.jreality.scene.Viewer {

private SceneGraphComponent sceneRoot;
private SceneGraphComponent auxiliaryRoot;
private SceneGraphPath cameraPath;

private JOGLRenderer renderer;

public static final int CROSS_EYED_STEREO = 0;
public static final int RED_BLUE_STEREO = 1;
public static final int RED_GREEN_STEREO = 2;
public static final int RED_CYAN_STEREO = 3;
public static final int HARDWARE_BUFFER_STEREO = 4;
public static final int STEREO_TYPES = 5;
int stereoType = CROSS_EYED_STEREO;

private GL2 gl;
private int metric;

public Jogl4WWViewer() {
super();
}
/**
* initialize this viewer with given params.
* @param gl
* @param camPath
* @param root
*/
public void initialize(GL2 gl, SceneGraphPath camPath, SceneGraphComponent root) {
this.gl = gl;
setAuxiliaryRoot(SceneGraphUtility
.createFullSceneGraphComponent("AuxiliaryRoot"));

this.setSceneRoot(root);
this.setCameraPath(camPath);
this.renderer = new JOGLRenderer(this);
this.renderer.init(this.gl);
}

public SceneGraphComponent getSceneRoot() {
return sceneRoot;
}

public void setSceneRoot(SceneGraphComponent r) {
if (r == null) {
JOGLConfiguration.getLogger().log(Level.WARNING,
"Null scene root, not setting.");
return;
}
sceneRoot = r;
}

public SceneGraphComponent getAuxiliaryRoot() {
return auxiliaryRoot;
}

public void setAuxiliaryRoot(SceneGraphComponent auxiliaryRoot) {
this.auxiliaryRoot = auxiliaryRoot;
if (renderer != null)
renderer.setAuxiliaryRoot(auxiliaryRoot);
}

public SceneGraphPath getCameraPath() {
return cameraPath;
}

public void setCameraPath(SceneGraphPath p) {
cameraPath = p;
}

public void renderAsync() {
this.render();
}

public boolean hasViewingComponent() {
return false;
}

public Object getViewingComponent() {
return this.gl;
}


public int getMetric() {
return metric;
}

public void setMetric(int metric) {
this.metric = metric;
SceneGraphUtility.setMetric(sceneRoot, metric);

}

/*********** Non-standard set/get ******************/

public void setStereoType(int type) {
renderer.setStereoType(type);
}

// used in JOGLRenderer
public int getStereoType() {
return renderer.getStereoType();
}

// public boolean isFlipped() {
// return renderer.isFlipped();
// }
// public void setFlipped(boolean isFlipped) {
// renderer.setFlipped(isFlipped);
// }
//
public JOGLRenderer getRenderer() {
return renderer;
}

/****** Convenience methods ************/
public void addAuxiliaryComponent(SceneGraphComponent aux) {
if (auxiliaryRoot == null) {
setAuxiliaryRoot(SceneGraphUtility
.createFullSceneGraphComponent("AuxiliaryRoot"));
}
if (!auxiliaryRoot.isDirectAncestor(aux))
auxiliaryRoot.addChild(aux);
}

public void removeAuxiliaryComponent(SceneGraphComponent aux) {
if (auxiliaryRoot == null)
return;
if (!auxiliaryRoot.isDirectAncestor(aux))
return;
auxiliaryRoot.removeChild(aux);
}

private boolean pendingUpdate;

private final Object renderLock = new Object();
boolean autoSwapBuffers = true;

public boolean isRendering() {
synchronized (renderLock) {
return pendingUpdate;
}
}

boolean init = true;

int rot = 0;

private int gl_x;
private int gl_y;
private int gl_right;
private int gl_top;
/**
* re-initialized when the gl or viewport is changed.
* @param gl
* @param x
* @param y
* @param right
* @param top
*/
public void reInitGL(GL2 gl, int x, int y, int right,
int top) {
if (this.gl != gl){
this.renderer.init(gl);
}
if (gl_x != x || gl_y != y
|| gl_right != right || gl_top != top) {
this.gl_x = x;
this.gl_y = y;
this.gl_right = right;
this.gl_top = top;
this.renderer.reshape(this.gl, x, y, right, top);
}
}

public double getAspectRatio() {
return renderer.getAspectRatio();
}

public Dimension getViewingComponentSize() {
return new Dimension(gl_right-gl_x, gl_top-gl_y);
}

public boolean canRenderAsync() {
return true;
}

public void render() {
renderer.display(this.gl);
}
}



the key step is :

Position pos = Position.fromDegrees(0, 20);
double[] matrixArray;
Matrix posMatrix, matrix;

posMatrix = dc.getGlobe().computeSurfaceOrientationAtPosition(// boxPos);
pos.latitude, pos.longitude, -D3Man.surfaceElevation);//

matrix = dc.getView().getProjectionMatrix().multiply(posMatrix);
matrixArray = new double[16];
matrix.toArray(matrixArray, 0, false);
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glLoadMatrixd(matrixArray, 0);

matrix = dc.getView().getModelviewMatrix().multiply(posMatrix);
matrixArray = new double[16];
matrix.toArray(matrixArray, 0, false);
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glLoadMatrixd(matrixArray, 0);

I set the local coord as in cube demo, and adjust the projection/modelview matrix.
The source code JOGLRenderer.java is a bit long, so i list the commented codes:

public void render() {
if (disposed)
return;
Texture2DLoaderJOGL.clearAnimatedTextureTable(globalGL);
if (thePeerRoot == null || theViewer.getSceneRoot() != thePeerRoot
.getOriginalComponent()) {
setSceneRoot(theViewer.getSceneRoot());
thePeerRoot = ConstructPeerGraphVisitor
.constructPeerForSceneGraphComponent(theRoot, null, this);
}
if (auxiliaryRoot != null && thePeerAuxilliaryRoot == null)
thePeerAuxilliaryRoot = ConstructPeerGraphVisitor
.constructPeerForSceneGraphComponent(auxiliaryRoot, null,
this);

renderingState.oneTexture2DPerImage = topAp.isOneTexture2DPerImage();
renderingState.currentPath.clear();
renderingState.context = new Graphics3D(getCameraPath(),
renderingState.currentPath,
CameraUtility.getAspectRatio(theViewer));
//FIXME berg, no reset and background
// globalGL.glMatrixMode(GL2.GL_PROJECTION);
// globalGL.glLoadIdentity();//重置当前指定的矩阵为单位矩阵.
// JOGLRendererHelper.handleBackground(this, width, height,
// theRoot.getAppearance());

frontBanana = true;
renderOnePass();
if (topAp.isRenderSpherical()) {
frontBanana = false;
renderOnePass();
}
if (topAp.isForceResidentTextures())
forceResidentTextures();

}

private void renderOnePass() {
if (theCamera == null)
return;
// double aspectRatio = getAspectRatio();
// System.err.println("aspect ratio = "+aspectRatio);
// for pick mode the aspect ratio has to be set to that of the viewer
// component
//FIXME berg, no reset
// globalGL.glMatrixMode(GL2.GL_PROJECTION);
// globalGL.glLoadIdentity();
// if (topAp.isRenderSpherical()) {
// //把m指定的16个值作为一个矩阵,与当前矩阵相乘,并把结果存储在当前矩阵中,按行主序
// globalGL.glMultTransposeMatrixd(
// frontBanana ? frontZBuffer : backZBuffer, 0);
// // System.err.println("c2ndc = "+Rn.matrixToString(
// // Rn.times(null, frontBanana ? frontZBuffer : backZBuffer,
// // c2ndc)));
// }
// Rectangle2D viewPort = CameraUtility.getViewport(theCamera,
// aspectRatio);
// System.err.println("Camera viewport = "+viewPort.toString());
//FIXME berg, no camera tranform
// double[] c2ndc = CameraUtility.getCameraToNDC(theCamera,
// getAspectRatio(), whichEye);
// // System.err.println("C2ndc = "+Rn.matrixToString(c2ndc));
// globalGL.glMultTransposeMatrixd(c2ndc, 0);

//FIXME berg, no reset
// prepare for rendering the geometry
// globalGL.glMatrixMode(GL2.GL_MODELVIEW);
// globalGL.glLoadIdentity();

// renderingState.cameraToWorld = renderingState.context
// .getCameraToWorld();
// renderingState.worldToCamera = Rn.inverse(null,
// renderingState.cameraToWorld);
//FIXME berg, no reset
// renderingState.cameraToNDC = c2ndc;
// globalGL.glMultTransposeMatrixd(renderingState.worldToCamera, 0);
// if (topAp.getSkyboxCubemap() != null)
// JOGLSkyBox.render(globalGL, renderingState.worldToCamera,
// topAp.getSkyboxCubemap(),
// CameraUtility.getCamera(theViewer));

processLights();

processClippingPlanes();

rhStack.clear();
rhStack.push(RenderingHintsInfo.defaultRHInfo);
RenderingHintsInfo.defaultRHInfo.render(renderingState, null);
renderingState.flipNormals = (Rn
.determinant(renderingState.worldToCamera) < 0.0);
globalGL.glFrontFace(renderingState.flipNormals ? GL.GL_CW : GL.GL_CCW);

texResident = true;
renderPeerRoot();
if (thePeerAuxilliaryRoot != null)
thePeerAuxilliaryRoot.render();
if (topAp.isRenderSpherical() && !frontBanana)
globalGL.glPopMatrix();
//FIXME berg, no reset
// globalGL.glLoadIdentity();
}

The listed two method do the drawing works.
Then, i tried to use the same key step in jPCTLayer, but failed.
The output is:

Loading Texture...from InputStream
Loading file from InputStream
File from InputStream loaded...21824 bytes
Processing new material PolyShip_UnWrap!
Texture named POLYSHBP.JPG added to TextureManager!
Processing new material  -- default --!
Processing object from 3DS-file: SpaceFight
Object 'SpaceFight_jPCT0' created using 500 polygons and 259 vertices.
1DEBUG[[org.aoe.det.rsshell.ScriptShell.getScriptEngine()]: Use language beanshellx
[ Wed Sep 16 08:50:32 CST 2015 ] - WARNING: There's a problem with the object list not being consistent during rendering. This is often caused by concurrent modification of jPCT objects on a thread different from the rendering thread!
[ Wed Sep 16 08:50:32 CST 2015 ] - ERROR: null
九月 16, 2015 8:50:32 上午 gov.nasa.worldwind.AbstractSceneController draw
严重: Exception while rendering layer org.aoe.det.ww.d3man.ww.Jpct3DLayer
java.lang.NullPointerException
at com.threed.jpct.World.draw(World.java:2091)
at com.threed.jpct.World.draw(World.java:2073)
at com.threed.jpct.World.draw(World.java:1613)
at org.aoe.det.ww.d3man.ww.Jpct3DLayer.repaint(Jpct3DLayer.java:352)
at org.aoe.det.ww.d3man.ww.Jpct3DLayer.doRender(Jpct3DLayer.java:204)
at gov.nasa.worldwind.layers.AbstractLayer.render(AbstractLayer.java:232)
at gov.nasa.worldwind.AbstractSceneController.draw(AbstractSceneController.java:864)
at gov.nasa.worldwind.StereoOptionSceneController.draw(StereoOptionSceneController.java:153)
at gov.nasa.worldwind.BasicSceneController.doNormalRepaint(BasicSceneController.java:42)
at gov.nasa.worldwind.BasicSceneController.doRepaint(BasicSceneController.java:28)
at gov.nasa.worldwind.AbstractSceneController.repaint(AbstractSceneController.java:381)
at gov.nasa.worldwind.WorldWindowGLAutoDrawable.doDisplay(WorldWindowGLAutoDrawable.java:472)
at gov.nasa.worldwind.WorldWindowGLAutoDrawable.display(WorldWindowGLAutoDrawable.java:343)
at jogamp.opengl.GLDrawableHelper.displayImpl(GLDrawableHelper.java:665)
at jogamp.opengl.GLDrawableHelper.display(GLDrawableHelper.java:649)
at javax.media.opengl.awt.GLCanvas$10.run(GLCanvas.java:1289)
at jogamp.opengl.GLDrawableHelper.invokeGLImpl(GLDrawableHelper.java:1119)
at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:994)
at javax.media.opengl.awt.GLCanvas$11.run(GLCanvas.java:1300)
at javax.media.opengl.Threading.invoke(Threading.java:193)
at javax.media.opengl.awt.GLCanvas.display(GLCanvas.java:541)
at javax.media.opengl.awt.GLCanvas.paint(GLCanvas.java:595)
at sun.awt.RepaintArea.paintComponent(RepaintArea.java:264)
at sun.awt.RepaintArea.paint(RepaintArea.java:240)
at sun.awt.windows.WComponentPeer.handleEvent(WComponentPeer.java:358)
at java.awt.Component.dispatchEventImpl(Component.java:4957)
at java.awt.Component.dispatchEvent(Component.java:4703)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

[ Wed Sep 16 08:50:32 CST 2015 ] - WARNING: There's a problem with the object list not being consistent during rendering. This is often caused by concurrent modification of jPCT objects on a thread different from the rendering thread!
[ Wed Sep 16 08:50:32 CST 2015 ] - ERROR: null
九月 16, 2015 8:50:32 上午 gov.nasa.worldwind.AbstractSceneController draw
严重: Exception while rendering layer org.aoe.det.ww.d3man.ww.Jpct3DLayer
java.lang.NullPointerException
at com.threed.jpct.World.draw(World.java:2091)
at com.threed.jpct.World.draw(World.java:2073)
at com.threed.jpct.World.draw(World.java:1613)
at org.aoe.det.ww.d3man.ww.Jpct3DLayer.repaint(Jpct3DLayer.java:352)
at org.aoe.det.ww.d3man.ww.Jpct3DLayer.doRender(Jpct3DLayer.java:204)
at gov.nasa.worldwind.layers.AbstractLayer.render(AbstractLayer.java:232)
at gov.nasa.worldwind.AbstractSceneController.draw(AbstractSceneController.java:864)
at gov.nasa.worldwind.StereoOptionSceneController.draw(StereoOptionSceneController.java:153)
at gov.nasa.worldwind.BasicSceneController.doNormalRepaint(BasicSceneController.java:42)
at gov.nasa.worldwind.BasicSceneController.doRepaint(BasicSceneController.java:28)
at gov.nasa.worldwind.AbstractSceneController.repaint(AbstractSceneController.java:381)
at gov.nasa.worldwind.WorldWindowGLAutoDrawable.doDisplay(WorldWindowGLAutoDrawable.java:472)
at gov.nasa.worldwind.WorldWindowGLAutoDrawable.display(WorldWindowGLAutoDrawable.java:343)
at jogamp.opengl.GLDrawableHelper.displayImpl(GLDrawableHelper.java:665)
at jogamp.opengl.GLDrawableHelper.display(GLDrawableHelper.java:649)
at javax.media.opengl.awt.GLCanvas$10.run(GLCanvas.java:1289)
at jogamp.opengl.GLDrawableHelper.invokeGLImpl(GLDrawableHelper.java:1119)
at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:994)
at javax.media.opengl.awt.GLCanvas$11.run(GLCanvas.java:1300)
at javax.media.opengl.Threading.invoke(Threading.java:193)
at javax.media.opengl.awt.GLCanvas.display(GLCanvas.java:541)
at javax.media.opengl.awt.GLCanvas.paint(GLCanvas.java:595)
at javax.media.opengl.awt.GLCanvas.update(GLCanvas.java:795)
at sun.awt.RepaintArea.updateComponent(RepaintArea.java:255)
at sun.awt.RepaintArea.paint(RepaintArea.java:232)
at sun.awt.windows.WComponentPeer.handleEvent(WComponentPeer.java:358)
at java.awt.Component.dispatchEventImpl(Component.java:4957)
at java.awt.Component.dispatchEvent(Component.java:4703)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

[ Wed Sep 16 08:50:33 CST 2015 ] - WARNING: There's a problem with the object list not being consistent during rendering. This is often caused by concurrent modification of jPCT objects on a thread different from the rendering thread!
[ Wed Sep 16 08:50:33 CST 2015 ] - ERROR: null


Without source code, i do not know what is going on and what goes wrong.
I can only guess that something is not initialized or entitled a wrong value.

EgonOlsen

Have you checked that this warning actually isn't right about the cause:


WARNING: There's a problem with the object list not being consistent during rendering. This is often caused by concurrent modification of jPCT objects on a thread different from the rendering thread!


It might still be something else, but in 99.9% of all cases this is the problem when people are getting a null pointer during rendering...hence the log output. In your case, the "rendering thread" is the thread in which you call draw(), not the physical rendering thread of JOGL.