Simple Spherical terrain (improved)

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.

Simple Spherical terrain (improved)

Postby Grz- » Wed Jan 26, 2011 11:34 pm

Hello,

here is a scene node which you can use to get simple spherical terrain (no LOD) out of an heightmap (max heightmap size is 256x255), it is based on this article:

http://ahuynh.posterous.com/article-1-g ... -in-opengl

there is some issues like texture and geometry distorsions on the poles if you use it as it is BUT you can totally fix the texture distorsion by applying a triplanar texturing shader, this is demonstrated in the example, the geometry distorsion can be resolved by smoothing out totally the poles in the heightmap or hide it with objects...

the example provide a GLSL shader featuring triplanar texturing/splatting/rim-lighting which is used for the main planetoid, the "hole" in the planet is done using simple alpha discard in the pixel shader (from the alpha map alpha channel), the asteroids don't use any shaders

also the heightmap alpha channel can be used as an "inverse" heightmap, if a pixel in the heightmap has an alpha value of < 1.0 then as that position the terrain will go down (0.0 = center of the spherical terrain so it will be a big hole with high slope), so you can carve off the surface if you want down to the center, check the heightmap in the example with The GImp or whatever to see how it is done

Screenshots:

Image

Image

Download (bin+src)

CSphericalTerrain.cpp:

cpp Code: Select all
 
#include "CSphericalTerrain.h"
#include "IVideoDriver.h"
#include "ISceneManager.h"
#include "S3DVertex.h"
#include "SMesh.h"
#include "SMeshBuffer.h"
 
namespace irr
{
namespace scene
{
 
//! constructor
CSphericalTerrain::CSphericalTerrain(const io::path& heightMapFileName, ISceneManager* mgr, f32 radius, f32 maxHeight, ISceneNode* parent, s32 id,
            const core::vector3df& position, const core::vector3df& rotation, const core::vector3df& scale)
: ISceneNode(parent, mgr, id, position, rotation, scale), Mesh(0), Radius(radius), MaxHeight(maxHeight/255.0f)
{
    #ifdef _DEBUG
    setDebugName("CSphericalTerrain");
    #endif
 
    io::IReadFile* file = SceneManager->getFileSystem()->createAndOpenFile(heightMapFileName);
    HeightmapFile = file->getFileName();
 
    if(!file)
    {
        printf("Could not load spherical terrain, because file could not be opened.");
    }
    else
        Mesh = createSphericalTerrainMesh(SceneManager->getVideoDriver()->createImageFromFile(file), radius, MaxHeight);
 
    if(file)
        file->drop();
}
 
CSphericalTerrain::CSphericalTerrain(io::IReadFile* heightMapFile, ISceneManager* mgr, f32 radius, f32 maxHeight, ISceneNode* parent, s32 id,
            const core::vector3df& position, const core::vector3df& rotation, const core::vector3df& scale)
: ISceneNode(parent, mgr, id, position, rotation, scale), Mesh(0), Radius(radius), MaxHeight(maxHeight/255.0f)
{
    #ifdef _DEBUG
    setDebugName("CSphericalTerrain");
    #endif
 
    HeightmapFile = heightMapFile->getFileName();
    if(!heightMapFile)
    {
        printf("Could not load spherical terrain, because file could not be opened.");
    }
    else
        Mesh = createSphericalTerrainMesh(SceneManager->getVideoDriver()->createImageFromFile(heightMapFile), radius, MaxHeight);
}
 
//! destructor
CSphericalTerrain::~CSphericalTerrain()
{
    if (Mesh)
        Mesh->drop();
}
 
 
//! renders the node.
void CSphericalTerrain::render()
{
    if (!IsVisible || !SceneManager->getActiveCamera())
        return;
 
    if (!Mesh->getMeshBufferCount())
        return;
           
    video::IVideoDriver* driver = SceneManager->getVideoDriver();
 
    if (Mesh && driver)
    {
        driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
        driver->setMaterial(Mesh->getMeshBuffer(0)->getMaterial());
 
        driver->drawMeshBuffer(Mesh->getMeshBuffer(0));
        if ( DebugDataVisible & scene::EDS_BBOX )
        {
            video::SMaterial m;
            m.Lighting = false;
            driver->setMaterial(m);
            driver->draw3DBox(Mesh->getMeshBuffer(0)->getBoundingBox(), video::SColor(255,255,255,255));
        }
    }
}
 
 
 
//! returns the axis aligned bounding box of this node
const core::aabbox3d<f32>& CSphericalTerrain::getBoundingBox() const
{
    return Mesh ? Mesh->getBoundingBox() : Box;
}
 
 
void CSphericalTerrain::OnRegisterSceneNode()
{
    if (IsVisible)
        SceneManager->registerNodeForRendering(this);
 
    ISceneNode::OnRegisterSceneNode();
}
 
 
//! returns the material based on the zero based index i. To get the amount
//! of materials used by this scene node, use getMaterialCount().
//! This function is needed for inserting the node into the scene hirachy on a
//! optimal position for minimizing renderstate changes, but can also be used
//! to directly modify the material of a scene node.
video::SMaterial& CSphericalTerrain::getMaterial(u32 i)
{
    if (i>0 || !Mesh)
        return ISceneNode::getMaterial(i);
    else
        return Mesh->getMeshBuffer(i)->getMaterial();
}
 
//! returns amount of materials used by this scene node.
u32 CSphericalTerrain::getMaterialCount() const
{
    return 1;
}
 
 
//! Writes attributes of the scene node.
void CSphericalTerrain::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
{
    ISceneNode::serializeAttributes(out, options);
 
    out->addFloat("Radius", Radius);
    out->addFloat("MaxHeight", MaxHeight);
    out->addInt("PolyCountX", PolyCountX);
    out->addInt("PolyCountY", PolyCountY);
}
 
 
//! Reads attributes of the scene node.
void CSphericalTerrain::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
{
    f32 oldRadius = Radius;
    u32 oldPolyCountX = PolyCountX;
    u32 oldPolyCountY = PolyCountY;
    f32 oldMaxHeight = MaxHeight;
 
    Radius = in->getAttributeAsFloat("Radius");
    PolyCountX = in->getAttributeAsInt("PolyCountX");
    PolyCountY = in->getAttributeAsInt("PolyCountY");
    MaxHeight = in->getAttributeAsFloat("MaxHeight");
    // legacy values read for compatibility with older versions
    u32 polyCount = in->getAttributeAsInt("PolyCount");
    if (PolyCountX ==0 && PolyCountY == 0)
        PolyCountX = PolyCountY = polyCount;
 
    Radius = core::max_(Radius, 0.0001f);
 
    if ( !core::equals(Radius, oldRadius) || PolyCountX != oldPolyCountX || PolyCountY != oldPolyCountY || MaxHeight != oldMaxHeight)
    {
        if (Mesh)
            Mesh->drop();
 
        io::IReadFile* file = SceneManager->getFileSystem()->createAndOpenFile(HeightmapFile);
        Mesh = createSphericalTerrainMesh(SceneManager->getVideoDriver()->createImageFromFile(file), Radius, MaxHeight);
        if(file)
            file->drop();
    }
 
    ISceneNode::deserializeAttributes(in, options);
}
 
//! Creates a clone of this scene node and its children.
ISceneNode* CSphericalTerrain::clone(ISceneNode* newParent, ISceneManager* newManager)
{
    if (!newParent)
        newParent = Parent;
    if (!newManager)
        newManager = SceneManager;
 
    io::IReadFile* file = SceneManager->getFileSystem()->createAndOpenFile(HeightmapFile.c_str());
    CSphericalTerrain* nb = new CSphericalTerrain(file, newManager, Radius, MaxHeight, newParent,
        ID, RelativeTranslation);
    if(file)
    {
        file->drop();
    }
 
    nb->cloneMembers(this, newManager);
    nb->getMaterial(0) = Mesh->getMeshBuffer(0)->getMaterial();
 
    if ( newParent )
        nb->drop();
    return nb;
}
 
void CSphericalTerrain::setMaxHeight(f32 maxHeight)
{
    MaxHeight = maxHeight / 255.0f;
    if(Mesh)
        Mesh->drop();
 
    io::IReadFile* file = SceneManager->getFileSystem()->createAndOpenFile(HeightmapFile);
    Mesh = createSphericalTerrainMesh(SceneManager->getVideoDriver()->createImageFromFile(file), Radius, MaxHeight);
    if(file)
        file->drop();
}
 
IMesh* CSphericalTerrain::createSphericalTerrainMesh(video::IImage* heightMap, f32 radius, f32 MaxHeight)
{
    // thanks to Alfaz93 who made his code available for Irrlicht on which
    // this one is based!
 
    // we are creating the sphere mesh and displace it with the heightmap here.
    this->heightMap = heightMap;
   
    if (!heightMap)
        return 0;
 
    const core::dimension2d<u32> hMapSize = heightMap->getDimension();
 
    u32 polyCountX = hMapSize.Width-1;
    u32 polyCountY = hMapSize.Height-1;
    this->MaxHeight = MaxHeight;
 
    if (polyCountX < 2)
        polyCountX = 2;
    if (polyCountY < 2)
        polyCountY = 2;
 
    while (polyCountX * polyCountY > 65530) // prevent u16 overflow
    {
        polyCountX /= 2;
        polyCountY /= 2;
    }
   
    Radius = radius;
    PolyCountX = polyCountX;
    PolyCountY = polyCountY;
   
    //polyCountX += 1;
   
    const u32 polyCountXPitch = polyCountX+2; // get to same vertex on next level
 
    SMeshBuffer* buffer = new SMeshBuffer();
 
    buffer->Indices.reallocate((polyCountX * polyCountY) * 6);
   
    const video::SColor clr(100, 255,255,255);
 
    u32 level = 0;
 
    for (u32 p1 = 0; p1 < polyCountY-1; ++p1)
    {
        //main quads, top to bottom
        for (u32 p2 = 0; p2 < polyCountX - 1; ++p2)
        {
            const u32 curr = level + p2;
            buffer->Indices.push_back(curr + polyCountXPitch);
            buffer->Indices.push_back(curr);
            buffer->Indices.push_back(curr + 1);
            buffer->Indices.push_back(curr + polyCountXPitch);
            buffer->Indices.push_back(curr+1);
            buffer->Indices.push_back(curr + 1 + polyCountXPitch);
        }
 
        // the connectors from front to end
        buffer->Indices.push_back(level + polyCountX - 1 + polyCountXPitch);
        buffer->Indices.push_back(level + polyCountX - 1);
        buffer->Indices.push_back(level + polyCountX);
 
        buffer->Indices.push_back(level + polyCountX - 1 + polyCountXPitch);
        buffer->Indices.push_back(level + polyCountX);
        buffer->Indices.push_back(level + polyCountX + polyCountXPitch);
       
        // the one which fix the seam
        buffer->Indices.push_back(level + polyCountX - 1 + polyCountXPitch+1);
        buffer->Indices.push_back(level + polyCountX - 1+1);
        buffer->Indices.push_back(level + polyCountX+1);
 
        buffer->Indices.push_back(level + polyCountX - 1 + polyCountXPitch+1);
        buffer->Indices.push_back(level + polyCountX+1);
        buffer->Indices.push_back(level + polyCountX + polyCountXPitch+1);
       
        level += polyCountXPitch;
    }
 
    const u32 polyCountSq = polyCountXPitch * polyCountY; // top point
    const u32 polyCountSq1 = polyCountSq + 1; // bottom point
    const u32 polyCountSqM1 = (polyCountY - 1) * polyCountXPitch; // last row's first vertex
 
    for (u32 p2 = 0; p2 < polyCountX - 1; ++p2)
    {
        // create triangles which are at the top of the sphere
 
        buffer->Indices.push_back(polyCountSq);
        buffer->Indices.push_back(p2 + 1);
        buffer->Indices.push_back(p2);
 
        // create triangles which are at the bottom of the sphere
 
        buffer->Indices.push_back(polyCountSqM1 + p2);
        buffer->Indices.push_back(polyCountSqM1 + p2 + 1);
        buffer->Indices.push_back(polyCountSq1);
    }
 
    // create final triangle which is at the top of the sphere
 
    buffer->Indices.push_back(polyCountSq);
    buffer->Indices.push_back(polyCountX);
    buffer->Indices.push_back(polyCountX-1);
 
    // create final triangle which is at the bottom of the sphere
 
    buffer->Indices.push_back(polyCountSqM1 + polyCountX - 1);
    buffer->Indices.push_back(polyCountSqM1);
    buffer->Indices.push_back(polyCountSq1);
 
    // calculate the angle which separates all points in a circle
    const f64 AngleX = 2 * core::PI / polyCountX;
    const f64 AngleY = core::PI / polyCountY;
 
    u32 i=0;
    f64 axz;
 
    // we don't start at 0.
 
    f64 ay = 0.0;//AngleY / 2;
    //f32 averageHeight = 0.0f;
 
    buffer->Vertices.set_used((polyCountXPitch * polyCountY) + 2);
    for (u32 y = 0; y < polyCountY; ++y)
    {
        ay += AngleY;
        const f64 sinay = sin(ay);
        axz = 0;
       
        // calculate the necessary vertices without the doubled one
        for (u32 xz = 0;xz < polyCountX; ++xz)
        {
            // get height
            f32 height = heightMap->getPixel(xz, y).getAverage() * MaxHeight;
            f32 alpha = (f32)heightMap->getPixel(xz, y).getAlpha()/255.0f;
   
            // calculate points position
 
            core::vector3df pos(static_cast<f32>(radius * cos(axz) * sinay),
                        static_cast<f32>(radius * cos(ay)),
                        static_cast<f32>(radius * sin(axz) * sinay));
            pos.setLength((radius + height) * alpha);
 
            // for spheres the normal is the position
            core::vector3df normal(pos);
            normal.normalize();
 
            // calculate texture coordinates via sphere mapping
            // tu is the same on each level, so only calculate once
            f32 tu = 0.5f;
            if (y==0)
            {
                if (normal.Y != -1.0f && normal.Y != 1.0f)
                    tu = static_cast<f32>(acos(core::clamp(normal.X/sinay, -1.0, 1.0)) * 0.5 *core::RECIPROCAL_PI64);
                if (normal.Z < 0.0f)
                    tu=1-tu;
            } else {
                tu = buffer->Vertices[i-polyCountXPitch].TCoords.X;
            }
           
            buffer->Vertices[i] = video::S3DVertex(pos.X, pos.Y, pos.Z,
                        0,0,0,//normal.X, normal.Y, normal.Z,
                        clr, tu,
                        static_cast<f32>(ay*core::RECIPROCAL_PI64));
            ++i;
            axz += AngleX;
        }
       
        buffer->Vertices[i] = video::S3DVertex(buffer->Vertices[i-polyCountX]);
        buffer->Vertices[i].TCoords.X=1.0f;
       
        ++i;
       
        buffer->Vertices[i] = video::S3DVertex(buffer->Vertices[i-polyCountX]);
        buffer->Vertices[i].TCoords.X+=1.0f;
       
        ++i;
    }
   
    // the vertex at the top of the sphere
    buffer->Vertices[i] = video::S3DVertex(0.0f,buffer->Vertices[0].Pos.Y,0.0f, 0.0f,1.0f,0.0f, clr, 0.5f, 0.0f);
 
    // the vertex at the bottom of the sphere
    ++i;
    buffer->Vertices[i] = video::S3DVertex(0.0f,buffer->Vertices[i-2].Pos.Y,0.0f, 0.0f,-1.0f,0.0f, clr, 0.5f, 1.0f);
 
    for (u32 ind = 0; ind < buffer->Indices.size(); ind+=3)
    {
        core::vector3df v1 = buffer->Vertices[buffer->Indices[ind  ]].Pos;
        core::vector3df v2 = buffer->Vertices[buffer->Indices[ind+1]].Pos;
        core::vector3df v3 = buffer->Vertices[buffer->Indices[ind+2]].Pos;
       
        core::vector3df tmp = v1-v2;
        core::vector3df normal = tmp.crossProduct(v1-v3);
        normal.normalize();
   
        buffer->Vertices[buffer->Indices[ind  ]].Normal += normal;
        buffer->Vertices[buffer->Indices[ind+1]].Normal += normal;
        buffer->Vertices[buffer->Indices[ind+2]].Normal += normal;
    }
   
    // normalize&recalculate bounding box
    buffer->BoundingBox.reset(buffer->Vertices[i].Pos);
    for (u32 ind = 0; ind < buffer->Vertices.size(); ind++)
    {
        buffer->Vertices[ind].Normal.normalize();
        buffer->BoundingBox.addInternalPoint(buffer->Vertices[ind].Pos);
    }
 
    SMesh* mesh = new SMesh();
    mesh->addMeshBuffer(buffer);
    buffer->drop();
 
    mesh->setHardwareMappingHint(EHM_STATIC);
    mesh->recalculateBoundingBox();
 
    //heightMap->drop();
 
    return mesh;
}
 
bool CSphericalTerrain::removeChild(ISceneNode* child)
{
    return ISceneNode::removeChild(child);
}
 
 
} // end namespace scene
} // end namespace irr
 


CSphericalTerrain.h:

cpp Code: Select all
 
#ifndef __C_SPHERICAL_TERRAIN_H_INCLUDED__
#define __C_SPHERICAL_TERRAIN_H_INCLUDED__
 
#include "IMeshSceneNode.h"
#include "IMesh.h"
#include "IReadFile.h"
#include "IFileSystem.h"
#include "IImage.h"
#include "SMeshBuffer.h"
 
namespace irr
{
namespace scene
{
    class CSphericalTerrain : public scene::ISceneNode
    {
    public:
 
        //! constructor
        CSphericalTerrain(const io::path& heightMapFileName, ISceneManager* mgr, f32 size = 5.0f, f32 maxHeight = 2.0f, ISceneNode* parent = 0, s32 id = -1,
            const core::vector3df& position = core::vector3df(0,0,0),
            const core::vector3df& rotation = core::vector3df(0,0,0),
            const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f));
 
        CSphericalTerrain(io::IReadFile* heightMapFile, ISceneManager* mgr, f32 size = 5.0f, f32 maxHeight = 2.0f, ISceneNode* parent = 0, s32 id = -1,
            const core::vector3df& position = core::vector3df(0,0,0),
            const core::vector3df& rotation = core::vector3df(0,0,0),
            const core::vector3df& scale = core::vector3df(1.0f, 1.0f, 1.0f));
 
        //! destructor
        virtual ~CSphericalTerrain();
 
        virtual void OnRegisterSceneNode();
 
        //! renders the node.
        virtual void render();
 
        //! returns the axis aligned bounding box of this node
        virtual const core::aabbox3d<f32>& getBoundingBox() const;
 
        //! returns the material based on the zero based index i. To get the amount
        //! of materials used by this scene node, use getMaterialCount().
        //! This function is needed for inserting the node into the scene hirachy on a
        //! optimal position for minimizing renderstate changes, but can also be used
        //! to directly modify the material of a scene node.
        virtual video::SMaterial& getMaterial(u32 i);
 
        //! returns amount of materials used by this scene node.
        virtual u32 getMaterialCount() const;
 
        //! Returns type of the scene node
        virtual ESCENE_NODE_TYPE getType() const { return ESNT_SPHERE; }
 
        //! Writes attributes of the scene node.
        virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const;
 
        //! Reads attributes of the scene node.
        virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0);
 
        //! Creates a clone of this scene node and its children.
        virtual ISceneNode* clone(ISceneNode* newParent=0, ISceneManager* newManager=0);
 
        //! The mesh cannot be changed
        virtual void setMesh(IMesh* mesh) {}
       
        //! Returns the current mesh
        virtual IMesh* getMesh() { return Mesh; }
 
        //! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.
        /* In this way it is possible to change the materials a mesh causing all mesh scene nodes
        referencing this mesh to change too. */

        virtual void setReadOnlyMaterials(bool readonly) {}
 
        //! Returns if the scene node should not copy the materials of the mesh but use them in a read only style
        virtual bool isReadOnlyMaterials() const { return false; }
 
        //! This is basically a height multiplier.
        virtual void setMaxHeight(f32 maxHeight);
       
        virtual bool removeChild(ISceneNode* child);
       
        f32 getHeight(u32 x, u32 y) {
            const core::dimension2d<u32> hMapSize = heightMap->getDimension();
            if (x > hMapSize.Width-1 || y > hMapSize.Height-1) {
                return 0.0f;
            }
           
            return heightMap->getPixel(x, y).getAverage() * MaxHeight;
        }
 
        //! This can be used to place stuff on the terrain
        core::vector3df getSurfacePosition(u32 x, u32 y, f32 height = 1.0f) {
            f32 terrain_height = getHeight(x, y);
            f32 alpha = (f32)heightMap->getPixel(x, y).getAlpha()/255.0f;
           
            f64 AngleX = 2 * core::PI / PolyCountX;
            f64 AngleY = core::PI / PolyCountY;
            f64 ay = (y+1)*AngleY;
            f64 sinay = sin(ay);
            f64 axz = x*AngleX;
           
            core::vector3df pos(static_cast<f32>(Radius * cos(axz) * sinay),
                        static_cast<f32>(Radius * cos(ay)),
                        static_cast<f32>(Radius * sin(axz) * sinay));
            pos.setLength((Radius + terrain_height) * alpha);
           
            pos *= height;
            pos += getPosition();
            pos *= getScale();
           
            return pos;
        }
       
        //! This can be used to align stuff on the terrain
        core::vector3df getSurfaceNormal(u32 x, u32 y) {
            u32 index = x + y * (PolyCountX+2);
            return Mesh->getMeshBuffer(0)->getNormal(index);
        }
 
    private:
        IMesh* createSphericalTerrainMesh(video::IImage* heightMap, f32 radius, f32 MaxHeight);
 
        IMesh* Mesh;
        io::path HeightmapFile;
        video::IImage* heightMap;
        core::aabbox3d<f32> Box;
        f32 Radius;
        f32 MaxHeight;
        u32 PolyCountX;
        u32 PolyCountY;
    };
 
} // end namespace scene
} // end namespace irr
 
#endif
 


To use it:

cpp Code: Select all
    scene::ISceneNode* sphericalTerrainSceneNode = new scene::CSphericalTerrain("heightmap.png", smgr, 5.0f, 1.5f, smgr->getRootSceneNode(), -1);
    if (sphericalTerrainSceneNode)
    {
        sphericalTerrainNode->setMaterialTexture(0, driver->getTexture("surface.jpg"));
    }


setMaxHeight method can be used to set the terrain height later (a simple multiplier), keep in mind it will re-generate the geometry, if you want to do it fast you can use simple scaling

getSurfacePosition(x, y, height) method return the 3D surface position at the specified x,y of the heightmap, the third parameter can be used to get a position that is above or below the ground so you can place objects easily

getSurfaceNormal(x, y) method can be used to align stuff to the terrain surface, the x,y are of the heightmap

Use/improve it as you wish. :)
Last edited by Grz- on Sun Sep 08, 2013 10:08 pm, edited 4 times in total.
Grz-
 
Posts: 62
Joined: Wed Dec 26, 2007 1:06 pm

Postby bitplane » Thu Jan 27, 2011 12:05 am

Excellent, thanks for separating this out of the other topic and posting it here. Pretty sure people will find it useful :)
Submit bugs/patches to the tracker!
Need help right now? Visit the chat room
User avatar
bitplane
Admin
 
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England

Postby Kalango » Thu Jan 27, 2011 1:46 am

*bookmarked*
Looks pretty cool, must be a good way to generate puzzle game and mario galaxy-likes quickly.
Thanks for the snippet.
Kalango
 
Posts: 157
Joined: Thu Apr 26, 2007 12:46 am

Postby pippy3 » Thu Jan 27, 2011 4:18 am

I'm going to up you one and post my code for a noise generated tessellated icosahedron.

It has an orbiting object to allow objects on the planet, and can be tessellated to different polly counts. Also has seaming to minimise texture artifacts. click and drag to run around the planet and use the WASD+RF keys to change direction / orbit height.

The advantage of using a tessellated icosahedron is that all polygons are the same size. If the polar wrapping becomes an issue you can simply smooth out the poles in the texture.

I was planning on making a populous 3 rip-off, but got bored with it.

Image

Download Here

Edit: fixed download link
Last edited by pippy3 on Mon Jan 31, 2011 2:37 am, edited 1 time in total.
pippy3
 
Posts: 155
Joined: Tue Dec 15, 2009 7:32 am

Postby Granyte » Thu Jan 27, 2011 5:27 am

the isocaedron does not compile at leas on visual studio 2010
Granyte
 
Posts: 846
Joined: Tue Jan 25, 2011 11:07 pm

Postby pippy3 » Thu Jan 27, 2011 8:22 am

Granyte wrote:the isocaedron does not compile at leas on visual studio 2010


I use xCode/gcc. Any errors? it will probably be easy to fix.
pippy3
 
Posts: 155
Joined: Tue Dec 15, 2009 7:32 am

Postby Grz- » Thu Jan 27, 2011 9:35 am

It look quite nice and is surely better to make planets, thank for sharing. :)
Grz-
 
Posts: 62
Joined: Wed Dec 26, 2007 1:06 pm

Postby devsh » Thu Jan 27, 2011 5:10 pm

The reason you may have warping etc. is because you may be using the WRONG texture/sphere mapping

Are you using U=lat and V=lon???

Then use something like Peirce Quincuncial projection which distributes the pixels more evenly on the sphere.

I have a method just for this stuff, "ortographic projection". Imagine a unit sphere, with a centre at (0,1,0) and with rays shooting through the bottom hemisphere onto a flat plane from the North Pole (upper pole). These are your pixel positions within a circle 2 units in radius. Transform it to a square with a area preserving transform (Wolfram Math world). Repeat the whole process for the other hemisphere. Rotate the first square so it is a rhombus, cut the second with 2 diagonals into 4 triangles and attach the sides to the rhombus.
We chose to stream mesh data from Multiple OpenGL Contexts in many threads and do the other things, not because they are easy, but because they are hard! - JFK
User avatar
devsh
Competition winner
 
Posts: 1767
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK

Postby Anthony » Sat Jan 29, 2011 4:26 pm

Looks cool.

But to have the problem solved for most people who are addressing to a terrain:

first, if you want a huge terrain to walk on this won't work, it uses one IMeshBuffer thus you can't have more then 256 * 256 = 65536 vertices. Even for a populous sized plant it is very low.

second, if you want to walk on the surface (wich is the main goal I think of having a terrain) you would need a much higher detail. Thus you have to split up the mesh into several pieces.

third, leaving out LOD optimization will still keep you from adding any other object (NPC, player, trees, building etc.) as your system just can't handle it i.e. 2xvector3df 1xSColor and at least 1xvector2df for each vertex times 65536 is huge namely 256kb. Doesn't sound big but you have no detail what so ever. For a nice terrain you should/could divide the sphere into 6 parts. One part of it should be divided into 64*64 patches to have some detail if you are high in the sky. thus you get 256 kb time 64 times 64 = 1GB. And your system will always use memory too besides your application and you can't control that.For almost all systems you would crash -> 1GB terrain + All of your other game objects + an unknowing amount of system resources.

You can loose the LOD but you would still have the problem of having a walkover or fly over terrain that is accaptable.

For a planet in outerspace I think you could have just a simple sphere of just a few stacks and slices of just a few bytes. Or just even a simple object out of 4 vertices and 6 indices with or without a texture.

@pippy3:
Couldn't find your source howeever it looks like you did what I am trying to do.
No :shock: I am no noob, just checking if your not one too :roll:

Thanks to Niko and all others that made Irrlicht possible.
Anthony
 
Posts: 121
Joined: Sun Jan 09, 2011 12:03 pm

Postby Radikalizm » Sat Jan 29, 2011 4:51 pm

Anthony wrote:
first, if you want a huge terrain to walk on this won't work, it uses one IMeshBuffer thus you can't have more then 256 * 256 = 65536 vertices. Even for a populous sized plant it is very low.


This is only true for 16-bits mesh buffers, 32-bits buffers can hold a lot more vertices

Anthony wrote:third, leaving out LOD optimization will still keep you from adding any other object (NPC, player, trees, building etc.) as your system just can't handle it i.e. 2xvector3df 1xSColor and at least 1xvector2df for each vertex times 65536 is huge namely 256kb. Doesn't sound big but you have no detail what so ever. For a nice terrain you should/could divide the sphere into 6 parts. One part of it should be divided into 64*64 patches to have some detail if you are high in the sky. thus you get 256 kb time 64 times 64 = 1GB. And your system will always use memory too besides your application and you can't control that.For almost all systems you would crash -> 1GB terrain + All of your other game objects + an unknowing amount of system resources.


By this logic it would be impossible to use any higher polygon assets, using the right asset management, optimizations, streaming, etc. can overcome all the problems you just stated
Radikalizm
 
Posts: 1215
Joined: Tue Jan 09, 2007 7:03 pm
Location: Leuven, Belgium

Postby Anthony » Sat Jan 29, 2011 5:05 pm

Radikalizm wrote:This is only true for 16-bits mesh buffers, 32-bits buffers can hold a lot more vertices

perhaps it is ment as a reply at this thread

Radikalizm wrote:By this logic it would be impossible to use any higher polygon assets, using the right asset management, optimizations, streaming, etc. can overcome all the problems you just stated

Perhaps I did say that.

Do you have a solution DELETED(or are you just flaming)?
Last edited by Anthony on Sat Jan 29, 2011 5:20 pm, edited 2 times in total.
No :shock: I am no noob, just checking if your not one too :roll:

Thanks to Niko and all others that made Irrlicht possible.
Anthony
 
Posts: 121
Joined: Sun Jan 09, 2011 12:03 pm

Postby Radikalizm » Sat Jan 29, 2011 5:11 pm

I'm sorry, but to me it sounded more like you were trying to write this implementation off as 'useless' for people who wanted to make actual terrains, while it is perfectly suited for a task like this as long as you know what you are doing ;)

EDIT:
And no, I wasn't flaming you at all, you shouldn't feel offended by some criticism
Radikalizm
 
Posts: 1215
Joined: Tue Jan 09, 2007 7:03 pm
Location: Leuven, Belgium

Postby Anthony » Sat Jan 29, 2011 5:19 pm

This thread is a follow up of spherical terrain node or height mapping a sphere?

Maybe I wasn't clear myself but the thread linked here talks about a huge - real huge - terrain. And I wrote this with that in mind and so it is useless for that goal. It can be usefull for other things, that is certain.

EDIT: Now you have make it sound like I was flaming this post, wich isn't true either :wink: and we all live happely ever after :shock:
No :shock: I am no noob, just checking if your not one too :roll:

Thanks to Niko and all others that made Irrlicht possible.
Anthony
 
Posts: 121
Joined: Sun Jan 09, 2011 12:03 pm

Postby Radikalizm » Sat Jan 29, 2011 5:42 pm

Anthony wrote:Maybe I wasn't clear myself but the thread linked here talks about a huge - real huge - terrain. And I wrote this with that in mind and so it is useless for that goal.


For an incredibly huge terrain you would be right, it just wouldn't be a good idea in general to use a spherical solution for this, the better option here would be a streamed or sector-based terrain so only few parts have to be loaded at a time (this last system is what I've implemented in my game engine)
This will also prevent you from having to mess with your physics implementation to take into account the gravitational pull of the 'planet'

When I think spherical terrain, I think 'Super Mario Galaxy'-like terrains (or somewhat bigger than that), and this system is perfectly suited for those kind of terrains (although I still prefer to do terrain using a displacement map in max with manual alterings to produce details and things a regular heightmap can't do)
Radikalizm
 
Posts: 1215
Joined: Tue Jan 09, 2007 7:03 pm
Location: Leuven, Belgium

Postby Grz- » Sat Jan 29, 2011 6:49 pm

This was not for true sized planet (and for planet only too), more like a simple method for just height mapping a sphere, nothing more, actually the method of pippy3 with the tessellated icosahedron is way better suited for that, it don't have any holes on the pole and look better and more detailed at close range.
Grz-
 
Posts: 62
Joined: Wed Dec 26, 2007 1:06 pm

Next

Return to Code Snippets

Who is online

Users browsing this forum: No registered users and 0 guests