Using multiple textures with PlaneBufferGeometry

I’m creating a 513×513 plane and using a height map to represent terrain. To add some variety to the terrain there are several 256×256px texture files. Here’s a basic example of what I’d like to achieve:

In the example, each square of the 2×2 plane are a different texture (texture_1.png, texture_2.png, etc).

Some context…

I’m trying to recreate levels from a video game called Incoming. Here’s a screenshot from one of the Arctic missions:

As you can see, the terrain is a simple height map which uses various snowy textures; some examples:

I’m able to read the height map data from the game files and produce an accurate plane:


I’ve tried searching for similar topics and found one which mentioned using the addGroup method, however I was unable to get this to work without terrible performance.

This seems like it should be a relatively straightforward operation - am I missing something?

Considering that on the screenshot there are buildings specifically placed inside the “craters” on the textures - shouldn’t these textures be manually UV mapped onto the terrain - for each level separately, to fit the buildings / enemies / targets? :thinking:

(You can UV map models quite easily with Blender - especially simple heightmap / subdivided plane geometry.)

That’s correct - the texture data for panning/rotation etc. is stored in a separate file for each level.

Ideally I’d just like to be able to drag/drop the game files straight into a browser window and have it display the terrain with the correct textures. I feel like I’m pretty close already - just unsure how to proceed with specifying a texture index for each segment.

Ah, got it. Would it break any copyright laws if you shared the file structure :thinking: ? Do you need to blend the textures together, or just place correct texture squares on correct quads in the scene?

[…] or just place correct texture squares on correct quads in the scene?

Exactly.

The texture info for each level is stored in a *.bin file which is always 32,768 bytes (2 bytes per square × 128×128 grid). Here’s the first 16 bytes of one of these files:

0xA0 0x00
0x20 0x00
0xA0 0x00
0x20 0x00
0xA0 0x00
0x20 0x00
0xA0 0x00
0x20 0x00

Byte 1 contains the texture index in the first four bits and the texture panning in the last four bits (a simple 4-bit bitmask) - i.e. for the first byte, 0xA0:

  • 0xA0 & 0x0F == 0x0 - texture index 0 (every level has 8 textures)
  • 0xA0 & 0xF0 == 0xA - texture should be panned so the bottom right quarter is visible

Byte 2 is also a bitmask for flipping vertically/horizontally, rotation, and possibly other things I haven’t figured out yet.

I managed to reverse engineer most of the game’s formats last year and posted what I found on GitHub (there’s a better visual representation of the texture panning there if my example here didn’t make that much sense).

I was hacking around trying to get it to work last night and the following code kind of works in principle:

let x = 0;

// `materials` is an array of THREE.MeshBasicMaterial containing each of the level's 8 textures
for (let i = 0; i < materials.length; i++) { 
    geometry.addGroup(6 * x++, 6, i);
    geometry.addGroup(6 * x++, 6, i);
    geometry.addGroup(6 * x++, 6, i);
    geometry.addGroup(6 * x++, 6, i);
}

This gives me a strip of terrain with different textures:

I’m pretty sure this is an incredibly inefficient way of using groups, though, so I’m still a bit unsure of how best to proceed.