1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "nel/misc/file.h"
19 #include "nel/misc/common.h"
20 #include "nel/misc/bitmap.h"
21 #include "nel/misc/path.h"
22 #include "nel/misc/cmd_args.h"
23 #include "nel/misc/vector_2d.h"
24 #include "nel/misc/uv.h"
25 #include "nel/misc/algo.h"
29 CPoint(sint _x
, sint _y
) :x(_x
), y(_y
)
33 CPoint
operator + (const CPoint
&p
) const
35 return CPoint(x
+ p
.x
, y
+ p
.y
);
42 const CPoint
Up(0, -1);
43 const CPoint
Down(0, 1);
44 const CPoint
Left(-1, 0);
45 const CPoint
Right(1, 0);
47 uint TextureSize
= 4096;
49 const NLMISC::CRGBA DiscardColor
= NLMISC::CRGBA::Red
;
50 const NLMISC::CRGBA KeepColor
= NLMISC::CRGBA::Blue
;
52 typedef std::vector
<CPoint
> CPoints
;
56 std::vector
<uint
> indices
;
62 std::vector
<NLMISC::CUV
> textureCoords
;
63 std::vector
<CFace
> faces
;
67 nlinfo("Object %s processed with %u vertices and %u faces", name
.c_str(), (uint
)textureCoords
.size(), (uint
)faces
.size());
71 bool fillPoint(NLMISC::CBitmap
&bitmap
, sint width
, CPoints
&points
)
73 if (points
.empty()) return false;
75 // take last point in queue
76 CPoint
p(points
.back());
79 NLMISC::CRGBA c
= bitmap
.getPixelColor(p
.x
, p
.y
);
81 if (c
== NLMISC::CRGBA::White
)
83 // white is used for background
85 // replace with color we want to discard
86 bitmap
.setPixelColor(p
.x
, p
.y
, DiscardColor
);
88 uint w
= bitmap
.getWidth();
89 uint h
= bitmap
.getHeight();
91 // put adjacent pixels in queue to process later
92 if (p
.y
> 0) points
.push_back(p
+ Up
);
93 if (p
.y
< h
-1) points
.push_back(p
+ Down
);
94 if (p
.x
> 0) points
.push_back(p
+ Left
);
95 if (p
.x
< w
-1) points
.push_back(p
+ Right
);
97 else if (c
== NLMISC::CRGBA::Black
)
99 // black is used for vertices
101 // increase them by border width
102 for (sint y
= -width
; y
<= width
; ++y
)
104 for (sint x
= -width
; x
<= width
; ++x
)
106 bitmap
.setPixelColor(p
.x
+ x
, p
.y
+ y
, KeepColor
);
114 void drawEdge(NLMISC::CBitmap
&bitmap
, const CObject
&object
, const CFace
&face
, uint index0
, uint index1
)
116 NLMISC::CUV uv0
= object
.textureCoords
[face
.indices
[index0
]];
117 NLMISC::CUV uv1
= object
.textureCoords
[face
.indices
[index1
]];
119 std::vector
<std::pair
<sint
, sint
> > pixels
;
121 // draw the triangle with vertices UV coordinates
122 NLMISC::drawFullLine(uv0
.U
, uv0
.V
, uv1
.U
, uv1
.V
, pixels
);
124 // for each pixels, set them to black
125 for (uint j
= 0, jlen
= pixels
.size(); j
< jlen
; ++j
)
127 bitmap
.setPixelColor(pixels
[j
].first
, pixels
[j
].second
, NLMISC::CRGBA::Black
);
131 int main(int argc
, char **argv
)
133 NLMISC::CApplicationContext applicationContext
;
135 // Parse Command Line.
136 //====================
137 NLMISC::CCmdArgs args
;
139 args
.setDescription("Textures tool");
140 args
.addArg("c", "colorize", "color", "Colorize textures using a color (in HTML hexdecimal format like #rrggbb)");
141 args
.addArg("f", "fill", "color or image", "Fill background part with color or image");
142 args
.addArg("u", "uvmap", "", "Generate a UV Map texture from OBJ file");
143 args
.addArg("w", "width", "width of border", "Width of the border to fill (default 0)");
144 args
.addArg("s", "size", "size of output bitmap", "Width and height of generated bitmap (default 4096)");
145 args
.addArg("b", "background", "background color", "Color to use to fill background");
146 args
.addArg("o", "output", "filename", "Output filename");
147 args
.addAdditionalArg("filename", "File to process", true, true);
149 if (!args
.parse(argc
, argv
)) return 1;
151 std::string filename
= args
.getAdditionalArg("filename").front();
153 std::string output
= args
.haveArg("o") ? args
.getArg("o").front() : "";
155 if (args
.haveArg("s"))
157 // size of generated bitmap
158 NLMISC::fromString(args
.getArg("s").front(), TextureSize
);
161 if (args
.haveArg("c"))
167 color
.fromString(args
.getArg("c").front());
169 if (file
.open(filename
))
171 NLMISC::CBitmap bitmap
;
173 if (bitmap
.load(file
))
175 NLMISC::CObjectVector
<uint8
> &pixels
= bitmap
.getPixels();
177 NLMISC::CRGBA
*pRGBA
= (NLMISC::CRGBA
*)&pixels
[0];
179 uint32 size
= bitmap
.getSize();
181 for (uint j
= 0; j
< size
; ++j
)
183 pRGBA
->modulateFromColorRGBOnly(*pRGBA
, color
);
189 if (out
.open(output
))
191 bitmap
.writePNG(out
, 24);
197 if (args
.haveArg("f"))
199 // fill areas in a bitmap with another texture or color
202 // textures_tool -f normal.png -w 2 -b #000000 uvmap.png -o test_normal.png
203 // will use a copy 1024x1024 texture map on a 4096x4096 UV Map preserving the different areas
205 std::string foregroundColorOrFilename
= args
.getArg("f").front();
207 NLMISC::CRGBA foregroundColor
= NLMISC::CRGBA::Black
, backgroundColor
= NLMISC::CRGBA::Black
;
209 NLMISC::CBitmap textureBitmap
;
211 bool useTexture
= false;
213 // f parameter is required
214 if (NLMISC::CFile::fileExists(foregroundColorOrFilename
))
217 NLMISC::CIFile textureFile
;
219 if (!textureFile
.open(foregroundColorOrFilename
))
221 nlwarning("Unable to open %s", foregroundColorOrFilename
.c_str());
226 if (!textureBitmap
.load(textureFile
))
228 nlwarning("Unable to decode %s", foregroundColorOrFilename
.c_str());
236 // parse color from argument
237 foregroundColor
.fromString(foregroundColorOrFilename
);
240 if (args
.haveArg("b"))
242 // parse HTML color from argument
243 backgroundColor
.fromString(args
.getArg("b").front());
248 if (args
.haveArg("w"))
250 // parse width of borders
251 NLMISC::fromString(args
.getArg("w").front(), width
);
257 if (!file
.open(filename
))
259 nlwarning("Unable to open %s", filename
.c_str());
264 NLMISC::CBitmap inBitmap
;
266 if (!inBitmap
.load(file
))
268 nlwarning("Unable to decode %s", filename
.c_str());
274 // we can't have more than width * height points, so allocate memory for all of them
275 Points
.reserve(inBitmap
.getWidth() * inBitmap
.getHeight());
277 // first point to process
278 Points
.push_back(CPoint(0, 0));
280 // process all points from 0, 0
281 while(fillPoint(inBitmap
, width
, Points
)) { }
283 // create a new bitmap for output
284 NLMISC::CBitmap outBitmap
;
285 outBitmap
.resize(inBitmap
.getWidth(), inBitmap
.getHeight());
287 // copy points colors to new bitmap
288 for (sint y
= 0, h
= inBitmap
.getHeight(); y
< h
; ++y
)
290 for (sint x
= 0, w
= inBitmap
.getWidth(); x
< w
; ++x
)
292 if (inBitmap
.getPixelColor(x
, y
) != DiscardColor
)
294 // we copy this point, repeat texture image if using it
295 outBitmap
.setPixelColor(x
, y
, useTexture
? textureBitmap
.getPixelColor(x
% textureBitmap
.getWidth(), y
% textureBitmap
.getHeight()) : foregroundColor
);
299 // put a background color
300 outBitmap
.setPixelColor(x
, y
, backgroundColor
);
305 // save output bitmap
306 NLMISC::COFile outFile
;
308 if (outFile
.open(output
))
310 outBitmap
.writePNG(outFile
, 24);
314 if (args
.haveArg("u"))
316 NLMISC::CIFile objFile
;
318 if (!objFile
.open(filename
))
320 nlwarning("Unable to open %s", filename
.c_str());
328 while (!objFile
.eof())
330 objFile
.getline(buffer
, 1024);
333 std::string
line(buffer
);
335 if (line
.size() > 1022)
337 nlwarning("More than 1022 bytes on a line!");
341 if (line
.size() < 3) continue;
343 // texture coordinate
344 if (line
.substr(0, 3) == "vt ")
347 std::vector
<std::string
> tokens
;
348 NLMISC::explode(line
, std::string(" "), tokens
);
350 if (tokens
.size() == 3)
353 NLMISC::fromString(tokens
[1], u
);
354 NLMISC::fromString(tokens
[2], v
);
356 // V coordinates are inverted
357 object
.textureCoords
.push_back(NLMISC::CUV(u
* (float)TextureSize
, (1.f
- v
) * (float)TextureSize
));
361 nlwarning("Not 3 arguments for VT");
364 else if (line
.substr(0, 2) == "f ")
367 std::vector
<std::string
> tokens
;
368 NLMISC::explode(line
, std::string(" "), tokens
);
371 face
.indices
.resize(tokens
.size()-1);
373 bool faceValid
= true;
375 for (uint i
= 1, ilen
= tokens
.size(); i
< ilen
; ++i
)
377 std::vector
<std::string
> tokens2
;
378 NLMISC::explode(tokens
[i
], std::string("/"), tokens2
);
380 if (tokens2
.size() == 3)
382 if (NLMISC::fromString(tokens2
[1], face
.indices
[i
- 1]))
384 // we want indices start from 0 instead of 1
385 --face
.indices
[i
- 1];
394 nlwarning("Not 3 arguments for indices");
398 if (faceValid
) object
.faces
.push_back(face
);
400 else if (line
.substr(0, 2) == "o ")
403 object
.name
= line
.substr(2);
413 // create a new bitmap for output
414 NLMISC::CBitmap outBitmap
;
415 outBitmap
.resize(TextureSize
, TextureSize
);
418 memset(&outBitmap
.getPixels()[0], 255, TextureSize
* TextureSize
* 4);
421 for (uint i
= 0, ilen
= object
.faces
.size(); i
< ilen
; ++i
)
423 const CFace
&face
= object
.faces
[i
];
426 for (uint k
= 1, klen
= face
.indices
.size(); k
< klen
; ++k
)
428 drawEdge(outBitmap
, object
, face
, k
- 1, k
);
431 // link last and fist pixels
432 drawEdge(outBitmap
, object
, face
, face
.indices
.size()-1, 0);
435 // save output bitmap
436 NLMISC::COFile outFile
;
438 if (outFile
.open(output
))
440 outBitmap
.writePNG(outFile
, 24);