This project is read-only.

Adding 3D Models to Bing Maps

In addition to navigating around a three-dimensional model of the earth, Bing Maps 3D gives you the ability to import your own custom 3D models and place them anywhere in the virtual world.

Currently, Bing Maps supports models saved in the Wavefront object file format (file extension .obj, hereafter referred to as OBJ). This is a well-established, common format for 3D data, and models in the OBJ format can be opened, edited, and saved by almost all popular 3D modelling software, including 3D Studio, Blender, Caligari TrueSpace, and Google Sketchup Pro. The information inside an OBJ file is stored as plain ASCII text, which means you can even create and edit simple OBJ files using any basic text editor, such as Windows Notepad.
This guide will provide an overview of the structure of the OBJ format and explain the elements that can be used to create 3D models in Bing Maps. I'll then show you how to make a simple model file and import that into the Bing Maps 3D world. We'll then look at how to change the appearance of that model, using texture and lighting, and adjusting its size, position, and orientation.

Platform

  • Bing Maps Javascript Map Control v6.2
  • Bing Maps 3D (http://www.microsoft.com/Downloads/details.aspx?FamilyID=e9298080-50c4-4f2e-9fc4-4009074996ba)

Difficulty

  • Intermediate

Topics Covered

  • VEMap.SetMapMode()
  • VEMap.Import3DModel()
  • VEModelSourceSpecification
  • VEModelStatusCode
  • VEModelFormat
  • VEModelOrientation
  • VEModelScale
  • VEModelScaleUnit

 

Creating a Simple OBJ Model

Bing Maps 3D supports a subset of the OBJ file specification. The supported elements provide a syntax for the creation of coloured, textured, models created from a set of flat surfaces, which can be imported and placed anywhere in the Bing Maps 3D world. Some of the more advanced geometric features of the full OBJ standard are not supported, including the creation of curved surfaces. Furthermore, the materials used to render 3D objects in Bing Maps are limited to ambient and diffuse colour and texture. Nevertheless, with clever design, it is possible to create detailed, accurate models using the basic commands available.

In fact, when designing 3D models for web-applications, it is often the case that "less is more" – complex, high-definition models required significantly more time to download and render than simple models, and in many cases their increased accuracy offers little additional value in the sorts of applications for which Bing Maps 3D is likely to be used. Creating effective, simple low-polygon models is a discipline in itself, and very good practice in the art of good design.

Defining Geometric Vertices and Faces

Every 3D model is formed from vertices and faces.

  • A vertex (plural, vertices) is a point in three-dimensional space, specified using x, y, z coordinates. In general, the x-axis extends to the right, the y-axis extends upwards, and the z-axis extends forwards out of the screen, as shown in Figure 1.

3D_Fig1

Figure 1. x, y, and z axes in 3-dimensional space

  • A face is a single-sided, flat surface drawn between a set of vertices. A face that joins three vertices is a triangle, and a face that joins four vertices is called a quad. Faces may also join more than four vertices, in which case they are called polygons, or n-gons, however these types of faces are not supported by all 3D rendering engines, and are generally not recommended. In order to ensure compatibility across different programs, polygonal faces may be subdivided into several smaller triangular faces – a process known as triangulation.

In the OBJ file format, each vertex is listed using the v character followed by the coordinate values of that vertex listed in x, y, z order, separated by spaces. The following demonstrates the syntax required to define a single vertex located at coordinates (0,0,0):
<pre>v 0 0 0</ore>
The coordinate of each vertex in the OBJ file should be listed on a new line, with each line beginning with v. By using this list-based approach, every vertex can implicitly be assigned an index number, according to the order in which they are listed. Vertex indexes begin at 1, so the first line beginning with v defines the coordinates that represent vertex 1, the next line of coordinates will be vertex 2, and so on. It is common for all of the vertex data to be listed together in one block at the top of the OBJ file, but this does not have to be the case. The index numbering increases sequentially throughout the entire file, even when lines containing vertex data are separated by other data.
Having defined two or more vertexes, we can define a face by using the f keyword followed by the index numbers of the vertexes to which that face joins. The following example creates a triangular face joining the first three vertices listed in the file:
<pre>f 1 2 3</pre>
As with vertices, each face should be listed on a separate line. In addition to defining vertex coordinates (v), each face may optionally specify texture coordinates (vt) and normals (vn), which will be discussed later on.

Creating a Simple Box

Having considered the basic syntax required to define models in the OBJ format, let's illustrate these concepts by creating a simple box. Remember that OBJ files are just text files, so fire up your favourite text editor (Windows Notepad will do just fine), and create a new document.
We will create a model representing the box illustrated in Figure 2. This model contains 6 faces (labeled A – F), defined between 8 vertices (labeled 1 – 8). The box is 5 units wide, 3 units high, and 2 units deep. Note that the coordinate values listed in an OBJ file do not have any associated unit of measurement, so this model could represent an object 5 metres wide, 5 feet wide, or 5 miles wide! The scale of a model is determined at the point that it is imported into Bing Maps 3D, using the VEModelScale class and VEModelScaleUnit enumeration, which will be discussed later in this guide. To make life easier for yourself when creating your own models, I recommend ensuring that you base your coordinates on a standard unit of measurement (such as the metre), and that you use the same scale unit for all three axes.


18296f0303scrap

 Figure 2. A three-dimensional model of a box


To create the vertices and faces of this box, enter the following into your new text file:

#Define the x, y, z coordinates of the geometric vertices from 1 - 8

v 0.0 0.0 0.0
v 5.0 0.0 0.0
v 5.0 3.0 0.0
v 0.0 3.0 0.0
v 0.0 0.0 2.0
v 5.0 0.0 2.0
v 5.0 3.0 2.0
v 0.0 3.0 2.0

  

#Define the quad faces from A - F

f 5 6 7 8
f 6 2 3 7
f 2 1 4 3
f 1 5 8 4
f 8 7 3 4
f 1 2 6 5

Let's take a moment to review some important points from the above code listing:

  • You can include comments in a .obj file by using the hash (#) symbol at the beginning of a line. This will comment out the whole of that line.
  • The coordinates of each vertex are given as floating point numbers. Even though we are using whole units for each coordinate point in this example, I've deliberately included the decimal place to signify that a sub-unit precision can be stated.
  • I've deliberately listed the vertices of each face in counter-clockwise order, starting with the vertex at the bottom left corner as you look straight-on at the face. This is not necessary, but it does make things a lot easier when you come to mapping textures onto the faces, as we will do later in this guide.

That is all that's required to define a basic box, so save the file as box.obj, and we'll move onto how to import this box into Bing Maps.


Caution        Make sure that you save the .obj file using ANSI encoding, not Unicode (UTF-8 or UTF-16), or else Bing Maps 3D will not be able to load the file.


 

Importing the Model

Before getting into the details of how to import our model, let's first create a simple webpage that displays a Bing Maps 3D map. Create a new file and enter the following code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Importing 3D Models</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript"  src=http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.3></script>
<script type="text/javascript">
   var map = null;
   function getMap() {
     map = new VEMap('divMap');
     map.LoadMap(new VELatLong(52.6, 1.3), 19, VEMapStyle.Aerial, false, VEMapMode.Mode3D);
   }

   function disposeMap() {
     map.Dispose();
   }
</script>

</head>
<body onload="getMap();" onunload="disposeMap();">
<div id="divMap" style="position: relative; width: 800px; height: 600px;"></div>
</body>
</html>

There's nothing unusual in this code listing – we simply use the LoadMap() method to initialize a new Bing map, and place it in the divMap element on the page. Notice that we specify the VEMapMode.Mode3D enumeration as the fifth parameter of the LoadMap() method, so that the map initially loads up in 3D mode. Save this file using the .htm file extension (for simplicity, I'll assume that you name the file HTMLPage.htm) and load it in your browser. The page should appear as shown in Figure 3.


Tip           For details on all the parameters that can be supplied to the LoadMap() method, please see http://msdn.microsoft.com/en-us/library/bb412546.aspx


clip_image002

Figure 3. A basic 3D map


Note        If you do not have the Bing Maps 3D control installed on your computer, rather than seeing Figure 3 you will receive a message prompting you to download the 3D application first. Once you have downloaded and installed the application, reload the page to see the 3D map.


Now that we have our basic 3D template, let's add the code to load our box model.

Specifying the Model Source

To specify the properties of our model, we can define a new VEModelSourceSpecification as follows:

var modelSpec = new VEModelSourceSpecification(
    VEModelFormat.OBJ,
    location.href.substring(0, location.href.lastIndexOf('/')) + "/box.obj"
  );

This constructor takes two parameters:

  • The first parameter defines the format of the model being loaded. Currently only OBJ files are supported, specified using the VEModelFormat.OBJ enumeration.
  • The second parameter is the URL of the model file – in this case, the box.obj file we just created.

Note that the URL supplied in the second parameter must be a full URL to the .obj model file, not simply a relative link to the file. For this reason, I am using a substring of the location.href property to determine the URL of the current page, and appending the box.obj filename onto the end of the string. This assumes that the box.obj file is saved in the same directory as this html file itself. If this is not the case, you must specify the subdirectory in which it is located, or instead list the full URL to the box.obj file.

The VEModelSourceSpecification constructor also accepts an optional third parameter, specifying a shape layer into which the imported model will be added. Omitting this parameter, as in this example, means that the model will be added to the base layer of the map.

Importing the Model

Having defined a model specification, we load that model onto the map using the Import3DModel() method,as shown below:

map.Import3DModel(
  modelSpec,
  onModelLoad,
  new VELatLong(52.6, 1.3),
  null,
  null
);

This method accepts the model specification that we just created, together with a VELatLong() object representing the location at which to place the model. We have also provided the name of a callback method, onModelLoad, which will be called after the Import3DModel() method has finished (we'll create this function shortly). The final two parameters of Import3DModel() are used to specify the orientation and scale of the model, which we'll look at later on in this guide. For now, I've left both of these as null.

The Callback

The onModelLoad() callback receives two arguments from the Import3DModel() method. The first argument is a VEShape() representing a pushpin associated with the model, and the second is a status code to indicate whether or not the model was imported correctly. In our code, we'll check the value of this status code and display an appropriate message using the javascript alert method, as follows:

function onModelLoad(model, status) {
  if (status == VEModelStatusCode.Success) {
    alert("Successfully loaded 3D model.");
  }
  if (status == VEModelStatusCode.InvalidURL) {
    alert("Invalid URL specified.");
  }
  if (status == VEModelStatusCode.Failed) {
    alert("Failed to load 3D model.");
  }

Putting It All Together

The final step is to actually call the code that loads the 3d model from our page. We'll do this as soon as the map has finished loading, by placing the load3DModel() method in the onLoadMap callback of the LoadMap() method.

The final code listing is shown as follows:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
   <title></title>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   <script type="text/javascript" src="
http://dev.virtualearth.net/mapcontrol/mapcontrol.ashxv=6.3"></script>
   <script type="text/javascript">
     var map = null;
     function getMap() {
       map = new VEMap('divMap');
       map.onLoadMap = load3DModel;
       map.LoadMap(new VELatLong(52.6, 1.3), 19, VEMapStyle.Aerial, false, VEMapMode.Mode3D);
     }

     function disposeMap() {
       map.Dispose();
     }

     function load3DModel() {
       var modelSpec = new VEModelSourceSpecification(
         VEModelFormat.OBJ,
         location.href.substring(0, location.href.lastIndexOf('/')) + "/box.obj"
       );
       map.Import3DModel(modelSpec, onModelLoad, new VELatLong(52.6, 1.3), null, null);
     }

     function onModelLoad(model, status) {
       if (status == VEModelStatusCode.Success) {
         alert("Successfully loaded 3D model.");
       }
       if (status == VEModelStatusCode.InvalidURL) {
        alert("Invalid URL specified.");
      }
      if (status == VEModelStatusCode.Failed) {
        alert("Failed to load 3D model.");
      }
    }
  </script>
</head>
<body onload="getMap();" onunload="disposeMap();">
  <div id="divMap" style="position: relative; width: 800px; height: 600px;"></div>
</body>
</html>

Save the file, and then load it in your browser. After the page has loaded, you should receive a javascript alert message stating "Successfully loaded 3D model." Shortly afterwards, you will see the box rendered in the center of the map. Try zooming in and rotating around the model. It should appear as shown in Figure 4.

clip_image002[8]

Figure 4. A basic box in Bing Maps 3D

 

How Many Sides Does a Face Have?

All of the faces of a 3d model, specified using the f command in the OBJ file, are single-sided. This can be hard to conceptualise, because in real-life, one-sided surfaces don't really exist. Perhaps the closest analogy is a one-way mirror: if you stand in front of the mirror you can see yourself, but if you were to stand on the opposite side of the mirror, you could see right through it. The same thing happens in Bing Maps 3D: if you look at the face of a model from the wrong direction, it will appear to be completely invisible and you can see right through it.

The significance of this is that when we define a face, we must not only describe the vertices to which it connects, but also which direction it faces. In order to model a solid object, we therefore need to make sure that all of the sides face "outwards". There are two ways of doing so:

  • When creating a face, in addition to listing the vertex coordinates, each point may have an associated vertex normal. Vertex normals are 3d vectors that describe the normal direction at any point, used to determine which way the face faces. Vertex normals are listed in an .obj file using the vn command and, like vertex coordinates, are listed in x,y,z order. The following vertex normal specifies a normal that extends along the positive x axis:

     vn 1.0 0.0 0.0

            To specify the vertex normal, vn,  associated with each vertex, v,  when defining a face, you list pairs of vertex coordinates and vertex normals separated by double oblique strokes following the f command, as shown below:

     f v1//vn1 v2//vn2 v3//vn3

  • If no vertex normals are supplied for a given face, then the f command will determine the normal of the face implicitly based on the order in which the v vertexes are supplied. The rule followed is that, if you were to imagine yourself looking straight on at a face pointing towards you, the vertices would be listed in counter-clockwise order.

In the box created in this example, we will correct the normality of our faces by stating each coordinate in counter-clockwise order, starting at the bottom left hand corner of each face. If you find that some of the sides of your box appear to be see-through, or exhibit other strange rendering behavior, you may want to check that you listed the vertexes of each face in the correct order.

 

Adding Colour and Texture to the Model

In addition to the purely geometric properties of a model described by the v and f keywords, the OBJ file format also allows you to specify materials - colours and textures that can be applied to alter the appearance of a model.

The materials associated with a model are stored in a separate text file, normally using the same name as the .obj file to which they relate but using the file extension .mtl. Material files must be located either in the same base location as, or in a subfolder of, the associated .obj model file. To create a materials file containing colours and textures to apply to our box model, we will create a new file called box.mtl, and save it in the same directory as both box.obj and HTMLPage.htm.

A material file may contain many different materials. Each material begins with a new line starting with the newmtl command, followed by the name of the material that follows. The material name can be any length, but should not contain any spaces. Following the name comes a series of properties defining the material itself.

Materials are defined using a combination of colour maps and texture maps.

  • Colour maps consist of three values representing the red, green, and blue components of the overall reflectivity of the material.
  • Texture maps specify the name of an image file (e.g. a .jpg, .gif, or .png file) that represents the textured surface of a material.

When rendered, the texture map is multiplied by the corresponding colour map to produce the overall appearance of a material. Bing Maps 3D supports two sorts of texture and colour maps, representing ambient and diffuse properties of a material:

  • Ambient lighting refers to the general background level of illumination in the environment, independent of any particular light source. You can think of it as modeling the general amount of daylight in a scene, which comes from all directions and is scattered in all directions. The ambient colour and texture map of a material are defined using the Ka and map_Ka keywords, respectively.
  • Diffuse lighting comes from a particular light source, and the intensity with which it reflects off a surface is dependent on the angle at which the light strikes the face. Once reflected from a surface, diffuse light, like ambient light, is scattered equally in all directions. The diffuse colour and texture map of a material are defined in a .mtl file using Kd and map_Kd, respectively

To compare the differences between ambient and diffuse materials, let's experiment by creating two simple materials. First, let's create a diffuse green material. Add the following to the new .mtl file:

newmtl diffuse_green
Kd 0.0000 1.0000 0.0000

Colour maps are specified using three values, each ranging from 0 – 1, representing the red, green, and blue reflectivity values of the associated material. Since we are using the Kd command, the above code represents a diffuse green material.

To apply this new material onto our box, we need to go back to the box.obj file. At the top of the .obj file, we first need to include a reference to the box.mtl material library using the mtllib statement, like this:

mtllib box.mtl

Then, to apply any of the materials from the box.mtl file onto the faces of a model, use the usemtl file to specify the name of a material as it is listed in the materials file. This material will then be applied to all faces subsequently defined in the .obj file, until the next usemtl statement is encountered. So, to apply the diffuse_green material to every face of the box, we can include the following statement immediately before the list of faces:

usemtl diffuse_green

The box.obj file now appears as follows:

# Define the vertexes
v 0.0 0.0 0.0
v 5.0 0.0 0.0
v 5.0 3.0 0.0
v 0.0 3.0 0.0
v 0.0 0.0 2.0
v 5.0 0.0 2.0
v 5.0 3.0 2.0
v 0.0 3.0 2.0

# Load the material file
mtllib box.mtl

# Use the diffuse_green material
usemtl diffuse_green

# Define the faces
f 5 6 7 8
f 6 2 3 7
f 2 1 4 3
f 1 5 8 4
f 8 7 3 4
f 1 2 6 5

Save the file and reload the HTMLPage.htm page in your browser. Notice the appearance of the box. Now go back to the box.mtl file and define a new ambient green material by adding the following to the end of the file:

newmtl ambient_green
Ka 0.0000 1.0000 0.0000

To apply this material to the box, remember to go back to the box.obj file and alter the usemtl statement to use the ambient_green material rather than the diffuse_green material. Save the file and then reload your browser. Figure 5 illustrates the difference between diffuse and ambient materials on two cubes shown side-by-side.

clip_image002[10]

Figure 5. Comparing diffuse material (left) and ambient material (right)

The final parameter that can be used to affect the appearance of a material is the dissolve factor – in other words, the transparency of the material. Dissolve factors range from 0.0 to 1.0, with 1.0 being fully opaque, while 0.0 is fully transparent. The default value is 1.0.

 

Changing Size and Orientation of the Model

Remember from earlier that the Import3dModel() method accepts two optional parameters specifying the orientation and scale of the model. Currently, null values have been passed for both of these, leaving the model at default scale and orientation, in which one unit of measure equals one metre, and the "front" of the box faces north. In this section, we'll look at how we can override these defaults.

Orientation

To specify the orientation of a model, you can create an instance of the VEModelOrientation class. The constructor requires three arguments – all of which are floating-point values in decimal degrees, as follows:

  • heading: The rotation of the model about the z-axis, in a counter-clockwise direction as seen facing along the z-axis away from the origin.
  • tilt: The rotation of the model about the x-axis, in a counter-clockwise direction as seen facing along the positive x-axis away from the origin.
  • roll: The rotation of the model about the y-axis, in a counter-clockwise direction as seen facing along the positive y-axis away from the origin.

The following code illustrates the VEModelOrientation required to rotate the box to face North-East, tilted up 10 degrees towards the sky:

new VEModelOrientation(0, 45, 20)

Scale

By default, each "unit" specified in the OBJ file corresponds to one metre. The front face of the box in this example extends from the vertex at (0, 0, 0) to (5, 3, 0) – so it is 5 metres wide and 3 metres high. You can adjust this so that each unit corresponds to a different unit of measure. For example, the following code transforms the box to be 5 feet wide by 3 feet tall.

new VEModelScale(VEModelScaleUnit.Feet)

Alternatively, you can specify scale factors by which each of the X, Y, and Z dimensions should be multiplied. The following code listing demonstrates how to scale the model so that it has normal width and depth (5 metres and 2 metres), but double height (6 metres):

new VEModelScale(1, 1, 2)

Last edited Oct 17, 2010 at 5:08 PM by crpietschmann, version 7

Comments

No comments yet.