Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ protected void onPostExecute(Void notUsed) {
Placemark drone = new Placemark(
Position.fromDegrees(32.4520, 63.44553, 3000),
MilStd2525.getPlacemarkAttributes("SFAPMFQM--GIUSA", modifiers, null));
drone.getAttributes().setDrawLeader(true);

symbolLayer.addRenderable(drone);

Expand All @@ -96,6 +97,7 @@ protected void onPostExecute(Void notUsed) {
Placemark launcher = new Placemark(
Position.fromDegrees(32.4014, 63.3894, 0),
MilStd2525.getPlacemarkAttributes("SHGXUCFRMS----G", modifiers, null));
launcher.setAltitudeMode(WorldWind.CLAMP_TO_GROUND);

symbolLayer.addRenderable(launcher);

Expand All @@ -109,6 +111,7 @@ protected void onPostExecute(Void notUsed) {
Placemark machineGun = new Placemark(
Position.fromDegrees(32.3902, 63.4161, 0),
MilStd2525.getPlacemarkAttributes("SFGPEWRH--MTUSG", modifiers, null));
machineGun.setAltitudeMode(WorldWind.CLAMP_TO_GROUND);

symbolLayer.addRenderable(machineGun);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ public Bitmap createBitmap() {
Rect bounds = imageInfo.getImageBounds(); // The extents of the image, including text modifiers
this.placemarkOffset = new Offset(
WorldWind.OFFSET_PIXELS, centerPoint.x, // x offset
WorldWind.OFFSET_PIXELS, bounds.height() - centerPoint.y); // y offset converted to lower-left origin
// Use billboarding or lollipopping to prevent icon clipping by terrain as described in MIL-STD-2525C APPENDIX F.5.1.1.2
WorldWind.OFFSET_PIXELS, 0/*bounds.height() - centerPoint.y*/); // y offset converted to lower-left origin

// Apply the placemark offset to the attributes on the main thread. This is necessary to synchronize write
// access to placemarkAttributes from the thread that invokes this BitmapFactory and read access from the
Expand Down
57 changes: 33 additions & 24 deletions worldwind/src/main/java/gov/nasa/worldwind/shape/Placemark.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public interface LevelOfDetailSelector {
*/
protected static final double DEFAULT_EYE_DISTANCE_SCALING_THRESHOLD = 1e6;

protected static final double DEFAULT_DEPTH_OFFSET = -0.1;
protected static final double DEFAULT_DEPTH_OFFSET = -0.03;

private static Vec3 placePoint = new Vec3();

Expand Down Expand Up @@ -684,8 +684,9 @@ protected void doRender(RenderContext rc) {

// Compute a screen depth offset appropriate for the current viewing parameters.
double depthOffset = 0;
if (this.cameraDistance < rc.horizonDistance) {
depthOffset = DEFAULT_DEPTH_OFFSET;
double absTilt = Math.abs( rc.camera.tilt );
if (this.cameraDistance < rc.horizonDistance && absTilt <= 90) {
depthOffset = ( 1 - absTilt / 90 ) * DEFAULT_DEPTH_OFFSET;
}

// Project the placemark's model point to screen coordinates, using the screen depth offset to push the screen
Expand Down Expand Up @@ -808,48 +809,56 @@ protected void determineActiveTexture(RenderContext rc) {
// offset is defined with its origin at the image's bottom-left corner and axes that extend up and to the right
// from the origin point. When the placemark has no active texture the image scale defines the image size and no
// other scaling is applied.
double offsetX, offsetY, scaleX, scaleY;
if (this.activeTexture != null) {
int w = this.activeTexture.getWidth();
int h = this.activeTexture.getHeight();
double s = this.activeAttributes.imageScale * visibilityScale;
this.activeAttributes.imageOffset.offsetForSize(w, h, offset);

unitSquareTransform.multiplyByTranslation(
screenPlacePoint.x - offset.x * s,
screenPlacePoint.y - offset.y * s,
screenPlacePoint.z);

unitSquareTransform.multiplyByScale(w * s, h * s, 1);
offsetX = offset.x * s;
offsetY = offset.y * s;
scaleX = w * s;
scaleY = h * s;
} else {
// This branch serves both non-textured attributes and also textures that haven't been loaded yet.
// We set the size for non-loaded textures to the typical size of a contemporary "small" icon (24px)
double size = this.activeAttributes.imageSource != null ? 24 : this.activeAttributes.imageScale;
size *= visibilityScale;
this.activeAttributes.imageOffset.offsetForSize(size, size, offset);
offsetX = offset.x;
offsetY = offset.y;
scaleX = scaleY = size;
}

unitSquareTransform.multiplyByTranslation(
screenPlacePoint.x - offset.x,
screenPlacePoint.y - offset.y,
// Position image on screen
unitSquareTransform.multiplyByTranslation(
screenPlacePoint.x,
screenPlacePoint.y,
screenPlacePoint.z);

unitSquareTransform.multiplyByScale(size, size, 1);
// Divide Z by 2^24 to prevent texture clipping when tilting (where 24 is depth buffer bit size).
// Doing so will limit depth range to (diagonal length)/2^24 and make its value within 0..1 range.
unitSquareTransform.multiplyByScale(1, 1, 1d / (1 << 24) );

// Perform the tilt so that the image tilts back from its base into the view volume
if (this.imageTilt != 0) {
double tilt = this.imageTiltReference == WorldWind.RELATIVE_TO_GLOBE ?
rc.camera.tilt + this.imageTilt : this.imageTilt;
unitSquareTransform.multiplyByRotation(-1, 0, 0, tilt);
}

// ... perform image rotation
// Perform image rotation
if (this.imageRotation != 0) {
double rotation = this.imageRotationReference == WorldWind.RELATIVE_TO_GLOBE ?
rc.camera.heading - this.imageRotation : -this.imageRotation;
unitSquareTransform.multiplyByTranslation(0.5, 0.5, 0);
rc.camera.heading - this.imageRotation : -this.imageRotation;
unitSquareTransform.multiplyByRotation(0, 0, 1, rotation);
unitSquareTransform.multiplyByTranslation(-0.5, -0.5, 0);
}

// ... and perform the tilt so that the image tilts back from its base into the view volume.
if (this.imageTilt != 0) {
double tilt = this.imageTiltReference == WorldWind.RELATIVE_TO_GLOBE ?
rc.camera.tilt + this.imageTilt : this.imageTilt;
unitSquareTransform.multiplyByRotation(-1, 0, 0, tilt);
}
// Apply pivot translation
unitSquareTransform.multiplyByTranslation(-offsetX, -offsetY, 0);

// Apply scale
unitSquareTransform.multiplyByScale(scaleX, scaleY, 1);
}

/**
Expand Down