7

Worldwind の Point PlaceMark レンダリング可能オブジェクトには、次のスクリーンショットのようにsetLineEnabledを呼び出すことで、Placemark から地形までラインをドロップする機能があります。

ここに画像の説明を入力

私がやろうとしているのは、Tactical Symbol renderable でも機能するこのような行を追加することです。私が最初に考えたのは、PointPlacemarkレンダリング可能オブジェクトからこれを行うためのロジックを借りて、それをAbstractTacticalSymbolレンダリング可能オブジェクトに追加することでした。私はそれを試みましたが、これまでのところ成功していません。

これが私がこれまでに行ったことです:

  1. これを OrderedSymbol クラスに追加しました:

    public Vec4 terrainPoint;
    
  2. TerrainPoint を計算するように computeSymbolPoints を更新しました

    protected void computeSymbolPoints(DrawContext dc, OrderedSymbol osym)
    {
        osym.placePoint = null;
        osym.screenPoint = null;
        osym.terrainPoint = null;
        osym.eyeDistance = 0;
    
        Position pos = this.getPosition();
        if (pos == null)
            return;
    
        if (this.altitudeMode == WorldWind.CLAMP_TO_GROUND || dc.is2DGlobe())
        {
            osym.placePoint = dc.computeTerrainPoint(pos.getLatitude(), pos.getLongitude(), 0);
        }
        else if (this.altitudeMode == WorldWind.RELATIVE_TO_GROUND)
        {
            osym.placePoint = dc.computeTerrainPoint(pos.getLatitude(), pos.getLongitude(), pos.getAltitude());
        }
        else // Default to ABSOLUTE
        {
            double height = pos.getElevation() * dc.getVerticalExaggeration();
            osym.placePoint = dc.getGlobe().computePointFromPosition(pos.getLatitude(), pos.getLongitude(), height);
        }
    
        if (osym.placePoint == null)
            return;
    
        // Compute the symbol's screen location the distance between the eye point and the place point.
        osym.screenPoint = dc.getView().project(osym.placePoint);
        osym.eyeDistance = osym.placePoint.distanceTo3(dc.getView().getEyePoint());
    
        // Compute a terrain point if needed.
        if (this.isLineEnabled() && this.altitudeMode != WorldWind.CLAMP_TO_GROUND && !dc.is2DGlobe())
            osym.terrainPoint = dc.computeTerrainPoint(pos.getLatitude(), pos.getLongitude(), 0);
    
    }
    
  3. このロジックを追加しました (PointPlacemark.java から取得し、AbstractTacticalSymbol.java に準拠するように更新しました)。lineEnabled を true に設定しているため、デフォルトで線が描画されることに注意してください。

    boolean lineEnabled = true;
    
    
    double lineWidth = 1;
    protected int linePickWidth = 10;
    Color lineColor = Color.white;
    
    /**
     * Indicates whether a line from the placemark point to the corresponding position on the terrain is drawn.
     *
     * @return true if the line is drawn, otherwise false.
     */
    public boolean isLineEnabled()
    {
        return lineEnabled;
    }
    
    /**
     * Specifies whether a line from the placemark point to the corresponding position on the terrain is drawn.
     *
     * @param lineEnabled true if the line is drawn, otherwise false.
     */
    public void setLineEnabled(boolean lineEnabled)
    {
        this.lineEnabled = lineEnabled;
    }
    
    /**
     * Determines whether the placemark's optional line should be drawn and whether it intersects the view frustum.
     *
     * @param dc the current draw context.
     *
     * @return true if the line should be drawn and it intersects the view frustum, otherwise false.
     */
    protected boolean isDrawLine(DrawContext dc, OrderedSymbol opm)
    {
        if (!this.isLineEnabled() || dc.is2DGlobe() || this.getAltitudeMode() == WorldWind.CLAMP_TO_GROUND
            || opm.terrainPoint == null)
            return false;
    
        if (dc.isPickingMode())
            return dc.getPickFrustums().intersectsAny(opm.placePoint, opm.terrainPoint);
        else
            return dc.getView().getFrustumInModelCoordinates().intersectsSegment(opm.placePoint, opm.terrainPoint);
    }
    
    
    
    
    /**
     * Draws the placemark's line.
     *
     * @param dc             the current draw context.
     * @param pickCandidates the pick support object to use when adding this as a pick candidate.
     */
    protected void drawLine(DrawContext dc, PickSupport pickCandidates, OrderedSymbol opm)
    {
        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
    
        if ((!dc.isDeepPickingEnabled()))
            gl.glEnable(GL.GL_DEPTH_TEST);
        gl.glDepthFunc(GL.GL_LEQUAL);
        gl.glDepthMask(true);
    
        try
        {
            dc.getView().pushReferenceCenter(dc, opm.placePoint); // draw relative to the place point
    
            this.setLineWidth(dc);
            this.setLineColor(dc, pickCandidates);
    
            gl.glBegin(GL2.GL_LINE_STRIP);
            gl.glVertex3d(Vec4.ZERO.x, Vec4.ZERO.y, Vec4.ZERO.z);
            gl.glVertex3d(opm.terrainPoint.x - opm.placePoint.x, opm.terrainPoint.y - opm.placePoint.y,
                opm.terrainPoint.z - opm.placePoint.z);
            gl.glEnd();
        }
        finally
        {
            dc.getView().popReferenceCenter(dc);
        }
    }
    
    
    /**
     * Sets the width of the placemark's line during rendering.
     *
     * @param dc the current draw context.
     */
    protected void setLineWidth(DrawContext dc)
    {
        Double lineWidth = this.lineWidth;
        if (lineWidth != null)
        {
            GL gl = dc.getGL();
    
            if (dc.isPickingMode())
            {
                gl.glLineWidth(lineWidth.floatValue() + linePickWidth);
            }
            else
                gl.glLineWidth(lineWidth.floatValue());
    
            if (!dc.isPickingMode())
            {
                gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_FASTEST);
                gl.glEnable(GL.GL_LINE_SMOOTH);
            }
        }
    }
    
    
    /**
     * Sets the color of the placemark's line during rendering.
     *
     * @param dc             the current draw context.
     * @param pickCandidates the pick support object to use when adding this as a pick candidate.
     */
    protected void setLineColor(DrawContext dc, PickSupport pickCandidates)
    {
        GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
    
        if (!dc.isPickingMode())
        {
            Color color = this.lineColor;
            gl.glColor4ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue(),
                (byte) color.getAlpha());
        }
        else
        {
            Color pickColor = dc.getUniquePickColor();
            Object delegateOwner = this.getDelegateOwner();
            pickCandidates.addPickableObject(pickColor.getRGB(), delegateOwner != null ? delegateOwner : this,
                this.getPosition());
            gl.glColor3ub((byte) pickColor.getRed(), (byte) pickColor.getGreen(), (byte) pickColor.getBlue());
        }
    }
    
  4. 次の呼び出しを drawOrderedRenderable メソッドの先頭に追加しました。

    boolean drawLine = this.isDrawLine(dc, osym);
    if (drawLine)
        this.drawLine(dc, pickCandidates, osym);
    

これは PointPlacemark が地形への線を表示するために行っていることをよく反映していると思いますが、変更を加えてTacticalSymbolsの例を実行すると、次のようになります。

ここに画像の説明を入力

これが私の(試みられた)変更を含むAbsractTacticalSymbolファイル全体です: http://pastebin.com/aAC7zn0p (SOには大きすぎます)

4

1 に答える 1

5

ここでの問題は、フレームワーク内での正射投影と透視投影の混合です。重要なことに、PointPlaceMark を見ると、次のbeginDrawingことがわかります。

GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.

int attrMask =
        GL2.GL_DEPTH_BUFFER_BIT // for depth test, depth mask and depth func
            ... bunch more bits being set ...

gl.glPushAttrib(attrMask);

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

それでおしまい。しかし、AbstractTacticalSymbolbeginDrawingを見ると、さらに多くのコードが表示されます。特に次の 2 行です。

this.BEogsh.pushProjectionIdentity(gl);
gl.glOrtho(0d, viewport.getWidth(), 0d, viewport.getHeight(), 0d, -1d);

OpenGL プロジェクションをパースペクティブ モードから正投影モードに切り替えると、2 つの大きく異なるプロジェクション手法がうまく混ざりません。正投影レンダリングと透視レンダリングの違いを示すビデオ

言葉で説明するのは難しいと思いますが、パースペクティブ レンダリングではパースペクティブが得られますが、正射投影レンダリングでは得られないため、UI には適していますが、リアルな 3D 画像には適していない 2D ゲームのようなものが得られます。

しかし、PointPlaceMark はアイコンもレンダリングするので、そのファイルは 2 つの投影モードをどこで切り替えるのでしょうか? 彼らは呼び出したdoDrawOrderedRenderable にそれを行うことがわかりましたdrawLine(976行目)。

では、なぜうまくいかないのでしょうか。現在、フレームワーク内で多くの魔法が行われているため、何が起こっているのか正確にはわかりませんが、何がうまくいかないかについては大まかな考えを持っています. 透視投影では、(フレームワーク内の) 正射投影とは大きく異なる方法で座標を指定できるため、うまくいきません。この場合、投影レンダリングに (x,y,z) を指定すると、(X,Y,Z ) ワールド空間、正投影レンダリングは (x,y,z) 画面空間 (またはクリップ空間、私はこれのプロではありません) でレンダリングします。したがって、座標 (300000,300000,z) でアイコンから地面まで線を引くと、300000x3000000 ピクセルの画面がないため、それらはもちろん画面から外れて見えなくなります。 . また、どちらのアプローチでもワールド空間で座標を提供できる可能性があります (これはありそうにありませんが)。この場合、下の図は問題を示しています。両方のカメラは、下のボックスで同じ方向を向いていますが、異なるものを見ています。

遠近法と正射法 特に、パースペクティブ レンダリングにより、より多くのボックスを表示できることに注目してください。

したがって、レンダリング コードはrender()メソッドの透視投影で開始されるため、これを修正するには、PointPlaceMark のコードのように、線を描画した後に正射投影の開始を遅らせるだけです。それはまさに私がここで行っていることです(1962 行から 1968 行)。これは、コードの数行を正射投影の外に移動するだけなので、以前beginDrawingはほぼ完了していました。

コードの機能は実行順序に大きく依存するため、このソリューションはあまり洗練されたものではありません。これは、私が行った単純な修正が原因の 1 つですが、ほとんどの場合、フレームワークが非推奨の OpenGL 標準に準拠してパースペクティブを切り替えるため (とりわけ) であるため、真に完璧なソリューションを作成することはできません。能力。

好みに応じて、継承を使用してSymbolWithLineスーパークラスまたはインターフェイスを作成したり、構成を使用して機能を追加したりすることができます。または、他の多くのクラスでこの機能が必要ない場合は、このままにしておくこともできます。とにかく、これがこの問題を解決するのに十分な情報であることを願っています.

ご要望に応じて、次の行は線幅と線の色の変更を示しています (1965 行目)。

this.lineColor = Color.CYAN;
this.lineWidth = 3;
this.drawLine(dc, this.pickSupport, osym);

線の色と幅

AbstractTacticalSymbol の更新されたコード

これが「正規の回答」に該当するかどうかはわかりませんが、提案を含めて回答を更新したり、説明をもう少し明確にしたりできれば幸いです。答えの核心は、正投影と透視投影の理解にあると思いますが、これは実際にはその質問に対する標準的な答えの場所ではないと思います.

于 2016-06-25T21:27:05.337 に答える