Worldwind的Point PlaceMark可渲染对象具有通过调用setLineEnabled将地标中的一条线放到地形上的功能,如以下屏幕截图所示:

java - 从符号到地形的世界风线-LMLPHP

我想做的是添加一条这样的行,该行也可与“战术符号”可渲染一起使用。我首先想到的是从PointPlacemark渲染器中借用执行此操作的逻辑,并将其添加到AbstractTacticalSymbol渲染器中。我已经尝试过了,但到目前为止没有成功。

到目前为止,这是我所做的:

  • 将此添加到OrderedSymbol类:
    public Vec4 terrainPoint;
    
  • 更新了computeSymbolPoints以计算terrainPoint
    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);
    
    }
    
  • 添加了此逻辑(取自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());
        }
    }
    
  • 将此调用添加到drawOrderedRenderable方法的开头:
    boolean drawLine = this.isDrawLine(dc, osym);
    if (drawLine)
        this.drawLine(dc, pickCandidates, osym);
    

  • 我相信这与PointPlacemark所做的工作很相似,以使线出现在地形上,但这是在运行带有更改的TacticalSymbols示例时得到的结果:

    java - 从符号到地形的世界风线-LMLPHP

    这是完整的AbsractTacticalSymbol文件,其中包含我(尝试过的)更改:http://pastebin.com/aAC7zn0p(对于SO而言太大)

    最佳答案

    好的,所以这里的问题是框架中正投影和透视投影之间的混合。至关重要的是,如果我们查看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);
    }
    

    而已。但是,如果我们查看AbstractTacticalSymbol的beginDrawing,我们会看到更多的代码,尤其是以下两行:
    this.BEogsh.pushProjectionIdentity(gl);
    gl.glOrtho(0d, viewport.getWidth(), 0d, viewport.getHeight(), 0d, -1d);
    

    它将OpenGL投影从“透视”模式切换为“正交”模式,两种非常不同的投影技术不能很好地融合在一起,除了少数值得注意的情况:其中一种是在3D场景上进行UI渲染,例如:渲染图标! video showing the difference between orthographic and perspective rendering

    我觉得用语言解释很尴尬,但是透视图渲染可以提供透视图,而正交渲染则不能,因此您得到的东西很像2D游戏,适用于UI,但不适用于逼真的3D图像。

    但是PointPlaceMark还渲染图标,因此该文件在两种投影模式之间切换到哪里?原来,他们在调用doDrawOrderedRenderable(行976)后以drawLine进行了此操作。

    那么,为什么会出错呢?现在,框架内部发生了很多魔术,所以我不能完全确定会发生什么,但是我已经有了一个广泛的想法,那就是出了什么问题。这是错误的,因为透视投影允许您以与正投影(在框架中)完全不同的方式提供坐标,在这种情况下,可能向投影渲染提供(x,y,z)会在(X,Y,Z )世界空间,而正交渲染则在(x,y,z)屏幕空间(或剪辑空间,我不是专业人士)上渲染。因此,当您现在从图标到地面在坐标(300000,300000,z)上画一条线时,它们当然会掉出屏幕,并且不可见,因为您没有300000x3000000像素的屏幕。也可能是这两种方法都允许在世界空间中提供坐标(尽管这似乎不太可能),在这种情况下,下图说明了该问题。在下面的方框中,两个摄像机都指向相同的方向,但是看到的东西却不同。

    java - 从符号到地形的世界风线-LMLPHP
    特别注意透视渲染如何使您能够看到更多的盒子。

    因此,由于渲染代码是从render()方法的透视投影开始的,因此解决此问题就像在绘制直线后延迟正交投影的开始一样简单,就像在PointPlaceMark的代码中一样。
    这就是我所做的here(1962年至1968年的行),只是将您的代码的几行移到了正射投影之外,因此在beginDrawing之前,您已经差不多完成了。

    现在,此解决方案不是很好,因为代码的功能现在很大程度上取决于执行顺序,通常这很容易出错。这部分是由于我所做的简单修复,而主要是因为该框架遵循了不赞成使用的OpenGL标准(用于切换视角)(因此),因此,无论这样的解决方案是否在我自己的范围内,我都无法提供真正完美的解决方案能力。

    根据您的喜好,您可能需要使用继承来创建SymbolWithLine父类(super class)或接口(interface),或者使用组合来添加功能。或者,如果不需要其他许多类的此功能,则可以像这样保留它。无论如何,我希望这是足够的信息,可以解决此问题。

    根据您的要求,以下几行代码演示了行宽和行颜色的变化(行1965):
    this.lineColor = Color.CYAN;
    this.lineWidth = 3;
    this.drawLine(dc, this.pickSupport, osym);
    

    java - 从符号到地形的世界风线-LMLPHP

    Updated code for AbstractTacticalSymbol

    我不确定这是否可以称为“规范答案”,但是我很乐意通过任何建议来更新答案,或者多澄清一点我的解释。我认为答案的关键在于对正交投影与透视投影的理解,但是我觉得这并不是对这个问题做出规范答案的地方。

    10-07 17:00