三个J - 如何挑选区域内的所有物体?

时间:2013-11-24 00:28:01

标签: three.js

我正在使用Three.js,我想知道如何获取给定区域内的所有对象?

例如,获取绿色方块中找到的所有对象: Mass object selection

解决方案:

        getEntitiesInSelection: function(x, z, width, height, inGroup) {
            var self = this,
                entitiesMap = [],
                color = 0,
                colors = [],
                ids = [],
                pickingGeometry = new THREE.Geometry(),
                pickingMaterial = new THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors } ),
                pickingScene = new THREE.Scene(),
                pickingTexture = new THREE.WebGLRenderTarget( this._renderer.domElement.width, this._renderer.domElement.height),
                cloneMesh,
                entities = inGroup ?
                    engine.getObjectsByGroup(inGroup) : engine.getRegisteredEntities();

            pickingTexture.generateMipmaps = false;

            //Go over each entity, change its color into its ID
            _.forEach(entities, function(entity) {
                if(undefined == entity.threeRenderable) {
                    return ;
                }

                //Clone entity
                cloneMesh = entity.threeRenderable.mesh().clone();
                cloneMesh.material = entity.threeRenderable.mesh().material.clone();
                cloneMesh.material.map = null;
                cloneMesh.material.vertexColors = THREE.VertexColors;
                cloneMesh.geometry = entity.threeRenderable.mesh().geometry.clone();
                cloneMesh.position.copy( entity.threeRenderable.mesh().position );
                cloneMesh.rotation.copy( entity.threeRenderable.mesh().rotation );
                cloneMesh.scale.copy( entity.threeRenderable.mesh().scale );

                //Cancel shadow
                cloneMesh.castShadow = false;
                cloneMesh.receiveShadow  = false;

                //Set color as entity ID
                entitiesMap[color] = entity.id();
                self._applyVertexColors(cloneMesh.geometry, new THREE.Color( color ) );
                color++;

                THREE.GeometryUtils.merge( pickingGeometry,  cloneMesh);
            });

            pickingScene.add( new THREE.Mesh( pickingGeometry, pickingMaterial ) );

            //render the picking scene off-screen
            this._renderer.render(pickingScene, this._objs[this._mainCamera], pickingTexture );
            var gl = this._renderer.getContext();

            //read the pixel under the mouse from the texture
            var pixelBuffer = new Uint8Array( 4 * width * height );
            gl.readPixels( x, this._renderer.domElement.height - z, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelBuffer );

            //Convert RGB in the selected area back to color
            for(var i=0; i<pixelBuffer.length; i+=4) {
                if( 0 == pixelBuffer[i] && 0 == pixelBuffer[i+1] && 0 == pixelBuffer[i+2] && 0 == pixelBuffer[i+3] ) {
                    continue;
                }

                color = ( pixelBuffer[i] << 16 ) | ( pixelBuffer[i+1] << 8 ) | ( pixelBuffer[i+2] );
                colors.push(color);
            }
            colors = _.unique(colors);

            //Convert colors to ids
            _.forEach(colors, function(color) {
                ids.push(entitiesMap[color]);
            });

            return ids;
        }

engine.getObjectsByGroup(inGroup) : engine.getRegisteredEntities();行 只返回一个实体数组,然后我遍历实体:

 _.forEach(entities, function(entity) { ...

只有具有'threeRenderable'属性(对象)的实体才可见,因此,我忽略那些没有它的实体:

if(undefined == entity.threeRenderable) {
      return ;
}

然后我将实体的克隆网格与pickingGeometry合并:

THREE.GeometryUtils.merge( pickingGeometry, cloneMesh);

最终,我将pickingGeometry添加到pickingScene:

 pickingScene.add( new THREE.Mesh( pickingGeometry, pickingMaterial ) );

然后我读取所选区域的颜色,并返回一个ID数组。

你可以查看我当时写的Node.js game engine

1 个答案:

答案 0 :(得分:1)

我想要实现这样的东西,我选择了一种非常不同的方法 - 可能更糟糕,我真的不知道 - 但是更容易做IMO,所以我把它放在这里以防有人想要它。< / p>

基本上,我只使用了2个光线投射来了解投影在地平面上的选择矩形的第一个和最后一个点,并迭代我的对象以了解哪些是。

一些非常基本的代码:

   function onDocumentMouseDown(event) {
      // usual Raycaster stuff ...

      // get the ground intersection
      var intersects = raycaster.intersectObject(ground);

      GlobalGroundSelection = {
        screen: { x: event.clientX, y: event.clientY },
        ground: intersects[0].point
      };
    }

   function onDocumentMouseUp(event) {
      // ends a ground selection
      if (GlobalGroundSelection) {
        // usual Raycaster stuff ...

        // get the ground intersection
        var intersects = raycaster.intersectObjects(ground);

        var selection = {
          begins: GlobalGroundSelection.ground,
          ends: intersects[0].point
        };

        GlobalGroundSelection = null;
        selectCharactersInZone(selection.begins, selection.ends);
      }
    }

    function onDocumentMouseMove(event) {

      if (GlobalGroundSelection) {
        // in a selection, draw a rectangle
        var p1 = GlobalGroundSelection.screen,
            p2 = { x: event.clientX, y: event.clientY };

        /* with these coordinates
          left: p1.x > p2.x ? p2.x : p1.x,
          top: p1.y > p2.y ? p2.y : p1.y,
          width: Math.abs(p1.x - p2.x),
          height: Math.abs(p1.y - p2.y)
        */
      }
    }

这是我的选择功能:

function selectCharactersInZone (start, end) {

  var selected = _.filter( SELECTABLE_OBJECTS , function(object) {
    // warning: this ignore the Y elevation value
    var itsin = object.position.x > start.x
            && object.position.z > start.z 
            && object.position.x < end.x
            && object.position.z < end.z;

    return itsin;
  });

  return selected;
}

一些警告:据我所知,这种技术仅在您不关心Y位置且您的选择是基本矩形时才可用。

我的2c