https://github.com/xbpeng/DeepTerrainRL
Raw File
Tip revision: ed82e2ebe5f14fa875cc3d0a2180c64980408e8f authored by Glen on 19 October 2016, 17:49:36 UTC
Update README.md
Tip revision: ed82e2e
TerrainGen2D.cpp
#include "TerrainGen2D.h"
#include <algorithm>

const float cTerrainGen2D::gVertSpacing = 0.1f;
const std::string cTerrainGen2D::gTypeKey = "Type";
const std::string cTerrainGen2D::gParamsKey = "Params";

const cTerrainGen2D::tParamDef cTerrainGen2D::gParamDefs[] =
{
	{"GapSpacingMin", 4},
	{"GapSpacingMax", 7},
	{"GapWMin", 0.5},
	{"GapWMax", 2},
	{"GapHMin", -2},
	{"GapHMax", -2},

	{"WallSpacingMin", 6},
	{"WallSpacingMax", 8},
	{"WallWMin", 0.2},
	{"WallWMax", 0.2},
	{"WallHMin", 0.25},
	{"WallHMax", 0.5},

	{"StepSpacingMin", 5},
	{"StepSpacingMax", 7},
	{"StepH0Min", 0.1},
	{"StepH0Max", 0.4},
	{"StepH1Min", -0.4},
	{"StepH1Max", -0.1},

	{"BumpHMin", 0},
	{"BumpHMax", 0.03},

	{"NarrowGapSpacingMin", 3},
	{"NarrowGapSpacingMax", 6},
	{"NarrowGapDistMin", 0.1},
	{"NarrowGapDistMax", 0.4},
	{"NarrowGapWMin", 0.15},
	{"NarrowGapWMax", 0.5},
	{"NarrowGapDepthMin", -2},
	{"NarrowGapDepthMax", -2},
	{"NarrowGapCountMin", 1},
	{"NarrowGapCountMax", 4},

	{ "CliffSpacingMin", 5 },
	{ "CliffSpacingMax", 7 },
	{ "CliffH0Min", 0.1 },
	{ "CliffH0Max", 0.4 },
	{ "CliffH1Min", -0.4 },
	{ "CliffH1Max", -0.1 },
	{ "CliffMiniCountMax", 0},

	{"SlopeDeltaRange", 0.25},
	{ "SlopeDeltaMin", -0.35 },
	{"SlopeDeltaMax", 0.35}
};

cTerrainGen2D::tParams cTerrainGen2D::GetDefaultParams()
{
	tParams params;
	assert(sizeof(gParamDefs) / sizeof(gParamDefs[0]) == eParamsMax);
	for (int i = 0; i < eParamsMax; ++i)
	{
		params[i] = gParamDefs[i].mDefaultVal;
	}
	return params;
}

void cTerrainGen2D::LoadParams(const Json::Value& root, Eigen::VectorXd& out_params)
{
	out_params = cTerrainGen2D::GetDefaultParams();
	for (int i = 0; i < eParamsMax; ++i)
	{
		const std::string& name = gParamDefs[i].mName;
		if (!root[name].isNull())
		{
			double val = root[name].asDouble();
			out_params[i] = val;
		}
	}
}

void cTerrainGen2D::ParseType(const std::string& str, eType& out_type)
{
	if (str == "flat"
		|| str == "")
	{
		out_type = eTypeFlat;
	}
	else if (str == "gaps")
	{
		out_type = eTypeGaps;
	}
	else if (str == "steps")
	{
		out_type = eTypeSteps;
	}
	else if (str == "walls")
	{
		out_type = eTypeWalls;
	}
	else if (str == "bumps")
	{
		out_type = eTypeBumps;
	}
	else if (str == "mixed")
	{
		out_type = eTypeMixed;
	}
	else if (str == "narrow_gaps")
	{
		out_type = eTypeNarrowGaps;
	}
	else if (str == "slopes")
	{
		out_type = eTypeSlopes;
	}
	else if (str == "slopes_gaps")
	{
		out_type = eTypeSlopesGaps;
	}
	else if (str == "slopes_steps")
	{
		out_type = eTypeSlopesSteps;
	}
	else if (str == "slopes_walls")
	{
		out_type = eTypeSlopesWalls;
	}
	else if (str == "slopes_mixed")
	{
		out_type = eTypeSlopesMixed;
	}
	else if (str == "slopes_narrow_gaps")
	{
		out_type = eTypeSlopesNarrowGaps;
	}
	else if (str == "cliffs")
	{
		out_type = eTypeCliffs;
	}
	else
	{
		assert(false); // unsupported terrain type
	}
}

cTerrainGen2D::tTerrainFunc cTerrainGen2D::GetTerrainFunc(eType terrain_type)
{
	switch(terrain_type)
	{
	case eTypeGaps:
		return BuildGaps;
	case eTypeSteps:
		return BuildSteps;
	case eTypeWalls:
		return BuildWalls;
	case eTypeBumps:
		return BuildBumps;
	case eTypeMixed:
		return BuildMixed;
	case eTypeNarrowGaps:
		return BuildNarrowGaps;
	case eTypeSlopes:
		return BuildSlopes;
	case eTypeSlopesGaps:
		return BuildSlopesGaps;
	case eTypeSlopesSteps:
		return BuildSlopesSteps;
	case eTypeSlopesWalls:
		return BuildSlopesWalls;
	case eTypeSlopesMixed:
		return BuildSlopesMixed;
	case eTypeSlopesNarrowGaps:
		return BuildSlopesNarrowGaps;
	case eTypeCliffs:
		return BuildCliffs;
	default:
		return BuildFlat;
	}
}



double cTerrainGen2D::BuildFlat(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	return AddFlat(width, out_data);
}

double cTerrainGen2D::BuildGaps(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	double spacing_min = params[eParamsGapSpacingMin];
	double spacing_max = params[eParamsGapSpacingMax];
	double gap_w_min = params[eParamsGapWidthMin];
	double gap_w_max = params[eParamsGapWidthMax];
	double gap_d_min = params[eParamsGapDepthMin];
	double gap_d_max = params[eParamsGapDepthMax];

	double total_w = 0;
	while (total_w < width)
	{
		double spacing = rand.RandDouble(spacing_min, spacing_max);
		double w = rand.RandDouble(gap_w_min, gap_w_max);
		double d = rand.RandDouble(gap_d_min, gap_d_max);

		double curr_w = AddBox(spacing, w, d, out_data);
		total_w += curr_w;
	}

	return total_w;
}

double cTerrainGen2D::BuildSteps(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	double spacing_min = params[eParamsStepSpacingMin];
	double spacing_max = params[eParamsStepSpacingMax];
	double step_h0_min = params[eParamsStepHeight0Min];
	double step_h0_max = params[eParamsStepHeight0Max];
	double step_h1_min = params[eParamsStepHeight1Min];
	double step_h1_max = params[eParamsStepHeight1Max];

	double total_w = 0;
	while (total_w < width)
	{
		double min_h = 0;
		double max_h = 0;

		bool valid_h0 = (step_h0_min != 0 || step_h0_max != 0);
		bool valid_h1 = (step_h1_min != 0 || step_h1_max != 0);
		if (valid_h0 && valid_h1)
		{
			bool heads = rand.FlipCoin();
			min_h = (heads) ? step_h0_min : step_h1_min;
			max_h = (heads) ? step_h0_max : step_h1_max;
		}
		else if (valid_h0)
		{
			min_h = step_h0_min;
			max_h = step_h0_max;
		}
		else
		{
			min_h = step_h1_min;
			max_h = step_h1_max;
		}

		double w = rand.RandDouble(spacing_min, spacing_max);
		double h = rand.RandDouble(min_h, max_h);

		double curr_w = AddStep(w, h, out_data);
		total_w += curr_w;
	}

	return total_w;
}

double cTerrainGen2D::BuildWalls(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	double spacing_min = params[eParamsWallSpacingMin];
	double spacing_max = params[eParamsWallSpacingMax];
	double wall_w_min = params[eParamsWallWidthMin];
	double wall_w_max = params[eParamsWallWidthMax];
	double wall_g_min = params[eParamsWallHeightMin];
	double wall_g_max = params[eParamsWallHeightMax];

	double total_w = 0;
	while (total_w < width)
	{
		double spacing = rand.RandDouble(spacing_min, spacing_max);
		double w = rand.RandDouble(wall_w_min, wall_w_max);
		double h = rand.RandDouble(wall_g_min, wall_g_max);

		double curr_w = AddBox(spacing, w, h, out_data);
		total_w += curr_w;
	}

	return total_w;
}

double cTerrainGen2D::BuildBumps(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	double bump_h_min = params[eParamsBumpHeightMin];
	double bump_h_max = params[eParamsBumpHeightMax];

	int beg_idx = static_cast<int>(out_data.size());
	double total_w = BuildFlat(width, params, rand, out_data);
	int end_idx = static_cast<int>(out_data.size());

	OverlayBumps(bump_h_min, bump_h_max, beg_idx, end_idx, rand, out_data);

	return total_w;
}

double cTerrainGen2D::BuildMixed(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	double total_w = 0;
	const int num_types = 3;
	const double dummy_w = gVertSpacing;

	while (total_w < width)
	{
		double curr_w = 0;
		int rand_type = rand.RandInt(0, num_types);

		if (rand_type == 0)
		{
			curr_w = BuildGaps(dummy_w, params, rand, out_data);
		}
		else if (rand_type == 1)
		{
			curr_w = BuildSteps(dummy_w, params, rand, out_data);
		}
		else if (rand_type == 2)
		{
			curr_w = BuildWalls(dummy_w, params, rand, out_data);
		}

		total_w += curr_w;
	}

	return total_w;
}

double cTerrainGen2D::BuildNarrowGaps(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	double spacing_min = params[eParamsNarrowGapSpacingMin];
	double spacing_max = params[eParamsNarrowGapSpacingMax];
	double dist_min = params[eParamsNarrowGapDistMin];
	double dist_max = params[eParamsNarrowGapDistMax];
	double gap_w_min = params[eParamsNarrowGapWidthMin];
	double gap_w_max = params[eParamsNarrowGapWidthMax];
	double gap_d_min = params[eParamsNarrowGapDepthMin];
	double gap_d_max = params[eParamsNarrowGapDepthMax];
	int count_min = std::max(1, static_cast<int>(params[eParamsNarrowGapCountMin]));
	int count_max = std::max(1, static_cast<int>(params[eParamsNarrowGapCountMax]));

	double total_w = 0;
	while (total_w < width)
	{
		double spacing = rand.RandDouble(spacing_min, spacing_max);
		int count = rand.RandInt(count_min, count_max + 1);
		for (int i = 0; i < count; ++i)
		{
			double w = rand.RandDouble(gap_w_min, gap_w_max);
			double d = rand.RandDouble(gap_d_min, gap_d_max);

			double curr_w = AddBox(spacing, w, d, out_data);
			total_w += curr_w;

			spacing = rand.RandDouble(dist_min, dist_max);
		}
	}
	
	return total_w;
}

double cTerrainGen2D::BuildSlopes(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	const double delta_range = std::abs(params[eParamsSlopeDeltaRange]);
	const double delta_min = params[eParamsSlopeDeltaMin];
	const double delta_max = params[eParamsSlopeDeltaMax];

	int beg_idx = static_cast<int>(out_data.size());
	double total_w = BuildFlat(width, params, rand, out_data);
	int end_idx = static_cast<int>(out_data.size());

	OverlaySlopes(delta_range, delta_min, delta_max, 0, beg_idx, end_idx, rand, out_data);

	return total_w;
}

double cTerrainGen2D::BuildSlopesGaps(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	const double delta_range = std::abs(params[eParamsSlopeDeltaRange]);
	const double delta_min = params[eParamsSlopeDeltaMin];
	const double delta_max = params[eParamsSlopeDeltaMax];

	int beg_idx = static_cast<int>(out_data.size());
	double total_w = BuildGaps(width, params, rand, out_data);
	int end_idx = static_cast<int>(out_data.size());

	OverlaySlopes(delta_range, delta_min, delta_max, 0, beg_idx, end_idx, rand, out_data);

	return total_w;
}

double cTerrainGen2D::BuildSlopesSteps(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	const double delta_range = std::abs(params[eParamsSlopeDeltaRange]);
	const double delta_min = params[eParamsSlopeDeltaMin];
	const double delta_max = params[eParamsSlopeDeltaMax];

	int beg_idx = static_cast<int>(out_data.size());
	double total_w = BuildSteps(width, params, rand, out_data);
	int end_idx = static_cast<int>(out_data.size());

	OverlaySlopes(delta_range, delta_min, delta_max, 0, beg_idx, end_idx, rand, out_data);

	return total_w;
}

double cTerrainGen2D::BuildSlopesWalls(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	const double delta_range = std::abs(params[eParamsSlopeDeltaRange]);
	const double delta_min = params[eParamsSlopeDeltaMin];
	const double delta_max = params[eParamsSlopeDeltaMax];

	int beg_idx = static_cast<int>(out_data.size());
	double total_w = BuildWalls(width, params, rand, out_data);
	int end_idx = static_cast<int>(out_data.size());

	OverlaySlopes(delta_range, delta_min, delta_max, 0, beg_idx, end_idx, rand, out_data);

	return total_w;
}

double cTerrainGen2D::BuildSlopesMixed(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	const double delta_range = std::abs(params[eParamsSlopeDeltaRange]);
	const double delta_min = params[eParamsSlopeDeltaMin];
	const double delta_max = params[eParamsSlopeDeltaMax];

	int beg_idx = static_cast<int>(out_data.size());
	double total_w = BuildMixed(width, params, rand, out_data);
	int end_idx = static_cast<int>(out_data.size());

	OverlaySlopes(delta_range, delta_min, delta_max, 0, beg_idx, end_idx, rand, out_data);

	return total_w;
}

double cTerrainGen2D::BuildSlopesNarrowGaps(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	const double delta_range = std::abs(params[eParamsSlopeDeltaRange]);
	const double delta_min = params[eParamsSlopeDeltaMin];
	const double delta_max = params[eParamsSlopeDeltaMax];

	int beg_idx = static_cast<int>(out_data.size());
	double total_w = BuildNarrowGaps(width, params, rand, out_data);
	int end_idx = static_cast<int>(out_data.size());

	OverlaySlopes(delta_range, delta_min, delta_max, 0, beg_idx, end_idx, rand, out_data);

	return total_w;

}

double cTerrainGen2D::BuildCliffs(double width, const tParams& params, cRand& rand, std::vector<float>& out_data)
{
	double spacing_min = params[eParamsCliffSpacingMin];
	double spacing_max = params[eParamsCliffSpacingMax];
	double cliff_h0_min = params[eParamsCliffHeight0Min];
	double cliff_h0_max = params[eParamsCliffHeight0Max];
	double cliff_h1_min = params[eParamsCliffHeight1Min];
	double cliff_h1_max = params[eParamsCliffHeight1Max];
	int mini_count_max = static_cast<int>(params[eParamsCliffMiniCountMax]);

	double bump_h_min = params[eParamsBumpHeightMin];
	double bump_h_max = params[eParamsBumpHeightMax];

	const double delta_range = std::abs(params[eParamsSlopeDeltaRange]);
	const double delta_min = params[eParamsSlopeDeltaMin];
	const double delta_max = params[eParamsSlopeDeltaMax];

	int beg_idx = static_cast<int>(out_data.size());

	double total_w = 0;
	while (total_w < width)
	{
		double min_h = 0;
		double max_h = 0;

		bool valid_h0 = (cliff_h0_min != 0 || cliff_h0_max != 0);
		bool valid_h1 = (cliff_h1_min != 0 || cliff_h1_max != 0);
		if (valid_h0 && valid_h1)
		{
			bool heads = rand.FlipCoin();
			min_h = (heads) ? cliff_h0_min : cliff_h1_min;
			max_h = (heads) ? cliff_h0_max : cliff_h1_max;
		}
		else if (valid_h0)
		{
			min_h = cliff_h0_min;
			max_h = cliff_h0_max;
		}
		else
		{
			min_h = cliff_h1_min;
			max_h = cliff_h1_max;
		}

		double w = rand.RandDouble(spacing_min, spacing_max);
		double h = rand.RandDouble(min_h, max_h);

		double curr_w = 0;
		double curr_delta_h = 0;
		int num_mini = rand.RandInt(0, mini_count_max + 1);
		for (int i = 0; i < num_mini + 1; ++i)
		{
			const double mini_w = (i == 0) ? w : 0.1;
			double mini_h = rand.RandDouble(curr_delta_h, h);
			mini_h = (i == num_mini) ? h : mini_h;

			double dh = mini_h - curr_delta_h;
			curr_w += AddStep(mini_w, dh, out_data);

			curr_delta_h = mini_h;
		}

		total_w += curr_w;
	}

	int end_idx = static_cast<int>(out_data.size());
	OverlaySlopes(delta_range, delta_min, delta_max, 0, beg_idx, end_idx, rand, out_data);
	OverlayBumps(bump_h_min, bump_h_max, beg_idx, end_idx, rand, out_data);

	return total_w;
}

int cTerrainGen2D::CalcNumVerts(double w)
{
	int num_verts = static_cast<int>(std::ceil(w / gVertSpacing)) + 1;
	return num_verts;
}

double cTerrainGen2D::AddFlat(double width, std::vector<float>& out_data)
{
	int num_verts = CalcNumVerts(width);
	int buffer_size = static_cast<int>(out_data.size());
	int buffer_size0 = buffer_size;
	bool start_empty = buffer_size0 == 0;

	float base_h = 0;
	if (!start_empty)
	{
		--num_verts;
		base_h = out_data[buffer_size0 - 1];
	}

	for (int i = 0; i < num_verts; ++i)
	{
		out_data.push_back(base_h);
	}

	buffer_size = static_cast<int>(out_data.size());
	int verts_added = buffer_size - buffer_size0;
	if (start_empty)
	{
		--verts_added;
	}
	double width_added = verts_added * gVertSpacing;

	return width_added;
}

double cTerrainGen2D::AddBox(double spacing, double width, double depth, std::vector<float>& out_data)
{
	int num_verts = CalcNumVerts(spacing);
	int buffer_size = static_cast<int>(out_data.size());
	int buffer_size0 = buffer_size;
	bool start_empty = buffer_size0 == 0;

	float base_h = 0;
	if (!start_empty)
	{
		--num_verts;
		base_h = out_data[buffer_size0 - 1];
	}

	out_data.reserve(buffer_size + num_verts);
	for (int i = 0; i < num_verts; ++i)
	{
		out_data.push_back(base_h);
	}

	num_verts = CalcNumVerts(width) - 1;
	buffer_size = static_cast<int>(out_data.size());

	out_data.reserve(buffer_size + num_verts);
	float gap_h = static_cast<float>(base_h + depth);
	for (int i = 0; i < num_verts; ++i)
	{
		out_data.push_back(gap_h);
	}
	out_data.push_back(base_h);

	buffer_size = static_cast<int>(out_data.size());
	int verts_added = buffer_size - buffer_size0;
	if (start_empty)
	{
		--verts_added;
	}
	double width_added = verts_added * gVertSpacing;

	return width_added;
}

double cTerrainGen2D::AddStep(double width, double height, std::vector<float>& out_data)
{
	int num_verts = CalcNumVerts(width);
	int buffer_size = static_cast<int>(out_data.size());
	int buffer_size0 = buffer_size;
	bool start_empty = buffer_size0 == 0;

	float base_h = 0;
	if (!start_empty)
	{
		--num_verts;
		base_h = out_data[buffer_size0 - 1];
	}

	out_data.reserve(buffer_size + num_verts + 1);
	for (int i = 0; i < num_verts; ++i)
	{
		out_data.push_back(base_h);
	}
	out_data.push_back(static_cast<float>(base_h + height));

	buffer_size = static_cast<int>(out_data.size());
	int verts_added = buffer_size - buffer_size0;
	if (start_empty)
	{
		--verts_added;
	}
	double width_added = verts_added * gVertSpacing;

	return width_added;
}

double cTerrainGen2D::AddSlope(double width, double slope, std::vector<float>& out_data)
{
	int num_verts = CalcNumVerts(width) - 1;
	int buffer_size = static_cast<int>(out_data.size());
	int buffer_size0 = buffer_size;
	bool start_empty = buffer_size0 == 0;

	float base_h = 0;
	if (start_empty)
	{
		out_data.push_back(base_h);
	}
	else
	{
		base_h = out_data[buffer_size0 - 1];
	}

	out_data.reserve(buffer_size + num_verts);
	for (int i = 0; i < num_verts; ++i)
	{
		double d = (i + 1) * (width / num_verts);
		out_data.push_back(static_cast<float>(base_h + slope * d));
	}

	buffer_size = static_cast<int>(out_data.size());
	int verts_added = buffer_size - buffer_size0;
	if (start_empty)
	{
		--verts_added;
	}
	double width_added = verts_added * gVertSpacing;

	return width_added;
}

void cTerrainGen2D::OverlaySlopes(double delta_range, double delta_min, double delta_max, double init_slope, int beg_idx, int end_idx,
									cRand& rand, std::vector<float>& out_data)
{
	double curr_slope = init_slope;
	double curr_delta_h = 0;
	assert(beg_idx < out_data.size());
	assert(end_idx <= out_data.size());
	assert(delta_min < delta_max);

	double delta_mean = 0.5 * (delta_min + delta_max);
	double delta_diff = 0.5 * (delta_max - delta_min);

	for (int i = beg_idx; i < end_idx; ++i)
	{
		const double w = 0.1;
		double delta = rand.RandDouble(0, delta_range);

		double sign_rand = rand.RandDouble(-1, 1);
		double sign_threshold = (curr_slope - delta_mean) / delta_diff;
		bool neg = sign_rand < sign_threshold;
		delta = (neg) ? -delta : delta;

		curr_slope += delta;
		curr_delta_h += curr_slope * gVertSpacing;

		out_data[i] += static_cast<float>(curr_delta_h);
	}
}

void cTerrainGen2D::OverlayBumps(double min_delta_h, double max_delta_h, int beg_idx, int end_idx,
									cRand& rand, std::vector<float>& out_data)
{
	double curr_slope = 0;
	double curr_delta_h = 0;
	assert(beg_idx < out_data.size());
	assert(end_idx <= out_data.size());

	for (int i = beg_idx; i < end_idx - 1; ++i)
	{
		double delta = rand.RandSign() * rand.RandDouble(min_delta_h, max_delta_h);
		out_data[i] += static_cast<float>(delta);
	}
}
back to top