How To


Note: a WebGL-enabled browser is needed to run the demos presented below. Please refer to this page for a quick HOWTO or click here to test your browser.


Description

This page teaches how to place interactive hotspots on a 3D scene and how to associate them to the useful event handlers provided by 3DHOP.

Featured Set Scene Elements

  • Meshes
  • Spots


Hotspots and events

Click to run live demo
Advanced interactions: clickable instances, geometric hotspots and event handlers in 3DHOP

Set Up

While designing a fully interactive 3D scene, it can be very useful to place specific clickable elements of interest somewhere in the scene and linking them with particular information contents present on the HTML page. In order to meet this need, 3DHOP provides a way to create geometrical hotspots, and a series of events to detect user clicks on 3D models and hotspots. Again, the idea is to provide basic building blocks, that can be used to create a wide series of user interaction schemes, suitable for different web page layouts.
We will firstly describe how to add hosptots (clickable geometries) in the scene, by adding a new kind of element in the scene definition; then, we will detail how to detect user clicks on these geometries, by registering handler functions.

Instead of having hotspots defined by a single point, as it happens in many 3D visualization tools, we decide to implement in 3DHOP a more flexible system of clickable geometries. These geometries are rendered with a solid color using transparency, and can be turned visible/invisible at will during the visualization (like for the instance, it is possible to make visible/invisible single hotspots or groups of hotspots).
As for the other rendered geometries, it is firstly need to declare the mesh you want to use for the hotspots in the "meshes" section of the scene, as shown here:

    ...
    presenter.setScene({
      meshes: {
        "Gargoyle": {
          url: "models/multires/gargo.nxs"
        },
        "Cube": {
          url: "models/singleres/cube.ply"
        },
        "Sphere": {
          url: "models/singleres/sphere.ply"
        },
        "Wing" : { 
          url: "models/singleres/wing.ply"
        }
      },
      ...

In this case, we have two meshes that will beused for instances (the "Gargoyle" and the "Cube" meshes) and two other that will be used for hotspots (the "Sphere" and the "Wing" meshes). In this case, the two hotspots have a very simple geometry, so a single-resolution PLY file was used, but also multiresolution NXS geometries, and even pointclouds may be used as hotspots geometry.
Since a very common situation is the need of a clickable object with a simple shape, 3DHOP provides in the "models" folder, two light-weight single-resolution meshes, a cube and a sphere, suitable for most of these simple hotspots needs.
In any case, it is important to note that there is absolute equality between the meshes declared in this section. A mesh, once declared, may be used for either instance(s) and hotspot(s) (also both at the same time).

In a symmetrical fashion to what happens when declaring in the "setScene" field the renderable objects as "modelInstances", we will now declare the hotspots. This is done in another section of the "setScene" structure, called "spots". Each hotspot is an entry in this section.
Some of the keywords used in declaring a spot are the same used for the instances. In the following code: "Wing" is the unique ID of the spot, "mesh" link the hotspot with one of the meshes previously declared using its name (in this case the "wing" mesh), "transform" contains the 3D spatial transformations (specified as for the models) that allow to scale and deform the hotspot geometry, or move it on the scene. There are two other elments, in the example: "color", used to choose the hotspot solid color, and "alpha", used to set the transparency level of the hotspot geometry.
The "color" keyword accepts as input a 3-dimensional array containing color values express in RGB arithmetic notation (from 0.0 to 1.0), while "alpha" accepts as input the alpha transparency value expressed by a single scalar, varying from 0.0 (completely transparent) to 1.0 (solid); "color" and "alpha" keywords are optional, if they are not explicitly declared, the hotspots color and alpha default values are respectively [0.0, 0.25, 1.0] and 0.5).

      ...
      spots: {
        "Wing": {
          mesh : "Wing",
          transform : { 
            matrix: SglMat4.mul(SglMat4.translation([ 3.3, 110.0, -7.0]), 
                      SglMat4.rotationAngleAxis(sglDegToRad(-5.0), [0.0, 0.0, 1.0])) 
          },
          color: [0.0, 0.25, 1.0]
        },
        "Sphere": {
          mesh: "Sphere",
          transform: {
            matrix: SglMat4.mul(SglMat4.translation([-2.0, 12.0, 25.0]), SglMat4.scaling([30.0, 15.0, 15.0]))
          },
          color: [0.0, 0.25, 1.0],
          alpha: 0.5
        }
      },
      ...
    });
    ...

Exactly as for the model instances, hotspot geometries too would be visible by default. To control their visibility during declaration, add the "visible" field to the hotspot and set it to "true" or "false".
A common situation is to start the visualization without visible hotspots, and turn the hotspots on or off when the user decides to do so. To do this, one easy way is to set all hotspots invisible at declaration time, or to use one of the visibility functions to turn ALL of them off just after the scene declaration, as shown just below. Similarly to the model instance function, the first parameter HOP_ALL means "all the spots", the second one (false) means invisible, the third one (true) tells the system to redraw the view.

    ...
    presenter.setSpotVisibility(HOP_ALL, false, true);
    ...

Then, it is possibel to turn them all visible and then invisible again using a toolbar button, as shown here. We added new "hotspot" buttons in the toolbar (in the BODY section of the HTML page), in both the version on and off (respectively using the ID "hotspot_on" and "hotspot"):

<div id="3dhop" class="tdhop" onmousedown="if (event.preventDefault) event.preventDefault()">
 <div id="toolbar">
  <img id="home"       title="Home"                  src="skins/dark/home.png"   /><br/> 
  <img id="zoomin"     title="Zoom In"               src="skins/dark/zoomin.png" /><br/>
  <img id="zoomout"    title="Zoom Out"              src="skins/dark/zoomout.png"/><br/>
  <img id="light_on"   title="Disable Light Control" src="skins/dark/light_on.png" 
                                                          style="position:absolute; visibility:hidden;"/>
  <img id="light"      title="Enable Light Control"  src="skins/dark/light.png"  /><br/>
  <img id="hotspot_on" title="Hide Hotspots"         src="skins/dark/pin_on.png" 
                                                          style="position:absolute; visibility:hidden;"/>
  <img id="hotspot"    title="Show Hotspots"         src="skins/dark/pin.png"    /><br/>
  <img id="full_on"    title="Exit Full Screen"      src="skins/dark/full_on.png" 
                                                          style="position:absolute; visibility:hidden;"/>
  <img id="full"       title="Full Screen"           src="skins/dark/full.png"   />
 </div>
 <canvas id="draw-canvas" style="background-image: url(skins/light/background.png)"/>
</div>

This done, we added the new button action in the "actionsToolbar" JavaScript function (already described in one of the first HOW-TOs), in the scripting block of the webpage:

function actionsToolbar(action) {
  if(action=='home')
    presenter.resetTrackball();
  else if(action=='zoomin') 
    presenter.zoomIn();
  else if(action=='zoomout') 
    presenter.zoomOut(); 
  else if(action=='light' || action=='light_on') { 
    presenter.enableLightTrackball(!presenter.isLightTrackballEnabled()); lightSwitch(); } 
  else if(action=='hotspot'|| action=='hotspot_on') { 
    presenter.toggleSpotVisibility(HOP_ALL, true); presenter.enableOnHover(!presenter.isOnHoverEnabled()); 
    hotspotSwitch(); }
  else if(action=='full'  || action=='full_on') 
    fullscreenSwitch(); 
}

The new code is represented by the second-last "else if" statement, handling the "hotspot" and "hotspot_on" action IDs. When you press the hotspot toolbar button the "actionsToolbar" function is called, and the appropriate action is carried out. In this case, at the begin it is called a function ("toggleSpotVisibility") that simply invert the visibility status of all the hotspot entities and then redraws the scene. Then, another method ("enableOnHover") is then used to toggle the highlighting of the spot when the mouse passes over it, and finally "hotspotSwitch" swaps the hotspot button icon from off to on (and vice versa).

The above functions ("setSpotVisibility" and "toggleSpotVisibility") belongs to a group of three related to the hotspot visibility

isSpotVisibilityEnabled-returns the visibility status of a specified hotspot via tag;
setSpotVisibility-sets the visibility of a specified hotspot via tag;
toggleSpotVisibility-toggles the visibility of a specified hotspot via tag;

These functions correspond exactly to the ones used to manage the visibility of instances introduced in the previous tutorial. The first of the listed functions ("isSpotVisibilityEnabled") accepts in input the ID tag string (enclose in quotes) related to the desired hotspot instance and returns as output a boolean variable that specifies its visibility status ("true" if it is visible, "false" if it is not).
The second method ("setSpotVisibility") to set the visibility status of an instance takes in input three parameters: the ID tag string (enclose in quotes) related to the desired hotspot instance, a boolean variable that defines the new visibility status ("true" to set the visibility on, "false" to set off) and another boolean variable that ask for the 3D scene redrawing ("true" to redraw, "false" to not redraw).
Then, the "toggleSpotVisibility" function toggle the current visibility status of a spot instance; it accepts as input two parameters: the ID tag string (enclose in quotes) related to the desired hotspot instance and a boolean variable that acts the 3D scene redrawing ("true" to redraw, "false" to not redraw).
All these methods, as seen in the previous how-to, can use the ID tag system to refer groups of instances, and have a correspondent "ByName" function, useful to link directly the single hotspots via its name ID:

isSpotVisibilityEnabledByName-returns the visibility status of a specified hotspot via name;
setSpotVisibilityByName-sets the visibility of a specified hotspot via name;
toggleSpotVisibilityByName-toggles the visibility of a specified hotspot via name;

These three new functions has exactly the same function and parameters as their "tag" version, save for the first input parameter string, that in this case is the ID of a specific hotspot (still enclose in quotes).
All the above methods, as also seen in this tutorial example, can use as ID tag string the constant value HOP_ALL (without quotation marks), that refers to all the hotspot.
Take a look to the documentation section to find out more.

At this point the hotspots has been created, placed on the virtual scene on the right position, and linked to the relative toolbar button. To make them work, we need to tell 3DHOP what to do when an instance is clicked.
3DHOP provides a set of events, activated by mouse actions, that can be registered to specific handler function.

_onEnterSpot-bind an event handler to be called when the mouse enters an hotspot;
_onLeaveSpot-bind an event handler to be called when the mouse leaves an hotspot;
_onPickedSpot-bind an event handler to be called when the mouse clicks an hotspot;

Since it may be helpful to also know when the mouse is over a certain instance, or when an instance is clicked, each one of these above methods has a corresponding one for the instance geometries. So, it is possible to associate event listeners and related processes not only to hotspots but to instances too (giving a lot of flexibility to the design of your interactive 3D visualization webpage):

_onEnterInstace-bind an event handler to be called when the mouse enters an instance;
_onLeaveInstace-bind an event handler to be called when the mouse leaves an instance;
_onPickedInstace-bind an event handler to be called when the mouse clicks an instance;

To use all these event handlers, they must be registered to a JavaScript function, defined in the HTML page, that defines what happens when the event is raised, as shown here below.
All the functions receive as a parameter the unique ID of the hotspot or instance that raised the event. In thse functions, you basically can specify what happens when an instance or hotspot is clicked, or when the mouse enter/exit an instance or hotspot.

    ...
    presenter._onPickedSpot = onPickedSpot;
    presenter._onPickedInstance = onPickedInstance;
}

In this tutorial example we chose to associate only two of the possible six event listeners above listed, i.e. those ones related to the click on the scene geometries (both hotspots and models).
For example, the function handling the click on a hotspot, "onPickedSpot", that receives as a parameter the ID of the clicked hotspot, works like this: the ID value is used to determine which spot has been clicked, and then a simple pop-up window is created (using the JavaScript "alert" function).

function onPickedSpot(id) {
  switch(id) {
     case 'Wing'   : alert("Wing Hotspot Clicked"); break;
     case 'Sphere' : alert("Base Hotspot Clicked"); break;
  }
}

Likewise the "onPickedInstace" JavaScript function associated to the click on an instance, is the following:

function onPickedInstance(id) {
  switch(id) {
     case 'Gargo' : alert("Gargoyle Model Clicked "); break;
     case 'Base'  : alert("Pedestal Model Clicked"); break;
  }
}

At this point the tutorial is finished, you can run the live demo, enable the hotspots via the toolbar, and then see what happens when you click on a hotspot or a model. Concerning this, is interesting to note that by default the hotspot click behaviour is "transparent" to the clicks, i.e. clicking a hotspot placed on a model, the 3DHOP firstly calls the function associated the hotspot click event, and then call that one associated to the instance click event. To avoid this behaviour and stop the event propagation, you can add the follow line of code at the beginning of the 3D model clicking function definition:

function onPickedInstance(id) {
  if(presenter._lastPickedSpot!="null") return;
  ...
  //your code goes here
  ...
}

In this way when you click a hotspot placed on a model only the code related with the spot picking will be run, and that one related to the model will be leave out.


Notes

The hotspots management in 3DHOP is again a series of basic components and building blocks that can be configured and used in many different ways, according to the specific needs of your project. Please see the gallery for some examples of the use of hotspots.

  • Each click/mouse handler uses additional resources; so, add only the ones that are needed by your interaction.
  • The most resource-consuming functionality is to detect the mouse-over events. If the scene is complex, this may slow down the rendering. If so, you may deactivate the on-hover detection, using the presenter.enableOnHover() (see the documentation).
  • Pointclouds can be used as hotspot geoemtry and instances using pointclouds as geometry are clickable; however, as the click is detected ON THE SPECIFIC CLICKED PIXEL, if the use clicks on an empty area between points of a pointcloud, no event will be registered.


Well, summarizing, to place functional interactive hotspots on the 3D virtual scene you just had to load some new geometries and then create hotspots it in the section "spots" of the scene definition. Done this, it only remains to register the right event handler, design the actions to perform when the event is fired and… you are done!

The complete sources of this example are provided together the 3DHOP code in the download section.

If you want instead to learn more about how to add a measurement tool to the 3DHOP viewer click here and go to the next HOWTO.