VertexType getHeightMap(const glm::vec2 position) { const glm::vec2 dx(1.0,0.0); const glm::vec2 dy(0.0,1.0); VertexType v; float h = heightMap(position); float hx = 100.f * (heightMap(position + 0.01f*dx) - h ); float hy = 100.f * (heightMap(position + 0.01f*dy) - h ); v.position = glm::vec3(position,h); v.normal = glm::normalize(glm::vec3(-hx,-hy,1.0)); float c = sin(h*5.f)*0.5+0.5; v.color = glm::vec4(c,1.0-c,1.0,1.0); return v; }
helsing::HeightMap Continuous2DSignalTerrain::generateHeightMap( unsigned int gridPoints, float gridSpacing) { helsing::HeightMap heightMap(gridPoints); if(signal == NULL){ return heightMap; //if there's no signal, simply return a flat HeightMap } for(unsigned int i=0; i<gridPoints; ++i){ for(unsigned int j=0; j<gridPoints; ++j){ heightMap.setHeight(i,j, signal->get(i/gridSpacing, j/gridSpacing)*gridSpacing*amplitude); //TODO GRIDSPACING? } } return heightMap; }
void GridMesh::AddNoise(vector<PositionTexCoordVertex>& vertices) { uint numVerticies = rows * columns; vector<float> heightMap(numVerticies); for (uint row = 0; row < rows; row++) { for (uint column = 0; column < columns; column++) { vertices[row * column].position.y = heightMap[row * column]; } } int width = 128; int height = 128; auto xRange = 1.0; auto yRange = 1.0; auto xFactor = yRange / width; auto yFactor = yRange / height; for (uint i = 0; i < width; i++) { for (uint j = 0; j < height; j++) { float x = xFactor * i; float y = yFactor * j; float z = 0.0; auto value = glm::perlin(vec3(x, y, z)); value = (value + 1.0f) * 0.5f; value = glm::min(value, 1.0f); value = glm::max(value, 0.0f); heightMap[i * j] = value; } } }
// loads a BMP file as the height map for the terrain. If the data // from filename is valid the world is reset and new terrain is created. int terrain::terrainLoadFile() { QImage heightMap(m_terrainFilename); if(heightMap.isNull()){ m_parent->printText("Terrain File could not open"); return 0; // if the file does not open return } QStringList keyList = heightMap.textKeys(); if(keyList.contains("sizex")) m_terrainSize.setX(heightMap.text("sizex").toFloat()); // import the size of the terrain from the image if(keyList.contains("sizey")) m_terrainSize.setY(heightMap.text("sizey").toFloat()); if(keyList.contains("sizez")) m_terrainSize.setZ(heightMap.text("sizez").toFloat()); tTool->setSize(m_terrainSize); // write the new size to the tool m_pixelSize = heightMap.size(); // set the pixel size of the map int imgSize = m_pixelSize.width()*m_pixelSize.height(); // get the number of pixels unsigned int *imgData = new unsigned int[imgSize+1]; // create a new array to hold all the height data from the image if(imgData == NULL) { m_parent->printText("Memory error"); return 0; } imgData[imgSize] = 0; // the last element in the array contains the maximum height for(int j=0; j<m_pixelSize.height(); j++){ // load all the height data from the gray scale part of the height image for(int i=0; i<m_pixelSize.width(); i++){ int k = i + j * m_pixelSize.width(); imgData[k] = qGray(heightMap.pixel(i,j)); // get the height of the pixel if(imgData[k] > imgData[imgSize]) imgData[imgSize] = imgData[k]; // save the highest height as the last element } } terrainCreateMesh(imgData); // create the new terrain mesh delete imgData; // free the temporary height data return 1; }
void TerrainTessellationScene::initialise() { m_material = MaterialPtr( new Material ); m_material->setShaders( ":/shaders/terraintessellation.vert", ":/shaders/terraintessellation.tcs", ":/shaders/terraintessellation.tes", ":/shaders/terraintessellation.geom", ":/shaders/terraintessellation.frag" ); QImage heightMapImage( "../../../opengl/assets/textures/heightmap-1024x1024.png" ); TexturePtr heightMap( new Texture ); heightMap->create(); heightMap->bind(); heightMap->setImage( heightMapImage ); SamplerPtr sampler( new Sampler ); sampler->create(); sampler->setMinificationFilter( GL_LINEAR ); sampler->setMagnificationFilter( GL_LINEAR ); sampler->setWrapMode( Sampler::DirectionS, GL_CLAMP_TO_EDGE ); sampler->setWrapMode( Sampler::DirectionT, GL_CLAMP_TO_EDGE ); m_material->setTextureUnitConfiguration( 0, heightMap, sampler, QByteArrayLiteral( "heightMap" ) ); // Create a renderable object prepareVertexBuffers( heightMapImage.size() ); prepareVertexArrayObject(); // Enable depth testing glEnable( GL_DEPTH_TEST ); m_funcs = m_context->versionFunctions<QOpenGLFunctions_4_0_Core>(); if ( !m_funcs ) qFatal( "Could not obtain required OpenGL context version" ); m_funcs->initializeOpenGLFunctions(); }
Terrain::Terrain(const SystemBody *body) : m_seed(body->GetSeed()), m_rand(body->GetSeed()), m_heightScaling(0), m_minh(0), m_minBody(body) { // load the heightmap if (!body->GetHeightMapFilename().empty()) { RefCountedPtr<FileSystem::FileData> fdata = FileSystem::gameDataFiles.ReadFile(body->GetHeightMapFilename()); if (!fdata) { Output("Error: could not open file '%s'\n", body->GetHeightMapFilename().c_str()); abort(); } ByteRange databuf = fdata->AsByteRange(); Sint16 minHMap = INT16_MAX, maxHMap = INT16_MIN; Uint16 minHMapScld = UINT16_MAX, maxHMapScld = 0; // XXX unify heightmap types switch (body->GetHeightMapFractal()) { case 0: { Uint16 v; bufread_or_die(&v, 2, 1, databuf); m_heightMapSizeX = v; bufread_or_die(&v, 2, 1, databuf); m_heightMapSizeY = v; const Uint32 heightmapPixelArea = (m_heightMapSizeX * m_heightMapSizeY); std::unique_ptr<Sint16[]> heightMap(new Sint16[heightmapPixelArea]); bufread_or_die(heightMap.get(), sizeof(Sint16), heightmapPixelArea, databuf); m_heightMap.reset(new double[heightmapPixelArea]); double *pHeightMap = m_heightMap.get(); for (Uint32 i = 0; i < heightmapPixelArea; i++) { const Sint16 val = heightMap.get()[i]; minHMap = std::min(minHMap, val); maxHMap = std::max(maxHMap, val); // store then increment pointer (*pHeightMap) = val; ++pHeightMap; } assert(pHeightMap == &m_heightMap[heightmapPixelArea]); //Output("minHMap = (%hd), maxHMap = (%hd)\n", minHMap, maxHMap); break; } case 1: { Uint16 v; // XXX x and y reversed from above *sigh* bufread_or_die(&v, 2, 1, databuf); m_heightMapSizeY = v; bufread_or_die(&v, 2, 1, databuf); m_heightMapSizeX = v; const Uint32 heightmapPixelArea = (m_heightMapSizeX * m_heightMapSizeY); // read height scaling and min height which are doubles double te; bufread_or_die(&te, 8, 1, databuf); m_heightScaling = te; bufread_or_die(&te, 8, 1, databuf); m_minh = te; std::unique_ptr<Uint16[]> heightMapScaled(new Uint16[heightmapPixelArea]); bufread_or_die(heightMapScaled.get(), sizeof(Uint16), heightmapPixelArea, databuf); m_heightMap.reset(new double[heightmapPixelArea]); double *pHeightMap = m_heightMap.get(); for (Uint32 i = 0; i < heightmapPixelArea; i++) { const Uint16 val = heightMapScaled[i]; minHMapScld = std::min(minHMapScld, val); maxHMapScld = std::max(maxHMapScld, val); // store then increment pointer (*pHeightMap) = val; ++pHeightMap; } assert(pHeightMap == &m_heightMap[heightmapPixelArea]); //Output("minHMapScld = (%hu), maxHMapScld = (%hu)\n", minHMapScld, maxHMapScld); break; } default: assert(0); } } switch (Pi::detail.textures) { case 0: textures = false; m_fracnum = 2; break; default: case 1: textures = true; m_fracnum = 0; break; } switch (Pi::detail.fracmult) { case 0: m_fracmult = 100; break; case 1: m_fracmult = 10; break; case 2: m_fracmult = 1; break; case 3: m_fracmult = 0.5; break; default: case 4: m_fracmult = 0.1; break; } m_sealevel = Clamp(body->GetVolatileLiquid(), 0.0, 1.0); m_icyness = Clamp(body->GetVolatileIces(), 0.0, 1.0); m_volcanic = Clamp(body->GetVolcanicity(), 0.0, 1.0); // height scales with volcanicity as well m_surfaceEffects = 0; const double rad = m_minBody.m_radius; // calculate max height if (!body->GetHeightMapFilename().empty() && body->GetHeightMapFractal() > 1) { // if scaled heightmap m_maxHeightInMeters = 1.1 * pow(2.0, 16.0) * m_heightScaling; // no min height required as it's added to radius in lua } else { // max mountain height for earth-like planet (same mass, radius) m_maxHeightInMeters = std::max(100.0, (9000.0 * rad * rad * (m_volcanic + 0.5)) / (body->GetMass() * 6.64e-12)); m_maxHeightInMeters = std::min(rad, m_maxHeightInMeters); // small asteroid case } // and then in sphere normalized j**z m_maxHeight = m_maxHeightInMeters / rad; //Output("%s: max terrain height: %fm [%f]\n", m_minBody.name.c_str(), m_maxHeightInMeters, m_maxHeight); m_invMaxHeight = 1.0 / m_maxHeight; m_planetRadius = rad; m_planetEarthRadii = rad / EARTH_RADIUS; // Pick some colors, mainly reds and greens for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_rockColor)); i++) { double r, g, b; r = m_rand.Double(0.3, 1.0); g = m_rand.Double(0.3, r); b = m_rand.Double(0.3, g); r = std::max(b, r * body->GetMetallicity()); g = std::max(b, g * body->GetMetallicity()); m_rockColor[i] = vector3d(r, g, b); } // Pick some darker colours mainly reds and greens for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_darkrockColor)); i++) { double r, g, b; r = m_rand.Double(0.05, 0.3); g = m_rand.Double(0.05, r); b = m_rand.Double(0.05, g); r = std::max(b, r * body->GetMetallicity()); g = std::max(b, g * body->GetMetallicity()); m_darkrockColor[i] = vector3d(r, g, b); } // grey colours, in case you simply must have a grey colour on a world with high metallicity for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_greyrockColor)); i++) { double g; g = m_rand.Double(0.3, 0.9); m_greyrockColor[i] = vector3d(g, g, g); } // Pick some plant colours, mainly greens // TODO take star class into account for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_plantColor)); i++) { double r, g, b; g = m_rand.Double(0.3, 1.0); r = m_rand.Double(0.3, g); b = m_rand.Double(0.2, r); g = std::max(r, g * body->GetLife()); b *= (1.0 - body->GetLife()); m_plantColor[i] = vector3d(r, g, b); } // Pick some darker plant colours mainly greens // TODO take star class into account for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_darkplantColor)); i++) { double r, g, b; g = m_rand.Double(0.05, 0.3); r = m_rand.Double(0.00, g); b = m_rand.Double(0.00, r); g = std::max(r, g * body->GetLife()); b *= (1.0 - body->GetLife()); m_darkplantColor[i] = vector3d(r, g, b); } // Pick some sand colours, mainly yellow // TODO let some planetary value scale this colour for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_sandColor)); i++) { double r, g, b; r = m_rand.Double(0.6, 1.0); g = m_rand.Double(0.6, r); //b = m_rand.Double(0.0, g/2.0); b = 0; m_sandColor[i] = vector3d(r, g, b); } // Pick some darker sand colours mainly yellow // TODO let some planetary value scale this colour for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_darksandColor)); i++) { double r, g, b; r = m_rand.Double(0.05, 0.6); g = m_rand.Double(0.00, r); //b = m_rand.Double(0.00, g/2.0); b = 0; m_darksandColor[i] = vector3d(r, g, b); } // Pick some dirt colours, mainly red/brown // TODO let some planetary value scale this colour for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_dirtColor)); i++) { double r, g, b; r = m_rand.Double(0.3, 0.7); g = m_rand.Double(r - 0.1, 0.75); b = m_rand.Double(0.0, r / 2.0); m_dirtColor[i] = vector3d(r, g, b); } // Pick some darker dirt colours mainly red/brown // TODO let some planetary value scale this colour for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_darkdirtColor)); i++) { double r, g, b; r = m_rand.Double(0.05, 0.3); g = m_rand.Double(r - 0.05, 0.35); b = m_rand.Double(0.0, r / 2.0); m_darkdirtColor[i] = vector3d(r, g, b); } // These are used for gas giant colours, they are more m_random and *should* really use volatileGasses - TODO for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_gglightColor)); i++) { double r, g, b; r = m_rand.Double(0.0, 0.5); g = m_rand.Double(0.0, 0.5); b = m_rand.Double(0.0, 0.5); m_gglightColor[i] = vector3d(r, g, b); } //darker gas giant colours, more reds and greens for (int i = 0; i < int(COUNTOF(m_entropy)); i++) m_entropy[i] = m_rand.Double(); for (int i = 0; i < int(COUNTOF(m_ggdarkColor)); i++) { double r, g, b; r = m_rand.Double(0.0, 0.3); g = m_rand.Double(0.0, r); b = m_rand.Double(0.0, std::min(r, g)); m_ggdarkColor[i] = vector3d(r, g, b); } }
//------------------------------------------------------------------------------ void CTerrain::updateVirtual() { const uint32 stepX = 1 << terrain.stepX; // edges count (vertices: step + 1) const uint32 stepZ = 1 << terrain.stepZ; const uint32 divStepX = 1 << terrain.divStepX; // count of chunks const uint32 divStepZ = 1 << terrain.divStepZ; const uint32 stepMeshX = stepX / divStepX; // number of edges in chunk const uint32 stepMeshZ = stepZ / divStepZ; QVector<SVertex> vx((stepX + 1) * (stepZ + 1)); QVector<float> heightMap((stepX + 1) * (stepZ + 1), 0.0f); QVector<bool> usedHeightMap((stepX + 1) * (stepZ + 1), false); QVector<int32> heightMapLayer((stepX + 1) * (stepZ + 1), -1); float depth = 1.0f; srand(terrain.seed); // compute heightmap if(!terrain.landBorder) { heightMap[0] = rndHeight(); heightMap[stepX] = rndHeight(); heightMap[(stepX + 1) * stepZ] = rndHeight(); heightMap[(stepX + 1) * stepZ + stepX] = rndHeight(); } usedHeightMap[0] = true; usedHeightMap[stepX] = true; usedHeightMap[(stepX + 1) * stepZ] = true; usedHeightMap[(stepX + 1) * stepZ + stepX] = true; fractalGrid(&heightMap[0], &usedHeightMap[0], stepX, stepZ, stepX, stepZ, 0, depth); // compute positions float x = 0.0f; float z = 0.0f; const float xInc = 1.0f / stepX; const float zInc = 1.0f / stepZ; for(uint32 i = 0; i <= stepX; i++, x += xInc) { for(uint32 j = 0; j <= stepZ; j++, z += zInc) { const uint32 k = ((stepX + 1) * i + j); vx[k].position = QVector3D(x - 0.5f, heightMap[k] * terrain.heightMultiplier, z - 0.5f); vx[k].normal = QVector3D(0.0, 1.0, 0.0); vx[k].texCoord = QVector2D(-x * 10, z * 10); vx[k].color = QVector3D(1.0, 0.0, 0.0); vx[k].color2 = QVector3D(0.0, 0.0, 0.0); } z = 0.0f; } // compute normals, tangents, bitangents for(int32 v = 0; v < vx.size(); v++) { uint32 x = v % (stepX + 1); uint32 z = v / (stepX + 1); if((x > 0) && (x < stepX) && (z > 0) && (z < stepZ)) { QVector3D &vC = vx[v].position; QVector3D &vL = vx[v - 1].position; QVector3D &vR = vx[v + 1].position; QVector3D &vT = vx[v - stepX - 1].position; QVector3D &vB = vx[v + stepX + 1].position; vx[v].normal = QVector3D::normal(vC, vL, vT); vx[v].normal += QVector3D::normal(vC, vT, vR); vx[v].normal += QVector3D::normal(vC, vR, vB); vx[v].normal += QVector3D::normal(vC, vB, vL); vx[v].normal.normalize(); vx[v].normalTangent = QVector3D(vx[v].normal.y(), -vx[v].normal.x(), -vx[v].normal.z()); vx[v].normalBitangent = QVector3D(-vx[v].normal.x(), -vx[v].normal.z(), vx[v].normal.y()); } } // compute colors by layers for(int32 v = 0; v < vx.size(); v++) { for(int32 l = 0; l != terrain.layers.size(); l++) { const SMaterialLayer *layer = &terrain.layers[l]; if(!(((layer->useHeightLimit) && ((vx[v].position.y() < layer->minHeight) || (vx[v].position.y() > layer->maxHeight))) || ((layer->useSlopeLimit) && (((1.0f - vx[v].normal.y()) < layer->minSlope) || ((1.0f - vx[v].normal.y()) > layer->maxSlope))))) { vx[v].color = layer->color.toVector3D() * layer->color.w(); heightMapLayer[v] = l; } } } /*for(int32 v = 0; v < vx.size(); v++) qDebug(QString("y %1, x %2: %3").arg(v / (stepX + 1)).arg(v % (stepX + 1)).arg(heightMapLayer[v]).toStdString().c_str());*/ // divide to meshes, fill model meshes /*model.materials.clear(); model.materials.push_back(SMaterial(context->getShaders()->getShaderProgram(NShader::PROGRAM_TERRAIN), context->getMaps()->addMap(SMap(":/maps/data/maps/rocks00n.png")), context->getMaps()->addMap(SMap(":/maps/data/maps/rocks00n.png")), context->getMaps()->addMap(SMap(":/maps/data/maps/rocks00n.png"))));*/ /*model.meshes.clear(); for(uint32 m = 0; m < (divStepX * divStepZ); m++) { model.meshes.push_back(SMesh()); SMesh *mesh = &model.meshes.back(); mesh->transformation = SMatrix::composeTransformation(mesh->position, mesh->rotation, mesh->scale); mesh->faces.push_back(SFaces()); SFaces *faces = &mesh->faces.back(); mesh->vertices.resize((stepMeshX + 1) * (stepMeshZ + 1)); faces->faces.resize(stepMeshX * stepMeshZ * NTerrain::FACE_BLOCK_SIZE); uint32 offset = (m / divStepZ) * stepMeshZ * (stepX + 1) + (m % divStepX) * stepMeshX; uint32 mCol2 = 0; for(uint32 mRow = 0; mRow <= stepMeshZ; mRow++, offset += (stepX + 1)) { for(uint32 mCol = 0; mCol <= stepMeshX; mCol++, mCol2++) { mesh->vertices[mCol2] = vx[offset + mCol]; if((mRow == stepMeshZ) || (mCol == stepMeshX)) mesh->vertices[mCol2].color = QVector3D(0.8, 0.2, 0.2); } } for(uint32 i = 0; i < stepMeshX; i++) { for(uint32 j = 0; j < stepMeshZ; j++) { const uint32 k = (stepMeshX * i + j) * NTerrain::FACE_BLOCK_SIZE; const uint32 l0 = (stepMeshX + 1) * i + j + 0; const uint32 l1 = (stepMeshX + 1) * i + j + 1; const uint32 l2 = (stepMeshX + 1) * (i + 1) + j + 0; const uint32 l3 = (stepMeshX + 1) * (i + 1) + j + 1; faces->faces[k + 0] = SFace(l0, l1, l2); faces->faces[k + 1] = SFace(l2, l1, l3); } } }*/ model.materials.clear(); const CMap *noMap = context->getMaps()->getMap(NMap::DEFAULT_NORMAL); // default map model.meshes.clear(); for(uint32 gz = 0; gz < divStepZ; gz++) { for(uint32 gx = 0; gx < divStepX; gx++) { // sub mesh //const uint32 groupBase = gz * divStepZ + gx; const uint32 vxGroupBase = gz * stepMeshZ * (stepX + 1) + gx * stepMeshX; //qDebug("new mesh"); model.meshes.push_back(SMesh()); SMesh *mesh = &model.meshes.back(); mesh->transformation = SMatrix::composeTransformation(mesh->position, mesh->rotation, mesh->scale); for(uint32 ez = 0; ez < stepMeshZ; ez++) { for(uint32 ex = 0; ex < stepMeshX; ex++) { // two faces block const uint32 vx0 = vxGroupBase + ez * (stepX + 1) + ex; const uint32 vx1 = vx0 + 1; const uint32 vx2 = vx0 + (stepX + 1); const uint32 vx3 = vx2 + 1; for(uint8 v = 0; v < NTerrain::FACE_BLOCK_SIZE; v++) { // one face const uint32 face[NModel::FACE_SIZE] = { (!v) ? vx0 : vx2, (!v) ? vx1 : vx1, (!v) ? vx2 : vx3 }; // material signature QSet<int32> faceSet; faceSet.insert(heightMapLayer[face[0]]); faceSet.insert(heightMapLayer[face[1]]); faceSet.insert(heightMapLayer[face[2]]); QVector<int32> faceMats = faceSet.toList().toVector(); qSort(faceMats.begin(), faceMats.end()); //qDebug(QString("%1 %2 %3 : %4 %5 %6 : %7").arg(face[0]).arg(face[1]).arg(face[2]).arg(heightMapLayer[face[0]]).arg(heightMapLayer[face[1]]).arg(heightMapLayer[face[2]]).arg(faceMats.size()).toStdString().c_str()); /*for(int32 i : faceMats) qDebug(QString("%1").arg(i).toStdString().c_str());*/ SFaces *faces = NULL; for(auto f = mesh->faces.begin(); f != mesh->faces.end(); f++) { // find faces group by material signature if(!f->material) { //qDebug("chyba mat chybi"); break; } else if(f->material->layerVx == faceMats) { //qDebug("nasel"); faces = &(*f); break; } } if(!faces) { SMaterial *material = NULL; for(auto m = model.materials.begin(); m != model.materials.end(); m++) { // find previous used material if(m->layerVx == faceMats) { //qDebug("nenasel, ale predchozí mat ano"); material = &(*m); break; } } if(!material) { //qDebug("nenasel"); const CMap *maps[NModel::FACE_SIZE]; for(int32 i = 0; static_cast<uint32>(i) < NModel::FACE_SIZE; i++) { maps[i] = (faceMats.size() > i) ? ((faceMats[i] != -1) ? terrain.layers[faceMats[i]].detailTexture : noMap) : NULL; /*if(maps[i]) qDebug(maps[i]->getMap()->file.toStdString().c_str()); else qDebug("no map");*/ } model.materials.push_back(SMaterial(context->getShaders()->getShaderProgram(NShader::PROGRAM_TERRAIN), NULL, NULL, NULL, maps[0], NULL, NULL, NULL, NULL, NULL, maps[1], maps[2], faceMats)); material = &model.materials.back(); } mesh->faces.push_back(SFaces(material)); faces = &mesh->faces.back(); } uint16 newFace[NModel::FACE_SIZE]; SVertex newVx[NModel::FACE_SIZE] = { vx[face[0]], vx[face[1]], vx[face[2]] }; for(uint32 i = 0; i < NModel::FACE_SIZE; i++) { // set texture using multiplier if(heightMapLayer[face[i]] == faceMats[0]) newVx[i].color2.setX(1.0f); else if(heightMapLayer[face[i]] == faceMats[1]) newVx[i].color2.setY(1.0f); else if(heightMapLayer[face[i]] == faceMats[2]) newVx[i].color2.setZ(1.0f); //qDebug(QString("face %1: %2 %3 %4").arg(i).arg(newVx[i].color2.x()).arg(newVx[i].color2.y()).arg(newVx[i].color2.z()).toStdString().c_str()); bool found = false; for(int32 v = 0; v < mesh->vertices.size(); v++) { // find same vertex if((mesh->vertices[v].position == newVx[i].position) && (mesh->vertices[v].color2 == newVx[i].color2)) { //qDebug("same vx found"); found = true; newFace[i] = v; break; } } if(!found) { //qDebug("same vx not found"); newFace[i] = mesh->vertices.size(); mesh->vertices.push_back(newVx[i]); } } faces->faces.push_back(SFace(newFace[0], newFace[1], newFace[2])); } } } //qDebug(QString("-------- face groups: %1").arg(mesh->faces.size()).toStdString().c_str()); } } //qDebug(QString("-------- meshes: %1").arg(model.meshes.size()).toStdString().c_str()); }
void MapGenerator::GenerateMap(const int width, const int height, const float resource, const float obstacle) { std::vector<int>& mHeightMap = pSceneGame->mHeightMap; std::vector<int>& mGameMap = pSceneGame->mGameMap; std::vector<Scene_Game::LightMapData>& mLightMap = pSceneGame->mLightMap; std::vector<double> heightMap(width); mHeightMap.resize(width); pSceneGame->iMapWidth = width; pSceneGame->iMapHeight = height; pSceneGame->iMapWidthPixel = width*BLOCK_SIZE; pSceneGame->iMapHeightPixel = height*BLOCK_SIZE; for (int i=0; i<width; i++) { heightMap[i] = (height*0.6+rand.NextDouble()*2); } // 당기기 for (int i=0; i<width/20; i++) { int max_height = rand.Next()%41-20; double power = rand.NextDouble()*0.5+0.4; int seed_x = rand.Next()%width; while (max_height-->0) { double uplift = (max_height>0)? 1:-1; int offset = 0; while (abs(uplift)>0.1) { if (offset > 0) heightMap[(seed_x-offset+width)%width] += uplift; heightMap[(seed_x+offset)%width] += uplift; uplift *= power; offset++; } } } // 스무스 for (int i=0; i<width; i++) { double h = 0; mHeightMap[i] = (int)((heightMap[(i-1+width)%width] + heightMap[i] + heightMap[(i+1)%width])/3); } // 생성 mGameMap.resize(width*height); for (int i=0; i<width; i++) { int dirt = 6+rand.Next()%2; for (int j=0; j<height; j++) { if (j<height-mHeightMap[i]) mGameMap[i+j*width] = BLOCK_AIR; else if (j >= (int)(height*0.7f)-1 && j<= (int)(height*0.7f)+1) mGameMap[i+j*width] = BLOCK_OBSIDIAN; else if (j >= height-2) mGameMap[i+j*width] = BLOCK_BEDROCK; else if(dirt) { mGameMap[i+j*width] = BLOCK_DIRT; dirt--; } else mGameMap[i+j*width] = BLOCK_ROCK; } } // 호수를 만든다. for (int i=0; i<width/40; i++) { int seed_x = rand.Next()%width; int seed_y = 0; bool retry = false; // 씨드를 뿌린다. while (true) { bool stop = true; while (mGameMap[seed_x+seed_y*width] == 0) seed_y++; if (mGameMap[seed_x+seed_y*width] == BLOCK_WATER) { retry = true; break; } for (int j=-5; j<=5; j++) { if (mGameMap[(seed_x+j+width)%width+seed_y*width] == 0) { stop = false; seed_x = (seed_x+j+width)%width; seed_y += 1; break; } } if (stop) break; } if (retry) { i--; continue; } // 그 자리에서부터 땅을 파고 들어간다. // 위에 있는 땅을 제거한다. double w = 3+rand.Next()%7; for (int k = seed_y; k>=seed_y-3; k--) { for (int j = (int)floor(-w/2); j < w/2; j++) { mGameMap[(seed_x+j+width)%width+k*width] = 0; } } while (w >= 1.5) { for (int j = (int)floor(-w/2); j < w/2; j++) { mGameMap[(seed_x+j+width)%width+seed_y*width] = BLOCK_WATER; } w *= 0.75; seed_y += 1; if (w >= 2) seed_x += rand.Next()%3-1; } } // 본부를 만든다. while (true) { int seed_x = rand.Next()%(width-30)+15; int seed_y = 0; bool retry = false; // 씨드를 뿌린다. while (true) { bool stop = true; while (mGameMap[seed_x+seed_y*width] == 0) seed_y++; if (mGameMap[seed_x+seed_y*width] == BLOCK_WATER) { retry = true; break; } for (int j=-10; j<=10; j++) { if (mGameMap[(seed_x+j+width)%width+seed_y*width] == 0) { stop = false; seed_x = (seed_x+j+width)%width; seed_y += 1; break; } } if (stop) break; } if (retry) continue; // 그 자리에 공간을 만든다. // 6X4 for (int k = seed_y-1; k>=0; k--) { for (int j = -4; j <= 4; j++) { mGameMap[(seed_x+j+width)%width+k*width] = 0; } } for (int j = -4; j <= 4; j++) { mGameMap[(seed_x+j+width)%width+(seed_y+1)*width] = mGameMap[(seed_x+j+width)%width+seed_y*width] = BLOCK_BEDROCK; } pSceneGame->mBaseCamp.SetBasePos(seed_x, seed_y-1); pSceneGame->SetPlayerPos(seed_x, seed_y-1); break; } int size = width*height; MakeMine(BLOCK_WATER, (int)(size*0.0013f*resource), 3, 0); // 40 MakeMine(BLOCK_MAGMA, (int)(size*0.0012f*obstacle), 3, (int)(height*0.6f)); // 35 MakeMine(BLOCK_OBSIDIAN, (int)(size*0.001f*obstacle), 3, (int)(height*0.7f)); // 30 MakeMine(BLOCK_MINERAL, (int)(size*0.0025f*resource), 5, 0); // 75 MakeMine(BLOCK_COAL, (int)(size*0.0017f*resource), 4, 0); // 50 MakeMine(BLOCK_OIL, (int)(size*0.001f*resource), 5, pSceneGame->mBaseCamp.iY+30); // 30 MakeMine(BLOCK_URANIUM, (int)(size*0.0007f*resource), 2, pSceneGame->mBaseCamp.iY+(height-pSceneGame->mBaseCamp.iY)/2); // 20 MakeMine(BLOCK_UNOBTAINIUM, (int)(size*0.0001f*resource), 5, height-20); // 3 MakeMine(BLOCK_COOKIE, 1, 2, height-4); // 1 // 라이트맵을 초기화한다. mLightMap.resize(width*height); for (int i=0; i<height; i++) { for (int j=0; j<width; j++) { if (mGameMap[j+i*width] == BLOCK_AIR) { mLightMap[j+i*width].light = mLightMap[j+i*width].base_light = 255; mLightMap[j+i*width].lighted = true; // for (int k=0; k<5;k++) // { // if (i+k >= height) break; // mLightMap[j+(i+k)*width].light += 10; // } } else { mLightMap[j+i*width].light = mLightMap[j+i*width].base_light = min(max(255-(i-pSceneGame->mBaseCamp.iY)*10, 0), 255); mLightMap[j+i*width].lighted = mLightMap[j+i*width].base_light>64; } } } }
Chunk::Chunk(int x, int z, unsigned int seed) : m_x(x), m_z(z) { PerlinNoise heightMap(seed); PerlinNoise noise(seed + 1); PerlinNoise caves(seed + 2); // Lower means more mountains and valleys const float SMOOTHNESS = 25.0; // Larger means flatter const float DETAIL = 1 / 16.0; // Larger means more overhangs and caves const float CARVING = 2.0; // Larger means more caves const float CAVES = 3.0; for (int i = 0; i < SIZE; ++i) { for (int j = 0; j < SIZE; ++j) { float heightSample = heightMap.sample(SMOOTHNESS * (x * SIZE + i), 0.0, SMOOTHNESS * (z * SIZE + j)); float height = (DEPTH / 2) + SCALE * heightSample; //(0.5 + 0.25 * heightSample); for (int k = 0; k < DEPTH; ++k) { float sample = noise.sample(DETAIL * (x * SIZE + i), CARVING * DETAIL * k, DETAIL * (z * SIZE + j)); sample += (height - k) / (SCALE / 4.0); // Ground threshold if (sample > 0.0f) { // Stone threshold if (sample > 0.5f) newBlock(x * SIZE + i, k, z * SIZE + j, BlockLibrary::STONE); else newBlock(x * SIZE + i, k, z * SIZE + j, BlockLibrary::DIRT); } } } } // Convert top-level dirt to grass for (int i = 0; i < SIZE; ++i) { for (int j = 0; j < SIZE; ++j) { // Fill any gap below sea level with water for (int k = 0; k < 0.45 * DEPTH; ++k) { Coordinate location(x * SIZE + i, k, z * SIZE + j); if (!get(location)) { newBlock(location.x, location.y, location.z, BlockLibrary::WATER); } } } } // Cut out some caves for (int i = 0; i < SIZE; ++i) { for (int j = 0; j < SIZE; ++j) { for (int k = 0; k < DEPTH; ++k) { Coordinate location(x * SIZE + i, k, z * SIZE + j); if (m_blocks.find(location) == m_blocks.end()) continue; float caveSample = caves.sample(DETAIL * (x * SIZE + i), CAVES * DETAIL * k, DETAIL * (z * SIZE + j)); caveSample = pow(caveSample, 3.0); // Ground threshold if (caveSample <= -0.1) { removeBlock(location); } } // Convert top-level dirt to grass for (int k = DEPTH - 1; k >= 0; --k) { Coordinate location(x * SIZE + i, k, z * SIZE + j); if (get(location)) { auto& block = m_blocks[location]; if (block->blockType == BlockLibrary::DIRT) block->blockType = BlockLibrary::GRASS; // We only work on the top-most block in a column. break; } } } } }
void terrain(Mesh *mesh) { QImage heightMap("clouds2.png"); heightMap = heightMap.convertToFormat(QImage::Format_ARGB32); int height = heightMap.height(); int width = heightMap.width(); int verticesCount = height * width; int indicesCount = 2 * 3 * (height - 1) * (width - 1); vec3f *vertices = (vec3f *) malloc(verticesCount * sizeof(vec3f)); int i = 0; int h = height >> 1; int w = width >> 1; for (int y = 0; y < height; ++y) { int *p = (int *) heightMap.scanLine(y); for (int x = 0; x < width; ++x) { vertices[i].x = -w + x; vertices[i].y = (GLfloat) (p[x] & 0xff) / 5.0f; vertices[i].z = -h + y; ++i; } } for (int i = 0; i < verticesCount; ++i) { vertices[i].x *= 2.0f; vertices[i].z *= 2.0f; } uint *indices = (uint *) malloc(indicesCount * sizeof(uint)); i = 0; for (int y = 0; y < height - 1; ++y) { for (int x = 0; x < width - 1; ++x) { indices[i++] = (y + 1) * width + x; indices[i++] = (y + 1) * width + x + 1; indices[i++] = y * width + x + 1; indices[i++] = (y + 1) * width + x; indices[i++] = y * width + x + 1; indices[i++] = y * width + x; } } vec3f *normals = (vec3f *) malloc(verticesCount * sizeof(vec3f)); memset(normals, 0, verticesCount * sizeof(vec3f)); for (int i = 0; i < indicesCount; i += 3) { vec3f a = vertices[indices[i + 1]] - vertices[indices[i]]; vec3f b = vertices[indices[i + 2]] - vertices[indices[i + 1]]; vec3f c = normalize(cross(a, b)); normals[indices[i]] = normals[indices[i]] + c; normals[indices[i + 1]] = normals[indices[i + 1]] + c; normals[indices[i + 2]] = normals[indices[i + 2]] + c; } for (int i = 0; i < verticesCount; ++i) { normals[i] = normalize(normals[i]); } float *fogCoordinates = (float *) malloc(verticesCount * sizeof(float)); for (int i = 0; i < verticesCount; ++i) { fogCoordinates[i] = 255.0f - vertices[i].y; } float *textureCoordinates = (float *) malloc(2 * verticesCount * sizeof(float)); i = 0; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { float s = (float) x / (float) (width - 1); float t = (float) y / (float) (height - 1); textureCoordinates[i++] = s; textureCoordinates[i++] = t; } } mesh->loadVertices(vertices, verticesCount * sizeof(vec3f)); mesh->loadNormals(normals, verticesCount * sizeof(vec3f)); mesh->loadFogCoordinates(fogCoordinates, verticesCount * sizeof(float)); mesh->loadTexture2DCoordinates(textureCoordinates, 2 * verticesCount * sizeof(float)); mesh->loadIndices(indices, indicesCount * sizeof(uint)); free(vertices); free(normals); free(fogCoordinates); free(textureCoordinates); free(indices); }
graphics::Model *TerrainGenerator::simplexTerrain2(float xRange, float zRange, float vertexDensity, float octaves[], float yScales[], int nOctaves) { graphics::Model* model = new graphics::Model; graphics::ModelGroup group; cv::Mat heightMap(xRange*vertexDensity, zRange*vertexDensity, CV_32FC1); cv::Mat heightMapThresh(xRange*vertexDensity, zRange*vertexDensity, CV_32FC1); int step = heightMap.cols; qDebug() << "Number of octaves: " << nOctaves; float y = 0; // find max scale to scale correctly when saved to cv::Mat float maxScale = 0; for (int i = 0; i < nOctaves; i++){ if (yScales[i] > maxScale){ maxScale = yScales[i]; } } qDebug() << "MaxScale: " << maxScale; // Generate height map and texture coordinates for (int x = 0; x < xRange*vertexDensity; x++){ for (int z = 0; z < zRange*vertexDensity; z++){ y = 0; for(int i = 0; i < nOctaves; i++){ y += SimplexNoise1234::noise(x/octaves[i], z/octaves[i]) * yScales[i]; } group.vertices.push_back(QVector3D((float)x/vertexDensity, y, (float)z/vertexDensity)); group.texCoords.push_back(QVector3D((float)x/(xRange*vertexDensity),(float)z/(zRange*vertexDensity), 0)); heightMap.at<float>(x,z) = (y + maxScale) / (2*maxScale); } } //cv::imshow("heightMap", heightMap); cv::threshold(heightMap, heightMapThresh, 0.5, 1, 1); //cv::imshow("heightMapThresh", heightMapThresh); qDebug() << "step: " << step; // Tie vertices together. openGL indexing starts at 0 tydligen.. for (int x = 1; x < xRange*vertexDensity; x++){ for (int z = 1; z < zRange*vertexDensity; z++){ // First triangle group.indices.push_back( (x-1)*step + z-1); // 0 group.indices.push_back( x*step + z ); // 3 group.indices.push_back( x*step + z-1); // 2 // Second triangle group.indices.push_back( (x-1)*step + z-1); // 0 group.indices.push_back( (x-1)*step + z ); // 1 group.indices.push_back( x*step + z ); // 3 } } float y00, y01, y10; QVector3D tangent1, tangent2; QVector3D normal; // Calculate normals for (int z = 0; z < zRange*vertexDensity; z++){ group.normals.push_back(QVector3D(0,1,0)); } for (int x = 1; x < xRange*vertexDensity; x++){ group.normals.push_back(QVector3D(0,1,0)); for (int z = 1; z < zRange*vertexDensity; z++){ y00 = heightMap.at<float>(x-1,z-1); y01 = heightMap.at<float>(x-1,z); y10 = heightMap.at<float>(x,z-1); tangent1 = QVector3D(1, y10-y00, 0); tangent2 = QVector3D(0, y01-y00, 1); normal = normal.crossProduct(tangent2, tangent1); normal.normalize(); group.normals.push_back(normal); } } model->groups.push_back(group); model->uploadToGPU(); return model; }
Terrain::Terrain( const QString & heightMapPath, const QVector3D & size, const QVector3D & offset, const int & smoothingPasses ) { QImage heightMap( heightMapPath ); if( heightMap.isNull() ) { qFatal( "\"%s\" not found!", heightMapPath.toLocal8Bit().constData() ); } mMapSize = heightMap.size(); mSize = size; mOffset = offset; mToMapFactor = QSizeF( (float)mMapSize.width()/(float)mSize.x(), (float)mMapSize.height()/(float)mSize.z() ); mVertices.resize( mMapSize.width() * mMapSize.height() ); // prepare positions QVector<QVector3D> rawPositions; for( int h=0; h<mMapSize.height(); h++ ) { for( int w=0; w<mMapSize.width(); w++ ) { rawPositions.push_back( offset + QVector3D( w*(mSize.x()/mMapSize.width()), (float)qRed( heightMap.pixel( w, h ) )*(mSize.y()/256.0), h*(mSize.z()/mMapSize.height()) ) ); } } // smoothed positions for( int i=0; i<smoothingPasses; i++ ) rawPositions = smoothedPositions( rawPositions, mMapSize ); // copy smoothed positions to vertex array for( int i=0; i<mVertices.size(); i++ ) { mVertices[i].position = rawPositions[i]; } // normals for( int h=0; h<mMapSize.height()-1; h++ ) { for( int w=0; w<mMapSize.width()-1; w++ ) { vertex( w, h ).normal = QVector3D::normal( getVertexPosition( w, h ), getVertexPosition( w, h+1 ), getVertexPosition( w+1, h ) ); } vertex( mMapSize.width()-1, h ).normal = QVector3D(0,1,0); // last vertex in row } for( int w=0; w<mMapSize.width(); w++ ) { vertex( w, mMapSize.height()-1 ).normal = QVector3D(0,1,0); // last row } // texture coordinates for( int h=0; h<mMapSize.height(); h++ ) { for( int w=0; w<mMapSize.width(); w++ ) { vertex( w, h ).texCoord = QVector2D( w, h ); } } mVertexBuffer = QGLBuffer( QGLBuffer::VertexBuffer ); mVertexBuffer.create(); mVertexBuffer.bind(); mVertexBuffer.setUsagePattern( QGLBuffer::StaticDraw ); mVertexBuffer.allocate( mVertices.data(), mVertices.size()*VertexP3fN3fT2f::size() ); mVertexBuffer.release(); // indices QVector<unsigned int> indices; for( int h=0; h<mMapSize.height()-1; ++h ) { for( int w=0; w<mMapSize.width(); ++w ) { indices.push_back( w + h*(unsigned int)mMapSize.width() ); indices.push_back( w + (h+1)*(unsigned int)mMapSize.width() ); } } mIndexBuffer = QGLBuffer( QGLBuffer::IndexBuffer ); mIndexBuffer.create(); mIndexBuffer.bind(); mIndexBuffer.setUsagePattern( QGLBuffer::StaticDraw ); mIndexBuffer.allocate( indices.data(), indices.size()*sizeof(unsigned int) ); mIndexBuffer.release(); }