Jump to content

Pyrophorus

Community Members
  • Posts

    78
  • Joined

  • Last visited

  • Days Won

    2

Posts posted by Pyrophorus

  1. 1 hour ago, elexis said:

    Given that schedule, you might want to consider dropping alpha 22 support, the PointXY prototype was removed for instance.

    There's no need to do that. The fractal library can work with both Vector2D and PointXZ (I think you mean this one).

    1 hour ago, elexis said:

    I totally agree that the cost of constructing Vector2D objects is an addition. But it has to be compared to the rest of the performance cost. It's currently more the avoidClasses / stayClasses constraints with large numbers that take really long. But there's a plan for that (some ConstantConstraint wrapper that is evaluated once per createArea / createObjectGroups call rather than reevaluated for each random coordinate in that loop).

    The cell object offers another (much faster) way to deal with the problem, but as I said some times ago, I think all this is not mature enough to enter alpha23. So I think a decision should be made and it would concern only the fractal part of the library (fractal.js) which rely NOT on the cell object. The fractal painters are no more than other painters in rmgen. You can copy/paste them somewhere as they are and if you want to review the code, it's only 340 lines, a great many of them being comments.  The remaining part of the fractal library is more a convenience and a code example to create globally height maps. It can be avoided if you want. I can remove PointXZ support for you if you want too, but I don't want to do that in my mod until the release is out, and have no reason to do so if finally you decide to include nothing in alpha23.

    Friendly,

  2. Hi !

    @FeXoR Thanks for your feed back and appreciation.

    4 hours ago, FeXoR said:

    I agree that trying to add a map with this features (though gorgeous) for the coming Alpha would be a bit hasty. But I would love to see one entering the review queue at some time!

    At least, I think it should be possible to add the fractal painters. It's only adding new painters. There is no compatibility problems. The remainder of the fractal.js can be inserted too, but is more a convenience to create  whole maps in one pass.

    5 hours ago, FeXoR said:

    ATM I can't see a huge advantage of this besides partial upgrading (which also might need border tiles) ... but it's absolutely possible I'm blind (I'll read it again after I hit the pillow) ;p
    Also this would AFAICS restrict this object only to properties in from the beginning (e.g. normals, waterHeight, waterVelocity are not used in most maps but might be wanted in others ... which then can't add them). If I am wrong here please teach me differently! Is inheriting from an instance possible (assuming the instance is generated in the libs which don't know the needs of a map)?

    Javascript has not strict inheritance but allows to add properties to an object at any time. So it's easy to add new values to the Cell object or its prototype when they're needed. And we need not to define a full featured Cell object as in Java or C++. In the code, it would look quite the same. For now, if you want to use slope, you have to create a separate slope map first which is stored in a array. Slope (or anything) would be stored in the Cell objects instead. And if you want to know if the work has already be done (rather useful in a library where you have no control on operation order), you can test if the property is undefined on any Cell.

    For myself, I use four members which will probably never been used in other functions. But it's not a problem. I can easily enhance the Cell object if exists.

    5 hours ago, FeXoR said:

    Also what about the performance of derived properties like grad/rot/div of the original properties? Any benefit here (besides partial updates)?
    So, sorry I can't give you an opinion right away :pardon:

    IMO, there should be no penalty because, obviously, Javascript uses references when manipulating arrays or such containers. It matters not if the object is large or not. But creating a new object has a cost. Now the operator === is certainly faster than (x1 == x2) && (y1 == y2), but I'm not sure it adds a real benefit. If you look at this line:

    if((neigh.slope < this.slopemax) && (neigh.slope >= this.slopemin) && (neigh.alt < this.altmax) && (neigh.alt >= this.altmin) && !(this.mask & neigh.lock))

    where neigh is a Cell object. Using separate maps would result in:

    if((slope[x][y] < this.slopemax) && (slope[x][y] >= this.slopemin) && (alt[x][y] < this.altmax) && (alt[x][y] >= this.altmin) && !(this.mask & lock[x][y]))

    which is certainly not faster. But I don't suggest this to improve performances. The real way to avoid updating many times cell objects (or maps) slope or anything is to compute this value only when height map is quite finished. If you modify the height map anywhere in the main script... :wacko:

    I'm a object oriented programmer, so I'm not easy with your way to split in many arrays what is to me properties of a cell. In my experience, the main benefit of this approach is not in performances but it saves development and maintenance time. If  you library had a cell object and used mainly references to them, the change from PointXZ to Vector2D would have been much easier, because most often, you don't use the actual coordinates. And if there was accessors to the coordinates, it would have been enough to change a few lines of code in the cell object only.

    Friendly,

    • Like 2
  3. Hi !

    Just a word to say I have updated Github with the new features described in earlier posts.

    Since I have no new from the programming team, I suppose they will catch only the fractal part of the library. It can work as is, in alpha22 or alpha 23. I made provisions to ensure it works with both Vector2D and PointXZ. The various fractal painters have no dependency with the fractal generation of global map. This one has two rather large methods allowing to draw composite maps. They can be included or not, at will.
    I have nothing more in my TODO list, at least  for the library.

    Friendly,

  4. Hi all !

    Today, I'm making some kind of request for comments. I have finalized two methods of a placer and would like to have [random] map makers opinion and suggestions. Have they the use of such tools ? As they are or working another way ? I'm open to suggestions and requests for features now and later.

    The picture below shows the principle:

    concentric.jpg.8fe0dd02ded1d358cc9220e1487baec1.jpgThe center of the spot (deep green) is created with a good ol' clump placer. From this region, we deduce approximatively concentric regions with the placer. The first step is using a method initializing the placer with the central region. As all methods of this placer, it computes the border of the region (one tile wide), and can be used only in this purpose: get the border of any connexe region.

    Next another method expands the region, adding the chosen number of tiles. Those tiles can be added to the initial region or returned as a separate one (only the ring). This step can be repeated. Here, we call the function twice, and finally paint the border of the last created region.

    As one can see, the expansion is not isotropic nor regular, because there are other ways to create geometric shapes. The expansion is ruled by three factors: the distance to the center of the region, the tile height and slope, the weight of each one being a parameter of the placer. Some noise can be added too, which essentially change the border.

    Maybe other factors could be desirable too or instead. Suggestions are welcomed.

    I used this very spot to create the pictures below. Applying a fractal painter to the inner region, and raising the two rings to a fixed height:

    concentric-2.jpg.f9f476c9bfdf76e76a0fdd4dc3756e1b.jpg

    Spoiler

    - Mom ! Mom ! Look ! It's Gondor ! - Shut up stupid boy ! I don't want copyright problems...

    This one is created with exactly the same code, but, a different map seed gives another result:

    concentric-3.jpg.8a0a7d2fd6c11843bc37e1ca5159947a.jpg

    I hope the discussion will bring new interesting ideas.

    Friendly,

    • Like 2
  5. Hi !

    This is a (long) technical post. Don't read if you're not interested in programming. This is a proposal to address integration problems.

    Spoiler

    This integration suggestion relies on the fact any object holding a member pair x,y (and even z, duplicating y) can replace the PointXZ and the Vector2D in the rmgen library, since those routines use only these two values. What would be impossible in a strong typed language is perfectly possible in Javascript. One can call any rmgen function requiring an array of points using an anonymous object {x:10,y:7,z:7}, or a more elaborate one.
    So my proposal is to create a mapSize*mapSize array of more elaborated objects having the properties x and y (and even z if this prove to be useful) instead of PointXZ and Vector2D.
    The main advantage would be to avoid multiple copies of those objects representing the same tile. In present state, it's impossible to enhance them with other members (like the slope) without running into intricated synchronisation problems. It's a well known problem in databases.
    The changes needed are very few: there are only 11 lines in rmgnen where those points objects are created, for example:
    pointQ.push(new PointXZ(nx, nz));
    which can be replaced with:
    pointQ.push(g_TOMap.gCells[nx][nz]);
    Here we created not a new object, but a new reference to the unique object representing a tile, and no other changes are needed since the program only requires a reference to an object holding a coordinate pair. We can substitute anything to these points objects.
    The code below is only a proposal to show a possible implementation and give an idea.

    
    /**
     * First of all, a cell or tile object
     * @param x
     * @param y
     * @param slope
     */
    
    function Cell(x,y,slope) {
    	// if Vector2D exists and is not only a coordinates pair, set up inheritance.
    	// There are better syntaxes to do that, using 'class' for instance, but in this Javascript engine, they work not.
    	Vector2D.call(x,y);
    	// else:
    	this.x = x;
    	this.y = y;
    	this.z = y; // ??? why not, if compatibility is still needed ?
    	
    	this.slope = slope;
    	// ... add more members at will, inclination, height, whatever...
    	// but since they can be added dynamically, we don't need to worry about.
    }
    
    // if Vector2D exists and is not only a coordinates pair, set up inheritance:
    Cell.prototype = Object.create(Vector2D.prototype);
    
    
    Cell.prototype.update = function() {
    		// update members when g_Map.height is changed
    		let s1 = Math.abs(g_Map.height[this.x+1][this.y+1] - g_Map.height[this.x][this.y]);
    		let s2 = Math.abs(g_Map.height[this.x+1][this.x] - g_Map.height[this.x][this.y+1]);
    		this.slope = (s1 > s2) ? s1 : s2;
    		// ... more update if needed 
    
    }
    
    /**
     * 	mutator replacing direct setting of g_Map.height
     * Not mandatory but recommended if few modifications are made. Avoid using global updates, see below
     */
    Cell.prototype.setHeight = function(h) {
    		g_Map.height[this.x][this.y] = h;
    		this.update();
    		if((this.x > 0) && (this.y > 0))
    			gCells[this.x-1][this.y-1].update();
    		if((this.x > 0))
    			gCells[this.x-1][this.y].update();
    		if((this.y > 0))
    			gCells[this.x][this.y-1].update();
    }
    
    /**
     * Another access to the slope, more expansive, but always up to date
     * Can be provided along with the other solution.
     * 
     * @returns : slope
     */
    Cell.prototype.getSlope = function() {
    
    	let s1 = Math.abs(g_Map.height[this.x+1][this.y+1] - g_Map.height[this.x][this.y]);
    	let s2 = Math.abs(g_Map.height[this.x+1][this.x] - g_Map.height[this.x][this.y+1]);
    	return (s1 > s2) ? s1 : s2;
    	
    }
    
    // ================================ The cells map ============================
    
    /**
     * The cell map: 2D Array of cells
     * I made it an object, but since it's most probably a singleton, it can be directly created as an empty Array or a member of g_Map.
    */
    function CellMap() {
    	this.gCells = []; 
    	for (let i = 0; i < mapSize; i++)
    	{
    		this.gCells[i] = [];
    		for (let j = 0; j < mapSize; j++)
    		{
    			this.gCells[i][j] = new Cell(i,j,0); //At start, all cells have slope = 0, so we can build the array like this.
    		}
    	}
    }
    
    /**
     * creates an iterator returning all neighbor cells of this one (checking map boundaries)
     * 
     * To get all the existing neighbors of a cell c in loop:
     * 	for(let neighbor of c)
     * 
     * @param tObjMap a CellMap object
     * @returns
     */
    function setTilesNeighbors(tObjMap) {
    	for (let i = 0; i < mapSize; i++)
    	{
    		for (let j = 0; j < mapSize; j++)
    		{
    			tObjMap.gCells[i][j][Symbol.iterator] = function* () {
    				if(this.x > 0) {
    					if(this.y > 0)
    						yield tObjMap.gCells[this.x-1][this.y-1];
    					yield tObjMap.gCells[this.x-1][this.y];
    					if(this.y < (mapSize -1))
    						yield tObjMap.gCells[this.x-1][this.y+1];
    				}
    				if(this.y > 0)
    					yield tObjMap.gCells[this.x][this.y-1];
    				if(this.y < (mapSize -1))
    					yield tObjMap.gCells[this.x][this.y+1];
    				if(this.x < (mapSize - 1)) {
    					if(this.y > 0)
    						yield tObjMap.gCells[this.x+1][this.y-1];
    					yield tObjMap.gCells[this.x+1][this.y];
    					if(this.y < (mapSize -1))
    						yield tObjMap.gCells[this.x+1][this.y+1];
    				}
    			};
    		}
    	}
    }
    
    /**
     * To start up the thing
     */
    var g_TOMap = new CellMap();
    setTilesNeighbors(g_TOMap);
    
    /**
     * When most map creation is done (or at any time if accurate slope is needed)
     */
    CellMap.prototype.updateCellMap = function () {
    	for (let i = 0; i < mapSize; i++)
    	{
    		for (let j = 0; j < mapSize; j++)
    		{
    			this.gCells[i][j].update();
    		}
    	}
    }
    
    /**
     * Update only a region (in the future, could be added to painters modifying the map)
     * Region must be a Cell array of course.
     */
    function updateCellRegion(region) {
    	for(let c of region)
    		c.update();
    }
    

     

    The point is open to discussion, but my development will be stalled until I have a reply.

    Friendly,

     

    • Like 1
  6. Hi !

    Here come some previews of the fractal painter. As shown before in this  thread, it creates a little height map to modify a region of the g_Map. This region is defined by a pair of coordinates array, i.e. something like {x:some_value,y:some_value}. So it can be anything including Vector2D, cell objects and even PointXZ (converted on the fly). In the former version, the painter replaced indistinctly all the heights of the regions, which gives this:

    fractal-2.jpg.aabc6dc8080dc54a01cc78190aad64ce.jpg

    The result is not great, because it's possible to have chiasms at the border of the region. This mode is still available but the default is now to modify only the parts where the computed height is higher than original terrain:

    fractal-1.jpg.b453ad2815e7127129cfa6ae3c05a279.jpg

    The merge is now much better. What if we want not a mountains patch but a depression ? There is a new painter for that:

    fractal-4.jpg.379858347276dd33ea321381a54ed72b.jpg

    It creates a bumped hole in the map, but the default here modifies only the parts lower than the original map and it merges better:

    fractal-3.jpg.2b498f69bb9772a49f28dd18165ee0d8.jpg

    Now another painter. Mesas or cliffs:

    fractal-5.jpg.008b3d3f7ec3f7feb834a8cbe7494c58.jpg

    The top of the mesa (deep green) is perfectly flat, but this can be changed easily: at the end of the painting, the painter stores into a member array all the top points. The top of  the mesa is painted using this region. A new fractal painter (or anything else) can be applied to modify this part if something less plain is desired. The counterpart of this painter obviously creates holes whose bottom is flat:

    fractal-6.jpg.37221f6dbe6eaf909a8ab45de76d8de1.jpg

    In the same way, the flat part is available after painting, for morphing, painting or populating. Just put an Orthanc tower here, some Fangorn forests around and you've got Isenguard ring :P

    Please note the painter can be applied on any kind of regions, not only the rather round one I use here.

    More of it, everyone can define easily a new fractal painter because they all inherit from an abstract object holding all the code complexity. The child painters hold only the part where the two maps are mixed. Here is an example:

    function MyNewFractalPainter(area,centerHeight,bump,rough,progress, your_parameters_if_needed) {
    	AbstractFractalPainter.call(this,area,centerHeight,bump,rough,progress);
    	this.param1 = your_parameters_if_needed;
    }
    MyNewFractalPainter.prototype = Object.create(AbstractFractalPainter.prototype);
    
    MyNewFractalPainter.prototype.paint = function() {
    	let res = this.maps(); // this computes the temporary fractal map
    	let depx = res[0]; // these are the offsets of the fractal map in the main map. No need to change them.
    	let depy = res[1];
    	
    		for (let pt of this.region)
    		{
    		// melting the g_Map and the temporary map. This is the only part to modify.
    			let xg = pt.x - depx;
    			let yg = pt.y - depy;
    			if(g_Map.height[pt.x][pt.y] < this.tMap[xg][yg])
    				continue; // we skip this value
    			g_Map.height[pt.x][pt.y] = this.tMap[xg][yg]; // we keep this one
    		}	
    }

    Not in Github for now. The reason is I don't know yet in which format the flat regions created by the two last painters should be created.

    Friendly,

     

     

     

    • Like 3
  7. @stanislas69

    28 minutes ago, stanislas69 said:

    Well we did have alpha maps to ensure smooth transitions but I believe they are broken. So if c++ is one of your skills you might want to look into that I can fetch the tickets if you need me too.

    :P Hey !  Wait ! I'll finish this map script business first ! Then maybe I'll give a go since it' badly needed. I think I have seen somewhere some description of the feature and it's a pity if it is broken.
     

    34 minutes ago, stanislas69 said:

    About rotating textures it's not possible as far as I know

    OK, it's not a problem. I'll give a try rotating them by hand and test the result. For now, I don't want to put a lot of stuff into my mod, making it impossible to include in OAD distribution if this is the final decision.But if the idea is abandoned, then everything becomes possible ! Sparkling animated eye candies, strange looking terrains and fantasmagoric landscapes ! Hurray ! :blush:

    Friendly,

    • Like 1
  8. @stanislas69 Thanks for these suggestions. I don't think I need really more terrains for now or vegetation for now. My problem is more to select a set  giving good looking results. I'm rather happy with the green parts of the map and mountains, but much less with the others. Desert plants look too green, and in the kind of steppe lying between the players patches, the map exhibits some patterns. I believed at first there was something wrong with the RNG, but at a closer look it is not. Some terrains textures share the same layout, and the color only change. I wonder if is possible to rotate terrains when painting, as we can rotate actors. It would certainly break those patterns.

    Friendly,

  9. Hi !

    @elexis Thanks for your reply.

    3 hours ago, elexis said:

    Using what we currently have in rmgen is a necessity. I like maps inventing their own thing, but it should be so universal that every other map can be combined with this new feature. That the Placer, Painter and Constraint interfaces were implemented is a very good improvement.

    OK, I perfectly understand that, but including my work in the rmgen library is YOUR idea, not mine. I never asked for this, not because I don't want to collaborate, but because I'm conscious it's  really difficult to achieve. I did implement the Placer, Painter and Constraint in my development even if I don't use them and I don't need them, but if this not enough... My goal was not and is not to refactor rmgen or replace it but to experiment other ways to do the same things. At the highest abstract level, we all do exactly the same: creating a heightmap, painting it and placing entities, and at this level, many things look duplicates. Now, looking more deeply on them may reveal they don't give the same result, and according to circumstances and personal tastes, a method can be preferred. Why don't you say my fractal height map creation duplicates DiamondSquare which already exists ? They do exactly the same job using very similar algorithms.:unsure:

    Now it's your responsibility to pick in my work what you think possible/interesting to include in your development. But please, take a deeper look at my code/results and stop saying it duplicates other things. I read rather thoroughly the whole rmgen library code and I haven't the habit to reinvent hot water.

    3 hours ago, elexis said:

    buildRoads -> this looks like the RandomPathPlacer

    Can it create up to 100 or more optimal roads between arbitrary points, crossing the moutains in a realistic way, just in one call ? Mine does and not just looping road after road: the work is done in one pass on the map for all the roads at the same time. As a side effect, it returns how many roads nets it created (some points may not be connected because impassable mountains or lakes), a reliable test to know if some parts of the map are unreachable. I'm not trying to sell you my work as better than yours. Just let me say it's not a duplicate.:angel: And if you want me encapsulating this in a placer, it's five minutes work.

    6 hours ago, elexis said:

    paintPointsArray -> createArea + MapBoundsPlacer + TerrainPainter.

    I don't need that area, have already checked map boundaries, and TerrainPainter does nothing more than PlaceTerrain(). Where is the problem ? I don't expect you adding this to rmgen of course, but do you really want to deny me the right to create some convenience short functions ?

    4 hours ago, elexis said:

    putForestChunks should be Terrain painting or createObjectGroups

    The problem is creating the region covered with forests. The YPatchPlacer I use creates not the kind of regions rmgen placers does. Since they use geometric shapes, they define rather regular regions. You told me you had the problem in Ambush. Do you see anything of the like in my maps ? Now, I have not yet experimented fully all the capacities of this placer, but as a side effect, it provides the border of the region created . See the hedges in the pictures above, can you tell me how I could do that with rmgen with three lines of code ?:unsure:

    Now the placer should be able to expand a previously created region to create an irregular ring around, and even expand and find the border of any other region. For instance, it should be able to be fed with a ChainPlacer result and add some blur and noise at the border or only find the border. Do you really think it's nothing more than a HeightPlacer ? You may think these features are uninteresting and not worth to be integrated, but don't say it's a duplicate.:)

    4 hours ago, elexis said:

    I'm not particularly fond of the TileObjectMap prototype as it reinvents what is covered by the library for a great part, as it has a very confined use case and is a global that needs to be initialized, kept in sync and used throughout the map generation.

    Confined use case ? Are you conscious I'm creating this whole script map without any call to rmgen except to set up players things ? (Which don't work very well BTW, I have problems with this, and I'm tempted to substitute my own code) . Of course, you can say so because no random map script uses these features for now...:P

    That said, I know you don't like TileObjectMap for good reasons, but I already told you it was a fundamental design clash and not only something which could be changed or avoided easily. Rmgen has no cell object and splits  informations here and there in many places. And the reasons you give apply to rmgen as well: g_Map needs to be initialized, and you can put the slope in a cell object or in a separate map like heightmap library does, they will run out of of sync exactly at the same time and for the same reason. And what if the programmer inadvertently creates two or more inconsistent slope maps as (s)he can perfectly do for now ? Another problem is you can't use the === operator to know if two Vector2D or PointXZ objects are the same, because the library can freely create more than one object holding the same coordinates. So you must access the members and compare them, and you couldn't use a Set container with reliable results because it would eventually store duplicates. This is why I use a cell object, because in the reality, there is only one tile at x,y coordinates, not many. And this is why you can't add any member to this coordinates pair without running into awful synchronization problems. So why make them an object ? Of course, the cell object map should be in g_Map and should have mutators dealing with the sync problem, but we already discuss the matter and it was rejected for some reasons. Then I created my own map to avoid the risk to fool ExportMap() with unexpected additions.

    Now, I went somewhat further than I use to go. I'm not your boss and give only advices when I am asked. I'm not speaking the TRUTH here, but only my humble opinion: to me, it's poor design so I have no reason to follow it since I don't belong to your programming team. The proof is in the result: I don't need all the rmgen stuff and that's why object oriented programming exists. It simplify greatly the work and makes the design clearer and easier to enhance. I can add easily all compatibility code you want like the place() and paint() methods I don't use, constraints which are here as a proof of concept and never used as well, but don't ask me to rewrite the main code without a Cell unique object and map. I hope you'll understand this and will not see it as a mark of contempt about your skills or your work.

    Now, you are at the crossroad: if you drop the Cell object (or whatever variation it could be),  you drop all the content of placers.js and the random map script as well. I'm conscious it is a difficult choice because you are aware of your library consistency. But I think you should make a clear decision now and not expect I will solve magically the problem. For myself, I don't mind. My job can perfectly live in a mod without clashing with your development process and maybe it's the best solution.Again, do what you think best for OAD. :)

    Friendly,

    • Like 1
  10. Hi !

    As @stanislas69 suggested, I finally refactored the whole thing to make it a mod and push it into GitHub.

    There's no feature change from the previous version, but the script has been split in three files, two of them holding most of the code:

    • fractal.js: all the height map creation is here, including a fractal painter.
    • placers.js: various utilities, constraints and placers using the global tile map object.

    I removed from the script and the library all uses of points objects (PointXZ and Vector2D as well), because I don't need them. There is still one place where it can't be avoided: the place() method of the placers objects which must return an array of one of these types. To make things easier, I return those arrays through a convenience method (zoneToPoints) located at the beginning of placers.js. It creates a point array from a cell array and one can change this to the particular object type (s)he expects.

    Just for fun, an impregnable fortress :P:

    fortress.jpg.c4f5cd553d2e86e8112daf0a847b5c69.jpg

    I have to fix this...

    Friendly,

     

    • Like 1
    • Haha 1
  11. Hi...

    A new version of the script for testing purpose only. It includes roads. This feature is not only decorative: it allow to detect unplayable maps where no path exists between some players. Since the algorithm tries to find the easiest pathes (avoiding steep slopes, water and mountains if possible), it's not very likely you'll see a road crossing mountains, but in some cases, they make their way through the mountains like here, digging a trench where the slope is too steep:

    Roads.jpg.50514042c106d1b820aeb96cbb68de4a.jpg

    As usual, if you detect some bugs or ugly things, please report the size, the seed and the players number of the offending map.

    Thanks in advance for you interest and testing...

     

    EgyptOasis.zip

    • Like 5
  12. Hi !

    13 hours ago, elexis said:
    • At this point we might want to start using our code review platform instead, then I can post inline comments right in the code, might make things easier. http://code.wildfiregames.com/
    •  

    I'm sorry but I have no idea what I'm supposed to do on this site. Create an account ?

     

    14 hours ago, elexis said:

    A copy of the WIP library code in the map is not going to be committed by me. The library file counts 430 lines, the mapfile 1136 lines. The library must be removed from the map. A random map script should consist of a buch of texture/entity/height constants + 20-50 function calls. Lops should almost always be library functions. I suspect your demo map will be that short too if the code is sorted.

    As you say, this is work in progress, so the code is not clean yet and not ready to be committed. That's why it still contains "magic numbers", useless calls to warn() and cosmetic defaults. Now, my job is to create the best scripts I can. Stating which part should go to a library or not is yours because it's your project, not mine. I have for now no clue of what you finally intend to do: merge some parts into the existing libraries ? Create a new one ? I have understood you only want the fractal map creation and painter, is it still true ?  Now, the code needs not to be sorted: the library part (or more accurately, the part I would push into a library if it was only my project) is at the beginning of the file and ends at the InitMap() line.

    Now, about this:

    14 hours ago, elexis said:
    • The duplication in HeightArray.prototype.createAltitudes should be removed using a loop, possibly a function.
    • Whenever there are x and z, those should be in a vector. Most often one create the vectors in advance. Sometimes it improves performance, othertimes it doesn't. I haven't an inacceptable occurence yet and I don't want anybody to read ugly code to reduce the loading screen time from 15 to 12 seconds in the best case (some maps need 1-2 min).
    • "tObjMap.gCells[j][Symbol.iterator] = function* () {" never seen such code, probably avoidable. I have some reading to do. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
    •  

    First of all, I'm an old man about 70, and I began programming those game maps in assembler on 8086 PCs running at 15Mhz with 64Ko RAM. Not to declare you're an ignorant youngster, but to say that at this time, saving memory and execution time was not an option, and I have kept the habit to have always an eye on it. Now I think programs are mainly made for computers, and not for humans. Of course, it's good practice to make as easy as possible programs understanding for maintenance and communication purposes, but not at any cost. Then yes, this line for instance:

     

    14 hours ago, elexis said:

    let cent1 = (patches[dist.b1].barx - centPos) * (patches[dist.b1].barx - centPos) + (patches[dist.b1].bary - centPos) * (patches[dist.b1].bary - centPos);

    is not clear at first glance. What is it ? It's the square of the distance between a  point and the center of the map. Of course, I could write this using a nice vector function providing directly the length of a vector. It would add a call to a lot of functions including a square root, which is not a cheap function, and is here perfectly useless since I compare distances. Comparing their square works as well. The overhead looks not so important, but if you execute this code mapSize*mapSize times, it becomes significant. That's why I use here (and in every double loops of this kind) only arithmetic operations and no time consuming functions. This may lead to code replication and things looking awkward that's right, but I don't think it would improve anything: embed the createAltitudes code in a loop will only obfuscate the code, because the loop itself will not be trivial, and the reader will wonder at it, trying to understand what the hell it could do. Try it and you'll see.
    BTW, the iterator on tiles object provides the connected tiles to this tile object, if exist: so there's no need to check maps boundaries each time I need to fetch the neighbors of this tile and I do this very often.

     

    15 hours ago, elexis said:

    "crocodiles and palms on the shore" don't. That loop implements a custom coordinate randomization, but it should leave that up to createObjectGroups or createObjectGroupsByArea. One could implement a constraint for this g_TOMap.gCells[zx][zy].done check, though I'm not sure what that does and if it isn't redundant with tileclasses.

    Of course, it's possible to do that with rmgen functions, but I can't see the real benefit. The done flag in the cells map works along with the PatchPlacer and replaces a lot of tileclasses and constraints. If you look at the code part where I build the player environment with legacy functions, it's not more compact: you need to compute a lot of parameters instead.

    So what ? To me there's something obvious: except the cosmetic and good practices remarks, all your critics strike on the same topic, which IMO is a real design problem. My code rely on another data organization, not the rmgen one. That's why I use so little features of rmgen, not because I think they are bad or mine are better. Their design clash, at least for now. And suggesting to implement a constraint in rmgen fashion to check the done flag only hides the incoherence. I need  a cell object, where to store and retrieve easily informations about a tile. Some of them may exist here and there in some rmgen data structures like tile classes, but certainly not the key value, the done flag and the road pointer which are specific to my algorithms. You denied the interest to have some other informations stored as well, like the slope, saying you have already a slope constraint. But I don't use slope only as a constraint and I need its value, not only checking boundaries, same for height and other things. Asking to work only with what I can find in rmgen is like cutting my wings and asking me to fly.

    I agree, of course, with the need to provide a consistent interface in the whole set of map libraries, but if you expect me to do that within a week while the whole thing is still in development, the reply is clearly no. I say that without any angriness or frustration, it's just impossible. Remember: "Cheap, right and fast, choose two of them". Now, you may pick any part you want in my code and even rework it as you will to fit your guidelines.Do what you think best for OAD, it will be OK to me..

    Friendly,

    • Like 1
  13. 1 hour ago, elexis said:
    On 2/2/2018 at 8:50 PM, Pyrophorus said:

    FractalPainter

    Gimme gimme gimme!

    You have it in the YAMGLibrary I posted yesterday...

    1 hour ago, elexis said:
    18 hours ago, Pyrophorus said:

    The script is self sufficient. There is no need to install a library as I inserted all the needed code into.

    If it contains functions that ought to be in the library, then we'll put it in the library. Otherwise we're just adding work for later. But that's not too much work for now. I can transfer it.

    Well... Catch what you want in the script.

    1 hour ago, elexis said:
    18 hours ago, Pyrophorus said:

    Suggestions about decorations and such are welcomed of course, but not so deadly wanted

    The saver of all boring flat maps: createBumps. Also createPatches / createLayeredPatches to randomize the textures a bit. The mountains should be painted according to Slope with the SlopeConstraint, I can look into it when adapting to a23. Another huge improvement are paths (consider the one on danubius for instance). Lorraine Plaine also shows how paths (here: rivers) can have a treeline parallel to them. It's just hard to try to find a path that doesn't break the terrain, so might need lots of retry loops which in turn can consume too much time. But there are remedies for that too (first computing placeable areas, then finding coordinates within that area, rather than picking random coordinates and testing against constraint that are highly likely to fail).

    I put his in my TODO list.

    1 hour ago, elexis said:
    18 hours ago, Pyrophorus said:

    encounter some cases where some parts of the map are unreachable and even players probably cannot join, because mountains/water pools are on the way. I aware of this and will provide a solution.

    That's more problematic. Eventually we will need some reachability test. I also ran into this problem bigtime when reducing the radius of the playerbase flattening on Ambush and had to add a ramp so that the players can reach a nearby bluff (so that they can't be surrounded by an impassable bluff) #4993. A nice solution would be to somehow detect the unreachable case and use createPassage (a23 function) or a PathPlacer + elevation setter to force a way to somewhere else. But tricky to achieve and likely messy (typically paths should be placed first too and terrain only placed around it).

    Ahem... Do you think something like that ?

    preview-3.jpg.5ddd173a1ebec85b250de57f1de6dff7.jpg

     

    This is far from perfect, but one can catch the idea: the blue road digs its way through the mountains. This may solve the accessibility problem in cases like this one where the map is split in two by a probably impassable wall :

    preview-4.jpg.8953dff90dae51290b510f2ca849f1b3.jpg

    I worked faster than I thought and the linking of every caer by winding roads is OK as you can see (well.. enlarge the picture... :)). Ford crossing is already working: you have one near the middle of the map. I need to better manage mountain crossing, but it's not a difficult problem.

    2 hours ago, elexis said:
    18 hours ago, Pyrophorus said:

    swiss-knife placer

    Whut? Let me see. (I'm really missing a new centered placer, we only have the two ones, ClumpPlacer and ChainPlacer, which were iirc present when rmgen code was in C++ 12 years ago).

    Yeah, I think rmgen would really benefit to have algorithms of this kind along with the existing ones. This because rmgen mainly works with geometric shapes, circles, rectangles, more or less randomized. Instead, I make a heavy use of filling algorithms and more accurately, stack based filling algorithms. Its like pouring slowly some liquid on a surface. The liquid spot will grow until it eventually finds a boundary, and if the surface is not perfectly regular, will quickly turn to a twisted shape. The surface slope and the viscosity of the liquid rules the expansion. In the picture above, one can see examples. The green surfaces have been drawn with viscosity, using the tiles slope, within a height fork: this results in rather regular shapes, near rectangular, where the terrain is flat and unlimited. The players bases are drawn with even more viscosity and some noise on the borders, and, on the contrary, woods are set using little viscosity and much more slope influence, so the shape is much less compact. Even roads are set with a variant of this algorithm.

    Friendly,

     

    • Like 1
  14. 8 hours ago, Skhorn said:

    Awesome, although i think it deserves more randomness on terrain textures

    @Skhorn  I admit willingly the result is not fully satisfactory (I'm not very good at this terrain painting) but i can't see clearly what you mean. Terrains are already painted with four to five different textures chosen at random, but I imagine it is not exactly the randomness you have in mind. Could you make suggestions to improve this ?

    Friendly,

    • Like 1
  15. Hi !

    Here are some news:

    -- The YAMG library with only the fractal generation program.

    -- A first version of a random map script named "Egyptian oasis". Mountains, pools, and of course camels and crocodiles. Here is a preview:

    preview-2.png.21a5d43e2b64c0ab97f43fd4623cd488.png

    and a more general view:

    preview-1.jpg.94afd953d38c5d471cce98916388fe2c.jpg

    If I understood correctly, this script is a candidate to enter alpha23 OAD version. I certainly would be glad it would be so, but I doubt because it should be seriously tested before, and time is very short ! That's why I post it today. I know there are still some problems and improvements needed, but I need help to test the main features. Let explain a little:

    The script is self sufficient. There is no need to install a library as I inserted all the needed code into. It needs only the standard rmgen and Heightmap libraries. The map creation is done with merging two height maps: a chaotic one for the mountains and a rather soft to install players bases and battlefield. This allow to control rather exactly how much mountains and water are on the map. The real problem with this approach is finding reasonable places for players bases. Most scripts (and maybe all of them), set the bases first and create terrain and relief around. I do the contrary, and my attempt works rather well, I think, but within some limits. Every base has the same starting resources, but the map can advantage neatly some players. Is it playable as is ? I hope some people will get the script and create some maps to give their opinions and criticisms. Suggestions about decorations and such are welcomed of course, but not so deadly wanted. If you find something really ugly, please try to report the seed, map size and players number settings.

    The script works without error on any map size, but is not really designed to create tiny or small maps with a lot of players, just because there is not room enough. I think it works best on size within medium to giant.

    Doing so, you may encounter some cases where some parts of the map are unreachable and even players probably cannot join, because mountains/water pools are on the way. I aware of this and will provide a solution.

    One word about the code (for the braves who dare to put their nose into...). It is not very clean yet, of course. The map creation is not the fullMap() method of the YAMG library but a brand new one. New too is the swiss-knife placer I developed for this. It's a very versatile placer allowing to create quite any area you want, particularly if you want to avoid geometric shapes and patterns. Quite everything is placed with it, except the players starting units and resources which are set with rmgen standard procedures.

    Waiting for you feedback...

    Friendly yours,

    YAMGLibrary.js

    EgyptianOasis.zip

    • Like 6
  16. On 2/1/2018 at 3:46 PM, elexis said:

    Given that you speak of InitMap, you must still be developing with alpha 22. That is reasonable on one hand because the library in alpha 23 changes every day. But judging from the screenshots, your project matures and we should start to think about committing something to our codebase.

    What matters most to me would be to throw out all redundant code. For instance I assume there is no invention in entiy placement, is there?

    Not yet, at least, given the two weeks delay, I shall not have new things stable enough to get into your codebase. I have made some progresses with the  player base setting, but I have still some way to go before publishing a "good playable map".

    Now, I can give you the map creation code and the FractalPainter at once. This code don't rely on rmgen at all. The only point is in the FractalPainter. It uses the "points" member of an area to get coordinates points.x and points.z. I don't know if switching to Vector2D impact this or not. I think it's better to let you choose exactly where to insert the code, and even rename objects if you want. You can either choose to incorporate the fullMap method of the HeightArray object or not. It's more or less a template to use the fractal map creation and you may find it's not the place for this in a library.

    Along with alpha23, I agree with you, but I think I have not enough time left to install the dev version whole shebang AND finalizing something of a "good playable map". So, unless you disagree, I will focus on this last goal. Maybe the script shall be more alpha22 than alpha23, but I imagine you maintain some ascending compatibility :)

    Friendly,

     

     

  17. Hi,

    The artifacts problem in my generation code comes from the the algorithm working on squares, and the height map itself being squares. I think this is why may various attempts to blur the map introduce new artifacts of another kind. Our eyes are trained to detect any geometric shapes even in complex pictures and it's very difficult to fool them.

    That said, it's possible to improve the result, for instance using 'globalSmoothHeightmap(1);', which is probably highly advisable: it doesn't change the map too much and improves certainly regions accessibility. But I guess map decoration should be enough to solve the problem. Here is a map, not painted at all, showing clearly some artifacts:

    sample-1.jpg.8ef24dc05671edbdbaa3c77d34f4e4aa.jpg

    And npw the same, painted in a very plain way:

    sample-2.jpg.b737ef41563a1e7631b07eb5965ab765.jpg

    The artifacts are less noticeable I think, and will probably even less if more decorations are added.

    18 hours ago, elexis said:

    As of #4950 we also have a logger showing how long each operation of the map takes. This way we can identify bottlenecks. How much faster is that bitmask invention? I fear the increased complexity might just make things harder to read and maintain. It's easier if all maps use the same style and all maps can use all features and benefit from the same improvements. Which arrays are we talking about actually? I never timed the RangeOp code used by tileclasses.js.

    Actually, I didn't notice the TileClass object had an inclusion map which is in fact similar to the "bitmask invention", at least in performance  (it costs much more memory, but this is not a problem). And of course, increasing complexity must be avoided if only little benefits are expected.

    I made some quick tests on giant maps to identify where some bottlenecks could be.

    - First of all, creating a flat map with no painting at all, already takes a few seconds: just  loading the libraries and executing InitMap() and ExportMap(). It certainly could be sped up loading only what is needed.
    - Creating a height map, smoothing it, is very fast and don't increase significantly execution time. Adding textures on terrain may add a second. Not a big deal.
    - The real problem I found is with placing objects. In the giant map below, I created forests, painting only forest floor, omitting only to place  the trees (with placeObject() ). Costs one second or two even if there are large forests (28 994 trees and a lot more tiles to scan):

    sample-3.jpg.8ed4bf657922fa892ac609ce1f505558.jpg

    Trying to place the 28 994 trees on the snowy areas lead to disaster: Atlas hangs during a minute or so and finally crashes (I have an i7 processor and a lot of RAM). :o

    Friendly,

     

    • Like 1
  18. 21 hours ago, elexis said:

    I'd take the unique terrain generation + sample map packet without the redundancy.

    The parts 1 and 2, if I understand correctly.

     

    21 hours ago, elexis said:

    I assume you're working with alpha 22 code, but that was rewritten for 5 months. I can rebase it, but I expect the TileObjectMap to be dropped and or merged.

    Actually, there is no call to rmgen in those parts, only access to g_Map.height. It should work as is.

    I agree with what you and Fexor say about the TileObjectMap (or whatever name we call it). It's seems to me the design is not mature enough to syntheses all those features in a single object.

    21 hours ago, FeXoR said:

    Recomputing those maps over and over again is indeed insanely inefficient. I have the same problem with the collision map (updating whenever an entity is placed is fast but means to hook in the collision system in even if it's not needed - though no performance impact than with a collision.enabled flag. Updating whenever needed means "number of uses" more calculations of the entire map). I think the way to go is "update manually when needed" for those heightmap related maps - though this is prone to induce bugs by oversight. Updating whenever used and update needed might be appropriate in some cases - a copy of the underlying object could be stored and - on change - update the derived thing if used (this is somehow against the "direct access" policy I usually prefer though).

    Yes, I'm aware of this too. The slope member, for instance, remains accurate while you avoid modifying the height map after computing the slope, and reading the various random map scripts, one can see height map manipulation can occur anywhere, not only at the beginning.

    I'm aware of efficiency too: javascript is Ok, but... really slow. This is the reason why I added a 'lock' member to my tile object, because I think testing a bitfield with a mask is much faster than fetching one or more arrays to enforce constraints. It's not difficult to create a tile class from a bit pattern or set a lock bit from a tile class, but it must be done 'manually' which is error prone.

    BTW, I'm rather surprised rmgen don't provide set operations on tile classes. Maybe, I didn't dig enough :blush:

    Friendly,

    P.S. The idea I had to remove artifacts don't work, or more accurately, removes them but introduces new ones ! too bad !

    • Like 2
  19. Thanks for your replies,

    I have just got an idea to remove the artifacts. Not sure it will work, but I'll test it today.

    Now I have still no idea of what you will do with my code. Actually, there are three parts in the library, each of them being independent.

    1-  The full map creation.
    2- The FractalPainter (standalone)
    3- The TileObjectMap on which rely some placers (the fillerPlacer and the road placer in the future, and maybe more) and constraints (BTW, there is already a slope constaint in the library ! :P).

    Actually, the third object duplicates and enhances the g_Map object, but thanks to Javascript, those members could be added directly to g_Map. My design should not be used as is, but I think it would be nice to add the slope to g_Map for instance. The tile slope is useful in many contexts, and computing it each time one needs it is not very efficient. The slope constraints are trivial to write when you have the slope in g_Map . Same with the iterator which provides the tiles connected to any tile (checking map boundaries), and maybe other members.

    Friendly,

  20. Hi,

    Still a work in progress, but there are significant changes which can be seen in the 'aRandomMap.js':

    - I have embedded a rather complex map creation into a 'fullMap' method of HeightArray object. More control is still available anyway as before.

    *** Edit: I made a painter object to apply the height creation algorithm to a defined area in the map. See some results here:

    fractalpainter-1.thumb.jpg.266b3678a3af4bfee61d5967bf8a7532.jpg

    The area is created with a ChainPlacer, colored in black and modified with the painter. Here we have some chaotic terrain created  on a flat field. More interesting maybe is the contrary: smoothing a rough area:

    fractalpainter-3.thumb.jpg.24cb8eacdec174c6d4478c06065c5316.jpg

     

    fractalpainter-2.thumb.jpg.89e592c2396a5d27185b25c61eb9b585.jpg

    - All the ground painting and trees setting is now done with createArea  calls, using two new placers objects. The ground painting is not very interesting and probably duplicates other things in rmgen, but it takes into account the tiles slope. The YFillerPlacer is more original and despite elexis opinion, is not another kind of HeightPlacer. It allows to define areas which crawl along the relief, and has much more in common with the ChainPlacer.

    This way, I think the library can now nicely melt into rmgen.
    Now, my next step is to provide a road Placer (I know, there is already one in rmgen, but mine is a very different algorithm and will not give the same result).

    Friendly,

     

     

    yamg.zip

    • Like 2
  21. Hi !

    Thanks for your quick reply.
    Except your first three points which are not problematic, I would say maybe I wasn't clear enough about the painting (textures and objects) used in the library. I'm conscious they could be greatly improved using the rmgen library. Actually, I didn't pay much attention to them and the way I use them is only a proof of concept to illustrate height based area creation.

    In other words, what is good and new (hopefully) in my filling algorithms is the area definition. The painting and actors placement are a quick and dirty hack and you're right to direct me to improve it. I'm not quite sure I have fully understood the rmgen code yet, but AFAICS, createArea rely on placer objects of various kinds. Maybe a way to achieve a better integration could be to create new placer classes which could be used like the existing ones.

    I already had the idea to supply some functions which would return a tile class using the data created in the library (slope, terrain kind and so on). Maybe it could be placers too, and so I could remove from the library all this painting and placing code which lacks generality.

    Friendly,

  22. Hi everyone (and happy new year !)

    Here come the code changes asked by @elexis :excl:Please note there is no new functionality and this library version works not with previous posted scripts. This is not an update !!!

    So, I created two new object definitions: one dedicated to the height map creation, another one for map decoration, each one can be used independently. There are no more simple functions in the library (except a private one), only members functions. Similarly, I removed all global variable uses in the function and replaced them with parameters. I think the code is now fully encapsulated, but it is not final yet: I have to set default parameters and rework fully the comments.

    Please tell me if this fits your requirements...

    Friendly,

    yamg.zip

    • Like 1
  23. 22 hours ago, elexis said:
    • TWO_PI -> 2 * Math.PI
    • paintMap() -> The map should decide what textures should be painted. There may be no references to globals in the library (tWater, tCliff, ...)
    • Names like "fillWith" are a bit common, other libraries might want to add a function with that name too. So we should probably move all of that into a prototype. (I can do that if it's difficult, but it's not really hard, just look at the constraints prototypes for example)

    OK... That's easy to do. I'll try to add some little (useful, I hope) things, and I will post a new version very soon.

    Friendly,

    • Like 2
×
×
  • Create New...