In my previous blog I talked about loading a simple non-textured SketchUp model into your Windows Phone application. This blog will deal with loading textured SketchUp models.
I did some searching about and found a textured SketchUp model online of the Moon and Earth.

Using the same DirectX/XNA Exporter Plugin from the last blog, I exported them. What you end up with is an DirectX file and a jpg file for each texture used in your model. You need to put these files in your Content project directory and make sure you include the DirectX files in the project. It’s not necessary to include the jpg files into the project, but they do need to be in the same directory.

Try to build the solution now. You’re very likely to get an error like this one.

As the error states, in the XNA framework, all Texture2D sizes must be powers of two. This is fortunately easy to fix. You can edit the jpg in MS Paint. Just select the resize tool and make the new size a power of two. You can get away with this because the vertex texture coordinates are relative to the size of the texture.

Save, close Paint, and rebuild the solution.
I created a class called TexturedMeshObject to help with the creating and drawing the mesh.
public class TexturedMeshObject
{ private Model model;
public Matrix World { get; set; } public Matrix View { get; set; } public Matrix Projection { get; set; }
public TexturedMeshObject(GraphicsDevice device,
ContentManager contentManager, string modelName)
{ model = contentManager.Load<Model>(modelName);
// Setup Default Matices
this.World = Matrix.Identity;
this.View = Matrix.CreateLookAt(
new Vector3(0f, 0f, 50f), Vector3.Zero, Vector3.Up);
this.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver4, device.Viewport.AspectRatio, 1.0f, 100.0f);
}
public void Draw()
{ Matrix[] transforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in model.Meshes)
{ foreach (IEffectMatrices effect in mesh.Effects)
{ effect.World = transforms[mesh.ParentBone.Index] * this.World;
effect.View = this.View;
effect.Projection = this.Projection;
}
mesh.Draw();
}
}
}
This class sets up default values for the world, view, and projection matrixes. The Draw() function handles the drawing of the imported mesh.
Now back to the XNA page, we add the following code to load the content.
...
// load your game content here
Matrix projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver4, graphicsDevice.Viewport.AspectRatio, 1.0f, 10000.0f);
meshes = new List<TexturedMeshObject>();
center = new Vector3(0, earthdOffset, 0);
AddPlanetoidMesh(ref projection, center, "Earth");
AddPlanetoidMesh(ref projection, new Vector3(2038.0f, earthdOffset, 0), "Moon");
AddPlanetoidMesh(ref projection, new Vector3(203.8f, earthdOffset, 0), "Moon");
...
}
private void AddPlanetoidMesh(ref Matrix projection, Vector3 initial, string meshName)
{ Matrix world = Matrix.Identity;
world = Matrix.CreateRotationX(MathHelper.ToRadians(90)) * world;
world.M41 = initial.X;
world.M42 = initial.Y;
world.M43 = initial.Z;
TexturedMeshObject mesh =
new TexturedMeshObject(graphicsDevice, contentManager, meshName);
mesh.World = world;
mesh.Projection = projection;
meshes.Add(mesh);
}
The first thing we do is create the projection matrix. Then we create a list to keep track of all our TexturedMeshObjects.
We add a helper function to help with loading our planetoid meshes. Because of how the models are exported from SketchUp we have to rotate the mesh about the X axis by 90 degrees. We also set the initial position of the planetoid, create the TexturedMeshObject, initialize the TexturedMeshObject World and Projection matrixes, and add the new TexturedMeshObject to our list of meshes.
You can see that we are adding two Moons. This is because we are initializing the Earth and the first Moon to be sized and positioned relative to how they are in real life. In real life, they are very far away from each other with the Moon (about 1/4 the diameter of the Earth) being about 30 Earth diameters away. When we display both on the Windows Phone screen at the same time, they look like two very small dots on each side of the screen. So we add another Moon that is 10 times closer to the Earth than it should be so we can see the textures of both the Earth and the Moon at the same time.
In the OnDraw() event handler we need to actually draw all of the meshes. Because most of the work is in our TexturedMeshObject, this ends up being very simple. The following code is all that’s needed.
private void OnDraw(object sender, GameTimerEventArgs e)
{ SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
foreach (var mesh in meshes)
{ mesh.Draw();
}
}
To jazz up this sample a bit, I took some code from my Mangnetometer blog to give this sample a bit of alternative reality (AR) look and feel. We’re positioning the earth and the moons a few feet north of the phone. What kind of planetoid application would this be without the moons rotating around the Earth? To do this we have to do some matrix manipulation. With the AR code and the matrix manipulation, our OnUpdate() function now looks like this.
private void OnUpdate(object sender, GameTimerEventArgs e)
{ if (useMotion)
{ var pitch = motion.CurrentValue.Attitude.Pitch;
var roll = motion.CurrentValue.Attitude.Roll;
var yaw = motion.CurrentValue.Attitude.Yaw;
Matrix matrix = Matrix.CreateRotationX(-pitch);
matrix = Matrix.CreateRotationY(-roll) * matrix;
matrix = Matrix.CreateRotationZ(-yaw) * matrix;
foreach (var mesh in meshes)
{ mesh.View = matrix;
}
Matrix world = meshes[0].World;
world = Matrix.CreateRotationY(
MathHelper.ToRadians(earthdAngle)) * world;
meshes[0].World = world;
RotatePlanetoid(meshes[1], center, earthdAngle / moonRotRatio);
RotatePlanetoid(meshes[2], center, earthdAngle / moonRotRatio);
}
}
private void RotatePlanetoid(
TexturedMeshObject mesh, Vector3 center, float angle)
{ Matrix world = mesh.World;
world.M41 -= center.X;
world.M42 -= center.Y;
world.M43 -= center.Z;
world = world * Matrix.CreateRotationZ(
MathHelper.ToRadians(angle));
world.M41 += center.X;
world.M42 += center.Y;
world.M43 += center.Z;
mesh.World = world;
}
We first compute the View matrix for the phone based upon the phone orientation and apply that matrix for all of the TexturedMeshObjects.
In the second part of this function, we actually compute the rotation of each mesh. The first mesh, the Earth, we just want to rotate in place. So we just need to take the World matrix of the Earth and rotate it by some amount. If this case, we have a private float called earthdAngle, which is set to 5. That means each call of the OnUpdate() function will rotate the Earth by 5 degrees. We have to rotate the moons a little differently. Because we are rotating them around the earth, we must first back out the initial position do the rotation and then add the initial position back in. We do this with the helper function RotatePlanetoid(). We divide the earthAngle by moonRotRatio (set to 27.3) to get the moons to rotate around the earth at the correct ratio to the Earth rotation.
Now you can use the phone to look around to see the Earth (you have to point the phone North) and the moons rotate.

In this screen shot you can see the moons are in the process of rotating to the back side of the Earth. You really have to look for the “distance to scale” moon on the far right.

In this screen shot you can see the “non-distance to scale 10 times closer” moon about to rotate between the phone and the Earth.
Other than downloading the example and running it on your phone, the best way to view this is to watch a video.