Page 1 of 1

[fixed]Global ambient with different APIs (D3D & GL)

Posted: Thu Oct 01, 2009 9:26 am
by Piraaate
Hi there !
I ran into a bug, well, it's more a difference between graphical APIs I think.
It concerns ambient lighting. In Irrlicht (and in most engines/API) it's a combination of three things :
- a global parameter set by setAmbientLight on the ISceneManager or directly on the IVideoDriver (global)
- a light parameter, AmbientColor member of SLight struct (light)
- a material parameter, AmbientColor member of the SMaterial class (material)

Now the problem seems to be that Direct3D and OpenGL don't compute the same way :
Direct3D : global*light*material
OpenGL : global + light*material
I'm not exactly sure, but I figured this out by some tests, if someone can confirm and point to an "official" explanation that'd be great.

So I created this patch in order to have the same behavior in OpenGL and Direct3D, I arbitrarely chose the D3D one...
https://sourceforge.net/tracker/?func=d ... tid=540678

Posted: Thu Oct 01, 2009 2:33 pm
by hybrid
Hmm, interesting. I coudl have sworn that OpenGL also multiplies. I'll check this and think about your patch.

Posted: Fri Oct 02, 2009 8:23 am
by Piraaate
hybrid wrote:Hmm, interesting. I coudl have sworn that OpenGL also multiplies. I'll check this and think about your patch.
I sure would have too :(
I might have the wrong results, that's why I 'm asking if someone know a good source of information on that topic...

Posted: Fri Oct 02, 2009 10:12 am
by tonic
Hmm. I too have got frustrated at some point when OGL and D3D lighting wouldn't match.

Although, if I remember correctly, I think my problem has been that OGL was actually darker than D3D with lights. Which sounds like it was a different problem than this ambient one.

Posted: Fri Oct 02, 2009 12:52 pm
by hybrid
Both APIs simply sum up all light parts (ambient, diffuse, emissive). The parts are multiplied from light and material, taking into account the normal. I couldn't see any differences in the basic formulae, so results should be the same. The darkness could be affected by gamma settings, range, etc, but the basic lighting should be the same. I guess we need a simply app to play around with lights and materials.

Posted: Fri Oct 02, 2009 5:25 pm
by tonic
I'm pretty sure it wasn't the range as I was aware of that difference between the APIs and made sure to tweak around if that made any difference. If it is possible that gamma settings somehow differ by used 3D API, then maybe that does something...

However, I got the feeling that the difference would have came from the fact that OGL uses combined MODELVIEW matrix, and D3D has separate model and view matrix.

The case I had was a scenario with subdivided flat ground and 1-2 sky lights which were probably point lights or directional lights at the time (I probably toyed around with both), so that in certain situations the ground plane got light pretty differently.

I am sorry I don't have an actual test case to show this, and it has been a while when I actually had this problem (around v1.4 / pre-1.5 or so). Back then I just decided that I'll stick with one API and hope I don't have to support both with a single game, and if I do, then I might have to investigate this issue a bit more (to find a workaround for asset creation or a fix).

Posted: Fri Oct 02, 2009 7:48 pm
by hybrid
Did you use textured materials? Because vertex colors were handled quite differently prior to Irrlicht 1.6

Posted: Sat Oct 03, 2009 3:32 am
by CuteAlien
I've seen a similar problem a few days ago with material EMT_LIGHTMAP_ADD, but no dynamic lights. Haven't found time yet to reproduce it in a smaller example but I also had GL darker than DX.

Which material type are you using?

Posted: Tue Oct 20, 2009 2:04 am
by CuteAlien
A simple app to check lights & materials. I haven't tested yet too much, but at least in the Irrlicht version I'm using there have been already some rather surprising differences - it nearly looks GL&DX apply textures in a different order. Maybe the application could even be useful for Irrlicht examples:

edit: just did see that the original problem was with global ambient not with light with ambient, I'm going to add that also so it can be set, but too late now today.
edit2: minor cleanup
edit3: added control for global ambient
edit4: Oops, didn't change vertex formats. A lot more similar now in most materials. Not 100% sure yet if my GetVertexTypeForMaterialType is correct for all materials.

Code: Select all

#include <irrlicht.h>
#include <iostream>

using namespace irr;

namespace
{
    const wchar_t* const DriverTypeNames[] =
    {
        L"NULL",
        L"SOFTWARE",
        L"BURNINGSVIDEO",
        L"DIRECT3D8",
        L"DIRECT3D9",
        L"EDT_OPENGL",
        0,
    };

	enum EGUI_IDS
	{
		GUI_ID_OPEN_TEXTURE = 1,
	};

	const core::stringw CLEAR_TEXTURE = "CLEAR texture";

	const irr::video::SColor SCOL_BLACK     = irr::video::SColor(255, 0,   0,   0);
	const irr::video::SColor SCOL_BLUE      = irr::video::SColor(255, 0,   0,  255);
	const irr::video::SColor SCOL_CYAN      = irr::video::SColor(255, 0,  255, 255);
	const irr::video::SColor SCOL_GRAY      = irr::video::SColor(255, 128,128, 128);
	const irr::video::SColor SCOL_GREEN     = irr::video::SColor(255, 0,  255,  0);
	const irr::video::SColor SCOL_MAGENTA   = irr::video::SColor(255, 255, 0,  255);
	const irr::video::SColor SCOL_RED       = irr::video::SColor(255, 255, 0,   0);
	const irr::video::SColor SCOL_YELLOW    = irr::video::SColor(255, 255, 255, 0);
	const irr::video::SColor SCOL_WHITE     = irr::video::SColor(255, 255, 255, 255);
};	// namespace

s32 MakeUniqueId()
{
	static int unique = 10000;
	++unique;
	return unique;
}

video::E_VERTEX_TYPE GetVertexTypeForMaterialType(video::E_MATERIAL_TYPE materialType)
{
    using namespace video;

    switch ( materialType )
    {
		case EMT_SOLID:
            return EVT_STANDARD;

		case EMT_SOLID_2_LAYER:
            return EVT_STANDARD;

		case EMT_LIGHTMAP:
		case EMT_LIGHTMAP_ADD:
		case EMT_LIGHTMAP_M2:
		case EMT_LIGHTMAP_M4:
		case EMT_LIGHTMAP_LIGHTING:
		case EMT_LIGHTMAP_LIGHTING_M2:
		case EMT_LIGHTMAP_LIGHTING_M4:
            return EVT_2TCOORDS;

		case EMT_DETAIL_MAP:
            return EVT_2TCOORDS;

		case EMT_SPHERE_MAP:
            return EVT_STANDARD;

		case EMT_REFLECTION_2_LAYER:
            return EVT_2TCOORDS;

		case EMT_TRANSPARENT_ADD_COLOR:
            return EVT_STANDARD;

		case EMT_TRANSPARENT_ALPHA_CHANNEL:
            return EVT_STANDARD;

		case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:
            return EVT_STANDARD;

		case EMT_TRANSPARENT_VERTEX_ALPHA:
            return EVT_STANDARD;

		case EMT_TRANSPARENT_REFLECTION_2_LAYER:
            return EVT_2TCOORDS;

		case EMT_NORMAL_MAP_SOLID:
		case EMT_NORMAL_MAP_TRANSPARENT_ADD_COLOR:
		case EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA:
		case EMT_PARALLAX_MAP_SOLID:
		case EMT_PARALLAX_MAP_TRANSPARENT_ADD_COLOR:
		case EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA:
            return EVT_TANGENTS;

		case EMT_ONETEXTURE_BLEND:
            return EVT_STANDARD;
    }
}

struct SConfig
{
	SConfig()
	: RenderInBackground(true)
	, DriverType(video::EDT_BURNINGSVIDEO)
	, ScreenSize(640, 480)
	{
	}

	bool RenderInBackground;
	video::E_DRIVER_TYPE DriverType;
	core::dimension2d<u32> ScreenSize;
};

class CColorControl : public IEventReceiver
{
public:
	CColorControl()
		: DirtyFlag(true)
		, ColorStatic(0)
		, EditAlpha(0)
		, EditRed(0)
		, EditGreen(0)
		, EditBlue(0)
	{
		ButtonSetId = MakeUniqueId();
	}

	virtual bool OnEvent(const SEvent &event)
	{
		if ( event.EventType != EET_GUI_EVENT )
			return false;

		if ( event.GUIEvent.Caller->getID() == ButtonSetId && event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
		{
			Color = GetColorFromEdits();
			SetEditsFromColor(Color);
		}

		return false;
	}

	void init( gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text )
	{
		using namespace gui;

		const core::rect< s32 > rectControls(pos, core::dimension2d<s32>(80, 75));
		IGUIStaticText * groupElement =	guiEnv->addStaticText (L"", rectControls, /*bool border=*/true, /*bool wordWrap=*/false, /*IGUIElement *parent=*/0, /*s32 id=*/-1, /*bool fillBackground=*/false);

		guiEnv->addStaticText (text, core::rect<s32>(0,0,80,15), /*bool border=*/false, /*bool wordWrap=*/false, /*IGUIElement *parent=*/groupElement, /*s32 id=*/-1, /*bool fillBackground=*/false);

		EditAlpha = addEditForNumbers(guiEnv, core::position2d<s32>(0,15), L"a", -1, groupElement );
		EditRed = addEditForNumbers(guiEnv, core::position2d<s32>(0,30), L"r", -1, groupElement );
		EditGreen = addEditForNumbers(guiEnv, core::position2d<s32>(0,45), L"g", -1, groupElement );
		EditBlue = addEditForNumbers(guiEnv, core::position2d<s32>(0,60), L"b", -1, groupElement );

		ColorStatic = guiEnv->addStaticText (L"", core::rect<s32>(60,15,80,75), /*bool border=*/true, /*bool wordWrap=*/false, /*IGUIElement *parent=*/groupElement, /*s32 id=*/-1, /*bool fillBackground=*/true);

		guiEnv->addButton (core::rect<s32>(60,35,80,50), groupElement, ButtonSetId, L"set");
		SetEditsFromColor(Color);
	}

	void setColor(const video::SColor& col)
	{
		setDirty(true);
		Color = col;
		SetEditsFromColor(Color);
	}

	const video::SColor& getColor()
	{
		return Color;
	}

	void setDirty(bool dirty)
	{
		DirtyFlag = dirty;
	}

	// when the color was changed the dirty flag is set
	bool isDirty()
	{
		return DirtyFlag;
	};

protected:

	gui::IGUIEditBox* addEditForNumbers(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, s32 id, gui::IGUIElement * parent)
	{
		using namespace gui;

		core::rect< s32 > rect(pos, core::dimension2d<s32>(10, 15));
		guiEnv->addStaticText (text, rect, /*bool border=*/false, /*bool wordWrap=*/false, /*IGUIElement *parent=*/parent, /*s32 id=*/-1, /*bool fillBackground=*/false);
		rect += core::position2d<s32>( 20, 0 );
		rect.LowerRightCorner.X += 20;
		gui::IGUIEditBox* edit = guiEnv->addEditBox(L"0", rect, /*bool border=*/true, parent, id);
		return edit;
	}

	video::SColor GetColorFromEdits()
	{
		video::SColor col;

		u32 alpha=col.getAlpha();
		if ( EditAlpha )
		{
			alpha = (u32)core::strtol10(  core::stringc( EditAlpha->getText() ).c_str(), 0);
			if ( alpha > 255 )
				alpha = 255;
		}
		col.setAlpha(alpha);

		u32 red=col.getRed();
		if ( EditRed )
		{
			red = (u32)core::strtol10( core::stringc( EditRed->getText() ).c_str(), 0);
			if ( red > 255 )
				red = 255;
		}
		col.setRed(red);

		u32 green=col.getGreen();
		if ( EditGreen )
		{
			green = (u32)core::strtol10( core::stringc( EditGreen->getText() ).c_str(), 0);
			if ( green > 255 )
				green = 255;
		}
		col.setGreen(green);


		u32 blue=col.getBlue();
		if ( EditBlue )
		{
			blue = (u32)core::strtol10( core::stringc( EditBlue->getText() ).c_str(), 0);
			if ( blue > 255 )
				blue = 255;
		}
		col.setBlue(blue);

		return col;
	}

	void SetEditsFromColor(video::SColor col)
	{
		setDirty(true);
		if ( EditAlpha )
			EditAlpha->setText( core::stringw(col.getAlpha()).c_str() );
		if ( EditRed )
			EditRed->setText( core::stringw(col.getRed()).c_str() );
		if ( EditGreen )
			EditGreen->setText( core::stringw(col.getGreen()).c_str() );
		if ( EditBlue )
			EditBlue->setText( core::stringw(col.getBlue()).c_str() );
		if ( ColorStatic )
			ColorStatic->setBackgroundColor(col);
	}

private:

	bool DirtyFlag;
	video::SColor Color;
	s32 ButtonSetId;
	gui::IGUIStaticText * ColorStatic;
	gui::IGUIEditBox * EditAlpha;
	gui::IGUIEditBox * EditRed;
	gui::IGUIEditBox * EditGreen;
	gui::IGUIEditBox * EditBlue;
};

struct SAllColorsControl
{
	virtual bool OnEvent(const SEvent &event)
	{
		if ( ControlAmbientColor.OnEvent(event) )
			return true;
		if ( ControlDiffuseColor.OnEvent(event) )
			return true;
		if ( ControlEmissiveColor.OnEvent(event) )
			return true;
		if ( ControlSpecularColor.OnEvent(event) )
			return true;
		return false;
	}

	void init(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t * description, bool hasEmissive)
	{
		core::rect<s32> rect(pos, core::dimension2d<s32>(60, 15));
		guiEnv->addStaticText (description, rect, /*bool border=*/false, /*bool wordWrap=*/false, /*IGUIElement *parent=*/0, /*s32 id=*/-1, /*bool fillBackground=*/false);
		createColorControls(guiEnv, pos + core::position2d<s32>(0, 15), hasEmissive);
	}

	// any colors changed?
	bool isDirty()
	{
		return ControlAmbientColor.isDirty() || ControlDiffuseColor.isDirty() || ControlSpecularColor.isDirty() || ControlEmissiveColor.isDirty();
	}

	void setDirty(bool dirty)
	{
		ControlAmbientColor.setDirty(dirty);
		ControlDiffuseColor.setDirty(dirty);
		ControlSpecularColor.setDirty(dirty);
		ControlEmissiveColor.setDirty(dirty);
	}

	CColorControl	ControlAmbientColor;
	CColorControl	ControlDiffuseColor;
	CColorControl	ControlSpecularColor;
	CColorControl	ControlEmissiveColor;

protected:
	void createColorControls(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, bool hasEmissive)
	{
		ControlAmbientColor.init( guiEnv, pos, L"ambient" );
		ControlDiffuseColor.init( guiEnv, pos + core::position2d<s32>(0, 75), L"diffuse" );
		ControlSpecularColor.init( guiEnv, pos + core::position2d<s32>(0, 150), L"specular" );
		if ( hasEmissive )
		{
			ControlEmissiveColor.init( guiEnv, pos + core::position2d<s32>(0, 225), L"emissive" );
		}
	}
};

struct STextureControl
{
	STextureControl() : Initialized(false), DirtyFlag(false), ComboTexture(0)
	{
	}

	virtual bool OnEvent(const SEvent &event)
	{
		if ( event.EventType != EET_GUI_EVENT )
			return false;

		if ( event.GUIEvent.Caller == ComboTexture && event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
		{
			setDirty(true);
		}

		return false;
	}

	// return selected texturename (if any, otherwise 0)
	const wchar_t * getSelectedTextureName()
	{
		if ( !Initialized )
			return 0;

		s32 selected = ComboTexture->getSelected();
		if ( selected < 0 )
			return 0;
		return ComboTexture->getItem(selected);
	}

	void setDirty(bool dirty)
	{
		DirtyFlag = dirty;
	}

	// when the texture was changed the dirty flag is set
	bool isDirty()
	{
		return DirtyFlag;
	};

	void init(gui::IGUIEnvironment* guiEnv, video::IVideoDriver * driver, const core::position2d<s32> & pos)
	{
		if ( Initialized || !guiEnv || !driver )
			return;
		core::rect<s32> rectCombo(pos.X, pos.Y, pos.X+100, pos.Y+15);
		ComboTexture = guiEnv->addComboBox (rectCombo);
		Initialized = true;

		updateTextures(driver);
	}

	void updateTextures(video::IVideoDriver * driver)
	{
		if ( !Initialized )
			return;

		s32 oldSelected = ComboTexture->getSelected();
		s32 selectNew = -1;
		const wchar_t * oldTextureName = 0;
		if ( oldSelected >= 0 )
		{
			oldTextureName = ComboTexture->getItem(oldSelected);
		}
		ComboTexture->clear();
		for ( u32 i=0; i < driver->getTextureCount(); ++i )
		{
			video::ITexture * texture = driver->getTextureByIndex(i);
			core::stringw name( texture->getName() );
			ComboTexture->addItem( name.c_str() );
			if ( oldTextureName && selectNew < 0 && name == oldTextureName )
				selectNew = i;
		}

		ComboTexture->addItem( CLEAR_TEXTURE.c_str() );
		if ( CLEAR_TEXTURE == oldTextureName )
			selectNew = ComboTexture->getItemCount()-1;

		if ( selectNew >= 0 )
			ComboTexture->setSelected(selectNew);

		setDirty(true);
	}

private:
	bool Initialized;
	bool DirtyFlag;
	gui::IGUIComboBox * ComboTexture;
};

struct SMeshNodeControl
{
	SMeshNodeControl() : Initialized(false), Driver(0), SceneNode(0), SceneNode2T(0), SceneNodeTangents(0), ButtonLighting(0), ComboMaterial(0) {}

	virtual bool OnEvent(const SEvent &event)
	{
		if ( AllColorsControl.OnEvent(event) )
			return true;
		if ( Texture1.OnEvent(event) )
			return true;
		if ( Texture2.OnEvent(event) )
			return true;
		return false;
	}

	void init(scene::IMeshSceneNode* node, IrrlichtDevice * device, const core::position2d<s32> & pos, const wchar_t * description)
	{
		if ( Initialized || !node || !device) // initializing twice or with invalid data not allowed
			return;

		Driver = device->getVideoDriver ();
		gui::IGUIEnvironment* guiEnv = device->getGUIEnvironment();
		scene::ISceneManager* smgr = device->getSceneManager();

		SceneNode = node;
        scene::IMeshManipulator * meshManip = smgr->getMeshManipulator();

		scene::IMesh * mesh2T = meshManip->createMeshWith2TCoords(node->getMesh());
		SceneNode2T = smgr->addMeshSceneNode(mesh2T, /*ISceneNode *parent=*/0, /*s32 id=*/-1
                                            , SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
        mesh2T->drop();

        scene::IMesh * meshTangents = meshManip->createMeshWithTangents(node->getMesh(), /*bool recalculateNormals=*/false, /*bool smooth=*/false, /*bool angleWeighted=*/false);
        SceneNodeTangents = smgr->addMeshSceneNode(meshTangents, /*ISceneNode *parent=*/0, /*s32 id=*/-1
                                            , SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
        meshTangents->drop();

		video::SMaterial & material = SceneNode->getMaterial(0);
		material.Lighting = true;
		AllColorsControl.init(guiEnv, pos, description, true);
		AllColorsControl.ControlAmbientColor.setColor(material.AmbientColor);
		AllColorsControl.ControlDiffuseColor.setColor(material.DiffuseColor);
		AllColorsControl.ControlEmissiveColor.setColor(material.EmissiveColor);
		AllColorsControl.ControlSpecularColor.setColor(material.SpecularColor);

		core::rect<s32> rectBtn(pos + core::position2d<s32>(0, 320), core::dimension2d<s32>(100, 15));
		ButtonLighting = guiEnv->addButton (rectBtn, 0, -1, L"Lighting");
		ButtonLighting->setIsPushButton(true);
		ButtonLighting->setPressed(material.Lighting);

		core::rect<s32> rectCombo(pos.X, rectBtn.LowerRightCorner.Y, pos.X+100, rectBtn.LowerRightCorner.Y+15);
		ComboMaterial = guiEnv->addComboBox (rectCombo);
		for ( int i=0; i <= (int)video::EMT_ONETEXTURE_BLEND; ++i )
		{
			ComboMaterial->addItem( core::stringw(video::sBuiltInMaterialTypeNames[i]).c_str() );
		}
		ComboMaterial->setSelected( (s32)material.MaterialType );

		core::position2d<s32> posTex(rectCombo.UpperLeftCorner.X,rectCombo.LowerRightCorner.Y);
		Texture1.init(guiEnv, Driver, posTex);
		posTex.Y += 15;
		Texture2.init(guiEnv, Driver, posTex);

		Initialized = true;
	}

	void update()
	{
		if ( !Initialized )
			return;

		video::SMaterial & material = SceneNode->getMaterial(0);
		video::SMaterial & material2T = SceneNode2T->getMaterial(0);
		video::SMaterial & materialTangents = SceneNodeTangents->getMaterial(0);

		s32 selectedMaterial = ComboMaterial->getSelected();
		if ( selectedMaterial >= (s32)video::EMT_SOLID && selectedMaterial <= (s32)video::EMT_ONETEXTURE_BLEND)
		{
            video::E_VERTEX_TYPE vertexType = GetVertexTypeForMaterialType((video::E_MATERIAL_TYPE)selectedMaterial);
            switch ( vertexType )
            {
                case video::EVT_STANDARD:
                    material.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
                    SceneNode->setVisible(true);
                    SceneNode2T->setVisible(false);
                    SceneNodeTangents->setVisible(false);
                    break;
                case video::EVT_2TCOORDS:
                    material2T.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
                    SceneNode->setVisible(false);
                    SceneNode2T->setVisible(true);
                    SceneNodeTangents->setVisible(false);
                    break;
                case video::EVT_TANGENTS:
                    materialTangents.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
                    SceneNode->setVisible(false);
                    SceneNode2T->setVisible(false);
                    SceneNodeTangents->setVisible(true);
                    break;
            }
        }

        updateMaterial(material);
        updateMaterial(material2T);
        updateMaterial(materialTangents);

        AllColorsControl.setDirty(false);
        Texture1.setDirty(false);
        Texture2.setDirty(false);
	}

	void updateTextures()
	{
		Texture1.updateTextures(Driver);
		Texture2.updateTextures(Driver);
	}

protected:

    void updateMaterial(video::SMaterial & material)
    {
		if ( AllColorsControl.isDirty() )
		{
			material.AmbientColor = AllColorsControl.ControlAmbientColor.getColor();
			material.DiffuseColor = AllColorsControl.ControlDiffuseColor.getColor();
			material.EmissiveColor = AllColorsControl.ControlEmissiveColor.getColor();
			material.SpecularColor = AllColorsControl.ControlSpecularColor.getColor();
		}
		material.Lighting = ButtonLighting->isPressed();
		if ( Texture1.isDirty() )
		{
			material.TextureLayer[0].Texture = Driver->getTexture( io::path(Texture1.getSelectedTextureName()) );
		}
		if ( Texture2.isDirty() )
		{
			material.TextureLayer[1].Texture = Driver->getTexture( io::path(Texture2.getSelectedTextureName()) );
		}
    }

	bool Initialized;
	video::IVideoDriver * Driver;
	scene::IMeshSceneNode* SceneNode;
	scene::IMeshSceneNode* SceneNode2T;
	scene::IMeshSceneNode* SceneNodeTangents;
	SAllColorsControl AllColorsControl;
	gui::IGUIButton * ButtonLighting;
	gui::IGUIComboBox * ComboMaterial;
	STextureControl Texture1;
	STextureControl Texture2;
};

struct SLightNodeControl
{
	SLightNodeControl() : Initialized(false), SceneNode(0) {}

	virtual bool OnEvent(const SEvent &event)
	{
		if ( AllColorsControl.OnEvent(event) )
			return true;
		return false;
	}

	void init(scene::ILightSceneNode* node, gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t * description)
	{
		if ( Initialized || !node || !guiEnv) // initializing twice or with invalid data not allowed
			return;
		SceneNode = node;
		AllColorsControl.init(guiEnv, pos, description, false);
		const video::SLight & lightData = SceneNode->getLightData();
		if ( AllColorsControl.isDirty() )
		{
			AllColorsControl.ControlAmbientColor.setColor(lightData.AmbientColor.toSColor());
			AllColorsControl.ControlDiffuseColor.setColor(lightData.DiffuseColor.toSColor());
			AllColorsControl.ControlSpecularColor.setColor(lightData.SpecularColor.toSColor());
			AllColorsControl.setDirty(false);
		}
		Initialized = true;
	}

	void update()
	{
		if ( !Initialized )
			return;

		video::SLight & lightData = SceneNode->getLightData();
		lightData.AmbientColor = video::SColorf( AllColorsControl.ControlAmbientColor.getColor() );
		lightData.DiffuseColor = video::SColorf( AllColorsControl.ControlDiffuseColor.getColor() );
		lightData.SpecularColor = video::SColorf( AllColorsControl.ControlSpecularColor.getColor() );
	}

protected:
	bool Initialized;
	scene::ILightSceneNode* SceneNode;
	SAllColorsControl AllColorsControl;
};

// Main application class
class CApp : public IEventReceiver
{
    friend int main(int argc, char *argv[]);

public:
    CApp()
	: IsRunning(false)
	, Device(0)
	, Camera(0)
	{
	}

    ~CApp()
	{
	}

	// stop running - will quit at end of mainloop
    void stopApp()
	{
		IsRunning = false;
	}

	virtual bool OnEvent(const SEvent &event)
	{
		if ( NodeLeft.OnEvent(event) )
			return true;
		if ( NodeRight.OnEvent(event) )
			return true;
		if ( LightControl.OnEvent(event) )
			return true;
		if ( GlobalAmbient.OnEvent(event) )
			return true;

		if (event.EventType == EET_GUI_EVENT)
		{
			//s32 id = event.GUIEvent.Caller->getID();
			gui::IGUIEnvironment* env = Device->getGUIEnvironment();

			switch(event.GUIEvent.EventType)
			{
				case gui::EGET_MENU_ITEM_SELECTED:
				{
					gui::IGUIContextMenu* menu = (gui::IGUIContextMenu*)event.GUIEvent.Caller;
					s32 id = menu->getItemCommandId(menu->getSelectedItem());

					switch(id)
					{
						case GUI_ID_OPEN_TEXTURE: // File -> Open Texture
							env->addFileOpenDialog(L"Please select a texture file to open");
						break;
					}
				}
				break;

				case gui::EGET_FILE_SELECTED:
				{
					// load the model file, selected in the file open dialog
					gui::IGUIFileOpenDialog* dialog =
						(gui::IGUIFileOpenDialog*)event.GUIEvent.Caller;
					loadTexture(io::path(dialog->getFileName()).c_str());
				}
				break;

				default:
				break;
			}
		}

		return false;
	}

protected:

    bool init(int argc, char *argv[])
	{
		Config.DriverType = getDriverTypeFromConsole();
		if ( (int)Config.DriverType < 0 )
			return false;

		Device = createDevice(Config.DriverType, Config.ScreenSize);
		if (!Device)
			return false;

        Device->setWindowCaption( DriverTypeNames[Config.DriverType] );
		Device->setEventReceiver(this);
		scene::ISceneManager* smgr = Device->getSceneManager();
		video::IVideoDriver * driver = Device->getVideoDriver ();
		gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();

		createDefaultTextures(driver);

		gui::IGUIContextMenu * menuBar = guiEnv->addMenu();
		menuBar->addItem(L"File", -1, true, true);

		gui::IGUIContextMenu* subMenuFile = menuBar->getSubMenu(0);
		subMenuFile->addItem(L"Open texture ...", GUI_ID_OPEN_TEXTURE);

		Camera = smgr->addCameraSceneNode (/*ISceneNode *parent=*/0,
											/*const core::vector3df &position=*/core::vector3df(0, 0, 0),
											/*const core::vector3df &lookat=*/core::vector3df(0, 0, 100),
		                                    /*s32 id=*/-1);

		scene::IMeshSceneNode* nodeL = smgr->addCubeSceneNode (/*f32 size=*/10.0f, /*ISceneNode *parent=*/0, /*s32 id=*/-1,
		                                   /*const core::vector3df &position=*/core::vector3df(-35, 0, 100),
		                                   /*const core::vector3df &rotation=*/core::vector3df(0, 0, 0),
		                                   /*const core::vector3df &scale=*/core::vector3df(3.0f, 3.0f, 3.0f));
		NodeLeft.init( nodeL, Device, core::position2d<s32>(10,20), L"left node" );

		scene::IMeshSceneNode* nodeR = smgr->addCubeSceneNode (/*f32 size=*/10.0f, /*ISceneNode *parent=*/0, /*s32 id=*/-1,
		                                   /*const core::vector3df &position=*/core::vector3df(35, 0, 100),
		                                   /*const core::vector3df &rotation=*/core::vector3df(0, 0, 0),
		                                   /*const core::vector3df &scale=*/core::vector3df(3.0f, 3.0f, 3.0f));
		NodeRight.init( nodeR, Device, core::position2d<s32>(530,20), L"right node" );

		scene::ILightSceneNode* nodeLight = smgr->addLightSceneNode(/*ISceneNode *parent=*/0,
														/*const core::vector3df &position=*/core::vector3df(0, 0, 0),
														/*video::SColorf color=*/video::SColorf(1.0f, 1.0f, 1.0f),
														/*f32 radius=*/100.0f);
		LightControl.init(nodeLight, guiEnv, core::position2d<s32>(270,20), L"light" );

		GlobalAmbient.init( guiEnv, core::position2d<s32>(270, 300), L"global ambient" );
		GlobalAmbient.setColor( smgr->getAmbientLight().toSColor() );

		return true;
	}

	video::E_DRIVER_TYPE getDriverTypeFromConsole()
	{
		printf("Please select the driver you want for this example:\n"\
			" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
			" (d) Software Renderer\n (e) Burning's Software Renderer\n"\
			" (f) NullDevice\n (otherKey) exit\n\n");

		char i;
		std::cin >> i;

		switch(i)
		{

			case 'a': return video::EDT_DIRECT3D9;
			case 'b': return video::EDT_DIRECT3D8;
			case 'c': return video::EDT_OPENGL;
			case 'd': return video::EDT_SOFTWARE;
			case 'e': return video::EDT_BURNINGSVIDEO;
			case 'f': return video::EDT_NULL;
			default: return video::E_DRIVER_TYPE(-1);
		}
	}

	bool update()
	{
		using namespace irr;

    	video::IVideoDriver* videoDriver =  Device->getVideoDriver();
		if ( !Device->run() )
			return false;

		if ( Device->isWindowActive() || Config.RenderInBackground )
		{
			gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
			scene::ISceneManager* smgr = Device->getSceneManager();
			gui::IGUISkin * skin = guiEnv->getSkin();

			// remove some alpha value because it makes those menus harder to read otherwise
			video::SColor col3dHighLight( skin->getColor(gui::EGDC_APP_WORKSPACE) );
			col3dHighLight.setAlpha(255);
			video::SColor colHighLight( col3dHighLight );
			skin->setColor(gui::EGDC_HIGH_LIGHT, colHighLight );
			skin->setColor(gui::EGDC_3D_HIGH_LIGHT, col3dHighLight );

			NodeLeft.update();
			NodeRight.update();
			LightControl.update();

			if ( GlobalAmbient.isDirty() )
			{
				smgr->setAmbientLight( GlobalAmbient.getColor() );
				GlobalAmbient.setDirty(false);
			}

			video::SColor bkColor( skin->getColor(gui::EGDC_APP_WORKSPACE) );
			videoDriver->beginScene(true, true, bkColor);

			smgr->drawAll();
			guiEnv->drawAll();

			videoDriver->endScene();
		}

		return true;
	}

    void run()
	{
		IsRunning = true;

		if ( !Device )
			return;

		// main application loop
		while(IsRunning)
		{
			if ( !update() )
				break;

			Device->sleep( 5 );
		}
	}

    void quit()
	{
		IsRunning = false;
		if ( Device )
		{
			Device->closeDevice();
			Device->drop();
			Device = NULL;
		}
	}

	// Create some useful textures.
	// Note that the function put readability over speed, you shouldn't use setPixel at runtime but for initialization it's nice.
	void createDefaultTextures(video::IVideoDriver * driver)
	{
		const u32 width = 256;
		const u32 height = 256;
		video::IImage * imageA8R8G8B8 = driver->createImage (video::ECF_A8R8G8B8, core::dimension2d<u32>(width, height));
		if ( !imageA8R8G8B8 )
			return;
		const u32 pitch = imageA8R8G8B8->getPitch();

		// some nice caro with 9 useful colors
		for ( u32 y = 0; y < height; ++ y )
		{
			for ( u32 x = 0; x < pitch; ++x )
			{
				if ( y < height/3 )
				{
					if ( x < width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_BLACK);
					else if ( x < 2*width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_BLUE);
					else
						imageA8R8G8B8->setPixel (x, y, SCOL_CYAN);
				}
				else if ( y < 2*height/3 )
				{
					if ( x < width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_GRAY);
					else if ( x < 2*width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_GREEN);
					else
						imageA8R8G8B8->setPixel (x, y, SCOL_MAGENTA);
				}
				else
				{
					if ( x < width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_RED);
					else if ( x < 2*width/3 )
						imageA8R8G8B8->setPixel (x, y, SCOL_YELLOW);
					else
						imageA8R8G8B8->setPixel (x, y, SCOL_WHITE);
				}
			}
		}
		driver->addTexture (io::path("CARO_A8R8G8B8"), imageA8R8G8B8);

		imageA8R8G8B8->fill(SCOL_WHITE);
		driver->addTexture (io::path("WHITE_A8R8G8B8"), imageA8R8G8B8);

		imageA8R8G8B8->fill(SCOL_BLACK);
		driver->addTexture (io::path("BLACK_A8R8G8B8"), imageA8R8G8B8);

		// gray-scale
		for ( u32 y = 0; y < height; ++ y )
		{
			for ( u32 x = 0; x < pitch; ++x )
			{
				imageA8R8G8B8->setPixel (x, y, video::SColor(y, x,x,x) );
			}
		}
		driver->addTexture (io::path("GRAYSCALE_A8R8G8B8"), imageA8R8G8B8);
	}

	void loadTexture(const io::path &name)
	{
		Device->getVideoDriver()->getTexture(name);
		NodeLeft.updateTextures();
		NodeRight.updateTextures();
	}

private:
	SConfig						Config;
    volatile bool				IsRunning;
	IrrlichtDevice * 			Device;
	scene::ICameraSceneNode *	Camera;
	SMeshNodeControl			NodeLeft;
	SMeshNodeControl			NodeRight;
	SLightNodeControl			LightControl;
	CColorControl				GlobalAmbient;
};

int main(int argc, char *argv[])
{
	CApp APP;

    if ( !APP.init(argc, argv) )
	{
		printf("init failed\n");
        return 1;
	}

	APP.run();
	APP.quit();

	return 0;
}

Posted: Tue Oct 20, 2009 11:29 pm
by CuteAlien
OK, I hunted a little for differences and found at least those material differences bothering me currently. I've not yet checked in to svn because I'm not sure yet if the lightmap fix is OK and into which version I should put it (those are bugfixes - but they will probably change colors in many existing projects).

So my finds where:

EMT_LIGHTMAP_ADD uses D3DTOP_ADD on D3D which just adds and GL_ADD_SIGNED_ARB on GL which does (Arg0 + Arg1 - 0.5). Should either use GL_ADD or D3DTOP_ADDSIGNED or more lightmap enums or parametrized lightmaps. I decided on just using ADD now everywhere because of the name of the enum, but that signed_add certainly sounds useful, so not sure yet what is to prefer.

EMT_LIGHTMAP_LIGHTING_M2 and EMT_LIGHTMAP_LIGHTING_M4 behave so far like EMT_LIGHTMAP_LIGHTING in D3D because of some missing checks. This made them look darker than in GL in those modes.

EMT_TRANSPARENT_ALPHA_CHANNEL_REF did ignore the light in GL. Using GL_MODULATE instead of GL_REPLACE in COpenGLMaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF will fix that.

There's a few more material differences especially on reflection2 and all the normalmaps (color transition seems always smoother on GL than on D3D), but I don't know enough how those should look correctly.

Also EMT_TRANSPARENT_ALPHA_CHANNEL has some 1-pixel border for me on D3D which it hasn't on GL which didn't bother me enough today but probably there's something open to fix.

Posted: Fri Oct 23, 2009 12:47 pm
by hybrid
Did you find any problems with ambient lighting so far? I'll put the ticket on 'Pending', please submit any findings there to keep it alive.

Posted: Wed Nov 18, 2009 12:55 pm
by tonic
hybrid wrote:Did you use textured materials? Because vertex colors were handled quite differently prior to Irrlicht 1.6
I think the problem case for me might have been one with even no textures but lightness coming from vertex lighting with geometry being subdivided a lot... I think I didn't use vertex colors in that case though, but I probably had a version of irr with ogl driver hacked to enable gl_color_material.

Have to check out at some point if the 1.6 has fixed this stuff already, at least the fixes for vertex colors are nice. :)

Posted: Tue Dec 01, 2009 3:46 pm
by CuteAlien
The code from above is now (in slightly updated form) in the svn trunk as example 22.

It would be great if you could use it to try if all such problems are fixed in current svn or if there are still differences.