TerrainGenerator C++ using diamond-square-algorithm

Hallo,

Ich bin momentan dabei, für ein Uni Projekt den Diamond-Square-Algorithmus zu implementieren. Es ist schon ziemlich viel vorgegeben gewesen. es geht darum, dass ich einen eindimensionalen vector erzeugen soll, der zu einer bestimmten resolution, die in der build command line angegeben wird, das height_field ausgeben soll. height_min ist 0 und height_max ist 1

mein code wie er momentan ist liefert mir jedoch beim kompilieren ständig "vector subscript out of bounds". woran kann das liegen. da ich relativ neu zu c++ bin ist mein code sicher fehlerhaft.

bitte um hilfe :rolleyes:

LG

Spacemoose

Code:
#include "DiamondSquare.h"
#include <stdlib.h>
#include <time.h>
#include <vector>
using namespace std;
#include <iostream>
unsigned int i;


DiamondSquare::DiamondSquare(int resolution, float height_min, float height_max)
	: m_resolution(resolution), m_height_min(height_min), m_height_max(height_max)
{
	float default_height = m_height_min;
	m_heightfield.resize(resolution*resolution, default_height);
}

DiamondSquare::~DiamondSquare(void)
{
}

float DiamondSquare::GetHeight(int x, int y)
{ 
	return m_heightfield[y*m_resolution+x];
}

uint16_t DiamondSquare::GetHeightAsUint(int x, int y)
{
	float height = GetHeight(x,y);

	//normalize height to [0;1]
	float height_normalized = (height - m_height_min) / (m_height_max - m_height_min);

	//convert to integer and return height
	return (uint16_t)(height_normalized * (float)UINT16_MAX);
}


// random number between -1 and 1
float rnd() {
	float r = 2.0f * ((float)rand() / (float)RAND_MAX) - 1.0f;
	return r;
}

void DiamondSquare::SetHeight(int x, int y, float value) 
{
	m_heightfield[y*m_resolution+x] = value;
}



void DiamondSquare::GenerateHeightfield() 
{
	srand((unsigned)time(0));
	
	double roughness = 1.0;
	int size = m_resolution*m_resolution;
	int initialStepsize = m_resolution;
	int stepsize=0;
	float x = 0;
	float y = 0;

	//initialise with random numbers

	for(float i = 1; i<size; i++){
		m_heightfield.at(i)=0;
	}

	//diamond-square-algorithm

	//TODO


	
}

void DiamondSquare::SquareStep(float x, float y, int size, double value){
	int halfstep = size / 2;
	//square

	// a     b 
	//
	//    x
	//
	// c     d

	uint16_t a = GetHeight(x - halfstep, y - halfstep);
	uint16_t b = GetHeight(x + halfstep, y - halfstep);
	uint16_t c = GetHeight(x - halfstep, y + halfstep);
	uint16_t d = GetHeight(x + halfstep, y + halfstep);
	SetHeight(x, y, ((a + b + c + d) / 4.0) + value);
}

void DiamondSquare::DiamondStep(float x, float y, int size, double value){
	int halfstep = size / 2;
	//diamond

	//   c
	//
	//a  x  b
	//
	//   d

	uint16_t a = GetHeight(x - halfstep, y);
	uint16_t b = GetHeight(x + halfstep, y);
	uint16_t c = GetHeight(x, y - halfstep);
	uint16_t d = GetHeight(x, y + halfstep);

	SetHeight(x, y, ((a + b + c + d) / 4.0) + value);
}
 
Zuletzt bearbeitet:
Könntest du bitte noch den Header posten bzw. alles was nötig ist, damit man das Problem nachstellen kann?

mfg benediktibk
 
hier der header:

Code:
#pragma once
#include <cstdint>
#include <vector>

class DiamondSquare
{
public:
	DiamondSquare(int resolution, float height_min, float height_max);
	~DiamondSquare(void);

	void GenerateHeightfield();
	void DiamondStep(float x, float y, int size, double value);
	void SquareStep(float x, float y, int size, double value);

	float GetHeight(int x, int y);
	uint16_t GetHeightAsUint(int x, int y);	

private:
	void SetHeight(int x, int y, float value);

	std::vector<float> m_heightfield;
	int                m_resolution;
	float              m_height_min;
	float              m_height_max;
};

und noch die main:

Code:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdint>
#include "DiamondSquare.h"

bool SaveBlock(const char * Path, const uint16_t * Block, size_t Count) {
	if(!Block) return false;
	FILE * filePointer = NULL;
	errno_t error = fopen_s(&filePointer, Path, "wb");
	if(error) return false;
	fwrite(Block, sizeof(uint16_t), Count, filePointer);
	fclose(filePointer);
	return true;
}

int main(int argc, char * argv[]) 
{
	//simple commandline validity check
	if(argc<5 || strcmp(argv[1],"-r") != 0 || strcmp(argv[3],"-o") != 0) {
		puts("Syntax: TerrainGenerator -r <Power of 2 resolution> -o <Output filename>\n");
		system("pause");
		return -1;
	}

	//parse commandline arguments
	int resolution = atoi(argv[2]);
	char* filename = argv[4];

	////create raw height field and initialize with zero
	//uint16_t* height_raw = new uint16_t[resolution*resolution];
	//for (int i=0; i<resolution*resolution; i++)
	//{
	//	height_raw[i] = 0;
	//}

	// resolution for Diamond Square should be 2^n + 1
	DiamondSquare diamondSquare(resolution + 1, 0.0f, 1.0f);
	diamondSquare.GenerateHeightfield();

	//create raw height field and set to heightfield
	uint16_t* height_raw = new uint16_t[resolution*resolution];
	for (int y=0; y<resolution; y++) {
		for(int x=0; x<resolution; x++) {
			height_raw[x+resolution*y] = diamondSquare.GetHeightAsUint(x,y);
		}
	}

	//write to file
	bool success = SaveBlock(filename, height_raw, resolution*resolution);

	//free allocated memory
	delete [] height_raw;

	//print warning if write failed
	if (!success)
	{
		puts("SaveBlock failed\n");
		system("pause");
		return -1;
	}

	return 0;
}

der Fehler muss irgendwo in der methode generateHeightfield() liegen. es soll ein eindimensionaler vector aus floats zurückgegeben werden, die sich alle zwischen 0 und 1 befinden. aber es kompiliert ja nicht mal...

LG

Spacemoose
 
Bei mir kompiliert der Code, mit Ausnahme der main. Die musste ich wegen mangelnder Portabilität anpassen, aber ansonsten beschwert sich der gcc nicht. Welchen Fehler spuckt der Compiler aus?

Code:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdint>
#include "DiamondSquare.h"

bool SaveBlock(const char * Path, const uint16_t * Block, size_t Count) {
	if(!Block) return false;
	FILE * filePointer = fopen(Path, "wb");
	if(0 == filePointer) return false;
	fwrite(Block, sizeof(uint16_t), Count, filePointer);
	fclose(filePointer);
	return true;
}

int main(int argc, char * argv[]) 
{
	//simple commandline validity check
	if(argc<5 || strcmp(argv[1],"-r") != 0 || strcmp(argv[3],"-o") != 0) {
		puts("Syntax: TerrainGenerator -r <Power of 2 resolution> -o <Output filename>\n");
		return -1;
	}

	//parse commandline arguments
	int resolution = atoi(argv[2]);
	char* filename = argv[4];

	////create raw height field and initialize with zero
	//uint16_t* height_raw = new uint16_t[resolution*resolution];
	//for (int i=0; i<resolution*resolution; i++)
	//{
	//	height_raw[i] = 0;
	//}

	// resolution for Diamond Square should be 2^n + 1
	DiamondSquare diamondSquare(resolution + 1, 0.0f, 1.0f);
	diamondSquare.GenerateHeightfield();

	//create raw height field and set to heightfield
	uint16_t* height_raw = new uint16_t[resolution*resolution];
	for (int y=0; y<resolution; y++) {
		for(int x=0; x<resolution; x++) {
			height_raw[x+resolution*y] = diamondSquare.GetHeightAsUint(x,y);
		}
	}

	//write to file
	bool success = SaveBlock(filename, height_raw, resolution*resolution);

	//free allocated memory
	delete [] height_raw;

	//print warning if write failed
	if (!success)
	{
		puts("SaveBlock failed\n");
		return -1;
	}

	return 0;
}
 
Habe den code aus DiamondSquare.cpp upgedatet !


das mit dem kompilieren hat sich schon erledigt. es kompiliert jetzt, und ich habe scheinbar die möglichkeit meinen m_heightfield vector so zu editieren wie ich will.

:)

Nächstes Problem: Die Implementierung des Diamond Square Algorithmuses. Mittels Midpoint Displacement in einem 2 dimensionalen array, soll jeweils der mittelpunkt gefunden werden, und mit einer zufälligen zahl zwischen -1 und 1 vefrschoben werden. also eine detaillierte anleitung findet man hier:


Diamond-square algorithm - Wikipedia, the free encyclopedia


ich habe versucht eine methode squareStep und diamondStep zu schreiben, aber der eigentliche algorithmus fehlt mir noch. ich sitze da schon seit tagen dran und komme nicht vorwärts.

:(

bitte um inspiration/hilfe
 
Weil es mich gejuckt hat habe ich das mal schnell implementiert. Allerdings nur eine stark vereinfachte Version, so übermotiviert war ich dann doch nicht:

Code:
#include <vector>
#include <stdio.h>
#include <cstdlib>

using namespace std;

const unsigned int maxHeight = 256;

unsigned int getRandomHeight()
{
    return rand() % maxHeight;
}

int getRandomError()
{
    return rand() % 4 - 2;
}

void squareStep(vector<unsigned int> &heightValues, unsigned int size, unsigned int x, unsigned int y, unsigned int stepSize)
{
    if (x + stepSize >= size || y + stepSize >= size)
        return;

    unsigned int leftUpper = heightValues.at(x * size + y);
    unsigned int leftLower = heightValues.at((x + stepSize) * size + y);
    unsigned int rightUpper = heightValues.at(x * size + y + stepSize);
    unsigned int rightLower = heightValues.at((x + stepSize) * size + y + stepSize);

    // calculate value of the middle
    int value = (int)((leftUpper + leftLower + rightUpper + rightLower) / 4) + getRandomError();

    if (value < 0 )
        value = 0;
    else if (value >= maxHeight)
        value = maxHeight - 1;

    heightValues.at((x + stepSize / 2) * size + y + stepSize / 2) = value;

    // calculate values at the sides
    heightValues.at((x + stepSize / 2) * size + y) = (leftUpper + leftLower) / 2;
    heightValues.at((x) * size + y + stepSize / 2) = (leftUpper + rightUpper) / 2;
    heightValues.at((x + stepSize) * size + y + stepSize / 2) = (leftLower + rightLower) / 2;
    heightValues.at((x + stepSize / 2) * size + y + stepSize) = (rightUpper + rightLower) / 2;
}

int main(int argc, char * argv[])
{
    const unsigned int size = 9;
    vector<unsigned int> heightValues(size * size, 0);

    // for testing purposal allways the same field is generated
    srand(0);

    // set random values for the corners
    heightValues.at(0) = getRandomHeight();
    heightValues.at(size - 1) = getRandomHeight();
    heightValues.at(size * (size - 1)) = getRandomHeight();
    heightValues.at(size * size - 1) = getRandomHeight();

    // initialize stepSize with too big value, as it will be decreased before the first iteration
    unsigned int stepSize = (size - 1) * 2;

    do
    {
        stepSize /= 2;

        // call squareStep on every possible square with actual stepSize
        for (unsigned int i = 0; i < size; i += stepSize)
            for (unsigned int j = 0; j < size; j += stepSize)
                squareStep(heightValues, size, i, j, stepSize);

    } while (stepSize > 1); // terminate if stepSize won't get smaller anymore

    // print result on the console
    for (unsigned int i = 0; i < size; ++i)
    {
        for (unsigned int j = 0; j < size; ++j)
            printf("%4u", heightValues.at(i * size + j));

        printf("\n");
    }

    return 0;
}

Ich denke das sollte aber ausreichen um dir eine Idee zu geben, wie du Diamond-Square implementieren kannst.

mfg benediktibk
 
vielen dank !

das ist auf jeden fall eine große hilfe. ich muss morgenabend abgeben, aber ich denke, dass ich den diamondstep alleine hinbekomme !

Viele Grüße und schönes Wochenende! Danke das du dir zeit genommen hast! :thumb_up:
 
Zurück
Oben