356 lines
11 KiB
C++
356 lines
11 KiB
C++
#include "block.h"
|
|
|
|
Perlin getPerlinNoise(int seed, double frequency, int octave) {
|
|
Perlin noise;
|
|
noise.SetSeed(seed);
|
|
noise.SetFrequency(frequency);
|
|
noise.SetOctaveCount(octave);
|
|
return noise;
|
|
}
|
|
|
|
Perlin Block::m_temperature = getPerlinNoise(5243, 0.02, 3);
|
|
Perlin Block::m_height = getPerlinNoise(8241, 0.02, 3);
|
|
Perlin Block::m_montainHeight = getPerlinNoise(2763, 0.16, 3);
|
|
|
|
Block::Block(int x, int z, unsigned int width, double step): m_x(x), m_z(z), m_step(step), m_width(width), m_debug(false)
|
|
{
|
|
m_land = new Mesh(float(signed(m_width) * m_step));
|
|
m_water = new Mesh(float(signed(m_width) * m_step));
|
|
|
|
for (int j = signed(m_width) * z; j < signed(m_width) * (z + 1); j++) {
|
|
for (int i = signed(m_width) * x; i < signed(m_width) * (x + 1); i++) {
|
|
createUnitBlock(double(i) * m_step, double(j) * m_step);
|
|
}
|
|
}
|
|
m_land->removeDoubles();
|
|
m_land->removeDoubles();
|
|
m_land->normalizeNormals();
|
|
createWaterSurface();
|
|
}
|
|
|
|
Block::~Block()
|
|
{
|
|
delete m_water;
|
|
delete m_land;
|
|
}
|
|
|
|
Biome Block::getBiome(double temperature, double height)
|
|
{
|
|
if (height < 0.1) {
|
|
return Biome::Sea;
|
|
} else if (height < 0.5 && temperature <= 0){
|
|
return Biome::GrassLand;
|
|
} else if (height >= 0.5 && temperature <= 0) {
|
|
return Biome::Montain;
|
|
} else if (height < 0.5 && temperature > 0) {
|
|
return Biome::Desert;
|
|
} else {
|
|
return Biome::Canyon;
|
|
}
|
|
}
|
|
|
|
double Block::getYGrassLand(double height)
|
|
{
|
|
return 0.5 + ((height - 0.1) / 0.9) * 3;
|
|
}
|
|
|
|
double Block::getYDesert(double temperature, double height)
|
|
{
|
|
return temperature * 3 + getYGrassLand(height);
|
|
}
|
|
|
|
double Block::getYSea(double height)
|
|
{
|
|
if (height < -1) {
|
|
height = -1; // HOT FIX
|
|
}
|
|
if (height >= 0) {
|
|
return getYGrassLand(height);
|
|
} else {
|
|
return (height / (1 + (height * 0.9))) * 5;
|
|
}
|
|
}
|
|
|
|
double Block::getYCanyon(double temperature, double height)
|
|
{
|
|
return getYDesert(temperature, height) + 5;
|
|
}
|
|
|
|
double Block::getYMontain(double height, double x, double z)
|
|
{
|
|
double montainFactor = (height - 0.5) * 2;
|
|
double montainHeight = m_montainHeight.GetValue(x, 0, z);
|
|
return (1 - montainFactor) * getYGrassLand(height) + montainFactor * (12 + montainHeight * montainHeight * 3);
|
|
}
|
|
|
|
double Block::getYFromBiomeParam(Biome biome, double temperature, double height, double x, double z)
|
|
{
|
|
switch (biome) {
|
|
case Biome::GrassLand:
|
|
return getYGrassLand(height);
|
|
case Biome::Sea:
|
|
return getYSea(height);
|
|
case Biome::Desert:
|
|
return getYDesert(temperature, height);
|
|
case Biome::Canyon:
|
|
return getYCanyon(temperature, height);
|
|
case Biome::Montain:
|
|
return getYMontain(height, x, z);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
double Block::getYFromPosition(double x, double z)
|
|
{
|
|
double temperature = m_temperature.GetValue(x, 0, z);
|
|
double height = m_height.GetValue(x, 0, z);
|
|
Biome biome = getBiome(temperature, height);
|
|
|
|
return getYFromBiomeParam(biome, temperature, height, x, z);
|
|
}
|
|
|
|
TypedVertex Block::getVertex(double x, double z)
|
|
{
|
|
|
|
TypedVertex typedvertex;
|
|
double temperature = m_temperature.GetValue(x, 0, z);
|
|
double height = m_height.GetValue(x, 0, z);
|
|
Biome biome = getBiome(temperature, height);
|
|
|
|
typedvertex.x = float(x);
|
|
typedvertex.z = float(z);
|
|
typedvertex.y = float(getYFromBiomeParam(biome, temperature, height, x, z));
|
|
|
|
typedvertex.normalX = 0;
|
|
typedvertex.normalY = 0;
|
|
typedvertex.normalZ = 0;
|
|
|
|
typedvertex.height = float(height);
|
|
typedvertex.temperature = float(temperature);
|
|
typedvertex.biome = biome;
|
|
|
|
return typedvertex;
|
|
}
|
|
|
|
Mesh *Block::land() const
|
|
{
|
|
return m_land;
|
|
}
|
|
|
|
Mesh *Block::water() const
|
|
{
|
|
return m_water;
|
|
}
|
|
|
|
void Block::createUnitBlock(double x, double z)
|
|
{
|
|
TypedVertex v1 = getVertex(x, z);
|
|
TypedVertex v2 = getVertex(x + m_step, z);
|
|
TypedVertex v3 = getVertex(x, z + m_step);
|
|
TypedVertex v4 = getVertex(x + m_step, z + m_step);
|
|
|
|
linkGenericQuad(v1, v2, v3, v4);
|
|
}
|
|
|
|
TypedVertex Block::getBiomeIntersection(TypedVertex &start, TypedVertex &end)
|
|
{
|
|
TypedVertex segmentStart(start);
|
|
TypedVertex segmentEnd(end);
|
|
|
|
TypedVertex segmentMiddle(segmentStart);
|
|
|
|
float distance = float(m_step);
|
|
float threshold = 0.0005f * distance;
|
|
|
|
Biome wantedBiome = start.biome;
|
|
|
|
while (distance > threshold) {
|
|
float xMiddle = (segmentStart.x + segmentEnd.x) / 2;
|
|
float zMiddle = (segmentStart.z + segmentEnd.z) / 2;
|
|
|
|
if (segmentMiddle.biome == wantedBiome) {
|
|
segmentStart = segmentMiddle;
|
|
} else {
|
|
segmentEnd = segmentMiddle;
|
|
}
|
|
|
|
segmentMiddle = getVertex(double(xMiddle), double(zMiddle));
|
|
|
|
float dx = segmentStart.x - segmentEnd.x;
|
|
float dz = segmentStart.z - segmentEnd.z;
|
|
|
|
distance = dx * dx + dz * dz;
|
|
}
|
|
|
|
return segmentMiddle;
|
|
}
|
|
|
|
void Block::getSplitPoints(TypedVertex &v1, TypedVertex &v2, TypedVertex &before, TypedVertex &after)
|
|
{
|
|
float dx = (v2.x - v1.x) / 10;
|
|
float dz = (v2.z - v1.z) / 10;
|
|
|
|
after = v1;
|
|
uint i = 0;
|
|
|
|
do {
|
|
before = after;
|
|
after = getVertex(double(v1.x + (dx * i)), double(v1.z + (dz * i)));
|
|
i++;
|
|
} while (i < 11 && before.biome == after.biome);
|
|
}
|
|
|
|
void Block::splitTriangle(TypedVertex &v1, TypedVertex &v2, TypedVertex &v3, int splitCount)
|
|
{
|
|
Biome wantedBiome = v1.biome;
|
|
|
|
TypedVertex triangle[3] = {v1, v2, v3};
|
|
|
|
int wantedVertex = 0;
|
|
int sameVerticesCount = 0;
|
|
bool changeable = true;
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
if (triangle[i].biome == wantedBiome) {
|
|
if (changeable) {
|
|
changeable = false;
|
|
wantedVertex = i;
|
|
}
|
|
sameVerticesCount ++;
|
|
} else {
|
|
changeable = true;
|
|
}
|
|
}
|
|
|
|
if (sameVerticesCount == 2) {
|
|
wantedVertex = (wantedVertex + 2) % 3;
|
|
}
|
|
|
|
int firstSegmentEnd = (wantedVertex + 2) % 3;
|
|
int secondSegmentEnd = (wantedVertex + 1) % 3;
|
|
|
|
TypedVertex firstIntervalStart;
|
|
TypedVertex firstIntervalEnd;
|
|
|
|
TypedVertex secondIntervalStart;
|
|
TypedVertex secondIntervalEnd;
|
|
|
|
getSplitPoints(triangle[wantedVertex], triangle[firstSegmentEnd], firstIntervalStart, firstIntervalEnd);
|
|
getSplitPoints(triangle[wantedVertex], triangle[secondSegmentEnd], secondIntervalStart, secondIntervalEnd);
|
|
|
|
TypedVertex firstIntersectionWanted = getBiomeIntersection(firstIntervalStart, firstIntervalEnd);
|
|
TypedVertex secondIntersectionWanted = getBiomeIntersection(secondIntervalStart, secondIntervalEnd);
|
|
|
|
TypedVertex firstIntersectionOther(firstIntersectionWanted);
|
|
TypedVertex secondIntersectionOther(secondIntersectionWanted);
|
|
|
|
changeBiome(firstIntersectionWanted, firstIntervalStart.biome);
|
|
changeBiome(secondIntersectionWanted, secondIntervalStart.biome);
|
|
|
|
changeBiome(firstIntersectionOther, firstIntervalEnd.biome);
|
|
changeBiome(secondIntersectionOther, secondIntervalEnd.biome);
|
|
|
|
addTriangleOrSplit(firstIntersectionWanted, triangle[wantedVertex], secondIntersectionWanted, splitCount);
|
|
linkGenericQuad(firstIntersectionOther, secondIntersectionOther, triangle[firstSegmentEnd], triangle[secondSegmentEnd], splitCount);
|
|
|
|
if (abs(firstIntersectionWanted.y - firstIntersectionOther.y) < 0.05f) {
|
|
joinBiomeMiddle(firstIntersectionWanted, firstIntersectionOther, secondIntersectionWanted, secondIntersectionOther);
|
|
} else {
|
|
joinBiomeQuad(firstIntersectionOther, firstIntersectionWanted, secondIntersectionOther, secondIntersectionWanted);
|
|
}
|
|
}
|
|
|
|
void Block::changeBiome(TypedVertex &v, Biome newBiome)
|
|
{
|
|
v.temperature = float(m_temperature.GetValue(double(v.x), 0, double(v.z)));
|
|
v.height = float(m_height.GetValue(double(v.x), 0, double(v.z)));
|
|
v.y = float(getYFromBiomeParam(newBiome, double(v.temperature), double(v.height), double(v.x), double(v.z)));
|
|
v.biome = newBiome;
|
|
}
|
|
|
|
void Block::joinBiomeMiddle(TypedVertex &firstSegmentStart, TypedVertex &firstSegmentEnd, TypedVertex &secondSegmentStart, TypedVertex &secondSegmentEnd)
|
|
{
|
|
firstSegmentEnd.y = firstSegmentStart.y;
|
|
secondSegmentEnd.y = secondSegmentStart.y;
|
|
}
|
|
|
|
void Block::joinBiomeQuad(TypedVertex firstSegmentStart, TypedVertex firstSegmentEnd, TypedVertex secondSegmentStart, TypedVertex secondSegmentEnd)
|
|
{
|
|
if (firstSegmentStart.y > firstSegmentEnd.y) {
|
|
firstSegmentEnd.biome = firstSegmentStart.biome;
|
|
secondSegmentEnd.biome = secondSegmentStart.biome;
|
|
} else {
|
|
firstSegmentStart.biome = firstSegmentEnd.biome;
|
|
secondSegmentStart.biome = secondSegmentEnd.biome;
|
|
}
|
|
linkGenericQuad(firstSegmentStart, firstSegmentEnd, secondSegmentStart, secondSegmentEnd, 0);
|
|
|
|
}
|
|
|
|
void Block::linkGenericQuad(TypedVertex &v1, TypedVertex &v2, TypedVertex &v3, TypedVertex &v4)
|
|
{
|
|
linkGenericQuad(v1, v2, v3, v4, 0);
|
|
}
|
|
|
|
void Block::linkGenericQuad(TypedVertex &v1, TypedVertex &v2, TypedVertex &v3, TypedVertex &v4, int splitCount)
|
|
{
|
|
addTriangleOrSplit(v1, v2, v3, splitCount);
|
|
addTriangleOrSplit(v3, v2, v4, splitCount);
|
|
}
|
|
|
|
void Block::addTriangleOrSplit(TypedVertex &v1, TypedVertex &v2, TypedVertex &v3, int splitCount)
|
|
{
|
|
if (v1.biome == v2.biome && v1.biome == v3.biome) {
|
|
QVector3D normal = faceNormal(&v1, &v2, &v3);
|
|
|
|
v1.normalX = normal.x();
|
|
v3.normalX = normal.x();
|
|
v2.normalX = normal.x();
|
|
|
|
v1.normalY = normal.y();
|
|
v3.normalY = normal.y();
|
|
v2.normalY = normal.y();
|
|
|
|
v1.normalZ = normal.z();
|
|
v3.normalZ = normal.z();
|
|
v2.normalZ = normal.z();
|
|
|
|
m_land->addTriangle(m_land->addVertex(v1), m_land->addVertex(v2), m_land->addVertex(v3));
|
|
|
|
// TODO: Voir pour changer la condition d'arret pour la taille du triangle (puis fuisionner les points si triangle petit)
|
|
} else if (splitCount <= 10) {
|
|
splitTriangle(v1, v2, v3, splitCount + 1);
|
|
}
|
|
}
|
|
|
|
|
|
QVector3D Block::faceNormal(TypedVertex* v1, TypedVertex* v2, TypedVertex* v3)
|
|
{
|
|
QVector3D v1v2 = QVector3D(v2->x - v1->x, v2->y - v1->y, v2->z - v1->z);
|
|
QVector3D v1v3 = QVector3D(v3->x - v1->x, v3->y - v1->y, v3->z - v1->z);
|
|
|
|
|
|
return QVector3D::normal(v1v3, v1v2);
|
|
}
|
|
|
|
void Block::createWaterSurface()
|
|
{
|
|
std::vector<int> indexMap;
|
|
indexMap.resize(m_land->vertices().size(), -1);
|
|
for (uint i = 0; i < m_land->indexes().size(); i++)
|
|
{
|
|
uint vertexIndex = m_land->indexes()[i];
|
|
if (m_land->vertices()[vertexIndex].biome == Biome::Sea) {
|
|
if (indexMap[vertexIndex] < 0) {
|
|
TypedVertex newVertex = m_land->vertices()[vertexIndex];
|
|
newVertex.normalX = 0;
|
|
newVertex.normalY = 1;
|
|
newVertex.normalZ = 0;
|
|
indexMap[vertexIndex] = int(m_water->vertices().size());
|
|
m_water->vertices().push_back(newVertex);
|
|
}
|
|
m_water->indexes().push_back(GLuint(indexMap[vertexIndex]));
|
|
}
|
|
}
|
|
}
|