Vegetation SceneNode

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Vegetation SceneNode

Post by Seven »

Below is a vegetation scenenode along with a small demo program showing it's use.
the model files that I use are from Arteria3D and are not public domain so I cannot share them.

theory :

terrain created.
vegetation layer created.
add mesh templates to vegetation layer
process vegetation layer (using terrain as base to determine position and height)
1) create array of positions, types, rotations, and scales
2) populate the arrays in the process() function
2a) calls functions for density, rotation, type and scale of the meshes

during scenenode render call
calculate a slightly expanded camera frustum
check position array to determine if mesh should be visible
if so then create mesh of proper type, position, scale, and rotation


sandbox program creates 6 vegetation layers
I use many models for the vegetation, however, any model will do. (i used dwarfs.x as for testing)

thanks to the forum for all of the ideas for making this faster. I used many examples to speed things up.

a small video showing it in action
https://www.youtube.com/watch?v=1gPSPg4OFGw

media files
https://www.dropbox.com/sh/72dj1338mt63 ... gS2Oa?dl=0
Last edited by Seven on Mon Aug 16, 2021 3:42 pm, edited 3 times in total.
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Vegetation SceneNode

Post by Seven »

Code: Select all

#pragma once

#include "Irrlicht.h"
using namespace irr;
using namespace video;
using namespace core;
using namespace gui;
using namespace scene;
using namespace io;

namespace IGE
{
	// if color is below this value thn no node is present
	#define CUTOFF 50
	#define YCUTOFF 0

	// simple rendom number generators
	extern int getRandomInteger(int min, int max);
	extern float getRandomFloat(float min, float max);

	// simple structure to hold a template node and the YOffset to apply when positioning the node
	struct IGE_MeshTemplate
	{
		IGE_MeshTemplate(IMeshSceneNode* node, float yoffset) : m_Node(node), m_YOffset(yoffset) {}
		IMeshSceneNode* m_Node		= nullptr;
		float			m_YOffset	= 0.0f;
	};

	/*
	*  the scenenode class
	*   once the scenenode is instantiated
	*	add as many templates as desired (these are the different nodes to be used)
	*   
	*   
	*/
	class IGE_SceneNode_Vegetation : public ISceneNode
	{
	public:
		ITerrainSceneNode*			m_Terrain		= nullptr;							// the terrain scenenode we are attached to
		IImage*						m_LayoutImage	= nullptr;							// the layout image - used to determine whether a node/object is at any given position

		array<IGE_MeshTemplate>		m_MeshTemplate;										// list	of nodes being used
		array<ISceneNode*>			m_Nodes;											// list of visible nodes
		array<short int>			m_NodeType;											// list of node types (index into the MeshTemplates)
		array<vector3df>			m_NodePos;											// list of node positions
		array<vector3df>			m_NodeScale;										// list of node scales
		array<vector3df>			m_NodeRotation;										// list of node rotations
		array<bool>					m_IsNodePosFree;									// list of open and closed slots in the node list

		float						m_ViewDistance				= 1000;					// distance from camera at which nodes become visible
		f32							m_VisibleOffset				= 200.f;				// resize the camera frustum by this amount, you can use the maximal radius of your mesh to prevent close plane popping
		vector3df					m_LayoutRandomness			= vector3df(0,0,0);		// add some randomness to the layout procedure

		int							m_HelperForDeleting			= false;				// variable used during update
		bool						m_UseLight					= false;				// simple variable to track light use
		bool						m_UseFog					= false;				// simple variable to track fog use	

	public:
		// add a node template
		void				addMeshTemplate(stringc meshfilename, float YOffset);

		// using the terrain as the location base and the layout functions to determine position, rotation and scale of nodes
		void				processLayout();

		// clear all lists
		void				clear();

		// called each tick. determine which nodes should be visible based on the camera position and rotation
		void				update(vector3df center, ICameraSceneNode* camera);

		// use color information to determine the density, style, rotation and scale of the nodes
		virtual int			getDensityFromLayoutImageColor(SColor pixel);
		virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos);
		virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos);
		virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos);

		// simple getter functions
		float				getViewDistance()				{ return m_ViewDistance;	}
		bool				getUseLight()					{ return m_UseLight;		}
		bool				getUseFog()						{ return m_UseFog;			}

		// toggle light, fog and visibility
		virtual				void setViewDistance(float d)	{ m_ViewDistance = d;		}
		virtual				void setUseLight(bool v);
		virtual				void setUseFog(bool v);
		virtual				void setVisible(bool v);

	private:
		// is the point visible within the camera frustum
		bool				canBeSeenByCamPyramidPlanes(const SViewFrustum& frust, const vector3df& pos);

	public:

			//! constructor
			IGE_SceneNode_Vegetation(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutRandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id);

			//! destructor
			virtual ~IGE_SceneNode_Vegetation();

			//! frame
			virtual void OnRegisterSceneNode() _IRR_OVERRIDE_;

			//! renders the node.
			virtual void render() _IRR_OVERRIDE_;

			//! returns the axis aligned bounding box of this node
			virtual const core::aabbox3d<f32>& getBoundingBox() const _IRR_OVERRIDE_;

			//! Writes attributes of the scene node.
			virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options = 0) const _IRR_OVERRIDE_;

			//! Reads attributes of the scene node.
			virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options = 0) _IRR_OVERRIDE_;

			//! Returns type of the scene node
			virtual ESCENE_NODE_TYPE getType() const _IRR_OVERRIDE_ { return ESNT_MESH; }

	protected:
			core::aabbox3d<f32>				Box;
	};

} // end namespace IGE

Code: Select all


#include "IGE_SceneNode_Vegetation.h"

namespace IGE
{
	int getRandomInteger(int min, int max) { return rand() % (max - min + 1) + min; } //return int((double)rand() / (RAND_MAX + 1) * (max - min) + min);
	float getRandomFloat(float min, float max) { return float((double)rand() / (RAND_MAX + 1) * (max - min) + min); }

	//! constructor
	IGE_SceneNode_Vegetation::IGE_SceneNode_Vegetation(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutRandomness, bool uselight, bool usefog, 
		ISceneManager* smgr, s32 id)
		: ISceneNode(parent, smgr, id, vector3df(),vector3df(), vector3df(1,1,1)),m_Terrain(parent),m_ViewDistance(viewdistance),m_LayoutRandomness(layoutRandomness),m_UseLight(uselight),m_UseFog(usefog)
	{
#ifdef _DEBUG
		setDebugName("IGE_SceneNode_Vegetation");
#endif
		Box = parent->getBoundingBox();
		m_LayoutImage = smgr->getVideoDriver()->createImageFromFile(layoutimagefilename.c_str());
	}


	//! destructor
	IGE_SceneNode_Vegetation::~IGE_SceneNode_Vegetation()
	{
		if (m_LayoutImage) m_LayoutImage->drop(); m_LayoutImage = 0;
		clear();
	}


	//! frame
	void IGE_SceneNode_Vegetation::OnRegisterSceneNode()
	{
		if (IsVisible)
		{
			SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);

			ISceneNode::OnRegisterSceneNode();
		}
	}


	//! renders the node.
	void IGE_SceneNode_Vegetation::render()
	{
		update(SceneManager->getActiveCamera()->getPosition(), SceneManager->getActiveCamera());
	//	ISceneNodeList::Iterator it = Children.begin();
	//	for (; it != Children.end(); ++it)
	//		(*it)->render();
	}

	//! returns the axis aligned bounding box of this node
	const core::aabbox3d<f32>& IGE_SceneNode_Vegetation::getBoundingBox() const
	{
		return Box;
	}

	//! Writes attributes of the scene node.
	void IGE_SceneNode_Vegetation::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
	{
		ISceneNode::serializeAttributes(out, options);
	}


	//! Reads attributes of the scene node.
	void IGE_SceneNode_Vegetation::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
	{
		ISceneNode::deserializeAttributes(in, options);
	}

	// add a new template to the list
	void IGE_SceneNode_Vegetation::addMeshTemplate(stringc meshfilename, float YOffset)
	{
		// load the mesh
		IMesh* Mesh = SceneManager->getMesh(meshfilename.c_str());

		// if the mesh is valid we will use it
		if (Mesh)
		{
			IMeshSceneNode* node = SceneManager->addMeshSceneNode(Mesh);
			node->setPosition(vector3df(0, -99999, 0));
			node->getMesh()->setHardwareMappingHint(EHM_STATIC);
			node->setName("plsDoNotDeleteMe");
			node->setMaterialFlag(EMF_FOG_ENABLE, m_UseFog);
			node->setMaterialFlag(EMF_LIGHTING, m_UseLight);
			node->setMaterialType(EMT_TRANSPARENT_ALPHA_CHANNEL_REF);

			// add the mesh to the list
			IGE_MeshTemplate t(node, YOffset);
			m_MeshTemplate.push_back(t);
		}

	}

	// clear everything
	void IGE_SceneNode_Vegetation::clear()
	{
		// clear the visible nodes / objects
		if (!m_Nodes.empty())
		{
			for (u32 x = 0; x < m_Nodes.size(); x++)
			{
				if (m_Nodes[x] != NULL)
				{
					m_Nodes[x]->remove();
					m_Nodes[x] = nullptr;
				}
			}
		}

		// clear all of the lists
		m_Nodes.clear();
		m_Nodes.reallocate(0, true);
		m_NodePos.clear();
		m_NodePos.reallocate(0, true);
		m_NodeScale.clear();
		m_NodeScale.reallocate(0, true);
		m_NodeRotation.clear();
		m_NodeRotation.reallocate(0, true);
		m_NodeType.clear();
		m_NodeType.reallocate(0, true);
		m_IsNodePosFree.clear();
		m_IsNodePosFree.reallocate(0, true);
	}

	void IGE_SceneNode_Vegetation::processLayout()
	{
		// if we dont have any node / object efinitions to use then bail
		if (!(m_MeshTemplate.size() > 0))
		{
			printf("WARNING! IGE_SceneNode_Vegetation::processLayout() no plane templates found!\n");
			return;
		}

		// if we dont have a terrain then bail
		if (!m_Terrain)
		{
			printf("ERROR! IGE_SceneNode_Vegetation::processLayout() no terrain object found!\n");
			return;
		}

		// make sure that we have a layout image else bail
		if (!m_LayoutImage)
		{
			printf("ERROR! IGE_SceneNode_Vegetation::processLayout() no layout image\n");
			return;
		}

		// get the edges of the terrain scenenode
		aabbox3d<f32> terrainBox = m_Terrain->getBoundingBox();
		Box = terrainBox;
		float minX = terrainBox.MinEdge.X;
		float minZ = terrainBox.MinEdge.Z;

		m_Terrain->updateAbsolutePosition();
		float terrainX = m_Terrain->getPosition().X;
		float terrainZ = m_Terrain->getPosition().Z;

		// get the parsing scale of the layout image vs the terrain sizes
		float scaleX = terrainBox.getExtent().X / m_LayoutImage->getDimension().Width;
		float scaleZ = terrainBox.getExtent().Z / m_LayoutImage->getDimension().Height;

		float density = 1.0f;

		// based on the density, run throught the lists
		for (float i = minX; i < minX + terrainBox.getExtent().X; i+=density)
		{
			for (float ii = minZ; ii < minZ + terrainBox.getExtent().Z; )
			{
				// randomize the position a bit to prevent straight lines
				vector3df randomness(
					getRandomFloat(-m_LayoutRandomness.X, m_LayoutRandomness.X),
					getRandomFloat(-m_LayoutRandomness.Y, 0),
					getRandomFloat(-m_LayoutRandomness.Z, m_LayoutRandomness.Z));

				// get the height of the terrain at the current position
				float height = m_Terrain->getHeight(i + randomness.X, ii + randomness.Z);
				if (height > -99990)
				{
					// get the layout image pixel color at the point relative to the terrain
					int x2 = (int)(i / scaleX);
					int y2 = (int)(ii / scaleZ);
					SColor pixel = m_LayoutImage->getPixel(x2, y2);

					vector3df pos(terrainX + i + randomness.X, height + randomness.Y, terrainZ + ii + randomness.Z);
					
					// let the user select a style based on the color
					int         style		= getStyleFromLayoutImageColor(pixel, m_MeshTemplate.size(), pos);
					vector3df   scale		= getScaleFromLayoutImageColor(pixel, m_MeshTemplate.size(), style, pos);
					vector3df   rotation	= getRotationFromLayoutImageColor(pixel, m_MeshTemplate.size(), style, pos);
				
					density = getDensityFromLayoutImageColor(pixel);
					ii += density;

					// if there is a style then a node should be created at this position
					// add at least a NULL to each list to make sure the lists sizes are matched
					if (style != -2)
					{
						pos.Y -= m_MeshTemplate[style].m_YOffset;

						m_Nodes.push_back(nullptr);
						m_NodeType.push_back(style);
						m_NodePos.push_back(pos);
						m_NodeScale.push_back(scale);
						m_NodeRotation.push_back(rotation);
						m_IsNodePosFree.push_back(true);
					}
				}
			}
		}
	}

	// check if a point is inside the viewfromcamera view frustrum 
	bool IGE_SceneNode_Vegetation::canBeSeenByCamPyramidPlanes(const SViewFrustum& frust, const vector3df& pos)
	{
		for (s32 i = 0; i < scene::SViewFrustum::VF_PLANE_COUNT; ++i)
		{
			if (frust.planes[i].classifyPointRelation(pos) == core::ISREL3D_FRONT)
				return false;
		}
		return true;
	}

	// called each frame to update what is visible and what is not
	void IGE_SceneNode_Vegetation::update(vector3df center, ICameraSceneNode* camera)
	{
		if (!isVisible()) return;

		if (m_HelperForDeleting == 0) m_HelperForDeleting = 1; else m_HelperForDeleting = 0;

		// make the frustum a little bigger to stop popping near the front plane
		SViewFrustum origFrustum = *camera->getViewFrustum();
		SViewFrustum frustum = origFrustum;
		for (int i = 0; i < scene::SViewFrustum::VF_PLANE_COUNT; ++i)
		{
			frustum.planes[i].recalculateD(frustum.planes[i].getMemberPoint() + frustum.planes[i].Normal * m_VisibleOffset);
		}
		frustum.recalculateBoundingBox();

		for (u32 i = m_HelperForDeleting; i < m_NodePos.size(); i += 2)
		{
			line3d<f32> ray;
			ray.start = center;
			ray.end = m_NodePos[i];
			float d = ray.getLength();
			if (m_IsNodePosFree[i] && d < m_ViewDistance && canBeSeenByCamPyramidPlanes(frustum, m_NodePos[i]))
			{
				if (m_MeshTemplate[m_NodeType[i]].m_Node != nullptr)
				{
					m_Nodes[i] = m_MeshTemplate[m_NodeType[i]].m_Node->clone();
					m_Nodes[i]->setPosition(m_NodePos[i]);
					m_Nodes[i]->setRotation(m_NodeRotation[i]);
					m_Nodes[i]->setScale(m_NodeScale[i]);
					m_Nodes[i]->setMaterialFlag(EMF_LIGHTING, m_UseLight);
					m_Nodes[i]->setMaterialFlag(EMF_FOG_ENABLE, m_UseFog);
					m_IsNodePosFree[i] = false;
				}
			}
			else
			{
				if (d > m_ViewDistance || !canBeSeenByCamPyramidPlanes(frustum, m_NodePos[i]))
				{
					if (m_Nodes[i] != nullptr) m_Nodes[i]->remove(); m_Nodes[i] = nullptr;
					m_IsNodePosFree[i] = true;
				}
			}
		}
	}

	int	IGE_SceneNode_Vegetation::getDensityFromLayoutImageColor(SColor pixel)
	{
		return 1;
	}

	int	IGE_SceneNode_Vegetation::getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos)
	{ 
		return (getRandomInteger(1,templatesize))-1;
	}

	vector3df   IGE_SceneNode_Vegetation::getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos)
	{ 
		return vector3df(0, getRandomFloat(0, 360), 0);
	}

	vector3df   IGE_SceneNode_Vegetation::getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos)
	{ 
		return vector3df(getRandomFloat(1, 3), getRandomFloat(1, 3), getRandomFloat(1, 3));
	}

	void  IGE_SceneNode_Vegetation::setUseLight(bool v)
	{
		m_UseLight = v;
		for (u32 i = 0; i < m_Nodes.size(); i++)
			if (m_Nodes[i] != nullptr)
				m_Nodes[i]->setMaterialFlag(E_MATERIAL_FLAG::EMF_LIGHTING, v);

		for (u32 i = 0; i < m_MeshTemplate.size(); i++)
			m_MeshTemplate[i].m_Node->setMaterialFlag(E_MATERIAL_FLAG::EMF_LIGHTING, v);
	}

	void  IGE_SceneNode_Vegetation::setUseFog(bool v)
	{
		m_UseFog = v;
		for (u32 i = 0; i < m_Nodes.size(); i++)
			if (m_Nodes[i] != nullptr)
				m_Nodes[i]->setMaterialFlag(E_MATERIAL_FLAG::EMF_FOG_ENABLE, v);

		for (u32 i = 0; i < m_MeshTemplate.size(); i++)
				m_MeshTemplate[i].m_Node->setMaterialFlag(E_MATERIAL_FLAG::EMF_FOG_ENABLE, v);
	}

	void  IGE_SceneNode_Vegetation::setVisible(bool v)
	{
		if (isVisible() == v) return;
		ISceneNode::setVisible(v);
		for (u32 i = 0; i < m_Nodes.size(); i++)
			if (m_Nodes[i] != nullptr)
				m_Nodes[i]->setVisible(v);
		
		for (u32 i = 0; i < m_MeshTemplate.size(); i++)
				m_MeshTemplate[i].m_Node->setVisible(v);
	}


} // end namspace IGE

Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Vegetation SceneNode

Post by Seven »

Code: Select all

#include <irrlicht.h>
#include "driverChoice.h"
#include "exampleHelper.h"
#include "IGE_SceneNode_Vegetation.h"
#include <conio.h>

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

// globals are easier for simple demos
IrrlichtDevice*				device		= 0;
video::IVideoDriver*		driver		= 0;
scene::ISceneManager*		smgr		= 0;
gui::IGUIEnvironment*		env			= 0;

// a few global variables specific to this demo 
ITerrainSceneNode*			terrain = 0;		// the terrain to cover with grass
scene::ICameraSceneNode*	camera = 0;			// the main FPS camera
scene::ICameraSceneNode*	camera2 = 0;		// a test camera to check grass rendering through
scene::ICameraSceneNode*	viewfromcamera = 0;	// which camera to check grass rendering through	
bool						useLight = false;
bool						useFog = false;

// some view distances for each layer of vegetation
float VIEW_DISTANCE_GRASS		= 10000;
float VIEW_DISTANCE_FLOWERS		= 5000;
float VIEW_DISTANCE_SHRUBS		= 7000;
float VIEW_DISTANCE_SMALLTREES	= 8000;
float VIEW_DISTANCE_LARGETREES	= 10000;
float VIEW_DISTANCE_ROCKS		= 10000;

// the terrain scale
#define TERRAIN_SCALE		vector3df(400.f, 4.4f, 400.f)

// the vegetation layers
array<IGE_SceneNode_Vegetation*> vegetationLayers;

/// /////////////////////////////////////////////////////////////////////
//  create 6 new scenenodes, each derived from the IGE_SceneNode_Vegetation class
//  this allows us to setup each layer independently
//  I commented the first layer (grass) extensively but the others are the same just used for different scenenodes
/// /////////////////////////////////////////////////////////////////////

class IGE_SceneNode_Vegetation_Grass : public IGE_SceneNode_Vegetation
{
public:	
	//! constructor - do nothing constructor
	IGE_SceneNode_Vegetation_Grass(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance,layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor - do nothing destructor
	virtual ~IGE_SceneNode_Vegetation_Grass() {}

	// override to provide scenenode with layout values based on pixel colors
	// in this case we are just hard coding the values, but you could use the pizel color to determine each attribute
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 200; }

	// override to provide scenenode with layout values based on pixel colors
	// in this case, if the pixel color green attribute is greater that 100 then return one of the grass templates
	// otherwise return -2 which is interpreted as no grass node in this area
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos)
	{ 
		if (pixel.getGreen() > 100) return getRandomInteger(1, templatesize) - 1;
		else return -2;
	}

	// override to provide scenenode with layout values based on pixel colors
	// in this case we are just hard coding the values, but you could use the pizel color to determine each attribute
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }

	// override to provide scenenode with layout values based on pixel colors
	// in this case we are just hard coding the values, but you could use the pizel color to determine each attribute
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(3, getRandomInteger(1,3), 3); }
};

class IGE_SceneNode_Vegetation_Flowers : public IGE_SceneNode_Vegetation
{
public:	
	//! constructor
	IGE_SceneNode_Vegetation_Flowers(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_Flowers() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 2000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(3, 8, 3); }
};

class IGE_SceneNode_Vegetation_Shrubs : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_Shrubs(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_Shrubs() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 1000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(5, 5, 5); }
};

class IGE_SceneNode_Vegetation_SmallTrees : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_SmallTrees(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_SmallTrees() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 3000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(8, 8, 8); }
};

class IGE_SceneNode_Vegetation_LargeTrees : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_LargeTrees(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_LargeTrees() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 4000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(14,14,14); }
};

class IGE_SceneNode_Vegetation_Rocks : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_Rocks(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_Rocks() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 5000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(2, 2, 2); }
};

// create all of the vegetation
// in this case, we create 6 different scenenodes and add mesh templates to each
void createVegetation()
{
	// if ther is no terrain then bail
	if (!terrain)
	{
		printf("terrain node is not valid!");
		return;
	}

	// clear any existing vegetation layers
	for (u32 i = 0; i < vegetationLayers.size(); i++) vegetationLayers[i]->remove(); vegetationLayers.clear();

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Grass* grass = new IGE_SceneNode_Vegetation_Grass(terrain, "media/_Assets/_Foliage/_LayoutImages/grasslayout.jpg",VIEW_DISTANCE_GRASS, vector3df(5, 0, 5), useLight, useFog, smgr, 0);
		grass->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/misc/trop_grass(region).b3d", 0);
	vegetationLayers.push_back(grass);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Flowers* flowers = new IGE_SceneNode_Vegetation_Flowers(terrain, "media/_Assets/_Foliage/_LayoutImages/flowerslayout.jpg",VIEW_DISTANCE_FLOWERS, vector3df(50, 0, 50), useLight, useFog, smgr, 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 1.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 2.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 3.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 4.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 5.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 6.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 7.x", 0);
		vegetationLayers.push_back(flowers);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Shrubs* shrubs = new IGE_SceneNode_Vegetation_Shrubs(terrain, "media/_Assets/_Foliage/_LayoutImages/shrubslayout.jpg", VIEW_DISTANCE_SHRUBS, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/fan plant.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/fan plant version 2.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/fan plant version 3.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant a.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant b.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant c.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant d.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant e.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant g.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant h.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant k.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant l.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant m.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant n.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant n version2.b3d", 0);
		vegetationLayers.push_back(shrubs);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_SmallTrees* SmallTrees = new IGE_SceneNode_Vegetation_SmallTrees(terrain, "media/_Assets/_Foliage/_LayoutImages/smalltreeslayout.jpg", VIEW_DISTANCE_SMALLTREES, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2 b.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm 2.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm1.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm2.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm3.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm3 B.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm3 C.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm4.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm5.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm6.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/worn palm.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/yuka.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/yuka 2.b3d", 0);
		vegetationLayers.push_back(SmallTrees);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_LargeTrees* LargeTrees = new IGE_SceneNode_Vegetation_LargeTrees(terrain, "media/_Assets/_Foliage/_LayoutImages/largetreeslayout.jpg", VIEW_DISTANCE_LARGETREES, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2 b.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm 2.b3d", 0);
		vegetationLayers.push_back(LargeTrees);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Rocks* rocks = new IGE_SceneNode_Vegetation_Rocks(terrain, "media/_Assets/_Foliage/_LayoutImages/rockslayout.jpg", VIEW_DISTANCE_ROCKS, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock1.x", 0);
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock2.x", 0);
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock3.x", 0);
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock4.x", 0);
		vegetationLayers.push_back(rocks);

	// now process all of the layers
	// each layer will scan the layout image and create node positions, rotations and scales based on the layout image
	for (u32 i = 0; i < vegetationLayers.size(); i++)
		vegetationLayers[i]->processLayout();
}

// simple method to toggle lighting 
void toggleUseLight()
{
	useLight = !useLight;
	for (u32 i = 0; i < vegetationLayers.size(); i++)
		vegetationLayers[i]->setUseLight(useLight);
	if (terrain) terrain->setMaterialFlag(E_MATERIAL_FLAG::EMF_LIGHTING, useLight);
}

// simple method to toggle fog 
void toggleUseFog()
{
	useFog = !useFog;
	for (u32 i = 0; i < vegetationLayers.size(); i++)
		vegetationLayers[i]->setUseFog(useFog);
	if (terrain) terrain->setMaterialFlag(E_MATERIAL_FLAG::EMF_FOG_ENABLE, useFog);
}

// create everything specific to this demo
// we use two cameras so that the user can see the frustum in action if desired (press Q or W to toggle which camera the vegetation is referencing)
void createScene()
{
	// add a test camera with a cube to show us where it is at
	camera2 = smgr->addCameraSceneNode();
	
	// add the primary user controlled camera
	camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 1.2f);
		camera->setPosition(core::vector3df(0, 500, 500));
		camera->setTarget(core::vector3df(0, 0, 0));
		camera->setFarValue(42000.0f);
		camera->setInputReceiverEnabled(false);

	// start the demo using the user controlled camera
	viewfromcamera = camera;

	// add some lighting and fog
	smgr->addLightSceneNode(camera, vector3df(0, -50, 0), SColorf(0.5, 1, 1, 1), 4000);
	driver->setFog(SColor(0,0,0,0),EFT_FOG_LINEAR,2000,8000);

	// enable the  mouse cursor
	device->getCursorControl()->setVisible(true);

	// add terrain scene node
	terrain = smgr->addTerrainSceneNode(
		"media/_assets/_terrain/heightmaps/terrain-plains.jpg",
		0,                  // parent node
		-1,                 // node id
		core::vector3df(0.f, 0.f, 0.f),     // position
		core::vector3df(0.f, 0.f, 0.f),     // rotation
		TERRAIN_SCALE,  // scale
		video::SColor(255, 255, 255, 255),   // vertexColor
		5,                  // maxLOD
		scene::ETPS_17,             // patchSize
		4                   // smoothFactor
	);

	terrain->setMaterialFlag(video::EMF_LIGHTING, false);
	terrain->setMaterialTexture(0, driver->getTexture("media/_assets/_terrain/textures/aerial_grass_rock_diff_4k.jpg"));
	terrain->setMaterialTexture(1,driver->getTexture("media/detailmap3.jpg"));
	terrain->setMaterialTexture(1, driver->getTexture("media/_assets/_terrain/textures/aerial_grass_rock_diff_4k.jpg"));
	terrain->setMaterialType(video::EMT_DETAIL_MAP);
	terrain->scaleTexture(1.0f, 20.0f);


	// create the vegetation
	createVegetation();

	// turn on lighting and fog for the scene
	toggleUseLight();
	toggleUseFog();
}

void createGui()
{
}

// simple eventhandler
class MyEventReceiver : public IEventReceiver
{
private:
public:
	MyEventReceiver() : IEventReceiver()
	{
	}

	virtual bool OnEvent(const SEvent& event)
	{
		switch (event.EventType)
		{
			// toggle camera isinputreceiving with the right mouse button
		case EEVENT_TYPE::EET_MOUSE_INPUT_EVENT:
			if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
				smgr->getActiveCamera()->setInputReceiverEnabled(!smgr->getActiveCamera()->isInputReceiverEnabled());
			break;

		case EEVENT_TYPE::EET_KEY_INPUT_EVENT:
			if (event.KeyInput.PressedDown)
			{
				switch (event.KeyInput.Key)
				{
				case KEY_KEY_F: toggleUseFog(); break;				// toggle fog use
				case KEY_KEY_L: toggleUseLight(); break;			// toggle light use
				case KEY_KEY_Q: viewfromcamera = camera; break;		// toggle which camera the vegetation is referencing
				case KEY_KEY_W: viewfromcamera = camera2; break;	// toggle which camera the vegetation is referencing

					// render further away
				case KEY_PLUS	: 
					for (u32 i = 0; i < vegetationLayers.size(); i++)
						vegetationLayers[i]->setViewDistance(vegetationLayers[i]->getViewDistance() + 1000);
					break;
					// render closer
				case KEY_MINUS:
					for (u32 i = 0; i < vegetationLayers.size(); i++)
						vegetationLayers[i]->setViewDistance(vegetationLayers[i]->getViewDistance() - 1000);
					break;
					// toggle individual vegetation layer visibility
				case KEY_KEY_1:
					vegetationLayers[0]->setVisible(!vegetationLayers[0]->isVisible());
					break;
				case KEY_KEY_2:
					vegetationLayers[1]->setVisible(!vegetationLayers[1]->isVisible());
					break;
				case KEY_KEY_3:
					vegetationLayers[2]->setVisible(!vegetationLayers[2]->isVisible());
					break;
				case KEY_KEY_4:
					vegetationLayers[3]->setVisible(!vegetationLayers[3]->isVisible());
					break;
				case KEY_KEY_5:
					vegetationLayers[4]->setVisible(!vegetationLayers[4]->isVisible());
					break;
				case KEY_KEY_6:
					vegetationLayers[5]->setVisible(!vegetationLayers[5]->isVisible());
					break;
				}
			}
			break;
		}
		return false;
	}
};



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// everything below here is just setting up the program


MyEventReceiver receiver;

bool genericDemoSetup()
{
	// create device with full flexibility over creation parameters
	// you can add more parameters if desired, check irr::SIrrlichtCreationParameters
	irr::SIrrlichtCreationParameters params;
		params.DriverType = EDT_OPENGL;
		params.WindowSize = core::dimension2d<u32>(640, 480);
	device = createDeviceEx(params);
		if (device == 0) return false; 
			driver = device->getVideoDriver();
			smgr = device->getSceneManager();
			env = device->getGUIEnvironment();
			driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);


	// create event receiver
	device->setEventReceiver(&receiver);

	return true;
}


int main()
{
	// setup the demo
	if (!genericDemoSetup()) return 1;

	// create all of the items specific to this demo
	createScene();

	createGui();

	// run the demo
	int lastFPS = -1;
	while (device->run())
		if (device->isWindowActive())
		{
			driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(0));
			smgr->drawAll();
			env->drawAll();
			driver->endScene();

			// display frames per second in window title
			int fps = driver->getFPS();
			if (lastFPS != fps)
			{
				core::stringw str = "FPS:"; str += fps;
				str += "             #Primitive Drawn = "; str += driver->getPrimitiveCountDrawn();
				str += "               Method = ";
				if (viewfromcamera == camera) str += "  camera1";
				if (viewfromcamera == camera2) str += "  camera2";
				device->setWindowCaption(str.c_str());
				
				
				lastFPS = fps;
			}
		}

	device->drop();

	printf("Press any key to continue, press any other ley to quit.........");
	_getch();
	return 0;
}


CuteAlien
Admin
Posts: 9648
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Vegetation SceneNode

Post by CuteAlien »

Video looks good. Guess to run demo I'd have to work a bit on replacing models, not done yet ;-)

Tiny hints:
- include "irrlicht.h" not "Irrlicht.h", otherwise it won't compile on systems which don't ignore the difference (aka Unix).
- terrain detailmap texture is set twice (kinda doesn't matter probably which one you set...)
- "using namespace" should never be in a header file. Yes, that means you have to type full type-names always in headers. But otherwise it's no longer possible to use competing namespaces as soon as you include such a header, which kills the reason to have namespaces in the first place.
edit: Thinking a bit more about it - although everyone always says don't use "using namespace" in a header - it still should only be valid in a scope. So if you put those using namepace calls inside the "namespace IGE" scope it should be fine and you can avoid typing all types. So just move it a bit.

Thanks for the code!
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Vegetation SceneNode

Post by Seven »

here is test code using only dwarf.x file
you have to imagine what is a tree, rock etc :)

Code: Select all

// to use a single model for all of this uncomment this line
#define USE_SINGLE_MODEL
#define MODELNAME "media/dwarf.x"

#include <irrlicht.h>
#include "driverChoice.h"
#include "exampleHelper.h"
#include "IGE_SceneNode_Vegetation.h"
#include <conio.h>

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

// globals are easier for simple demos
IrrlichtDevice*				device		= 0;
video::IVideoDriver*		driver		= 0;
scene::ISceneManager*		smgr		= 0;
gui::IGUIEnvironment*		env			= 0;

// a few global variables specific to this demo 
ITerrainSceneNode*			terrain = 0;		// the terrain to cover with grass
scene::ICameraSceneNode*	camera = 0;			// the main FPS camera
scene::ICameraSceneNode*	camera2 = 0;		// a test camera to check grass rendering through
scene::ICameraSceneNode*	viewfromcamera = 0;	// which camera to check grass rendering through	
bool						useLight = false;
bool						useFog = false;

// some view distances for each layer of vegetation
float VIEW_DISTANCE_GRASS		= 10000;
float VIEW_DISTANCE_FLOWERS		= 5000;
float VIEW_DISTANCE_SHRUBS		= 7000;
float VIEW_DISTANCE_SMALLTREES	= 8000;
float VIEW_DISTANCE_LARGETREES	= 10000;
float VIEW_DISTANCE_ROCKS		= 10000;

// the terrain scale
#define TERRAIN_SCALE		vector3df(400.f, 4.4f, 400.f)

// the vegetation layers
array<IGE_SceneNode_Vegetation*> vegetationLayers;

/// /////////////////////////////////////////////////////////////////////
//  create 6 new scenenodes, each derived from the IGE_SceneNode_Vegetation class
//  this allows us to setup each layer independently
//  I commented the first layer (grass) extensively but the others are the same just used for different scenenodes
/// /////////////////////////////////////////////////////////////////////

class IGE_SceneNode_Vegetation_Grass : public IGE_SceneNode_Vegetation
{
public:	
	//! constructor - do nothing constructor
	IGE_SceneNode_Vegetation_Grass(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance,layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor - do nothing destructor
	virtual ~IGE_SceneNode_Vegetation_Grass() {}

	// override to provide scenenode with layout values based on pixel colors
	// in this case we are just hard coding the values, but you could use the pizel color to determine each attribute
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 200; }

	// override to provide scenenode with layout values based on pixel colors
	// in this case, if the pixel color green attribute is greater that 100 then return one of the grass templates
	// otherwise return -2 which is interpreted as no grass node in this area
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos)
	{ 
		if (pixel.getGreen() > 100) return getRandomInteger(1, templatesize) - 1;
		else return -2;
	}

	// override to provide scenenode with layout values based on pixel colors
	// in this case we are just hard coding the values, but you could use the pizel color to determine each attribute
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }

	// override to provide scenenode with layout values based on pixel colors
	// in this case we are just hard coding the values, but you could use the pizel color to determine each attribute
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(3, getRandomInteger(1,3), 3); }
};

class IGE_SceneNode_Vegetation_Flowers : public IGE_SceneNode_Vegetation
{
public:	
	//! constructor
	IGE_SceneNode_Vegetation_Flowers(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_Flowers() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 2000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(3, 8, 3); }
};

class IGE_SceneNode_Vegetation_Shrubs : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_Shrubs(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_Shrubs() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 1000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(5, 5, 5); }
};

class IGE_SceneNode_Vegetation_SmallTrees : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_SmallTrees(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_SmallTrees() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 3000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(8, 8, 8); }
};

class IGE_SceneNode_Vegetation_LargeTrees : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_LargeTrees(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_LargeTrees() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 4000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(14,14,14); }
};

class IGE_SceneNode_Vegetation_Rocks : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_Rocks(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_Rocks() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 5000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(2, 2, 2); }
};

// create all of the vegetation
// in this case, we create 6 different scenenodes and add mesh templates to each
void createVegetation()
{
	// if ther is no terrain then bail
	if (!terrain)
	{
		printf("terrain node is not valid!");
		return;
	}

	// clear any existing vegetation layers
	for (u32 i = 0; i < vegetationLayers.size(); i++) vegetationLayers[i]->remove(); vegetationLayers.clear();

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Grass* grass = new IGE_SceneNode_Vegetation_Grass(terrain, "media/_Assets/_Foliage/_LayoutImages/grasslayout.jpg",VIEW_DISTANCE_GRASS, vector3df(5, 0, 5), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
	grass->addMeshTemplate(MODELNAME, 0);
#else
	grass->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/misc/trop_grass(region).b3d", 0);
#endif
	vegetationLayers.push_back(grass);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Flowers* flowers = new IGE_SceneNode_Vegetation_Flowers(terrain, "media/_Assets/_Foliage/_LayoutImages/flowerslayout.jpg",VIEW_DISTANCE_FLOWERS, vector3df(50, 0, 50), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
	grass->addMeshTemplate(MODELNAME, 0);
#else
	flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 1.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 2.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 3.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 4.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 5.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 6.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 7.x", 0);
#endif
		vegetationLayers.push_back(flowers);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Shrubs* shrubs = new IGE_SceneNode_Vegetation_Shrubs(terrain, "media/_Assets/_Foliage/_LayoutImages/shrubslayout.jpg", VIEW_DISTANCE_SHRUBS, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
	grass->addMeshTemplate(MODELNAME, 0);
#else
	shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/fan plant.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/fan plant version 2.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/fan plant version 3.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant a.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant b.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant c.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant d.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant e.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant g.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant h.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant k.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant l.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant m.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant n.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant n version2.b3d", 0);
#endif
		vegetationLayers.push_back(shrubs);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_SmallTrees* SmallTrees = new IGE_SceneNode_Vegetation_SmallTrees(terrain, "media/_Assets/_Foliage/_LayoutImages/smalltreeslayout.jpg", VIEW_DISTANCE_SMALLTREES, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
	grass->addMeshTemplate(MODELNAME, 0);
#else
	SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2 b.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm 2.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm1.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm2.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm3.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm3 B.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm3 C.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm4.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm5.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm6.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/worn palm.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/yuka.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/yuka 2.b3d", 0);
#endif
		vegetationLayers.push_back(SmallTrees);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_LargeTrees* LargeTrees = new IGE_SceneNode_Vegetation_LargeTrees(terrain, "media/_Assets/_Foliage/_LayoutImages/largetreeslayout.jpg", VIEW_DISTANCE_LARGETREES, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
		grass->addMeshTemplate(MODELNAME, 0);
#else
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2 b.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm 2.b3d", 0);
#endif
		vegetationLayers.push_back(LargeTrees);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Rocks* rocks = new IGE_SceneNode_Vegetation_Rocks(terrain, "media/_Assets/_Foliage/_LayoutImages/rockslayout.jpg", VIEW_DISTANCE_ROCKS, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
		grass->addMeshTemplate(MODELNAME, 0);
#else
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock1.x", 0);
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock2.x", 0);
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock3.x", 0);
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock4.x", 0);
#endif
		vegetationLayers.push_back(rocks);

	// now process all of the layers
	// each layer will scan the layout image and create node positions, rotations and scales based on the layout image
	for (u32 i = 0; i < vegetationLayers.size(); i++)
		vegetationLayers[i]->processLayout();
}

// simple method to toggle lighting 
void toggleUseLight()
{
	useLight = !useLight;
	for (u32 i = 0; i < vegetationLayers.size(); i++)
		vegetationLayers[i]->setUseLight(useLight);
	if (terrain) terrain->setMaterialFlag(E_MATERIAL_FLAG::EMF_LIGHTING, useLight);
}

// simple method to toggle fog 
void toggleUseFog()
{
	useFog = !useFog;
	for (u32 i = 0; i < vegetationLayers.size(); i++)
		vegetationLayers[i]->setUseFog(useFog);
	if (terrain) terrain->setMaterialFlag(E_MATERIAL_FLAG::EMF_FOG_ENABLE, useFog);
}

// create everything specific to this demo
// we use two cameras so that the user can see the frustum in action if desired (press Q or W to toggle which camera the vegetation is referencing)
void createScene()
{
	// add a test camera with a cube to show us where it is at
	camera2 = smgr->addCameraSceneNode();
	
	// add the primary user controlled camera
	camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 1.2f);
		camera->setPosition(core::vector3df(0, 500, 500));
		camera->setTarget(core::vector3df(0, 0, 0));
		camera->setFarValue(42000.0f);
		camera->setInputReceiverEnabled(false);

	// start the demo using the user controlled camera
	viewfromcamera = camera;

	// add some lighting and fog
	smgr->addLightSceneNode(camera, vector3df(0, -50, 0), SColorf(0.5, 1, 1, 1), 4000);
	driver->setFog(SColor(0,0,0,0),EFT_FOG_LINEAR,2000,8000);

	// enable the  mouse cursor
	device->getCursorControl()->setVisible(true);

	// add terrain scene node
	terrain = smgr->addTerrainSceneNode(
		"media/_assets/_terrain/heightmaps/terrain-plains.jpg",
		0,                  // parent node
		-1,                 // node id
		core::vector3df(0.f, 0.f, 0.f),     // position
		core::vector3df(0.f, 0.f, 0.f),     // rotation
		TERRAIN_SCALE,  // scale
		video::SColor(255, 255, 255, 255),   // vertexColor
		5,                  // maxLOD
		scene::ETPS_17,             // patchSize
		4                   // smoothFactor
	);

	terrain->setMaterialFlag(video::EMF_LIGHTING, false);
	terrain->setMaterialTexture(0, driver->getTexture("media/_assets/_terrain/textures/aerial_grass_rock_diff_4k.jpg"));
	terrain->setMaterialTexture(1, driver->getTexture("media/_assets/_terrain/textures/aerial_grass_rock_diff_4k.jpg"));
	terrain->setMaterialType(video::EMT_DETAIL_MAP);
	terrain->scaleTexture(1.0f, 20.0f);


	// create the vegetation
	createVegetation();

	// turn on lighting and fog for the scene
	toggleUseLight();
	toggleUseFog();
}

void createGui()
{
}

// simple eventhandler
class MyEventReceiver : public IEventReceiver
{
private:
public:
	MyEventReceiver() : IEventReceiver()
	{
	}

	virtual bool OnEvent(const SEvent& event)
	{
		switch (event.EventType)
		{
			// toggle camera isinputreceiving with the right mouse button
		case EEVENT_TYPE::EET_MOUSE_INPUT_EVENT:
			if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
				smgr->getActiveCamera()->setInputReceiverEnabled(!smgr->getActiveCamera()->isInputReceiverEnabled());
			break;

		case EEVENT_TYPE::EET_KEY_INPUT_EVENT:
			if (event.KeyInput.PressedDown)
			{
				switch (event.KeyInput.Key)
				{
				case KEY_KEY_F: toggleUseFog(); break;				// toggle fog use
				case KEY_KEY_L: toggleUseLight(); break;			// toggle light use
				case KEY_KEY_Q: viewfromcamera = camera; break;		// toggle which camera the vegetation is referencing
				case KEY_KEY_W: viewfromcamera = camera2; break;	// toggle which camera the vegetation is referencing

					// render further away
				case KEY_PLUS	: 
					for (u32 i = 0; i < vegetationLayers.size(); i++)
						vegetationLayers[i]->setViewDistance(vegetationLayers[i]->getViewDistance() + 1000);
					break;
					// render closer
				case KEY_MINUS:
					for (u32 i = 0; i < vegetationLayers.size(); i++)
						vegetationLayers[i]->setViewDistance(vegetationLayers[i]->getViewDistance() - 1000);
					break;
					// toggle individual vegetation layer visibility
				case KEY_KEY_1:
					vegetationLayers[0]->setVisible(!vegetationLayers[0]->isVisible());
					break;
				case KEY_KEY_2:
					vegetationLayers[1]->setVisible(!vegetationLayers[1]->isVisible());
					break;
				case KEY_KEY_3:
					vegetationLayers[2]->setVisible(!vegetationLayers[2]->isVisible());
					break;
				case KEY_KEY_4:
					vegetationLayers[3]->setVisible(!vegetationLayers[3]->isVisible());
					break;
				case KEY_KEY_5:
					vegetationLayers[4]->setVisible(!vegetationLayers[4]->isVisible());
					break;
				case KEY_KEY_6:
					vegetationLayers[5]->setVisible(!vegetationLayers[5]->isVisible());
					break;
				}
			}
			break;
		}
		return false;
	}
};



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// everything below here is just setting up the program


MyEventReceiver receiver;

bool genericDemoSetup()
{
	// create device with full flexibility over creation parameters
	// you can add more parameters if desired, check irr::SIrrlichtCreationParameters
	irr::SIrrlichtCreationParameters params;
		params.DriverType = EDT_OPENGL;
		params.WindowSize = core::dimension2d<u32>(640, 480);
	device = createDeviceEx(params);
		if (device == 0) return false; 
			driver = device->getVideoDriver();
			smgr = device->getSceneManager();
			env = device->getGUIEnvironment();
			driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);


	// create event receiver
	device->setEventReceiver(&receiver);

	return true;
}


int main()
{
	// setup the demo
	if (!genericDemoSetup()) return 1;

	// create all of the items specific to this demo
	createScene();

	createGui();

	// run the demo
	int lastFPS = -1;
	while (device->run())
		if (device->isWindowActive())
		{
			driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(0));
			smgr->drawAll();
			env->drawAll();
			driver->endScene();

			// display frames per second in window title
			int fps = driver->getFPS();
			if (lastFPS != fps)
			{
				core::stringw str = "FPS:"; str += fps;
				str += "             #Primitive Drawn = "; str += driver->getPrimitiveCountDrawn();
				str += "               Method = ";
				if (viewfromcamera == camera) str += "  camera1";
				if (viewfromcamera == camera2) str += "  camera2";
				device->setWindowCaption(str.c_str());
				
				
				lastFPS = fps;
			}
		}

	device->drop();

	printf("Press any key to continue, press any other ley to quit.........");
	_getch();
	return 0;
}

CuteAlien
Admin
Posts: 9648
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Vegetation SceneNode

Post by CuteAlien »

OK, got it working with own files.

Another tiny bug: if (height > -99990) can lead to and endless loop. Probably with my terrain-file and it somehow hitting outside or something like that. So ii never increased. But just removing that line things worked (or probably could add density to ii in every loop instead).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Vegetation SceneNode

Post by Seven »

glad to hear it is working for you. I noticed that the 2nd camera isnt working here. I will investigate.
in the mean time I would be interested in any thoughts you have on it all.
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Vegetation SceneNode

Post by Seven »

Final version...........

IGE_SceneNode_Vegetation.h

Code: Select all

#pragma once

#include "Irrlicht.h"
using namespace irr;
using namespace video;
using namespace core;
using namespace gui;
using namespace scene;
using namespace io;

namespace IGE
{
	// if color is below this value thn no node is present
	#define CUTOFF 50
	#define YCUTOFF 0

	// simple rendom number generators
	extern int getRandomInteger(int min, int max);
	extern float getRandomFloat(float min, float max);

	// simple structure to hold a template node and the YOffset to apply when positioning the node
	struct IGE_MeshTemplate
	{
		IGE_MeshTemplate(IMeshSceneNode* node, float yoffset) : m_Node(node), m_YOffset(yoffset) {}
		IMeshSceneNode* m_Node		= nullptr;
		float			m_YOffset	= 0.0f;
	};

	/*
	*  the scenenode class
	*   once the scenenode is instantiated
	*	add as many templates as desired (these are the different nodes to be used)
	*   
	*   
	*/
	class IGE_SceneNode_Vegetation : public ISceneNode
	{
	public:
		ITerrainSceneNode*			m_Terrain		= nullptr;							// the terrain scenenode we are attached to
		IImage*						m_LayoutImage	= nullptr;							// the layout image - used to determine whether a node/object is at any given position

		array<IGE_MeshTemplate>		m_MeshTemplate;										// list	of nodes being used
		array<ISceneNode*>			m_Nodes;											// list of visible nodes
		array<short int>			m_NodeType;											// list of node types (index into the MeshTemplates)
		array<vector3df>			m_NodePos;											// list of node positions
		array<vector3df>			m_NodeScale;										// list of node scales
		array<vector3df>			m_NodeRotation;										// list of node rotations
		array<bool>					m_IsNodePosFree;									// list of open and closed slots in the node list

		float						m_ViewDistance				= 1000;					// distance from camera at which nodes become visible
		f32							m_VisibleOffset				= 200.f;				// resize the camera frustum by this amount, you can use the maximal radius of your mesh to prevent close plane popping
		vector3df					m_LayoutRandomness			= vector3df(0,0,0);		// add some randomness to the layout procedure

		int							m_HelperForDeleting			= false;				// variable used during update
		bool						m_UseLight					= false;				// simple variable to track light use
		bool						m_UseFog					= false;				// simple variable to track fog use	

		ICameraSceneNode*			m_CameraToUse				= 0;					// camera to use for determining node visibility
	public:
		// add a node template
		void				addMeshTemplate(stringc meshfilename, float YOffset);

		// using the terrain as the location base and the layout functions to determine position, rotation and scale of nodes
		void				processLayout();

		// clear all lists
		void				clear();

		// set which camera we use for determining node visibility
		void setCameraToUse(ICameraSceneNode* CameraToUse) { m_CameraToUse = CameraToUse; };

		// called each tick. determine which nodes should be visible based on the camera position and rotation
		void				update(vector3df center, ICameraSceneNode* camera);

		// use color information to determine the density, style, rotation and scale of the nodes
		virtual int			getDensityFromLayoutImageColor(SColor pixel);
		virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos);
		virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos);
		virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos);

		// simple getter functions
		float				getViewDistance()				{ return m_ViewDistance;	}
		bool				getUseLight()					{ return m_UseLight;		}
		bool				getUseFog()						{ return m_UseFog;			}

		// toggle light, fog and visibility
		virtual				void setViewDistance(float d)	{ m_ViewDistance = d;		}
		virtual				void setUseLight(bool v);
		virtual				void setUseFog(bool v);
		virtual				void setVisible(bool v);

	private:
		// is the point visible within the camera frustum
		bool				canBeSeenByCamPyramidPlanes(const SViewFrustum& frust, const vector3df& pos);

	public:

			//! constructor
			IGE_SceneNode_Vegetation(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutRandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id);

			//! destructor
			virtual ~IGE_SceneNode_Vegetation();

			//! frame
			virtual void OnRegisterSceneNode() _IRR_OVERRIDE_;

			//! renders the node.
			virtual void render() _IRR_OVERRIDE_;

			//! returns the axis aligned bounding box of this node
			virtual const core::aabbox3d<f32>& getBoundingBox() const _IRR_OVERRIDE_;

			//! Writes attributes of the scene node.
			virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options = 0) const _IRR_OVERRIDE_;

			//! Reads attributes of the scene node.
			virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options = 0) _IRR_OVERRIDE_;

			//! Returns type of the scene node
			virtual ESCENE_NODE_TYPE getType() const _IRR_OVERRIDE_ { return ESNT_MESH; }

	protected:
			core::aabbox3d<f32>				Box;
	};

} // end namespace IGE
IGE_SceneNode_Vegetation.cpp

Code: Select all


#include "IGE_SceneNode_Vegetation.h"

namespace IGE
{
	int getRandomInteger(int min, int max) { return rand() % (max - min + 1) + min; } //return int((double)rand() / (RAND_MAX + 1) * (max - min) + min);
	float getRandomFloat(float min, float max) { return float((double)rand() / (RAND_MAX + 1) * (max - min) + min); }

	//! constructor
	IGE_SceneNode_Vegetation::IGE_SceneNode_Vegetation(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutRandomness, bool uselight, bool usefog, 
		ISceneManager* smgr, s32 id)
		: ISceneNode(parent, smgr, id, vector3df(),vector3df(), vector3df(1,1,1)),m_Terrain(parent),m_ViewDistance(viewdistance),m_LayoutRandomness(layoutRandomness),m_UseLight(uselight),m_UseFog(usefog)
	{
#ifdef _DEBUG
		setDebugName("IGE_SceneNode_Vegetation");
#endif
		Box = parent->getBoundingBox();
		m_LayoutImage = smgr->getVideoDriver()->createImageFromFile(layoutimagefilename.c_str());
	}


	//! destructor
	IGE_SceneNode_Vegetation::~IGE_SceneNode_Vegetation()
	{
		if (m_LayoutImage) m_LayoutImage->drop(); m_LayoutImage = 0;
		clear();
	}


	//! frame
	void IGE_SceneNode_Vegetation::OnRegisterSceneNode()
	{
		if (IsVisible)
		{
			SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);

			ISceneNode::OnRegisterSceneNode();
		}
	}


	//! renders the node.
	void IGE_SceneNode_Vegetation::render()
	{
		if (m_CameraToUse != 0)
		{
			update(m_CameraToUse->getPosition(), m_CameraToUse);
		}
		else
		{
			update(SceneManager->getActiveCamera()->getPosition(), SceneManager->getActiveCamera());
		}
	//	ISceneNodeList::Iterator it = Children.begin();
	//	for (; it != Children.end(); ++it)
	//		(*it)->render();
	}

	//! returns the axis aligned bounding box of this node
	const core::aabbox3d<f32>& IGE_SceneNode_Vegetation::getBoundingBox() const
	{
		return Box;
	}

	//! Writes attributes of the scene node.
	void IGE_SceneNode_Vegetation::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const
	{
		ISceneNode::serializeAttributes(out, options);
	}


	//! Reads attributes of the scene node.
	void IGE_SceneNode_Vegetation::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options)
	{
		ISceneNode::deserializeAttributes(in, options);
	}

	// add a new template to the list
	void IGE_SceneNode_Vegetation::addMeshTemplate(stringc meshfilename, float YOffset)
	{
		// load the mesh
		IMesh* Mesh = SceneManager->getMesh(meshfilename.c_str());

		// if the mesh is valid we will use it
		if (Mesh)
		{
			IMeshSceneNode* node = SceneManager->addMeshSceneNode(Mesh);
			node->setPosition(vector3df(0, -99999, 0));
			node->getMesh()->setHardwareMappingHint(EHM_STATIC);
			node->setName("plsDoNotDeleteMe");
			node->setMaterialFlag(EMF_FOG_ENABLE, m_UseFog);
			node->setMaterialFlag(EMF_LIGHTING, m_UseLight);
			node->setMaterialType(EMT_TRANSPARENT_ALPHA_CHANNEL_REF);

			// add the mesh to the list
			IGE_MeshTemplate t(node, YOffset);
			m_MeshTemplate.push_back(t);
		}

	}

	// clear everything
	void IGE_SceneNode_Vegetation::clear()
	{
		// clear the visible nodes / objects
		if (!m_Nodes.empty())
		{
			for (u32 x = 0; x < m_Nodes.size(); x++)
			{
				if (m_Nodes[x] != NULL)
				{
					m_Nodes[x]->remove();
					m_Nodes[x] = nullptr;
				}
			}
		}

		// clear all of the lists
		m_Nodes.clear();
		m_Nodes.reallocate(0, true);
		m_NodePos.clear();
		m_NodePos.reallocate(0, true);
		m_NodeScale.clear();
		m_NodeScale.reallocate(0, true);
		m_NodeRotation.clear();
		m_NodeRotation.reallocate(0, true);
		m_NodeType.clear();
		m_NodeType.reallocate(0, true);
		m_IsNodePosFree.clear();
		m_IsNodePosFree.reallocate(0, true);
	}

	void IGE_SceneNode_Vegetation::processLayout()
	{
		// if we dont have any node / object efinitions to use then bail
		if (!(m_MeshTemplate.size() > 0))
		{
			printf("WARNING! IGE_SceneNode_Vegetation::processLayout() no plane templates found!\n");
			return;
		}

		// if we dont have a terrain then bail
		if (!m_Terrain)
		{
			printf("ERROR! IGE_SceneNode_Vegetation::processLayout() no terrain object found!\n");
			return;
		}

		// make sure that we have a layout image else bail
		if (!m_LayoutImage)
		{
			printf("ERROR! IGE_SceneNode_Vegetation::processLayout() no layout image\n");
			return;
		}

		// get the edges of the terrain scenenode
		aabbox3d<f32> terrainBox = m_Terrain->getBoundingBox();
		Box = terrainBox;
		float minX = terrainBox.MinEdge.X;
		float minZ = terrainBox.MinEdge.Z;

		m_Terrain->updateAbsolutePosition();
		float terrainX = m_Terrain->getPosition().X;
		float terrainZ = m_Terrain->getPosition().Z;

		// get the parsing scale of the layout image vs the terrain sizes
		float scaleX = terrainBox.getExtent().X / m_LayoutImage->getDimension().Width;
		float scaleZ = terrainBox.getExtent().Z / m_LayoutImage->getDimension().Height;

		float density = 1.0f;

		// based on the density, run throught the lists
		for (float i = minX; i < minX + terrainBox.getExtent().X; i+=density)
		{
			for (float ii = minZ; ii < minZ + terrainBox.getExtent().Z; )
			{
				// randomize the position a bit to prevent straight lines
				vector3df randomness(
					getRandomFloat(-m_LayoutRandomness.X, m_LayoutRandomness.X),
					getRandomFloat(-m_LayoutRandomness.Y, 0),
					getRandomFloat(-m_LayoutRandomness.Z, m_LayoutRandomness.Z));

				// get the height of the terrain at the current position
				float height = m_Terrain->getHeight(i + randomness.X, ii + randomness.Z);
				//if (height > -99990)
				{
					// get the layout image pixel color at the point relative to the terrain
					int x2 = (int)(i / scaleX);
					int y2 = (int)(ii / scaleZ);
					SColor pixel = m_LayoutImage->getPixel(x2, y2);

					vector3df pos(terrainX + i + randomness.X, height + randomness.Y, terrainZ + ii + randomness.Z);
					
					// let the user select a style based on the color
					int         style		= getStyleFromLayoutImageColor(pixel, m_MeshTemplate.size(), pos);
					vector3df   scale		= getScaleFromLayoutImageColor(pixel, m_MeshTemplate.size(), style, pos);
					vector3df   rotation	= getRotationFromLayoutImageColor(pixel, m_MeshTemplate.size(), style, pos);
				
					density = getDensityFromLayoutImageColor(pixel);
					ii += density;

					// if there is a style then a node should be created at this position
					// add at least a NULL to each list to make sure the lists sizes are matched
					if (style != -2)
					{
						pos.Y -= m_MeshTemplate[style].m_YOffset;

						m_Nodes.push_back(nullptr);
						m_NodeType.push_back(style);
						m_NodePos.push_back(pos);
						m_NodeScale.push_back(scale);
						m_NodeRotation.push_back(rotation);
						m_IsNodePosFree.push_back(true);
					}
				}
			}
		}
	}

	// check if a point is inside the viewfromcamera view frustrum 
	bool IGE_SceneNode_Vegetation::canBeSeenByCamPyramidPlanes(const SViewFrustum& frust, const vector3df& pos)
	{
		for (s32 i = 0; i < scene::SViewFrustum::VF_PLANE_COUNT; ++i)
		{
			if (frust.planes[i].classifyPointRelation(pos) == core::ISREL3D_FRONT)
				return false;
		}
		return true;
	}

	// called each frame to update what is visible and what is not
	void IGE_SceneNode_Vegetation::update(vector3df center, ICameraSceneNode* camera)
	{
		if (!isVisible()) return;

		if (m_HelperForDeleting == 0) m_HelperForDeleting = 1; else m_HelperForDeleting = 0;

		// make the frustum a little bigger to stop popping near the front plane
		SViewFrustum origFrustum = *camera->getViewFrustum();
		SViewFrustum frustum = origFrustum;
		for (int i = 0; i < scene::SViewFrustum::VF_PLANE_COUNT; ++i)
		{
			frustum.planes[i].recalculateD(frustum.planes[i].getMemberPoint() + frustum.planes[i].Normal * m_VisibleOffset);
		}
		frustum.recalculateBoundingBox();

		for (u32 i = m_HelperForDeleting; i < m_NodePos.size(); i += 2)
		{
			line3d<f32> ray;
			ray.start = center;
			ray.end = m_NodePos[i];
			float d = ray.getLength();
			if (m_IsNodePosFree[i] && d < m_ViewDistance && canBeSeenByCamPyramidPlanes(frustum, m_NodePos[i]))
			{
				if (m_MeshTemplate[m_NodeType[i]].m_Node != nullptr)
				{
					m_Nodes[i] = m_MeshTemplate[m_NodeType[i]].m_Node->clone();
					m_Nodes[i]->setPosition(m_NodePos[i]);
					m_Nodes[i]->setRotation(m_NodeRotation[i]);
					m_Nodes[i]->setScale(m_NodeScale[i]);
					m_Nodes[i]->setMaterialFlag(EMF_LIGHTING, m_UseLight);
					m_Nodes[i]->setMaterialFlag(EMF_FOG_ENABLE, m_UseFog);
					m_IsNodePosFree[i] = false;
				}
			}
			else
			{
				if (d > m_ViewDistance || !canBeSeenByCamPyramidPlanes(frustum, m_NodePos[i]))
				{
					if (m_Nodes[i] != nullptr) m_Nodes[i]->remove(); m_Nodes[i] = nullptr;
					m_IsNodePosFree[i] = true;
				}
			}
		}
	}

	int	IGE_SceneNode_Vegetation::getDensityFromLayoutImageColor(SColor pixel)
	{
		return 1;
	}

	int	IGE_SceneNode_Vegetation::getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos)
	{ 
		return (getRandomInteger(1,templatesize))-1;
	}

	vector3df   IGE_SceneNode_Vegetation::getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos)
	{ 
		return vector3df(0, getRandomFloat(0, 360), 0);
	}

	vector3df   IGE_SceneNode_Vegetation::getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos)
	{ 
		return vector3df(getRandomFloat(1, 3), getRandomFloat(1, 3), getRandomFloat(1, 3));
	}

	void  IGE_SceneNode_Vegetation::setUseLight(bool v)
	{
		m_UseLight = v;
		for (u32 i = 0; i < m_Nodes.size(); i++)
			if (m_Nodes[i] != nullptr)
				m_Nodes[i]->setMaterialFlag(E_MATERIAL_FLAG::EMF_LIGHTING, v);

		for (u32 i = 0; i < m_MeshTemplate.size(); i++)
			m_MeshTemplate[i].m_Node->setMaterialFlag(E_MATERIAL_FLAG::EMF_LIGHTING, v);
	}

	void  IGE_SceneNode_Vegetation::setUseFog(bool v)
	{
		m_UseFog = v;
		for (u32 i = 0; i < m_Nodes.size(); i++)
			if (m_Nodes[i] != nullptr)
				m_Nodes[i]->setMaterialFlag(E_MATERIAL_FLAG::EMF_FOG_ENABLE, v);

		for (u32 i = 0; i < m_MeshTemplate.size(); i++)
				m_MeshTemplate[i].m_Node->setMaterialFlag(E_MATERIAL_FLAG::EMF_FOG_ENABLE, v);
	}

	void  IGE_SceneNode_Vegetation::setVisible(bool v)
	{
		if (isVisible() == v) return;
		ISceneNode::setVisible(v);
		for (u32 i = 0; i < m_Nodes.size(); i++)
			if (m_Nodes[i] != nullptr)
				m_Nodes[i]->setVisible(v);
		
		for (u32 i = 0; i < m_MeshTemplate.size(); i++)
				m_MeshTemplate[i].m_Node->setVisible(v);
	}


} // end namspace IGE

Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Vegetation SceneNode

Post by Seven »

and the final test program...........

Code: Select all

// to use a single model for all of this uncomment this line
// #define USE_SINGLE_MODEL
#define MODELNAME "media/_Assets/_Models/_Foliage/Seven/wheat1.obj"


#include <irrlicht.h>
#include "driverChoice.h"
#include "exampleHelper.h"
#include "IGE_SceneNode_Vegetation.h"
#include <conio.h>

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

// globals are easier for simple demos
IrrlichtDevice*				device		= 0;
video::IVideoDriver*		driver		= 0;
scene::ISceneManager*		smgr		= 0;
gui::IGUIEnvironment*		env			= 0;

// a few global variables specific to this demo 
ITerrainSceneNode*			terrain = 0;		// the terrain to cover with grass
scene::ICameraSceneNode*	MainCamera = 0;			// the main FPS camera
scene::ICameraSceneNode*	Camera2 = 0;		// a test camera to check grass rendering through
bool						useLight = false;
bool						useFog = false;

// some view distances for each layer of vegetation
float VIEW_DISTANCE_GRASS		= 10000;
float VIEW_DISTANCE_FLOWERS		= 5000;
float VIEW_DISTANCE_SHRUBS		= 7000;
float VIEW_DISTANCE_SMALLTREES	= 8000;
float VIEW_DISTANCE_LARGETREES	= 10000;
float VIEW_DISTANCE_ROCKS		= 10000;

// the terrain scale
#define TERRAIN_SCALE		vector3df(400.f, 4.4f, 400.f)

// the vegetation layers
array<IGE_SceneNode_Vegetation*> vegetationLayers;

/// /////////////////////////////////////////////////////////////////////
//  create 6 new scenenodes, each derived from the IGE_SceneNode_Vegetation class
//  this allows us to setup each layer independently
//  I commented the first layer (grass) extensively but the others are the same just used for different scenenodes
/// /////////////////////////////////////////////////////////////////////

class IGE_SceneNode_Vegetation_Grass : public IGE_SceneNode_Vegetation
{
public:	
	//! constructor - do nothing constructor
	IGE_SceneNode_Vegetation_Grass(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance,layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor - do nothing destructor
	virtual ~IGE_SceneNode_Vegetation_Grass() {}

	// override to provide scenenode with layout values based on pixel colors
	// in this case we are just hard coding the values, but you could use the pizel color to determine each attribute
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 200; }

	// override to provide scenenode with layout values based on pixel colors
	// in this case, if the pixel color green attribute is greater that 100 then return one of the grass templates
	// otherwise return -2 which is interpreted as no grass node in this area
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos)
	{ 
		if (pixel.getGreen() > 100) return getRandomInteger(1, templatesize) - 1;
		else return -2;
	}

	// override to provide scenenode with layout values based on pixel colors
	// in this case we are just hard coding the values, but you could use the pizel color to determine each attribute
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }

	// override to provide scenenode with layout values based on pixel colors
	// in this case we are just hard coding the values, but you could use the pizel color to determine each attribute
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(3, getRandomInteger(1,10), 3); }
};

class IGE_SceneNode_Vegetation_Flowers : public IGE_SceneNode_Vegetation
{
public:	
	//! constructor
	IGE_SceneNode_Vegetation_Flowers(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_Flowers() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 2000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(3, 8, 3); }
};

class IGE_SceneNode_Vegetation_Shrubs : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_Shrubs(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_Shrubs() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 1000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(5, 5, 5); }
};

class IGE_SceneNode_Vegetation_SmallTrees : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_SmallTrees(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_SmallTrees() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 3000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(8, 8, 8); }
};

class IGE_SceneNode_Vegetation_LargeTrees : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_LargeTrees(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_LargeTrees() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 4000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(14,14,14); }
};

class IGE_SceneNode_Vegetation_Rocks : public IGE_SceneNode_Vegetation
{
public:
	//! constructor
	IGE_SceneNode_Vegetation_Rocks(ITerrainSceneNode* parent, stringc layoutimagefilename, float viewdistance, vector3df layoutrandomness, bool uselight, bool usefog, ISceneManager* smgr, s32 id)
		: IGE_SceneNode_Vegetation(parent, layoutimagefilename, viewdistance, layoutrandomness, uselight, usefog, smgr, id)
	{}

	//! destructor
	virtual ~IGE_SceneNode_Vegetation_Rocks() {}

	// override to provide scenenode with layout values based on pixel colors
	virtual int			getDensityFromLayoutImageColor(SColor pixel) { return 5000; }
	virtual int			getStyleFromLayoutImageColor(SColor pixel, int templatesize, vector3df pos) { return (getRandomInteger(1, templatesize)) - 1; }
	virtual vector3df   getRotationFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(0, getRandomFloat(0, 360), 0); }
	virtual vector3df   getScaleFromLayoutImageColor(SColor pixel, int templatesize, int style, vector3df pos) { return vector3df(2, 2, 2); }
};

// create all of the vegetation
// in this case, we create 6 different scenenodes and add mesh templates to each
void createVegetation()
{
	// if ther is no terrain then bail
	if (!terrain)
	{
		printf("terrain node is not valid!");
		return;
	}

	// clear any existing vegetation layers
	for (u32 i = 0; i < vegetationLayers.size(); i++) vegetationLayers[i]->remove(); vegetationLayers.clear();

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Grass* grass = new IGE_SceneNode_Vegetation_Grass(terrain, "media/_Assets/_Foliage/_LayoutImages/grasslayout.jpg",VIEW_DISTANCE_GRASS, vector3df(5, 0, 5), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
	grass->addMeshTemplate(MODELNAME, 0);
#else
	grass->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/misc/trop_grass(region).b3d", 0);
#endif
	vegetationLayers.push_back(grass);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Flowers* flowers = new IGE_SceneNode_Vegetation_Flowers(terrain, "media/_Assets/_Foliage/_LayoutImages/flowerslayout.jpg",VIEW_DISTANCE_FLOWERS, vector3df(50, 0, 50), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
	grass->addMeshTemplate(MODELNAME, 0);
#else
	flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 1.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 2.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 3.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 4.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 5.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 6.x", 0);
		flowers->addMeshTemplate("media/_assets/_Models/_Foliage/tropical/arteria3d_tropicalpack/flowers/flower 7.x", 0);
#endif
		vegetationLayers.push_back(flowers);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Shrubs* shrubs = new IGE_SceneNode_Vegetation_Shrubs(terrain, "media/_Assets/_Foliage/_LayoutImages/shrubslayout.jpg", VIEW_DISTANCE_SHRUBS, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
	grass->addMeshTemplate(MODELNAME, 0);
#else
	shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/fan plant.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/fan plant version 2.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/fan plant version 3.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant a.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant b.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant c.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant d.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant e.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant g.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant h.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant k.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant l.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant m.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant n.b3d", 0);
		shrubs->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/plants/plant n version2.b3d", 0);
#endif
		vegetationLayers.push_back(shrubs);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_SmallTrees* SmallTrees = new IGE_SceneNode_Vegetation_SmallTrees(terrain, "media/_Assets/_Foliage/_LayoutImages/smalltreeslayout.jpg", VIEW_DISTANCE_SMALLTREES, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
	grass->addMeshTemplate(MODELNAME, 0);
#else
	SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2 b.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm 2.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm1.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm2.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm3.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm3 B.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm3 C.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm4.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm5.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/palm6.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/worn palm.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/yuka.b3d", 0);
		SmallTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/yuka 2.b3d", 0);
#endif
		vegetationLayers.push_back(SmallTrees);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_LargeTrees* LargeTrees = new IGE_SceneNode_Vegetation_LargeTrees(terrain, "media/_Assets/_Foliage/_LayoutImages/largetreeslayout.jpg", VIEW_DISTANCE_LARGETREES, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
		grass->addMeshTemplate(MODELNAME, 0);
#else
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/bananatree version2 b.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm.b3d", 0);
		LargeTrees->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/trees/floorpalm 2.b3d", 0);
#endif
		vegetationLayers.push_back(LargeTrees);

	// create the layer and add some templates to it
	IGE_SceneNode_Vegetation_Rocks* rocks = new IGE_SceneNode_Vegetation_Rocks(terrain, "media/_Assets/_Foliage/_LayoutImages/rockslayout.jpg", VIEW_DISTANCE_ROCKS, vector3df(500, 0, 500), useLight, useFog, smgr, 0);
#ifdef USE_SINGLE_MODEL
		grass->addMeshTemplate(MODELNAME, 0);
#else
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock1.x", 0);
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock2.x", 0);
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock3.x", 0);
		rocks->addMeshTemplate("media/_assets/_models/_foliage/tropical/arteria3d_tropicalpack/rocks/rock4.x", 0);
#endif
		vegetationLayers.push_back(rocks);

	// now process all of the layers
	// each layer will scan the layout image and create node positions, rotations and scales based on the layout image
	for (u32 i = 0; i < vegetationLayers.size(); i++)
		vegetationLayers[i]->processLayout();
}

// simple method to toggle lighting 
void toggleUseLight()
{
	useLight = !useLight;
	for (u32 i = 0; i < vegetationLayers.size(); i++)
		vegetationLayers[i]->setUseLight(useLight);
	if (terrain) terrain->setMaterialFlag(E_MATERIAL_FLAG::EMF_LIGHTING, useLight);
}

// simple method to toggle fog 
void toggleUseFog()
{
	useFog = !useFog;
	for (u32 i = 0; i < vegetationLayers.size(); i++)
		vegetationLayers[i]->setUseFog(useFog);
	if (terrain) terrain->setMaterialFlag(E_MATERIAL_FLAG::EMF_FOG_ENABLE, useFog);
}

// create everything specific to this demo
// we use two cameras so that the user can see the frustum in action if desired (press Q or W to toggle which camera the vegetation is referencing)
void createScene()
{
	// add a test camera with a cube to show us where it is at
	Camera2 = smgr->addCameraSceneNode();
	Camera2->setFarValue(42000.0f);

	// add the primary user controlled camera
	MainCamera = smgr->addCameraSceneNodeFPS(0, 100.0f, 1.2f);
		MainCamera->setPosition(core::vector3df(0, 500, 500));
		MainCamera->setTarget(core::vector3df(0, 0, 0));
		MainCamera->setFarValue(42000.0f);
		MainCamera->setInputReceiverEnabled(false);

	// add some lighting and fog
	smgr->addLightSceneNode(MainCamera, vector3df(0, -50, 0), SColorf(0.5, 1, 1, 1), 4000);
	smgr->addLightSceneNode(Camera2, vector3df(0, -50, 0), SColorf(0.5, 1, 1, 1), 4000);
	driver->setFog(SColor(0,0,0,0),EFT_FOG_LINEAR,2000,8000);

	// enable the  mouse cursor
	device->getCursorControl()->setVisible(true);

	// add terrain scene node
	terrain = smgr->addTerrainSceneNode(
		"media/_assets/_terrain/heightmaps/terrain-plains.jpg",
		0,                  // parent node
		-1,                 // node id
		core::vector3df(0.f, 0.f, 0.f),     // position
		core::vector3df(0.f, 0.f, 0.f),     // rotation
		TERRAIN_SCALE,  // scale
		video::SColor(255, 255, 255, 255),   // vertexColor
		5,                  // maxLOD
		scene::ETPS_17,             // patchSize
		4                   // smoothFactor
	);

	terrain->setMaterialFlag(video::EMF_LIGHTING, false);
	terrain->setMaterialTexture(0, driver->getTexture("media/_assets/_terrain/textures/aerial_grass_rock_diff_4k.jpg"));
	terrain->setMaterialTexture(1, driver->getTexture("media/_assets/_terrain/textures/aerial_grass_rock_diff_4k.jpg"));
	terrain->setMaterialType(video::EMT_DETAIL_MAP);
	terrain->scaleTexture(1.0f, 20.0f);


	// create the vegetation
	createVegetation();

	// turn on lighting and fog for the scene
	toggleUseLight();
	toggleUseFog();
}

void createGui()
{
}

// simple eventhandler
class MyEventReceiver : public IEventReceiver
{
private:
public:
	MyEventReceiver() : IEventReceiver()
	{
	}

	virtual bool OnEvent(const SEvent& event)
	{
		switch (event.EventType)
		{
			// toggle camera isinputreceiving with the right mouse button
		case EEVENT_TYPE::EET_MOUSE_INPUT_EVENT:
			if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
				smgr->getActiveCamera()->setInputReceiverEnabled(!smgr->getActiveCamera()->isInputReceiverEnabled());
			break;

		case EEVENT_TYPE::EET_KEY_INPUT_EVENT:
			if (event.KeyInput.PressedDown)
			{
				switch (event.KeyInput.Key)
				{
				case KEY_KEY_F: toggleUseFog(); break;				// toggle fog use
				case KEY_KEY_L: toggleUseLight(); break;			// toggle light use

				case KEY_KEY_Q: 
					for (u32 i = 0; i < vegetationLayers.size(); i++)
						vegetationLayers[i]->setCameraToUse(MainCamera);
					break;		// toggle which camera the vegetation is referencing
				case KEY_KEY_W: 
					Camera2->setPosition(MainCamera->getPosition());
					Camera2->setTarget(MainCamera->getTarget());
					Camera2->updateAbsolutePosition();
					Camera2->render();
					for (u32 i = 0; i < vegetationLayers.size(); i++)
						vegetationLayers[i]->setCameraToUse(Camera2);
					break;	// toggle which camera the vegetation is referencing

					// render further away
				case KEY_PLUS	: 
					for (u32 i = 0; i < vegetationLayers.size(); i++)
						vegetationLayers[i]->setViewDistance(vegetationLayers[i]->getViewDistance() + 1000);
					break;
					// render closer
				case KEY_MINUS:
					for (u32 i = 0; i < vegetationLayers.size(); i++)
						vegetationLayers[i]->setViewDistance(vegetationLayers[i]->getViewDistance() - 1000);
					break;
					// toggle individual vegetation layer visibility
				case KEY_KEY_1:
					vegetationLayers[0]->setVisible(!vegetationLayers[0]->isVisible());
					break;
				case KEY_KEY_2:
					vegetationLayers[1]->setVisible(!vegetationLayers[1]->isVisible());
					break;
				case KEY_KEY_3:
					vegetationLayers[2]->setVisible(!vegetationLayers[2]->isVisible());
					break;
				case KEY_KEY_4:
					vegetationLayers[3]->setVisible(!vegetationLayers[3]->isVisible());
					break;
				case KEY_KEY_5:
					vegetationLayers[4]->setVisible(!vegetationLayers[4]->isVisible());
					break;
				case KEY_KEY_6:
					vegetationLayers[5]->setVisible(!vegetationLayers[5]->isVisible());
					break;
				}
			}
			break;
		}
		return false;
	}
};



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// everything below here is just setting up the program


MyEventReceiver receiver;

bool genericDemoSetup()
{
	// create device with full flexibility over creation parameters
	// you can add more parameters if desired, check irr::SIrrlichtCreationParameters
	irr::SIrrlichtCreationParameters params;
		params.DriverType = EDT_OPENGL;
		params.WindowSize = core::dimension2d<u32>(640, 480);
	device = createDeviceEx(params);
		if (device == 0) return false; 
			driver = device->getVideoDriver();
			smgr = device->getSceneManager();
			env = device->getGUIEnvironment();
			driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);


	// create event receiver
	device->setEventReceiver(&receiver);

	return true;
}


int main()
{
	// setup the demo
	if (!genericDemoSetup()) return 1;

	// create all of the items specific to this demo
	createScene();

	createGui();

	// run the demo
	int lastFPS = -1;
	while (device->run())
		if (device->isWindowActive())
		{
			driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(0));
			smgr->drawAll();
			env->drawAll();
			driver->endScene();

			// display frames per second in window title
			int fps = driver->getFPS();
			if (lastFPS != fps)
			{
				core::stringw str = "FPS:"; str += fps;
				str += "             #Primitive Drawn = "; str += driver->getPrimitiveCountDrawn();
				device->setWindowCaption(str.c_str());
				
				
				lastFPS = fps;
			}
		}

	device->drop();

	printf("Press any key to continue, press any other ley to quit.........");
	_getch();
	return 0;
}

Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Vegetation SceneNode

Post by Seven »

Keyboard entries :

1,2,3,4,5,6 hide / show layers
F - toggle using fog
L - toggle using light
Q - use main FPS camera for determining node visibility
W - place 2nd camera at FPS camera position and rotation (allows us to 'see' what the visibility frustum looks like
PLUS - increase visibility distance
MINUS - decrease visibility distance

right mouse - toggle FPS camera input receiver status

final thoughts :
I am pleased with the speed and the looks of the system. I like that it is now a scenenode also as I dont have to manage it in the program.
I am also pleased with the ability to control type, rotation and scale via images.

~ 400fps using 6 layers (grass, flowers, shrubs, small trees, large trees and rocks.
Processor Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz, 4008 Mhz, 4 Core(s), 8 Logical Processor(s)
NVidia GeForce GTX 1080 at 1920 x 1080 windowed mode

I think that I will create an octtree structure for holding node positions and whatnot. it might make it a little faster yet.
also will add feature to allow loading from file (maybe use XML to store mesh filenames)

oh well, off to work on the actual game program again.....
CuteAlien
Admin
Posts: 9648
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Vegetation SceneNode

Post by CuteAlien »

Quadtree would be sufficient (basically same as octree but in 2d). Thought I prefer using grids in most cases (way simpler and often even faster). But that will improve speed only if the visibility checks are the bottleneck - if it's the sending to the gpu (which I'd suspect for this...) then it won't make a difference. With grids you might be able to merge similar objects together grid-wise so you have to send less nodes.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Post Reply