• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Tool Idea - Tilesetter Tool

Hey guys, I was testing out using some tiles from an old World of Warcraft alpha build that someone sent me way back, and I wanted to use them as tiles for Warcraft III. To that end, I wrote a script on my computer that would procedurally generate a Warcraft III style of tile from a file that was "repeatable" but didn't have Warcraft III specific organization. For example, here is the input and output of one of the files from this program:

Input:
FeralasGrass.png


Output:
1678034369967.png


This script would run on a whole folder and allow me to instantly convert all the textures inside, and then generate an output folder of textures as well as possibly print out all at once a CSV for me to copy in Microsoft Excel so that I have the tileset metadata to make it available in World Editor too:

Code:
E000,-1,Tileset\Feralas,FeralasBrush,FeralasBrush,FeralasBrush,1,0,1,1,0,Fdrt,1,0
E001,-1,Tileset\Feralas,FeralasBrushRoots,FeralasBrushRoots,FeralasBrushRoots,1,0,1,1,0,Fdrt,1,0
E002,-1,Tileset\Feralas,FeralasDirt,FeralasDirt,FeralasDirt,1,0,1,1,0,Fdrt,1,0
E003,-1,Tileset\Feralas,FeralasGrass,FeralasGrass,FeralasGrass,1,0,1,1,0,Fdrt,1,0
E004,-1,Tileset\Feralas,FeralasRoad,FeralasRoad,FeralasRoad,1,0,1,1,0,Fdrt,1,0
E005,-1,Tileset\Feralas,FeralasRockBrown,FeralasRockBrown,FeralasRockBrown,1,0,1,1,0,Fdrt,1,0
E006,-1,Tileset\Feralas,FeralasRocksGround,FeralasRocksGround,FeralasRocksGround,1,0,1,1,0,Fdrt,1,0
E007,-1,Tileset\Feralas,FeralasSand,FeralasSand,FeralasSand,1,0,1,1,0,Fdrt,1,0

Then this group of textures becomes available in the World Editor for people who are doing more advanced modding.
1678034485343.png


A lot of people who are making maps would not be able to use the new tileset probably because the game might reject a map made with a tileset that isn't in the game data. But I recall thinking that it is possible to import a TerrainArt.slk into custom maps at least for some Warcraft III game versions, so if these tiles were added to the tile list probably for a map set on a base WC3 tileset then it might work fine.

The math that I was using currently was aiming to be a perfect circle, so that tiles generated in this way when placed at a single cell in Warcraft III would be perfectly circular:
1678034711739.png


The math calculation is fairly straightforward so I am probably not the first person who thought of this:

Java:
                    for (int k = 1; k < 16; k++) {
                        boolean bottomRight = (k & 0x1) != 0;
                        boolean bottomLeft = (k & 0x2) != 0;
                        boolean topRight = (k & 0x4) != 0;
                        boolean topleft = (k & 0x8) != 0;
                        int cellX = (k) % 4;
                        int cellY = (k) / 4;
                        for (int pixelX = 0; pixelX < bufferedImg.getWidth(); pixelX++) {
                            for (int pixelY = 0; pixelY < bufferedImg.getHeight(); pixelY++) {
                                float fX = pixelX / (float) bufferedImg.getWidth();
                                float fY = pixelY / (float) bufferedImg.getWidth();
                                float alpha = 1;
                                if (bottomRight && !bottomLeft && !topRight && !topleft) {
                                    alpha = Math.max(0, 1f - (float) new Point2D.Float(fX, fY).distance(1, 1));
                                } else if (!bottomRight && bottomLeft && !topRight && !topleft) {
                                    alpha = Math.max(0, 1f - (float) new Point2D.Float(fX, fY).distance(0, 1));
                                } else if (bottomRight && bottomLeft && !topRight && !topleft) {
                                    alpha = fY;
                                } else if (!bottomRight && !bottomLeft && topRight && !topleft) {
                                    alpha = Math.max(0, 1f - (float) new Point2D.Float(fX, fY).distance(1, 0));
                                } else if (bottomRight && !bottomLeft && topRight && !topleft) {
                                    alpha = fX;
                                } else if (!bottomRight && bottomLeft && topRight && !topleft) {
                                    alpha = Math.min(1,
                                            Math.max(0, 1f - (float) new Point2D.Float(fX, fY).distance(1, 0)) + Math.max(0,1f
                                                    - (float) new Point2D.Float(fX, fY).distance(0, 1)));
                                } else if (bottomRight && bottomLeft && topRight && !topleft) {
                                    alpha = Math.min(1,
                                            Math.max(0,(float) new Point2D.Float(fX, fY).distance(0, 0)));
                                } else if (!bottomRight && !bottomLeft && !topRight && topleft) {
                                    alpha = Math.max(0, 1f - (float) new Point2D.Float(fX, fY).distance(0, 0));
                                } else if (bottomRight && !bottomLeft && !topRight && topleft) {
                                    alpha = Math.min(1,
                                            Math.max(0, 1f - (float) new Point2D.Float(fX, fY).distance(1, 1)) + Math.max(0,1f
                                                    - (float) new Point2D.Float(fX, fY).distance(0, 0)));
                                } else if (!bottomRight && bottomLeft && !topRight && topleft) {
                                    alpha = 1 - fX;
                                } else if (bottomRight && bottomLeft && !topRight && topleft) {
                                    alpha = Math.min(1,
                                            Math.max(0,(float) new Point2D.Float(fX, fY).distance(1, 0)));
                                } else if (!bottomRight && !bottomLeft && topRight && topleft) {
                                    alpha = 1 - fY;
                                } else if (bottomRight && !bottomLeft && topRight && topleft) {
                                    alpha = Math.min(1,
                                            Math.max(0,(float) new Point2D.Float(fX, fY).distance(0, 1)));
                                } else if (!bottomRight && bottomLeft && topRight && topleft) {
                                    alpha = Math.min(1,
                                            Math.max(0,(float) new Point2D.Float(fX, fY).distance(1, 1)));
                                }
                                int alphaB = (int)(alpha*255) << 24;
                                int x = pixelX + cellX * bufferedImg.getWidth();
                                int y = pixelY + cellY * bufferedImg.getHeight();
                                fixed.setRGB(x, y, (fixed.getRGB(x, y) & 0xFFFFFF) | alphaB);
                            }
                        }
                    }

But with all of this in mind, is this concept something that should remain as a script that I ran for myself in some obscure case, or would it benefit Hive users to have a GUI tool for this where they could click a folder or maybe an individual texture that was repeatable on all its sides but not in the Warcraft III format, and convert it to Warcraft III format?

I'm asking because although my project might end up as a silly personal project that dies, this type of tool might be really generally useful for Warcraft III map makers who want access to more custom tiles.
 

Nyctaeus

Hosted Project: W3E
Level 18
Joined
Oct 16, 2021
Messages
180
I do want it. It's cool. I'd like to replace my current and super-annoying workflow [classic vanilla tile replacement] with this.

Thought about applying some noise that would make the edges more irregular and natural-looking? Something like alpha masks from Strydhaizer's tutorial would be enough.
 
Top