Jaa


Mapping mouseclick on 3DModel to a 2D texture coordinate

Some time back I posted a gadget with the rotating earth. This looked nice but it lacked some interaction. One of the things to make it interactive was to map the point of clicking to the texture applied on the globe. To get the 2D coordinate we need to perform a hittest on the Viewport3D and this will give us access to RayMeshGeometry3DHitTestResult object.  This provides info on the mesh, geometrymodel, vertex indices of the triangle hit, and the barycentric coordinates. To obtain the coordinate we multiply the vertex weights (barycentric coordinates) with the cordinates of the triangle hit. We then scale this point with the texture size to get the actual pixel within the texture (The link to the barycentric coordinates is a nice read and explains the math behind these calculations).

The image below is the same rotating globe but which shows the continent name when clicked on the globe.. The continent boundaries are not exact - so you might see some land not being mapped to anything ;).. A red dot indicates the mouse point/click

The code (by Kurt Berglund) looks something like the below:

    1 public HitTestResultBehavior HTResult(System.Windows.Media.HitTestResult rawresult)

    2 {

    3     RayHitTestResult rayResult = rawresult as RayHitTestResult;

    4 

    5     if (rayResult != null)

    6     {

    7         RayMeshGeometry3DHitTestResult rayMeshResult = rayResult as RayMeshGeometry3DHitTestResult;               

    8         if (rayMeshResult != null)

    9         {

   10             GeometryModel3D hitgeo = rayMeshResult.ModelHit as GeometryModel3D;

   11             MeshGeometry3D geom = rayMeshResult.MeshHit;

   12 

   13             // pull the barycentric coordinates of the intersection point

   14             double vertexWeight1 = rayMeshResult.VertexWeight1;

   15             double vertexWeight2 = rayMeshResult.VertexWeight2;

   16             double vertexWeight3 = rayMeshResult.VertexWeight3;

   17 

   18             // the indices in to where the actual intersection occurred

   19             int index1 = rayMeshResult.VertexIndex1;

   20             int index2 = rayMeshResult.VertexIndex2;

   21             int index3 = rayMeshResult.VertexIndex3;

   22 

   23             // texture coordinates of the three vertices hit

   24             Point texCoord1 = geom.TextureCoordinates[index1];

   25             Point texCoord2 = geom.TextureCoordinates[index2];

   26             Point texCoord3 = geom.TextureCoordinates[index3];

   27 

   28             // get the final uv values based on the barycentric coordinates

   29             Point finalPoint = new Point(texCoord1.X * vertexWeight1 +

   30                                         texCoord2.X * vertexWeight2 +

   31                                         texCoord3.X * vertexWeight3,

   32                                         texCoord1.Y * vertexWeight1 +

   33                                         texCoord2.Y * vertexWeight2 +

   34                                         texCoord3.Y * vertexWeight3);

   35 

   36             MessageBox.Show(finalPoint.ToString());  

   37             //Multiply the final point with the texture size

   38 

   39         }

   40     }

   41 

   42     return HitTestResultBehavior.Continue;

   43 }

Sample project is attached :)

mapping.zip

Comments

  • Anonymous
    December 03, 2006
    This is just plain cool and I had to link to it before I pack. Lester Lobo has posted a great little