Custom Scene Nodes and .irr Files

A forum to store posts deemed exceptionally wise and useful
Post Reply
killthesand
Posts: 42
Joined: Sat Sep 29, 2007 3:33 am
Contact:

Custom Scene Nodes and .irr Files

Post by killthesand »

Hello, I have been tinkering with Irrlicht and meandering about these forums for a while now. I've been looking through all of the tutorials, howtos, and examples there are to offer so that I can put together, in my head, everything I would have to do to quickly and easily develop Irrlicht applications. Most of this documentation is satisfactory but twice now I have had some difficulty concerning .irr files. Now I've figured out at least everything that I needed to know. It wasn't too difficult, but I feel like this is something that should be documented somehow. So here's my attempt to write up an explanation of the .irr scene file format and a tutorial to show you how to read and write custom scene nodes. Anyone feel free to correct or add to anything here.

For starters, an .irr file is the same as an XML file. If you search the internet for XML you should find plenty of information there. Basically, XML is a convenient way to store data. The only difference between XML and .irr is that a .irr file is storing data that is specific to Irrlicht while an XML file can store any data.

The basic layout of an .irr file. (This is not a valid file!)

Code: Select all

<scene>
	<node 1>
		<attributes of node 1>
			<Attributes go here />
		</end of attributes>
	</end of node 1>

	<node 2>
		<attributes of node 2>
			<Attributes go here />
		</end of attributes>
		
		<node 3, child of node 2>
			<attributes of node 3>
				<Attributes go here />
			</end of attributes>
		</end of node 3>
	</end of node 2>
</end of scene>
What information is useful to Irrlicht? Well let's look at the data members of a standard scene node.

irr::scene::ISceneNode
AbsoluteTransformation
Animators
AutomaticCullingState
Children
DebugDataVisible
ID
IsDebugObject
IsVisible
Name
Parent
RelativeRotation
RelativeScale
RelativeTranslation
SceneManager
TriangleSelector

Now let's look at the XML that defines a cube.

Code: Select all

<node type="cube">
	<attributes>
		<string name="Name" value="testCube" />
		<int name="Id" value="-1" />
		<vector3d name="Position" value="0.000000, 0.000000, 0.000000" />
		<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
		<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
		<bool name="Visible" value="true" />
		<enum name="AutomaticCulling" value="box" />
		<int name="DebugDataVisible" value="0" />
		<bool name="IsDebugObject" value="false" />
		<float name="Size" value="1.000000" />
	</attributes>
</node>
Some of the values you can see like Name and DebugDataVisible are exactly the same. There are other values like Position, Scale, and Size that have to be calculated into both the absolute and relative data members. Irrlicht sends the .irr file data to and from actual scene nodes by way of the functions serializeAttributes and deserializeAttributes. SerializeAttributes sends data out. DeserializeAttributes brings data in. Here are the functions taken from Irrlicht-1.4/include/iscenenode.h

Code: Select all

//! Writes attributes of the scene node.
//! Implement this to expose the attributes of your scene node for
//! scripting languages, editors, debuggers or xml serialization purposes.
virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
{
	out->addString	("Name", Name.c_str());
	out->addInt	("Id", ID );

	out->addVector3d("Position", getPosition() );
	out->addVector3d("Rotation", getRotation() );
	out->addVector3d("Scale", getScale() );

	out->addBool	("Visible", IsVisible );
	out->addEnum	("AutomaticCulling", AutomaticCullingState, AutomaticCullingNames);
	out->addInt	("DebugDataVisible", DebugDataVisible );
	out->addBool	("IsDebugObject", IsDebugObject );
}

//! Reads attributes of the scene node.
//! Implement this to set the attributes of your scene node for
//! scripting languages, editors, debuggers or xml deserialization purposes.
virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
{
	Name = in->getAttributeAsString("Name");
	ID = in->getAttributeAsInt("Id");

	setPosition(in->getAttributeAsVector3d("Position"));
	setRotation(in->getAttributeAsVector3d("Rotation"));
	setScale(in->getAttributeAsVector3d("Scale"));

	IsVisible = in->getAttributeAsBool("Visible");
	AutomaticCullingState = (scene::E_CULLING_TYPE ) in->getAttributeAsEnumeration("AutomaticCulling", scene::AutomaticCullingNames);

	DebugDataVisible = (scene::E_DEBUG_SCENE_TYPE ) in->getAttributeAsInt("DebugDataVisible");
	IsDebugObject = in->getAttributeAsBool("IsDebugObject");

	updateAbsolutePosition();
}
The code here should be fairly self-explanatory, getting and setting values as necessary.

Now let's look at what we need to do to create a custom scene node and have it correctly save and load data to a .irr. We'll start with the custom scene node from tutorial 3 but let's add another member to it, a string called WindowCaption.

ExampleSceneNode.h

Code: Select all

#ifndef  EXAMPLE_SCENE_NODE_H
#define EXAMPLE_SCENE_NODE_H

#include <irrlicht.h>

using namespace irr;
using namespace scene;

class CSampleSceneNode : public scene::ISceneNode
{
	core::aabbox3d<f32> Box;
	video::S3DVertex Vertices[4];
	video::SMaterial Material;

public:

	CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id);

	virtual void OnRegisterSceneNode();
	virtual void render();
	virtual const core::aabbox3d<f32>& getBoundingBox() const;
	virtual u32 getMaterialCount();
	virtual video::SMaterial& getMaterial(u32 i);
	virtual ESCENE_NODE_TYPE getType() const;
	ISceneNode* clone(ISceneNode* newParent, ISceneManager* newManager);
	virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const;
	virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0);
	core::string<char> WindowCaption;
};

#endif
ExampleSceneNode.cpp

Code: Select all

#include "ExampleSceneNode.h"
CSampleSceneNode::CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id)
	: scene::ISceneNode(parent, mgr, id)
{
	Material.Wireframe = false;
	Material.Lighting = false;

	Vertices[0] = video::S3DVertex(0,0,10, 1,1,0, video::SColor(255,0,255,255), 0, 1);
	Vertices[1] = video::S3DVertex(10,0,-10, 1,0,0, video::SColor(255,255,0,255), 1, 1);
	Vertices[2] = video::S3DVertex(0,20,0, 0,1,1, video::SColor(255,255,255,0), 1, 0);
	Vertices[3] = video::S3DVertex(-10,0,-10, 0,0,1, video::SColor(255,0,255,0), 0, 0);

	Box.reset(Vertices[0].Pos);
	for (s32 i=1; i<4; ++i)
		Box.addInternalPoint(Vertices[i].Pos);
}


void CSampleSceneNode::OnRegisterSceneNode()
{
	if (IsVisible)
		SceneManager->registerNodeForRendering(this);

	ISceneNode::OnRegisterSceneNode();
}


void CSampleSceneNode::render()
{
	u16 indices[] = {	0,2,3, 2,1,3, 1,0,3, 2,0,1	};
	video::IVideoDriver* driver = SceneManager->getVideoDriver();

	driver->setMaterial(Material);
	driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
	driver->drawIndexedTriangleList(&Vertices[0], 4, &indices[0], 4);
}


const core::aabbox3d<f32>& CSampleSceneNode::getBoundingBox() const
{
	return Box;
}


u32 CSampleSceneNode::getMaterialCount()
{
	return 1;
}


video::SMaterial& CSampleSceneNode::getMaterial(u32 i)
{
	return Material;
}	



//! Creates a clone of this scene node and its children.
ISceneNode* CSampleSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)
{
	if (!newParent) newParent = Parent;
	if (!newManager) newManager = SceneManager;

	CSampleSceneNode* nb = new CSampleSceneNode(newParent, 
		newManager, ID);

	nb->cloneMembers(this, newManager);

	nb->drop();
	return nb;
}
If you have trouble with any of this so far, refer back to Tutorial 3 – Custom Scene Node.

In order for Irrlicht to actually recognize our new scene node type we need to create and register a new Scene Node Factory. One Scene Node Factory can handle many new scene node types. For this example we are going to work with only one new type and we will define the new factory in ExampleSceneNode.h and ExampleSceneNode.cpp after the includes and before the custom scene node definition. Most of this code was copied from irrlicht-1.4/source/Irrlicht/CDefaultSceneNodeFactory.h and irrlicht-1.4/source/Irrlicht/CDefaultSceneNodeFactory.cpp then was modified to suit this purpose. Some was copied from the IrrEdit plugin tutorial.

ExampleSceneNode.h

Code: Select all

...
// custom factory implementation
class CExampleSceneNodeFactory : public ISceneNodeFactory
{
public:

	CExampleSceneNodeFactory(ISceneManager* mgr);

	//! adds a scene node to the scene graph based on its type id
	/** \param type: Type of the scene node to add.
	\param parent: Parent scene node of the new node, can be null to add the scene node to the root.
	\return Returns pointer to the new scene node or null if not successful. */
	virtual ISceneNode* addSceneNode(ESCENE_NODE_TYPE type, ISceneNode* parent=0);

	//! adds a scene node to the scene graph based on its type name
	/** \param typeName: Type name of the scene node to add.
	\param parent: Parent scene node of the new node, can be null to add the scene node to the root.
	\return Returns pointer to the new scene node or null if not successful. */
	virtual ISceneNode* addSceneNode(const c8* typeName, ISceneNode* parent=0);

	//! returns amount of scene node types this factory is able to create
	virtual u32 getCreatableSceneNodeTypeCount() const;

	//! returns type name of a createable scene node type by index
	/** \param idx: Index of scene node type in this factory. Must be a value between 0 and
	uetCreatableSceneNodeTypeCount() */
	virtual const c8* getCreateableSceneNodeTypeName(u32 idx) const;

	//! returns type of a createable scene node type
	/** \param idx: Index of scene node type in this factory. Must be a value between 0 and
	getCreatableSceneNodeTypeCount() */
	virtual ESCENE_NODE_TYPE getCreateableSceneNodeType(u32 idx) const;

	//! returns type name of a createable scene node type 
	/** \param idx: Type of scene node. 
	\return: Returns name of scene node type if this factory can create the type, otherwise 0. */
	virtual const c8* getCreateableSceneNodeTypeName(ESCENE_NODE_TYPE type) const;

private:

	ESCENE_NODE_TYPE getTypeFromName(const c8* name) const;

	struct SSceneNodeTypePair
	{
		SSceneNodeTypePair(ESCENE_NODE_TYPE type, const c8* name) : Type(type), TypeName(name){}
		ESCENE_NODE_TYPE Type;
		core::stringc TypeName;
	};

	core::array<SSceneNodeTypePair> SupportedSceneNodeTypes;

	ISceneManager* Manager;
};
...
ExampleSceneNode.cpp

Code: Select all

...
// -------------------------------------------------------------------------------
// example factory implementation
// -------------------------------------------------------------------------------

// id for our scene node, 'xmpl', short for 'example'.
const int CSAMPLESCENENODE_ID = MAKE_IRR_ID('x','m','p','l');

// type name for our scene node
const char* CSampleSceneNodeTypeName = "CSampleSceneNode";

CExampleSceneNodeFactory::CExampleSceneNodeFactory(ISceneManager* mgr): Manager(mgr)
{
	// don't grab the scene manager here to prevent cyclic references
}

//! adds a scene node to the scene graph based on its type id
ISceneNode* CExampleSceneNodeFactory::addSceneNode(ESCENE_NODE_TYPE type, ISceneNode* parent)
{
	if (!parent)
		parent = Manager->getRootSceneNode();

	if (type == CSAMPLESCENENODE_ID)
	{
		CSampleSceneNode* node = new CSampleSceneNode(parent, Manager, -1);
		node->drop();
		return node;
	}

	return 0;
}

//! adds a scene node to the scene graph based on its type name
ISceneNode* CExampleSceneNodeFactory::addSceneNode(const c8* typeName, ISceneNode* parent)
{
	return addSceneNode( getTypeFromName(typeName), parent );
}

//! returns amount of scene node types this factory is able to create
u32 CExampleSceneNodeFactory::getCreatableSceneNodeTypeCount() const
{
	return 1;
}

//! returns type of a createable scene node type
ESCENE_NODE_TYPE CExampleSceneNodeFactory::getCreateableSceneNodeType(u32 idx) const
{
	if (idx==0)
		return (ESCENE_NODE_TYPE)CSAMPLESCENENODE_ID;

	return ESNT_UNKNOWN;
}


//! returns type name of a createable scene node type 
const c8* CExampleSceneNodeFactory::getCreateableSceneNodeTypeName(u32 idx) const
{
	if (idx==0)
		return CSampleSceneNodeTypeName;

	return 0;
}

//! returns type name of a createable scene node type 
const c8* CExampleSceneNodeFactory::getCreateableSceneNodeTypeName(ESCENE_NODE_TYPE type) const
{
	if (type == CSAMPLESCENENODE_ID)
		return CSampleSceneNodeTypeName;

	return 0;
}

ESCENE_NODE_TYPE CExampleSceneNodeFactory::getTypeFromName(const c8* name) const
{
	if (!strcmp(name, CSampleSceneNodeTypeName))
		return (ESCENE_NODE_TYPE)CSAMPLESCENENODE_ID;

	return ESNT_UNKNOWN;
}
...
The important parts here are creating the new id and type name. The rest is mostly just returning those values. Now when Irrlicht finds a CSampleSceneNode in an .irr file it will call the CsampleSceneNode's deserializeAttributes function. The one thing we have left to do is to add these functions to our scene node.

Add the following public members to the custom scene node definition.

Code: Select all

virtual ESCENE_NODE_TYPE getType() const;
virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const;
virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0);
Add these functions to the end of ExampleSceneNode.cpp

Code: Select all

//! Returns type of the scene node
ESCENE_NODE_TYPE CSampleSceneNode::getType() const
{
	return (ESCENE_NODE_TYPE)CSAMPLESCENENODE_ID;
}

void CSampleSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
{
	out->addString	("Name", Name.c_str());
	out->addInt	("Id", ID );

	out->addVector3d("Position", getPosition() );
	out->addVector3d("Rotation", getRotation() );
	out->addVector3d("Scale", getScale() );

	out->addBool	("Visible", IsVisible );
	out->addEnum	("AutomaticCulling", AutomaticCullingState, AutomaticCullingNames);
	out->addInt	("DebugDataVisible", DebugDataVisible );
	out->addBool	("IsDebugObject", IsDebugObject );
	out->addString ("WindowCaption", WindowCaption.c_str());
}

void CSampleSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
{
	Name = in->getAttributeAsString("Name");
	ID = in->getAttributeAsInt("Id");

	setPosition(in->getAttributeAsVector3d("Position"));
	setRotation(in->getAttributeAsVector3d("Rotation"));
	setScale(in->getAttributeAsVector3d("Scale"));

	IsVisible = in->getAttributeAsBool("Visible");
	AutomaticCullingState = (scene::E_CULLING_TYPE ) in->getAttributeAsEnumeration("AutomaticCulling", scene::AutomaticCullingNames);

	DebugDataVisible = (scene::E_DEBUG_SCENE_TYPE ) in->getAttributeAsInt("DebugDataVisible");
	IsDebugObject = in->getAttributeAsBool("IsDebugObject");

	WindowCaption = in->getAttributeAsString("WindowCaption");
	updateAbsolutePosition();
}
There we have everything set up to read and write our WindowCaption data member. Let's try to use it.

Here is an .irr file to try this out with:
scene.irr

Code: Select all

<?xml version="1.0"?>
<irr_scene>

	<attributes>
		<string name="Name" value="root" />
		<int name="Id" value="-1" />
		<colorf name="AmbientLight" value="0.000000, 0.000000, 0.000000, 0.000000" />
	</attributes>

	<node type="CSampleSceneNode">
		<attributes>
			<string name="Name" value="Custom" />
			<int name="Id" value="-1" />
			<vector3d name="Position" value="0.000000, 0.000000, 0.000000" />
			<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
			<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
			<bool name="Visible" value="true" />
			<enum name="AutomaticCulling" value="box" />
			<int name="DebugDataVisible" value="0" />
			<bool name="IsDebugObject" value="false" />
			<string name="WindowCaption" value="Custom Scene Node Hooray!" />
		</attributes>
	</node>
</irr_scene>
Now for our main.cpp

Code: Select all

#include <iostream>
using namespace std;

#include <irrlicht.h>
using namespace irr;

using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#pragma comment(lib, "Irrlicht.lib")

#include "ExampleSceneNode.h"

IrrlichtDevice* device = 0;

int main() {
	IrrlichtDevice *device = createDevice(EDT_DIRECT3D9, dimension2d<s32>(720, 480));
	
	if (device == 0)
		return 1;

	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* guienv = device->getGUIEnvironment();
So far this should be pretty easy stuff. Now we need to register our Scene Node Factory before we can load the scene.

Code: Select all

	ISceneNodeFactory *fact = new CExampleSceneNodeFactory(smgr);
	smgr->registerSceneNodeFactory(fact);

	// load the scene
	smgr->loadScene("scene.irr");

	ICameraSceneNode *cam = smgr->addCameraSceneNodeFPS();

	device->getCursorControl()->setVisible(false);
Now we're going to want to retrieve the string from our custom scene node. This can be a bit tricky. We need to create a pointer to our node. If you noticed, in the .irr file, we have named this node “Custom”. The function smgr->getSceneNodeFromName("Custom") will search the scene graph for our node and return a pointer. The problem is, it returns a pointer for an ISceneNode not for a CsampleSceneNode. We have to do a cast to make this work.

Code: Select all

CSampleSceneNode *custom = (CSampleSceneNode *)(smgr->getSceneNodeFromName("Custom"));
Great, now lets run our main loop and finish the program.

Code: Select all

	int lastFPS = -1;

	while(device->run()) {

		driver->beginScene(true, true, video::SColor(0,200,200,200));
		smgr->drawAll();

		driver->endScene();

		int fps = driver->getFPS();

		if (lastFPS != fps) {
			core::stringw str = L"Irrlicht Engine [";
			str += driver->getName();
			str += "] FPS:";
			str += fps;
			str += " - ";
			str += custom->WindowCaption.c_str();
			device->setWindowCaption(str.c_str());
			lastFPS = fps;
		}
	}

	device->drop();

	return 0;
}
So far this doesn't do anything profound, but now you have a way to set your window caption from within an .irr file. You couldn't do that before unless you misused the data members for some other node. Now go and create something usefull with your new wisdom.

I case you got lost somewhere or you just want to play with the code, here are the four full files that are used here. I just compiled and ran this code so I can't really help any errors that you may encounter.

ExampleSceneNode.h

Code: Select all

#ifndef EXAMPLE_SCENE_NODE_H
#define EXAMPLE_SCENE_NODE_H

#include <irrlicht.h>

using namespace irr;
using namespace scene;


// own factory implementation
class CExampleSceneNodeFactory : public ISceneNodeFactory
{
public:

	CExampleSceneNodeFactory(ISceneManager* mgr);

	//! adds a scene node to the scene graph based on its type id
	/** \param type: Type of the scene node to add.
	\param parent: Parent scene node of the new node, can be null to add the scene node to the root.
	\return Returns pointer to the new scene node or null if not successful. */
	virtual ISceneNode* addSceneNode(ESCENE_NODE_TYPE type, ISceneNode* parent=0);

	//! adds a scene node to the scene graph based on its type name
	/** \param typeName: Type name of the scene node to add.
	\param parent: Parent scene node of the new node, can be null to add the scene node to the root.
	\return Returns pointer to the new scene node or null if not successful. */
	virtual ISceneNode* addSceneNode(const c8* typeName, ISceneNode* parent=0);

	//! returns amount of scene node types this factory is able to create
	virtual u32 getCreatableSceneNodeTypeCount() const;

	//! returns type name of a createable scene node type by index
	/** \param idx: Index of scene node type in this factory. Must be a value between 0 and
	uetCreatableSceneNodeTypeCount() */
	virtual const c8* getCreateableSceneNodeTypeName(u32 idx) const;

	//! returns type of a createable scene node type
	/** \param idx: Index of scene node type in this factory. Must be a value between 0 and
	getCreatableSceneNodeTypeCount() */
	virtual ESCENE_NODE_TYPE getCreateableSceneNodeType(u32 idx) const;

	//! returns type name of a createable scene node type 
	/** \param idx: Type of scene node. 
	\return: Returns name of scene node type if this factory can create the type, otherwise 0. */
	virtual const c8* getCreateableSceneNodeTypeName(ESCENE_NODE_TYPE type) const;

private:

	ESCENE_NODE_TYPE getTypeFromName(const c8* name) const;

	struct SSceneNodeTypePair
	{
		SSceneNodeTypePair(ESCENE_NODE_TYPE type, const c8* name) : Type(type), TypeName(name){}
		ESCENE_NODE_TYPE Type;
		core::stringc TypeName;
	};

	core::array<SSceneNodeTypePair> SupportedSceneNodeTypes;

	ISceneManager* Manager;
};

// own scene node implementation
class CSampleSceneNode : public scene::ISceneNode
{
	core::aabbox3d<f32> Box;
	video::S3DVertex Vertices[4];
	video::SMaterial Material;

public:

	CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id);

	virtual void OnRegisterSceneNode();
	virtual void render();
	virtual const core::aabbox3d<f32>& getBoundingBox() const;
	virtual u32 getMaterialCount();
	virtual video::SMaterial& getMaterial(u32 i);
	virtual ESCENE_NODE_TYPE getType() const;
	ISceneNode* clone(ISceneNode* newParent, ISceneManager* newManager);
	virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const;
	virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0);
	core::string<char> WindowCaption;
};

#endif
ExampleSceneNode.cpp

Code: Select all

#include "ExampleSceneNode.h"

// -------------------------------------------------------------------------------
// example factory implementation
// -------------------------------------------------------------------------------

// id for our scene node, 'xmpl', short for 'example'.
const int CSAMPLESCENENODE_ID = MAKE_IRR_ID('x','m','p','l');

// type name for our scene node
const char* CSampleSceneNodeTypeName = "CSampleSceneNode";

CExampleSceneNodeFactory::CExampleSceneNodeFactory(ISceneManager* mgr): Manager(mgr)
{
	// don't grab the scene manager here to prevent cyclic references
}

//! adds a scene node to the scene graph based on its type id
ISceneNode* CExampleSceneNodeFactory::addSceneNode(ESCENE_NODE_TYPE type, ISceneNode* parent)
{
	if (!parent)
		parent = Manager->getRootSceneNode();

	if (type == CSAMPLESCENENODE_ID)
	{
		CSampleSceneNode* node = new CSampleSceneNode(parent, Manager, -1);
		node->drop();
		return node;
	}

	return 0;
}

//! adds a scene node to the scene graph based on its type name
ISceneNode* CExampleSceneNodeFactory::addSceneNode(const c8* typeName, ISceneNode* parent)
{
	return addSceneNode( getTypeFromName(typeName), parent );
}

//! returns amount of scene node types this factory is able to create
u32 CExampleSceneNodeFactory::getCreatableSceneNodeTypeCount() const
{
	return 1;
}

//! returns type of a createable scene node type
ESCENE_NODE_TYPE CExampleSceneNodeFactory::getCreateableSceneNodeType(u32 idx) const
{
	if (idx==0)
		return (ESCENE_NODE_TYPE)CSAMPLESCENENODE_ID;

	return ESNT_UNKNOWN;
}


//! returns type name of a createable scene node type 
const c8* CExampleSceneNodeFactory::getCreateableSceneNodeTypeName(u32 idx) const
{
	if (idx==0)
		return CSampleSceneNodeTypeName;

	return 0;
}

//! returns type name of a createable scene node type 
const c8* CExampleSceneNodeFactory::getCreateableSceneNodeTypeName(ESCENE_NODE_TYPE type) const
{
	if (type == CSAMPLESCENENODE_ID)
		return CSampleSceneNodeTypeName;

	return 0;
}

ESCENE_NODE_TYPE CExampleSceneNodeFactory::getTypeFromName(const c8* name) const
{
	if (!strcmp(name, CSampleSceneNodeTypeName))
		return (ESCENE_NODE_TYPE)CSAMPLESCENENODE_ID;

	return ESNT_UNKNOWN;
}

// -------------------------------------------------------------------------------
// example scene node implementation 
// -------------------------------------------------------------------------------

CSampleSceneNode::CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id)
	: scene::ISceneNode(parent, mgr, id)
{
	Material.Wireframe = false;
	Material.Lighting = false;

	Vertices[0] = video::S3DVertex(0,0,10, 1,1,0, video::SColor(255,0,255,255), 0, 1);
	Vertices[1] = video::S3DVertex(10,0,-10, 1,0,0, video::SColor(255,255,0,255), 1, 1);
	Vertices[2] = video::S3DVertex(0,20,0, 0,1,1, video::SColor(255,255,255,0), 1, 0);
	Vertices[3] = video::S3DVertex(-10,0,-10, 0,0,1, video::SColor(255,0,255,0), 0, 0);

	Box.reset(Vertices[0].Pos);
	for (s32 i=1; i<4; ++i)
		Box.addInternalPoint(Vertices[i].Pos);
}

void CSampleSceneNode::OnRegisterSceneNode()
{
	if (IsVisible)
		SceneManager->registerNodeForRendering(this);

	ISceneNode::OnRegisterSceneNode();
}

void CSampleSceneNode::render()
{
	u16 indices[] = {	0,2,3, 2,1,3, 1,0,3, 2,0,1	};
	video::IVideoDriver* driver = SceneManager->getVideoDriver();

	driver->setMaterial(Material);
	driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
	driver->drawIndexedTriangleList(&Vertices[0], 4, &indices[0], 4);
}


const core::aabbox3d<f32>& CSampleSceneNode::getBoundingBox() const
{
	return Box;
}

u32 CSampleSceneNode::getMaterialCount()
{
	return 1;
}

video::SMaterial& CSampleSceneNode::getMaterial(u32 i)
{
	return Material;
}	

//! Creates a clone of this scene node and its children.
ISceneNode* CSampleSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager)
{
	// this method is only implemented to let irrEdit be able to copy the
	// scene node via CTRL+C, it's not necessary

	if (!newParent) newParent = Parent;
	if (!newManager) newManager = SceneManager;

	CSampleSceneNode* nb = new CSampleSceneNode(newParent, 
		newManager, ID);

	nb->cloneMembers(this, newManager);

	nb->drop();
	return nb;
}

//! Returns type of the scene node
ESCENE_NODE_TYPE CSampleSceneNode::getType() const
{
	return (ESCENE_NODE_TYPE)CSAMPLESCENENODE_ID;
}

void CSampleSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
{
	out->addString	("Name", Name.c_str());
	out->addInt	("Id", ID );

	out->addVector3d("Position", getPosition() );
	out->addVector3d("Rotation", getRotation() );
	out->addVector3d("Scale", getScale() );

	out->addBool	("Visible", IsVisible );
	out->addEnum	("AutomaticCulling", AutomaticCullingState, AutomaticCullingNames);
	out->addInt	("DebugDataVisible", DebugDataVisible );
	out->addBool	("IsDebugObject", IsDebugObject );
	out->addString ("WindowCaption", WindowCaption.c_str());
}

void CSampleSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
{
	Name = in->getAttributeAsString("Name");
	ID = in->getAttributeAsInt("Id");

	setPosition(in->getAttributeAsVector3d("Position"));
	setRotation(in->getAttributeAsVector3d("Rotation"));
	setScale(in->getAttributeAsVector3d("Scale"));

	IsVisible = in->getAttributeAsBool("Visible");
	AutomaticCullingState = (scene::E_CULLING_TYPE ) in->getAttributeAsEnumeration("AutomaticCulling", scene::AutomaticCullingNames);

	DebugDataVisible = (scene::E_DEBUG_SCENE_TYPE ) in->getAttributeAsInt("DebugDataVisible");
	IsDebugObject = in->getAttributeAsBool("IsDebugObject");

	WindowCaption = in->getAttributeAsString("WindowCaption");
	updateAbsolutePosition();
}
main.cpp

Code: Select all

#include <iostream>
using namespace std;

#include <irrlicht.h>
using namespace irr;

using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#pragma comment(lib, "Irrlicht.lib")

#include "ExampleSceneNode.h"

IrrlichtDevice* device = 0;

int main() {
	IrrlichtDevice *device = createDevice(EDT_DIRECT3D9, dimension2d<s32>(720, 480));
	
	if (device == 0)
		return 1;

	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* guienv = device->getGUIEnvironment();

	ISceneNodeFactory *fact = new CExampleSceneNodeFactory(smgr);
	smgr->registerSceneNodeFactory(fact);

	// load the scene
	smgr->loadScene("scene.irr");
	
	ICameraSceneNode *cam = smgr->addCameraSceneNodeFPS();

	device->getCursorControl()->setVisible(false);

	CSampleSceneNode *custom = (CSampleSceneNode *)(smgr->getSceneNodeFromName("Custom"));

	int lastFPS = -1;

	while(device->run()) {

		driver->beginScene(true, true, video::SColor(0,200,200,200));
		smgr->drawAll();

		driver->endScene();

		int fps = driver->getFPS();

		if (lastFPS != fps) {
			core::stringw str = L"Irrlicht Engine [";
			str += driver->getName();
			str += "] FPS:";
			str += fps;
			str += " - ";
			str += custom->WindowCaption.c_str();
			device->setWindowCaption(str.c_str());
			lastFPS = fps;
		}
	}

	device->drop();

	return 0;
}
scene.irr

Code: Select all

<?xml version="1.0"?>
<irr_scene>

	<attributes>
		<string name="Name" value="root" />
		<int name="Id" value="-1" />
		<colorf name="AmbientLight" value="0.000000, 0.000000, 0.000000, 0.000000" />
	</attributes>

	<node type="CSampleSceneNode">
		<attributes>
			<string name="Name" value="Custom" />
			<int name="Id" value="-1" />
			<vector3d name="Position" value="0.000000, 0.000000, 0.000000" />
			<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
			<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
			<bool name="Visible" value="true" />
			<enum name="AutomaticCulling" value="box" />
			<int name="DebugDataVisible" value="0" />
			<bool name="IsDebugObject" value="false" />
			<string name="WindowCaption" value="Custom Scene Node Hooray!" />
		</attributes>
	</node>
</irr_scene>

[/code]
Last edited by killthesand on Sat Feb 02, 2008 4:22 am, edited 1 time in total.
dlangdev
Posts: 1324
Joined: Tue Aug 07, 2007 7:28 pm
Location: Beaverton OR
Contact:

Post by dlangdev »

thanks for writing this stuff up.

i'm going to need this tutorial for the code i'm going to write this weekend.

i may have some questions later when i get stuck into something, which is quite normal for me.
Image
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

That's a wonderful tutorial on an advanced topic (which are unfortunately the most neglected ones in the tutorials). Thanks!
Post Reply