Merge branch 'develop' into ryzomclassic-develop

ryzomclassic-develop
kaetemi 4 years ago
commit fdbe73937a

@ -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<NLMISC::CRect> _AtlasNodes;
std::vector <SLetterInfo> _Letters;
std::map<SLetterKey, SLetterInfo> _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<SGlyphInfo> _GlyphCache;
std::map<SLetterKey, SGlyphInfo> _GlyphCache;
SGlyphInfo* findLetterGlyph(SLetterInfo *letter, bool insert);
// render letter glyph into glyph cache

@ -1,4 +1,5 @@
ADD_SUBDIRECTORY(font)
ADD_SUBDIRECTORY(font_perf)
ADD_SUBDIRECTORY(cluster_viewer)
ADD_SUBDIRECTORY(shape_viewer)

@ -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 <ESC> 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));

@ -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)

@ -0,0 +1,99 @@
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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 <http://www.gnu.org/licenses/>.
#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 <windows.h>
#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;
}

@ -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<SLetterKey, SLetterInfo>::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<uint8>&src = btm.getPixels();
for(std::list<SGlyphInfo>::iterator it = _GlyphCache.begin(); it != _GlyphCache.end(); ++it)
for(std::map<SLetterKey, SGlyphInfo>::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<SGlyphInfo>::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<SGlyphInfo>::iterator it = _GlyphCache.begin(); it != _GlyphCache.end(); ++it)
{
if (it->GlyphIndex == letter->GlyphIndex &&
it->Size == bitmapFontSize &&
it->Embolden == letter->Embolden &&
it->Oblique == letter->Oblique &&
it->FontGenerator == letter->FontGenerator)
SLetterKey g = *letter;
g.Size = bitmapFontSize;
std::map<SLetterKey, SGlyphInfo>::iterator it = _GlyphCache.find(g);
if (it != _GlyphCache.end())
{
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<uint64>
for(uint i = 0; i < _Letters.size(); ++i)
std::map<SLetterKey, SLetterInfo>::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;

@ -350,7 +350,7 @@ void setCPUMask (IThread *thread, uint process)
}
// Set the CPU mask
thread->setCPUMask (1<<i);
// thread->setCPUMask (1<<i);
}
}
@ -379,7 +379,7 @@ public:
void run()
{
// Set the CPU mask
setCPUMask (Thread, _Process);
// setCPUMask (Thread, _Process);
_ZoneLighter->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)

@ -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();
}
}
CCurlHttpClient CurlHttpClient;

@ -32,6 +32,7 @@ IF(WITH_NEL_TOOLS)
zone_ig_lighter
zone_lighter
zone_welder
unbuild_elevation
zone_elevation
shapes_exporter
shape2obj

@ -65,6 +65,7 @@ struct SExportOptions
float ZFactor1;
string HeightMapFile2;
float ZFactor2;
bool ExtendCoords;
string LandFile;
// -----------------------------------------------------------------------
@ -105,6 +106,9 @@ struct SExportOptions
CConfigFile::CVar &cvZFactor2 = cf.getVar("ZFactor2");
ZFactor2 = cvZFactor2.asFloat();
CConfigFile::CVar &cvExtendCoords = cf.getVar("ExtendCoords");
ExtendCoords = cvExtendCoords.asFloat();
CConfigFile::CVar &cvLandFile = cf.getVar("LandFile");
LandFile = cvLandFile.asString();
}
@ -214,13 +218,19 @@ static float getHeightMapZ(float x, float y, const CZoneLimits &zl, const SExpo
sint32 SizeX = zl._ZoneMaxX - zl._ZoneMinX + 1;
sint32 SizeY = zl._ZoneMaxY - zl._ZoneMinY + 1;
clamp (x, options.CellSize * zl._ZoneMinX, options.CellSize * (zl._ZoneMaxX + 1));
clamp (y, options.CellSize * zl._ZoneMinY, options.CellSize * (zl._ZoneMaxY + 1));
if (heightMap1 != NULL)
{
color = heightMap1->getColor ( (x - options.CellSize * zl._ZoneMinX) / (options.CellSize * SizeX),
1.0f - ((y - options.CellSize * zl._ZoneMinY) / (options.CellSize * SizeY)));
float xc = (x - options.CellSize * zl._ZoneMinX) / (options.CellSize * SizeX);
float yc = 1.0f - ((y - options.CellSize * zl._ZoneMinY) / (options.CellSize * SizeY));
if (options.ExtendCoords)
{
uint32 w = heightMap1->getWidth(), h = heightMap1->getHeight();
xc -= .5f / (float)w;
yc -= .5f / (float)h;
xc = xc * (float)(w + 1) / (float)w;
yc = yc * (float)(h + 1) / (float)h;
}
color = heightMap1->getColor(xc, yc);
color *= 255.f;
deltaZ = color.A;
deltaZ = deltaZ - 127.0f; // Median intensity is 127
@ -229,8 +239,17 @@ static float getHeightMapZ(float x, float y, const CZoneLimits &zl, const SExpo
if (heightMap2 != NULL)
{
color = heightMap2->getColor ( (x - options.CellSize * zl._ZoneMinX) / (options.CellSize * SizeX),
1.0f - ((y - options.CellSize * zl._ZoneMinY) / (options.CellSize * SizeY)));
float xc = (x - options.CellSize * zl._ZoneMinX) / (options.CellSize * SizeX);
float yc = 1.0f - ((y - options.CellSize * zl._ZoneMinY) / (options.CellSize * SizeY));
if (options.ExtendCoords)
{
uint32 w = heightMap2->getWidth(), h = heightMap2->getHeight();
xc -= .5f / (float)w;
yc -= .5f / (float)h;
xc = xc * (float)(w + 1) / (float)w;
yc = yc * (float)(h + 1) / (float)h;
}
color = heightMap2->getColor(xc, yc);
color *= 255.f;
deltaZ2 = color.A;
deltaZ2 = deltaZ2 - 127.0f; // Median intensity is 127
@ -260,6 +279,7 @@ int main(int nNbArg, char**ppArgs)
printf ("ZFactor1 = 1.0;\n");
printf ("HeightMapFile2 = \"R:/graphics/landscape/ligo/jungle/noise.tga\";\n");
printf ("ZFactor2 = 0.5;\n");
printf ("ExtendCoords = 0;\n");
printf ("LandFile = \"w:/matis.land\";\n");
return EXIT_FAILURE;

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -0,0 +1,42 @@
#include <windows.h>
#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

@ -0,0 +1,502 @@
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2021 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
//
// 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 <http://www.gnu.org/licenses/>.
#include "../zone_lib/zone_utility.h"
#include <iostream>
#include <nel/misc/types_nl.h>
#include <nel/misc/file.h>
#include <nel/misc/i_xml.h>
#include <nel/misc/common.h>
#include <nel/misc/cmd_args.h>
#include <nel/misc/bitmap.h>
//#include <nel/3d/quad_tree.h>
#include <nel/3d/zone.h>
//#include <nel/3d/landscape.h>
//#include <nel/3d/zone_smoother.h>
//#include <nel/3d/zone_tgt_smoother.h>
//#include <nel/3d/zone_corner_smoother.h>
#include <nel/ligo/zone_region.h>
#include <vector>
#include <set>
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;
bool s_ExtendCoords;
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" --extendcoords
// --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" --extendcoords
// --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" --extendcoords
// --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" --extendcoords
// --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" --extendcoords
// --land "R:\graphics\landscape\ligo\desert\r2_desert.land" --referenceext zonew "X:\wsl\check_r2_desert.py" "R:\reference\2008_july\data\r2_desert" "R:\pipeline\export\continents\r2_desert\zone_weld" --extendcoords
// --land "R:\graphics\landscape\ligo\jungle\r2_jungle.land" --referenceext zonew "X:\wsl\check_r2_jungle.py" "R:\reference\2008_july\data\r2_jungle" "R:\pipeline\export\continents\r2_jungle\zone_weld" --extendcoords
// --land "R:\graphics\landscape\ligo\primes_racines\r2_roots.land" --referenceext zonew "X:\wsl\check_r2_roots.py" "R:\reference\2008_july\data\r2_roots" "R:\pipeline\export\continents\r2_roots\zone_weld" --extendcoords
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;
if (!s_ExtendCoords)
{
++w;
++h;
}
}
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;
xc = (x - s_CellSize * s_ZoneMinX) / (s_CellSize * sizeX);
yc = 1.0f - ((y - s_CellSize * s_ZoneMinY) / (s_CellSize * sizeY));
if (s_ExtendCoords)
{
int w, h;
getBitmapSize(w, h);
xc -= .5f / (float)w;
yc -= .5f / (float)h;
xc = xc * (float)(w + 1) / (float)w;
yc = yc * (float)(h + 1) / (float)h;
}
}
bool processZone(std::vector<NLMISC::CVector> &output, const std::string &sourceFile, const std::string &referenceFile)
{
std::string zone = CFile::getFilenameWithoutExtension(referenceFile);
std::vector<CVector> sourceVertices;
std::vector<CVector> 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<bool> processedSourceIndices;
std::vector<bool> 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<std::string> referenceZones;
CPath::getPathContent(s_ReferenceDir, true, false, true, referenceZones);
int totalZones = 0;
std::vector<NLMISC::CVector> output;
for (std::vector<std::string>::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;
if (zone == "137_JK") // Bad zone
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<bool> processedOutput;
processedOutput.resize(output.size());
std::vector<NLMISC::CVector> 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<NLMISC::CVector> 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;
}
s_ExtendCoords = args.haveLongArg("extendcoords");
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)");
args.addArg("", "extendcoords", "flag", "Extend coordinates to edge of bitmap pixels");
if (!args.parse(argc, argv))
{
return EXIT_FAILURE;
}
if (!unbuildElevation(args))
{
args.displayHelp();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/* end of file */

@ -53,6 +53,8 @@ NLMISC::CBitmap *s_HeightMap;
float s_ZFactor2 = 1.0f;
NLMISC::CBitmap *s_HeightMap2;
bool s_ExtendCoords;
std::string s_InputZone; // UTF-8
std::string s_OutputZone; // UTF-8
@ -121,13 +123,18 @@ float getHeight(float x, float y)
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));
if (s_HeightMap != NULL)
{
float xc = (x - s_CellSize * s_ZoneMinX) / (s_CellSize * sizeX);
float yc = 1.0f - ((y - s_CellSize * s_ZoneMinY) / (s_CellSize * sizeY));
if (s_ExtendCoords)
{
uint32 w = s_HeightMap->getWidth(), h = s_HeightMap->getHeight();
xc -= .5f / (float)w;
yc -= .5f / (float)h;
xc = xc * (float)(w + 1) / (float)w;
yc = yc * (float)(h + 1) / (float)h;
}
color = s_HeightMap->getColor(xc, yc);
color *= 255;
deltaZ = color.A;
@ -139,6 +146,14 @@ float getHeight(float x, float y)
{
float xc = (x - s_CellSize * s_ZoneMinX) / (s_CellSize * sizeX);
float yc = 1.0f - ((y - s_CellSize * s_ZoneMinY) / (s_CellSize * sizeY));
if (s_ExtendCoords)
{
uint32 w = s_HeightMap2->getWidth(), h = s_HeightMap2->getHeight();
xc -= .5f / (float)w;
yc -= .5f / (float)h;
xc = xc * (float)(w + 1) / (float)w;
yc = yc * (float)(h + 1) / (float)h;
}
color = s_HeightMap2->getColor(xc, yc);
color *= 255;
deltaZ2 = color.A;
@ -186,6 +201,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 +321,9 @@ void applyZoneHeightmap()
std::vector<CBorderVertex> zoneBorderVertices;
zone.retrieve(zonePatches, zoneBorderVertices);
// Bkup the original patchs.
vector<CPatchInfo> 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 +377,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);
@ -279,6 +490,7 @@ int main(int argc, char **argv)
args.addArg("", "heightmap", "bitmap", "Heightmap");
args.addArg("", "zfactor2", "factor", "Factor for second heightmap");
args.addArg("", "heightmap2", "bitmap", "Second heightmap");
args.addArg("", "extendcoords", "flag", "Extend coordinates to edge of bitmap pixels");
// TODO: args.addArg("", "batch", "", "Process all zones in input (specify input as C:/folder/.zonenhw)");
if (!args.parse(argc, argv))
@ -382,6 +594,8 @@ int main(int argc, char **argv)
goto Fail;
}
s_ExtendCoords = args.haveLongArg("extendcoords");
if (args.haveLongArg("cellsize"))
{
if (!NLMISC::fromString(args.getLongArg("cellsize")[0], s_CellSize))

@ -23,7 +23,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
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)

@ -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 <<<")

@ -94,6 +94,7 @@ def igElevation(inputIgDir, outputIgDir):
cf.write("ZFactor1 = " + LigoExportZFactor1 + ";\n")
cf.write("HeightMapFile2 = \"" + DatabaseDirectory + "/" + LigoBaseSourceDirectory + "/" + LigoExportHeightmap2 + "\";\n")
cf.write("ZFactor2 = " + LigoExportZFactor2 + ";\n")
cf.write("ExtendCoords = " + str(LigoExportExtendCoords) + ";\n")
cf.write("\n")
cf.write("LandFile = \"" + DatabaseDirectory + "/" + LigoBaseSourceDirectory + "/" + LigoExportLand + "\";\n")
cf.write("\n")

@ -96,6 +96,7 @@ if LigoExportLand != "":
cf.write("ZFactor1 = " + LigoExportZFactor1 + ";\n")
cf.write("HeightMapFile2 = \"" + DatabaseDirectory + "/" + LigoBaseSourceDirectory + "/" + LigoExportHeightmap2 + "\";\n")
cf.write("ZFactor2 = " + LigoExportZFactor2 + ";\n")
cf.write("ExtendCoords = " + str(LigoExportExtendCoords) + ";\n")
cf.write("\n")
cf.write("ZoneLight = 0;\n")
cf.write("CellSize = 160;\n")

@ -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 == "":

@ -145,9 +145,28 @@ else:
files = findFiles(log, ExportBuildDirectory + "/" + ZoneWeldBuildDirectory, "", ".zonenhw")
for file in files:
sourceFile = ExportBuildDirectory + "/" + ZoneWeldBuildDirectory + "/" + file
destFile = ExportBuildDirectory + "/" + ZoneWeldBuildDirectory + "/" + os.path.basename(file)[0:-len(".zonenhw")] + ".zonew"
destFile = ExportBuildDirectory + "/" + ZoneWeldBuildDirectory + "/" + os.path.basename(file)[0:-len(".zonenhw")] + ".zone"
if needUpdateLogRemoveDest(log, sourceFile, destFile):
subprocess.call([ ZoneElevation, sourceFile, destFile, "--land", land, "--heightmap", heightMap1, "--zfactor", LigoExportZFactor1, "--heightmap2", heightMap2, "--zfactor2", LigoExportZFactor2 ])
command = [ ZoneElevation, sourceFile, destFile, "--land", land, "--heightmap", heightMap1, "--zfactor", LigoExportZFactor1, "--heightmap2", heightMap2, "--zfactor2", LigoExportZFactor2 ]
if LigoExportExtendCoords != 0:
command.append("--extendcoords")
callParallelProcess(command)
flushParallelProcesses()
printLog(log, "")
printLog(log, ">>> Re-weld zone with heightmap <<<")
if ZoneWelder == "":
toolLogFail(log, ZoneWelderTool, ToolSuffix)
elif ExecTimeout == "":
toolLogFail(log, ExecTimeoutTool, ToolSuffix)
else:
mkPath(log, ExportBuildDirectory + "/" + ZoneWeldBuildDirectory)
files = findFiles(log, ExportBuildDirectory + "/" + ZoneWeldBuildDirectory, "", ".zone")
for file in files:
sourceFile = ExportBuildDirectory + "/" + ZoneWeldBuildDirectory + "/" + file
destFile = ExportBuildDirectory + "/" + ZoneWeldBuildDirectory + "/" + os.path.basename(file)[0:-len(".zone")] + ".zonew"
if needUpdateLogRemoveDest(log, sourceFile, destFile):
subprocess.call([ ExecTimeout, str(ZoneBuildWeldTimeout), ZoneWelder, sourceFile, destFile ])
printLog(log, "")
log.close()

@ -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()

@ -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<CPolygon> 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<CPolygon> 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);
}
}
}

@ -176,6 +176,7 @@ struct SOptions : public SExportOptions
this->ZFactor = getFloat ("ZFactor1");
this->HeightMapFile2 = getStr ("HeightMapFile2");
this->ZFactor2 = getFloat ("ZFactor2");
this->ExtendCoords = getFloat ("ExtendCoords");
this->Light = (uint8)getInt ("ZoneLight");
this->CellSize = getFloat ("CellSize");
this->Threshold = getFloat ("Threshold");

@ -2171,13 +2171,19 @@ float CExport::getHeight (float x, float y)
sint32 SizeX = _ZoneMaxX - _ZoneMinX + 1;
sint32 SizeY = _ZoneMaxY - _ZoneMinY + 1;
clamp (x, _Options->CellSize*_ZoneMinX, _Options->CellSize*(_ZoneMaxX+1));
clamp (y, _Options->CellSize*_ZoneMinY, _Options->CellSize*(_ZoneMaxY+1));
if (_HeightMap != NULL)
{
color = _HeightMap->getColor ( (x-_Options->CellSize*_ZoneMinX)/(_Options->CellSize*SizeX),
1.0f - ((y-_Options->CellSize*_ZoneMinY)/(_Options->CellSize*SizeY)));
float xc = (x - _Options->CellSize * _ZoneMinX) / (_Options->CellSize * SizeX);
float yc = 1.0f - ((y - _Options->CellSize * _ZoneMinY) / (_Options->CellSize * SizeY));
if (_Options->ExtendCoords)
{
uint32 w = _HeightMap->getWidth(), h = _HeightMap->getHeight();
xc -= .5f / (float)w;
yc -= .5f / (float)h;
xc = xc * (float)(w + 1) / (float)w;
yc = yc * (float)(h + 1) / (float)h;
}
color = _HeightMap->getColor(xc, yc);
color *= 255;
deltaZ = color.A;
deltaZ = deltaZ - 127.0f; // Median intensity is 127
@ -2186,8 +2192,17 @@ float CExport::getHeight (float x, float y)
if (_HeightMap2 != NULL)
{
color = _HeightMap2->getColor ( (x-_Options->CellSize*_ZoneMinX)/(_Options->CellSize*SizeX),
1.0f - ((y-_Options->CellSize*_ZoneMinY)/(_Options->CellSize*SizeY)));
float xc = (x - _Options->CellSize * _ZoneMinX) / (_Options->CellSize * SizeX);
float yc = 1.0f - ((y - _Options->CellSize * _ZoneMinY) / (_Options->CellSize * SizeY));
if (_Options->ExtendCoords)
{
uint32 w = _HeightMap2->getWidth(), h = _HeightMap2->getHeight();
xc -= .5f / (float)w;
yc -= .5f / (float)h;
xc = xc * (float)(w + 1) / (float)w;
yc = yc * (float)(h + 1) / (float)h;
}
color = _HeightMap2->getColor(xc, yc);
color *= 255;
deltaZ2 = color.A;
deltaZ2 = deltaZ2 - 127.0f; // Median intensity is 127

@ -101,6 +101,7 @@ struct SExportOptions
float ZFactor;
std::string HeightMapFile2;
float ZFactor2;
bool ExtendCoords;
uint8 Light; // Roughly light the zone (0-none, 1-patch, 2-noise)

Loading…
Cancel
Save