• entries
  • comments
  • views

Design Roadblock

Sign in to follow this  


So for this entry, text, text, a bit of code, and more text. I'm going to talk about a few issues I'm having with the design part of the generator, in particular when adding new tools, with their own operations.

[font='comic sans ms']Design Roadblock[/font]

In my previous entry I talked about a bit of eye candy I did for the generator and lightly touched the issue I am having right now, but I'll go ove rit again for the sake of explainin'.

[font='comic sans ms']



Before I started with the whole editor/tool/thing, I had a simple setup for the terrain generator: You would have parameters, fill those in, hit the corresponding generate buttons in order, and that was it.

This makes everything simpler, you do stuff in the wrong order, it breaks, so you shouldn't do so. But the whole "Editor" part means that you dear user shouldn't follow those strict rules right? What if you didn't liked that ridge you put there in the north and you're all the way on the last pass of the generator? You should be able to go back and change it, then go forward and pick up where you left it.

All of this imposes several challenges on a model that is strictly dependent on the order you do things. First the ridges, then the blurring, then the rivers, then the MDI, then finally the Midpoint-Inverse (or better, Diamond-Square).

So you're in step 4, Midpoint Inverse Pass, and want to add a ridge? That's gonna cost considerable time.

First, throw of the window the current map (delete it and reallocate it or re initialize it, both work), then draw the old ridges, then the new ridge... but that's just to get the desired changes written in the map. Then you have to get to where the user was before, that means, blur the map (pretty slow for now), recalculate all river paths (throwing away a lot of stored river positions in the process), do the MDI pass and only then we're back where we started, for just a single ridge in the whole map.

Hell, maybe the user doesn't goes back to the MDI pass but just returns to the river drawing tool (which means that the rest of the stuff would be recalculated anyway).

[font='comic sans ms']Other functions[/font]

A common function in any editor is the undo function, it poses exactly the same kind of challenges. For example, river undoing.

Say that you actually don't change the tool, but just want to undo a river that you just drew. Its a quite demanding thing actually, the river has to be removed from the generator, then the heightmap has to be reset because the river that you unmade affected the heightmap, then the particle map has to be reset too because the river particle is written there, then you have to draw everything again because you just unmade the last three passes with this process! (ridge drawing, blurring, river generation).

That require some kind of structure to handle those kind of changes. It would be really easy to just add methods to handle each particular case, that's how it is handled right now:public void undoRiverPaint ( final int mapIndex ){ // Retrieve both generators corresponding to the currently selected map. final GeneratorRiver genRiver = riverGenList.get( mapIndex ); final GeneratorRidge genRidge = ridgeGenList.get( mapIndex ); // Check if there is any river to undo. if ( genRiver.isAnyRiverLeft() ) { // Retrieve currently selected map. final ElevationMap map = mapList.get( mapIndex ); // Remove last added river from generator. genRiver.removeLastRiver(); // Reset heightmap OpsTerrain.resetHeightState( map ); // Reset particle map. OpsTerrain.resetParticleState( map ); // Write ridges again. genRidge.writeRidgeLines( map ); // Retrieve blur associated with the map. final BlurringMap blurMap = blurList.get( mapIndex ); // Force blurring of the map with previous blur settings. blurMap.setBlurred( false ); blurMap.blur( map, blurMap.getLastRadius()); // Rewrite rivers. genRiver.writeRiverLines( map ); } }
Now I dunno about you but to me it looks pretty wasteful.

Not only that but also that's a method in my Controller class, which is very restrictive. It means that for each kind of thing you did (draw a river, draw a ridge, do an MDI pass), you'd need to add a specific method in the Controller class to handle it.

[font='comic sans ms']More extensions[/font]

I wanted to leave the amount of "tools" you can use open, say for example that I want to add more land features that aren't ridges, like canyons, valleys, and such. Or maybe other water features, like an ocean level.

So for supporting the basic do/undo functions I need a more robust system to handle those, a "Tool Manager" if you will.

I can't seem to figure out a way to do this, ideally, adding a new tool would mean just extending an "AbstractTool", adding it to the manager, make corresponding changes on the UI (buttons, settings panel, etc), and that should be it.

Given that the tools have somewhat different requirements (they need to have different kind of knowledge on the maps, generators, and such) I'll could try to make a tool interface, that these tools would implement.

I'm trying to follow an MVC pattern here for the whole architecture, that's why I already have going a "ToolManager" for the UI part, which is in charge of switching the Listeners that know what method to call (which I hear isn't at all efficient but oh well), and the settings panel that is appropriate for the selected tool.

This one makes working with it pretty simple:toolAdmin.switchTo( tool );
As long as the tool is set up properly, it shouldn't have many problems. The "bad" part is that the river tool needs knowledge about two settings panels, blurring and river, so that one is done under the same interfaces, but in its own class.

[font='comic sans ms']

More abstractions


I'd like to have something similar going on for the "Controller" part. An easy way to switch tools, with their draw and undo behaviors, but coming down to a common subset of functionality among the tools that I have (and that I will have), and how they would interact is kinda hard.

Well, that's it for the wall o' text. Bye, until the next entry! :)
Sign in to follow this  


Recommended Comments

I'm already using up to 200Mb of memory so I'd prefer not to go through that route since I'll probably use a lot of more memory (I don't have only height data but direction data, state, particle at each point, etc).


But I did thought of a limited set of "undo operations". Maybe 10, or simply user defined. With a save/load system it shouldn't be too catastrophic. That way simply storing the whole ElevationMap doesn't seems that bad, it will have an upper limit.


In any case storing the state of the heightmap is not enough, also the generators need to know which river/ridge has been removed. So that part has to be done anyway.

Share this comment

Link to comment

It may be tricky, but I'd suggest the ability to recreate a region of the map. So to remove a river you would mark the path of the river as dirty, plus x units in every direction from the river, then just regenerate those bits. I'm working on the assumption that your passes are semi-localised, for example running a blur on the left side of the map will have no effect on the right side of the map. Or just do that for the steps that are localised, and completely re-run the other steps, such as rivers.


Another possibility is to make it more asynchronous. Let the user keep making changes while the map is regenerated. Then batch up all changes made since the last regen for the next regen, etc. It wouldn't seem so real-time, but may be useful. You may need to show their changes as some sort of translucent crappy looking overlay while the regen is running just so they know that their mouse clicks had some effect.

Share this comment

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now