Cutting up Heightmaps into smaller squares...

Hey all,

This is a more general topic (general meaning non-engine-specific) so I figure this may be a better place to ask my question.

First off, a little background. I have a tile-loader which loads tiles which consist of a terrain-splatting technique and a heightmap. The TileManager class detects (based on your coordinates in the world) what “tile” you would be standing on, and then tries to find the heightmap file in the heightmap directory. If it can’t find it, it simply loads a default heightmap (to give us endless terrain).

I am creating my worlds (both heightmaps and texturing via alpha maps) in FreeWorld3D (www.freeworld3d.org), so I was wondering if anybody could offer input as to take a heightmap and cut it into smaller squares?

What I want to do is calculate how many new heightmaps will come out of the original (i.e. 1024 64x64 heightmaps from 1 2048x2048), then create them and name them appropriately. The naming will be very specific to my application and should be based on their coordinates within the image (i.e. 0,0.bmp; 1,1.bmp; 3,4.bmp).

Does anybody have some example code which can take a BMP image and create new smaller images based on subimages? If you do, I’d appreciate it very much.

I was originally using RAW heightmaps, but because of the difficulty of doing this conversion with RAW heightmaps I think I’ll be using BMP heightmaps.

-Tyler

Stick with RAW. BMP has only 8 bits per color-channel, and 256 heightvalues just won’t cut it. Further, I don’t see what would make RAW any harder to split up too…

lets say you have a heightmap in a short[w*h]


public static short[] sub(short[] sOrg, int wOrg, int x, int y, int wSub, int hSub)
{
   short[] sSub = new short[w*h];
   for(int i=0; i<wSub; i++)
      for(int k=0; k<hSub; k++)
         sSub[k*w+i] = sOrg[(y+k)*wOrg+(x+i)];
   return sSub;
}

I assume you ask this in the context of your MMORPG highschool project. In that case, I’ll give you some advise you might not like:

Stay away from building a fancy terrain-renderer with LODing and fancy re-triangulation. This is a project in itself, and will take a long time. Time you could have spent on gameplay! Programming gameplay/interaction is hard enough.

For the sake of building the game, instead of a ‘glorified model viewer’, i’d advise you to keep the world like 64x64 tiles for the next few months, to keep your framerate high, so you can work on other things without being tempted into optimizing terrain.

I don’t have anything fancy at all, really… the TileLoader just grabs heightmaps and converts them into Terrain Geometry then textures them based on if it has designated alphas in the different layer areas.

The only reason I wish to convert from 1 big map to many small maps is because it’s easier to design on a larger scale and then split the maps up… that way I can have a big hill that is in the middle of four different maps and it’ll still look fine.

My program (FW3D) exports 24- or 32-bit BMP… is what you said still a problem?

The reason I would like to do BMP is because it allows me to simultaneously cut up the heightmap itself, then the alpha maps… along with the fact that I have no idea how to write a 16-bit RAW heightmap file…

Well the BMP heightmaps didn’t work out so spectacularly. There are spaces between each tile and the heights are all wrong. I loved RAW heightmaps, but was never able to cut them up. Let me show you what progress I have made, and perhaps you can tell me if you see something wrong.


package heightmapsplitter;

import com.jmex.terrain.util.RawHeightMap;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

/**
 *
 * @author Tyler Trussell
 */
public class HeightMapSplitterTest
{
    public static void main(String args[]) throws FileNotFoundException, IOException
    {
        int oldSize = 512;
        int newSize = 64;
        
        String originalName = "mymap.raw";
        String fileName = "output/";
        
        int startX = 0;
        int startY = 0;
        
        int numMaps = oldSize / newSize;
        
        RawHeightMap oldMap;
        int[] oldMapData;
        
        oldMap = new RawHeightMap(HeightMapSplitterTest.class.getClassLoader().getResource(originalName), oldSize + 1, RawHeightMap.FORMAT_16BITLE, false);
        oldMapData = oldMap.getHeightMap();
        
        
        for(int i=0; i<numMaps; i++) //x
        {
            for(int n=0; n<numMaps; n++) //y
            {
                int[] newMap = new int[newSize * newSize];
                for (int x = 0; x < newSize; x++)
                {
                    for (int y = 0; y < newSize; y++)
                    {
                        newMap[y * newSize + x] = oldMapData[(n + y) * oldSize + (i + x)];
                    }
                }
                for(int r=0; r<newMap.length; r++)
                    System.out.print(newMap[i] + " ");
                System.out.println();
                FyrestoneMap map = new FyrestoneMap(newMap);
                map.save(fileName + (i+startX) + "," + (n+startY) + ".raw");
            }
        }
        
    }
}

But the heightmap files never work out.

I see this:
“oldSize + 1”

that basically says that your old heightmap has a dimension of 513, right? (to render 512x512 tiles you need 513x513 heightvalues)

Further, you seemed to have misinterpreted my code. the X and Y are the cell offset, not the tile offset.

PLEASE use method-calls… as the above is hardly maintainable.
To take this into account in your own code:


sub(oldMapData, oldSize+1, i*newSize, n*newSize, newSize+1, newSize+1);

Imagine to write this without the method-call, and things get very messy.

I’ve always seen it as an array of integers… I guess it doesn’t really matter since it’s independent of the Game Engine.

Is there a header for a 16-bit LE RAW heightmap, or is it literally just an array of integers / shorts?

You may be interested in L3DT (http://www.bundysoft.com/L3DT/) which is able to export tilemaps out of the box (it’s called mosaik map there)

From the gallery, that looks like a program designed for high-quality renderings (like TerraGen)… ATM I am unable to test the editor. Does it have an interface designed for level-building (i.e. you can see the terrain, rendered, while you build it, supports texturing and alphamaps, etc)?

RAW does not have a header.

So… what is the file size?
512x512x2 bytes or 513x513x2 bytes ?

Does rendering work with the non-splitted-data?

As you didn’t really respond to my comment about how you misinterpretated the code-sample, I fear you might still be using it wrong. Your last provided code will behave like getting ‘random’ samples from the big map: very spiky, as you noticed.

I crashed my development machine, so I am unable to get to my projects folder and edit the code at the moment, which is the reason I haven’t responded to anything. I’m not sure about the file size… I know that it’s an array of integers with a size of mapSize * mapSize… Other than that I’m not really an expert on the subject.

wait so if it doesnt’ have a file size couldn’t I just use an ObjectOutputStream to output an array of integers and name it “.raw” and it would work?

Say what?

I just ask you the file size because i want to know the grid dimensions.
The dimensions are used in the calculations to split up the map.

But how that relates to an ObjectOutputStream…?

I was responding to the “RAW does not have a header.” If it doesn’t have a header, then all I have to do is write an array of integers to a file using ObjectOutputStream, and I have a RAW file… is that correct?

Yup, but then I’d advice DataOutputStream

However, these classes write ints and shorts in BigEndian, while you probably have LittleEndian byte order in your original RAW files.

Writing your own LittleEndianDataOutputStream is trivial, ofcourse.

IIRC using an ObjectOutputStream to write a RAW file won’t work because it will write more than just the int values (i.e. it will have headers).
For the rest, do as Riven says: use DataOutputStream and be aware of potential endianness issues.

AFAIK ObjectOutputStream has headers after writeObject has been invoked for the first time, but I might need to check the sourcecode…

Normally I abuse MemoryCacheImageInputStream to read raw data.

It’s a terrain generator, not a real editor. In the editor you can “paint” a coarse design map from which the generator generates high detail height-, texture-, alpha-, light-, normal-, water- and attribute-maps. You can also regenerate parts or aspects of the terrain after you edit some details in the dataset or generation configuration.

Although there is a 3D component to view the generated terrain in realtime and adjust the heightmap, it is not an interactive editor to build a level and the generation can take up to multiple hours depending on the terrain size (1024+1024 should be pretty quick, though).

Just try it out if it suits your need.

Btw. If you want to use L3DTs own heightmap format, you can use http://xith3d.svn.sourceforge.net/viewvc/xith3d/trunk/src/org/xith3d/terrain/HF2Map.java?view=markup, it also contains code to read from raw files.

I tried L3DT but it wasn’t suitable for my needs, but thank you very much for the recommendation!

As for the DataOutputStream and creating my own, I don’t even know the difference between Little/Big Endian (but you are right–the original heightmaps ARE LE, I only know this because I tried both BE and LE on my importer and LE worked).

If you could describe to me the differences between BE and LE so I could get started writing my output stream, it would be much appreciated.

Take a look here.

Thanks very much!