【问题标题】:Picking under elements of an element in Autodesk Forge viewer在 Autodesk Forge 查看器中的元素下拾取
【发布时间】:2017-05-07 01:14:21
【问题描述】:

我想完成一项可以在 Three.js 中完成但在 Autodesk Forge 查看器中无法完成的功能。以下是测试链接:http://app.netonapp.com/JavaScript/Three.js/select_inner_objects.html

要求是选择对象内的对象。这项工作可以在上面的演示中使用 THREE.Raycaster 来完成,使用 raycaster 来检测光线通过线上的所有元素。然后我可以在另一个对象的后面或内部获取对象。

我在 Autodesk Forge 查看器中尝试了这个概念,但没有成功。代码如下:

// Change this to:
// true to use original Three.js
// false to use Autodesk Forge Viewer API
var useThreeJS = true;

var container = $('div.canvas-wrap')[0];

container.addEventListener('mousedown', function (event) {
    if (useThreeJS) {
        var canvas = _viewer.impl.canvas;
        var containerWidth = canvas.clientWidth;
        var containerHeight = canvas.clientHeight;

        var camera = _viewer.getCamera();

        var mouse = mouse || new THREE.Vector3();
        var raycaster = raycaster || new THREE.Raycaster();

        mouse.x = 2 * (event.clientX / containerWidth) - 1;
        mouse.y = 1 - 2 * (event.clientY / containerHeight);
        mouse.unproject(camera);

        raycaster.set(camera.position, mouse.sub(camera.position).normalize());
        var intersects = raycaster.intersectObjects(objects);

        if (intersects.length == 1) {
            var obj = intersects[0].object;
            obj.material.color.setRGB(1.0 - i / intersects.length, 0, 0);
        } else if (intersects.length > 1) {
            // Exclude the first which is the outer object (i == 0)
            for (var i = 1; i < intersects.length; i++) {
                var obj = intersects[i].object;
                obj.material.color.setRGB(1.0 - i / intersects.length, 0, 0);
            }
        }
    } else {
        var vp = _viewer.impl.clientToViewport(event.canvasX, event.canvasY);
        var renderer = _viewer.impl.renderer();

        var dbId = renderer.idAtPixel(vp.x, vp.y);
        if (dbId) {
            console.debug("Selected Id: " + dbId);
            _viewer.select(dbId);
            _viewer.impl.invalidate(true);
        }
    }
}, false);

我发现 Forge 查看器有 viewer.impl.renderer().idAtPixel 方法,该方法非常适合在拾取像素处获取元素。但是,我希望它做更多的事情,在拾取像素处选择所有元素(在下面或嵌套)。如何使用 Forge Viewer API 做到这一点?

【问题讨论】:

    标签: autodesk-forge autodesk-viewer


    【解决方案1】:

    根据另一个post中吴钟的建议,这里是选择另一个元素下或内部的元素的最终解决方案。我创建了一个 Autodesk Forge 查看器扩展来轻松使用它。

    ///////////////////////////////////////////////////////////////////////////////
    // InnerSelection viewer extension
    // by Khoa Ho, December 2016
    //
    ///////////////////////////////////////////////////////////////////////////////
    AutodeskNamespace("Autodesk.ADN.Viewing.Extension");
    
    Autodesk.ADN.Viewing.Extension.InnerSelection = function (viewer, options) {
    
        Autodesk.Viewing.Extension.call(this, viewer, options);
    
        var _self = this;
    
        var _container = viewer.canvas.parentElement;
        var _renderer = viewer.impl.renderer();
        var _instanceTree = viewer.model.getData().instanceTree;
        var _fragmentList = viewer.model.getFragmentList();
        var _eventSelectionChanged = false;
        var _viewport;
        var _outerDbId;
    
        _self.load = function () {
    
            _container.addEventListener('mousedown',
                onMouseDown);
    
            viewer.addEventListener(
                Autodesk.Viewing.SELECTION_CHANGED_EVENT,
                onItemSelected);
    
            console.log('Autodesk.ADN.Viewing.Extension.InnerSelection loaded');
    
            return true;
        };
    
        _self.unload = function () {
    
            _container.removeEventListener('mousedown',
                onMouseDown);
    
            viewer.removeEventListener(
                Autodesk.Viewing.SELECTION_CHANGED_EVENT,
                onItemSelected);
    
            console.log('Autodesk.ADN.Viewing.Extension.InnerSelection unloaded');
    
            return true;
        };
    
        function onMouseDown(e) {
    
            var viewport = viewer.impl.clientToViewport(e.canvasX, e.canvasY);
            _viewport = viewport; // Keep this viewport to use in onItemSelected()
    
            var dbId = _renderer.idAtPixel(viewport.x, viewport.y);
    
            if (_outerDbId == dbId) {
                _outerDbId = -1;
    
                // Deselect everything
                viewer.select();
            } else {
                _outerDbId = dbId;
    
                // Hide outer element temporarily to allow picking its behind element
                viewer.hideById(dbId);
    
                _eventSelectionChanged = true;
            }
    
            viewer.impl.sceneUpdated(true);
        }
    
        function onItemSelected(e) {
    
            if (_eventSelectionChanged) {
    
                // Prevent self looping on selection
                _eventSelectionChanged = false;
    
                // Show outer element back
                viewer.show(_outerDbId);
    
                // Get inner element Id after the outer element
                // was just hidden on mouse down event
                var innerDbId = _renderer.idAtPixel(_viewport.x, _viewport.y);
    
                if (innerDbId > -1) {
                    // Select the inner element when it is found
                    viewer.select(innerDbId);
    
                    console.debug("Selected inner Id: " + innerDbId);
    
                } else if (_outerDbId > -1) {
                    // Select the outer element if the inner element is not found
                    viewer.select(_outerDbId);
    
                    console.debug("Selected outer Id: " + _outerDbId);
    
                }
            }
        }
    
    };
    
    Autodesk.ADN.Viewing.Extension.InnerSelection.prototype =
        Object.create(Autodesk.Viewing.Extension.prototype);
    
    Autodesk.ADN.Viewing.Extension.InnerSelection.prototype.constructor =
        Autodesk.ADN.Viewing.Extension.InnerSelection;
    
    Autodesk.Viewing.theExtensionManager.registerExtension(
        'Autodesk.ADN.Viewing.Extension.InnerSelection',
        Autodesk.ADN.Viewing.Extension.InnerSelection);

    【讨论】:

      【解决方案2】:

      我看到方法viewer.impl.renderer().idAtPixelviewer.impl.hitTestViewport 更好地选择鼠标选择元素。第一个可以通过hidden/ghost元素点击获取后面元素的objectId。而第二个不能。这是要测试的代码:

      var container = $('div.canvas-wrap')[0];
      
      container.addEventListener('mousedown', function (event) {
      
          var clickThroughHiddenElement = true;
      
          if (clickThroughHiddenElement) {
              var vp = _viewer.impl.clientToViewport(event.canvasX, event.canvasY);
              var renderer = _viewer.impl.renderer();
      
              var dbId = renderer.idAtPixel(vp.x, vp.y);
              if (!!dbId) {
                  _viewer.select(dbId);
              }
              console.debug("Selected Id: " + dbId);
          } else {
              var screenPoint = {
                  x: event.clientX,
                  y: event.clientY
              };
              var viewport = _viewer.navigation.getScreenViewport();
              var x = (screenPoint.x - viewport.left) / viewport.width;
              var y = (screenPoint.y - viewport.top) / viewport.height;
              // Normalize point
              x = x * 2.0 - 1.0;
              y = (1.0 - y) * 2.0 - 1.0;
      
              var vpVec = new THREE.Vector3(x, y, 1);
              var result = _viewer.impl.hitTestViewport(vpVec, false);
              if (!!result) {
                  var dbId = result.dbId;
                  _viewer.select(dbId);
                  console.debug("Selected Id: " + dbId);
              }
          }
      }
      

      但是,它们不是我想要的,单击透明元素以获取后面的元素。如果用户选择透明元素,它将被选中。如果用户选择内部元素,它将忽略外部透明元素来选择内部元素。

      我检查了 Forge 查看器使用带有元素边界框的 THREE.Raycaster 来检测鼠标单击时的交叉点。看来我的问题在 Forge 查看器中是可行的,就像在我的 Three.js demo 中一样。

      【讨论】:

        【解决方案3】:

        截至目前(12 月 16 日),当您使用鼠标单击选择时,查看器将不会忽略透明元素,因此即使它是透明的,它也会选择一个元素。下面是我用来跟踪光标下的内容的代码,也许会有用。

        // use jQuery to bind a mouve move event
        $(_viewer.container).bind("mousemove", onMouseMove);
        
        function onMouseMove(e) {
            var screenPoint = {
               x: event.clientX,
               y: event.clientY
            };
            var n = normalize(screenPoint);
            var dbId = /*_viewer.utilities.getHitPoint*/ getHitDbId(n.x, n.y);
            //
            // use the dbId somehow...
            //
        }
        
        // This is a built-in method getHitPoint, but the original returns
        // the hit point, so this modified version returns the dbId
        function getHitDbId(){
            y = 1.0 - y;
            x = x * 2.0 - 1.0;
            y = y * 2.0 - 1.0;
            var vpVec = new THREE.Vector3(x, y, 1);
            var result = _viewer.impl.hitTestViewport(vpVec, false);
        
            //return result ? result.intersectPoint : null; // original implementation
            return result ? result.dbId : null;
        }
        
        function normalize(screenPoint) {
            var viewport = _viewer.navigation.getScreenViewport();
            var n = {
               x: (screenPoint.x - viewport.left) / viewport.width,
               y: (screenPoint.y - viewport.top) / viewport.height
            };
            return n;
        }
        

        【讨论】:

        • viewer.impl.hitTestViewport 无法选择隐藏/幽灵元素后面的元素。 viewer.impl.renderer().idAtPixel 效果更好。
        猜你喜欢
        • 2021-11-12
        • 1970-01-01
        • 2021-09-19
        • 2017-05-06
        • 2021-12-19
        • 2020-09-06
        • 2020-10-23
        • 2017-01-07
        • 2019-05-04
        相关资源
        最近更新 更多