Jump to content

Export height map from Unigine?


photo

Recommended Posts

Hi Tim,

Export was available in the past for the very simple ObjectTerrain. Right now there is no export heights available in the Editor (mostly because of the very high height density that is being calculated on GPU). It's not that easy to export such highly detailed data.

You can write your own simplified export that will fit your project's needs via C++ / C# API. You can also check C++ Samples project in SDK Browser -> Samples -> Demos -> C++ Samples -> Landscape Terrain -> Mesh - it shows how to convert heights to mesh (but you can also do a heights -> texture / image conversion on your side as well).

Thanks!

 

How to submit a good bug report
---
FTP server for test scenes and user uploads:

Link to comment
  • 2 months later...

HI, I need to export a mesh out of a landscape (the old one not landscapeTerrainObject) out of 2.13.0.1, how would this be possible ? I can't find the sample for 2.13.0.1 only for 2.14.+ ...

Edited by romain.janil
Link to comment

We have some old snippet from 2.10 (or earlier versions). Some adjustments for this code for sure would be required:

#include "AppSystemLogic.h"
#include <UnigineApp.h>
#include <UnigineConsole.h>
#include <UnigineFileSystem.h>
#include <UnigineGame.h>
#include <UnigineWorld.h>
#include <UnigineEditor.h>
#include <UnigineTilesetFile.h>

using namespace Unigine;
using namespace Math;

// System logic, it exists during the application life cycle.
// These methods are called right after corresponding system script's (UnigineScript) methods.

AppSystemLogic::AppSystemLogic()
{
}

AppSystemLogic::~AppSystemLogic()
{
}


void make_mesh_terrain(int argc, char **argv)
{
	int lod = 1;


	ObjectTerrainGlobalPtr terrain;
	if (argc == 1)
	{
		int num_terrain = 0;
		Vector<NodePtr> nodes;
		World::get()->getNodes(nodes);
		for (auto & it : nodes)
		{
			if (it->getType() == Node::OBJECT_TERRAIN_GLOBAL)
			{
				terrain = ObjectTerrainGlobal::cast(it);
				Log::message("\t%d terrain: '%s'\n", num_terrain++, terrain->getName());
			}
		}
	}
	else if (argc == 2)
	{
		String terrain_name= argv[1];
		terrain = ObjectTerrainGlobal::cast(Editor::get()->getNodeByName(terrain_name.get()));
	}
	else
	{
		return;
	}

	if (!terrain)
	{
		Log::error("cant find terrain!\n");
		return;
	}

	Log::message("current terrain %s\n", terrain->getName());

	int tileres = terrain->getHeightLods()->getLod(lod)->getTileset()->getTileResolution(); //128
	float tile_density = terrain->getHeightLods()->getLod(lod)->getTileset()->getTileDensity(); // 104.23116
	float tile_size = terrain->getHeightLods()->getLod(lod)->getTileset()->getTileSize(); // 13341.5888

	String path = terrain->getHeightLods()->getLod(lod)->getTileset()->getPath(); // "ladscapeBugulmaWinter/heights/lod1/"

	int count_tiles_x = 1;
	int count_tiles_y = 1;

	TilesetFilePtr tf = TilesetFile::create();
	if (tf->load((path + "data").get()))
	{

		int maxx = 0;
		int maxy = 0;
		int minx = 0;
		int miny = 0;

		for (int i = 0 ; i < tf->getNumTiles(); i++)
		{
			int x, y;
			tf->getTilePos(i, x, y);
			maxx = max(maxx, x);
			maxy = max(maxy, y);
			minx = min(minx, x);
			miny = min(miny, y);
		}

		count_tiles_x = maxx - minx + 1;
		count_tiles_y = maxy - miny + 1;
	}


	float step_x = tile_density;
	float step_y = step_x;

	float size_x = tile_size * count_tiles_x;
	float size_y = tile_size * count_tiles_y;

	Log::message("lasdscape %s: density %f, size %f x %f\n", terrain->getName(), step_x, size_x, size_y);

	ObjectMeshDynamicPtr mesh = ObjectMeshDynamic::create();
	mesh->setMaterial("mesh_base", "*");

	// allocate space in index and vertex buffers

	int count_x = size_x/step_x;
	int count_y = size_y/step_y;

	terrain->setForceIntersection(1);

	mesh->allocateVertex(count_x * count_y);
	mesh->allocateIndices(((count_x-1) * 2 * (count_y-1)) * 3);

	for (int i = 0 ; i < count_x; i++)
	{
		float x = i * step_x - (count_x * step_x * 0.5f);
		for (int j = 0; j < count_y; j++)
		{
			float y = j * step_y - (count_y * step_y * 0.5f);
			Vec3 point;
			vec3 normal;
			vec3 up;
			if (terrain->fetchTopologyData(x, y, point, normal, up, 1))
				mesh->addVertex(vec3(point));
			else
			{
				vec3 p1 = vec3(x, y, 10000);
				vec3 p2 = vec3(x, y, -10000);
				vec4 texcord;
				vec3 retpoint;
				int index, inst, surf;
				if (terrain->getIntersection(p1, p2, ~0, &retpoint, &normal, &texcord, &index, &inst, &surf))
				{
					mesh->addVertex(retpoint);
				}
				else
					mesh->addVertex(vec3(x, y, 0));
			}
		}
	}

	for (int i = 0; i < count_x-1; i++)
	{
		for (int j = 0 ; j < count_y-1; j++)
		{
			mesh->addIndex((i*count_x) + j); // 0
			mesh->addIndex((i+1) * count_x + j + 1); //4
			mesh->addIndex((i*count_x) + j + 1); // 1
			mesh->addIndex((i*count_x) + j); //0
			mesh->addIndex((i+1) * count_x + j); // 3
			mesh->addIndex((i+1) * count_x + j + 1); // 4
		}
	}

	// calculate tangent vectors
	mesh->updateTangents();
	// optimize vertex and index buffers, if necessary
	// mesh->updateIndices();

	// calculate a mesh bounding box
	mesh->updateBounds();

	String name = String::format("%s_%s.mesh", World::get()->getName(), terrain->getName());
	mesh->saveMesh(name.get());
	Log::message("mesh saved to %s (%d vertex)\n", name.get(), mesh->getNumVertex());
}

int AppSystemLogic::init()
{
	Console::get()->addCommand("create_mesh_terrain","", MakeCallback(&make_mesh_terrain));

	return 1;
}

If you have very dense elevation grid it may not be saved correctly.

How to submit a good bug report
---
FTP server for test scenes and user uploads:

Link to comment

doesn't compile in 2.13 , have really little time to investigate, would you be so kind to check what's wrong with code in 2.13 (i would have only this code in default project, just to see if I'm able to export a terrainGlobal as mesh)

Link to comment

Hi! 
here is snippet for 2.13 version 

// somewhere in AppSystemLogic.cpp
void create_mesh_terrain(int argc, char** argv)
{
	int lod = 1;

	ObjectTerrainGlobalPtr terrain;
	if (argc == 1)
	{
		int num_terrain = 0;
		Vector<NodePtr> nodes;
		World::getNodes(nodes);
		for (auto& it : nodes)
		{
			if (it->getType() == Node::OBJECT_TERRAIN_GLOBAL)
			{
				terrain = static_ptr_cast<ObjectTerrainGlobal>(it);
				Log::message("\t%d terrain: '%s'\n", num_terrain++, terrain->getName());
			}
		}
	}
	else if (argc == 2)
	{
		String terrain_name = argv[1];
		terrain = static_ptr_cast<ObjectTerrainGlobal>(World::getNodeByName(terrain_name.get()));
	}
	else
	{
		return;
	}

	if (!terrain)
	{
		Log::error("cant find terrain!\n");
		return;
	}

	Log::message("current terrain %s\n", terrain->getName());

	int tileres = terrain->getHeightLods()->getLod(lod)->getTileset()->getTileResolution(); //128
	float tile_density = terrain->getHeightLods()->getLod(lod)->getTileset()->getTileDensity(); // 104.23116
	float tile_size = terrain->getHeightLods()->getLod(lod)->getTileset()->getTileSize(); // 13341.5888

	String path = terrain->getHeightLods()->getLod(lod)->getTileset()->getPath(); 

	int count_tiles_x = 1;
	int count_tiles_y = 1;

	TilesetFilePtr tf = TilesetFile::create();
	if (tf->load((path + "data").get()))
	{

		int maxx = 0;
		int maxy = 0;
		int minx = 0;
		int miny = 0;

		for (int i = 0; i < tf->getNumTiles(); i++)
		{
			int x, y;
			tf->getTilePos(i, x, y);
			maxx = max(maxx, x);
			maxy = max(maxy, y);
			minx = min(minx, x);
			miny = min(miny, y);
		}

		count_tiles_x = maxx - minx + 1;
		count_tiles_y = maxy - miny + 1;
	}


	float step_x = tile_density;
	float step_y = step_x;

	float size_x = tile_size * count_tiles_x;
	float size_y = tile_size * count_tiles_y;

	Log::message("lasdscape %s: density %f, size %f x %f\n", terrain->getName(), step_x, size_x, size_y);

	ObjectMeshDynamicPtr mesh = ObjectMeshDynamic::create();
	mesh->setMaterial("mesh_base", "*");

	// allocate space in index and vertex buffers

	int count_x = size_x / step_x;
	int count_y = size_y / step_y;

	terrain->setForceIntersection(1);

	mesh->allocateVertex(count_x * count_y);
	mesh->allocateIndices(((count_x - 1) * 2 * (count_y - 1)) * 3);

	for (int i = 0; i < count_x; i++)
	{
		float x = i * step_x - (count_x * step_x * 0.5f);
		for (int j = 0; j < count_y; j++)
		{
			float y = j * step_y - (count_y * step_y * 0.5f);
			Vec3 point;
			vec3 normal;
			vec3 up;
			if (terrain->fetchTopologyData(x, y, point, normal, up, 1))
				mesh->addVertex(vec3(point));
			else
			{
				Vec3 p1 = Vec3(x, y, 10000);
				Vec3 p2 = Vec3(x, y, -10000);
				vec4 texcord;
				Vec3 retpoint;
				int index, inst, surf;
				if (terrain->getIntersection(p1, p2, ~0, &retpoint, &normal, &texcord, &index, &inst, &surf))
				{
					mesh->addVertex(vec3(retpoint));
				}
				else
					mesh->addVertex(vec3(x, y, 0));
			}
		}
	}

	for (int i = 0; i < count_x - 1; i++)
	{
		for (int j = 0; j < count_y - 1; j++)
		{
			mesh->addIndex((i * count_x) + j); // 0
			mesh->addIndex((i + 1) * count_x + j + 1); //4
			mesh->addIndex((i * count_x) + j + 1); // 1
			mesh->addIndex((i * count_x) + j); //0
			mesh->addIndex((i + 1) * count_x + j); // 3
			mesh->addIndex((i + 1) * count_x + j + 1); // 4
		}
	}

	// calculate tangent vectors
	mesh->updateTangents();
	// optimize vertex and index buffers, if necessary
	// mesh->updateIndices();

	// calculate a mesh bounding box
	mesh->updateBounds();

	String name = String::format("%s_%s.mesh", World::getPath(), terrain->getName());
	mesh->saveMesh(name.get());
	Log::message("mesh saved to %s (%d vertex)\n", name.get(), mesh->getNumVertex());
}

// in AppSystemLogic::init():
Console::addCommand("create_mesh_terrain", "\ncreate_mesh_terrain [nameofterrain] - export terrain to mesh file. \nif 'nameofterrain' is empty - the first one is chosen\n", MakeCallback(&create_mesh_terrain));

 

  • Like 3
Link to comment

If you have very big terrain you would need to export it as a grid of meshes. Provided snippet is just a proof of concept that you need to tune for your needs.

Regular mesh precision is float, so there is no really point to store anything bigger than 10x10km inside it.

How to submit a good bug report
---
FTP server for test scenes and user uploads:

Link to comment
×
×
  • Create New...