In this tutorial, you will extend the simple new cell type you created in Part 1 and
Part 2 of this tutorial series by texturing the basic geometric shape that is
drawn in the cell. You will learn how to interact with the asset manager that comes part of the Project Wonderland
client software to download the texture (a .png file) from the artwork repository.
This tutorial is designed for the v0.3 and v0.4 releases of Project Wonderland.
Expected Duration: 30 minutes
Prerequisites
Before completing this tutorial, you should have already successfully completed Part 1
and Part 2 of this tutorial series. You will be extending the functionality you implemented
there.
Artwork Repositories
All artwork in Project Wonderland--whether they be geometric models that were created using a 3rd party modeling tool such
as Maya or Blender or textures created using a standard graphics drawing package--are downloaded by the Wonderland client
from a central artwork repository. The artwork that comes with the Project Wonderland demo world is available via a public
art server at: http://192.18.37.42/trunk/lg3d-wonderland-art/compiled_models/. Go ahead and point your web browser to this
URL, you should see directories containing the models and textures.
In this tutorial, you'll be texturing your basic shape with a picture of a mountain landscape (textures/default_building/MountainPicture.jpg).
You may want to create your own image to use as a texture--in this case, you'll need to setup your own artwork repository.
Please see these instructions on how to configure Wonderland to use your own artwork repository.
High-Level Design
You will enhance the custom shape cell to texture the sphere or box with an image from an artwork repository. The relative
path (within the repository) of the texture will be specified in the cell's XML file within WFS. The path of the texture is then
communicated to the client-side cell class that loads the image using the asset management facilities provided by Project
Wonderland and then sets the shape's texture.
Adding Attributes to the ShapeCellSetup Class
First, you need to make some minor enhancements to the ShapeCellSetup class--this class, as you may recall, represents
the cell type-specific configuration information that is communicated from the server to the client. In ShapeCellSetup.java,
add the following two attributes near the top of the class:
Since this class follows the Java Bean pattern, you must also add the corresponding setter and getter methods for these two
new properties:
public String getBaseURL() {
return baseURL;
}
public void setBaseURL(String baseURL) {
this.baseURL = baseURL;
}
public void setTexturePath(String texturePath) {
this.texturePath = texturePath;
}
public String getTexturePath() {
return this.texturePath;
}
The baseURL parameter gives the URL of the artwork repository. You do not set this within WFS, rather your server-side
cell class (ShapeCellGLO) sets this parameter based upon Wonderland configuration files. The texturePath property
gives the relative path of the texture file within the artwork repository, and is set within your WFS cell file.
Specifying the Texture Path in shape-wlc.xml
While we are on the subject of configuration, now's a good time to add the path of the texture to your shape-wlc.xml file
in your WFS. Within the cellSetup property, beneath the entry for shapeType, add the following lines:
Note that this relative path excludes the textures/ directory parent--when you load this texture file via Wonderland's asset
manager, it automatically prepends textures/ before the value of the texturePath property.
Modify the ShapeCellGLO Class
Changes to the server-side ShapeCellGLO class are relatively simple: you just need to store the texturePath property in
a member variable and populate the ShapeCellSetup class when needed. First, add a member variable near the top of
the class to store the texture path:
private String texturePath = null;
Next, when you create a new ShapeCellSetup class in the getSetupData() method, set the value of the texture path property
from this class. You will also need to set the value of the baseURL property in the ShapeCellSetup class. The value of this
property is obtained from the WonderlandConfig.getBaseURL() method. This method returns the value of the
wonderland.art.url.base property (if using a remote artwork repository) or the wonderland.art.url.local property (if using an
artwork repository stored locally on disk) defined in Wonderland's configuration files. (Strictly speaking, the CellGLO superclass
contains a protected member variable called baseUrl with this value, however, this tutorial code calls
WonderlandConfig.getBaseURL() directly to highlight where the value actually comes from).
Your getSetupData() method should now look like this:
@Override
public ShapeCellSetup getSetupData() {
ShapeCellSetup setup = new ShapeCellSetup();
setup.setShapeType(this.shapeType);
setup.setBaseURL(WonderlandConfig.getBaseURL() + "/textures");
setup.setTexturePath(this.texturePath);
return setup;
}
You likely also need to add this import statement to the top of your ShapeCellGLO.java file:
Finally, for the ShapeCellGLO class, you need to modify the setupCell() method to fetch the value of the texturePath property
specified in the WFS file and set the member variable, this.texturePath. Simply add the following line after the
this.shapeType = .... line:
Your ShapeCell class that resides on the client-side needs modification--in fact, it's where all of the major changes go. First,
add some imports that you'll need for later:
Next, you'll modify the setup() method. In this method, you'll perform the following additional tasks:
Fetch the baseURL property from the ShapeCellSetup class.
Fetch the texturePath property from the ShapeCellSetup class.
Load the texture using the Wonderland client-side asset manager.
Create instances of the Java 3D Appearance and Texture objects.
Modify the creation of the Sphere and Box object to include your newly-created Appearance.
For the first two steps, you can add the following code beneath the initial SceneGraphUtil.setCapabilitiesGraph() call.
Hopefully these steps should be self-explanatory.
@Override
public void setup(CellSetup setupData) {
bg = new BranchGroup();
bg.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
bg.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
SceneGraphUtil.setCapabilitiesGraph(bg, false);
URL baseURL;
String url = ((ShapeCellSetup) setupData).getBaseURL();
try {
baseURL = new URL(url);
} catch (MalformedURLException ex) {
System.err.println("Failed to load slide, bad baseURL " + url);
return;
}
String texturePath = ((ShapeCellSetup)setupData).getTexturePath();
Next, load the texture using the Wonderland's asset manager. The asset manager checks whether
the texture is already present in the local cache (via checksums) and uses the cached copy if so.
Otherwise, it loads the texture from baseURL + "textures/" + texturePath. (The method automatically
inserts the "textures/" part). This method returns an object of type ImageComponent2DURL; it's a
subclass of Java 3D's ImageComopnent2D class, that is used to represent textures, among other
things. Using this method to load the texture also automatically updates the window in Wonderland
that pops up and displays the status of assets that are being download. The loading itself takes
place asynchronously--although the method returns quickly, the shape remains texture-less until
it has finished loading, at which time the texture will automatically appear. (In our case here, with a
suitably quick-enough internet connection, it may appear that the shape is textured instantaneously).
ImageComponent2DURL ic = AssetManager.getAssetManager().createImageComponent2DURL(baseURL, texturePath);
In the fourth step, you will need to create two objects: an Appearance object and a Texture object that will control the
appearance of your basic shapes. These two classes contain lots of configuration features to control the appearance of your
shape. In this tutorial, you will simply set the texture for the shape. It's beyond the scope of the tutorial to examine all of the
options available in the Appearance and Texture classes. Please visit the complete
Java 3D API Documentation for more details about this API.
Appearance app = new Appearance();
Texture2D tex = new Texture2D(Texture.BASE_LEVEL, Texture.RGBA, ic.getWidth(), ic.getHeight());
tex.setImage(0, ic);
app.setTexture(tex);
Note: It may be required that the width and height of your image be a power of 2. (Our texture,
MountainPicture.png has dimensions that are powers of 2). Please refer to the Java 3D API Documentation (specifically,
the Texture2D for more details). The second argument to the constructor of the Texture2D class takes the format that
matches the texture image--in our case, RGBA (RGB with an alpha blending component). Another common format is RGB.
Finally, you will need to modify how you instantiate your Sphere and Box objects. You simply use a different constructor
for each that now takes the Appearance object you just created. The remainder of your setup() method should
look like this:
if (((ShapeCellSetup)setupData).getShapeType().compareTo("BOX") == 0) {
Box box = new Box(1, 1, 1, Box.GENERATE_TEXTURE_COORDS, app);
SceneGraphUtil.setCapabilitiesGraph(box, false);
bg.addChild(box);
}
else if (((ShapeCellSetup)setupData).getShapeType().compareTo("SPHERE") == 0) {
Sphere sphere = new Sphere(1, Sphere.GENERATE_TEXTURE_COORDS, app);
SceneGraphUtil.setCapabilitiesGraph(sphere, false);
bg.addChild(sphere);
}
cellLocal.addChild(bg);
}
In the constructor of the Box and Sphere classes, we ask the class to generate the texture coordinates.
If you are creating your own geometry (e.g. using GeomtetryArray), you will need to generate the texture
coordinates yourself. Once again, a detailed discussion of Java 3D is beyond the scope of this tutorial.
Running With Your New Textured Shape
To recompile and re-run both the Wonderland server and client, in two separate terminal windows:
% ant run-sgs
% ant run
If you chose a Box to be displayed, your Wonderland world should look like this:
Next Steps
In the next tutorial in this series, you will learn how your cell can capture keyboard and
mouse input. Using it, you'll also learn how to synchronize the state of your cell among many
clients.
Part 4 - Handle input events for your cell Part 5 - Complete the cell