irrlicht/examples/22.MaterialViewer/main.cpp
cutealien a3adfc196b CIrrDeviceWin32::yield() now uses Sleep(0) instead of Sleep(1).
We had changed that once before in the other direction in svn r421
Reason back then was "Sleep(0) doesn't allow any lower priority threads to execute"
But Microsoft changed the behaviour of Sleep(0) after Windows XP so that's no longer true.
And the costs of it is pretty high - due to this using a timer with a 15ms resolutions it meant not just giving up the thread but it also always waited for 15ms on Windows.
I also replaced a few sleep calls in examples for that reason with yield() calls.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6459 dfc29bdd-3216-0410-991c-e03cc46cb475
2023-04-03 15:32:41 +00:00

1146 lines
37 KiB
C++
Executable File

/** Example 022 Material Viewer
This example can be used to experiment with material settings and watch the results.
Only the default non-shader materials are used in here.
You have a node with a mesh, one dynamic light and global ambient light to play around with.
You can move the light with cursor-keys and +/-.
You can move the camera while left-mouse button is clicked.
*/
// TODO: Should be possible to set all material values by the GUI.
// For now just change the defaultMaterial in CApp::setActiveMeshNodeType for the rest.
#include <irrlicht.h>
#include "driverChoice.h"
#include "exampleHelper.h"
#include "main.h"
using namespace irr;
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
/*
Variables within the empty namespace are globals which are restricted to this file.
*/
namespace
{
// For the gui id's
enum EGUI_IDS
{
GUI_ID_OPEN_TEXTURE = 1,
GUI_ID_QUIT,
GUI_ID_MAX
};
// Name used in texture selection to clear the textures on the node
const core::stringw CLEAR_TEXTURE = L"CLEAR texture";
// some useful color constants
const video::SColor SCOL_BLACK = video::SColor(255, 0, 0, 0);
const video::SColor SCOL_BLUE = video::SColor(255, 0, 0, 255);
const video::SColor SCOL_CYAN = video::SColor(255, 0, 255, 255);
const video::SColor SCOL_GRAY = video::SColor(255, 128,128, 128);
const video::SColor SCOL_GREEN = video::SColor(255, 0, 255, 0);
const video::SColor SCOL_MAGENTA = video::SColor(255, 255, 0, 255);
const video::SColor SCOL_RED = video::SColor(255, 255, 0, 0);
const video::SColor SCOL_YELLOW = video::SColor(255, 255, 255, 0);
const video::SColor SCOL_WHITE = video::SColor(255, 255, 255, 255);
}; // namespace
/*
Returns a new unique number on each call.
*/
s32 makeUniqueId()
{
static int unique = GUI_ID_MAX;
++unique;
return unique;
}
/*
Find out which vertex-type is needed for the given material type.
*/
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;
case EMT_FORCE_32BIT:
return EVT_STANDARD;
}
return EVT_STANDARD;
}
/*
Custom GUI-control to edit color values.
*/
CColorControl::CColorControl(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, IGUIElement* parent, s32 id)
: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect< s32 >(pos, pos+core::dimension2d<s32>(80, 75)))
, DirtyFlag(true)
, Color(0)
, ColorStatic(0)
, EditAlpha(0)
, EditRed(0)
, EditGreen(0)
, EditBlue(0)
{
using namespace gui;
ButtonSetId = makeUniqueId();
const core::rect< s32 > rectControls(0,0,AbsoluteRect.getWidth(),AbsoluteRect.getHeight() );
IGUIStaticText * groupElement = guiEnv->addStaticText (L"", rectControls, true, false, this, -1, false);
groupElement->setNotClipped(true);
s32 border=guiEnv->getSkin()->getSize(EGDS_TEXT_DISTANCE_X);
guiEnv->addStaticText(text, core::rect<s32>(border,border,80,15), false, false, groupElement, -1, true);
EditAlpha = addEditForNumbers(guiEnv, core::position2d<s32>(border,15), L"a", -1, groupElement );
EditRed = addEditForNumbers(guiEnv, core::position2d<s32>(border,30), L"r", -1, groupElement );
EditGreen = addEditForNumbers(guiEnv, core::position2d<s32>(border,45), L"g", -1, groupElement );
EditBlue = addEditForNumbers(guiEnv, core::position2d<s32>(border,60), L"b", -1, groupElement );
ColorStatic = guiEnv->addStaticText (L"", core::rect<s32>(60,15,80,75), true, false, groupElement, -1, true);
guiEnv->addButton (core::rect<s32>(60,35,80,50), groupElement, ButtonSetId, L"set");
setEditsFromColor(Color);
}
// event receiver
bool CColorControl::OnEvent(const SEvent &event)
{
if ( event.EventType == EET_GUI_EVENT
&& event.GUIEvent.Caller->getID() == ButtonSetId
&& event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
{
Color = getColorFromEdits();
setEditsFromColor(Color);
}
return false;
}
// set the color values
void CColorControl::setColor(const video::SColor& col)
{
DirtyFlag = true;
Color = col;
setEditsFromColor(Color);
}
// Add a statictext for a description + an editbox so users can enter numbers
gui::IGUIEditBox* CColorControl::addEditForNumbers(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, s32 id, gui::IGUIElement * parent)
{
using namespace gui;
core::recti rect(pos, pos+core::dimension2d<s32>(10, 15));
guiEnv->addStaticText(text, rect, false, false, parent, -1, false);
rect += core::position2d<s32>( 20, 0 );
rect.LowerRightCorner.X += 20;
gui::IGUIEditBox* edit = guiEnv->addEditBox(L"0", rect, true, parent, id);
return edit;
}
// Get the color value from the editboxes
video::SColor CColorControl::getColorFromEdits() const
{
video::SColor col;
if (EditAlpha)
{
u32 alpha = core::min_(core::strtoul10(core::stringc(EditAlpha->getText()).c_str()), 255u);
col.setAlpha(alpha);
}
if (EditRed)
{
u32 red = core::min_(core::strtoul10(core::stringc(EditRed->getText()).c_str()), 255u);
col.setRed(red);
}
if (EditGreen)
{
u32 green = core::min_(core::strtoul10(core::stringc(EditGreen->getText()).c_str()), 255u);
col.setGreen(green);
}
if (EditBlue)
{
u32 blue = core::min_(core::strtoul10(core::stringc(EditBlue->getText()).c_str()), 255u);
col.setBlue(blue);
}
return col;
}
// Fill the editboxes with a color value
void CColorControl::setEditsFromColor(video::SColor col)
{
DirtyFlag = 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);
}
/*
Custom GUI-control for to edit all colors typically used in materials and lights
*/
// Constructor
CTypicalColorsControl::CTypicalColorsControl(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, bool hasEmissive, IGUIElement* parent, s32 id)
: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect<s32>(pos,pos+core::dimension2d<s32>(60,250)))
, ControlAmbientColor(0), ControlDiffuseColor(0), ControlSpecularColor(0), ControlEmissiveColor(0)
{
ControlAmbientColor = new CColorControl( guiEnv, core::position2d<s32>(0, 0), L"Ambient", this);
ControlDiffuseColor = new CColorControl( guiEnv, core::position2d<s32>(0, 75), L"Diffuse", this );
ControlSpecularColor = new CColorControl( guiEnv, core::position2d<s32>(0, 150), L"Specular", this );
if ( hasEmissive )
{
ControlEmissiveColor = new CColorControl( guiEnv, core::position2d<s32>(0, 225), L"Emissive", this );
}
}
// Destructor
CTypicalColorsControl::~CTypicalColorsControl()
{
ControlAmbientColor->drop();
ControlDiffuseColor->drop();
if ( ControlEmissiveColor )
ControlEmissiveColor->drop();
ControlSpecularColor->drop();
}
// Set the color values to those within the material
void CTypicalColorsControl::setColorsToMaterialColors(const video::SMaterial & material)
{
ControlAmbientColor->setColor(material.AmbientColor);
ControlDiffuseColor->setColor(material.DiffuseColor);
ControlEmissiveColor->setColor(material.EmissiveColor);
ControlSpecularColor->setColor(material.SpecularColor);
}
// Update all changed colors in the material
void CTypicalColorsControl::updateMaterialColors(video::SMaterial & material) const
{
if ( ControlAmbientColor->isDirty() )
material.AmbientColor = ControlAmbientColor->getColor();
if ( ControlDiffuseColor->isDirty() )
material.DiffuseColor = ControlDiffuseColor->getColor();
if ( ControlEmissiveColor->isDirty() )
material.EmissiveColor = ControlEmissiveColor->getColor();
if ( ControlSpecularColor->isDirty() )
material.SpecularColor = ControlSpecularColor->getColor();
}
// Set the color values to those from the light data
void CTypicalColorsControl::setColorsToLightDataColors(const video::SLight & lightData)
{
ControlAmbientColor->setColor(lightData.AmbientColor.toSColor());
ControlDiffuseColor->setColor(lightData.DiffuseColor.toSColor());
ControlSpecularColor->setColor(lightData.SpecularColor.toSColor());
}
// Update all changed colors in the light data
void CTypicalColorsControl::updateLightColors(video::SLight & lightData) const
{
if ( ControlAmbientColor->isDirty() )
lightData.AmbientColor = video::SColorf( ControlAmbientColor->getColor() );
if ( ControlDiffuseColor->isDirty() )
lightData.DiffuseColor = video::SColorf( ControlDiffuseColor->getColor() );
if ( ControlSpecularColor->isDirty() )
lightData.SpecularColor = video::SColorf(ControlSpecularColor->getColor() );
}
// To reset the dirty flags
void CTypicalColorsControl::resetDirty()
{
ControlAmbientColor->resetDirty();
ControlDiffuseColor->resetDirty();
ControlSpecularColor->resetDirty();
if ( ControlEmissiveColor )
ControlEmissiveColor->resetDirty();
}
/*
GUI-Control to offer a selection of available textures.
*/
CTextureControl::CTextureControl(gui::IGUIEnvironment* guiEnv, video::IVideoDriver * driver, const core::position2d<s32> & pos, IGUIElement* parent, s32 id)
: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect<s32>(pos,pos+core::dimension2d<s32>(150,15)))
, DirtyFlag(true), ComboTexture(0)
{
core::rect<s32> rectCombo(0, 0, AbsoluteRect.getWidth(),AbsoluteRect.getHeight());
ComboTexture = guiEnv->addComboBox (rectCombo, this);
updateTextures(driver);
}
bool CTextureControl::OnEvent(const SEvent &event)
{
if ( event.EventType == EET_GUI_EVENT
&& event.GUIEvent.Caller == ComboTexture
&& event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
{
DirtyFlag = true;
}
return false;
}
// Workaround for a problem with comboboxes.
// We have to get in front when the combobox wants to get in front or combobox-list might be drawn below other elements.
bool CTextureControl::bringToFront(IGUIElement* element)
{
bool result = gui::IGUIElement::bringToFront(element);
if ( Parent && element == ComboTexture )
result &= Parent->bringToFront(this);
return result;
}
// return selected texturename (if any, otherwise 0)
const wchar_t * CTextureControl::getSelectedTextureName() const
{
s32 selected = ComboTexture->getSelected();
if ( selected < 0 )
return 0;
return ComboTexture->getItem(selected);
}
void CTextureControl::selectTextureByName(const irr::core::stringw& name)
{
for (u32 i=0; i< ComboTexture->getItemCount(); ++i)
{
if ( name == ComboTexture->getItem(i))
{
ComboTexture->setSelected(i);
DirtyFlag = true;
return;
}
}
}
// Put the names of all currently loaded textures in a combobox
void CTextureControl::updateTextures(video::IVideoDriver * driver)
{
s32 oldSelected = ComboTexture->getSelected();
core::stringw oldTextureName;
if ( oldSelected >= 0 )
{
oldTextureName = ComboTexture->getItem(oldSelected);
}
ComboTexture->clear();
s32 selectNew = -1;
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.empty() && selectNew < 0 && name == oldTextureName )
selectNew = i;
}
// add another name which can be used to clear the texture
ComboTexture->addItem( CLEAR_TEXTURE.c_str() );
if ( CLEAR_TEXTURE == oldTextureName )
selectNew = ComboTexture->getItemCount()-1;
if ( selectNew >= 0 )
ComboTexture->setSelected(selectNew);
DirtyFlag = true;
}
/*
Control which allows setting some of the material values for a meshscenenode
*/
void CMaterialControl::init(IrrlichtDevice * device, const core::position2d<s32> & pos, const wchar_t * description)
{
if ( Initialized || !device) // initializing twice or with invalid data not allowed
return;
Driver = device->getVideoDriver ();
gui::IGUIEnvironment* guiEnv = device->getGUIEnvironment();
s32 top = pos.Y;
// Description
guiEnv->addStaticText(description, core::rect<s32>(pos.X, top, pos.X+150, top+15), true, false, 0, -1, true);
top += 15;
// Control for material type
core::rect<s32> rectCombo(pos.X, top, pos.X+150, top+15);
top += 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(0);
// Control to enable/disabling material lighting
core::rect<s32> rectBtn(core::position2d<s32>(pos.X, top), core::dimension2d<s32>(100, 15));
top += 15;
ButtonLighting = guiEnv->addButton (rectBtn, 0, -1, L"Lighting");
ButtonLighting->setIsPushButton(true);
core::rect<s32> rectInfo( rectBtn.LowerRightCorner.X, rectBtn.UpperLeftCorner.Y, rectBtn.LowerRightCorner.X+50, rectBtn.UpperLeftCorner.Y+15 );
InfoLighting = guiEnv->addStaticText(L"", rectInfo, true, false );
InfoLighting->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER );
// Controls for colors
TypicalColorsControl = new CTypicalColorsControl(guiEnv, core::position2d<s32>(pos.X, top), true, guiEnv->getRootGUIElement());
top += 300;
guiEnv->addStaticText(L"Shininess", core::rect<s32>(pos.X, top, pos.X + 150, top + 15), true, false, 0, -1, true);
top += 15;
ShininessControl = guiEnv->addScrollBar(true, core::rect<s32>(pos.X, top, pos.X + 150, top + 15));
ShininessControl->setMax(10000);
top += 20;
// Controls for selecting the material textures
guiEnv->addStaticText(L"Textures", core::rect<s32>(pos.X, top, pos.X+150, top+15), true, false, 0, -1, true);
top += 15;
// The default material types only use first 2 textures
irr::u32 maxTextures = core::min_(2u, irr::video::MATERIAL_MAX_TEXTURES);
for (irr::u32 i=0; i<maxTextures; ++i)
{
TextureControls.push_back(new CTextureControl(guiEnv, Driver, core::position2di(pos.X, top), guiEnv->getRootGUIElement()));
top += 15;
}
Initialized = true;
}
void CMaterialControl::setMaterial(const irr::video::SMaterial & material)
{
if (ComboMaterial)
ComboMaterial->setSelected( (s32)material.MaterialType );
if (ButtonLighting)
ButtonLighting->setPressed(material.Lighting);
if (TypicalColorsControl)
TypicalColorsControl->setColorsToMaterialColors(material);
for (irr::u32 i=0; i<TextureControls.size(); ++i)
TextureControls[i]->setDirty();
if (ShininessControl)
ShininessControl->setPos((int)(material.Shininess*100.f));
}
void CMaterialControl::update(scene::IMeshSceneNode* sceneNode, scene::IMeshSceneNode* sceneNode2T, scene::IMeshSceneNode* sceneNodeTangents)
{
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)
{
// Show the node which has a mesh to work with the currently selected material
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;
}
}
// Always update materials of all nodes, otherwise the tool is confusing to use.
updateMaterial(material);
updateMaterial(material2T);
updateMaterial(materialTangents);
if ( ButtonLighting->isPressed() )
InfoLighting->setText(L"is on");
else
InfoLighting->setText(L"is off");
TypicalColorsControl->resetDirty();
for (irr::u32 i=0; i<TextureControls.size(); ++i)
TextureControls[i]->resetDirty();
}
void CMaterialControl::updateTextures()
{
for (irr::u32 i=0; i<TextureControls.size(); ++i)
TextureControls[i]->updateTextures(Driver);
}
void CMaterialControl::selectTextures(const irr::core::stringw& name)
{
for (irr::u32 i=0; i<TextureControls.size(); ++i)
TextureControls[i]->selectTextureByName(name);
}
bool CMaterialControl::isLightingEnabled() const
{
return ButtonLighting && ButtonLighting->isPressed();
}
void CMaterialControl::updateMaterial(video::SMaterial & material)
{
TypicalColorsControl->updateMaterialColors(material);
material.Lighting = ButtonLighting->isPressed();
for (irr::u32 i=0; i<TextureControls.size(); ++i)
{
if ( TextureControls[i]->isDirty() )
{
material.TextureLayer[i].Texture = Driver->findTexture( io::path(TextureControls[i]->getSelectedTextureName()) );
}
}
material.Shininess = ShininessControl->getPos() * 0.01f;
}
/*
Control to allow setting the color values of a lightscenenode.
*/
void CLightNodeControl::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;
gui::IGUIStaticText* st = guiEnv->addStaticText(description, core::rect<s32>(pos.X, pos.Y, pos.X+80, pos.Y+15), true, false, 0, -1, true);
st->setAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
TypicalColorsControl = new CTypicalColorsControl(guiEnv, core::position2d<s32>(pos.X, pos.Y+15), false, guiEnv->getRootGUIElement());
TypicalColorsControl->setAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
const video::SLight & lightData = node->getLightData();
TypicalColorsControl->setColorsToLightDataColors(lightData);
Initialized = true;
}
void CLightNodeControl::update(scene::ILightSceneNode* node)
{
if ( !Initialized )
return;
video::SLight & lightData = node->getLightData();
TypicalColorsControl->updateLightColors(lightData);
}
/*
Main application class
*/
// Event handler
bool CApp::OnEvent(const SEvent &event)
{
if (event.EventType == EET_GUI_EVENT)
{
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;
case GUI_ID_QUIT: // File -> Quit
setRunning(false);
break;
}
}
break;
case gui::EGET_FILE_SELECTED:
{
// load the texture file, selected in the file open dialog
gui::IGUIFileOpenDialog* dialog =
(gui::IGUIFileOpenDialog*)event.GUIEvent.Caller;
loadTexture(io::path(dialog->getFileName()).c_str());
}
break;
case gui::EGET_COMBO_BOX_CHANGED:
if (event.GUIEvent.Caller == ComboMeshType )
{
irr::scene::IMeshSceneNode* currentNode = getVisibleMeshNode();
if (currentNode)
{
// ensure next mesh will get same color and material settings
if ( ControlVertexColors )
{
video::S3DVertex * vertices = (video::S3DVertex *)currentNode->getMesh()->getMeshBuffer(0)->getVertices();
ControlVertexColors->setColor(vertices[0].Color);
}
if ( MeshMaterialControl )
MeshMaterialControl->setMaterial(currentNode->getMaterial(0));
}
setActiveMeshNodeType((ENodeType)ComboMeshType->getSelected());
return true;
}
break;
default:
break;
}
}
else if (event.EventType == EET_KEY_INPUT_EVENT)
{
KeysPressed[event.KeyInput.Key] = event.KeyInput.PressedDown;
}
else if (event.EventType == EET_MOUSE_INPUT_EVENT)
{
if (!MousePressed && event.MouseInput.isLeftPressed())
{
gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
if ( guiEnv->getHovered() == guiEnv->getRootGUIElement() ) // Click on background
{
MousePressed = true;
MouseStart.X = event.MouseInput.X;
MouseStart.Y = event.MouseInput.Y;
}
}
else if (MousePressed && !event.MouseInput.isLeftPressed())
{
MousePressed = false;
}
}
return false;
}
// Application initialization
// returns true when it was successful initialized, otherwise false.
bool CApp::init(int argc, char *argv[])
{
// ask user for driver
Config.DriverType=driverChoiceConsole();
if (Config.DriverType==video::EDT_COUNT)
return false;
// create the device with the settings from our config
Device = createDevice(Config.DriverType, Config.ScreenSize);
if (!Device)
return false;
Device->setWindowCaption( core::stringw(video::DRIVER_TYPE_NAMES[Config.DriverType]).c_str() );
Device->setEventReceiver(this);
scene::ISceneManager* smgr = Device->getSceneManager();
video::IVideoDriver * driver = Device->getVideoDriver ();
gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
MeshManipulator = smgr->getMeshManipulator();
// set a nicer font
gui::IGUISkin* skin = guiEnv->getSkin();
gui::IGUIFont* font = guiEnv->getFont(getExampleMediaPath() + "fonthaettenschweiler.bmp");
if (font)
skin->setFont(font);
// 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 );
// Add some textures which are useful to test material settings
createDefaultTextures(driver);
// create a menu
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);
subMenuFile->addSeparator();
subMenuFile->addItem(L"Quit", GUI_ID_QUIT);
// a static camera
Camera = smgr->addCameraSceneNode (0, core::vector3df(0, 30, -50),
core::vector3df(0, 0, 0),
-1);
setActiveMeshNodeType(ENT_CUBE);
const s32 controlsTop = 20;
MeshMaterialControl = new CMaterialControl();
MeshMaterialControl->init( Device, core::position2d<s32>(10,controlsTop), L"Material");
MeshMaterialControl->setMaterial(SceneNode->getMaterial(0));
MeshMaterialControl->selectTextures(core::stringw("CARO_A8R8G8B8")); // set a useful default texture
// add one light
const f32 lightRadius = 80.f;
NodeLight = smgr->addLightSceneNode(0, core::vector3df(0, 30, -70),
video::SColorf(1.0f, 1.0f, 1.0f),
lightRadius);
LightControl = new CLightNodeControl();
LightControl->init(NodeLight, guiEnv, core::position2d<s32>(550,controlsTop), L"Dynamic light" );
#if 0 // enable to have some visual feedback for the light size
scene::IMeshSceneNode* lightRadiusNode = smgr->addSphereSceneNode(lightRadius, 64, NodeLight);
lightRadiusNode->getMaterial(0).Lighting = false;
lightRadiusNode->getMaterial(0).Wireframe = true;
#endif
// one large cube around everything. That's mainly to make the light more obvious.
scene::IMeshSceneNode* backgroundCube = smgr->addCubeSceneNode (200.0f, 0, -1, core::vector3df(0, 0, 0),
core::vector3df(45, 0, 0),
core::vector3df(1.0f, 1.0f, 1.0f));
backgroundCube->getMaterial(0).BackfaceCulling = false; // we are within the cube, so we have to disable backface culling to see it
backgroundCube->getMaterial(0).EmissiveColor.set(255,50,50,50); // we keep some self lighting to keep texts visible
// Add a the mesh UI controls
gui::IGUIStaticText* stMesh = guiEnv->addStaticText(L"Mesh", core::rect<s32>(440, controlsTop, 520, controlsTop+15), true, false, 0, -1, true);
stMesh->setAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
ComboMeshType = guiEnv->addComboBox(core::rect<s32>(440, controlsTop+16, 520, controlsTop+30), 0, -1);
ComboMeshType->setAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
ComboMeshType->addItem(L"cube");
ComboMeshType->addItem(L"sphere");
ComboMeshType->addItem(L"sphere highres");
ControlVertexColors = new CColorControl( guiEnv, core::position2d<s32>(440, controlsTop+30), L"Vertex colors", guiEnv->getRootGUIElement());
ControlVertexColors->setAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
ControlVertexColors->setColor(irr::video::SColor(255,255,255,255));
// Add a control for ambient light
GlobalAmbient = new CColorControl( guiEnv, core::position2d<s32>(550, 300), L"Global ambient", guiEnv->getRootGUIElement());
GlobalAmbient->setColor( smgr->getAmbientLight().toSColor() );
GlobalAmbient->setAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
return true;
}
/*
Update one frame
*/
bool CApp::update()
{
video::IVideoDriver* videoDriver = Device->getVideoDriver();
if ( !Device->run() )
return false;
// Figure out delta time since last frame
u32 newTick = Device->getTimer()->getRealTime();
f32 deltaTime = RealTimeTick > 0 ? f32(newTick-RealTimeTick)/1000.f : 0.f; // in seconds
RealTimeTick = newTick;
if ( Device->isWindowActive() || Config.RenderInBackground )
{
gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
scene::ISceneManager* smgr = Device->getSceneManager();
gui::IGUISkin * skin = guiEnv->getSkin();
// update our controls
MeshMaterialControl->update(SceneNode, SceneNode2T, SceneNodeTangents);
LightControl->update(NodeLight);
// Update vertices
if ( ControlVertexColors->isDirty() )
{
MeshManipulator->setVertexColors (SceneNode->getMesh(), ControlVertexColors->getColor());
MeshManipulator->setVertexColors (SceneNode2T->getMesh(), ControlVertexColors->getColor());
MeshManipulator->setVertexColors (SceneNodeTangents->getMesh(), ControlVertexColors->getColor());
ControlVertexColors->resetDirty();
}
// update ambient light settings
if ( GlobalAmbient->isDirty() )
{
smgr->setAmbientLight( GlobalAmbient->getColor() );
GlobalAmbient->resetDirty();
}
const float zoomSpeed = (KeysPressed[KEY_LSHIFT] ? 40.f : 10.f) * deltaTime;
const float rotationSpeed = (KeysPressed[KEY_LSHIFT] ? 20.f : 100.f) * deltaTime;
// Let the user move the light around
irr::gui::IGUIElement* focus=guiEnv->getFocus(); // some checks to prevent interfering with UI input
if ( !focus || focus == guiEnv->getRootGUIElement()
|| focus->getType() == irr::gui::EGUIET_STATIC_TEXT
|| focus->getType() == irr::gui::EGUIET_BUTTON
)
{
if ( KeysPressed[KEY_PLUS] || KeysPressed[KEY_ADD])
ZoomOut(NodeLight, zoomSpeed);
if ( KeysPressed[KEY_MINUS] || KeysPressed[KEY_SUBTRACT])
ZoomOut(NodeLight, -zoomSpeed);
if ( KeysPressed[KEY_RIGHT])
RotateHorizontal(NodeLight, rotationSpeed);
if ( KeysPressed[KEY_LEFT])
RotateHorizontal(NodeLight, -rotationSpeed);
UpdateRotationAxis(NodeLight, LightRotationAxis);
if ( KeysPressed[KEY_UP])
RotateAroundAxis(NodeLight, rotationSpeed, LightRotationAxis);
if ( KeysPressed[KEY_DOWN])
RotateAroundAxis(NodeLight, -rotationSpeed, LightRotationAxis);
}
// Let the user move the camera around
if (MousePressed && !focus)
{
gui::ICursorControl* cursorControl = Device->getCursorControl();
const core::position2d<s32>& mousePos = cursorControl->getPosition ();
RotateHorizontal(Camera, rotationSpeed * (MouseStart.X - mousePos.X));
RotateAroundAxis(Camera, rotationSpeed * (mousePos.Y - MouseStart.Y), CameraRotationAxis);
MouseStart = mousePos;
}
// draw everything
video::SColor bkColor( skin->getColor(gui::EGDC_APP_WORKSPACE) );
videoDriver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, bkColor);
smgr->drawAll();
guiEnv->drawAll();
if ( MeshMaterialControl->isLightingEnabled() )
{
// draw a line from the light to the target
video::SMaterial lineMaterial;
lineMaterial.Lighting = false;
videoDriver->setMaterial(lineMaterial);
videoDriver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
videoDriver->draw3DLine(NodeLight->getAbsolutePosition(), SceneNode->getAbsolutePosition());
}
videoDriver->endScene();
}
// be nice
Device->yield();
return true;
}
// Close down the application
void CApp::quit()
{
IsRunning = false;
delete LightControl;
LightControl = NULL;
delete MeshMaterialControl;
MeshMaterialControl = NULL;
if ( ControlVertexColors )
{
ControlVertexColors->drop();
ControlVertexColors = NULL;
}
if ( GlobalAmbient )
{
GlobalAmbient->drop();
GlobalAmbient = NULL;
}
if ( Device )
{
Device->closeDevice();
Device->drop();
Device = NULL;
}
}
// Create some useful textures.
void CApp::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 square-pattern with 9 typical colors
// Note that the function put readability over speed, you shouldn't use setPixel at runtime but for initialization it's nice.
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);
// all white
imageA8R8G8B8->fill(SCOL_WHITE);
driver->addTexture (io::path("WHITE_A8R8G8B8"), imageA8R8G8B8);
// all black
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);
imageA8R8G8B8->drop();
}
// Load a texture and make sure UI knows it when more textures are available.
void CApp::loadTexture(const io::path &name)
{
Device->getVideoDriver()->getTexture(name);
MeshMaterialControl->updateTextures();
}
void CApp::RotateHorizontal(irr::scene::ISceneNode* node, irr::f32 angle)
{
if ( node )
{
core::vector3df pos(node->getPosition());
core::vector2df dir(pos.X, pos.Z);
dir.rotateBy(angle);
pos.X = dir.X;
pos.Z = dir.Y;
node->setPosition(pos);
}
}
void CApp::RotateAroundAxis(irr::scene::ISceneNode* node, irr::f32 angle, const irr::core::vector3df& axis)
{
if ( node )
{
// TOOD: yeah, doesn't rotate around top/bottom yet. Fixes welcome.
core::vector3df pos(node->getPosition());
core::matrix4 mat;
mat.setRotationAxisRadians (core::degToRad(angle), axis);
mat.rotateVect(pos);
node->setPosition(pos);
}
}
void CApp::ZoomOut(irr::scene::ISceneNode* node, irr::f32 units)
{
if ( node )
{
core::vector3df pos(node->getPosition());
irr::f32 len = pos.getLength() + units;
pos.setLength(len);
node->setPosition(pos);
}
}
void CApp::UpdateRotationAxis(irr::scene::ISceneNode* node, irr::core::vector3df& axis)
{
// Find a perpendicular axis to the x,z vector. If none found (vector straight up/down) continue to use the existing one.
core::vector3df pos(node->getPosition());
if ( !core::equals(pos.X, 0.f) || !core::equals(pos.Z, 0.f) )
{
axis.X = -pos.Z;
axis.Z = pos.X;
axis.normalize();
}
}
void CApp::setActiveMeshNodeType(ENodeType nodeType)
{
scene::ISceneManager* smgr = Device->getSceneManager();
if ( SceneNode )
smgr->addToDeletionQueue(SceneNode);
SceneNode = nullptr;
if ( SceneNode2T )
smgr->addToDeletionQueue(SceneNode2T);
SceneNode2T = nullptr;
if ( SceneNodeTangents )
smgr->addToDeletionQueue(SceneNodeTangents);
SceneNodeTangents = nullptr;
// default material
video::SMaterial defaultMaterial;
defaultMaterial.Shininess = 20.f;
// add the nodes which are used to show the materials
const irr::f32 size = 35.f;
if ( nodeType == ENT_CUBE )
{
SceneNode = smgr->addCubeSceneNode (size, 0, -1,
core::vector3df(0, 0, 0),
core::vector3df(0.f, 45.f, 0.f),
core::vector3df(1.0f, 1.0f, 1.0f),
scene::ECMT_1BUF_24VTX_NP);
// avoid wrong colored lines at cube-borders (uv's go from 0-1 currently, which does not work well with interpolation)
for ( u32 i=0; i < irr::video::MATERIAL_MAX_TEXTURES_USED; ++i)
{
defaultMaterial.TextureLayer[i].TextureWrapU = irr::video::ETC_CLAMP_TO_EDGE;
defaultMaterial.TextureLayer[i].TextureWrapV = irr::video::ETC_CLAMP_TO_EDGE;
}
}
else
{
SceneNode = smgr->addSphereSceneNode(size * 0.5f, nodeType == ENT_SPHERE_HIGHRES ? 128 : 16);
}
// off center to test shader
//SceneNode->setPosition(core::vector3df(20.f, -4.f, 10.f));
//SceneNode->setScale(core::vector3df(1.f, 0.2f, 1.5f));
//SceneNode->setRotation(core::vector3df(0.f, 30.f, -10.f));
//defaultMaterial.NormalizeNormals = true;
SceneNode->getMaterial(0) = defaultMaterial;
// SceneNode->setDebugDataVisible(scene::EDS_NORMALS); // showing normals can sometimes be useful to understand what's going on
// create nodes with other vertex types
scene::IMesh * mesh2T = MeshManipulator->createMeshWith2TCoords(SceneNode->getMesh());
SceneNode2T = smgr->addMeshSceneNode(mesh2T, 0, -1, SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
mesh2T->drop();
scene::IMesh * meshTangents = MeshManipulator->createMeshWithTangents(SceneNode->getMesh(), false, false, false);
SceneNodeTangents = smgr->addMeshSceneNode(meshTangents, 0, -1
, SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
meshTangents->drop();
}
irr::scene::IMeshSceneNode* CApp::getVisibleMeshNode() const
{
if ( SceneNode && SceneNode->isVisible() )
return SceneNode;
if ( SceneNode2T && SceneNode2T->isVisible() )
return SceneNode2T;
if ( SceneNodeTangents && SceneNodeTangents->isVisible() )
return SceneNodeTangents;
return nullptr;
}
/*
Short main as most is done in classes.
*/
int main(int argc, char *argv[])
{
CApp APP;
if ( !APP.init(argc, argv) )
{
printf("init failed\n");
APP.quit();
return 1;
}
APP.setRunning(true);
/*
main application loop
*/
while(APP.isRunning())
{
if ( !APP.update() )
break;
}
APP.quit();
return 0;
}
/*
**/