topical media & game development

talk show tell print

graphic-webgl-scenejs-examples-ray-picking-ray-picking.js / js



  
SceneJS Example - Ray picking example Lindsay Kay lindsay.kay AT xeolabs.com March 2010 github.com/xeolabs/scenejs/wiki/picking

  
  
  /*----------------------------------------------------------------------
   * Function to return JSON defining a 3D array of teapots.
   *
   * Each teapot has a random colour, and is wrapped by a "name" node
   * which identifies the teapot's world-space location.
   *
   * A picking hit will contain the name of the picked teapot.
   *---------------------------------------------------------------------*/
  
  function createTeapotArray() {
  
    var nodes = [];
  
    for (var x = -250; x <= 250; x += 100) {
      for (var y = -250; y <= 250; y += 100) {
        for (var z = -250; z <= 250; z += 100) {
          nodes.push({
            type: "material",
            baseColor:   { r: 0.2 + Math.random(), g: 0.2 + Math.random(), b: 0.2 + Math.random() },
            specularColor: { r: 0.9, g: 0.9, b: 0.9 },
            specular:    0.9,
            shine:     6.0,
  
            nodes: [
              {
                type: "translate",
                x: x,
                y: y,
                z: z,
  
                nodes: [
                  {
                    type: "name",
                    name: "object_" + x + "_" + y + "_" + z + "",
                    nodes: [
                      {
                        type: "scale",
                        x: 10.0,
                        y: 10.0,
                        z: 10.0,
  
                        nodes: [
                          {
                            type:"teapot"
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          });
        }
      }
    }
  
    return {
      type: "node",
      nodes: nodes
    };
  }
  
  /*----------------------------------------------------------------------
   * Create scene containing our array of teapots.
   *
   * The scene also contains a white sphere that we will move to the
   * world-space location of the ray intersection of each pick hit, to
   * indicate the picked point on the surface of each teapot we pick.
   *---------------------------------------------------------------------*/
  
  SceneJS.createScene({
    type: "scene",
    id: "theScene",
    canvasId: 'theCanvas',
    loggingElementId: "theLoggingDiv",
  
    nodes: [
      {
        type: "lookAt",
        id: "theLookAt",
        eye : { x: 0, y: 0, z: -400},
        look : { x : 0.0, y : 0.0, z : 0 },
        up : { x: 0.0, y: 1.0, z: 0.0 },
  
        nodes: [
          {
            type: "camera",
  
            optics: {
              type: "perspective",
              fovy : 45.0,
              aspect : 1.47,
              near : 0.10,
              far : 1000.0
            },
  
            nodes: [
              {
                type: "light",
                mode:          "dir",
                color:         { r: 1.0, g: 1.0, b: 1.0 },
                diffuse:        true,
                specular:        true,
                dir:          { x: -1.0, y: -0.5, z: 0.0 }
              },
              {
                type: "light",
                mode:          "dir",
                color:         { r: 0.7, g: 0.7, b: 0.7 },
                diffuse:        true,
                specular:        true,
                dir:          { x: 1.0, y: 0.5, z: 1.0 }
              },
              {
                type: "node",
                nodes: [
  
                  {
                    type: "material",
                    baseColor:   { r: 1.0, g: 1.0, b: 1.0 },
                    specularColor: { r: 0.9, g: 0.9, b: 0.9 },
                    specular:    0.9,
                    shine:     6.0,
  
                    nodes: [
  
                      /*-----------------------------------------------------------------------
                       * Add array of pickable teapots
                       *----------------------------------------------------------------------*/
  
                      createTeapotArray(),
  
                      /*-----------------------------------------------------------------------
                       * White dot that indicates world-space ray intersection point
                       * of each pick hit.
                       *
                       * We'll update its translation with the pick position on each hit.
                       *----------------------------------------------------------------------*/
                      {
                        type: "name",
                        name: "indicator",
                        nodes: [
                          {
                            type: "material",
                            baseColor: { r: 1, g: 1, b: 1 },
                            specularColor: { r: 1, g: 1, b: 1 },
  
                            nodes: [
                              {
                                type: "translate",
                                id: "pickIndicator",
  
                                x: -20,
                                z: -70,
  
                                nodes: [
                                  {
                                    type: "scale",
                                    x: 1.5,
                                    y: 1.5,
                                    z: 1.5,
                                    nodes: [
  
                                      {
                                        type: "sphere"
                                      }
                                    ]
                                  }
                                ]
                              }
                            ]
                          }
                        ]
                      }
                    ]
                  }
  
                ]
              }
            ]
          }
        ]
      }
    ]
  });
  
  var scene = SceneJS.scene("theScene");
  var indicatorSphere = scene.findNode("pickIndicator");
  var lookAt = scene.findNode("theLookAt");
  
  var canvas = document.getElementById("theCanvas");
  
  var updated = false;
  
  var dist = -400;
  
  var lastX;
  var lastX2;
  var lastY2;
  var lastY;
  var dragging = false;
  
  var yaw = 0;
  var pitch = 0;
  
  function mouseDown(event) {
    lastX = event.clientX;
    lastY = event.clientY;
    dragging = true;
  }
  
  function mouseUp(event) {
    dragging = false;
  
    //  if (event.clientX == lastX && event.clientY == lastY) {
    //
    //
    //  }
  }
  
  function mouseMove(event) {
    if (dragging) {
      yaw += (event.clientX - lastX) * -.1;
      pitch += (lastY - event.clientY) * .1;
      lastX = event.clientX;
      lastY = event.clientY;
      updated = true;
  
    } else {
  
      /*-----------------------------------------------------------------------
       * On click, do ray-pick; if we get a hit, update the white
       * indicator sphere's translation to show the picked position
       *----------------------------------------------------------------------*/
  
      var coords = clickCoordsWithinElement(event);
  
      var hit = scene.pick(coords.x, coords.y, { rayPick: true });
  
      if (hit) {  // Ignore hits on the indicator sphere
  
        if (hit.name != "indicator") {
          
          $("#pickIndicator").show();
  
          var worldPos = hit.worldPos;
  
          indicatorSphere.set({
            x: worldPos[0],
            y: worldPos[1],
            z: worldPos[2]
          });
  
          updateLabelPos(
              hit.name,
              hit.canvasPos,
              hit.worldPos);
        }
  
      } else { // Nothing picked
        $("#pickIndicator").hide();
      }
    }
  }
  
  function clickCoordsWithinElement(event) {
    var coords = { x: 0, y: 0};
    if (!event) {
      event = window.event;
      coords.x = event.x;
      coords.y = event.y;
    } else {
      var element = event.target ;
      var totalOffsetLeft = 0;
      var totalOffsetTop = 0 ;
  
      while (element.offsetParent)
      {
        totalOffsetLeft += element.offsetLeft;
        totalOffsetTop += element.offsetTop;
        element = element.offsetParent;
      }
      coords.x = event.pageX - totalOffsetLeft;
      coords.y = event.pageY - totalOffsetTop;
    }
    return coords;
  }
  
  function updateLabelPos(pickName, canvasPos, worldPos) {
  
    var offset = $("#theCanvas").offset();
  
    $("#pickIndicator").show();
    $("#pickIndicator").html(
        "Hit info:<br/>"
            + "<br/>name: \"" + pickName + "\""
            + "<br/>canvasPos : " + roundFloat(canvasPos[0]) + ", " + roundFloat(canvasPos[1])
            + "<br/>worldPos : " + roundFloat(worldPos[0]) + ", " + roundFloat(worldPos[1]) + ", " + roundFloat(worldPos[2])
        );
  
    $("#pickIndicator").css("left", offset.left + canvasPos[0] + 5);
    $("#pickIndicator").css("top", offset.top + canvasPos[1] + 5);
  }
  
  function roundFloat(v) {
    return Math.round(v * 10) / 10;
  }
  
  function mouseWheel(event) {
    var delta = 0;
    if (!event) event = window.event;
    if (event.wheelDelta) {
      delta = event.wheelDelta / 120;
      if (window.opera) delta = -delta;
    } else if (event.detail) {
      delta = -event.detail / 3;
    }
    if (delta) {
      if (delta < 0) {
        dist -= 10.0;
      } else {
        dist += 10.0;
      }
    }
    if (event.preventDefault)
      event.preventDefault();
    event.returnValue = false;
    updated = true;
  }
  
  canvas.addEventListener('mousedown', mouseDown, true);
  canvas.addEventListener('mousemove', mouseMove, true);
  canvas.addEventListener('mouseup', mouseUp, true);
  canvas.addEventListener('mousewheel', mouseWheel, true);
  canvas.addEventListener('DOMMouseScroll', mouseWheel, true);
  
  scene.start({
    idleFunc: function() {
  
      if (!updated) {
        return;
      }
      updated = false;
  
      var eyeVec = [0,0,dist];
  
      var pitchMat = Matrix.Rotation(pitch * 0.0174532925, V([1,0,0]));
      var yawMat = Matrix.Rotation(yaw * 0.0174532925, V([0,1,0]));
  
      eyeVec = pitchMat.multiply(V(eyeVec)).elements;
      eyeVec = yawMat.multiply(V(eyeVec)).elements;
  
      lookAt.set({
        eye: {x: eyeVec[0], y: eyeVec[1], z: eyeVec[2] }
      });
  
      $("#pickIndicator").hide();
    }
  });
  
  


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.