Worldwind PointPlacemark Pitch
Asked Answered
C

1

6

I'm trying to figure out why the setPitch in the PointPlacemarkAttributes does not seem to work correctly.

I believe this JOGL code in PointPlacemark.java is where things are going wrong:

        Double heading = getActiveAttributes().getHeading();
        Double pitch = getActiveAttributes().getPitch();

        // Adjust heading to be relative to globe or screen
        if (heading != null)
        {
            if (AVKey.RELATIVE_TO_GLOBE.equals(this.getActiveAttributes().getHeadingReference()))
                heading = dc.getView().getHeading().degrees - heading;
            else
                heading = -heading;
        }

        // Apply the heading and pitch if specified.
        if (heading != null || pitch != null)
        {
            gl.glTranslated(xscale / 2, yscale / 2, 0);
            if (pitch != null)
                gl.glRotated(pitch, 1, 0, 0);
            if (heading != null)
                gl.glRotated(heading, 0, 0, 1);
            gl.glTranslated(-xscale / 2, -yscale / 2, 0);
        }

        // Scale the unit quad
        gl.glScaled(xscale, yscale, 1);

Here is a simple driver I've been using to play with it:

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

            final RenderableLayer layer = new RenderableLayer();

            PointPlacemark pp = new PointPlacemark(Position.fromDegrees(28, -102, 30000));
            pp.setLabelText("PointPlacemark");
            pp.setLineEnabled(false);
            pp.setAltitudeMode(WorldWind.ABSOLUTE);
            PointPlacemarkAttributes attrs = new PointPlacemarkAttributes();
            attrs.setImageAddress("gov/nasa/worldwindx/examples/images/georss.png");
            attrs.setScale(1.0);
            attrs.setImageOffset(Offset.CENTER);


            attrs.setPitch(45.0);

            pp.setAttributes(attrs);
            layer.addRenderable(pp);

            // Add the layer to the model.
            insertBeforeCompass(getWwd(), layer);
        }
    }

    public static void main(String[] args) {
        ApplicationTemplate.start("WorldWind Placemarks", AppFrame.class);
    }
}

If I set no pitch, it looks fine:

enter image description here

But when I set a pitch of 45 degrees it looks like this:

enter image description here

Which I'm not understanding how it correlates to the value I set. I'd expect it to work like the Compass does in the CompassLayer:

enter image description here

Update

Comment suggested to iterate through pitch values to see how it works. I did that and I'm still not seeing how it is supposed to work. It looks like it is just "cropping" the image horizontally, and not doing anything else. Here is some code:

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

            final RenderableLayer layer = new RenderableLayer();

            PointPlacemark pp = new PointPlacemark(Position.fromDegrees(28, -102, 30000));
            pp.setLabelText("PointPlacemark");
            pp.setLineEnabled(false);
            pp.setAltitudeMode(WorldWind.ABSOLUTE);
            PointPlacemarkAttributes attrs = new PointPlacemarkAttributes();
            attrs.setImageAddress("gov/nasa/worldwindx/examples/images/georss.png");
            attrs.setScale(1.0);
            attrs.setImageOffset(Offset.CENTER);


            pp.setAttributes(attrs);
            layer.addRenderable(pp);

            // Add the layer to the model.
            insertBeforeCompass(getWwd(), layer);

            Thread t = new Thread(new Runnable() {

                @Override
                public void run() {
                    for(double i = 0.0; i<360; i+=.1) {
                        attrs.setPitch(i);


                        System.out.println("Pitch is now "+i);

                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }

                        AppFrame.this.getWwd().redrawNow();
                    }

                }
            });
            t.start();
        }
    }

    public static void main(String[] args) {
        ApplicationTemplate.start("WorldWind Placemarks", AppFrame.class);
    }
}

And a screen recorded GIF:

enter image description here

Commanding answered 3/4, 2018 at 19:33 Comment(4)
Seems pitch is a rotation about X-axis, in the direction of a "parallel" (Y-axis goes as a "meridian"). heading rotates around Z-axis, perpendicular to a tangent plane. Set different, small steps increasing, values of pitch and heading to see how they work.Precocious
Great idea -- I added some code to my question above that does this. The Heading works as I'd expect, and I can see it rotate about the Z-axis. The pitch does not seem to rotate about the Y-axis though. To me, it looks like it is just cropping the image.Commanding
By the way, I noticed that the repo that you pointed to code from is an old deprecated one; look at the open issues to see what I mean. github.com/NASAWorldWind/WorldWindJava Seems to be the active one, and indeed there was a bug report on this issue, maybe even by @systemoutprintln...Acea
Ah. Ok -- I fixed the link. Yep, that's me :-).Commanding
A
3

The problem is that in PointPlacemark.doDrawOrderedRenderable(), the orthographic projection matrix used uses a range of depth values from -1 to 1.

When the pitch remains at 0, the z coordinates also remain at 0, safely in the middle of this range (actually, there is some slight fudging of this coordinate in WorldWind, but never mind that). As it pitches, of course the z coordinates change, until at 90° all of the y coordinates are 0 while z will go to half of the height of the image. This is why only a slice of the image that falls within the range -1,1 is visible while the rest is clipped.

That z range is defined by the following code:

// The image is drawn using a parallel projection.
osh.pushProjectionIdentity(gl);
gl.glOrtho(0d, dc.getView().getViewport().width, 0d, dc.getView().getViewport().height, -1d, 1d);

If we examine the equivalent code in CompassLayer, we can see that here they do factor in the scaled icon size (although the comment suggests that perhaps at some earlier iteration, less care had been taken over the z dimension):

double width = this.getScaledIconWidth();
double height = this.getScaledIconHeight();

// Load a parallel projection with xy dimensions (viewportWidth, viewportHeight)
// into the GL projection matrix.
java.awt.Rectangle viewport = dc.getView().getViewport();
ogsh.pushProjectionIdentity(gl);
double maxwh = width > height ? width : height;
if (maxwh == 0)
    maxwh = 1;
gl.glOrtho(0d, viewport.width, 0d, viewport.height, -0.6 * maxwh, 0.6 * maxwh);

In this case, the arguments for z (±0.6 * maxwh) use 0.6 presumably as 0.5 plus some margin. The actual geometry is a unit quad, which is translated by half width/height in x/y, scaled and rotated accordingly.

For PointPlacemark, we can account for the size of the renderable in a similar way. Rearranging the code slightly so that scale computation happens before setting the projection, and adding a maxwh value:

// Compute the scale
double xscale;
Double scale = this.getActiveAttributes().getScale();
if (scale != null)
    xscale = scale * this.activeTexture.getWidth(dc);
else
    xscale = this.activeTexture.getWidth(dc);

double yscale;
if (scale != null)
    yscale = scale * this.activeTexture.getHeight(dc);
else
    yscale = this.activeTexture.getHeight(dc);
double maxwh = Math.max(xscale, yscale);

// The image is drawn using a parallel projection.
osh.pushProjectionIdentity(gl);
gl.glOrtho(0d, dc.getView().getViewport().width, 0d, dc.getView().getViewport().height, -0.6 * maxwh, 0.6 * maxwh);

Again, 0.6 allows some margin.

It would probably be perfectly fine to have hardcoded values for the z range, as long as they were large enough for any image we might want to draw but not so large that numerical precision became an issue. Conversely, one could go even further and factor in trig to work out the actual depth needed for a given rotation and image size, but there would not be much to gain by doing so.

This was indeed a bug with WorldWindJava that has been reported, along with a link here for the fix.

Acea answered 10/4, 2018 at 14:28 Comment(8)
Wow that is awesome. I think I've played with everything in this file, except for those in an attempt to figure this out. For the -100 to 100 range, can you help me understand how that would relate to the actual size of the renderable I'm using? Thank you!!Commanding
Or what they are representingCommanding
The numbers relate to which coordinates in the modelview matrix will be projected into the view. So x and y go from 0 to width / height respectively, while z was defined as going from -1 to 1. Without rotation from pitch, the coordinates all had z=0. As it rotates in x, z escaped the range -1,1 so that only part was visible. Debugging around the last line of code you mentioned as being suspect, I see that xscale and yscale are 38.4 (this might be different with the different icon loaded) - this will be the value z can go to.Acea
In fact, if you look at the equivalent code in CompassLayer, you'll see that they use ±0.6 * maxwh where maxwh is the larger of getScaledIconWidth\Height(). I believe the 0.6 is 0.5 + breathing room. Actually, perhaps in that case the image itself doesn't extend to the edges as even 0.4 seems ok... Anyway, I'll edit my answer to include a more proper solution.Acea
Thanks, that helps a lot. It looks like you already found the github issue I submitted a couple months ago. github.com/NASAWorldWind/WorldWindJava/issues/… I guess we'll see if we get a response from the WorldWind team. I'll take a look at your code and then give you the bounty soon.Commanding
Yay, fake internet points! Cheers @systemoutprintln, good luck with your endeavours.Acea
@Acea if you don't like the fake internet points, you can always give them to me! :P Well deserved though. In my attempt to solve the problem I couldn't fix that glitch.Porphyry
Some familiarity with OpenGL / 3d geometry definitely helped here @SteliosAdamantidis, I hope my answer is clear enough that you understand the issue now, and how it fits in with the wider 3d graphics context... fairly unlikely that you encounter quite the same problem again, but the knowledge could definitely apply elsewhere.Acea

© 2022 - 2024 — McMap. All rights reserved.