Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411283 Posts in 69325 Topics- by 58380 Members - Latest Member: bob1029

March 29, 2024, 07:40:48 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Procedural Planetary Generation
Pages: [1] 2 3 ... 8
Print
Author Topic: Procedural Planetary Generation  (Read 25154 times)
Hedgehodg
Level 1
*


...


View Profile
« on: April 27, 2012, 02:35:40 AM »

Hello!

I've had this idea for a game for quite some time now, and started working on it some time ago, but haven't lately because I got stuck. Anyhow, my idea for it is to have planets which are procedurally generated (like shown in the following video

), I have posted numerous posts on SE regarding it, but, no one really seems that willing to help...

So, I thought I would start off by constructing something like the following first (

), since it seems like a part of what I am trying to achieve, even better, I found the source code for the project (https://github.com/unconed/NFSpace, if anyone could convert that to c# that would be great :D LOL), but it's in C++ and uses Orge, and I am a n00b when it comes to understanding code in foreign languages. So my aim is to create something like in the aforementioned video, in C# and XNA. How would I structure the program? Namely how would I go about creating the Planet object, making a renderable and dynamic quad-tree mesh (and class), transforming the planet from a cube into a sphere, separating a parent node into child nodes, and doing the same for their respective polygons? And applying a height map to the planet (I don't even know where to start).

If anyone can help me/get me started on this I would really appreciate it!

Thanks!
Logged

Previously known as "darestium"...
eigenbom
Level 10
*****


@eigenbom


View Profile WWW
« Reply #1 on: April 27, 2012, 02:52:12 PM »

Its a massive undertaking, and depending on your experience may take a few months of work to get something decent. I would start off by generating a single tessellated quad and perturbing it with a a function like ridged multifractal noise. The book Texturing & Modelling: A Procedural Approach has more than enough info on procedural algorithms for landscape generation.

After that, the next step is to generate a sphere mesh, probably using a subdivided icosahedron (read up on geometry for this). You can then apply the landscape procedure to that. Then after that, you'll want texturing etc., which will be XNA specific.

Then comes the hard bit - when you zoom to the surface of the planet, you'll want to use a different mesh generation technique, cull it against the frustum, refine it based on distance from the camera, etc. This is very trick stuff, so I think you should start at the top and see where u get from there ...
Logged

Hedgehodg
Level 1
*


...


View Profile
« Reply #2 on: April 27, 2012, 04:36:25 PM »

What do you mean by "Single tessellated quad"? From what I understand it's an alternative to Chunked LOD? Is this correct? And if so how would I go about creating the "Single tessellated quad", I have very little experience in this area of 3d programming, all the stuff I've done has been with voxels and basic primitive objects. 
Logged

Previously known as "darestium"...
eigenbom
Level 10
*****


@eigenbom


View Profile WWW
« Reply #3 on: April 27, 2012, 07:05:27 PM »

Oh I just meant a uniformly subdivided quad, a grid.. the one on the left ...



Logged

Hedgehodg
Level 1
*


...


View Profile
« Reply #4 on: April 27, 2012, 10:29:51 PM »

And just make it the closer you are to the quad the more children it has  Smiley?
Logged

Previously known as "darestium"...
kamac
Level 10
*****


Notoriously edits his posts


View Profile
« Reply #5 on: April 27, 2012, 10:43:24 PM »

Nice!

I've always dreamt of a game where you can be an astronaut...

 Tears of Joy
Logged

eigenbom
Level 10
*****


@eigenbom


View Profile WWW
« Reply #6 on: April 27, 2012, 10:43:48 PM »

I'd leave the adaptive tessellation until last. First get the terrain working on a quad with a fixed number of subdivisions, then get it working on a sphere, and finally look into the adaptive stuff.  Working up from the ground like this will make the structure of the whole program clearer.
Logged

Hedgehodg
Level 1
*


...


View Profile
« Reply #7 on: April 27, 2012, 11:45:13 PM »

So in order for the terrain to be adaptable for the later stages of the project would I have to structure the classes like so:

Quad (class)
    Node[] nodes

    ... Getters/Setters

Node (class)
    Mesh mesh;
   
    ... Getters/Setters

Mesh (class)
    Vertex vertices;
    Index indices;

    ... Getters/Setters

Planet (class)
    Quad quads;

    draw()  {
        foreach (Quad quad in quads)  {
            foreach (Mesh mesh in quad.Nodes.Mesh)  {
                // Render mesh
            }
        }
    }

I doubt that would work though... Would you mind providing me with a better blueprint of the structure of the program? If you could I would really appreciate it Wink
Logged

Previously known as "darestium"...
eigenbom
Level 10
*****


@eigenbom


View Profile WWW
« Reply #8 on: April 27, 2012, 11:54:46 PM »

i can suggest to just try and grow the program naturally. if you've got design paralysis then just start with something simple, implement it, find its weaknesses etc, then rebuild it to add the next part. It will require refactoring things later, so get your code on Smiley
Logged

Hedgehodg
Level 1
*


...


View Profile
« Reply #9 on: April 28, 2012, 08:40:31 PM »

I've started implementing it and have gone for a structure like the following:

Code:
class Quad {
        GraphicsDevice device;

        private int rows = 1;
        private int columns = 1;

        private float width = 64;
        private float height = 64;

        private Quad[,] quads;
        private Quad parent;

        private Heightmap heightmap;

        private List<VertexPositionColor> vertices;
        private List<int> indices;

        private VertexBuffer vertexBuffer;
        private IndexBuffer indexBuffer;

        private Vector2 index;

        public List<VertexPositionColor> Vertices {
            get { return vertices; }
            set { }
        }

        public List<int> Indices {
            get { return indices; }
            set { }
        }

        public VertexBuffer VertexBuffer {
            get { return vertexBuffer; }
            set { }
        }

        public IndexBuffer IndexBuffer {
            get { return indexBuffer; }
            set { }
        }

        public Vector2 Index {
            get { return index; }
            set { index = value; }
        }

        public float Width {
            get { return width; }
            set { }
        }
       
        public float Height {
            get { return height; }
            set { }
        }

        public bool HasChildren() {
            return quads == null ? false : true;
        }

        public void SetSize(float width, float height) {
            this.width = width;
            this.height = height;
        }

        public void SetParent(Quad parent) {
            this.parent = parent;
        }

        public void SetHeightmap(Heightmap map) {
            this.heightmap = map;
        }

        public List<Quad> GetChildren() {
            List<Quad> quadList = new List<Quad>();

            if (quads != null) {
                for (int x = 0; x < this.quads.GetLength(0); x++) {
                    for (int y = 0; y < this.quads.GetLength(1); y++) {
                        quadList.AddRange(quads[x, y].GetChildren());

                        OutputBuffer.STRING_BUFFER.Clear();
                    }
                }

                OutputBuffer.Add("Children Count: " + quadList.Count);

                return quadList;
            }

            quadList.Add(this);
            return quadList;
        }

        public Quad(ref GraphicsDevice device) {
            this.device = device;
        }

        public void Construct() {
            if (parent == null) {
                ConstructVertices();

                ConstructBuffers();
            }
        }

        private void ConstructVertices() {
            int indexOffset = 0;

            vertices = new List<VertexPositionColor>();
            indices = new List<int>();

            foreach (Quad quad in GetChildren()) {
                if (quad.HasChildren() == false) {

                    Vector3 backLeft = new Vector3(quad.Index.X * quad.Width, 0, quad.Index.Y * quad.Height + quad.Height);
                    Vector3 backRight = new Vector3(quad.Index.X * quad.Width + quad.Width, 0, quad.Index.Y * quad.Height + quad.Height);
                    Vector3 frontLeft = new Vector3(quad.Index.X * quad.Width, 0, quad.Index.Y * quad.Height);
                    Vector3 frontRight = new Vector3(quad.Index.X * quad.Width + quad.Width, 0, quad.Index.Y * quad.Height);

                    vertices.Add(new VertexPositionColor(frontLeft, Color.White));
                    vertices.Add(new VertexPositionColor(frontRight, Color.White));
                    vertices.Add(new VertexPositionColor(backLeft, Color.White));
                    vertices.Add(new VertexPositionColor(backRight, Color.White));

                    AddIndices(indexOffset);
                    indexOffset += 4;
                }
            }
        }

        private void AddIndices(int i) {
            indices.AddRange(new int[] { 0 + i, 2 + i, 3 + i, 3 + i, 1 + i, 0 + i }.ToList());
        }

        private void ConstructBuffers() {
            vertexBuffer = new VertexBuffer(device, VertexPositionColor.VertexDeclaration, vertices.Count, BufferUsage.WriteOnly);
            vertexBuffer.SetData(vertices.ToArray());
            indexBuffer = new IndexBuffer(device, typeof(int), indices.Count, BufferUsage.WriteOnly);
            indexBuffer.SetData(indices.ToArray());
        }

        public void ConstructChildren() {
            if (quads == null) {
                quads = new Quad[2, 2];

                for (int x = 0; x < quads.GetLength(0); x++) {
                    for (int y = 0; y < quads.GetLength(1); y++) {
                        quads[x, y] = new Quad(ref device);
                        quads[x, y].Index = new Vector2(x + index.X * 2, y + index.Y * 2);
                        quads[x, y].SetSize(width / 2, height / 2);
                        quads[x, y].SetParent(this);
                    }
                }

                this.vertices = null;
                this.indices = null;
            } else {
                for (int x = 0; x < quads.GetLength(0); x++) {
                    for (int y = 0; y < quads.GetLength(1); y++) {
                        quads[x, y].ConstructChildren();
                    }
                }
            }
        }
    }


It divides the initial one quad into 4 then 16 but at 64 quads only 16 are shown... I'm still working on it but I think it has to do with the way I offset quads from each other (so at 64 four sets of 16 are drawn on top of each other) - I have their parent and I base their offset on their index (range between 0,1 0,1 and parent index) I'll post back when I figure it out.
« Last Edit: May 04, 2012, 06:53:41 PM by Darestium » Logged

Previously known as "darestium"...
eigenbom
Level 10
*****


@eigenbom


View Profile WWW
« Reply #10 on: April 28, 2012, 09:43:35 PM »

Have you got any procedural terrain generated yet? If not, why not just do the simplest thing for now:
Code:
#define SIZE 64
struct TerrainGrid {
  float height[SIZE][SIZE];
};
TerrainGrid createTerrain(){
  TerrainGrid tg;
  for(int i=0;i<SIZE;i++){
    for(int j=0;j<SIZE;j++){
      tg.height[j][i] = myHeightFunction(i,j);
    } 
  }
}
Logged

Hedgehodg
Level 1
*


...


View Profile
« Reply #11 on: April 28, 2012, 11:57:18 PM »

No I do not. But I asked a question on SE regarding my problem, there are screen shots too...
http://gamedev.stackexchange.com/questions/28247/subdividing-a-quad

« Last Edit: April 29, 2012, 12:38:54 AM by Darestium » Logged

Previously known as "darestium"...
Hedgehodg
Level 1
*


...


View Profile
« Reply #12 on: April 29, 2012, 12:45:16 AM »

As you can see the above is not working, but I'm trying to create the subdivision quad like you said above... But what do you mean by your latest post? I originally had it structured like that - I just had a collection of vertices and indices, and you could easily apply a heightmap to offset them, but then I had another look at your post and thought I was doing it all wrong.
Logged

Previously known as "darestium"...
eigenbom
Level 10
*****


@eigenbom


View Profile WWW
« Reply #13 on: April 29, 2012, 01:37:59 AM »

I didn't mean to confuse you, by subdivided/tessellated I meant "split up into many bits". In your above post you are using a quad-tree to store the data, and are generating the data recursively. You'll eventually want to use a data structure like that, but I was just suggesting to start off simple and investigate some different terrain generation functions ..

It's really up to you what you want to start with..
Logged

Hedgehodg
Level 1
*


...


View Profile
« Reply #14 on: May 01, 2012, 01:39:26 AM »

Yay! I got the tessellating quads working correctly Smiley what would you recommend I work on now? The terrain generation (with noise functions) or start applying the quad to a sphere (somehow) or something else entirely?
Logged

Previously known as "darestium"...
eigenbom
Level 10
*****


@eigenbom


View Profile WWW
« Reply #15 on: May 01, 2012, 02:23:33 AM »

I'd start with the noise functions. first perlin noise, then a fractal version (fbm), then some more advanced method like ridged multifractals.
Logged

fentlewoodlewix
Level 1
*


View Profile WWW
« Reply #16 on: May 02, 2012, 03:23:31 AM »

or start applying the quad to a sphere (somehow) or something else entirely?

As stated elsewhere, start applying the noise functions first.
I wanted to shed some light on the world sphere issue because it may be simpler than you are imagining.

If you take a subdivided cube (like a wireframe rubics cube but it works for any levels of subdivision) and assuming the vectors representing the vertices are from the centre, normalise them, you will get a sphere.



I know this is a very lightweight reply to a big challenge. Let me know if you want more specifics.

sean

p.s. Depending on details can be simple or difficult to apply your noise function to each quad. Remember that the noise functions applied to a quad on a flat plane will assume a direction perpendicualt to the plane (y-axis) if you are applying noise to a quad on a sphere you may need to take into account the normal of the quad (or rotate the whole thing).



Logged
rivon
Level 10
*****



View Profile
« Reply #17 on: May 02, 2012, 03:34:42 AM »

If you take a subdivided cube (like a wireframe rubics cube but it works for any levels of subdivision) and assuming the vectors representing the vertices are from the centre, normalise them, you will get a sphere.
Oh god, so simple? :D
Logged
fentlewoodlewix
Level 1
*


View Profile WWW
« Reply #18 on: May 02, 2012, 03:39:18 AM »

Yay! I got the tessellating quads working correctly Smiley what would you recommend I work on now? The terrain generation (with noise functions) or start applying the quad to a sphere (somehow) or something else entirely?

Congrats! You sounds like you are well on your way.

There is an alternative approach that could be considered.

Follow this link here to the opengl book (yes opengl, i know) and scroll down to the title. An Example: Building an Icosahedron

This image perhaps explains it all in a flash.



There is a example code for recursive subdivision of spheres made from triangles. Clearly the example is just an algorithm and it needs o be engineered into a solution somewhat, but....if we use a variant of the normalise function that has pseudo-random noise added then you can get a unique world very quickly.

(i like this one because it looks all retro, with flat shaded wireframes etc)

hope this helps, if you need more, let me know if i can help...
sean

(edit: oops, fixed broken link)
« Last Edit: May 02, 2012, 07:36:36 AM by sean » Logged
Geeze
Level 5
*****


Totally.


View Profile
« Reply #19 on: May 02, 2012, 06:15:17 AM »

On a sidenote, have you considered using three dimensional noise instead of 2D, it's easy to apply to a. sphere, and is probably less distorted than plain 2D applied to sphere. You could basically loop through all vertices in sphere, calculate noise for that position and apply that as a heightmap.
Logged

Pages: [1] 2 3 ... 8
Print
Jump to:  

Theme orange-lt created by panic