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); } }
Terrain::Terrain(const SystemBody *body) : m_body(body), m_seed(body->seed), m_rand(body->seed), m_heightMap(0), m_heightMapScaled(0), m_heightScaling(0), m_minh(0) { // load the heightmap if (m_body->heightMapFilename) { RefCountedPtr<FileSystem::FileData> fdata = FileSystem::gameDataFiles.ReadFile(m_body->heightMapFilename); if (!fdata) { fprintf(stderr, "Error: could not open file '%s'\n", m_body->heightMapFilename); abort(); } ByteRange databuf = fdata->AsByteRange(); // read size! Uint16 v; // XXX unify heightmap types switch (m_body->heightMapFractal) { case 0: { bufread_or_die(&v, 2, 1, databuf); m_heightMapSizeX = v; bufread_or_die(&v, 2, 1, databuf); m_heightMapSizeY = v; m_heightMap = new Sint16[m_heightMapSizeX * m_heightMapSizeY]; bufread_or_die(m_heightMap, sizeof(Sint16), m_heightMapSizeX * m_heightMapSizeY, databuf); break; } case 1: { // 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; // 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; m_heightMapScaled = new Uint16[m_heightMapSizeX * m_heightMapSizeY]; bufread_or_die(m_heightMapScaled, sizeof(Uint16), m_heightMapSizeX * m_heightMapSizeY, databuf); 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(m_body->m_volatileLiquid.ToDouble(), 0.0, 1.0); m_icyness = Clamp(m_body->m_volatileIces.ToDouble(), 0.0, 1.0); m_volcanic = Clamp(m_body->m_volcanicity.ToDouble(), 0.0, 1.0); // height scales with volcanicity as well const double rad = m_body->GetRadius(); // calculate max height if ((m_body->heightMapFilename) && m_body->heightMapFractal > 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 { m_maxHeightInMeters = std::max(100.0, (9000.0*rad*rad*(m_volcanic+0.5)) / (m_body->GetMass() * 6.64e-12)); if (!isfinite(m_maxHeightInMeters)) m_maxHeightInMeters = rad * 0.5; // ^^^^ max mountain height for earth-like planet (same mass, radius) // and then in sphere normalized j**z } m_maxHeight = std::min(1.0, m_maxHeightInMeters / rad); //printf("%s: max terrain height: %fm [%f]\n", m_body->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 * m_body->m_metallicity.ToFloat()); g = std::max(b, g * m_body->m_metallicity.ToFloat()); 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 * m_body->m_metallicity.ToFloat()); g = std::max(b, g * m_body->m_metallicity.ToFloat()); 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 * m_body->m_life.ToFloat()); b *= (1.0-m_body->m_life.ToFloat()); 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 * m_body->m_life.ToFloat()); b *= (1.0-m_body->m_life.ToFloat()); 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); } }