External models - efficiency

Suppose you have 50 buildings in a scene, some parts are rendered with brick wall, some parts have tiled roofs. Alot of them have the same front door and windows. Pretty much ilke a period town.



If the buildings are all created as 3ds or md3 buildings, then presumably they wont share the textures or the triangles used to make a door or a roof or a wall.



What im asking is :-

Is it worth making the buildings in JME as nodes - the doors for instance can be SharedMeshs's, as can also the roofs with the same dimensions, the textures will always be reused - only one brick wall texture, only one tudor beam texture. Or, would this not really save that much and would be much easier to make them as models. Maybe a compromise whereby the models are constructed from parts.



Any views welcome



I have a cache of textures that are reused - only defined once per TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState() when needed. This i believe means the GPU only needs to be told of the texture once. Its a possibility/intention to do the same with TriMeshs under SharedMesh's






Ok - a diagram helps



The texture from the paths are shared, likewise the texture of the doors and bricks;



The trimeshs of the doors, steps, buildings and paths are all sharedmesh’s



Ok, am nearly there



Models then could be better off by being broken up into parts and attached within jme. This is in the scenario where there are many models in a scene.



A common shape to use is a box. What if a box ( cube ) is defined as 1 x 1 x 1. It can then be utilised by a sharedmesh and the scale set on it, so - a scene that has 1000 boxes has only sent one trimesh to the GPU. Likewise other common shapes like spheres and pyramids.



Similar to the singleton method, getInstance, a static 1x1x1 box could easily be returned by a static class method call making it simple to obtain a 1x1x1 box, this would then  leave the SharedMesh to only have to set the texture ( Cached ) and the scale.



Does this seem like a sensible approach or can anyone see any pitfalls

Well, aside fromt he fact you shouldn't use actual 1x1x1 boxes in a game if you're going to use lighting (except if you do it per pixel) and want it to look nice, the way you can do that is by reusing the buffer objects. CloneCreator can do this for you, in fact taking another closer look at the cloning process in TestTerrainTrees you can see it's already doing that.



That's probably the most important reason why replacing the clones with SharedMesh doesn't give that much of a benefit…

Here is my implementation of caching imageicons and textures, hope you find useful. If you use a gui like swing, they can also utilise the imageicons.

The purpose is that the textures can be cached and shared by many meshs.

Havent addressed alpha state or material state, but can easily be modified.



For each new scene, a new ImageCache should be created, the old one can be garbage collected.





Somewhere you should have a catalog / hastable / index system of the filenames. this sytem should implement the ImageCachable interface. The implementation will resolve the category and filename to a URL of the image.



The only thing missing as far as i am aware is the textures may need to be cleared from the GPU when the cache is no longer used, i really dont know if this is the case but will report back when i know - unless anyone knows …



import java.util.Hashtable;
import javax.swing.ImageIcon;
import com.jme.image.Texture;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import ImageCachable;

/**
 * @author k
 * Stores loaded Textures and ImageIcons for applications that want to allow sharing of
 * the textures and images.
 */
public class ImageCache {
   
   private Hashtable <ImageCachable,ImageEntry> imageEntries;
   
   /**
    * Construct a new ImageCache
    */
   public ImageCache() {
      imageEntries = new Hashtable<ImageCachable,ImageEntry>();
   }
   
   /**
    * @param imageCachable The reference to the image
    * @param entry The container of the Texture and the ImageIcon
    */
   protected void addImageEntry(ImageCachable imageCachable, ImageEntry entry) {
      imageEntries.put(imageCachable, entry);      
   }
   
   /**
    * Looks to see if it has a reference to an already loaded Texture and ImageIcon. If not, it adds
    * the ImageEntry and returns it for convenience
    *
    * @param imageCachable The reference to the image
    * @return ImageEntry - The container of the Texture and the ImageIcon
    */
   public ImageEntry getImageEntry(ImageCachable imageCachable) {
      ImageEntry entry = imageEntries.get(imageCachable);
      if(entry == null) {
         ImageIcon productImage = imageCachable.buildImageIcon(imageCachable.getImageCategory(),imageCachable.getImageName());
         TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
   

If it works for you that's fine. I'd probably go for SoftReferences and something that allows different types of TextureStates for the same image, but you can always add in that type of stuff later.



Also, you don't have to unload textures from the GPU. The memory on your videocard works as a cache, the textures most used will be stored there, so when you stop using them, they'll automatically be removed.

kidneybean said:

a scene that has 1000 boxes has only sent one trimesh to the GPU. Likewise other common shapes like spheres and pyramids.


I know I keep harping on about this, but my incomplete GeometryBatch class does this - it lets you group multiple meshes into a single static object. Search the Wiki for GeometryBatch.

Stodge, have you got the code available to post.

It's all on the Wiki - the code for GeometryBatch and a simple example. I haven't had time to profile large examples.

Stodge, what kidneybean means is that a trimesh only has to be send once, and can then be drawn multiple times.

What your batching does is send one huge trimesh, and only draw it once. That'd be bad here because: the trimesh will be much bigger, what means it will take longer to send and it will take up much more space, and it'll be harder to clip.



However; if you were to make, for example, a doorknob. If you use a Sphere and a Box to make that, it's unlikely one will be clipped and the other not. To reduce overhead you could use batching to make it into a single object. However, with your current code, the more objects you want to merge the longer it'll take… still plan on tackling that?