Declarative terrain setup for PTS

Procedural Terrain Setup now can load terrain setups from XML files.  This new feature supports some broad, new use cases.



This works just by applying the J2SE XMLDecoder to Procedural Terrain System, due to PTS’s instance-based OOD.  The actual coding to accommodate consisted only of the loader code (as shown and explained below), adding a getter or two, and adjusting method visibilities.  The config files are hand written (easy to do if you start from a sample).  I have not yet implemented writing of the configs from apps, because I don’t yet know if there are any realistic needs for that.



You can see a sample of the very simple yet powerful syntax below.  The rules are nicely summarized in the J2SE spec here.



  • Create and tune your terrain by adjusting settings in your editor.  No recompilation necessary.

  • Saved Terrain configurations are very durable compared to Java serialization in that they are not tightly bound to the Java or application implementation.  The higher load time (compared to load of binary .ser files) for these small files is insignificant compared to the times needed to establish connectivity and to initialize a terrain.  The lack of durability of serialized files becomes an issue if you want to store configs for a long while (perhaps in a database), for cross-platform usage, and for app or Java upgrades.

  • Sharing of terrain configurations on a network is extremely fast and extremely easy.



The PTS home page now has 2 new demos which load their terrain configs from the Internet.  They are the red links (for now).

Here is a simple config file (used by the first red-linked demo).  See the 2nd red demo link for a more complete config file showing setup of multiple textures, elevation procedures, area limiters.


<?xml version="1.0" encoding="UTF-8"?>

<!--
    $Id: faults-terrainmap.xml 2772 2009-11-17 20:23:58Z blaine $

    Sample TerrainMap config for an untextured terrain built from a jME
    FaultFractalHeightMap.
-->

<java version="1.6.0_0" class="java.beans.XMLDecoder">
  <object class="com.admc.agf.terrain.TerrainMap">
    <float>30</float>  <!-- blockLen -->
    <float id="tm.lenPerVert">.3</float>  <!-- lenPerVert -->
    <void method="addProcedure">
      <object class="com.admc.agf.terrain.JmeHeightMapProcedure">
        <object class="com.jmex.terrain.util.FaultFractalHeightMap">
          <int>4097</int>
          <int>16</int>
          <int>1</int>
          <int>2</int>
          <float>.3</float>
          <long>7171</long>  <!--  Randomization key  -->
          <void property="heightScale"><float>.1</float></void>
        </object>
        <void property="lenPerDatum"> <float idref="tm.lenPerVert"/> </void>
        <void property="absoluteElevations"> <boolean>true</boolean> </void>
        <void method="init"/>
      </object>
    </void>
  </object>
</java>



Here is complete, working code needed to load a ProceduralTerrain with the configuration above (or indeed, any config file defining a TerrainMap).  Client-specific settings such as how many terrain blocks to cache and how far away to instantiate and attach blocks, is not in my stored config, but it could be.  TerrainRunner.java in the code base provides a more general config loader that will also load a TerrainTexturizer config if present in the file.


        XMLDecoder decoder = null;
        try {
            String urlString = "file:/tmp/faults-terrainmap.xml";
            InputStream iStream = new URL(urlString).openStream();
            if (iStream == null)
                throw new IllegalStateException(
                        "Resource is unavailable:  " + urlString);
            decoder = new XMLDecoder(new BufferedInputStream(iStream));
        } catch (IOException ioe) {
            throw new RuntimeException(ioe);
        }
        TerrainMap terrainMap = (TerrainMap) decoder.readObject();
        terrainMap.init();

        ProceduralTerrain newPTS = new ProceduralTerrain(
                "proceduralTerrain", center, terrainMap,
                Thread.currentThread().getPriority() - 1);
        newPTS.setLoadRange(95f);
        newPTS.setLoadedSpares(5);
        newPTS.setAttachRange(65f);

I've updated the demo which features a portal.  When you step onto the portal, it now loads the new terrain from the Internet.  Interested parties can see the portal event handling code in the bottom block of the method TerrainRunner.simpleUpdate().



I've noted this in red on the PTS demo list.  (I didn't change the link itself to red, so that the descriptions in my previous post will still be true).

Hi!


blaine said:

I've updated the demo which features a portal.  When you step onto the portal, it now loads the new terrain from the Internet. 

Do you implement only portals used as teleporters?

I see you use J2SE XML encoding and decoding, me too. When I have a transient attribute that is a collection and even though I modify the bean info to force the encoder to ignore all transient attributes, it does not work, the transient collection is encoded. Have you found a nice workaround for this problem/bug?
gouessej said:

Hi!

blaine said:

I've updated the demo which features a portal.  When you step onto the portal, it now loads the new terrain from the Internet.

Do you implement only portals used as teleporters?


No.  I did that because it served double-duty in that sample.  You can use ALT+fi in Modeler to teleport to an arbitrary new terrain now (provisional detection of terrain is by the file name matching *terrain*.xml).  In RPGs I usually toggle portals by clicking on a model (i.e. by a ray collision check).


I see you use J2SE XML encoding and decoding, me too.


Actually I am only using J2SE XML decoding, not encoding.  Games may need to alter terrain as they run (excavate, mine, dig, pile-up, change surface texture), but the terrain generally needs to be pre-defined by a designer.  If somebody convinces me otherwise or contributes the encoder-side work, I'll add it to PTS.

When I have a transient attribute that is a collection and even though I modify the bean info to force the encoder to ignore all transient attributes, it does not work, the transient collection is encoded.


I don't know in which sense you mean transient here.  The transient marker is for serialization, not JavaBeans (and therefore XMLEncoding).  JavaBean visibility is managed by method (not field) signatures and BeanInfo customization (if it can't be avoided).

Have you found a nice workaround for this problem/bug?


J2SE collections do not use the DefaultPersistenceDelegate, so they don't necessarily follow the encoder-typical bean rules.  A problem I've run into is that you can't extend the J2SE collection PersistenceDelegates for your collection extension classes, because the built-in J2SE-customized PersistenceDelegates are not extendable.  You can, of course, make completely new PersistenceDelegates for your own Collection classes, but for the case of Collection requirements, that may well require usage of the really esoteric (and poorly documented) PD features.

I don't think that XMLEncoder was designed or documented well.  Just my opinion.
blaine said:
J2SE collections do not use the DefaultPersistenceDelegate, so they don't necessarily follow the encoder-typical bean rules.  A problem I've run into is that you can't extend the J2SE collection PersistenceDelegates for your collection extension classes, because the built-in J2SE-customized PersistenceDelegates are not extendable.  You can, of course, make completely new PersistenceDelegates for your own Collection classes, but for the case of Collection requirements, that may well require usage of the really esoteric (and poorly documented) PD features.

I don't think that XMLEncoder was designed or documented well.  Just my opinion.

You have just taught me something very important, thank you so much.