forked from 0ad/0ad
Allow random map scripts to load heightmap image files, fixes #5018.
Move the atlas heightmap import code to MapIO.cpp and reuse it. Implements what33e3e6c2ab
and69b7f39bf1
wanted to be. This was SVN commit r21113.
This commit is contained in:
parent
c95901515e
commit
204b04f2d4
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017 Wildfire Games.
|
||||
/* Copyright (C) 2018 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -19,11 +19,14 @@
|
||||
|
||||
#include "MapGenerator.h"
|
||||
|
||||
#include "graphics/MapIO.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "lib/status.h"
|
||||
#include "lib/timer.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Profile.h"
|
||||
#include "ps/scripting/JSInterface_VFS.h"
|
||||
#include "scriptinterface/ScriptConversions.h"
|
||||
|
||||
// TODO: what's a good default? perhaps based on map size
|
||||
#define RMS_RUNTIME_SIZE 96 * 1024 * 1024
|
||||
@ -101,6 +104,7 @@ bool CMapGeneratorWorker::Run()
|
||||
// Functions for RMS
|
||||
JSI_VFS::RegisterScriptFunctions_Maps(*m_ScriptInterface);
|
||||
m_ScriptInterface->RegisterFunction<bool, std::wstring, CMapGeneratorWorker::LoadLibrary>("LoadLibrary");
|
||||
m_ScriptInterface->RegisterFunction<JS::Value, std::wstring, CMapGeneratorWorker::LoadHeightmap>("LoadHeightmapImage");
|
||||
m_ScriptInterface->RegisterFunction<void, JS::HandleValue, CMapGeneratorWorker::ExportMap>("ExportMap");
|
||||
m_ScriptInterface->RegisterFunction<void, int, CMapGeneratorWorker::SetProgress>("SetProgress");
|
||||
m_ScriptInterface->RegisterFunction<CParamNode, std::string, CMapGeneratorWorker::GetTemplate>("GetTemplate");
|
||||
@ -263,6 +267,27 @@ bool CMapGeneratorWorker::LoadScripts(const std::wstring& libraryName)
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Value CMapGeneratorWorker::LoadHeightmap(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& vfsPath)
|
||||
{
|
||||
OsPath realPath;
|
||||
if (g_VFS->GetRealPath(vfsPath, realPath) != INFO::OK)
|
||||
return JS::UndefinedValue();
|
||||
|
||||
std::vector<u16> heightmap;
|
||||
if (LoadHeightmapImage(realPath, heightmap) != INFO::OK)
|
||||
{
|
||||
LOGERROR("Could not load heightmap file '%s'", utf8_from_wstring(vfsPath).c_str());
|
||||
return JS::UndefinedValue();
|
||||
}
|
||||
|
||||
CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(pCxPrivate->pCBData);
|
||||
JSContext* cx = self->m_ScriptInterface->GetContext();
|
||||
JSAutoRequest rq(cx);
|
||||
JS::RootedValue returnValue(cx);
|
||||
ToJSVal_vector(cx, &returnValue, heightmap);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -123,6 +123,7 @@ private:
|
||||
// callbacks for script functions
|
||||
static bool LoadLibrary(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& name);
|
||||
static void ExportMap(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue data);
|
||||
static JS::Value LoadHeightmap(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& src);
|
||||
static void SetProgress(ScriptInterface::CxPrivate* pCxPrivate, int progress);
|
||||
static CParamNode GetTemplate(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName);
|
||||
static bool TemplateExists(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName);
|
||||
|
74
source/graphics/MapIO.cpp
Normal file
74
source/graphics/MapIO.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/* Copyright (C) 2018 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 0 A.D. 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "MapIO.h"
|
||||
|
||||
#include "graphics/Patch.h"
|
||||
#include "lib/file/file.h"
|
||||
#include "lib/os_path.h"
|
||||
#include "lib/status.h"
|
||||
#include "lib/tex/tex.h"
|
||||
#include "maths/MathUtil.h"
|
||||
|
||||
Status LoadHeightmapImage(const OsPath& filepath, std::vector<u16>& heightmap)
|
||||
{
|
||||
File file;
|
||||
RETURN_STATUS_IF_ERR(file.Open(OsString(filepath), O_RDONLY));
|
||||
|
||||
size_t fileSize = lseek(file.Descriptor(), 0, SEEK_END);
|
||||
lseek(file.Descriptor(), 0, SEEK_SET);
|
||||
|
||||
shared_ptr<u8> fileData = shared_ptr<u8>(new u8[fileSize]);
|
||||
|
||||
Status readvalue = read(file.Descriptor(), fileData.get(), fileSize);
|
||||
file.Close();
|
||||
RETURN_STATUS_IF_ERR(readvalue);
|
||||
|
||||
// Decode to a raw pixel format
|
||||
Tex tex;
|
||||
RETURN_STATUS_IF_ERR(tex.decode(fileData, fileSize));
|
||||
|
||||
// Convert to uncompressed BGRA with no mipmaps
|
||||
RETURN_STATUS_IF_ERR(tex.transform_to((tex.m_Flags | TEX_BGR | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)));
|
||||
|
||||
// Pick smallest side of texture; truncate if not divisible by PATCH_SIZE
|
||||
ssize_t tileSize = std::min(tex.m_Width, tex.m_Height);
|
||||
tileSize -= tileSize % PATCH_SIZE;
|
||||
|
||||
u8* mapdata = tex.get_data();
|
||||
ssize_t bytesPP = tex.m_Bpp / 8;
|
||||
ssize_t mapLineSkip = tex.m_Width * bytesPP;
|
||||
|
||||
// Copy image data into the heightmap
|
||||
heightmap.resize(SQR(tileSize + 1));
|
||||
for (ssize_t y = 0; y < tileSize + 1; ++y)
|
||||
for (ssize_t x = 0; x < tileSize + 1; ++x)
|
||||
{
|
||||
// Repeat the last pixel of the image for the last vertex of the heightmap
|
||||
int offset = std::min(y, tileSize - 1) * mapLineSkip + std::min(x, tileSize - 1) * bytesPP;
|
||||
|
||||
// Pick color channel with highest value
|
||||
u16 value = std::max({mapdata[offset], mapdata[offset + bytesPP], mapdata[offset + bytesPP * 2]});
|
||||
value = mapdata[offset];
|
||||
|
||||
heightmap[(tileSize - y) * (tileSize + 1) + x] = clamp(value * 256, 0, 65535);
|
||||
}
|
||||
|
||||
return INFO::OK;
|
||||
}
|
@ -18,6 +18,12 @@
|
||||
#ifndef INCLUDED_MAPIO
|
||||
#define INCLUDED_MAPIO
|
||||
|
||||
#include "lib/os_path.h"
|
||||
#include "lib/status.h"
|
||||
|
||||
// Opens the given texture file and stores it in a one-dimensional u16 vector.
|
||||
Status LoadHeightmapImage(const OsPath& filepath, std::vector<u16>& heightmap);
|
||||
|
||||
class CMapIO
|
||||
{
|
||||
public:
|
||||
|
@ -23,14 +23,15 @@
|
||||
|
||||
#include "graphics/GameView.h"
|
||||
#include "graphics/LOSTexture.h"
|
||||
#include "graphics/MapIO.h"
|
||||
#include "graphics/MapWriter.h"
|
||||
#include "graphics/Patch.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "graphics/TerrainTextureEntry.h"
|
||||
#include "graphics/TerrainTextureManager.h"
|
||||
#include "lib/bits.h"
|
||||
#include "lib/file/file.h"
|
||||
#include "lib/tex/tex.h"
|
||||
#include "lib/file/vfs/vfs_path.h"
|
||||
#include "lib/status.h"
|
||||
#include "maths/MathUtil.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/Filesystem.h"
|
||||
@ -167,80 +168,29 @@ MESSAGEHANDLER(LoadMap)
|
||||
|
||||
MESSAGEHANDLER(ImportHeightmap)
|
||||
{
|
||||
CStrW src = *msg->filename;
|
||||
|
||||
size_t fileSize;
|
||||
shared_ptr<u8> fileData;
|
||||
|
||||
// read in image file
|
||||
File file;
|
||||
if (file.Open(src, O_RDONLY) < 0)
|
||||
{
|
||||
LOGERROR("Failed to load heightmap.");
|
||||
return;
|
||||
}
|
||||
|
||||
fileSize = lseek(file.Descriptor(), 0, SEEK_END);
|
||||
lseek(file.Descriptor(), 0, SEEK_SET);
|
||||
|
||||
fileData = shared_ptr<u8>(new u8[fileSize]);
|
||||
|
||||
if (read(file.Descriptor(), fileData.get(), fileSize) < 0)
|
||||
{
|
||||
LOGERROR("Failed to read heightmap image.");
|
||||
file.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
file.Close();
|
||||
|
||||
// decode to a raw pixel format
|
||||
Tex tex;
|
||||
if (tex.decode(fileData, fileSize) < 0)
|
||||
std::vector<u16> heightmap_source;
|
||||
if (LoadHeightmapImage(*msg->filename, heightmap_source) != INFO::OK)
|
||||
{
|
||||
LOGERROR("Failed to decode heightmap.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert to uncompressed BGRA with no mipmaps
|
||||
if (tex.transform_to((tex.m_Flags | TEX_BGR | TEX_ALPHA) & ~(TEX_DXT | TEX_MIPMAPS)) < 0)
|
||||
{
|
||||
LOGERROR("Failed to transform heightmap.");
|
||||
return;
|
||||
}
|
||||
|
||||
// pick smallest side of texture; truncate if not divisible by PATCH_SIZE
|
||||
ssize_t terrainSize = std::min(tex.m_Width, tex.m_Height);
|
||||
terrainSize -= terrainSize % PATCH_SIZE;
|
||||
|
||||
// resize terrain to heightmap size
|
||||
// Notice that the number of tiles/pixels per side of the heightmap image is
|
||||
// one less than the number of vertices per side of the heightmap.
|
||||
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
|
||||
terrain->Resize(terrainSize / PATCH_SIZE);
|
||||
ENSURE(terrainSize + 1 == g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide());
|
||||
terrain->Resize((sqrt(heightmap_source.size()) - 1) / PATCH_SIZE);
|
||||
|
||||
// copy heightmap data into map
|
||||
u16* heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap();
|
||||
|
||||
u8* mapdata = tex.get_data();
|
||||
ssize_t bytesPP = tex.m_Bpp / 8;
|
||||
ssize_t mapLineSkip = tex.m_Width * bytesPP;
|
||||
|
||||
for (ssize_t y = 0; y < terrainSize + 1; ++y)
|
||||
{
|
||||
for (ssize_t x = 0; x < terrainSize + 1; ++x)
|
||||
{
|
||||
// repeat the last pixel of the image for the last vertex of the heightmap
|
||||
int offset = std::min(y, terrainSize - 1) * mapLineSkip + std::min(x, terrainSize - 1) * bytesPP;
|
||||
|
||||
// pick color channel with highest value
|
||||
u16 value = std::max(mapdata[offset+bytesPP*2], std::max(mapdata[offset], mapdata[offset+bytesPP]));
|
||||
heightmap[(terrainSize - y) * (terrainSize + 1) + x] = clamp(value * 256, 0, 65535);
|
||||
}
|
||||
}
|
||||
ENSURE(heightmap_source.size() == (std::size_t) SQR(g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide()));
|
||||
std::copy(heightmap_source.begin(), heightmap_source.end(), heightmap);
|
||||
|
||||
// update simulation
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||
if (cmpTerrain) cmpTerrain->ReloadTerrain();
|
||||
if (cmpTerrain)
|
||||
cmpTerrain->ReloadTerrain();
|
||||
|
||||
g_Game->GetView()->GetLOSTexture().MakeDirty();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user