java.net: Wiki

The Source for Java Technology Collaboration


 <<O>>  Difference Topic ProjectWonderlandNewCellPart4 (5 - 22 Aug 2008 - Main.uwekamper)
Line: 1 to 1
 
META TOPICPARENT name="ProjectWonderlandNewCellPart3"
Home | Changes | Index | Search | Go
<-- This creates the navigation links to :  Home | Help | Index | etc.  -->
Line: 209 to 209
 via the Wonderland server, making sure that all users see the same shape. In the final tutorial in this series, you will learn how to communicate with the Wonderland server and synchronize the state of your cell among many users.
Added:
>
>
Part 5 - Complete the cell
 \ No newline at end of file

 <<O>>  Difference Topic ProjectWonderlandNewCellPart4 (4 - 06 Jul 2008 - Main.jslott)
Line: 1 to 1
 
META TOPICPARENT name="ProjectWonderlandNewCellPart3"
Home | Changes | Index | Search | Go
<-- This creates the navigation links to :  Home | Help | Index | etc.  -->

 <<O>>  Difference Topic ProjectWonderlandNewCellPart4 (3 - 03 Jul 2008 - Main.jslott)
Line: 1 to 1
 
META TOPICPARENT name="ProjectWonderlandNewCellPart3"
Home | Changes | Index | Search | Go
<-- This creates the navigation links to :  Home | Help | Index | etc.  -->

Extending Project Wonderland by Creating New Cell Types - Part IV

by Jordan Slott (jslott@dev.java.net)
Deleted:
<
<
UNDER CONSTRUCTION -- CHECK BACK SOON!
 
<-- Your JavaDesktop? article goes here. Please try to include at least one sentence describing this topic. -->
<-- Also please try to include at least one sentence describing where each link goes. -->
<-- Please make sure some other page points to your new article so that others can find it! -->
Line: 16 to 14
 In this tutorial, you will enhance the simple new cell type you have created in Part 1, Part 2, and Part 3 of this tutorial series by
Changed:
<
<
registering a listener to receive keyboard and mouse events. You will modify your shape cell type to switch between
>
>
registering a listener to receive mouse button events. You will modify your shape cell type to switch between
 displaying a sphere and cube whenever the user clicks on your cell.

This tutorial is designed for the v0.3 and v0.4 releases of Project Wonderland.

Line: 29 to 27
 Part 2, and Part 3 of this tutorial series. You will be extending the functionality you implemented there.
Changed:
<
<

Loading the Texture on the Client

>
>

Handling user input events in ShapeCell.java

In this tutorial, your modifications will occur entirely in the ShapeCell.java file. You should use your existing code from previous tutorials as a base, even though you'll be gutting several of the methods in there. Conceptually, the changes are straightforward: you'll register for mouse events and when the user clicks the mouse button, you will render a sphere if the current shape is a cube, and visa versa.

To start, add the following import statements to the top of your file. (Although it is always good at the end to have your IDE fix all of your import statements for you):

import org.jdesktop.lg3d.wg.event.LgEvent;
import org.jdesktop.lg3d.wg.event.LgEventListener;
import org.jdesktop.lg3d.wg.event.MouseButtonEvent3D;
import org.jdesktop.lg3d.wg.internal.j3d.j3dnodes.J3dLgBranchGroup;
 
Changed:
<
<
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, make the following modifications to the class member variable declarations at the top of your class definition:
 
Changed:
<
<
import org.jdesktop.j3d.utils.scenegraph.ImageComponent2DURL; import org.jdesktop.lg3d.wonderland.scenemanager.AssetManager; import java.net.URL; import java.net.MalformedURLException;
>
>
J3dLgBranchGroup? bg = null; private URL baseURL = null; private String shapeType = null; private String texturePath = null;
 
Changed:
<
<
Next, you'll modify the setup() method. In this method, you'll perform the following additional tasks:
  1. Fetch the baseURL property from the ShapeCellSetup class.
  2. Fetch the texturePath property from the ShapeCellSetup class.
  3. Load the texture using the Wonderland client-side asset manager.
  4. Create instances of the Java 3D Appearance and Texture objects.
  5. Modify the creation of the Sphere and Box object to include your newly-created Appearance.
>
>
Note that the type of the variable bg changed from BranchGroup to J3dLgBranchGroup. The J3dLgBranchGroup object comes as part of the Looking Glass 3D platform upon which Wonderland is built. This change is needed to capture keyboard and mouse events.
 
Changed:
<
<
For the first two steps, you can add the following code beneath the initial SceneGraphUtil.setCapabilitiesGraph() call. Hopefully these steps should be self-explanatory.
>
>
Also, you added class member variables to store several properties passed from the server upon creating (or reconfiguring) the cell: the base URL for artwork assets, the relative path of the shape texture, and the type of the shape to create.

Next, you'll rework the setup() method quite a bit. The changes are (mostly) relatively minor, but it is probably best to completely replace your existing code for this method. First:

 
    @Override
    public void setup(CellSetup setupData) {
Deleted:
<
<
bg = new BranchGroup?(); bg.setCapability(BranchGroup?.ALLOW_CHILDREN_EXTEND); bg.setCapability(BranchGroup?.ALLOW_CHILDREN_WRITE); SceneGraphUtil?.setCapabilitiesGraph(bg, false);
 
Deleted:
<
<
URL baseURL;
  String url = ((ShapeCellSetup?) setupData).getBaseURL(); try {
Changed:
<
<
baseURL = new URL(url);
>
>
this.baseURL = new URL(url);
  } catch (MalformedURLException? ex) { System.err.println("Failed to load slide, bad baseURL " + url); return; }
Added:
>
>
this.texturePath = new String(((ShapeCellSetup?)setupData).getTexturePath()); this.shapeType = new String(((ShapeCellSetup?)setupData).getShapeType());
 
Changed:
<
<
String texturePath = ((ShapeCellSetup?)setupData).getTexturePath();
>
>
this.setup(); }
 
Changed:
<
<
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).
>
>
This setup() method simply takes the information passed from the server via the ShapeCellSetup object and sets the values of the class member variables. Note that it calls another setup() method (which takes no arguments) before it returns. Implement this new setup() method as follows:
 
Changed:
<
<
ImageComponent2DURL? ic = AssetManager?.getAssetManager().createImageComponent2DURL(baseURL, texturePath);
>
>
public void setup() { bg = new J3dLgBranchGroup?();

bg.setCapabilities(); bg.setMouseEventEnabled(true); bg.setMouseEventSource(MouseButtonEvent3D?.class, true); bg.addListener(new MouseButtonListener?());

 
Changed:
<
<
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.
>
>
bg.setCapability(BranchGroup?.ALLOW_CHILDREN_EXTEND); bg.setCapability(BranchGroup?.ALLOW_CHILDREN_WRITE); SceneGraphUtil?.setCapabilitiesGraph(bg, false);

ImageComponent2DURL? ic = AssetManager?.getAssetManager().createImageComponent2DURL(baseURL, this.texturePath);

 
Deleted:
<
<
  Appearance app = new Appearance(); Texture2D? tex = new Texture2D?(Texture.BASE_LEVEL, Texture.RGBA, ic.getWidth(), ic.getHeight()); tex.setImage(0, ic); app.setTexture(tex);
Deleted:
<
<
 
Changed:
<
<
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) {
>
>
if (this.shapeType.compareTo("BOX") == 0) {
  Box box = new Box(1, 1, 1, Box.GENERATE_TEXTURE_COORDS, app); SceneGraphUtil?.setCapabilitiesGraph(box, false); bg.addChild(box); }
Changed:
<
<
else if (((ShapeCellSetup?)setupData).getShapeType().compareTo("SPHERE") == 0) {
>
>
else if (this.shapeType.compareTo("SPHERE") == 0) {
  Sphere sphere = new Sphere(1, Sphere.GENERATE_TEXTURE_COORDS, app); SceneGraphUtil?.setCapabilitiesGraph(sphere, false); bg.addChild(sphere);
Line: 123 to 119
  }
Changed:
<
<
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.
>
>
This new setup() method is similar to the setup() method you defined in earlier tutorials with a couple of differences. First, it creates an J3dLgBranchGroup object instead of a BranchGroup object. It also uses the values for the base asset URL, relative texture path, and shape type stored in the class member variables. This is done so that (as shown below) you can change the value of shapeType and have this setup() method redraw your scene.

The most important change is the addition of the following lines of code near the top of the method:

        bg.setCapabilities();
        bg.setMouseEventEnabled(true);
        bg.setMouseEventSource(MouseButtonEvent3D.class, true);
        bg.addListener(new MouseButtonListener());

These four lines of code tell the J3dLgBranchGroup object that you want to receive any mouse button events that happen inside of the cell. Although not explicitly covered in this tutorial, listening for keyboard events is also done in a similar way. Consult the J3dLGBranchGroup Javadoc for more information. Your listener class, MouseButtonListener, which you define below, receives the mouse events.

Note: You may leave the existing reconfigure() method as defined in previous tutorials as-is.

Implementing the mouse listener

The remaining step is to implement the class that will handle the mouse events, MouseButtonListener. You can make this class an inner class of the ShapeCell class, to save you from passing some needed information between the two. At the end of the ShapeCell class (but still within the class definition), add:

    class MouseButtonListener implements LgEventListener {

        /** Default constructor */
        public MouseButtonListener() {}

        public void processEvent(LgEvent evt) {
            if (evt instanceof MouseButtonEvent3D) {
                MouseButtonEvent3D mbEvent = (MouseButtonEvent3D)evt;
                if (mbEvent.isClicked() == true) {
                    System.out.println("MOUSE BUTTON EVENT: " + evt.toString());
                    
                    shapeType = (shapeType.compareTo("BOX") == 0) ? "SPHERE" : "BOX";                    
                    if (bg != null) {
                        bg.detach();
                    }
                    setup();
                }
            }
        }

        @SuppressWarnings("unchecked")
        public Class<LgEvent>[] getTargetEventClasses() {
            return new Class[] { MouseButtonEvent3D.class };
        }
    } 

It is the processEvent() method that actually handles the mouse button event. The mouse button event is represented by MouseButtonEvent3D, a subclass of LgEvent. After it checks that the event you have received is indeed a mouse button event, the processEvent() method checks if the mouse button was clicked (your listener will also receive events for mouse button presses and releases, which you ignore). If the mouse button was clicked, you then change the shape drawn in the cell, detach the current shape's branch group from the scene, and ask the setup() method to redraw the shape in the scene.

Finally, the getTargetEventClasses() method simply returns an array of event classes that your listener is interested in-- in this case, MouseButtonEvent3D.

 

Running Your New Shape Cell

Line: 138 to 197
 % ant run
Changed:
<
<
Whenever you click on the box or sphere, it should change to the other shape type.
>
>
Whenever you click on the box or sphere, it should change to the other shape type. Note that you should keep a steady hand when clicking the mouse button. If you move the mouse even slightly, then a mouse button click event is not registered!
 

Next Steps

Deleted:
<
<
In the next and final 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.
 \ No newline at end of file
Added:
>
>
The code you wrote for this tutorial does have a flaw: any time you click on the shape to change the type of shape drawn, only you can see the new shape. Other avatars in the world looking at the shape will not see your changes. In order for this to be done, you must communicate the shape change to all other users via the Wonderland server, making sure that all users see the same shape. In the final tutorial in this series, you will learn how to communicate with the Wonderland server and synchronize the state of your cell among many users.

 <<O>>  Difference Topic ProjectWonderlandNewCellPart4 (2 - 03 Jul 2008 - Main.jslott)
Line: 1 to 1
 
META TOPICPARENT name="ProjectWonderlandNewCellPart3"
Home | Changes | Index | Search | Go
<-- This creates the navigation links to :  Home | Help | Index | etc.  -->
Line: 14 to 14
 

Purpose

Changed:
<
<
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.
>
>
In this tutorial, you will enhance the simple new cell type you have created in Part 1, Part 2, and Part 3 of this tutorial series by registering a listener to receive keyboard and mouse events. You will modify your shape cell type to switch between displaying a sphere and cube whenever the user clicks on your cell.
 This tutorial is designed for the v0.3 and v0.4 releases of Project Wonderland.
Line: 25 to 25
 

Prerequisites

Changed:
<
<
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:

    private String baseURL = null;
    private String texturePath = null;

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:

    <void property="texturePath">
      <string>default_building/MountainPicture.png</string>
    </void>

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());
        setup.setTexturePath(this.texturePath);
        return setup;
    }

You likely also need to add this import statement to the top of your ShapeCellGLO.java file:

import org.jdesktop.lg3d.wonderland.config.common.WonderlandConfig;

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:

    this.texturePath = setup.getCellSetup().getTexturePath();
>
>
Before completing this tutorial, you should have already successfully completed Part 1, Part 2, and Part 3 of this tutorial series. You will be extending the functionality you implemented there.
 

Loading the Texture on the Client

Line: 241 to 127
 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.
Changed:
<
<

Running With Your New Textured Shape

>
>

Running Your New Shape Cell

 To recompile and re-run both the Wonderland server and client, in two separate terminal windows:
Line: 252 to 138
 % ant run
Changed:
<
<
If you chose a Box to be displayed, your Wonderland world should look like this:

>
>
Whenever you click on the box or sphere, it should change to the other shape type.
 

Next Steps


 <<O>>  Difference Topic ProjectWonderlandNewCellPart4 (1 - 02 Jul 2008 - Main.jslott)
Line: 1 to 1
Added:
>
>
META TOPICPARENT name="ProjectWonderlandNewCellPart3"
Home | Changes | Index | Search | Go
<-- This creates the navigation links to :  Home | Help | Index | etc.  -->

Extending Project Wonderland by Creating New Cell Types - Part IV

by Jordan Slott (jslott@dev.java.net)

UNDER CONSTRUCTION -- CHECK BACK SOON!

<-- Your JavaDesktop? article goes here. Please try to include at least one sentence describing this topic. -->
<-- Also please try to include at least one sentence describing where each link goes. -->
<-- Please make sure some other page points to your new article so that others can find it! -->
<-- For more on how to write Javapedia articles please read the WritingArticles? page. -->

Purpose

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:

    private String baseURL = null;
    private String texturePath = null;

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:

    <void property="texturePath">
      <string>default_building/MountainPicture.png</string>
    </void>

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());
        setup.setTexturePath(this.texturePath);
        return setup;
    }

You likely also need to add this import statement to the top of your ShapeCellGLO.java file:

import org.jdesktop.lg3d.wonderland.config.common.WonderlandConfig;

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:

    this.texturePath = setup.getCellSetup().getTexturePath();

Loading the Texture on the Client

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:

import org.jdesktop.j3d.utils.scenegraph.ImageComponent2DURL;
import org.jdesktop.lg3d.wonderland.scenemanager.AssetManager;
import java.net.URL;
import java.net.MalformedURLException;

Next, you'll modify the setup() method. In this method, you'll perform the following additional tasks:

  1. Fetch the baseURL property from the ShapeCellSetup class.
  2. Fetch the texturePath property from the ShapeCellSetup class.
  3. Load the texture using the Wonderland client-side asset manager.
  4. Create instances of the Java 3D Appearance and Texture objects.
  5. 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 and final 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.


Topic ProjectWonderlandNewCellPart4 . { View | Diffs r5 < r4 < r3 < r2 | More }
 XML java.net RSS