Page 1 of 3

Texture Splatting [OpenGL]

Posted: Mon May 31, 2010 12:50 pm
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!

Posted: Mon May 31, 2010 1:00 pm
by netpipe
very thankfull thanks.

Posted: Mon May 31, 2010 3:32 pm
by ACE247
Thanks! 8 Detail textures, Really like this.

Posted: Mon May 31, 2010 4:37 pm
by Virion
*thumbs up*

Posted: Tue Jun 01, 2010 10:15 am
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

Posted: Tue Jun 01, 2010 1:29 pm
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.

Posted: Tue Jun 01, 2010 6:04 pm
by freetimecoder
Please do and post the shader code ;) Then I can make this working for both drivers automatically.

greetings

Posted: Mon Jun 07, 2010 11:05 pm
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).

Posted: Wed Jun 09, 2010 8:03 am
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

Posted: Wed Aug 04, 2010 4:16 pm
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

Posted: Thu Aug 05, 2010 4:46 am
by freetimecoder
Hi,

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

greetings

Posted: Wed Mar 16, 2011 6:12 pm
by nespa
made starting with your code:

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


Thanks!

Posted: Thu Mar 17, 2011 1:11 pm
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

Posted: Thu Mar 17, 2011 4:01 pm
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;

Posted: Thu Mar 17, 2011 8:10 pm
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