1

我想完成一个可以在 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 做到这一点?

4

3 回答 3

2

根据钟武在另一篇文章中的建议这里是选择位于另一个元素之下或内部的元素的最终解决方案。我创建了一个 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);

于 2016-12-29T05:08:34.870 回答
0

截至目前(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;
}
于 2016-12-22T15:20:18.973 回答
0

我看到方法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演示中一样。

于 2016-12-22T18:55:56.323 回答