Jump to content

Terrain in Turbo Game Engine

Josh

404 views

I wanted to work on something a bit easier before going back into voxel ray tracing, which is another difficult problem. "Something easier" was terrain, and it ended up consuming the entire month of August, but I think you will agree it was worthwhile.

In Leadwerks Game Engine, I used clipmaps to pre-render the terrain around the camera to a series of cascading textures. You can read about the implementation here:

This worked very well with the hardware we had available at the time, but did result in some blurriness in the terrain surface at far distances. At the time this was invented, we had some really severe hardware restrictions, so this was the best solution then. I also did some experiments with tessellation, but a finished version was never released.

New Terrain System

Vulkan gives us a lot more freedom to follow our dreams. When designing a new system, I find it useful to come up with a list of attributes I care about, and then look for the engineering solution that best meets those needs.

Here's what we want:

  • Unlimited number of texture layers
  • Pixel-perfect resolution at any distance
  • Support for tessellation, including physics that match the tessellated surface.
  • Fast performance independent from the number of texture layers (more layers should not slow down the renderer)

Hardware tessellation is easy to make a basic demo for, but it is hard to turn it into a usable feature, so I decided to attack this first. You can read my articles about the implementation below. Once I got the system worked out for models, it was pretty easy to carry that over to terrain.

So then I turned my attention to the basic terrain system. In the new engine, terrain is a regular old entity. This means you can move it, rotate it, and even flip it upside down to make a cave level. Ever wonder what a rotated terrain looks like?

Untitled.thumb.jpg.cc0950f13ffcf999d8bb9d5468b7d062.jpg

Now you know.

You can create multiple terrains, instead of just having one terrain per world like in Leadwerks. If you just need a little patch of terrain in a mostly indoor scene, you can create one with exactly the dimensions you want and place it wherever you like. And because terrain is running through the exact same rendering path as models, shadows work exactly the same.

Here is some of the terrain API, which will be documented in the new engine:

shared_ptr<Terrain> CreateTerrain(shared_ptr<World> world, const int tilesx, const int tiles, const int patchsize = 32, const int LODLevels = 4)
shared_ptr<Material> Terrain::GetMaterial(const int x, const int y, const int index = 0)
float Terrain::GetHeight(const int x, const int y, const bool global = true)
void Terrain::SetHeight(const int x, const int y, const float height)
void Terrain::SetSlopeConstraints(const float minimum, const float maximum, const float range, const int layer)
void Terrain::SetHeightConstraints(const float minimum, const float maximum, const float range, const int layer)
int Terrain::AddLayer(shared_ptr<Material> material)
void Terrain::SetMaterial(const int index, const int x, const int y, const float strength = 1.0, const int threadindex = 0)
Vec3 Terrain::GetNormal(const int x, const int y)
float Terrain::GetSlope(const int x, const int y)
void Terrain::UpdateNormals()
void Terrain::UpdateNormals(const int x, const int y, const int width, const int height)
float Terrain::GetMaterialStrength(const int x, const int y, const int index)

What I came up with is flexible it can be used in three ways.

  • Create one big terrain split up into segments (like Leadwerks Engine does, except non-square terrains are now supported).
  • Create small patches of terrain to fit in a specific area.
  • Create many terrains and tile them to simulate very large areas.

Updating Normals

I spent almost a full day trying to calculate terrain normal in local space. When they were scaled up in a non-linear scale, the PN Quads started to produce waves. I finally realized that normal cannot really be scaled. The scaled vector, even if normalized, is not the correct normal. I searched for some information on this issue, but the only thing I could find is a few mentions of an article called "Abnormal Normals" by someone named Eric Haines, but it seems the original article has gone down the memory hole. In retrospect it makes sense if I picture the normal vectors rotating instead of shifting each axis. So bottom line is that normal for any surface have to be recalculated if a non-uniform scale is used.

I'm doing more things on the CPU in this design because the terrain system is more complex, and because it's a lot harder to get Vulkan to do anything. I might move it over to the GPU in the future but for right now I will stick with the CPU. I used multithreading to improve performance by a lot:

Physics

Newton Dynamics provides a way to dynamically calculate triangles for collision. This will be used to calculate a high-res collision mesh on-the-fly for physics. (For future development.) Something similar could probably be done for the picking system, but that might not be a great idea to do.

Materials

At first I thought I would implement a system where one terrain vertex just has one material, but it quickly became apparent that this would result in very "square" patterns, and that per-vertex blending between multiple materials would be needed. You can see below the transitions between materials form a blocky pattern.

splat1.thumb.jpg.c2326e1a9fe8d375b850ef97c82674e0.jpg

So I came up with a more advanced system that gives nice smooth transitions between multiple materials, but is still very fast:

splat2.thumb.jpg.6744367ea0b04321f59d16c79b81ad3f.jpg

The new terrain system supports up to 256 different materials per terrain. I've worked out a system that runs fast no matter how many material layers you use, so you don't have to be concerned at all about using too many layers. You will run out of video memory before you run out of options.

Each layer uses a PBR material with full support for metalness, roughness, and reflections. This allows a wider range of materials, like slick shiny obsidian rocks and reflective ice. When combined with tessellation, it is possible to make snow that actually looks like snow.

snow.jpg.b1c5d0bfc614743aaa34b10bdc80b722.jpg

 Instancing

Like any other entity, terrain can be copied or instantiated. If you make an instance of a terrain, it will use the same height, material, normal, and alpha data as the original. When the new editor arrives, I expect that will allow you to modify one terrain and see the results appear on the other instance immediately. A lot of "capture the flag" maps have two identical sides facing each other, so this could be good for that.

untitled.png.a054b45aef9263a561fdf9fa874b0f2f.png

Final Shots

Loading up "The Zone" with a single displacement map added to one material produced some very nice results.

t1.thumb.jpg.c7ab92b604fa089e8b51b1765d4661f1.jpg

t2.thumb.jpg.5efa1b4ebe5b16c159be8b15d4599d7f.jpg

t3.thumb.jpg.c398bf00d33a3c2e120b77afb26cfb64.jpg

The new terrain system will be very flexible, it looks great, and it runs fast. (Tessellation requires a high-end GPU, but can be disabled.) I think this is one of the features that will make people very excited about using the new Turbo Game Engine when it comes out.

  • Like 9


8 Comments


Recommended Comments

Wonderful blog post!

When can we get a new "beta" for this?

Vegetation is the only feature missing to complete everything I need for where I am going with my game. I will be hiring a full time content creator on Oct 1st, so this is exciting.
 

Share this comment


Link to comment

@martyj Currently there is a bug that randomly makes the whole terrain black. Probably texture data not being sent correctly to the GPU. I need to fix that, and I might implement directional light shadows before an update, if they don't take too much time.

Share this comment


Link to comment

Would be cool if we could add a specular texture on the new terrain so as to get that after rain look or add oil spots etc.

Share this comment


Link to comment
54 minutes ago, havenphillip said:

Would be cool if we could add a specular texture on the new terrain so as to get that after rain look or add oil spots etc.

Metalness / roughness will do this.

Share this comment


Link to comment
2 hours ago, Josh said:

@martyj Currently there is a bug that randomly makes the whole terrain black. Probably texture data not being sent correctly to the GPU. I need to fix that, and I might implement directional light shadows before an update, if they don't take too much time.

Try running it on an AMD card.

Back when I was working on OpenGL I had a bug where a uniform was set to zero by default on Nvidia, yet it was randomized on AMD.

If the terrain flashes random colors, you know that's your issue.

  • Like 1

Share this comment


Link to comment

This is very sweet.  Curious if we'll get oceans back and possibly rivers but I know you're not there yet.  Edit: come to think of it, do you think it would be easier to implement with your new renderer and Vulkan?

Share this comment


Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Blog Entries

    • By Josh in Josh's Dev Blog 5
      You might have seen this graphic comparing the size of the world in different games. I've played Fuel, and never reached the end of the world in that game. You can drive for a very long time on those roads.

      We want to use the new engine for realistic simulations of air and ground movements. At normal cruising altitude of a commercial airliner, the pilot has a view range of about 400 kilometers. The image below shows that area (800 x 800 km). You can see the areas of the biggest games ever fit neatly into the corner of just our visible area.

      The gray space above is not the total world size, it is just the area you can see at once from high altitude. The total world size is about 50 times bigger.
      This is what I am working on now.
    • By Josh in Josh's Dev Blog 26
      Gamers have always been fascinated with the idea of endless areas to roam.  It seems we are always artificially constrained within a small area to play in, and the possibility of an entire world outside those bounds is tantalizing.  The game FUEL captured this idea by presenting the player with an enormous world that took hours to drive across:
      In the past, I always implemented terrain with one big heightmap texture, which had a fixed size like 1024x1024, 2048x2048, etc.  However, our vegetation system, featured in the book Game Engine Gems 3, required a different approach.  There was far too many instances of grass, trees, and rocks to store them all in memory, and I wanted to do something really radical.  The solution was to create an algorithm that could instantly calculate all the vegetation instances in a given area.  The algorithm would always produce the same result, but the actual data would never be saved, it was just retrieved in the area where you needed it, when you needed it.  So with a few modifications, our vegetation system is already set up to generate infinite instances far into the distance.

      However, terrain is problematic.  Just because an area is too far away to see doesn't mean it should stop existing.  If we don't store the terrain in memory then how do we prevent far away objects from falling into the ground?  I don't like the idea of disabling far away physics because it makes things very complex for the end user.  There are definitely some tricks we can add like not updating far away AI agents, but I want everything to just work by default, to the best of my ability.
      It was during the development of the vegetation system that I realized the MISSING PIECE to this puzzle.  The secret is in the way collision works with vegetation.  When any object moves all the collidable vegetation instances around it are retrieved and collision is performed on this fetched data.  We can do the exact same thing with terrain   Imagine a log rolling across the terrain.  We could use an algorithm to generate all the triangles it potentially could collide with, like in the image below.

      You can probably imagine how it would be easy to lay out an infinite grid of flat squares around the player, wherever he is standing in the world.

      What if we only save heightmap data for the squares the user modifies in the editor?  They can't possibly modify the entire universe, so let's just save their changes and make the default terrain flat.  It won't be very interesting, but it will work, right?
      What if instead of being flat by default, there was a function we had that would procedurally calculate the terrain height at any point?  The input would be the XZ position in the world and the output would be a heightmap value.

      If we used this, then we would have an entire procedurally generated terrain combined with parts that the developer modifies by hand with the terrain tools.  Only the hand-modified parts would have to be saved to a series of files that could be named "mapname_x_x.patch", i.e. "magickingdom_54_72.patch".  These patches could be loaded from disk as needed, and deleted from memory when no longer in use.
      The real magic would be in developing an algorithm that could quickly generate a height value given an XZ position.  A random seed could be introduced to allow us to create an endless variety of procedural landscapes to explore.  Perhaps a large brush could even be used to assign characteristics to an entire region like "mountainy", "plains", etc.
      The possibilities of what we can do in Leadwerks Engine 5 are intriguing.  Granted I don't have all the answers right now, but implementing a system like this would be a major step forward that unlocks an enormous world to explore.  What do you think?

    • By Haydenmango in Snowboarding Development Blog 6
      So I've been researching snowboarding lately to get an idea of what animations and mechanics I need to create for my game.  I have learned lots of interesting things since I've only seen snow once or twice in my entire life and have never even tried snowboarding or any other board sports (skateboarding, surfing, etc.) for that matter.
       
      Snowboarding tricks are quite interesting as they are mostly derived from skateboarding.  Snowboarding tricks pay homage to their equivalent skating tricks by sharing many concepts and names.  For example basic grabs in snowboarding share the same concepts and names as skateboarding: indy, mute, method, stalefish, nosegrab, and tailgrab.  Something interesting to note is in snowboarding you can grab Tindy or Tailfish but this is considered poor form since these grabs can't be done on a skateboard (due to the board not being attached to the skaters feet) and grabbing these areas is generally something a novice snowboarder does when failing or "half-assing" a normal grab.  Check out this diagram to see how grabs work -
       
       
      So, after reading lots of text descriptions for tricks I was still confused by what all these terms meant and how they were actually applied.  So my next step was to look up these tricks actually being done and I found some really cool videos showing off how to do various tricks.  This video in particular is the best reference material I've found as it contains nearly every trick back to back with labeled names and some tweaks -
       
      Sadly my rigged model doesn't handle leg animations with the snowboard that well so I can't animate as many tricks as I want to.  Regardless there will still be around 15 total grab/air tricks in the game.  Now it's time for me to stop procrastinating and start animating!  
×
×
  • Create New...