Deprecated: Creation of dynamic property Fusion::$sanitize is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/themes/Avada/includes/lib/inc/class-fusion.php on line 143

Deprecated: Creation of dynamic property Avada_Privacy_Embeds::$embed_defaults is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/themes/Avada/includes/class-avada-privacy-embeds.php on line 308

Deprecated: Creation of dynamic property FusionBuilder::$fusion_builder_gutenberg is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-builder.php on line 1895

Deprecated: Creation of dynamic property FusionBuilder::$dynamic_data is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-builder.php on line 1897

Deprecated: Creation of dynamic property FusionSC_Column::$shortcode_attr_id is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-column-element.php on line 123

Deprecated: Creation of dynamic property FusionSC_Column::$shortcode_classname is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-column-element.php on line 124

Deprecated: Creation of dynamic property FusionSC_Column::$shortcode_name is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-column-element.php on line 125

Deprecated: Creation of dynamic property FusionSC_Column::$content_filter is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-column-element.php on line 126

Deprecated: Creation of dynamic property FusionSC_ColumnInner::$shortcode_attr_id is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-column-element.php on line 123

Deprecated: Creation of dynamic property FusionSC_ColumnInner::$shortcode_classname is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-column-element.php on line 124

Deprecated: Creation of dynamic property FusionSC_ColumnInner::$shortcode_name is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-column-element.php on line 125

Deprecated: Creation of dynamic property FusionSC_ColumnInner::$content_filter is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-column-element.php on line 126

Deprecated: Creation of dynamic property FusionSC_RowInner::$shortcode_attr_id is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php on line 39

Deprecated: Creation of dynamic property FusionSC_RowInner::$shortcode_classname is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php on line 40

Deprecated: Creation of dynamic property FusionSC_RowInner::$shortcode_name is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php on line 41

Deprecated: Creation of dynamic property FusionSC_RowInner::$content_filter is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php on line 42

Deprecated: Creation of dynamic property FusionSC_Row::$shortcode_attr_id is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php on line 39

Deprecated: Creation of dynamic property FusionSC_Row::$shortcode_classname is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php on line 40

Deprecated: Creation of dynamic property FusionSC_Row::$shortcode_name is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php on line 41

Deprecated: Creation of dynamic property FusionSC_Row::$content_filter is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php on line 42

Deprecated: str_replace(): Passing null to parameter #3 ($subject) of type array|string is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-core/fusion-slider/class-fusion-slider.php on line 77

Warning: Cannot modify header information - headers already sent by (output started at /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php:39) in /home/mherbold/public_html/bravearmy.com/wp-includes/rest-api/class-wp-rest-server.php on line 1831

Warning: Cannot modify header information - headers already sent by (output started at /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php:39) in /home/mherbold/public_html/bravearmy.com/wp-includes/rest-api/class-wp-rest-server.php on line 1831

Warning: Cannot modify header information - headers already sent by (output started at /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php:39) in /home/mherbold/public_html/bravearmy.com/wp-includes/rest-api/class-wp-rest-server.php on line 1831

Warning: Cannot modify header information - headers already sent by (output started at /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php:39) in /home/mherbold/public_html/bravearmy.com/wp-includes/rest-api/class-wp-rest-server.php on line 1831

Warning: Cannot modify header information - headers already sent by (output started at /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php:39) in /home/mherbold/public_html/bravearmy.com/wp-includes/rest-api/class-wp-rest-server.php on line 1831

Warning: Cannot modify header information - headers already sent by (output started at /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php:39) in /home/mherbold/public_html/bravearmy.com/wp-includes/rest-api/class-wp-rest-server.php on line 1831

Deprecated: Creation of dynamic property FusionSC_Column::$is_nested is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-column-element.php on line 553

Deprecated: Creation of dynamic property FusionSC_FusionText::$params is deprecated in /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/shortcodes/fusion-text.php on line 127

Warning: Cannot modify header information - headers already sent by (output started at /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php:39) in /home/mherbold/public_html/bravearmy.com/wp-includes/rest-api/class-wp-rest-server.php on line 1831

Warning: Cannot modify header information - headers already sent by (output started at /home/mherbold/public_html/bravearmy.com/wp-content/plugins/fusion-builder/inc/class-fusion-row-element.php:39) in /home/mherbold/public_html/bravearmy.com/wp-includes/rest-api/class-wp-rest-server.php on line 1831
{"id":448,"date":"2018-10-01T11:29:37","date_gmt":"2018-10-01T18:29:37","guid":{"rendered":"http:\/\/bravearmy.com\/starflight\/?p=448"},"modified":"2018-10-02T11:13:37","modified_gmt":"2018-10-02T18:13:37","slug":"final-planet-generator-non-gas-giants","status":"publish","type":"post","link":"https:\/\/bravearmy.com\/starflight\/2018\/10\/01\/final-planet-generator-non-gas-giants\/","title":{"rendered":"Final Planet Generator (Non-Gas Giants)"},"content":{"rendered":"

My fellow Arthlings!<\/p>\n

Over the past couple of weeks, I have been working on the planet generator for this Starflight remake.\u00a0 As many of you will know, the planet generator for the original game was one of their proudest moments.\u00a0 Inside the cover of the original Starflight box, there is a blurb about their planet builder:<\/p>\n

\n

About nine months after we started the actual programming, we came up with the idea for the fractal generator.\u00a0 A fractal generator so powerful that it could create surfaces in space.\u00a0 It took 6 man-years to create the technology, but it gave us the ability to cram 800 complex and unique planets into each game, instead of the 50 we’d had before.\u00a0 There are so many that even we haven’t explored them all.<\/p>\n<\/blockquote>\n

On the back of the box, you will also note that they say it took 15 man-years to create the entire game.\u00a0 If you do the math, this means a full 40% of the entire development time was spent on just creating this fractal planet generator for the original game.<\/p>\n

So, as not to insult the spirit of this game, I decided to dedicate a lot of time into also creating a fractal planet generator for this remake.\u00a0 And lo and behold, here it is!<\/p>\n

The generator starts with the original color and key of the planet.\u00a0 The color and key are grabbed from screenshots taken while in orbit around the planet and the Captain > Land menu item is selected.<\/p>\n

\"Screenshot\"<\/a><\/p>\n

The first image is the original color extracted from the screenshot, and the second image is the height map extrapolated from the color key in the screenshot.\u00a0 These images are 48 by 24 in size.\u00a0 Not a lot of detail!<\/p>\n

\"Original<\/a>\u00a0\"Original<\/a><\/p>\n

The next image shows what our 3D planet would look like with the original color map applied to it, without any modification.<\/p>\n

\"\"<\/a><\/p>\n

Step 1 – Padding<\/h3>\n

The first step is to add some padding to the top and bottom (North and South) of the original color maps.\u00a0 Why?\u00a0 Because these maps are supposed to be equirectangular maps that are applied to a spherical object.\u00a0 Let’s take a look at a real equirectangular map (see first image below).\u00a0 Notice the North and South ends of the map are stretched out horizontally.\u00a0 The white snow of Antarctica stretches all the way from the West end to the East end of the map.\u00a0 This is because those ends of the map actually correspond to a single point on the globe!\u00a0 So, what happens if we have multiple colors on those ends of the map?\u00a0 You get pole pinching (see the second image).<\/p>\n

\"Equirectangular<\/a>\u00a0\"Pinched<\/a><\/p>\n

To avoid pinched poles, we add some padding to the top and bottom of the original map.\u00a0 What we pad the top and bottom with are selected by determining what the most used colors at the poles are.\u00a0 Here is the result – compare it with the original height map.\u00a0 I add 3 rows of padding on each side, making the map now 48 by 30 in size.<\/p>\n

\"Padded<\/a><\/p>\n

Step 2 – Contours<\/h3>\n

The next step is to increase the size and resolution of this height map.\u00a0 I have decided to implement a routine that I call “contours” generation.\u00a0 What it does is it sub-divides each pixel in the original color map into 2 by 2, and then each sub-pixel is replaced by either an “outside corner,” or an “inside corner,” or a “solid corner” tile based on the 3 by 3 pattern for each sub-pixel.\u00a0 The following images are the inside and outside corner tile images.\u00a0 The solid corner is just a solid fill.\u00a0 Note that these tiles are 21 by 17 in resolution.\u00a0 The reason is explained below.<\/p>\n

\"Inside<\/a> \"Outside<\/a><\/p>\n

Our target map resolution is 2048 by 1024.\u00a0 Since our height map currently has a resolution of 48 by 30, this means we need to scale up by a factor of 42 by 34.\u00a0 This will make the contours map 2016 by 1020.\u00a0 Since each pixel in the original map is subdivided into 2 by 2, this means each tile needs to be half the size of the scale factor, meaning they need to be 21 by 17 each.\u00a0 The following image shows the result of the contours generation step.<\/p>\n

\"Contours<\/a><\/p>\n

Step 3 – Scale to Power of Two<\/h3>\n

Now that’s looking a lot better already.\u00a0 For the next step, we need to scale that image up a little bit more so that it is truly 2048 by 1024.\u00a0 The naive approach is to just use bilinear filtering to scale it up.\u00a0 While that will work, it will blur the image just a tad bit.\u00a0 We want to keep this image as crisp as possible.\u00a0 So, instead of using bilinear filtering to scale the image up, I pad the top and bottom with rows of the same elevation value as used in step 2.\u00a0 That takes our vertical resolution from 1020 to 1024 by adding 4 rows.\u00a0 For the horizontal scaling, I duplicate some columns.\u00a0 I very cleverly select only columns that are on the centerline of each 2×2 tile to duplicate.\u00a0 That avoids duplicating edges, which would be very noticeable.\u00a0 The first image below is the result.\u00a0 I bet you can’t tell the difference between that image and the one above.\u00a0 The second image below indicates which columns I selected to duplicate (click on it to zoom in).\u00a0 The third image shows what a 3D rendering of the planet looks like now.<\/p>\n

\"Scale\u00a0<\/a>\"Duplicated<\/a>\u00a0\"\"<\/a><\/p>\n

Step 4 – Blur Pass<\/h3>\n

Now that we have a clean elevation map with a power of two horizontal and vertical resolution, it’s time to start doing some really interesting things to it.\u00a0 The first thing I do to it is to use a Gaussian blur filter to blur the elevation map.\u00a0 I blur it twice, first with a large blur kernel, and then with a smaller kernel.\u00a0 In between the two blurs, I reset the elevation to zero, where there is water.\u00a0 This gets me nice big blurs where the mountains should be, and a smaller blur to blend land into water (beaches).\u00a0 The first image below shows the result of the two blur passes. The second image shows an elevation profile, with the red line indicating the original elevation profile, and the blue line indicating the new elevation profile after the blur is complete.\u00a0 The third image shows what a 3D rendering of the planet looks like now.<\/p>\n

\"Gaussian<\/a>\u00a0<\/a>\"Gaussian<\/a>\u00a0\"\"<\/a>\u00a0\"\"<\/a><\/p>\n

Step 5 – Mountains and Hills<\/h3>\n

Now that we have a nice smooth elevation map, it is time to add some details.\u00a0 We want mountains in the higher elevations and hills in the lower and middle elevations.\u00a0 To do this, I apply some fractal noise.\u00a0 For the mountains, I use the “ridged” variation of Perlin noise, and for hills, I use standard Perlin noise.\u00a0 The first image shows the new elevation map after this process has been applied, and the second image shows the new elevation profile.\u00a0 The third image shows what a 3D rendering of the planet looks like now.<\/p>\n

\"Mountains<\/a>\u00a0\"Mountains<\/a><\/p>\n

\"\"<\/a><\/p>\n

Step 6 – Hydraulic Erosion<\/h3>\n

Most people would be content with what has been done so far.\u00a0 Not me!\u00a0 Remember, the original crew spent six man-years developing the original fractal planet generator.\u00a0 SIX!\u00a0 Surely, I can spend a few more hours on this.\u00a0 So… bring in the hydraulic erosion machine!\u00a0 What is hydraulic erosion?\u00a0 It’s just a fancy way of saying soil erosion due to moving liquids (like rain).\u00a0 The problem with using a fractal noise generator to create mountains and hills is that they do not account for the millions of years of soil erosion from rain.\u00a0 Rain carves channels and rivers into solid granite rock.\u00a0 Rain gave us the grand canyon.\u00a0 And so, rain will give us beautiful worlds for this Starflight remake.<\/p>\n

I made it rain, the likes that have not been seen since the days of Noah.\u00a0 Two million, 97 thousand, and 152 drops of rain each planet to be exact; one for each grid point on the elevation map.\u00a0 Each drop of rain is tracked as it hits the terrain and makes it way down to lower elevations.\u00a0 As the raindrop meanders throughout the elevation map, sediment is absorbed and deposited along the way.\u00a0 The full details of how this process works are too much for this blog, but you can find them in the code in Github.<\/p>\n

The following images show the changes in the terrain after some rain.\u00a0 The red areas indicate where sediment has been removed from the terrain.\u00a0 The green areas indicate where sediment has been added to the terrain.\u00a0 The first image shows changes after about 65,000 drops of rain.\u00a0 The second image shows changes after about 1 million drops of rain.\u00a0 The final image shows changes after all 2,097,152 drops of rain have been processed.<\/p>\n

\"Drops<\/a> \"Drops<\/a> \"Drops<\/a><\/p>\n

The next image shows what the terrain height map now looks like.\u00a0 Click on it to zoom in and see the details.<\/p>\n

\"Hydraulic<\/a><\/p>\n

Step 7 – Albedo Pass<\/h3>\n

For our next trick, we will convert the\u00a0terrain height map into colors.\u00a0 We do this by using a massaged version of the color key of the map from the original game.\u00a0 This is a pretty straightforward process.\u00a0 The image below shows the result.<\/p>\n

\"Albedo<\/a><\/p>\n

Step 8 – Effects Pass<\/h3>\n

Now it is time to create the effects texture map.\u00a0 What is an effects texture map?\u00a0 In our case, it is a texture map where the red, green, and blue channels control the parameters of some special effects applied during the shading process.\u00a0 The red channel controls the “roughness” of the terrain.\u00a0 The rougher the terrain is, the duller the specular shine will be.\u00a0 The green channel controls where “water” appears on the map.\u00a0 This channel is used to mask out where the animated water waves will be rendered.\u00a0 And then finally, the blue channel controls the “reflectivity” of the terrain.\u00a0 Water is where the terrain is most reflective – and this channel is somewhat related to the red (“roughness”) channel.\u00a0 The image below shows the effects map.<\/p>\n

\"Effects<\/a><\/p>\n

Step 9 – Normals Pass<\/h3>\n

Everything looks great so far, but we need to add one more texture map.\u00a0 This is called the “normal” map.\u00a0 It describes the facing direction of each point on the surface and affects all of the lighting calculations.\u00a0 The planet generator simply looks at each point on the map and compares its height with all of the surrounding points on the map, and figures out from that what direction that point on the surface is facing.\u00a0 The result is coded as a color map, but it is not actually color data.\u00a0 The colors are actually unit vectors, where the red, green, and blue channels represent the X, Y, and Z components of the vector.\u00a0 Here is the normal map image.<\/p>\n

\"Normal<\/a><\/p>\n

The Final Result<\/h3>\n

Put it all together, and this is what we have.<\/p>\n

\"Final<\/a><\/p>\n

I have implemented multi-threaded processing for most of the steps of the planet generator.\u00a0 Even with everything nicely threaded, it still takes up to 2 minutes to generate all of the texture maps on my 10-core Area-51 Alienware machine.\u00a0 Do the math – 811 planets x 2 minutes = a little more than 24 hours to generate texture maps for every planet in the game.\u00a0 But really it will be far less than this, as I plan to have a completely different (and much faster) process for gas giants.<\/p>\n<\/div>

<\/div><\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":486,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-448","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-starflight"],"_links":{"self":[{"href":"https:\/\/bravearmy.com\/starflight\/wp-json\/wp\/v2\/posts\/448"}],"collection":[{"href":"https:\/\/bravearmy.com\/starflight\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bravearmy.com\/starflight\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bravearmy.com\/starflight\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bravearmy.com\/starflight\/wp-json\/wp\/v2\/comments?post=448"}],"version-history":[{"count":0,"href":"https:\/\/bravearmy.com\/starflight\/wp-json\/wp\/v2\/posts\/448\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/bravearmy.com\/starflight\/wp-json\/wp\/v2\/media\/486"}],"wp:attachment":[{"href":"https:\/\/bravearmy.com\/starflight\/wp-json\/wp\/v2\/media?parent=448"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bravearmy.com\/starflight\/wp-json\/wp\/v2\/categories?post=448"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bravearmy.com\/starflight\/wp-json\/wp\/v2\/tags?post=448"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}