Texture Splatting [OpenGL]

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Texture Splatting [OpenGL]

Post by freetimecoder »

Hi,

as pronounced in this topic I have been working on a OpenGL version for Texture Splatting. Basically I just took the ShieldManager, changed it and wrote a new shader.
I also implemented a few extra things to make it possible to use normal ITerrainNodes with the manager, so that the terrain lod is usable.
I have not yet written a documentation, but the use is quite simple. Add the node you want to multitexture to the manager and add passes. Each pass needs a splatting map and 3 or less color textures. The splattingmap itself can use the values red, green and blue each representing one of the color textures. Depending on how bright the color value is the colortexture will be blended. The alpha value of the splatting map will be transparency channel for the pass, so it does not overpaint everything from the previous passes.

Enough talk, here it is:
Image

Here you go:
http://www.file-upload.net/download-256 ... ng.7z.html

CMultiTexturingManager.h

Code: Select all

#ifndef CMULTITEXTURINGMANAGER_H_INCLUDED
#define CMULTITEXTURINGMANAGER_H_INCLUDED

#include <irrlicht.h>
using namespace irr;

class CMultiTexturingManager: public video::IShaderConstantSetCallBack
{
public:
	struct STexturePass
	{
	    video::ITexture *splat_texture;
	    video::ITexture *red_texture;
	    video::ITexture *green_texture;
	    video::ITexture *blue_texture;
	};

private:
	struct SMultiTextureNode
	{
	    //The textured node
	    scene::ISceneNode *Node;
	    core::array<STexturePass*> array_Passes;
	};

public:
	CMultiTexturingManager(scene::ISceneManager* psmgr);

	~CMultiTexturingManager();

	void drawAll();

	virtual void OnSetConstants(video::IMaterialRendererServices* services,s32 userData);

	bool addNode(scene::ISceneNode *node);

	bool removeNode(scene::ISceneNode *node);

	STexturePass *addPass(scene::ISceneNode *node);
	STexturePass *addPass(scene::ISceneNode *node, video::ITexture *splat, video::ITexture *red, video::ITexture *green, video::ITexture *blue);
	STexturePass *addPass(scene::ISceneNode *node, STexturePass *pass);

	bool removePass(scene::ISceneNode *node, u32 layer);
	bool removePass(scene::ISceneNode *node, STexturePass *pass);

private:
    scene::ISceneManager* smgr;

    video::IVideoDriver* driver;

	core::array<SMultiTextureNode> array_Nodes;

    int shaderMaterial;

};

#endif // CMULTITEXTURINGMANAGER_H_INCLUDED
CMultiTexturingManager.cpp

Code: Select all

#include "CMultiTexturingManager.h"

CMultiTexturingManager::CMultiTexturingManager(scene::ISceneManager* psmgr){
    //Assign all parameters
    smgr = psmgr;
    driver=psmgr->getVideoDriver();

    ///GLSL Shader Code
    //Vertex Shader
    const c8 *vertShader = "void main()\
                            {\
                                gl_TexCoord[0] = gl_MultiTexCoord0;\
                                gl_TexCoord[1] = gl_MultiTexCoord1;\
                                gl_Position = ftransform();\
                            }\
                            ";

    //Fragment Shader
    const c8 *fragShader = "uniform sampler2D splatMap;\
                            uniform sampler2D layer_red;\
                            uniform sampler2D layer_green;\
                            uniform sampler2D layer_blue;\
                            \
                            void main(){\
                                vec4 SplatCol=texture2D(splatMap,gl_TexCoord[0].xy);\
                                vec4 RedCol=texture2D(layer_red,gl_TexCoord[1].xy);\
                                vec4 GreenCol=texture2D(layer_green,gl_TexCoord[1].xy);\
                                vec4 BlueCol=texture2D(layer_blue,gl_TexCoord[1].xy);\
                                gl_FragColor=(vec4(SplatCol.r*RedCol+SplatCol.g*GreenCol+SplatCol.b*BlueCol))*vec4(1,1,1,SplatCol.a);\
                            }\
                            ";

    //Create the shader material
    shaderMaterial = driver->getGPUProgrammingServices()->addHighLevelShaderMaterial(
            vertShader, "main", video::EVST_VS_1_1,
            fragShader, "main", video::EPST_PS_1_1,
            this, video::EMT_TRANSPARENT_ALPHA_CHANNEL);
}

CMultiTexturingManager::~CMultiTexturingManager(){
    //Erase everything
    for(u32 i=0; i<array_Nodes.size();i++){
        while(!array_Nodes[i].array_Passes.empty()){
            array_Nodes[i].array_Passes.erase(0);
        }
    }
    while(!array_Nodes.empty()){
        array_Nodes.erase(0);
    }
}

bool CMultiTexturingManager::addNode(scene::ISceneNode *node){
    for(u32 i=0; i<array_Nodes.size();i++){
	    if(array_Nodes[i].Node == node){
	        return false;
	    }
    }
    SMultiTextureNode tmpNode;
    tmpNode.Node = node;
    array_Nodes.push_back(tmpNode);
    return true;
}

bool CMultiTexturingManager::removeNode(scene::ISceneNode *node){
    for(u32 i=0; i<array_Nodes.size();i++){
	    if(array_Nodes[i].Node == node){
	        array_Nodes.erase(i);
	        return true;
	    }
    }
    return false;
}

CMultiTexturingManager::STexturePass *CMultiTexturingManager::addPass(scene::ISceneNode *node){
    for(u32 i=0; i<array_Nodes.size();i++){
        if(array_Nodes[i].Node == node){
            STexturePass *pass = new STexturePass();
            pass->red_texture = node->getMaterial(0).getTexture(0);
            pass->green_texture = node->getMaterial(0).getTexture(1);
            pass->blue_texture = node->getMaterial(0).getTexture(2);
            pass->splat_texture = node->getMaterial(0).getTexture(3);

            array_Nodes[i].array_Passes.push_back(pass);
            return pass;
        }
    }
    return 0;
}

CMultiTexturingManager::STexturePass *CMultiTexturingManager::addPass(scene::ISceneNode *node, video::ITexture *splat, video::ITexture *red, video::ITexture *green, video::ITexture *blue){
    for(u32 i=0; i<array_Nodes.size();i++){
        if(array_Nodes[i].Node == node){
            STexturePass *pass = new STexturePass();
            pass->red_texture = red;
            pass->green_texture = green;
            pass->blue_texture = blue;
            pass->splat_texture = splat;

            array_Nodes[i].array_Passes.push_back(pass);
            return pass;
        }
    }
    return 0;
}

CMultiTexturingManager::STexturePass *CMultiTexturingManager::addPass(scene::ISceneNode *node, STexturePass *pass){
    for(u32 i=0; i<array_Nodes.size();i++){
        if(array_Nodes[i].Node == node){
            STexturePass *Pass = pass;

            array_Nodes[i].array_Passes.push_back(pass);
            return pass;
        }
    }
    return 0;
}

bool CMultiTexturingManager::removePass(scene::ISceneNode *node, u32 layer){
    for(u32 i=0; i<array_Nodes.size();i++){
        if(array_Nodes[i].Node == node){
            for(u32 j = 0; j<array_Nodes[i].array_Passes.size();j++){
                if(j==layer)
                    array_Nodes[i].array_Passes.erase(j);
                    return true;
            }
        }
    }
    return false;
}

bool CMultiTexturingManager::removePass(scene::ISceneNode *node, STexturePass *pass){
    for(u32 i=0; i<array_Nodes.size();i++){
        if(array_Nodes[i].Node == node){
            for(u32 j = 0; j<array_Nodes[i].array_Passes.size();j++){
                if(array_Nodes[i].array_Passes[j]==pass)
                    array_Nodes[i].array_Passes.erase(j);
                    return true;
            }
        }
    }
    return false;
}


void CMultiTexturingManager::drawAll(){

	for(u32 i=0; i<array_Nodes.size();i++)
	{
        //I learned this meshbuffer trick from Viz_Fuerte's "Simple but useful projects"
        if(!smgr->isCulled(array_Nodes[i].Node))
        {
            array_Nodes[i].Node->setVisible(true);
            array_Nodes[i].Node->OnRegisterSceneNode();
            array_Nodes[i].Node->updateAbsolutePosition();
            array_Nodes[i].Node->setVisible(false);
            //Reset the transformation
            if(array_Nodes[i].Node->getType()==scene::ESNT_TERRAIN)
                driver->setTransform(video::ETS_WORLD,core::IdentityMatrix);
            else
                driver->setTransform(video::ETS_WORLD,array_Nodes[i].Node->getAbsoluteTransformation());

            for(u32 j=0; j<array_Nodes[i].array_Passes.size();j++){
                array_Nodes[i].Node->setMaterialTexture(0,array_Nodes[i].array_Passes[j]->splat_texture);
                array_Nodes[i].Node->setMaterialTexture(1,array_Nodes[i].array_Passes[j]->red_texture);
                array_Nodes[i].Node->setMaterialTexture(2,array_Nodes[i].array_Passes[j]->green_texture);
                array_Nodes[i].Node->setMaterialTexture(3,array_Nodes[i].array_Passes[j]->blue_texture);

                if(array_Nodes[i].Node->getType()==scene::ESNT_TERRAIN){
                    video::SMaterial material = array_Nodes[i].Node->getMaterial(0);
                    material.MaterialType = (video::E_MATERIAL_TYPE)shaderMaterial;
                    material.MaterialTypeParam =pack_texureBlendFunc(video::EBF_DST_COLOR,video::EBF_ONE);

                    driver->setMaterial(material);
                    driver->drawMeshBuffer(((scene::ITerrainSceneNode*)array_Nodes[i].Node)->getRenderBuffer());
                }else{
                    for(u32 k=0; k<array_Nodes[i].Node->getMaterialCount(); ++k)
                    {
                        video::SMaterial material = array_Nodes[i].Node->getMaterial(k);
                        material.MaterialType = (video::E_MATERIAL_TYPE)shaderMaterial;
                        material.MaterialTypeParam =pack_texureBlendFunc(video::EBF_DST_COLOR,video::EBF_ONE);

                        driver->setMaterial(material);
                        switch(array_Nodes[i].Node->getType()){
                        case scene::ESNT_ANIMATED_MESH:
                            driver->drawMeshBuffer(((scene::IAnimatedMeshSceneNode*)array_Nodes[i].Node)->getMesh()->getMeshBuffer(k));
                            break;
                        default:
                            driver->drawMeshBuffer(((scene::IMeshSceneNode*)array_Nodes[i].Node)->getMesh()->getMeshBuffer(k));
                            break;
                        };
                    }
                }
			}
        }
	}
}

void CMultiTexturingManager::OnSetConstants(video::IMaterialRendererServices* services, s32 userData)
{
    int layer0 = 0;
    int layer1 = 1;
    int layer2 = 2;
    int layer3 = 3;
    services->setPixelShaderConstant("splatMap",(float*)&layer0,1);
    services->setPixelShaderConstant("layer_red",(float*)&layer1,1);
    services->setPixelShaderConstant("layer_green",(float*)&layer2,1);
    services->setPixelShaderConstant("layer_blue",(float*)&layer3,1);
}

main.cpp

Code: Select all

#include <irrlicht.h>
using namespace irr;

#include "CMultiTexturingManager.h"

int main(int argc, char** argv)
{
    //Set up device and receiver
    IrrlichtDevice *device = createDevice(video::EDT_OPENGL, core::dimension2d<u32>(640,480), 32,false,false,false);

    video::IVideoDriver *driver = device->getVideoDriver();
    scene::ISceneManager *smgr = device->getSceneManager();

    //Create the camera
    SKeyMap keyMap[8];
    keyMap[0].Action = EKA_MOVE_FORWARD;
    keyMap[0].KeyCode = KEY_UP;
    keyMap[1].Action = EKA_MOVE_FORWARD;
    keyMap[1].KeyCode = KEY_KEY_W;

    keyMap[2].Action = EKA_MOVE_BACKWARD;
    keyMap[2].KeyCode = KEY_DOWN;
    keyMap[3].Action = EKA_MOVE_BACKWARD;
    keyMap[3].KeyCode = KEY_KEY_S;

    keyMap[4].Action = EKA_STRAFE_LEFT;
    keyMap[4].KeyCode = KEY_LEFT;
    keyMap[5].Action = EKA_STRAFE_LEFT;
    keyMap[5].KeyCode = KEY_KEY_A;

    keyMap[6].Action = EKA_STRAFE_RIGHT;
    keyMap[6].KeyCode = KEY_RIGHT;
    keyMap[7].Action = EKA_STRAFE_RIGHT;
    keyMap[7].KeyCode = KEY_KEY_D;

    scene::ICameraSceneNode *camera = smgr->addCameraSceneNodeFPS(0,100,0.6,-1,keyMap,8);
    camera->setPosition(core::vector3df(1300,250,0));
    camera->setTarget(core::vector3df(600,0,600));
    camera->setNearValue(0.01);

	scene::ISceneNode* skydome=smgr->addSkyDomeSceneNode(driver->getTexture("Data/textures/skydome.jpg"),16,8,0.95f,2.0f);
    skydome->setRotation(core::vector3df(0,-100,0));
    CMultiTexturingManager *texture_manager = new CMultiTexturingManager(smgr);

    //Create a terrain like in the terrain sample
    scene::ITerrainSceneNode *terrain = smgr->addTerrainSceneNode(
		"Data/textures/splatting_test.png",
		0,					// parent node
		-1,					// node id
		core::vector3df(0.f, 2.f, 0.f),		// position
		core::vector3df(0.f, 0.f, 0.f),		// rotation
		core::vector3df(10,3,10),	// scale
		video::SColor ( 255, 255, 255, 255 ),	// vertexColor
		5,					// maxLOD
		scene::ETPS_17,				// patchSize
		3					// smoothFactor
		);
    terrain->setMaterialFlag(video::EMF_LIGHTING,false);

	terrain->scaleTexture(1.0f, 20.0f);

    terrain->setScale(core::vector3df(5,2,5));

    //Add it to the manager
    texture_manager->addNode(terrain);

    //Set the passes
    //First Pass, with Sand, Gras and Rock
    texture_manager->addPass(terrain,
        driver->getTexture("Data/textures/splat_1.tga"),
        driver->getTexture("Data/textures/Rock.jpg"),
        driver->getTexture("Data/textures/Gras.jpg"),
        driver->getTexture("Data/textures/Sand.jpg"));

    //Second Pass with Snow, Mud and the Vulcano's base texture
    texture_manager->addPass(terrain,
        driver->getTexture("Data/textures/splat_2.tga"),
        driver->getTexture("Data/textures/Snow.jpg"),
        driver->getTexture("Data/textures/Mud.jpg"),
        driver->getTexture("Data/textures/Ashes.jpg"));

    //Third Pass. the base
    texture_manager->addPass(terrain,
        driver->getTexture("Data/textures/splat_3.tga"),
        0,
        0,
        driver->getTexture("Data/textures/Pebbles.jpg"));

    //Fourth Pass: the Lightmap
    texture_manager->addPass(terrain,
        driver->getTexture("Data/textures/lm_terrain.tga"),
        driver->getTexture("Data/textures/black.jpg"),
        0,
        0);

    //Fith Pass: Lava, drawn above the Lightmap to make it "glow"
    CMultiTexturingManager::STexturePass *anim_lava_pass = texture_manager->addPass(terrain,
        driver->getTexture("Data/textures/splat_4.tga"),
        driver->getTexture("Data/textures/lava_1.jpg"),
        0,
        0);

    //FPS measurement
    int lastFPS = -1;
    while(device->run())
    {

        //Draw the scene
        driver->beginScene(true, true, video::SColor(50,50,50,50));
        smgr->drawAll();
        texture_manager->drawAll();

        //Dirty Workaround to make the lava animated :P
        core::stringc name = "Data/textures/lava_";
        name+=1+int(floor((device->getTimer()->getRealTime()/40)%29));
        name+=".jpg";
        anim_lava_pass->red_texture = driver->getTexture(name.c_str());

        driver->endScene();


        //Display the FPS
        int fps = driver->getFPS();

        if (lastFPS != fps)
        {
            core::stringw str = L"Texture Splatting - Move with Mouse and WASD/Arrow Keys - FPS:";
            str += fps;

            device->setWindowCaption(str.c_str());
            lastFPS = fps;
        }

    }
    //Cleanup
    device->drop();
    delete texture_manager;

    return 0;
}
Have fun!
Last edited by freetimecoder on Tue Jun 01, 2010 10:15 am, edited 1 time in total.
netpipe
Posts: 669
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada
Contact:

Post by netpipe »

very thankfull thanks.
Live long and phosphor!
-- https://github.com/netpipe/Luna Game Engine Status 95%
ACE247
Posts: 704
Joined: Tue Mar 16, 2010 12:31 am

Post by ACE247 »

Thanks! 8 Detail textures, Really like this.
Virion
Competition winner
Posts: 2148
Joined: Mon Dec 18, 2006 5:04 am

Post by Virion »

*thumbs up*
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

@ACE247: Theoretically you can use as many detailtextures as you want, but more passes will of course slow down the performance. Also you can not use the textures at once, e.g. for a shader. The textures per pass stay 4.

greetings
ACE247
Posts: 704
Joined: Tue Mar 16, 2010 12:31 am

Post by ACE247 »

freetimecoder wrote:@ACE247: Theoretically you can use as many detailtextures as you want, but more passes will of course slow down the performance. Also you can not use the textures at once, e.g. for a shader. The textures per pass stay 4.

greetings
I know that!! :) Just didn't have time to code this this myself yet. But i'm gonna have to change the gl shader to hlsl, gl is too slow for me.
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Please do and post the shader code ;) Then I can make this working for both drivers automatically.

greetings
roelor
Posts: 240
Joined: Wed Aug 13, 2008 8:06 am

Post by roelor »

this doesnt work well on my intel gma 4500. framerate is low, (thats to be expected) but the terrain is transparant in the distance. (you can look through the terrain).
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Hi,

Is it transparent only in the distance or everywhere? Does it look like on the screenshot, ecxept the transparency? What OpenGL/GLSL version do you have? Does it give any errors in the console?

greetings
DGENXP
Posts: 124
Joined: Tue Dec 27, 2005 2:49 am
Contact:

Post by DGENXP »

Great Work on the code but i cant seem to compile it with Irrlicht 1.7 or any one to be honest, i saw the EXE i like the idea you used for the animated texture and i got over 100FPS so very good performance

BTW can you help me get it to compile it will be great to mess around with this
Someone once said A Jack of all trades is a master of none.

Join the Community at the RB3D Forums:

http://www.rpbuilder.org/forum
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Hi,

I can compile it with 1.7 or 1.7.1
What errors do you get? Code or graphics related?

greetings
nespa
Posts: 167
Joined: Wed Feb 24, 2010 12:02 pm

Post by nespa »

made starting with your code:

http://www.filefactory.com/file/cadd81c ... latGUI.rar


Thanks!
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Looks cool :) But the mouse movement is a bit bumpy and you shouldn't start your programm in fullscreen mode, at least my pc had problems with the resolution of the launcher.

greetings
nespa
Posts: 167
Joined: Wed Feb 24, 2010 12:02 pm

Post by nespa »

in the launcher there is a gui to select the video driver, video mode, terrain size and stages number to splatting;

the launcher starts with 800x600x32xfull, for poor computers;

the default parameters are: DirectX (9.0c), 1280x1024x16xfull, 64 terrain, 2 stages;

to continue press space, so.. if u don't make any choose in the gui options , the terrain size will be 64x64;

the mouse cursor steps only over the closest vertex of the selected triangle, it has not a continue movement;
freetimecoder
Posts: 226
Joined: Fri Aug 22, 2008 8:50 pm
Contact:

Post by freetimecoder »

Yea, that was the problem, it is an 800x600 fullscreen mode that ran on my 1680x1050 screen witch caused glitches at first.

The idea of a launcher is to make sure the user can select parameters that enable him to use the programm with the best configuration for his system. So a launcher works best in window mode with a low resolution.

greetings
Post Reply