Jump to content

  • Log In with Google      Sign In   
  • Create Account


Member Since 26 Oct 2006
Offline Last Active May 18 2016 11:45 AM

#5291293 Planet Rendering: Spherical Level-of-Detail in less than 100 lines of C++

Posted by spacerat on 12 May 2016 - 09:32 AM



I added a textured version to Github (see screenshot). Its much faster than the previous version that calculates the terrain in the shader.


For the next version, I plan to generate the meshes and textures on GPU or CPU and stream them into the GPU for rendering, basically caching the tiles.



Attached Thumbnails

  • Clipboard01.png

#5288943 Planet Rendering: Spherical Level-of-Detail in less than 100 lines of C++

Posted by spacerat on 27 April 2016 - 11:17 AM

Ok, just updated the code.


Here a brief summary

  • Changed to double on cpu side
  • Relative coords are used for close tiles
  • Speed and altitude are displayed in km
  • Movement is now via wsad 
  • Mousewheel adjusts the camera speed
  • Camera near/far is adjusted with regard to the camera altitude
  • The code is now split in two parts: simple (100 line version) and complex (patch based version) 

Left to do is to change to texture based heightmaps - hopefully this will improve the imprecision close up



Attached Thumbnails

  • Clipboard01.png
  • Clipboard03.png

#5287806 Planet Rendering: Spherical Level-of-Detail in less than 100 lines of C++

Posted by spacerat on 20 April 2016 - 11:59 AM

@Krypt0n: Yes, mega meshes is indeed a very nice paper. I remember that one. 

@rept_ai:  Very nice Demo ! I will look at the source.


Just modified the rendering. Basically it works nice, but once you go up close then float precision is not enough. What is the best solution to that other than splitting the frustum in near / far ?


Seems I need to read 





Edit: The Github code is updated and now also renders a planet with fractal texture+heightmap

Attached Thumbnails

  • planet1.png
  • close.png

#5285565 Planet Rendering: Spherical Level-of-Detail in less than 100 lines of C++

Posted by spacerat on 07 April 2016 - 05:05 AM

Thx for the tip :)

The edge idea was good.

Just adopted it to the code. 


Your bezier and subdivision samples look nice. I wonder if its easy to make it to a recursive approach (looks like uniform subdivision in the screenshots)


For the planet thats not needed at the moment.


1. Yes, due to normalizing it does not work immediately for the current code, but you could use  a displacement map and combine rendering with a tessellation shader.


2. Yes, fly though is in progress. First I needed some basic rendering code however. VBO is now already implemented and next is to find a good mapping for the heightmap to the surface. That will be another challenge to create a seamless heightmap that has same scaling all over the planet.




The new mesh now looks like the following:




Also using patches is possible right away without further meshes that fix LOD boundaries.




The new code looks like this:

static void draw_recursive(vec3f p1,vec3f p2,vec3f p3, vec3f center , float size=1)
    float ratio_size = size * gui.screen[0].slider["lod.ratio"].val; // default : 1
    float minsize = gui.screen[0].slider["detail"].val;    // default : 0.01
    vec3f edge_center[3] = { (p1 + p2) / 2, (p2 + p3) / 2, (p3 + p1) / 2 };
    bool edge_test[3]; double angle[3];
    loopi(0, 3)
        double dot = edge_center[i].dot(center);
        angle[i] = acos(clamp(dot, -1, 1)) / M_PI;
        edge_test[i] = angle[i] > ratio_size;
    if (min(angle[0], min(angle[1], angle[2])) > 0.5) return;//culling
    if ((edge_test[0] && edge_test[1] && edge_test[2]) || size < minsize)
        if (gui.screen[0].checkbox["patches"].checked)
            draw_patch(p1, p2, p3); 
            draw_triangle(p1, p2, p3);
    // Recurse
    vec3f p[6] = { p1, p2, p3, edge_center[0], edge_center[1], edge_center[2] };
    int idx[12] = { 0, 3, 5,    5, 3, 4,    3, 1, 4,    5, 4, 2 };
    bool valid[4] = { 1, 1, 1, 1 };
    if (edge_test[0]){ p[3] = p1; valid[0] = 0; } // skip triangle 0 ?
    if (edge_test[1]){ p[4] = p2; valid[2] = 0; } // skip triangle 2 ?
    if (edge_test[2]){ p[5] = p3; valid[3] = 0; } // skip triangle 3 ?
    loopi(0, 4) if (valid[i])
            p[idx[3 * i + 0]].norm(), 
            p[idx[3 * i + 1]].norm(),
            p[idx[3 * i + 2]].norm(),
            center,size/2 );

#5285454 Planet Rendering: Spherical Level-of-Detail in less than 100 lines of C++

Posted by spacerat on 06 April 2016 - 11:31 AM

it's a good start, you should give it a few more iterations.

1.there is a simpler way to tessellate to a sphere, starting with a tetrahedron (4 triangles) and halving the edges of the triangles at every step.
your tessellation has the classical t-junction issue, which usually takes the most code to fix. with tetrahedrons, when you evaluate per-edge, it's actually very simple to get a closed mesh.

2.usually you would increase the tessellation at the silhouette, not at the center (but that decision probably depends on a lot of factors)


Good point.


1. I just changed the algorithm to icosahedron. Now its just 69 lines of code and also a bit faster. The algorithm is now working for an arbitrary triangle mesh. You could also throw a custom 3DS MAX created mesh at it basically.


2. The Tessellation is intentionally in the center. In a common game, you would be walking on the planet surface, which is the point where the highest level of detail is in the demo. The actual camera in the demo is just for demonstration purpose, to have a better overview. If you want the most detail at the silhouette, that would be possible too, but you need to modify the recursion criteria to the following:


if (fabs(0.5-dist) > double(ratio)*double(size) || size < minsize) 


The result is as follows



#5285408 Planet Rendering: Spherical Level-of-Detail in less than 100 lines of C++

Posted by spacerat on 06 April 2016 - 04:34 AM

This is a simple tutorial of how to render planet using a triangle subdivision approach. The source is optimized for compactness and to make the algorithm easy to understand. The algorithm renders an icosahedron with 12 triangles, each of which is being sub-divided using a recursive function.


If you would render the planet in a game engine, you would have to render a triangle patch for NxN triangles with a VBO inside the draw_triangle function, rather than a single triangle with gl immediate mode.

The center of detail is where the camera would be in the game. The camera in the demo is above for demonstration purpose.

What the code is :

  • A simple demo to show how a planet renderer with subdivision works
  • As short as possible so you can experiment with the algorithm
  • Easy to understand

What the code is not:

  • A ready to use planet rendering library with shaders frustum culling etc
  • A way to render a planet with best performance
  • A demonstration of modern high performance gl rendering





struct World
    static void draw_triangle(vec3f p1, vec3f p2, vec3f p3)
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glVertex3f(p1.x, p1.y, p1.z);
        glVertex3f(p2.x, p2.y, p2.z);
        glVertex3f(p3.x, p3.y, p3.z);
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    static void draw_recursive(vec3f p1,vec3f p2,vec3f p3, vec3f center , float size=1)
        float ratio = gui.screen[0].slider["lod.ratio"].val; // default : 1
        float minsize = gui.screen[0].slider["detail"].val;  // default : 0.01
        double dot = double(((p1+p2+p3)/3).norm().dot(center));
        double dist = acos(clamp(dot, -1, 1)) / M_PI;
        if (dist > 0.5) return;//culling
        if (dist > double(ratio)*double(size) || size < minsize) 
            draw_triangle(p1, p2, p3); 
        // Recurse
        vec3f p[6] = { p1, p2, p3, (p1 + p2) / 2, (p2 + p3) / 2, (p3 + p1) / 2 };
        int idx[12] = { 0, 3, 5, 5, 3, 4, 3, 1, 4, 5, 4, 2 };
        loopi(0, 4)
                p[idx[3 * i + 0]].norm(), 
                p[idx[3 * i + 1]].norm(),
                p[idx[3 * i + 2]].norm(),
                center,size/2 );
    static void draw(vec3f center)
        // create icosahedron
        float t = (1.0 + sqrt(5.0)) / 2.0;
        std::vector<vec3f> p({ 
            { -1, t, 0 }, { 1, t, 0 }, { -1, -t, 0 }, { 1, -t, 0 },
            { 0, -1, t }, { 0, 1, t }, { 0, -1, -t }, { 0, 1, -t },
            { t, 0, -1 }, { t, 0, 1 }, { -t, 0, -1 }, { -t, 0, 1 },
        std::vector<int> idx({ 
            0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,
            1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 10, 7, 6, 7, 1, 8,
            3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,
            4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1
        loopi(0, idx.size() / 3)
                p[idx[i * 3 + 0]].norm(), // triangle point 1
                p[idx[i * 3 + 1]].norm(), // triangle point 2
                p[idx[i * 3 + 2]].norm(), // triangle point 3

#5260019 GameNet: Simple RPC System for Games - Sample Game included (C++11)

Posted by spacerat on 01 November 2015 - 01:42 PM


GameNet  is a client / server RPC system using newest C++11 templates to make the life easier for you. When registering RPCs, function parameters are auto-detected from the function pointer. When calling RPCs, variardic templates allow extracting data from arbitrary parameter counts.
* Supports GLM datatypes for use in 3D Games
* Supported data types : (u)char,(u)short,(u)int,float,double,vector,map, GLM vec,mat and quat
* Support for data type nesting like map [ string , vector ]
* RPC Data-type mismatch or wrong number of parameters are stated as error to ease debugging
* Numbers are automatically sent as the smallest possible datatype (byte, short , .. )
* Reliable and unreliable calls possible
* Function pointers of remote functions are not required
* Based on ENet
* Compression (Enet standard)
* Hack-safe - illegal packets are discarded
* Tested on Cygwin and Windows, should compile in Linux too
* Byte order preserved (hton/ntoh)
* RPCs cannot be class member functions
* No encryption
* Only void functions supported. Non-void functions were tested but complicated everything.
* Client to Client connections are not supported
Example Game Features
* Lobby 
* Multiple Games
* Handle spawning/removing of game objects
* Simple Shooting functionality
* Intentionally in text mode to make it as simple as possible for you to adapt the code
Example Usage
Server Side:
    NetServer server;
    void login(uint clientid, string name, string password)
        // clientid is attached as first parameter for server functions
        server.call(clientid, "set_pos", vec3(1,2,3));    
    int main()
        rpc_register_local(server.get_rpc(), login);
        rpc_register_remote(server.get_rpc(), set_pos);    
        core_sleep(10000) ; // wait client to do stuff
Client Side:
    NetClient client;
    void set_pos(vec3 pos)
        // do something
    int main()
        rpc_register_remote(client.get_rpc(), login);
        rpc_register_local(client.get_rpc(), set_pos);
        client.connect("localhost", 12345);
        client.call("login", "myname", "pass");
        while(1) client.process();
Screenshots of the included example game: 

#5245092 20 years of Voxel Engines - with Source Code

Posted by spacerat on 08 August 2015 - 07:47 AM

If you have technical questions, let me know. I might be able to explain.

#5244789 20 years of Voxel Engines - with Source Code

Posted by spacerat on 06 August 2015 - 04:36 AM

Since 1995 I have been working on various technologies for rendering voxels. I have decided to share the code and uploaded it to GitHub. Feel free to browse through and test the different technologies:

#5232442 Automatic UV map generation: OpenNL LSCM doesnt work - why ?

Posted by spacerat on 02 June 2015 - 01:31 PM

Ok, finally success with DX UV atlas + AMD/ATIs normal mapper after adding obj file support

No idea why they didnt natively support obj or similar common file types and instead only .sdkmesh (DX UV atlas) and  .nmf for ATIs normal mapper.

Attached Thumbnails

  • Clipboard01.png

#5192343 Procedural Map Generation

Posted by spacerat on 11 November 2014 - 07:47 PM


#5162779 Legal stuff when using free art / models in an own game

Posted by spacerat on 25 June 2014 - 09:27 AM

I am wondering how and to what extent free art / sprites / 3d models can be used in an own game.


For example, lets say in the case of an animated mickey-mouse sprite, where the readme inside states:


"This sprite may be freely distributed UNALTERED.  Which means, you can't pull the readme file out of the zip,or add your own stuff to it and pass it along as your own!"


I believe including it in the game an selling it bundled will not be allowed.

On the other hand, if the user downloads the free model himself and installs it in the game-folder, there wont be any problem.


Now where is the boundary ?

Lets consider the following cases:


Like what happens if the game does not contain this sprite and 


-downloads and installs it automatically after installation ?

-downloads and installs it on request (pushing a button) ?

-contains a script to download and install the sprite ?

#5153872 High Speed Quadric Mesh Simplification without problems (Resolved)

Posted by spacerat on 15 May 2014 - 07:31 PM

Thank you Spiro!


I just updated the code

  • Now everything is commented
  • Borders are preserved well in the new version
  • Added a new parameter (aggressiveness) to guide the collapsing speed. If the parameter is lower (5 instead of 7 e.g.), more iterations will be required and the result is closer to the optimum.
  • The matrix is reduced from 16 to 10 elements since its symmetric

Here a comparison to other methods, and the updated Code (got a bit larger due to border tests)  DOWNLOAD.



#5153306 High Speed Quadric Mesh Simplification without problems (Resolved)

Posted by spacerat on 13 May 2014 - 08:04 AM

Thanks a lot for the hints and the code. Now it works well cool.png


I have updated the DOWNLOAD


You are right - the error updating was the issue. I have resolved that now.

The main problem was apparently that i needed to compute the quadric per vertex from scratch each couple of iterations.

For the other iterations I simply added the quadric of the vertex to collapse to the target vertex and updated all edges.


Left to do is a check if a vertex is an border vertex and to add support for attributes.


Compared to Meshlab, its about 4x faster. 

It is several times slower than vertex clustering ( Rapid Simplification of Multi-Attribute Meshes HPG Paper ),

but has much higher quality due to the quadric metric.


Even there is no sorting and just a steadily increased threshold, the result is still good.

Left is the program output (5 seconds), right the Meshlab result (20 seconds) for reducing 2 million triangles down to 20k.



#5153215 High Speed Quadric Mesh Simplification without problems (Resolved)

Posted by spacerat on 12 May 2014 - 10:47 PM

Ok, now the new method almost works. I found two reasons why it didnt work before:

  • I recalculated the normal after collapsing - there its better to keep the original
  • Flipping happened after the triangle became almost line shaped and the normal was 0 

The new version avoids this as follows:

  • The new normal is always tested against the original normal
  • The inner angle between 2 edges of the new triangle is tested to not get too line shaped (dot > 0.99)

Remaining problem:

  • The edge collapse stops now even it does not reach the target number of triangles, as any further collapse would lead to violation of the above two tests.

Now, what is the best solution to that ?


Here the new version :  Simplify.h  ( including sorting by error and stopping at a certain number of triangles )