TerrainGraphics questions

The place to post your WML questions and answers.

Moderator: Forum Moderators

Forum rules
  • Please use [code] BBCode tags in your posts for embedding WML snippets.
  • To keep your code readable so that others can easily help you, make sure to indent it following our conventions.
Post Reply
BlueWinds
Posts: 8
Joined: December 29th, 2016, 7:53 am

TerrainGraphics questions

Post by BlueWinds »

I have a pet project I've been working on - rendering wesnoth .map files with javascript. An odd thing, but I'm having fun. :) I've run into a couple of oddities in the TerrainGraphics output from the wmlparser scripts, and was wondering if someone could help clarify.

1) When I use wmlparser3.py, I get a couple of image paths that don't exist. For example:

Code: Select all

"image": [
    {
        "name": "mountains/basic_range31.png",
        "base": "88,107"
    },
    {
        "name": "mountains/basic_range32.png",
        "base": "144,107"
    },
    ...etc...
In data/core/images/terrain/mountains there is basic_range3_1.png, 3_2.png, 3_3.png, etc, but not basic_range31.png, 32, 33.
How does the game know to load 3_3, rather that 33, and why does wmlparser3.py (or wmlparser2.py) output names which don't exist?

2) set_no_flag base vs. base2 seems to cause overlapping rules. Here are another couple of rules I'm trying to figure out:

Code: Select all

# Mountains
{
    "image": [{
        "variations": ";2;3;4;5;6",
        "name": "mountains/basic@V.png",
        "base": "90,107",
        "center": "90,144"
    }],
    "map": "\n, *\n* , *\n, 1\n* , *\n, *",
    "tile": [{
        "pos": "1",
        "set_no_flag": "base2",
        "type": "Mm"
   }],
   "probability": "100"
}

# Hills
{
    "image": [{
        "name": "hills/regular@V.png",
        "layer": "-1000",
        "base": "90,144",
        "center": "90,144"
    }],
    "map": "\n, *\n* , *\n, 1\n* , *\n, *",
    "tile": [{
        "pos": "1",
        "set_no_flag": "base",
        "type": "Mm"
    }],
    "probability": "100"
  }
It looks to me like these should both match for the same terrain - their maps are the same, the tile.type's are the same, and the set_no_flag's are different. The mountains should match, setting the base2 flag, then the hills should match, setting the base flag, and both images get drawn. What am I missing, such that the hill graphic doesn't always overlay the mountain one?
User avatar
zookeeper
WML Wizard
Posts: 9742
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: TerrainGraphics questions

Post by zookeeper »

BlueWinds wrote:I have a pet project I've been working on - rendering wesnoth .map files with javascript. An odd thing, but I'm having fun. :) I've run into a couple of oddities in the TerrainGraphics output from the wmlparser scripts, and was wondering if someone could help clarify.

1) When I use wmlparser3.py, I get a couple of image paths that don't exist. For example:

Code: Select all

"image": [
    {
        "name": "mountains/basic_range31.png",
        "base": "88,107"
    },
    {
        "name": "mountains/basic_range32.png",
        "base": "144,107"
    },
    ...etc...
In data/core/images/terrain/mountains there is basic_range3_1.png, 3_2.png, 3_3.png, etc, but not basic_range31.png, 32, 33.
How does the game know to load 3_3, rather that 33, and why does wmlparser3.py (or wmlparser2.py) output names which don't exist?
I'm not completely sure, but I'd suspect a bug in wmlparser; the actual WML does have the underscores in them, like here (the last macro argument):

Code: Select all

name={BUILD_IMAGE IMAGE_SINGLE {IMAGESTEM} _1}
My guess would be that while the game itself parses that correctly and considers the whole "_1" as the argument, wmlparser interprets the underscore as a translation marker and loses it. That's the only explanation I can come up with.
BlueWinds wrote:2) set_no_flag base vs. base2 seems to cause overlapping rules. Here are another couple of rules I'm trying to figure out:

Code: Select all

# Mountains
{
    "image": [{
        "variations": ";2;3;4;5;6",
        "name": "mountains/basic@V.png",
        "base": "90,107",
        "center": "90,144"
    }],
    "map": "\n, *\n* , *\n, 1\n* , *\n, *",
    "tile": [{
        "pos": "1",
        "set_no_flag": "base2",
        "type": "Mm"
   }],
   "probability": "100"
}

# Hills
{
    "image": [{
        "name": "hills/regular@V.png",
        "layer": "-1000",
        "base": "90,144",
        "center": "90,144"
    }],
    "map": "\n, *\n* , *\n, 1\n* , *\n, *",
    "tile": [{
        "pos": "1",
        "set_no_flag": "base",
        "type": "Mm"
    }],
    "probability": "100"
  }
It looks to me like these should both match for the same terrain - their maps are the same, the tile.type's are the same, and the set_no_flag's are different. The mountains should match, setting the base2 flag, then the hills should match, setting the base flag, and both images get drawn. What am I missing, such that the hill graphic doesn't always overlay the mountain one?
It's the layers. The hills get layer=-1000, causing them to always be drawn under the mountain which has no layer and thus defaults to 0. The base coordinates only affect draw order between images that have the same layer; an image with a higher layer can never be drawn behind an image with a lower layer, no matter what their base coordinates are. Or at least that's my understanding. :mrgreen:

P.S. There's also some other oddities with the mountain rules, such as hills transitions getting drawn in wrong and unnecessary places between mountain hexes, and a hills base being drawn under mountains which already cover the whole hex, but as it happens I'm right now working through some of that stuff to fix it.
BlueWinds
Posts: 8
Joined: December 29th, 2016, 7:53 am

Re: TerrainGraphics questions

Post by BlueWinds »

zookeeper wrote: I'm not completely sure, but I'd suspect a bug in wmlparser; the actual WML does have the underscores in them, like here (the last macro argument):

Code: Select all

name={BUILD_IMAGE IMAGE_SINGLE {IMAGESTEM} _1}
My guess would be that while the game itself parses that correctly and considers the whole "_1" as the argument, wmlparser interprets the underscore as a translation marker and loses it. That's the only explanation I can come up with.
"A bug" was my guess as well. For now I've been working around it by trying loadImg(name) || loadImg(name.replace(/(\d)(\d)/, '$1_$2').
zookeeper wrote: It's the layers. The hills get layer=-1000, causing them to always be drawn under the mountain which has no layer and thus defaults to 0. The base coordinates only affect draw order between images that have the same layer; an image with a higher layer can never be drawn behind an image with a lower layer, no matter what their base coordinates are. Or at least that's my understanding. :mrgreen:

P.S. There's also some other oddities with the mountain rules, such as hills transitions getting drawn in wrong and unnecessary places between mountain hexes, and a hills base being drawn under mountains which already cover the whole hex, but as it happens I'm right now working through some of that stuff to fix it.
Doh, I was forgetting to assign the default layer of 0 to images if they didn't have one. Thanks for your quick reply!

Another question, if you don't mind, about 'base', which I've been mostly ignoring until now. I get that one purpose of base[1] (the y coord) is that it helps sort the order images are drawn, but does base do anything else? Does base[0] (the x coord) do anything at all?
User avatar
zookeeper
WML Wizard
Posts: 9742
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: TerrainGraphics questions

Post by zookeeper »

BlueWinds wrote:"A bug" was my guess as well. For now I've been working around it by trying loadImg(name) || loadImg(name.replace(/(\d)(\d)/, '$1_$2').
If it looks like wmlparser can't easily be fixed to handle those cases right, then we can always simply disambiguate the WML by adding parentheses; mountains.cfg seems to be the only place where those kind of bare macro arguments starting with an underscore occur in mainline.
BlueWinds wrote:Doh, I was forgetting to assign the default layer of 0 to images if they didn't have one. Thanks for your quick reply!

Another question, if you don't mind, about 'base', which I've been mostly ignoring until now. I get that one purpose of base[1] (the y coord) is that it helps sort the order images are drawn, but does base do anything else? Does base[0] (the x coord) do anything at all?
No, as far as I can tell x is ignored, and the sorting happens exclusively according to the y value... which in a case of convenient coincidence I just stumbled upon yesterday, myself.
BlueWinds
Posts: 8
Joined: December 29th, 2016, 7:53 am

Re: TerrainGraphics questions

Post by BlueWinds »

It looks like basex does show up in one more place - calculating the new basey when doing a rotation. I'll have to add that to my code, I wasn't adjusting the 'base' during rotations.

https://github.com/wesnoth/wesnoth/blob ... r.cpp#L565

builder.cpp is the file I wish I'd known about when I started! By sheer convergent evolution a lot of my function names match the ones in there, but being able to read the actual source would have (and still will) save me a bunch of trial and error. I'll probably get the map renderer working this weekend - getting close now, just need to track down some bugs in the rules matching - and will post some pictures and the code.
User avatar
zookeeper
WML Wizard
Posts: 9742
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: TerrainGraphics questions

Post by zookeeper »

BlueWinds wrote:It looks like basex does show up in one more place - calculating the new basey when doing a rotation. I'll have to add that to my code, I wasn't adjusting the 'base' during rotations.

https://github.com/wesnoth/wesnoth/blob ... r.cpp#L565
Right, yeah of course. I forgot about that.
BlueWinds
Posts: 8
Joined: December 29th, 2016, 7:53 am

Re: TerrainGraphics questions

Post by BlueWinds »

Adding parens to mountains.cfg doesn't fig the issue. It still outputs mountains/basic_range23.png, etc even when using

Code: Select all

{BUILD_IMAGE IMAGE_SINGLE {IMAGESTEM} (_1)}
Easy bug to workaround on my end, so not going to spend time looking for the root cause / fixing wml parser right now. Maybe someday. :)

-------

What does ~CS(a, b, c) in an image name mean? It seems to do a color shift somehow, but AFIKT is undocumented. I'd be happy to document it for the wiki, or add link to it from the TerrainGraphicsWML page if it already exists somewhere and I just can't find it.

-------

Other funny thing I noticed - turns out when you draw a single mountain on top of grass, the transitions, which look decent, are actually the hill transitions, because it draws the hill (including transitions) then puts the mountain over the top. There are no rules that use the mountain transitions anywhere!

https://github.com/wesnoth/wesnoth/blob ... mall-n.png
^ totally unused. Wesnoth uses this instead:
https://github.com/wesnoth/wesnoth/blob ... ular-n.png

The mountain ones look a bit better, IMO, since they're designed to go with the mountain tiles.

Thanks again for all your help.
User avatar
James_The_Invisible
Posts: 534
Joined: October 28th, 2012, 1:58 pm
Location: Somewhere in the Northlands, fighting dark forces
Contact:

Re: TerrainGraphics questions

Post by James_The_Invisible »

What does ~CS(a, b, c) in an image name mean?
It is a function that shifts colors of an image. It is documented here.
BlueWinds
Posts: 8
Joined: December 29th, 2016, 7:53 am

Re: TerrainGraphics questions

Post by BlueWinds »

James_The_Invisible wrote:
What does ~CS(a, b, c) in an image name mean?
It is a function that shifts colors of an image. It is documented here.
Thanks! I'm not registered to edit the wiki yet (just sent in a request), but once I have access I'll add a link to that from https://wiki.wesnoth.org/TerrainGraphicsWML. I'll also update the description on the RefernceWML page to mention that it's used for more than just faction colors.
User avatar
zookeeper
WML Wizard
Posts: 9742
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: TerrainGraphics questions

Post by zookeeper »

ImagePathFunctions are usable as part of any image path anywhere, so while the page can of course be linked to from TerrainGraphicsWML, there's nothing special about them being supported in that context.

Anyway, if you intend to make the renderer accurate, you'll have to also handle the ~BLIT, ~MASK and ~O functions. And almost any other function could end up being used sooner or later.
BlueWinds wrote:Adding parens to mountains.cfg doesn't fig the issue. It still outputs mountains/basic_range23.png, etc even when using

Code: Select all

{BUILD_IMAGE IMAGE_SINGLE {IMAGESTEM} (_1)}
Easy bug to workaround on my end, so not going to spend time looking for the root cause / fixing wml parser right now. Maybe someday. :)
Right, I guess that actually makes sense. Parentheses only delimit the argument, but don't affect how the content gets interpreted. Adding quotes instead might work.
BlueWinds
Posts: 8
Joined: December 29th, 2016, 7:53 am

Re: TerrainGraphics questions

Post by BlueWinds »

Turns out that the flu sucks, so not going to finish this weekend. Still more bugs to work through. Here's a WIP screenshot, though: http://imgur.com/a/ZFuut. All the borders are currently offset one hex south of where they should be, but it draws simple terrain pretty well.
zookeeper wrote:ImagePathFunctions are usable as part of any image path anywhere, so while the page can of course be linked to from TerrainGraphicsWML, there's nothing special about them being supported in that context.

Anyway, if you intend to make the renderer accurate, you'll have to also handle the ~BLIT, ~MASK and ~O functions. And almost any other function could end up being used sooner or later.
Now that I've added a redraw step to rule compilation to support CS, adding more image manipulation functions is pretty easy. Here's the opacity one:

Code: Select all

shiftOpacity = (srcCanvas, opacity)->
  imageData = srcCanvas.getImageData(0, 0, src.width, src.height)
  data = imageData.data

  for i in [0...data.length] by 4
    data[i + 3] *= opacity

  srcCanvas.putImageData(imageData, 0, 0)
High level languages (coffeescript, in this case) are nice.
zookeeper wrote:Right, I guess that actually makes sense. Parentheses only delimit the argument, but don't affect how the content gets interpreted. Adding quotes instead might work.
Quotes work, good call. Created pull request, we'll see if I did it right. https://github.com/wesnoth/wesnoth/pull/907
BlueWinds
Posts: 8
Joined: December 29th, 2016, 7:53 am

Re: TerrainGraphics questions

Post by BlueWinds »

Let's say I have these two rules:

Code: Select all

{
   "tile": [
    {
     "set_no_flag": "base",
     "x": "0",
     "y": "0",
     "image": [
      {
       "name": "water/coast-tropical-A[01~15].png~CS(40,0,-30):110",
       "layer": "-1000"
      }
     ],
     "type": "Ww,Wwr,Wwf"
    }
   ]
  },
{
  "map": "\n, *\n* , *\n, 1\n* , *\n, *",
  "probability": "100",
  "tile": [
  {
    "type": "Ds",
    "set_no_flag": "base",
    "pos": "1"
  }
  ],
  "image": [
  {
    "base": "90,144",
    "variations": ";2;3;4;5;6;7;8;9;10;11",
    "center": "90,144",
    "layer": "-1000",
    "name": "sand/beach@V.png"
  }
  ]
}
I'm dealing with this section of a map, with the first water at (1,1):

Code: Select all

   .     .
.    Wwt
  Wwt    .
.    Ds
  Ds     .
.     .
   .     .
Here are some diagrams of how the rules match onto the map:

Code: Select all

Water at 1,1 and 2,1
   .     .
.    Wwt
  Wwt    .
.     .
   .     .
.     .
   .     .

Sand at 0,1
   .     .
.     .
   *     .
*     *
  Ds     .
*     *
   *     .

and at 1,1
   .     .
.     *
   *     *
.    Ds
   *     *
.     *
   .     .
So here's the top-left corner of each rule that matched:

Code: Select all

   .     .
s     w
   sw    .
.     .
   .     .
.     .
   .     .
Notice how the water and sand are offset horizontally but on the same row. Once the sand is moved due to the image's "center", I end up actually drawing this:

Code: Select all

   .     .
.     w
   w     .
.     .
   s     .
.     s
   .     .
What am I missing? It seems odd that the rules match at different locations, but I'm not sure how it should be working. Why are some one-tile rules surrounded by *s and offset with "center", but others just use a single "tile" rule with no center?
User avatar
zookeeper
WML Wizard
Posts: 9742
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: TerrainGraphics questions

Post by zookeeper »

I can't interpret all your diagrams, but:

There's no particular reason why the beach and water base tile rules are different like that. The beach sands simply use TERRAIN_BASE_RANDOM which eventually resolves to GENERIC_SINGLE_PLFB which uses a map-based rule which can accommodate bigger-than-hex images, whereas water uses ANIMATED_WATER_15 which doesn't.

However, one detail regarding map= and centering you should note (and which might be part of your current problem), is that if you have a map= like this...

Code: Select all

map="
, *
* , *
, 1
* , *
, *"
Then one might think that to center an image onto that you'd need center=90,108, but the game doesn't actually know how to deal with a map= that doesn't have a full hex at the top left corner. So, internally that ends up getting an extra top row and thus treated as this, meaning that you'd need center=90,144 instead:

Code: Select all

map="
. , .
, *
* , *
, 1
* , *
, *"
Finally, if the rule has rotations then you end up with every other rotation getting that treatment, which is why half of for example castle and wall tiles have a different vertical size than the other half (usually 144 and 180).
BlueWinds
Posts: 8
Joined: December 29th, 2016, 7:53 am

Re: TerrainGraphics questions

Post by BlueWinds »

Turns out I was parsing the "map" key wrong in a pretty fundamental way. ^^;; With that fixed, I'm getting to the point where full fledged maps look decent. Here's a couple of examples. http://imgur.com/a/Kbibz

I'm still having problems with Keeps and a few types of terrain with similar borders, as you can probably see. The trouble is that all the walls are rendering in the same place. Given this map:

Code: Select all

Gg, Khs, Gg
Gg, Gg, Gg
and this rule:

Code: Select all

[terrain_graphics]
	map="
2
,  3
1"
	probability=100
	rotations="tr,r,br,bl,l,tl"
	[tile]
		pos=1
		set_no_flag="wall-@R0"
		type="Kh,Kd*,Ko,Kv"
	[/tile]
	[tile]
		pos=2
		set_no_flag="wall-@R2"
		type="!,K*"
	[/tile]
	[tile]
		pos=3
		set_no_flag="wall-@R4"
		type="!,K*"
	[/tile]
	[image]
		base="54,72"
		layer=0
		name="castle/keep@V-convex-@R0.png"
		variations=";2;3;4;5;6"
	[/image]
[/terrain_graphics]
I end up with six rotations matching on the single keep hex, as expected - but the trouble is, the top left corner of every image is in the same location. They end up overlayed on each other, all in one spot (http://imgur.com/a/5jKQY), rather than spread out around the cobbles.

As I read the wiki, "base" is only used for determining layering - it doesn't influence image position. What part of this rule should be causing the borders of the keep to be placed as expected (forming a wall it) rather than in a big heap?
User avatar
zookeeper
WML Wizard
Posts: 9742
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: TerrainGraphics questions

Post by zookeeper »

Without an [image] center=, the images are by default aligned to the top left corner of the map=. But when you're dealing with multihex images with rotations, the stuff in my previous post becomes relevant.

So, if you have this:

Code: Select all

map="
2
,  3
1"
Then the subsequent rotations will internally end up being like this:

Code: Select all

#rotation r
map="
.
,  2
1
,  3"

#rotation br
map="
1
,  2
3"

#rotation bl
map="
.
,  1
3
,  2"

etc
In your example, the unrotated (tr) base rule places castle/keep@V-convex-tr.png and aligns it to the top left corner of the hex north of the castle hex.

The next rotation (r) places castle/keep@V-convex-r.png and aligns it to the top left corner of the hex north of the castle hex (because of that internally-created dummy padding hex).

The next rotation (br) places castle/keep@V-convex-br.png and aligns it to the top left corner of the castle hex.

If the rule does use [image] center= then, as you'd expect, that causes the image to be centered on those coordinates on the map= (again, calculated from the top left corner). However, I think the engine does automatically adjust the coordinates when rotating, so if you have a rotated map= like this with [image] center=90,144 then the image does actually stay in the center of hex 1 without bouncing up and down on every rotation:

Code: Select all

map="
, 2
* , *
, 1
* , *
, *"
Post Reply