DICE PACKS BUNDLE
  1. #1

    Generating LOS via SVG from GIMP

    Hi all,

    I create a lot of my own maps, and wanted the ability to use GIMP to select the negative areas of the maps, and use that as the basis for my walls.
    I know theres been some conversations about how to create LOS Walls, in the forums, but this is my own take on it.

    The process is based around making a selection in GIMP.
    Screenshot 2020-10-25 154041.png

    Convert that to a PATH, (Menu: Select-> To Path)


    Now Export it as a SVG File.
    Screenshot 2020-10-25 154140.png

    The SVG export from GIMP seems to be pretty specific SVG format, so I don't promise this works for all SVGs.
    Screenshot 2020-10-25 155356.png

    Then run the below powershell script that, for all SVG files in a directory:
    1. Gets the image size. (This is needed to convert to FGU's Frame of Reference: 0,0 at the centre, and orientation: Y axis reversed)
    2. Parse the path coords, and storing them in a List.
    3. Parse the List, removing doubles, (I extended this to reduce the number of points, by distance.)
    4. Output the list to the same filename as the svg, in the XML Occluder format that FGU's db.xml expects.

    Then you take that xml, find your Image Layer in your dbxml and put it in there.
    Screenshot 2020-10-25 155432.png

    Done!!
    Screenshot 2020-10-25 155537.png


    Note it only handles walls. But of course, you could just make selection paths for all the different features in gimp, and just toggle their type in the db.xml.

    Hope this helps someone!!
    Muklin

    Here is the powershell:

    Code:
    param(
        [String]$filePath = $PWD,
        [Int]$SmoothDistance = 10
    )
    
    if(test-path -path $filePath){
        $files = Get-ChildItem -name $filePath -Include *.svg
    }
    
    $files | %{
        $file= $filePath+"\"+$_
        $filename = $_ -replace ".svg",".occ"
        Write-Output "Processing $file..."
    
        if([System.IO.File]::Exists($file)){
            $content = get-content $file
        }else{
            Write-Error "Could not find file $file"
            exit 2
        }
    
        $matchResult = [regex]::Match($content,"viewBox=\`"0 0 ([0-9]+) ([0-9]+)\`"")
        $xWide = $matchResult.Groups[1].Value
        $yHigh = $matchResult.Groups[2].Value
    
        Write-Host "Detected an image $xWide by $yHigh"
    
        $xWideMod = $xWide/2
        $yHighMod = $yHigh/2 
    
        "<occluders>" | Out-file -Filepath $filename
        $occludercount = 0 
        $m = Select-String -InputObject $content -Pattern "C[0-9., `r`n]+Z" -AllMatches
        $array = [System.Collections.ArrayList]@()
        $m.Matches | %{
            $line = $_.Value.trim("C").trim("Z")
            $matching = Select-String -InputObject $line -Pattern "([0-9\.,]+)" -AllMatches
    
            foreach($match in $matching.Matches){
                $coord = $match.Value.split(",")
                $coord[0] = $coord[0] -as [int]
                $coord[1] = $coord[1] -as [int]
                                
                if($array.Count -gt 0 -and [Math]::Pow($array[-1][0]-$coord[0],2)+[Math]::Pow($array[-1][1]-$coord[1],2) -lt [Math]::Pow($SmoothDistance,2) ) {
                    #"discarded a point"
                }else{
                    $array.Add($coord) | Out-Null
                }
            }
            $out = ""
            $array | %{
                $modX = $_[0]-$xWideMod -as [String]
                $modY = $yHighMod-$_[1] -as [String]
                $out = $out+$modX+","+$modY+","
            }
            
            "`t<occluder>" | Out-file -append -Filepath $filename 
            "`t`t<id>$occludercount</id>" | Out-file -append -Filepath $filename 
            "`t`t<points>$($out.trim(","))</points>" | Out-file -append -Filepath $filename 
            "`t`t<closedpolygon>true</closedpolygon>" | Out-file -append -Filepath $filename 
            "`t</occluder>" | Out-file -append -Filepath $filename 
            $occludercount++
            $array.RemoveRange(0,$array.Count)
        }
        "</occluders>" | Out-file -append -Filepath $filename 
    }
    Last edited by muklin; October 26th, 2020 at 22:07.

  2. #2
    I would like to take some questions:
    1 - Can I do this type of export only with GIMP?
    2 - Are the colors defined by the Pen Tool that define the type of LoS or not?
    3 - Where would that Script be used at the end?

  3. #3
    1. The workflow I described is only tested using Gimp to create an SVG. Potentially, some other SVGs would work, but probably not. Potentially even other versions of GIMP wouldn't work.
    2. This is for FGU. See the LOS videos you can find linked in various places on this forum to see how LOS in FGU works.
    3. The script converts the SVG to the occluder data, to be inserted into the db.xml file for your campaign. So you'd use it after you created the SVG. As mentioned in the post.

  4. #4
    This is cool, thanks. Any chance of a brief step by step video explaining it further for those familiar with GIMP but not the dbxml files? (I don’t even know what those are).

  5. #5
    Quote Originally Posted by TheFabulousIronChef View Post
    This is cool, thanks. Any chance of a brief step by step video explaining it further for those familiar with GIMP but not the dbxml files? (I don’t even know what those are).
    db.xml is the main xml file that FG and FGU use to store the state of your campaign.

    You can find the file by browsing to the Fantasy Grounds Data directory.
    There are various links to this directory in the application, but also see:
    https://www.fantasygrounds.com/wiki/...Files_Overview

    Which says:
    "The Fantasy Grounds data folder is specified in the FG Settings when installing FG.
    The easiest way to access the data directory is to click the folder icon in the top right of the main FG startup screen. This will open a new file explorer window at the root of the data directory. "

    Inside here, browse to campaigns/<your campaign name
    db.xml will be in there, and is just a big ol xml file with the data in your campaign.

    A few notes and disclaimers on working with db.xml
    DISCLAIMER: All this information is what I've reverse engineered from using FG over the years. Take it with a grain of salt. It worked for me. I don't take responsibility for any loss of data.
    1. Before anything else, BACKUP your current db.xml. If you make a mistake, then you might end up losing everything. Having a back up is always a good idea.
    2. I recommend you only edit db.xml while your campaign is not loaded. This will save you heartache of making lots of edits in the file, only for FG to overwrite it when you make changes in the application.
    2a. Alternatively, you can use the /reload and /save command lines in FG to force it to load changes.
    3. Load the image you want to change into the campaign first, then close the campaign. this will create the image xml node in the db.xml.
    4. Alter the image node to have, (or replace, or alter) the generated <occluders> sub node.
    5. Save your file.
    6. Reload your campaign.
    7. Open the map and see if your occluders are set as you need them.
    8. Rinse and repeat til you're happy.

  6. #6
    A little of an old thread but still I had to say that this is an absolutely wonderful method of doing LoS. It works great.

    A couple of additions I would add...

    1. Use of a gaussian blur with the blending mode as color erase can help produce selections that inset the LoS into the wall slightly.
    2. Use of the advanced selection to path can help with a little better granulation on the path.

    I use this python-fu:

    img = gimp.image_list()[0]
    pdb.plug_in_sel2path_advanced(img,None,0.5,60.00,4 ,100.00,0.40,1,1.00,1,0.33,3,2,0,0.010,0.5,0.01,1, 0.1,4,0.09,3)

    Hope this helps others with this method.

    Jason

  7. #7

    Bash version

    I was inspired to try a bash (linux shell) version of this.

    Code:
    #!/bin/bash
    
    if [ $# -lt 1 ]
    then
      echo "Usage:\n$ $0 <svg_file>..."
      exit 1
    fi
    
    in_file="$1"
    out_file=${in_file%.svg}.occ
    vb_regex='viewBox="0 0 ([0-9]+) ([0-9]+)"'
    smooth_distance=10
    
    readarray content < "${in_file}"
    
    if [[ "${content[@]}" =~ $vb_regex ]]
    then
      xWide=${BASH_REMATCH[1]}
      yHigh=${BASH_REMATCH[2]}
    
      echo "Detected an image $xWide by $yHigh"
    fi
     
    let xWideMod=$xWide/2
    let yHighMod=$yHigh/2
    
    echo "<occluders>" > "${out_file}"
    export occludercount=0
    oc_regex='C[0-9\., \
    ]+Z'
    coord_regex='([0-9\.,])+'
    
    
    close_to_last() {
      if [ ${#array[@]} -eq 0 ]
      then
        return 0
      fi
      # echo "End of array: ${array[-1]}"
      local lastx=${array[-1]%,*}
      local lasty=${array[-1]/"$lastx,"/}
      return $(( ( ($lastx - ${coord[0]}) ** 2 + ($lasty - ${coord[1]}) ** 2 ) < ( $smooth_distance ** 2  ) ))
    }
    
    export OUT=""
    chunk="${content[@]}"
    while [[ "$chunk" =~ $oc_regex ]]
    do
      group="${BASH_REMATCH[0]}"
      # echo "$group"
      (
        declare -a array
        declare -a coord
        while [[ "$group" =~ $coord_regex ]]
        do
          t=${BASH_REMATCH[0]%,*}
          coord[0]=${t%.*}
          t=${BASH_REMATCH[0]/"$t,"/}
          coord[1]=${t%.*}
          # echo "Found: (${coord[0]},${coord[1]})"
          close_to_last 
          if [ $? -eq 0 ]
          then
            array+=("${coord[0]},${coord[1]}")
            # echo "Added: ${array[-1]}"
          fi
          group=${group/"${BASH_REMATCH[0]}"/}
        done
        if [ ${#array[@]} -gt 1 ]
        then
          for point in ${array[@]}
          do
            coord[0]=${point%,*}
            coord[1]=${point/"${coord[0]},"/}
            # echo -n "Processing (${coord[0]},${coord[1]})"
            let modX=${coord[0]}-$xWideMod
            let modY=$yHighMod-${coord[1]}
            # echo " = ($modX,$modY)"
            OUT="${OUT}${modX},${modY},"
          done
          out=${OUT%,}
          cat <<HERE >> "$out_file"
          <occluder>
            <id>$occludercount</id>
            <points>$out</points>
            <closedpolygon>true</closedpolygon>
          </occluder>
    HERE
        fi
        unset array
      )
      occludercount=$((occludercount+1))
      chunk=${chunk/"${BASH_REMATCH[0]}"/}
      # echo -n "Press enter to continue"
      # read enter
    done
    echo "</occluders>" >> "${out_file}"
    This seems to produce a valid occluders file for me. Dunno if there are any other Linux users out there - but here it is.
    Also on github: https://github.com/dimonic/fgu-scripts
    Last edited by dimonic; February 21st, 2022 at 16:59. Reason: Added git link

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
STAR TREK 2d20

Log in

Log in