diff --git a/nel/include/nel/3d/texture_font.h b/nel/include/nel/3d/texture_font.h index b250fecd8..61e92fa58 100644 --- a/nel/include/nel/3d/texture_font.h +++ b/nel/include/nel/3d/texture_font.h @@ -38,12 +38,43 @@ class CTextureFont : public ITexture public: + struct SLetterKey + { + u32char Char; + sint Size; + bool Embolden; + bool Oblique; + CFontGenerator *FontGenerator; + + SLetterKey() : Char(0), Size(0), Embolden(false), Oblique(false), FontGenerator(NULL) + { + } + + // Does not use FontGenerator in return value + inline uint64 getVal() const + { + return Char // 32 bits + | (uint64(Size & 0xFFFF) << 32) // 16 bits + | (uint64(Embolden) << (32+16)) // 1 bit + | (uint64(Oblique) << (32+16+1)); // 1 bit + } + + bool operator<(const SLetterKey &rhs) const + { + uint64 a = getVal(); + uint64 b = rhs.getVal(); + return (a < b) || ((a == b) && (FontGenerator < rhs.FontGenerator)); + } + }; + // Holds info for glyphs rendered on atlas - struct SGlyphInfo + struct SGlyphInfo : SLetterKey { // font atlas info uint32 CacheVersion; + uint32 GlyphIndex; + // atlas region with padding uint32 X, Y, W, H; @@ -54,30 +85,17 @@ public: // UV coords for rendered glyph without padding float U0, V0, U1, V1; - uint32 GlyphIndex; - sint Size; - bool Embolden; - bool Oblique; - CFontGenerator *FontGenerator; - SGlyphInfo() - : CacheVersion(0), + : CacheVersion(0), GlyphIndex(0), U0(0.f), V0(0.f), U1(0.f), V1(0.f), - X(0), Y(0), W(0), H(0), CharWidth(0), CharHeight(0), - GlyphIndex(0), Size(0), Embolden(false), Oblique(false), FontGenerator(NULL) + X(0), Y(0), W(0), H(0), CharWidth(0), CharHeight(0) { } }; // Holds info for glyphs displayed on screen - struct SLetterInfo + struct SLetterInfo : SLetterKey { - u32char Char; - sint Size; - bool Embolden; - bool Oblique; - CFontGenerator *FontGenerator; - uint32 GlyphIndex; uint32 CharWidth; // Displayed glyph height uint32 CharHeight; // Displayed glyph height @@ -88,29 +106,12 @@ public: SGlyphInfo* glyph; SLetterInfo() - : Char(0), Size(0), Embolden(false), Oblique(false), FontGenerator(NULL), - GlyphIndex(0), CharWidth(0), CharHeight(0), Top(0), Left(0), AdvX(0), + : GlyphIndex(0), CharWidth(0), CharHeight(0), Top(0), Left(0), AdvX(0), glyph(NULL) { } }; - struct SLetterKey - { - u32char Char; - sint Size; - bool Embolden; - bool Oblique; - CFontGenerator *FontGenerator; - - // Does not use FontGenerator in return value - uint32 getVal(); - - SLetterKey():Char(0), FontGenerator(NULL), Size(0), Embolden(false), Oblique(false) - { - } - }; - public: /** @@ -151,7 +152,7 @@ private: // Keep track of available space in main texture std::vector _AtlasNodes; - std::vector _Letters; + std::map _Letters; // lookup letter from letter cache or create new SLetterInfo* findLetter(SLetterKey& k, bool insert); @@ -165,7 +166,7 @@ private: uint _GlyphSizeStep; // rendered glyph cache - std::list _GlyphCache; + std::map _GlyphCache; SGlyphInfo* findLetterGlyph(SLetterInfo *letter, bool insert); // render letter glyph into glyph cache diff --git a/nel/samples/3d/CMakeLists.txt b/nel/samples/3d/CMakeLists.txt index 772ab0056..1062579cd 100644 --- a/nel/samples/3d/CMakeLists.txt +++ b/nel/samples/3d/CMakeLists.txt @@ -1,4 +1,5 @@ ADD_SUBDIRECTORY(font) +ADD_SUBDIRECTORY(font_perf) ADD_SUBDIRECTORY(cluster_viewer) ADD_SUBDIRECTORY(shape_viewer) diff --git a/nel/samples/3d/font/main.cpp b/nel/samples/3d/font/main.cpp index 5a656be49..1d5eaa114 100644 --- a/nel/samples/3d/font/main.cpp +++ b/nel/samples/3d/font/main.cpp @@ -22,6 +22,7 @@ #include "nel/misc/event_emitter.h" #include "nel/misc/event_listener.h" #include "nel/misc/path.h" +#include "nel/misc/random.h" // look at 3dinit example #include "nel/3d/nelu.h" @@ -97,10 +98,12 @@ int main(int argc, char **argv) CNELU::EventServer.addEmitter(CNELU::Driver->getEventEmitter()); CNELU::AsyncListener.addToServer(CNELU::EventServer); + NLMISC::CValueSmoother smoothFPS; + NLMISC::CRandom rnd; do { // look at 3dinit example - CNELU::clearBuffers(CRGBA(0,0,0)); + CNELU::clearBuffers(CRGBA(120,120,0)); // now, every frame, we have to render the computer string. @@ -168,12 +171,39 @@ int main(int argc, char **argv) tc.setHotSpot (CComputedString::BottomRight); tc.printAt (0.99f, 0.01f, string("Press to quit")); + /*for(uint i = 0; i < 1000; ++i) + { + uint fontSize = rnd.rand(40) + 10; + tc.setColor(CRGBA(rnd.rand(255), rnd.rand(255), rnd.rand(255))); + tc.setFontSize(fontSize); + tc.setHotSpot(CComputedString::MiddleMiddle); + tc.printAt(rnd.frand(1.f), rnd.frand(1.f), toString("%d", fontSize)); + }*/ + + { + static TTicks oldTick = CTime::getPerformanceTime(); + TTicks newTick = CTime::getPerformanceTime(); + double deltaTime = CTime::ticksToSecond (newTick-oldTick); + oldTick = newTick; + smoothFPS.addValue((float)deltaTime); + deltaTime = smoothFPS.getSmoothValue (); + if (deltaTime > 0.0) + { + //printf("FPS: %.5f\n", 1.f/deltaTime); + tc.setFontSize(16); + tc.setColor(CRGBA::Yellow); + tc.setHotSpot(CComputedString::TopLeft); + tc.printAt(0.01f, 0.99f, toString("FPS:%.f", 1.0f/deltaTime)); + } + } + // look 3dinit example CNELU::swapBuffers(); CNELU::screenshot(); // look at event example CNELU::EventServer.pump(true); + } while(!CNELU::AsyncListener.isKeyPushed(KeyESCAPE)); diff --git a/nel/samples/3d/font_perf/CMakeLists.txt b/nel/samples/3d/font_perf/CMakeLists.txt new file mode 100644 index 000000000..2f794c983 --- /dev/null +++ b/nel/samples/3d/font_perf/CMakeLists.txt @@ -0,0 +1,14 @@ +FILE(GLOB SRC *.cpp) + +ADD_EXECUTABLE(nl_sample_font_perf ${SRC}) + +ADD_DEFINITIONS(-DFONT_DIR="\\"${NL_SHARE_ABSOLUTE_PREFIX}/nl_sample_font_perf/\\"") + +TARGET_LINK_LIBRARIES(nl_sample_font_perf nelmisc nel3d) +NL_DEFAULT_PROPS(nl_sample_font_perf "NeL, Samples, 3D: Font Performance Test") +NL_ADD_RUNTIME_FLAGS(nl_sample_font_perf) + +INSTALL(TARGETS nl_sample_font_perf RUNTIME DESTINATION ${NL_BIN_PREFIX} COMPONENT samples3d) +INSTALL(FILES beteckna.ttf DESTINATION ${NL_SHARE_PREFIX}/nl_sample_font_perf COMPONENT samples3d) + + diff --git a/nel/samples/3d/font_perf/beteckna.ttf b/nel/samples/3d/font_perf/beteckna.ttf new file mode 100644 index 000000000..e9a117f4d Binary files /dev/null and b/nel/samples/3d/font_perf/beteckna.ttf differ diff --git a/nel/samples/3d/font_perf/main.cpp b/nel/samples/3d/font_perf/main.cpp new file mode 100644 index 000000000..d183d3fa8 --- /dev/null +++ b/nel/samples/3d/font_perf/main.cpp @@ -0,0 +1,99 @@ +// NeL - MMORPG Framework +// Copyright (C) 2020 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "nel/misc/types_nl.h" + +#include "nel/misc/event_emitter.h" +#include "nel/misc/event_listener.h" +#include "nel/misc/path.h" +#include "nel/misc/random.h" + +// look at 3dinit example +#include "nel/3d/nelu.h" + +// used for font management +#include "nel/3d/font_manager.h" +#include "nel/3d/computed_string.h" +#include "nel/3d/text_context.h" +#include "nel/3d/driver_user.h" + +#ifdef NL_OS_WINDOWS + #ifndef NL_COMP_MINGW + #define NOMINMAX + #endif + #include +#endif // NL_OS_WINDOWS + +#ifndef FONT_DIR +# define FONT_DIR "." +#endif + +using namespace std; +using namespace NL3D; +using namespace NLMISC; + +int main(int argc, char **argv) +{ + // look at 3dinit example + CNELU::init (800, 600, CViewport(), 32, true, 0, false, false); + + NLMISC::CPath::addSearchPath(FONT_DIR); + + // create a font manager + CFontManager fontManager; + + // set the font cache to 2 megabytes (default is 1mb) + fontManager.setMaxMemory(2000000); + + CTextContext tc; + + tc.init (CNELU::Driver, &fontManager); + + // The first param is the font name (could be ttf, pfb, fon, etc...). The + // second one is optional, it's the font kerning file + tc.setFontGenerator (NLMISC::CPath::lookup("beteckna.ttf")); + + NLMISC::CRandom rnd; + + uint nbCount = 100000; + TTicks startTick = CTime::getPerformanceTime(); + std::string txt; + for(uint i = 0; i < nbCount; ++i) + { + uint fontSize = rnd.rand(200); + bool embolden = rnd.rand(1) == 1; + bool oblique = rnd.rand(1) == 1; + txt = toString("Lorem ipsum %03d", fontSize); + + CComputedString cs; + fontManager.computeString(txt, tc.getFontGenerator(), CRGBA::White, fontSize, embolden, oblique, CNELU::Driver, cs); + } + + TTicks endTick = CTime::getPerformanceTime(); + + double deltaTime = CTime::ticksToSecond(endTick-startTick); + std::string msg = toString("Generated %d strings in %.2fs\n", nbCount, deltaTime); + + nlinfo("%s", msg.c_str()); + printf("%s", msg.c_str()); + + fontManager.dumpCache ("font_pref_cache_dump.tga"); + + // look at 3dinit example + CNELU::release(); + + return EXIT_SUCCESS; +} diff --git a/nel/src/3d/texture_font.cpp b/nel/src/3d/texture_font.cpp index b40083516..9aa08ca15 100644 --- a/nel/src/3d/texture_font.cpp +++ b/nel/src/3d/texture_font.cpp @@ -112,9 +112,9 @@ void CTextureFont::clearAtlas() _Data[0].fill(0); // clear glyph cache - for(uint i = 0; i< _Letters.size(); ++i) + for(std::map::iterator it = _Letters.begin(); it != _Letters.end(); ++it) { - _Letters[i].glyph = NULL; + it->second.glyph = NULL; } _GlyphCache.clear(); @@ -161,15 +161,15 @@ void CTextureFont::repackAtlas(uint32 newW, uint32 newH) _AtlasNodes.push_back(CRect(0, 0, _TextureSizeX, _TextureSizeY)); CObjectVector&src = btm.getPixels(); - for(std::list::iterator it = _GlyphCache.begin(); it != _GlyphCache.end(); ++it) + for(std::map::iterator it = _GlyphCache.begin(); it != _GlyphCache.end(); ++it) { - if (it->CacheVersion != _CacheVersion) + if (it->second.CacheVersion != _CacheVersion) { // TODO: must remove glyph from all letters before removing glyph from cache //continue; } - SGlyphInfo &glyph = *it; + SGlyphInfo &glyph = it->second; glyph.CacheVersion = newCacheVersion; @@ -403,18 +403,10 @@ CTextureFont::SGlyphInfo* CTextureFont::renderLetterGlyph(SLetterInfo *letter, u } copyGlyphBitmap(bitmap, charWidth, charHeight, atlasX, atlasY); - SGlyphInfo* glyphInfo = NULL; - { - // keep cache sorted by height (smaller first) - std::list::iterator it = _GlyphCache.begin(); - while(it != _GlyphCache.end() && it->CharHeight < charHeight) - { - ++it; - } + SLetterKey k = *letter; + k.Size = bitmapFontSize; - it = _GlyphCache.insert(it, SGlyphInfo()); - glyphInfo = &(*it); - } + SGlyphInfo* glyphInfo = &_GlyphCache[k]; glyphInfo->GlyphIndex = glyphIndex; glyphInfo->Size = bitmapFontSize; @@ -450,16 +442,13 @@ CTextureFont::SGlyphInfo* CTextureFont::findLetterGlyph(SLetterInfo *letter, boo } // CacheVersion not checked, all glyphs in cache must be rendered on texture - for(std::list::iterator it = _GlyphCache.begin(); it != _GlyphCache.end(); ++it) + SLetterKey g = *letter; + g.Size = bitmapFontSize; + + std::map::iterator it = _GlyphCache.find(g); + if (it != _GlyphCache.end()) { - if (it->GlyphIndex == letter->GlyphIndex && - it->Size == bitmapFontSize && - it->Embolden == letter->Embolden && - it->Oblique == letter->Oblique && - it->FontGenerator == letter->FontGenerator) - { - return &(*it); - } + return &(it->second); } if (insert) @@ -473,21 +462,15 @@ CTextureFont::SGlyphInfo* CTextureFont::findLetterGlyph(SLetterInfo *letter, boo // --------------------------------------------------------------------------- CTextureFont::SLetterInfo* CTextureFont::findLetter(SLetterKey &k, bool insert) { - // TODO: use std::map - for(uint i = 0; i < _Letters.size(); ++i) + std::map::iterator it = _Letters.find(k); + if (it != _Letters.end()) { - if (_Letters[i].Char == k.Char && _Letters[i].Size == k.Size && - _Letters[i].Embolden == k.Embolden && _Letters[i].Oblique == k.Oblique && - _Letters[i].FontGenerator == k.FontGenerator) - { - return &_Letters[i]; - } + return &(it->second); } if (insert) { - _Letters.push_back(SLetterInfo()); - SLetterInfo* letter = &_Letters.back(); + SLetterInfo* letter = &_Letters[k]; // get metrics for requested size letter->Char = k.Char; diff --git a/nel/src/3d/zone_lighter.cpp b/nel/src/3d/zone_lighter.cpp index 570ba1075..0ba8c2b95 100644 --- a/nel/src/3d/zone_lighter.cpp +++ b/nel/src/3d/zone_lighter.cpp @@ -350,7 +350,7 @@ void setCPUMask (IThread *thread, uint process) } // Set the CPU mask - thread->setCPUMask (1<setCPUMask (1<processCalc (_Process, *_Description); _ZoneLighter->_ProcessExitedMutex.enter(); @@ -624,7 +624,7 @@ void RenderTriangle (const CZoneLighter::CTriangle &triangle, const CZoneLighter void NL3D::CRenderZBuffer::run() { // Set the CPU mask - setCPUMask (Thread, _Process); + // setCPUMask (Thread, _Process); // Span array CPolygon2D::TRasterVect borders; @@ -927,7 +927,7 @@ void CZoneLighter::light (CLandscape &landscape, CZone& output, uint zoneToLight // Backup thread mask IThread *currentThread = IThread::getCurrentThread (); uint64 threadMask = currentThread->getCPUMask(); - currentThread->setCPUMask (1); + // currentThread->setCPUMask (1); // Calc the ray basis _SunDirection=description.SunDirection; @@ -1219,7 +1219,7 @@ void CZoneLighter::light (CLandscape &landscape, CZone& output, uint zoneToLight } // Reset old thread mask - currentThread->setCPUMask (threadMask); + // currentThread->setCPUMask (threadMask); // overflow ? if (_ZBufferOverflow) diff --git a/nel/src/web/http_client_curl.cpp b/nel/src/web/http_client_curl.cpp index 10bb0776c..b62300089 100644 --- a/nel/src/web/http_client_curl.cpp +++ b/nel/src/web/http_client_curl.cpp @@ -55,7 +55,10 @@ bool CCurlHttpClient::connect(const std::string &/* server */) curl_global_init(CURL_GLOBAL_ALL); _CurlStruct = curl_easy_init(); if(_Curl == NULL) + { + curl_global_cleanup(); return false; + } return true; } @@ -195,8 +198,8 @@ void CCurlHttpClient::disconnect() { curl_easy_cleanup(_Curl); _CurlStruct = NULL; + curl_global_cleanup(); } - curl_global_cleanup(); } CCurlHttpClient CurlHttpClient; diff --git a/nel/tools/3d/CMakeLists.txt b/nel/tools/3d/CMakeLists.txt index e6ba62e3b..8bf56b6c8 100644 --- a/nel/tools/3d/CMakeLists.txt +++ b/nel/tools/3d/CMakeLists.txt @@ -32,6 +32,7 @@ IF(WITH_NEL_TOOLS) zone_ig_lighter zone_lighter zone_welder + unbuild_elevation zone_elevation shapes_exporter shape2obj diff --git a/nel/tools/3d/unbuild_elevation/CMakeLists.txt b/nel/tools/3d/unbuild_elevation/CMakeLists.txt new file mode 100644 index 000000000..e6b7636d3 --- /dev/null +++ b/nel/tools/3d/unbuild_elevation/CMakeLists.txt @@ -0,0 +1,11 @@ +FILE(GLOB SRC *.cpp *.h *.rc) + +SOURCE_GROUP("" FILES ${SRC}) + +ADD_EXECUTABLE(unbuild_elevation ${SRC}) + +TARGET_LINK_LIBRARIES(unbuild_elevation nel3d nelmisc nelligo) +NL_DEFAULT_PROPS(unbuild_elevation "NeL, Tools, 3D: Unbuild Elevation") +NL_ADD_RUNTIME_FLAGS(unbuild_elevation) + +INSTALL(TARGETS unbuild_elevation RUNTIME DESTINATION ${NL_BIN_PREFIX} COMPONENT tools3d) diff --git a/nel/tools/3d/unbuild_elevation/gold_pill.ico b/nel/tools/3d/unbuild_elevation/gold_pill.ico new file mode 100644 index 000000000..618b67a5d Binary files /dev/null and b/nel/tools/3d/unbuild_elevation/gold_pill.ico differ diff --git a/nel/tools/3d/unbuild_elevation/main.rc b/nel/tools/3d/unbuild_elevation/main.rc new file mode 100644 index 000000000..d784a3092 --- /dev/null +++ b/nel/tools/3d/unbuild_elevation/main.rc @@ -0,0 +1,42 @@ +#include +#include "config.h" + +IDI_MAIN_ICON ICON DISCARDABLE "gold_pill.ico" + +#ifdef _DEBUG +#define NL_FILEEXT "_d" +#else +#define NL_FILEEXT "" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION NL_VERSION_RC + PRODUCTVERSION NL_VERSION_RC + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", AUTHOR + VALUE "FileDescription", "NeL Zone Elevation" + VALUE "FileVersion", NL_VERSION + VALUE "LegalCopyright", COPYRIGHT + VALUE "OriginalFilename", "zone_elevation" NL_FILEEXT ".exe" + VALUE "ProductName", "NeL Tools" + VALUE "ProductVersion", NL_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x9, 1200 + END +END diff --git a/nel/tools/3d/unbuild_elevation/unbuild_elevation.cpp b/nel/tools/3d/unbuild_elevation/unbuild_elevation.cpp new file mode 100644 index 000000000..f3c109a25 --- /dev/null +++ b/nel/tools/3d/unbuild_elevation/unbuild_elevation.cpp @@ -0,0 +1,477 @@ +// NeL - MMORPG Framework +// Copyright (C) 2021 Jan BOON (Kaetemi) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "../zone_lib/zone_utility.h" + +#include +#include +#include +#include +#include +#include +#include +//#include +#include +//#include +//#include +//#include +//#include +#include +#include +#include + +using namespace NL3D; +using namespace NLMISC; +using namespace NLLIGO; +using namespace std; + +namespace /* anonymous */ +{ + +sint32 s_ZoneMinX, s_ZoneMinY, s_ZoneMaxX, s_ZoneMaxY; +float s_CellSize = 160.0f; + +std::string s_SourceDir; /* R:\reference\2008_july\data\r2_desert */ +std::string s_ReferenceDir; /* R:\pipeline\export\continents\r2_desert\zone_weld */ + +std::string s_OutputPy; + +std::string s_SourceExt = "zonel"; +std::string s_ReferenceExt = "zonenhw"; + +CZoneRegion s_Land; /* "R:\graphics\landscape\ligo\desert\r2_desert.land" */ + +int s_Warnings; + +// unbuild_elevation --land "R:\graphics\landscape\ligo\desert\r2_desert.land" "Y:\temp\r2_desert_elevation.Py" "R:\reference\2008_july\data\r2_desert" "R:\pipeline\export\continents\r2_desert\zone_weld" +// --land "R:\graphics\landscape\ligo\jungle\zorai.land" --referenceext zonenhw "X:\wsl\big_zorai.py" "R:\reference\2008_july\data\zorai_zones" "R:\pipeline\export\continents\zorai\zone_weld" +// --land "R:\graphics\landscape\ligo\desert\r2_desert.land" "X:\wsl\big_r2_desert.py" "R:\reference\2008_july\data\r2_desert" "R:\pipeline\export\continents\r2_desert\zone_weld" +// --land "R:\graphics\landscape\ligo\jungle\r2_jungle.land" "X:\wsl\big_r2_jungle.py" "R:\reference\2008_july\data\r2_jungle" "R:\pipeline\export\continents\r2_jungle\zone_weld" +// --land "R:\graphics\landscape\ligo\jungle\r2_forest.land" "X:\wsl\big_r2_forest.py" "R:\reference\2008_july\data\r2_forest" "R:\pipeline\export\continents\r2_forest\zone_weld" +// --land "R:\graphics\landscape\ligo\lacustre\r2_lakes.land" "X:\wsl\big_r2_lakes.py" "R:\reference\2008_july\data\r2_lakes" "R:\pipeline\export\continents\r2_lakes\zone_weld" +// --land "R:\graphics\landscape\ligo\primes_racines\r2_roots.land" "X:\wsl\big_r2_roots.py" "R:\reference\2008_july\data\r2_roots" "R:\pipeline\export\continents\r2_roots\zone_weld" + +bool loadLand(const string &filename) +{ + try + { + CIFile fileIn; + if (fileIn.open (filename)) + { + CIXml xml(true); + nlverify(xml.init(fileIn)); + s_Land.serial(xml); + } + else + { + nlwarning("Can't open the land file: %s", filename.c_str()); + return false; + } + } + catch (const Exception& e) + { + nlwarning("Error in land file: %s", e.what()); + return true; + } + return true; +} + +bool getXYFromZoneName(sint32 &x, sint32 &y, const string &zoneName) +{ + string xStr, yStr; + uint32 i = 0; + while (zoneName[i] != '_') + { + yStr += zoneName[i]; + ++i; + if (i == zoneName.size()) + goto Fail; + } + if (!NLMISC::fromString(yStr, y)) + goto Fail; + y = -y; + ++i; + while (i < zoneName.size()) + { + xStr += zoneName[i]; + ++i; + } + if (xStr.size() != 2) + goto Fail; + xStr = NLMISC::toUpperAscii(xStr); + x = ((xStr[0] - 'A') * 26 + (xStr[1] - 'A')); + return true; +Fail: + x = -1; + y = -1; + return false; +} + +void getBitmapSize(int &w, int &h) +{ + sint32 sizeX = s_ZoneMaxX - s_ZoneMinX + 1; + sint32 sizeY = s_ZoneMaxY - s_ZoneMinY + 1; + w = sizeX * 20; + h = sizeY * 20; +} + +void getBitmapCoord(float &xc, float &yc, float x, float y) +{ + float deltaZ = 0.0f, deltaZ2 = 0.0f; + CRGBAF color; + sint32 sizeX = s_ZoneMaxX - s_ZoneMinX + 1; + sint32 sizeY = s_ZoneMaxY - s_ZoneMinY + 1; + + clamp(x, s_CellSize * s_ZoneMinX, s_CellSize * (s_ZoneMaxX + 1)); + clamp(y, s_CellSize * s_ZoneMinY, s_CellSize * (s_ZoneMaxY + 1)); + + xc = (x - s_CellSize * s_ZoneMinX) / (s_CellSize * sizeX); + yc = 1.0f - ((y - s_CellSize * s_ZoneMinY) / (s_CellSize * sizeY)); +} + +bool processZone(std::vector &output, const std::string &sourceFile, const std::string &referenceFile) +{ + std::string zone = CFile::getFilenameWithoutExtension(referenceFile); + + std::vector sourceVertices; + std::vector referenceVertices; + { + CZoneInfo zoneInfo; + { + CZone zone; + { + CIFile f(sourceFile); + zone.serial(f); + f.close(); + } + zone.retrieve(zoneInfo); + } + for (ptrdiff_t i = 0; i < (ptrdiff_t)zoneInfo.Patchs.size(); ++i) + { + sourceVertices.push_back(zoneInfo.Patchs[i].Patch.Vertices[0]); + sourceVertices.push_back(zoneInfo.Patchs[i].Patch.Vertices[1]); + sourceVertices.push_back(zoneInfo.Patchs[i].Patch.Vertices[2]); + sourceVertices.push_back(zoneInfo.Patchs[i].Patch.Vertices[3]); + } + } + { + CZoneInfo zoneInfo; + { + CZone zone; + { + CIFile f(referenceFile); + zone.serial(f); + f.close(); + } + zone.retrieve(zoneInfo); + } + for (ptrdiff_t i = 0; i < (ptrdiff_t)zoneInfo.Patchs.size(); ++i) + { + referenceVertices.push_back(zoneInfo.Patchs[i].Patch.Vertices[0]); + referenceVertices.push_back(zoneInfo.Patchs[i].Patch.Vertices[1]); + referenceVertices.push_back(zoneInfo.Patchs[i].Patch.Vertices[2]); + referenceVertices.push_back(zoneInfo.Patchs[i].Patch.Vertices[3]); + } + } + printf("Source vertices: %i, reference vertices: %i\n", (int)sourceVertices.size(), (int)referenceVertices.size()); + if (sourceVertices.size() != referenceVertices.size()) + { + nlwarning("Mismatching vertex count in zone %s, source vertices: %i, reference vertices: %i", zone.c_str(), (int)sourceVertices.size(), (int)referenceVertices.size()); + ++s_Warnings; + } + + std::vector processedSourceIndices; + std::vector processedReferenceIndices; + processedSourceIndices.resize(sourceVertices.size()); + processedReferenceIndices.resize(referenceVertices.size()); + + int referenceProcessedCount = 0; + int sourceProcessedCount = 0; + + for (ptrdiff_t i = 0; i < (ptrdiff_t)referenceVertices.size(); ++i) + { + if (processedReferenceIndices[i]) + continue; + + float referenceHeight = referenceVertices[i].z; + int referenceHeightDiv = 1; + ++referenceProcessedCount; + + for (ptrdiff_t j = i + 1; j < (ptrdiff_t)referenceVertices.size(); ++j) + { + if (processedReferenceIndices[j]) + continue; + + if (abs(referenceVertices[i].x - referenceVertices[j].x) < 0.1f + && abs(referenceVertices[i].y - referenceVertices[j].y) < 0.1f) + { + processedReferenceIndices[j] = true; + referenceHeight += referenceVertices[j].z; + ++referenceHeightDiv; + ++referenceProcessedCount; + } + } + + float sourceHeight = 0.f; + int sourceHeightDiv = 0; + + for (ptrdiff_t j = 0; j < (ptrdiff_t)sourceVertices.size(); ++j) + { + if (processedSourceIndices[j]) + continue; + + if (abs(referenceVertices[i].x - sourceVertices[j].x) < 0.1f + && abs(referenceVertices[i].y - sourceVertices[j].y) < 0.1f) + { + processedSourceIndices[j] = true; + sourceHeight += sourceVertices[j].z; + ++sourceHeightDiv; + ++sourceProcessedCount; + } + } + + if (!sourceHeightDiv) + { + nlwarning("No matching vertices in source for zone %s, x: %f, y: %f", zone.c_str(), referenceVertices[i].x, referenceVertices[i].y); + ++s_Warnings; + continue; + } + + if (referenceHeightDiv != sourceHeightDiv) + { + nlwarning("Mismatching vertex count for zone %s, x: %f, y: %f (reference: %i, source: %i)", zone.c_str(), referenceVertices[i].x, referenceVertices[i].y, referenceHeightDiv, sourceHeightDiv); + ++s_Warnings; + continue; + } + + referenceHeight /= (float)referenceHeightDiv; + sourceHeight /= (float)sourceHeightDiv; + + output.push_back(NLMISC::CVector(referenceVertices[i].x, referenceVertices[i].y, sourceHeight - referenceHeight)); + } + + return true; +} + +bool unbuildElevation() +{ + std::vector referenceZones; + CPath::getPathContent(s_ReferenceDir, true, false, true, referenceZones); + int totalZones = 0; + + std::vector output; + + for (std::vector::iterator it(referenceZones.begin()), end(referenceZones.end()); it != end; ++it) + { + if (CFile::getExtension(*it) != s_ReferenceExt) + continue; + + std::string zone = CFile::getFilenameWithoutExtension(*it); + std::string sourceZone = s_SourceDir + "/" + CFile::getFilenameWithoutExtension(*it) + "." + s_SourceExt; + + if (!CFile::fileExists(sourceZone)) + continue; + + printf("%s\n", nlUtf8ToMbcs(zone)); + ++totalZones; + + if (!processZone(output, sourceZone, *it)) + return false; + + printf("\n"); + } + + printf("Total zones: %i\n", totalZones); + +#if 1 + + std::vector processedOutput; + processedOutput.resize(output.size()); + std::vector reducedOutput; + for (ptrdiff_t i = 0; i < (ptrdiff_t)output.size(); ++i) + { + if (processedOutput[i]) + continue; + + CVector v = output[i]; + int div = 1; + + for (ptrdiff_t j = i + 1; j < (ptrdiff_t)output.size(); ++j) + { + if (processedOutput[j]) + continue; + + if (abs(output[i].x - output[j].x) < 0.1f + && abs(output[i].y - output[j].y) < 0.1f) + { + processedOutput[j] = true; + v.z += output[j].z; + ++div; + } + } + + v.z /= (float)div; + reducedOutput.push_back(v); + } + + printf("Reduced vertex count from %i to %i\n", (int)output.size(), (int)reducedOutput.size()); + +#else + + std::vector reducedOutput = output; + +#endif + + { + int w, h; + getBitmapSize(w, h); + + FILE *fo = fopen(nlUtf8ToMbcs(s_OutputPy), "w"); + if (!fo) + return false; + + fprintf(fo, "import naturalneighbor # https://github.com/innolitics/natural-neighbor-interpolation - pip3 install naturalneighbor\n"); + fprintf(fo, "import numpy as np\n"); + fprintf(fo, "import png # pip3 install pypng\n"); + fprintf(fo, "grid_ranges = [[0, 1, %i%s], [0, 1, %i%s], [-1, 1, 1j]]\n", w, "j", h, "j"); + + fprintf(fo, "points = np.array([\n"); + + for (ptrdiff_t i = 0; i < (ptrdiff_t)reducedOutput.size(); ++i) + { + float x, y; + getBitmapCoord(x, y, reducedOutput[i].x, reducedOutput[i].y); + fprintf(fo, "\t[ %f, %f, 0 ], # %f, %f\n", x, y, reducedOutput[i].x, reducedOutput[i].y); + } + + fprintf(fo, "])\n"); + fprintf(fo, "\n"); + fprintf(fo, "values = np.array([\n"); + + for (ptrdiff_t i = 0; i < (ptrdiff_t)reducedOutput.size(); ++i) + { + fprintf(fo, "\t%f,\n", reducedOutput[i].z); + } + + fprintf(fo, "])\n"); + + fprintf(fo, "nn_interpolated_values = naturalneighbor.griddata(points, values, grid_ranges)\n"); + fprintf(fo, "img = []\n"); + fprintf(fo, "for y in range(0, %i):\n", h); + fprintf(fo, "\tline = []\n"); + fprintf(fo, "\tfor x in range(0, %i):\n", w); + fprintf(fo, "\t\tline.append(np.round(nn_interpolated_values[x][y][0]).astype(int) + 127)\n"); + fprintf(fo, "\timg.append(line)\n"); + fprintf(fo, "with open('%s.png', 'wb') as f:\n", CFile::getFilenameWithoutExtension(s_OutputPy).c_str()); + fprintf(fo, "\tw = png.Writer(%i, %i, greyscale=True)\n", w, h); + fprintf(fo, "\tw.write(f, img)\n"); + fflush(fo); + fclose(fo); + } + + printf("Warnings: %i\n", s_Warnings); + return true; +} + +bool unbuildElevation(NLMISC::CCmdArgs &args) +{ + s_OutputPy = args.getAdditionalArg("output")[0]; + s_SourceDir = args.getAdditionalArg("source")[0]; + s_ReferenceDir = args.getAdditionalArg("reference")[0]; + + if (args.haveLongArg("zonemin") && args.haveLongArg("zonemax")) + { + sint32 zoneMinX, zoneMinY; + sint32 zoneMaxX, zoneMaxY; + if (!getXYFromZoneName(zoneMinX, zoneMinY, args.getLongArg("zonemin")[0]) + || !getXYFromZoneName(zoneMaxX, zoneMaxY, args.getLongArg("zonemax")[0])) + { + return false; + } + s_ZoneMinX = min(zoneMinX, zoneMaxX); + s_ZoneMaxX = max(zoneMinX, zoneMaxX); + s_ZoneMinY = min(zoneMinY, zoneMaxY); + s_ZoneMaxY = max(zoneMinY, zoneMaxY); + } + else if (args.haveLongArg("land")) + { + if (!loadLand(args.getLongArg("land")[0])) + return false; + s_ZoneMinX = s_Land.getMinX(); + s_ZoneMaxX = s_Land.getMaxX(); + s_ZoneMinY = s_Land.getMinY(); + s_ZoneMaxY = s_Land.getMaxY(); + } + else + { + nlwarning("Must have either both 'zonemin' and 'zonemax', or 'land' specified"); + return false; + } + + if (args.haveLongArg("cellsize")) + { + if (!NLMISC::fromString(args.getLongArg("cellsize")[0], s_CellSize)) + return false; + } + + if (args.haveLongArg("sourceext")) + { + s_SourceExt = args.getLongArg("sourceext")[0]; + } + + if (args.haveLongArg("referenceext")) + { + s_ReferenceExt = args.getLongArg("referenceext")[0]; + } + + return unbuildElevation(); +} + +} /* anonymous namespace */ + +int main(int argc, char **argv) +{ + NLMISC::CApplicationContext myApplicationContext; + + NLMISC::CCmdArgs args; + + args.addAdditionalArg("output", "Output Python file path"); + args.addAdditionalArg("source", "Input folder with zones at the right height"); + args.addAdditionalArg("reference", "Input folder with zones at the wrong height"); + + args.addArg("", "sourceext", "extension", "Source zone extension (default: zonel)"); + args.addArg("", "referenceext", "extension", "Reference zone extension (default: zonew)"); + + args.addArg("", "land", "land", "Ligo land file (either specify this or the boundaries)"); + args.addArg("", "zonemin", "zone", "Zone boundary"); + args.addArg("", "zonemax", "zone", "Zone boundary"); + args.addArg("", "cellsize", "meters", "Zone cell size (default: 160)"); + + if (!args.parse(argc, argv)) + { + return EXIT_FAILURE; + } + + if (!unbuildElevation(args)) + { + args.displayHelp(); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/* end of file */ \ No newline at end of file diff --git a/nel/tools/3d/zone_elevation/zone_elevation.cpp b/nel/tools/3d/zone_elevation/zone_elevation.cpp index 598cfda31..abe9b511c 100644 --- a/nel/tools/3d/zone_elevation/zone_elevation.cpp +++ b/nel/tools/3d/zone_elevation/zone_elevation.cpp @@ -186,6 +186,111 @@ NLMISC::CVector getHeightNormal(float x, float y) // compute the heightmap normal with the tangents return (ds ^ dt).normed(); } +// *************************************************************************** +// void CExport::computeSubdividedTangents(uint numBinds, const NL3D::CBezierPatch &patch, uint edge, NLMISC::CVector subTangents[8]) +void computeSubdividedTangents(uint numBinds, const NL3D::CBezierPatch &patch, uint edge, NLMISC::CVector subTangents[8]) +{ + // Subdivide the Bezier patch to get the correct tangents to apply to neighbors + CBezierPatch subPatchs1_2[2]; + CBezierPatch subPatchs1_4[4]; + + // subdivide on s if edge is horizontal + bool subDivideOnS= (edge&1)==1; + + // Subdivide one time. + if(subDivideOnS) patch.subdivideS(subPatchs1_2[0], subPatchs1_2[1]); + else patch.subdivideT(subPatchs1_2[0], subPatchs1_2[1]); + + // Subdivide again for bind 1/4. + if(numBinds==4) + { + if(subDivideOnS) + { + subPatchs1_2[0].subdivideS(subPatchs1_4[0], subPatchs1_4[1]); + subPatchs1_2[1].subdivideS(subPatchs1_4[2], subPatchs1_4[3]); + } + else + { + subPatchs1_2[0].subdivideT(subPatchs1_4[0], subPatchs1_4[1]); + subPatchs1_2[1].subdivideT(subPatchs1_4[2], subPatchs1_4[3]); + } + } + + // Now, fill the tangents according to edge. + bool invertPaSrc= edge>=2; + // Bind 1/2 case. + if(numBinds==2) + { + // 4 tangents to fill. + for(uint i=0;i<4;i++) + { + // get patch id from 0 to 1. + uint paSrcId= i/2; + // invert if edge is 2 or 3 + if(invertPaSrc) paSrcId= 1-paSrcId; + // get tg id in this patch. + uint tgSrcId= (i&1) + edge*2; + // fill result. + subTangents[i]= subPatchs1_2[paSrcId].Tangents[tgSrcId]; + } + } + // Bind 1/4 case. + else + { + // 8 tangents to fill. + for(uint i=0;i<8;i++) + { + // get patch id from 0 to 3. + uint paSrcId= i/2; + // invert if edge is 2 or 3 + if(invertPaSrc) paSrcId= 3-paSrcId; + // get tg id in this patch. + uint tgSrcId= (i&1) + edge*2; + // fill result. + subTangents[i]= subPatchs1_4[paSrcId].Tangents[tgSrcId]; + } + } +} + +// *************************************************************************** +// bool CExport::applyVertexBind(NL3D::CPatchInfo &pa, NL3D::CPatchInfo &oldPa, uint edgeToModify, bool startEdge, ... +bool applyVertexBind(NL3D::CPatchInfo &pa, NL3D::CPatchInfo &oldPa, uint edgeToModify, bool startEdge, + const NLMISC::CMatrix &oldTgSpace, const NLMISC::CMatrix &newTgSpace, + const NLMISC::CVector &bindedPos, const NLMISC::CVector &bindedTangent ) +{ + // Get the vertex to modify according to edge/startEdge + uint vertexToModify= edgeToModify + (startEdge?0:1); + vertexToModify&=3; + + // If already moved, no-op + if(pa.Patch.Vertices[vertexToModify]==bindedPos) + return false; + else + { + // Change the vertex + pa.Patch.Vertices[vertexToModify]= bindedPos; + + // change the tangent, according to startEdge + pa.Patch.Tangents[edgeToModify*2 + (startEdge?0:1) ]= bindedTangent; + + // Must change the tangent which is on the other side of the vertex: + uint tgToModify= 8 + edgeToModify*2 + (startEdge?-1:+2); + tgToModify&=7; + /* To keep the same continuity aspect around the vertex, we compute the original tangent in a + special space: the Binded Patch Tangent Space. Once we have the original tangent in the original patch TgSpace, + we reapply it in the transformed patch TgSpace, to get the transformed tangent + */ + pa.Patch.Tangents[tgToModify]= newTgSpace * ( oldTgSpace.inverted() * oldPa.Patch.Tangents[tgToModify] ); + + + // Do the same to the associated interior. + pa.Patch.Interiors[vertexToModify]= newTgSpace * ( oldTgSpace.inverted() * oldPa.Patch.Interiors[vertexToModify] ); + + + // modified + return true; + } +} void applyZoneHeightmap() { @@ -201,6 +306,9 @@ void applyZoneHeightmap() std::vector zoneBorderVertices; zone.retrieve(zonePatches, zoneBorderVertices); + // Bkup the original patchs. + vector oldPatchInfos = zonePatches; + // Apply the Heighmap to all vertices/tangents/interiors (see Land Export tool.) for (size_t i = 0; i < zonePatches.size(); ++i) { @@ -254,6 +362,94 @@ void applyZoneHeightmap() } } + // See `void CExport::transformZone (CZone &zeZone, sint32 nPosX, sint32 nPosY, uint8 nRot, uint8 nFlip, bool computeHeightmap)` + // 3. For all binds, reset the position of near vertices/tangents/interiors. Must do it at last + // -------- + bool bindVertexModified = true; + // Since this is a recursive problem (binded patchs may bind other patchs), do it unitl all vertices no more move :) + while (bindVertexModified) + { + bindVertexModified = false; + for (size_t i = 0; i < zonePatches.size(); ++i) + { + CPatchInfo &rPI = zonePatches[i]; + + // For all edges + for (size_t j = 0; j < 4; ++j) + { + uint numBinds = rPI.BindEdges[j].NPatchs; + // If this edge is binded on 2 or 4 patches. + if (numBinds == 2 || numBinds == 4) + { + // compute the 4 or 8 tangents along the edge (in CCW) + CVector subTangents[8]; + computeSubdividedTangents(numBinds, rPI.Patch, j, subTangents); + + // For all vertex to bind: 1 or 3. + for (uint vb = 0; vb < numBinds - 1; vb++) + { + // compute the s/t coordinate + float bindS, bindT; + // 0.5, or 0.25, 0.5, 0.75 + float ec = (float)(vb + 1) / (float)numBinds; + switch (j) + { + case 0: bindS= 0; bindT= ec; break; + case 1: bindS= ec; bindT= 1; break; + case 2: bindS= 1; bindT= 1-ec; break; + case 3: bindS= 1-ec; bindT= 0; break; + } + + // compute the vertex position from big patch. + CVector bindedPos; + bindedPos = rPI.Patch.eval(bindS, bindT); + + // Compute a TgSpace matrix around this position. + CMatrix oldTgSpace; + CMatrix newTgSpace; + + // Build the original tgtSpace (patch before deformation) + oldTgSpace.setRot(oldPatchInfos[i].Patch.evalTangentS(bindS, bindT), + oldPatchInfos[i].Patch.evalTangentT(bindS, bindT), + oldPatchInfos[i].Patch.evalNormal(bindS, bindT)); + oldTgSpace.normalize(CMatrix::ZYX); + oldTgSpace.setPos(oldPatchInfos[i].Patch.eval(bindS, bindT)); + + // Build the new tgtSpace + newTgSpace.setRot(rPI.Patch.evalTangentS(bindS, bindT), + rPI.Patch.evalTangentT(bindS, bindT), + rPI.Patch.evalNormal(bindS, bindT)); + newTgSpace.normalize(CMatrix::ZYX); + newTgSpace.setPos(bindedPos); + + // apply to the 2 smaller binded patchs which share this vertex. + uint edgeToModify; + sint ngbId; + CVector bindedTangent; + + // The first patch (CCW) must change the vertex which starts on the edge (CCW) + edgeToModify = rPI.BindEdges[j].Edge[vb]; + // get the tangent to set + bindedTangent = subTangents[vb * 2 + 1]; + // get the patch id. + ngbId = rPI.BindEdges[j].Next[vb]; + bindVertexModified |= applyVertexBind(zonePatches[ngbId], oldPatchInfos[ngbId], edgeToModify, + true, oldTgSpace, newTgSpace, bindedPos, bindedTangent); + + // The second patch (CCW) must change the vertex which ends on the edge (CCW) + edgeToModify = rPI.BindEdges[j].Edge[vb + 1]; + // get the tangent to set + bindedTangent = subTangents[vb * 2 + 2]; + // get the patch id. + ngbId = rPI.BindEdges[j].Next[vb + 1]; + bindVertexModified |= applyVertexBind(zonePatches[ngbId], oldPatchInfos[ngbId], edgeToModify, + false, oldTgSpace, newTgSpace, bindedPos, bindedTangent); + } + } + } + } + } + // Save zone zone.build(zoneId, zonePatches, zoneBorderVertices); COFile centerSave(s_OutputZone); diff --git a/nel/tools/build_gamedata/configuration/scripts.py b/nel/tools/build_gamedata/configuration/scripts.py index 5bbed497f..6e885bdd3 100755 --- a/nel/tools/build_gamedata/configuration/scripts.py +++ b/nel/tools/build_gamedata/configuration/scripts.py @@ -23,7 +23,7 @@ # along with this program. If not, see . # -import time, sys, os, shutil, subprocess, distutils.dir_util +import time, sys, os, shutil, subprocess, distutils.dir_util, multiprocessing, math ActiveProjectDirectory = os.getenv("NELBUILDACTIVEPROJECT", "configuration/project") sys.path.append(ActiveProjectDirectory) @@ -32,6 +32,27 @@ def printLog(log, text): log.write(text + "\n") print text +pendingProcesses = [] +processLimit = math.ceil(multiprocessing.cpu_count() * 0.75) + +def callParallelProcess(command): + res = 0 + if len(pendingProcesses) >= processLimit: + waitingProc = pendingProcesses.pop(0) + res = waitingProc.wait() + proc = subprocess.Popen(command) + pendingProcesses.append(proc) + return res + +def flushParallelProcesses(): + res = 0 + while (len(pendingProcesses) > 0): + waitingProc = pendingProcesses.pop(0) + procRes = waitingProc.wait() + if procRes != 0: + res = procRes + return res + def mkPath(log, path): printLog(log, "DIR " + path) distutils.dir_util.mkpath(path) diff --git a/nel/tools/build_gamedata/processes/ai_wmap/2_build.py b/nel/tools/build_gamedata/processes/ai_wmap/2_build.py index 8aa63f62f..8dd43be16 100755 --- a/nel/tools/build_gamedata/processes/ai_wmap/2_build.py +++ b/nel/tools/build_gamedata/processes/ai_wmap/2_build.py @@ -68,13 +68,15 @@ else: printLog(log, ">>> Generate sized wmap <<<") subprocess.call([ AiBuildWmap, "pacsBuildGabarit " + AiWmapContinentName ]) printLog(log, ">>> Generate cwmaps for each size <<<") - subprocess.call([ AiBuildWmap, "pacsBuildWmap " + AiWmapContinentName + "_0" ]) - subprocess.call([ AiBuildWmap, "pacsBuildWmap " + AiWmapContinentName + "_1" ]) - subprocess.call([ AiBuildWmap, "pacsBuildWmap " + AiWmapContinentName + "_2" ]) + callParallelProcess([ AiBuildWmap, "pacsBuildWmap " + AiWmapContinentName + "_0" ]) + callParallelProcess([ AiBuildWmap, "pacsBuildWmap " + AiWmapContinentName + "_1" ]) + callParallelProcess([ AiBuildWmap, "pacsBuildWmap " + AiWmapContinentName + "_2" ]) + flushParallelProcesses() printLog(log, ">>> Generate bitmap for each size <<<") - subprocess.call([ AiBuildWmap, "pacsBuildBitmap " + AiWmapContinentName + "_0" ]) - subprocess.call([ AiBuildWmap, "pacsBuildBitmap " + AiWmapContinentName + "_1" ]) - subprocess.call([ AiBuildWmap, "pacsBuildBitmap " + AiWmapContinentName + "_2" ]) + callParallelProcess([ AiBuildWmap, "pacsBuildBitmap " + AiWmapContinentName + "_0" ]) + callParallelProcess([ AiBuildWmap, "pacsBuildBitmap " + AiWmapContinentName + "_1" ]) + callParallelProcess([ AiBuildWmap, "pacsBuildBitmap " + AiWmapContinentName + "_2" ]) + flushParallelProcesses() printLog(log, ">>> Clear height maps for size 1 and 2 <<<") subprocess.call([ AiBuildWmap, "pacsClearHeightmap " + AiWmapContinentName ]) printLog(log, ">>> Cut tga for world editor <<<") diff --git a/nel/tools/build_gamedata/processes/rbank/2_build.py b/nel/tools/build_gamedata/processes/rbank/2_build.py index b38aea94b..6a55e94fc 100755 --- a/nel/tools/build_gamedata/processes/rbank/2_build.py +++ b/nel/tools/build_gamedata/processes/rbank/2_build.py @@ -55,6 +55,7 @@ printLog(log, "") # Build rbank bbox printLog(log, ">>> Build rbank bbox <<<") tempBbox = ExportBuildDirectory + "/" + RbankBboxBuildDirectory + "/temp.bbox" +rebuiltBbox = False if BuildIgBoxes == "": toolLogFail(log, BuildIgBoxesTool, ToolSuffix) else: @@ -70,6 +71,7 @@ else: else: printLog(log, "DETECT SKIP Shape->Bbox") if needUpdateIg or needUpdateShape: + rebuiltBbox = True printLog(log, "DETECT DECIDE UPDATE") cf = open("build_ig_boxes.cfg", "w") cf.write("\n") @@ -204,23 +206,35 @@ elif ExecTimeout == "": toolLogFail(log, ExecTimeoutTool, ToolSuffix) else: zonefiles = findFiles(log, ExportBuildDirectory + "/" + ZoneWeldBuildDirectory, "", ".zonew") + zonesToBuild = [] for zonefile in zonefiles: zone = os.path.basename(zonefile)[0:-len(".zonew")] lr1 = ExportBuildDirectory + "/" + RbankSmoothBuildDirectory + "/" + zone + ".lr" nearzones = subprocess.Popen([ GetNeighbors, zone ], stdout = subprocess.PIPE).communicate()[0].strip().split(" ") printLog(log, "ZONE " + zone + ": " + str(nearzones)) - zone_to_build = 0 + zoneToBuild = 0 for nearzone in nearzones: sourcePath = ExportBuildDirectory + "/" + ZoneWeldBuildDirectory + "/" + nearzone + ".zonew" if (os.path.isfile(sourcePath)): - if (needUpdate(log, sourcePath, lr1)): - zone_to_build = 1 + if (rebuiltBbox or needUpdate(log, sourcePath, lr1)): + zoneToBuild = 1 + zonesToBuild.append(os.path.basename(zonefile)) sourcePath = ExportBuildDirectory + "/" + ZoneWeldBuildDirectory + "/" + zone + ".zonew" - if zone_to_build: + if zoneToBuild: printLog(log, sourcePath + " -> " + lr1) - subprocess.call([ ExecTimeout, str(RbankBuildTesselTimeout), BuildRbank, "-c", "-P", "-g", os.path.basename(zonefile) ]) + # subprocess.call([ ExecTimeout, str(RbankBuildTesselTimeout), BuildRbank, "-c", "-P", "-g", os.path.basename(zonefile) ]) else: printLog(log, "SKIP " + lr1) + while len(zonesToBuild) > 0: + processCommand = [ ExecTimeout, str(RbankBuildTesselTimeout), BuildRbank, "-c", "-P", "-g" ] + processCommand.extend(zonesToBuild[:min(len(zonesToBuild), 64)]) + if len(zonesToBuild) > 64: + zonesToBuild = zonesToBuild[64:] + else: + zonesToBuild = [] + print processCommand + callParallelProcess(processCommand) + flushParallelProcesses() printLog(log, "") printLog(log, ">>> Detect modifications to rebuild lr <<<") @@ -245,7 +259,7 @@ if needUpdateBboxRbank: else: printLog(log, "DETECT SKIP Lr->Rbank") -if needUpdateCmbLr or needUpdateCmbRbank or needUpdateLrRbank or needUpdateBboxRbank: +if rebuiltBbox or needUpdateCmbLr or needUpdateCmbRbank or needUpdateLrRbank or needUpdateBboxRbank: printLog(log, "DETECT DECIDE UPDATE") printLog(log, ">>> Build rbank process global <<<") # This generates temp lr files. TODO: Check if the LR changed? if BuildRbank == "": diff --git a/nel/tools/build_gamedata/processes/zone/2_build.py b/nel/tools/build_gamedata/processes/zone/2_build.py index f9a31e3c4..cb9287c36 100755 --- a/nel/tools/build_gamedata/processes/zone/2_build.py +++ b/nel/tools/build_gamedata/processes/zone/2_build.py @@ -147,7 +147,8 @@ else: sourceFile = ExportBuildDirectory + "/" + ZoneWeldBuildDirectory + "/" + file destFile = ExportBuildDirectory + "/" + ZoneWeldBuildDirectory + "/" + os.path.basename(file)[0:-len(".zonenhw")] + ".zonew" if needUpdateLogRemoveDest(log, sourceFile, destFile): - subprocess.call([ ZoneElevation, sourceFile, destFile, "--land", land, "--heightmap", heightMap1, "--zfactor", LigoExportZFactor1, "--heightmap2", heightMap2, "--zfactor2", LigoExportZFactor2 ]) + callParallelProcess([ ZoneElevation, sourceFile, destFile, "--land", land, "--heightmap", heightMap1, "--zfactor", LigoExportZFactor1, "--heightmap2", heightMap2, "--zfactor2", LigoExportZFactor2 ]) + flushParallelProcesses() printLog(log, "") log.close() diff --git a/nel/tools/build_gamedata/processes/zone_light/2_build.py b/nel/tools/build_gamedata/processes/zone_light/2_build.py index 84d2b26a4..1e8e0c925 100755 --- a/nel/tools/build_gamedata/processes/zone_light/2_build.py +++ b/nel/tools/build_gamedata/processes/zone_light/2_build.py @@ -68,7 +68,8 @@ else: destFile = destDir + "/" + file[0:-len(".zonew")] + ".zonel" if (needUpdateLogRemoveDest(log, srcFile, destFile)): dependFile = dependDir + "/" + file[0:-len(".zonew")] + ".depend" - subprocess.call([ ExecTimeout, str(ZoneLightBuildTimeout), ZoneLighter, srcFile, destFile, ActiveProjectDirectory + "/generated/properties.cfg", dependFile ]) + callParallelProcess([ ExecTimeout, str(ZoneLightBuildTimeout), ZoneLighter, srcFile, destFile, ActiveProjectDirectory + "/generated/properties.cfg", dependFile ]) + flushParallelProcesses() printLog(log, "") # For each zone_light ig @@ -94,7 +95,8 @@ else: if (needUpdateLogRemoveDest(log, igsrcFile, destFile)): srcFile = srcDir + "/" + file dependFile = dependDir + "/" + file[0:-len(".zonel")] + ".depend" - subprocess.call([ ExecTimeout, str(ZoneIgLightBuildTimeout), ZoneIgLighter, srcFile, destFile, ActiveProjectDirectory + "/generated/properties.cfg", dependFile ]) + callParallelProcess([ ExecTimeout, str(ZoneIgLightBuildTimeout), ZoneIgLighter, srcFile, destFile, ActiveProjectDirectory + "/generated/properties.cfg", dependFile ]) + flushParallelProcesses() printLog(log, "") log.close() diff --git a/nel/tools/pacs/build_rbank/prim_checker.cpp b/nel/tools/pacs/build_rbank/prim_checker.cpp index e4c2b0b91..effd07bd7 100644 --- a/nel/tools/pacs/build_rbank/prim_checker.cpp +++ b/nel/tools/pacs/build_rbank/prim_checker.cpp @@ -336,13 +336,19 @@ void CPrimChecker::render(CPrimZone *zone, uint8 bits) */ void CPrimChecker::render(const CPolygon &poly, uint16 value) { + static const sint centerOffset = 20480; // zones are max 40960 + CPolygon polyOffset = poly; + for (ptrdiff_t i = 0; i < (ptrdiff_t)polyOffset.Vertices.size(); ++i) + // center to computeBorders range (-32k to 32k) + polyOffset.Vertices[i] += CVector(-centerOffset, centerOffset, 0); + list convex; // divide poly in convex polys - if (!poly.toConvexPolygons(convex, CMatrix::Identity)) + if (!polyOffset.toConvexPolygons(convex, CMatrix::Identity)) { convex.clear(); - CPolygon reverse = poly; + CPolygon reverse = polyOffset; std::reverse(reverse.Vertices.begin(), reverse.Vertices.end()); if (!reverse.toConvexPolygons(convex, CMatrix::Identity)) return; @@ -357,6 +363,7 @@ void CPrimChecker::render(const CPolygon &poly, uint16 value) sint ymin; convex2d.computeBorders(rasterized, ymin); + ymin -= centerOffset; // uncenter sint dy; for (dy=0; dy<(sint)rasterized.size(); ++dy) @@ -365,19 +372,19 @@ void CPrimChecker::render(const CPolygon &poly, uint16 value) for (x=rasterized[dy].first; x<=rasterized[dy].second; ++x) { - uint8 prevBits = _Grid.get((uint)x, (uint)(ymin+dy)); + uint8 prevBits = _Grid.get((uint)x + centerOffset, (uint)(ymin + dy)); // only set if there was not a water shape there or if previous was lower if ((prevBits & Water) != 0) { - uint16 prevWS = _Grid.index((uint)x, (uint)(ymin+dy)); + uint16 prevWS = _Grid.index((uint)x + centerOffset, (uint)(ymin + dy)); if (_WaterHeight[value] < _WaterHeight[prevWS]) continue; } - _Grid.index((uint)x, (uint)(ymin+dy), value); - _Grid.set((uint)x, (uint)(ymin+dy), Water); + _Grid.index((uint)x + centerOffset, (uint)(ymin + dy), value); + _Grid.set((uint)x + centerOffset, (uint)(ymin + dy), Water); } } } @@ -389,13 +396,19 @@ void CPrimChecker::render(const CPolygon &poly, uint16 value) */ void CPrimChecker::renderBits(const CPolygon &poly, uint8 bits) { + static const sint centerOffset = 20480; // zones are max 40960 + CPolygon polyOffset = poly; + for (ptrdiff_t i = 0; i < (ptrdiff_t)polyOffset.Vertices.size(); ++i) + // center to computeBorders range (-32k to 32k) + polyOffset.Vertices[i] += CVector(-centerOffset, centerOffset, 0); + list convex; // divide poly in convex polys - if (!poly.toConvexPolygons(convex, CMatrix::Identity)) + if (!polyOffset.toConvexPolygons(convex, CMatrix::Identity)) { convex.clear(); - CPolygon reverse = poly; + CPolygon reverse = polyOffset; std::reverse(reverse.Vertices.begin(), reverse.Vertices.end()); if (!reverse.toConvexPolygons(convex, CMatrix::Identity)) return; @@ -410,6 +423,7 @@ void CPrimChecker::renderBits(const CPolygon &poly, uint8 bits) sint ymin; convex2d.computeBorders(rasterized, ymin); + ymin -= centerOffset; // uncenter sint dy; for (dy=0; dy<(sint)rasterized.size(); ++dy) @@ -418,7 +432,7 @@ void CPrimChecker::renderBits(const CPolygon &poly, uint8 bits) for (x=rasterized[dy].first; x<=rasterized[dy].second; ++x) { - _Grid.set((uint)x, (uint)(ymin+dy), bits); + _Grid.set((uint)x + centerOffset, (uint)(ymin + dy), bits); } } }