Jump to content

OpenSimEarth gets forests and streets


chriscalef

Recommended Posts

http://opensimearth.com/images/screenshots/roads_header.jpg


Well, it's been a busy October for OpenSimEarth!


We're still totally under construction and not ready for business yet, of course, but this seemed like a pretty good time to report back. If you're interested in checking out the repos, see my last post on the subject for URLs.


The quick summary, as hinted in the title: I now have a somewhat functional system for paging streets (mesh roads) and forests as you travel around the world.


The streets are a further development from this post, and they follow directly from the work I did with the forests.


Torque has a very nice forest rendering system, capable of rendering impressive numbers of trees at very low cost... but at some point, even it will become unusable if you just keep stuffing trees into it forever. Since my goal is an open and virtually infinite world, I needed to have a way to control the forested area and keep it centered on the player.


My (somewhat predictable) solution was to subdivide my terrain tiles into a grid of forest cells, and load and drop them as the player moves around the map.


I needed to paint the forest procedurally, of course, so I copied the random forest painting technique from the forest brush tool into a function designed to fill a single cell, and then worked up a little logic to keep track of which cells around the player had been filled already and keep filling and dropping them as we move.


http://opensimearth.com/images/screenshots/road_05.jpg


Oh, before I forget, I did some things that could be of general interest here. First, if you ever get tired of painting forest with brushes arbitrarily limited to 150m in radius, the fix for that is in forestBrushTool.cpp, line 307:

 

void ForestBrushTool::setSize( F32 val )
{
   mSize = mClampF( val, 0.0f, 15000.0f );//150.0f
   Con::executef( this, "syncBrushToolbar" );
}

 

I'm not using the brush tool anymore, but that really annoyed me for a while, thought it might help somebody.


Second, I added a couple of functions that I think really ought to be included in stringFunctions.h. I'll be glad to submit it as a PR, but if anyone on the steering committee just wants to copy and paste them from here, that would be fine too, they're quite trivial. But unless I missed something there didn't seem to be a "Torque way" to convert char strings to long or double values:

 

inline long dAtol(const char *str)
{
   return strtol(str, NULL, 10);
}
 
inline double dAtod(const char *str)
{
   return strtod(str, NULL);
}

Anyway, once I got my forests paging around the map with me, I moved on to the next step, which was filling different areas with different types of forest. So far I've only accomplished the first part of this process, which was differentiating forest cover based on terrain texture. For my ultimate solution I intend to do this in addition to having arbitrary landcover polygons that I can place anywhere, but... one thing at a time.


Since I was already scheming about how to incrementally load both street nodes and other placed objects, this forest cell system led me right on to other things. However, the first thing I had to figure out was how to make sure the forest didn't get placed in the roads. (I was actually a little surprised Torque didn't do this already.) I'm still unsure about the best way to handle decal roads, but with meshroads it was possible to get a different object type from a castray function, so I added the following line to my getGroundAt function stolen from forestBrushTool:

 

if ( !hit || strcmp(rinfo.object->getClassName(),"TerrainBlock"))
      return false;

 

If you want the same behavior with your forestBrushTool, you can go there (forestBrushTool.cpp, line 673) and do the same thing. (Does this want to be pulled into the main trunk as well?) In addition to roads, it also keeps forests from growing on rocks or houses or any other static object. Not sure if a lot of people need contradictory behavior or not, for me this seems to work pretty well.


http://opensimearth.com/images/screenshots/road_07.jpg


Along the way here I lost a couple of days getting very excited about something called SpatiaLite, which is an add-on for SQLite to support GIS logic. I'm still hoping I get to move there sooner or later, because it sounds like THE way to do what I'm trying to do on my own here, but I found a large number of obstacles involved in compiling and using it with Visual Studio and ended up backing away in defeat with my tail between my legs. :-P My primary goal was to be able to do efficient regional searches of a potentially very large geographic database, and what I ended up doing was inventing a lat/long based cell tag for all of my street nodes (looking like "123d0834W_44d0566N"), and then creating an index on that tag to make it search faster. It wastes a little memory, and it's still too slow, but it definitely helped.


There's still a lot of work to do, of course, in many different directions. I'm setting road widths and (to the extent I have textures available, which is very little) road textures based on the highway type specified in the OpenStreetMap data. I hope to extend the same logic to rivers and streams, but I'll need to do terrain modification for them, and of course that would also help a lot with roads, to flatten the terrain underneath them.


Finally, as mentioned above, I'd love to find an easy way to detect decal roads with a raycast, anybody got any ideas on that? I'm using all mesh roads right now just because they're the only ones I can detect to keep the forests out of them.


Oh, also, I don't expect any magic help here for this one, but I'm running into a really annoying render glitch with my forests... at times, they seem to just not work at all, but when I raise my camera up a bit in free camera mode, they pop back in. You can see an example around the 0:21 point in this video, which demonstrates the current system:


Link to comment
Share on other sites

Wow, that sounds like a lot of impressive work! I noticed a while ago how useful it would be to have the forest tool avoid certain objects like roads and buildings so good work figuring it out! I agree it will be very useful if we can figure out how to do the same for decal roads. I'll let you know if I get a chance to look into it.

Link to comment
Share on other sites

Awesome, yeah please do. The other question I still have is whether there's any efficient way to do the same with groundCover objects. Just now I tracked down where you'd look for that - groundCover.cpp line 1226 or so, looks like this:

 

PROFILE_START( GroundCover_TerrainRayCast );
         // Transform billboard point into terrain's frame of reference.
         Point3F pp = Point3F(cp.x, cp.y, 0);
         terrainBlock->getWorldTransform().mulP(pp);
         hit = terrainBlock->getNormalHeightMaterial( Point2F ( pp.x, pp.y ),
                                                      &normal, &h, matName );
         PROFILE_END(); // GroundCover_TerrainRayCast

 

But it calls getNormalHeightMaterial instead of using castRay(), and I suspect that is because it's probably a whole lot faster. I'm out of time for now but I guess I'll try testing that later. If anybody has any ideas on the subject I'd love to hear them though!

Link to comment
Share on other sites

Actually, getting that stuff to avoid roads should be pretty easy.


DecalRoad has a 'containsPoint' function, where you pass in the worldPos and it does some quick math to see if the world pos falls inside the road segments(and will return back the node in question, if it matters)


So when generating the grass or trees, you should be able to get the intended x/y position, use get terrain height for the z, and then iterate through any decal roads in the mission(should be able to early out by testing it's bounds vs the current ground cover cell, and if they don't overlap, skip it).


Then just pass in that pos to the potential decal roads and see if it falls inside any of them. If it does, reject that grass item.


MeshRoad almost has similar, it has a containsPoint for the MeshRoadSegment struct, so you'd need to add a containsPoint function to MeshRoad that iterates through all the segments, but otherwise it should be a repeat of the above with the decal road.


Do that, and you should be able to easily reject the position if it'd overlap with a road.

Link to comment
Share on other sites

Ah, thanks Jeff, that sounds helpful. Although with Meshroads, it seems like my current solution is probably fastest, since we're already doing a raycast to find the ground anyway and the same cast is capable of excluding everything except terrain. But for decal roads that sounds like a great solution!

Link to comment
Share on other sites

Join the conversation

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

Guest
Reply to this topic...

×   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.

×
×
  • Create New...