// ----------------------------------------------------------------------------
// Blitz-Irrlicht 1.8.3 Wrapper by Ed Musolino 2015
// Based off of Frank Dodd's Irrlicht 1.7 Freebasic Wrapper (2006-2010)
// ----------------------------------------------------------------------------

//  This software is provided 'as-is', without any express Or implied
//  warranty.  In no event will the authors be held liable For any damages
//  arising from the use of this software.

//  Permission is granted To anyone To use this software For any purpose,
//  including commercial applications, And To alter it And redistribute it
//  freely, subject To the following restrictions:

//  1. The Origin of this software must Not be misrepresented; you must not
//     claim that you wrote the original software. If you use this software
//     in a product, an acknowledgement in the product documentation would be
//     appreciated but is Not required.
//  2. Altered source versions must be clearly marked as such, And must Not be
//     misrepresented as being the original software.
//  3. This notice may Not be removed Or altered from any source distribution.
//
// /////////////////////////////////////////////////////////////////////////////

#include "bIrrlicht.h"
#include "os.h"
#include "CBatchingMesh.h"


extern IrrlichtDevice*		device;
extern IVideoDriver*		driver;
extern ISceneManager*		smgr;
extern IGUIEnvironment*		guienv;


/* ////////////////////////////////////////////////////////////////////////////
Global Function Declarations

all of the below functions are declared as C functions and are exposed without
any mangled names so that they can be easily imported into imperative
languages like Blitz
*/
extern "C"
{


/*  CreateMesh([parent])
parent (optional) - This optional parameter allows you to specify another entity which will act as the parent to this mesh. 

Create a 'blank' mesh entity and returns its handle. 
When a mesh is first created it has no surfaces, vertices or triangles associated with it. 
To add geometry to this mesh, you will need to: 
CreateSurface() ; To make a surface 
AddVertex ; You will need to add at least 3 to make a Triangle 
AddTriangle ; This will add a triangle by connecting the Vertices (points) you added to the mesh. 
*/
/* ----------------------------------------------------------------------------
create a mesh from an array of verticies, an array of incidies to those
verticies that connect them to form triangles and finally texture co-ordinates
to map the verticies against a texture plane
*/
DLL_EXPORT int * BBCALL iCreateMesh(
                                char *cptrMeshName,
                                int iVertexCount,
                                IRR_VERT *vVertices,
                                int iIndicesCount,
                                unsigned short *usIndices )
{
    int iLoop;

    /* get the mesh cache for the scene */
    IMeshCache *mcache = smgr->getMeshCache();

    SMeshBuffer *smeshbuffer = new SMeshBuffer;
    SMesh *smesh = new SMesh;
    SAnimatedMesh * sanimmesh = new SAnimatedMesh;

    // add all of the verts
//    printf( "Adding %d verts\n", iVertexCount );
    smeshbuffer->Vertices.set_used(iVertexCount);
    for ( iLoop = 0; iLoop < iVertexCount; iLoop++ )
    {
        smeshbuffer->Vertices[iLoop].Pos.set(
                                            vVertices[iLoop].x,
                                            vVertices[iLoop].y,
                                            vVertices[iLoop].z );
        smeshbuffer->Vertices[iLoop].Normal.set(
                                            vVertices[iLoop].normal_x,
                                            vVertices[iLoop].normal_y,
                                            vVertices[iLoop].normal_z );
        smeshbuffer->Vertices[iLoop].Color.color = vVertices[iLoop].vcolor;
        smeshbuffer->Vertices[iLoop].TCoords.set(
                                            vVertices[iLoop].texture_x,
                                            vVertices[iLoop].texture_y );
    }

    // add all of the indices
//    printf( "Adding %d indicies\n", iIndicesCount );
    smeshbuffer->Indices.set_used(iIndicesCount);
    for ( iLoop = 0; iLoop < iIndicesCount; iLoop++ )
    {
        smeshbuffer->Indices[iLoop] = usIndices[iLoop];
    }

    // assemble the mesh objects
	smeshbuffer->recalculateBoundingBox();
    smesh->addMeshBuffer( smeshbuffer );
    smeshbuffer->drop();

    smesh->recalculateBoundingBox();
    sanimmesh->addMesh( smesh );
    mcache->addMesh( cptrMeshName, sanimmesh );

    // recalculate the bounding box around the object for clipping purposes
    sanimmesh->recalculateBoundingBox();

	return (int *)sanimmesh;
}

/*  LoadMesh(filename$,[Parent])
Filename$ - Name of the file containing the model to load. 
Parent (optional) - Specify an entity to act as a Parent to the loaded mesh. 

LoadMesh, as the name suggests, Loads a mesh from an .X, .3DS or .B3D file (Usually created in advance by one of a number of 3D model creation packages) and returns the mesh handle. 
Any hierarchy and animation information in the file will be ignored. Use LoadAnimMesh to maintain hierarchy and animation information. 
The optional parent parameter allows you to specify a parent entity for the mesh so that when the parent is moved the child mesh will move with it. However, this relationship is one way; applying movement commands to the child will not affect the parent. 
Specifying a parent entity will still result in the mesh being created at position 0,0,0 rather than at the parent entity's position. 
See also: LoadAnimMesh. 
*/
DLL_EXPORT int * BBCALL iLoadMesh( const char *cptrFile )
{
    /* load a mesh and add it to the scene */
	IAnimatedMesh* mesh = smgr->getMesh( cptrFile );
	return (int *)mesh;
}

DLL_EXPORT int * BBCALL iLoadMeshNode( const char *cptrFile, ISceneNode* parent = 0 )
{
    /* load a mesh and add it to the scene */
	IAnimatedMesh* mesh = smgr->getMesh( cptrFile );
	IMeshSceneNode* node = smgr->addMeshSceneNode( mesh, parent );//addAnimatedMeshSceneNode( mesh );
	if(parent)
		node->setParent( parent );
	return (int *)node;
	//return (int *)mesh;
}

/* ----------------------------------------------------------------------------
Gets the node's mesh.
*/
DLL_EXPORT int * BBCALL iGetNodeMesh( IMeshSceneNode* node)  //IAnimatedMesh* ISceneNode
{
	//return (int*)(reinterpret_cast<IAnimatedMeshSceneNode*>(node))->getMesh();
	return (int*)node->getMesh();

}

/* ----------------------------------------------------------------------------
Gets the node's mesh.
*/
//DLL_EXPORT void * BBCALL iGetNodeOctTreeMesh( ISceneNode* node)//IAnimatedMesh*
//{
//	return (reinterpret_cast<IMeshSceneNode*>(node))->getMesh();
//}

/*  LoadAnimMesh( Filename$, [Parent] )
Filename$ - Name of the file containing the model to load. 
Parent (optional) - Specify an entity to act as a Parent to the loaded mesh. 

LoadAnimMesh, similar to LoadMesh, Loads a mesh from an .X, .3DS or .B3D file and returns a mesh handle. 
The difference between LoadMesh and LoadAnimMesh is that any hierarchy and animation information present in the file is retained. You can then either activate the animation by using the Animate command or find child entities within the hierarchy by using the FindChild(), GetChild() functions. 
The optional parent parameter allows you to specify a parent entity for the mesh so that when the parent is moved the child mesh will move with it. However, this relationship is one way; applying movement commands to the child will not affect the parent. 
Specifying a parent entity will still result in the mesh being created at position 0,0,0 rather than at the parent entity's position. 
*/
DLL_EXPORT int * BBCALL iLoadAnimMesh( const char *cptrFile, ISceneNode* parent = 0 )
{
    /* load a mesh and add it to the scene */
	IAnimatedMesh* mesh = smgr->getMesh( cptrFile );
	IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh, parent );
//	if(parent)
//		node->setParent( parent );
	return (int *)node;
	//return (int *)mesh;
}

/* ----------------------------------------------------------------------------
write the first frame of the supplied animated mesh out to a file using the
specified file format
*/
DLL_EXPORT int BBCALL iWriteMesh( IAnimatedMesh* mesh, int type, const char * filename )
{
	int status = 0;
	EMESH_WRITER_TYPE writer_type[] = { EMWT_IRR_MESH, EMWT_COLLADA, EMWT_STL };

	/* create a mesh writer to save the object */
	IMeshWriter* meshwriter = smgr->createMeshWriter( writer_type[type] );

	if ( meshwriter )
	{
		status = 1;
		// open the file
		IWriteFile *file = guienv->getFileSystem()->createAndWriteFile( filename );
		if ( file )
		{
			status = 2;
			// get the static mesh from within the animated mesh object
			IMesh * staticmesh = mesh->getMesh(0);

			// write the mesh out to file
			if ( meshwriter->writeMesh ( file, staticmesh ))
			{
				status = 3;
			}

			// close the file
			file->drop();
		}
		// we have finished with the mesh writer
		meshwriter->drop();
	}
	return status;
}

/* ----------------------------------------------------------------------------
Clears all meshes that are held in the mesh cache but not used anywhere else.

Warning: If you have pointers to meshes that were loaded with
ISceneManager::getMesh() and you did not grab them, then they may become
invalid.
*/
DLL_EXPORT void BBCALL iClearUnusedMeshes( void )
{
    /* get the mesh cache for the scene */
    IMeshCache *mcache = smgr->getMeshCache();

    /* remove the selected mesh */
    mcache->clearUnusedMeshes();
}

/* ----------------------------------------------------------------------------
remove a loaded mesh from the scene cache useful if you are dealing with large
numbers of meshes that you dont want cached in memory at the same time (for
example when swapping between BSP maps for levels
*/
DLL_EXPORT void BBCALL iFreeMesh( IAnimatedMesh* mesh )
{
    /* get the mesh cache for the scene */
    IMeshCache *mcache = smgr->getMeshCache();

    /* remove the selected mesh */
    mcache->removeMesh( mesh );
}

/* ----------------------------------------------------------------------------
rename a loaded mesh through the scene cache, the mesh can then subsequently be
loaded again as a different mesh
*/
DLL_EXPORT void BBCALL iNameMesh( IAnimatedMesh* mesh, const char * name )
{
    /* get the mesh cache for the scene */
    IMeshCache *mcache = smgr->getMeshCache();

    /* remove the selected mesh */
    mcache->renameMesh( mesh, name );
}


/*  CreateCube( [parent] )
[parent] (optional) - This allows you to set the parent entity of Cube. 

Creates a cube mesh/entity and returns its handle. 
The cube will extend from -1,-1,-1 to +1,+1,+1. 
The optional parent parameter allow you to specify a parent entity for the cube so that when the parent is moved the child cube will move with it. However, this relationship is one way; applying movement commands to the child will not affect the parent. 
Specifying a parent entity will still result in the cube being created at position 0,0,0 rather than at the parent entity's position. 
Creation of cubes, cylinders and cones are a great way of getting scenes set up quickly, as they can act as placeholders for more complex pre-modeled meshes later on in program development. 
See also: CreateSphere(), CreateCylinder(), CreateCone(). 
*/
DLL_EXPORT int * BBCALL iCreateCube( float size, ISceneNode* parent )
{
    return (int *)smgr->addCubeSceneNode( size, parent );
}
/*DLL_EXPORT void BBCALL iNodeAlpha( ISceneNode* node, int alpha )
{
	node->getMaterial(0).AmbientColor.setAlpha(alpha);
    //IMeshSceneNode* mesh = smgr->addCubeSceneNode( size, parent );
	//video::E_COLOR_MATERIAL* color = mesh->getMaterial(0).DiffuseColor getDiffuseColor();
}
DLL_EXPORT void BBCALL iNodeColor( ISceneNode* node, int red, int green, int blue )
{
	node->getMaterial(0).AmbientColor.setRed(red);
	node->getMaterial(0).AmbientColor.setGreen(green);
	node->getMaterial(0).AmbientColor.setBlue(blue);
    //IMeshSceneNode* mesh = smgr->addCubeSceneNode( size, parent );
	//video::E_COLOR_MATERIAL* color = mesh->getMaterial(0).DiffuseColor getDiffuseColor();
}*/
/*  CreateSphere ( [segments][,parent] )
segments (optional) - sphere detail. Defaults to 8. 
parent (optional) - parent entity of sphere 

Creates a sphere mesh/entity and returns its handle. 
The sphere will be centred at 0,0,0 and will have a radius of 1. 
The segments value must be in the range 2-100 inclusive, although this is only checked in debug mode. A common mistake is to leave debug mode off and specify the parent parameter (usually an eight digit memory address) in the place of the segments value. As the amount of polygons used to create a sphere is exponentially proportional to the segments value, this will result in Blitz trying to create a sphere with unimaginable amounts of polygons! Depending on how unlucky you are, your computer will then crash. 
Example segments values: 
8: 224 polygons - bare minimum amount of polygons for a sphere 
16: 960 polygons - smooth looking sphere at medium-high distances 
32: 3968 polygons - smooth sphere at close distances 
The optional parent parameter allow you to specify a parent entity for the sphere so that when the parent is moved the child sphere will move with it. However, this relationship is one way; applying movement commands to the child will not affect the parent. 
Specifying a parent entity will still result in the sphere being created at position 0,0,0 rather than at the parent entity's position. 
See also: CreateCube, CreateCylinder, CreateCone. 
*/
DLL_EXPORT int * BBCALL iCreateSphere( int polyCount = 16, ISceneNode* parent = 0 )
{
	IMesh* mesh = smgr->addSphereMesh( "", 1.0, polyCount, polyCount );
    //IMeshSceneNode* node = smgr->addSphereSceneNode( radius, polyCount );
	IMeshSceneNode* node = smgr->addMeshSceneNode( mesh, parent );

	return (int *)node;

	//return (int *)smgr->addMeshSceneNode( mesh, parent, -1, vector3df(0,0,0), vector3df(0,0,0), vector3df(1.0,1.0,1.0),false);

}

/*  CreateCylinder ( [segments][,solid][,parent] )
segments (optional) - cylinder detail. Defaults to 8. 
solid (optional) - true for a cylinder, false for a tube. Defaults to true. 
parent (optional) - parent entity of cylinder 

Creates a cylinder mesh/entity and returns its handle. 
The cylinder will be centred at 0,0,0 and will have a radius of 1. 
The segments value must be in the range 3-100 inclusive, although this is only checked in debug mode. A common mistake is to leave debug mode off and specify the parent parameter (usually an eight digit memory address) in the place of the segments value. As the amount of polygons used to create a cylinder is exponentially proportional to the segments value, this will result in Blitz trying to create a cylinder with unimaginable amounts of polygons! Depending on how unlucky you are, your computer may then crash. 
Example segments values (solid=true): 
3: 8 polygons - a prism 
8: 28 polygons - bare minimum amount of polygons for a cylinder 
16: 60 polygons - smooth cylinder at medium-high distances 
32: 124 polygons - smooth cylinder at close distances 
The optional parent parameter allow you to specify a parent entity for the cylinder so that when the parent is moved the child cylinder will move with it. However, this relationship is one way; applying movement commands to the child will not affect the parent. 
Specifying a parent entity will still result in the cylinder being created at position 0,0,0 rather than at the parent entity's position. 
See also: CreateCube, CreateSphere, CreateCone. 
*/


/*  CreateCone ( [segments][,solid][,parent] )
segments (optional) - cone detail. Defaults to 8. 
solid (optional) - true for a cone with a base, false for a cone without a base. Defaults to true. 
parent (optional) - parent entity of cone 

Creates a cone mesh/entity and returns its handle. 
The cone will be centred at 0,0,0 and the base of the cone will have a radius of 1. 
The segments value must be in the range 3-100 inclusive, although this is only checked in debug mode. A common mistake is to leave debug mode off and specify the parent parameter (usually an eight digit memory address) in the place of the segments value. As the amount of polygons used to create a cone is exponentially proportional to the segments value, this will result in Blitz trying to create a cone with unimaginable amounts of polygons! Depending on how unlucky you are, your computer will then crash. 
Example segments values (solid=true): 
4: 6 polygons - a pyramid 
8: 14 polygons - bare minimum amount of polygons for a cone 
16: 30 polygons - smooth cone at medium-high distances 
32: 62 polygons - smooth cone at close distances 
The optional parent parameter allow you to specify a parent entity for the cone so that when the parent is moved the child cone will move with it. However, this relationship is one way; applying movement commands to the child will not affect the parent. 
Specifying a parent entity will still result in the cone being created at position 0,0,0 rather than at the parent entity's position. 
See also: CreateCube, CreateSphere, CreateCylinder. 
*/


/*  CopyMesh ( mesh[,parent] )
mesh - handle of mesh to be copied 
parent (optional) - handle of entity to be made parent of mesh 

Creates a copy of a mesh and returns the newly-created mesh's handle. 
The difference between CopyMesh and CopyEntity is that CopyMesh performs a 'deep' copy of a mesh. 
CopyMesh is identical to performing new_mesh=CreateMesh() : AddMesh mesh,new_mesh 
*/
DLL_EXPORT int * BBCALL iCopyMesh( IMesh* mesh )
{
	IMesh* newMesh = smgr->getMeshManipulator()->createMeshCopy( mesh );
	return (int *)newMesh;
}


/*  AddMesh ( source_mesh,dest_mesh )
source_mesh - source mesh handle 
dest_mesh - destination mesh handle 

Adds the source mesh to the destination mesh. 
AddMesh works best with meshes that have previously only had mesh commands used with them. 
So if you want to manipulate a mesh before adding it to another mesh, make sure you use ScaleMesh, PositionMesh, PaintMesh etc rather than ScaleEntity, PositionEntity, EntityTexture etc before using AddMesh. 
However, something to be aware of when using commands such as RotateMesh is that all mesh commands work from a global origin of 0,0,0. Therefore it is generally a good idea to scale and rotate a mesh before positioning it, otherwise your mesh could end up in unexpected positions. Also, when using AddMesh, the origin of the new all-in-one mesh will be set at 0,0,0. 
After using AddMesh, the original source_mesh will still exist, therefore use FreeEntity to delete it if you wish to do so. 
*/


/*  FlipMesh ( mesh )
mesh - mesh handle 

Flips all the triangles in a mesh. 
This is useful for a couple of reasons. Firstly though, it is important to understand a little bit of the theory behind 3D graphics. A 3D triangle is represented by three points; only when these points are presented to the viewer in a clockwise-fashion is the triangle visible. So really, triangles only have one side. 
Normally, for example in the case of a sphere, a model's triangles face the inside of the model, so it doesn't matter that you can't see them. However, what about if you wanted to use the sphere as a huge sky for your world, i.e. so you only needed to see the inside? In this case you would just use FlipMesh. 
Another use for FlipMesh is to make objects two-sided, so you can see them from the inside and outside if you can't already. In this case, you can copy the original mesh using CopyEntity, specifying the original mesh as the parent, and flip it using FlipMesh. You will now have two meshes occupying the same space - this will make it double-sided, but beware, it will also double the polygon count! 
The above technique is worth trying when an external modelling program has exported a model in such a way that some of the triangles appear to be missing. 
*/
DLL_EXPORT int BBCALL iFlipMesh( IMesh* mesh )		//  ED MUSOLINO
{
    // flip a mesh
	if(!mesh)
		return 0;
	smgr->getMeshManipulator()->flipSurfaces(mesh);
	return 1;
}
/*
DLL_EXPORT void BBCALL iFlipNodeMesh( IMeshSceneNode* node )		//  ED MUSOLINO
{
    // flip a nodes mesh
	IMesh* mesh = node->getMesh();
	smgr->getMeshManipulator()->flipSurfaces(mesh);
}*/


DLL_EXPORT int BBCALL iFlipNodeMesh( scene::ISceneNode* node )		//  ED MUSOLINO 
{
	if(!node)
		return 0;

	if (node->getType() == scene::ESNT_MESH || node->getType() == scene::ESNT_WATER_SURFACE)
	{
		scene::IMeshSceneNode* msn = (scene::IMeshSceneNode*)node;
		
		//IMesh* msh = msn->getMesh();
	    if (msn->getMesh())
	    {
			smgr->getMeshManipulator()->flipSurfaces(msn->getMesh());
			return 1;
		}
	}
	else if (node->getType() == scene::ESNT_ANIMATED_MESH)
	  {
	    IAnimatedMeshSceneNode* msn = (IAnimatedMeshSceneNode*)node;

		scene::IAnimatedMesh* am = msn->getMesh();
	    if (am)
		{
			smgr->getMeshManipulator()->flipSurfaces(am);
			return 1;
		}
	}

	return 0;


}

/*  PaintMesh ( mesh,brush )
mesh - mesh handle 
brush - brush handle 

Paints a mesh with a brush. 
This has the effect of instantly altering the visible appearance of the mesh, assuming the brush's properties are different to what was was applied to the surface before. 
The reason for using PaintMesh to apply specific properties to a mesh using a brush rather than just using EntityTexture, EntityColor, EntityShininess etc, is that you can pre-define one brush, and then paint meshes over and over again using just the one command rather than lots of separate ones. 
See also: PaintEntity, PaintSurface. 
*/


/*  LightMesh ( mesh,red#,green#,blue#[,range#][,light_x#][,light_y#][,light_z#] )
mesh - mesh handle 
red# - mesh red value 
green# - mesh green value 
blue# - mesh blue value 
range# (optional) - light range 
light_x# (optional) - light x position 
light_y# (optional) - light y position 
light_z# (optional) - light z position  

Performs a 'fake' lighting operation on a mesh. 
You need to use EntityFX ent,2 to enable vertex colors on the target mesh before you can see any results. 
Since V1.83 the default vertex color for primitives is 255,255,255 and not 0,0,0, so for LightMesh to effect primitives as it did before, you will first need to reset the vertex colors to 0,0,0. You can do this using LightMesh mesh,-255,-255,-255. 
*/


/*  FitMesh ( mesh,x#,y#,z#,width#,height#,depth#[,uniform] )
mesh - mesh handle 
x# - x position of mesh 
y# - y position of mesh 
z# - z position of mesh 
width# - width of mesh 
height# - height of mesh 
depth# - depth of mesh 
uniform (optional) - if true, the mesh will be scaled by the same amounts in x, y and z, so will not be distorted. Defaults to false.  

Scales and translates all vertices of a mesh so that the mesh occupies the specified box. 
Do not use a width#, height# or depth# value of 0, otherwise all mesh data will be destroyed and your mesh will not be displayed. Use a value of 0.001 instead for a flat mesh along one axis. 
See also: ScaleMesh, ScaleEntity. 
*/


/*  ScaleMesh ( mesh,x_scale#,y_scale#,z_scale# )
mesh - mesh handle 
x_scale# - x scale of mesh 
y_scale# - y scale of mesh 
z_scale# - z scale of mesh 

Scales all vertices of a mesh by the specified scaling factors. 
See also: FitMesh, ScaleEntity. 
*/
DLL_EXPORT void BBCALL iScaleMesh( IAnimatedMesh* mesh,
								 float scale,
								 int iFrame,
								 int iMeshBuffer,
								 IAnimatedMesh* sourceMesh )
{
    int iLoop;
    S3DVertex *vertexIn, *vertexOut;
    IMeshBuffer *mbIn, *mbOut;
    aabbox3df BoundingBox(0.0,0.0,0.0,0.0,0.0,0.0);

	// if a difference source mesh was supplied
	if ( sourceMesh )
	{
		// use that as a template
		mbIn = sourceMesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer);
	}
	else
	{
		// otherwise just scale the destination mesh into itself
		mbIn = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer);
	}
	mbOut = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer);

	// get the verticies
    switch ( mbOut->getVertexType())
    {
        // Standard vertex type used by the Irrlicht engine: video::S3DVertex.
        case EVT_STANDARD:
            vertexIn = (S3DVertex*)mbIn->getVertices();
            vertexOut = (S3DVertex*)mbOut->getVertices();
            break;

        // Vertex with two texture coordinates, video::S3DVertex2TCoords.
        // Usually used for geometry with lightmaps or other special materials.
        case EVT_2TCOORDS:
            vertexIn = (S3DVertex2TCoords*)mbIn->getVertices();
            vertexOut = (S3DVertex2TCoords*)mbOut->getVertices();
            break;

        // Usually used for tangent space normal mapping.
        // Vertex with a tangent and binormal vector, video::S3DVertexTangents.
        case EVT_TANGENTS:
            vertexIn = (S3DVertexTangents*)mbIn->getVertices();
            vertexOut = (S3DVertexTangents*)mbOut->getVertices();
            break;
    }

	// get the number of verticies in this mesh buffer
	int iVertexCount = mbIn->getVertexCount();

	// itterate the verticies
    for ( iLoop = 0; iLoop < iVertexCount; iLoop++ )
    {
		// copy and scale the data
        vertexOut[iLoop].Pos.X = vertexIn[iLoop].Pos.X * scale;
        vertexOut[iLoop].Pos.Y = vertexIn[iLoop].Pos.Y * scale;
        vertexOut[iLoop].Pos.Z = vertexIn[iLoop].Pos.Z * scale;

        BoundingBox.addInternalPoint(vertexOut[iLoop].Pos);
	}

	// set the bounding box of the mesh buffer
    mbOut->setBoundingBox( BoundingBox );

	// add in the bounding boxes of other mesh buffers
	IMeshBuffer *mbuff;
	for ( iLoop = 0; iLoop < (int)mesh->getMeshBufferCount(); iLoop++ )
	{
		// dont add in our own box
		if ( iLoop != iMeshBuffer )
		{
			mbuff = mesh->getMeshBuffer( iLoop );
			BoundingBox.addInternalBox( mbuff->getBoundingBox());

		}
	}

    // now set the bounding box of the mesh
    mesh->setBoundingBox( BoundingBox );
    mesh->getMesh(iFrame)->setBoundingBox( BoundingBox );
}


/*  RotateMesh ( mesh,pitch#,yaw#,roll# )
mesh - mesh handle 
pitch# - pitch of mesh 
yaw# - yaw of mesh 
roll# - roll of mesh 

Rotates all vertices of a mesh by the specified rotation. 
See also: RotateEntity, TurnEntity. 
*/


/*  PositionMesh mesh,x#,y#,z#
mesh - mesh handle 
x# - x position of mesh 
y# - y position of mesh 
z# - z position of mesh  

Moves all vertices of a mesh. 
See also: PositionEntity, MoveEntity, TranslateEntity. 
*/


/*  UpdateNormals ( mesh )
mesh - mesh handle 

Recalculates all normals in a mesh. This is necessary for correct lighting if you have not set surface normals using 'VertexNormals' commands. 
*/


/*  MeshesIntersect ( mesh_a,mesh_b )
mesh_a - mesh_a handle 
mesh_b - mesh_b handle 

Returns true if the specified meshes are currently intersecting. 
This is a fairly slow routine - use with discretion... 
This command is currently the only polygon->polygon collision checking routine available in Blitz3D. 
*/


/*  MeshWidth# ( mesh )
mesh - mesh handle 

Returns the width of a mesh. This is calculated by the actual vertex positions and so the scale of the entity (set by ScaleEntity) will not have an effect on the resultant width. Mesh operations, on the other hand, will effect the result. 
See also: MeshHeight, MeshDepth. 
*/


/*  MeshHeight# ( mesh )
mesh - mesh handle 

Returns the height of a mesh. This is calculated by the actual vertex positions and so the scale of the entity (set by ScaleEntity) will not have an effect on the resultant height. Mesh operations, on the other hand, will effect the result. 
See also: MeshWidth, MeshDepth. 
*/


/*  MeshDepth# ( mesh )
mesh - mesh handle 

Returns the depth of a mesh. This is calculated by the actual vertex positions and so the scale of the entity (set by ScaleEntity) will not have an effect on the resultant depth. Mesh operations, on the other hand, will effect the result. 
See also: MeshWidth, MeshHeight. 
*/


/*  CountSurfaces% ( mesh )
mesh - mesh handle 

Returns the number of surfaces in a mesh. 
Surfaces are sections of mesh. A mesh may contain only one section, or very many. 
See also: GetSurface. 
*/


/*  GetSurface% ( mesh, index )
mesh - mesh handle 
index - index of surface 

Returns the handle of the surface attached to the specified mesh and with the specified index number. 
Index should be in the range 1...CountSurfaces( mesh ), inclusive. 
You need to 'get a surface', i.e. get its handle, in order to be able to then use that particular surface with other commands. 
See also: CountSurfaces, FindSurface. 
*/




/* ----------------------------------------------------------------------------
add a hill plane mesh to the mesh pool
*/
DLL_EXPORT int * BBCALL iCreateHillPlaneMesh( const c8* meshname, float tileSizeX, float tileSizeY, int tileCountX, int tileCountY, SMaterial * material, float hillHeight, float countHillsX, float countHillsY, float textureRepeatCountX, float textureRepeatCountY )
{
    scene::IAnimatedMesh* mesh = smgr->addHillPlaneMesh(meshname,
        core::dimension2d<f32>(tileSizeX, tileSizeY),
        core::dimension2d<u32>(tileCountX, tileCountY),
        material,
        hillHeight,
        core::dimension2d<f32>(countHillsX, countHillsY),
        core::dimension2d<f32>(textureRepeatCountX, textureRepeatCountY)
        );

//	IMeshSceneNode* node = smgr->addMeshSceneNode( mesh );//addAnimatedMeshSceneNode( mesh );
//	if(parent)
//		node->setParent( parent );
//	return (int *)node;

    return (int *) mesh;
}

DLL_EXPORT int * BBCALL iAddMeshToScene ( IAnimatedMesh* mesh )
{
	IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
	return (int *)node;
}

DLL_EXPORT int * BBCALL iStaticMeshForNormalMapping ( IAnimatedMesh* mesh )
{
	IMesh* tangentMesh = smgr->getMeshManipulator()->createMeshWithTangents( mesh->getMesh(0));

	ISceneNode *scenenode = (ISceneNode *)smgr->addMeshSceneNode(tangentMesh);

	// drop mesh because we created it
	tangentMesh->drop();

	return (int *)scenenode;
}

/* ----------------------------------------------------------------------------
set a mesh to be a vertex buffer object offloaded to the graphics card. VBOs
are only supported for meshes of 500 verticies and over
*/
DLL_EXPORT void BBCALL iHWAcceleratedMesh( IAnimatedMesh* mesh, int iFrame )
{
	for (int iMeshBuffer=0; iMeshBuffer < (int)mesh->getMesh(iFrame)->getMeshBufferCount(); iMeshBuffer++)
	{
        mesh->getMeshBuffer(iMeshBuffer)->setHardwareMappingHint(scene::EHM_STATIC );
	}
    mesh->setDirty();
}

DLL_EXPORT void BBCALL iHWAcceleratedMeshNode( IAnimatedMeshSceneNode* node, int iFrame )
{
	IAnimatedMesh* mesh = node->getMesh();
	for (int iMeshBuffer=0; iMeshBuffer < (int)mesh->getMesh(iFrame)->getMeshBufferCount(); iMeshBuffer++)
	{
        mesh->getMeshBuffer(iMeshBuffer)->setHardwareMappingHint(scene::EHM_STATIC );
	}
    mesh->setDirty();
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/* ----------------------------------------------------------------------------
create a batching mesh that will be a collection of other meshes
*/
DLL_EXPORT int * BBCALL iCreateBatchingMesh( void )	//CBatchingMesh *
{
	CBatchingMesh* meshBatch = new CBatchingMesh();

	return (int *)meshBatch;
}

/* ----------------------------------------------------------------------------
add a mesh to the batch of meshes
*/
DLL_EXPORT void BBCALL iAddToBatchingMesh(	CBatchingMesh * meshBatch,
							IAnimatedMesh * mesh,
							float posX, float posY, float posZ,
							float rotX, float rotY, float rotZ,
							float scaX, float scaY, float scaZ )
{
	meshBatch->addMesh(	mesh,
						core::vector3df( posX,posY,posZ ),
						core::vector3df( rotX,rotY,rotZ ),
						core::vector3df( scaX,scaY,scaZ ));
}

/* ----------------------------------------------------------------------------
finish adding meshes to the batch mesh and create an animated mesh from it
*/
DLL_EXPORT int * BBCALL iFinalizeBatchingMesh( CBatchingMesh * meshBatch )	//IAnimatedMesh *
{
	meshBatch->finalize();
	IMeshManipulator * mm = smgr->getMeshManipulator();
	IAnimatedMesh * resultMesh = mm->createAnimatedMesh( meshBatch );

	return (int *)resultMesh;
}

/* ----------------------------------------------------------------------------
apply a material to a scene node
*/
DLL_EXPORT void BBCALL iMeshTexture ( IAnimatedMesh* mesh, ITexture *texture, int iIndex, int iBuffer )
{
    // apply a texture to the model
    mesh->getMeshBuffer( iBuffer )->getMaterial().setTexture( iIndex, texture );
}


/* ----------------------------------------------------------------------------
get the number of frames in the mesh
*/
DLL_EXPORT u32 BBCALL iMeshFrameCount( IAnimatedMesh* mesh )
{
	return mesh->getFrameCount();
}


/* ----------------------------------------------------------------------------
get the number of mesh buffers in the mesh
*/
DLL_EXPORT u32 BBCALL iMeshBufferCount( IAnimatedMesh* mesh, int iFrame )
{
	return mesh->getMesh(iFrame)->getMeshBufferCount();
}


/* ----------------------------------------------------------------------------
get the number of indicies in the mesh buffer
*/
DLL_EXPORT int BBCALL iMeshIndexCount( IAnimatedMesh* mesh, int iFrame, int iMeshBuffer )
{
    int iIndexCount;

    iIndexCount = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer)->getIndexCount();

	return iIndexCount;
}

/* ----------------------------------------------------------------------------
copy the indicies of a mesh into the supplied buffer, the caller must ensure
that the buffer is big enough to store the data
*/
DLL_EXPORT void BBCALL iCopyMeshIndicesTo ( IAnimatedMesh* mesh,
                                    int iFrame,
                                    unsigned short *indicies,
                                    int iMeshBuffer)
{
    int iLoop;
    int iIndexCount = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer)->getIndexCount();

    unsigned short *us = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer)->getIndices();

    for ( iLoop = 0; iLoop < iIndexCount; iLoop++ )
    {
        indicies[iLoop] = us[iLoop];
    }
}

/* ----------------------------------------------------------------------------
copy the indicies in the supplied buffer into the mesh , the caller must ensure
that the buffer is big enough to supply all of the data
*/
DLL_EXPORT void BBCALL iCopyMeshIndicesFrom ( IAnimatedMesh* mesh,
                                    int iFrame,
                                    unsigned short *indicies,
                                    int iMeshBuffer )
{
    int iLoop;
    int iIndexCount = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer)->getIndexCount();
    unsigned short *us = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer)->getIndices();

    for ( iLoop = 0; iLoop < iIndexCount; iLoop++ )
    {
        us[iLoop] = indicies[iLoop];
    }
}

/* ----------------------------------------------------------------------------
get the number of vertices in the mesh buffer
*/
DLL_EXPORT int BBCALL iMeshVertexCount (  IAnimatedMesh* mesh,
                                        int iFrame,
                                        int iMeshBuffer )
{
    int iVertexCount;

    iVertexCount = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer)->getVertexCount();

	return iVertexCount;
}

/* ----------------------------------------------------------------------------
copy the vertices of a mesh into the supplied buffer, the caller must ensure
that the buffer is big enough to store the data
*/
DLL_EXPORT void BBCALL iCopyMeshVerticesTo( IAnimatedMesh* mesh,
                                    int iFrame,
                                    IRR_VERT *verts,
                                    int iMeshBuffer )
{
    int iLoop;
    S3DVertex *s3d_verts;
    S3DVertex2TCoords *texture_verts;
    S3DVertexTangents *tangent_verts;

    IMeshBuffer *mb = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer);
    int iVertexCount = mb->getVertexCount();

    switch ( mb->getVertexType())
    {
    case EVT_STANDARD:
        // Standard vertex type used by the Irrlicht engine, video::S3DVertex
        s3d_verts = (S3DVertex *)mb->getVertices();
        for ( iLoop = 0; iLoop < iVertexCount; iLoop++ )
        {
            verts[iLoop].x = s3d_verts[iLoop].Pos.X;
            verts[iLoop].y = s3d_verts[iLoop].Pos.Y;
            verts[iLoop].z = s3d_verts[iLoop].Pos.Z;

            verts[iLoop].normal_x = s3d_verts[iLoop].Normal.X;
            verts[iLoop].normal_y = s3d_verts[iLoop].Normal.Y;
            verts[iLoop].normal_z = s3d_verts[iLoop].Normal.Z;

            verts[iLoop].vcolor = s3d_verts[iLoop].Color.color;
            verts[iLoop].texture_x = s3d_verts[iLoop].TCoords.X;
            verts[iLoop].texture_y = s3d_verts[iLoop].TCoords.Y;
        }
        break;

    case EVT_2TCOORDS:
        /* Vertex with two texture coordinates, video::S3DVertex2TCoords.
        Usually used for geometry with lightmaps or other special materials. */
        texture_verts = (S3DVertex2TCoords *)mb->getVertices();
        for ( iLoop = 0; iLoop < iVertexCount; iLoop++ )
        {
            verts[iLoop].x = texture_verts[iLoop].Pos.X;
            verts[iLoop].y = texture_verts[iLoop].Pos.Y;
            verts[iLoop].z = texture_verts[iLoop].Pos.Z;

            verts[iLoop].normal_x = texture_verts[iLoop].Normal.X;
            verts[iLoop].normal_y = texture_verts[iLoop].Normal.Y;
            verts[iLoop].normal_z = texture_verts[iLoop].Normal.Z;

            verts[iLoop].vcolor = texture_verts[iLoop].Color.color;
            verts[iLoop].texture_x = texture_verts[iLoop].TCoords.X;
            verts[iLoop].texture_y = texture_verts[iLoop].TCoords.Y;
        }
        break;

    case EVT_TANGENTS:
        /* Vertex with a tangent and binormal vector, video::S3DVertexTangents.
        Usually used for tangent space normal mapping. */
        tangent_verts = (S3DVertexTangents *)mb->getVertices();
        for ( iLoop = 0; iLoop < iVertexCount; iLoop++ )
        {
            verts[iLoop].x = tangent_verts[iLoop].Pos.X;
            verts[iLoop].y = tangent_verts[iLoop].Pos.Y;
            verts[iLoop].z = tangent_verts[iLoop].Pos.Z;

            verts[iLoop].normal_x = tangent_verts[iLoop].Normal.X;
            verts[iLoop].normal_y = tangent_verts[iLoop].Normal.Y;
            verts[iLoop].normal_z = tangent_verts[iLoop].Normal.Z;

            verts[iLoop].vcolor = tangent_verts[iLoop].Color.color;
            verts[iLoop].texture_x = tangent_verts[iLoop].TCoords.X;
            verts[iLoop].texture_y = tangent_verts[iLoop].TCoords.Y;
        }
        break;
    }
}

/* ----------------------------------------------------------------------------
return a pointer to the vertex memory for the supplied mesh operations can be
carried out very quickly on vertices through this function but object sizes and
array access needs to be handled by the caller
*/
DLL_EXPORT int * BBCALL iMeshVertexMemory( IAnimatedMesh* mesh,
										  int iFrame,
										  int iMeshBuffer )
{
    IMeshBuffer *mb = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer);
	return (int *)mb->getVertices();
}

/* ----------------------------------------------------------------------------
copy the vertices in the supplied buffer into the mesh , the caller must ensure
that the buffer is big enough to supply all of the data
*/
DLL_EXPORT void BBCALL iCopyMeshVerticesFrom( IAnimatedMesh* mesh,
                                    int iFrame,
                                    IRR_VERT *verts,
                                    int iMeshBuffer )
{
    int iLoop;
    S3DVertex *s3d_verts;
    S3DVertex2TCoords *texture_verts;
    S3DVertexTangents *tangent_verts;

	IMesh * imesh = mesh->getMesh(iFrame);
    IMeshBuffer *mb = imesh->getMeshBuffer(iMeshBuffer);
    int iVertexCount = mb->getVertexCount();
    aabbox3df BoundingBox(0.0,0.0,0.0,0.0,0.0,0.0);

    switch ( mb->getVertexType())
    {
    case EVT_STANDARD:
        // Standard vertex type used by the Irrlicht engine, video::S3DVertex
        s3d_verts = (S3DVertex *)mb->getVertices();

        for ( iLoop = 0; iLoop < iVertexCount; iLoop++ )
        {
            s3d_verts[iLoop].Pos.X = verts[iLoop].x;
            s3d_verts[iLoop].Pos.Y = verts[iLoop].y;
            s3d_verts[iLoop].Pos.Z = verts[iLoop].z;

            s3d_verts[iLoop].Normal.X = verts[iLoop].normal_x;
            s3d_verts[iLoop].Normal.Y = verts[iLoop].normal_y;
            s3d_verts[iLoop].Normal.Z = verts[iLoop].normal_z;

            s3d_verts[iLoop].Color.color = verts[iLoop].vcolor;
            s3d_verts[iLoop].TCoords.X = verts[iLoop].texture_x;
            s3d_verts[iLoop].TCoords.Y = verts[iLoop].texture_y;

            BoundingBox.addInternalPoint(s3d_verts[iLoop].Pos);
        }
        break;

    case EVT_2TCOORDS:
        /* Vertex with two texture coordinates, video::S3DVertex2TCoords.
        Usually used for geometry with lightmaps or other special materials. */
        texture_verts = (S3DVertex2TCoords *)mb->getVertices();

        for ( iLoop = 0; iLoop < iVertexCount; iLoop++ )
        {
            texture_verts[iLoop].Pos.X = verts[iLoop].x;
            texture_verts[iLoop].Pos.Y = verts[iLoop].y;
            texture_verts[iLoop].Pos.Z = verts[iLoop].z;

            texture_verts[iLoop].Normal.X = verts[iLoop].normal_x;
            texture_verts[iLoop].Normal.Y = verts[iLoop].normal_y;
            texture_verts[iLoop].Normal.Z = verts[iLoop].normal_z;

            texture_verts[iLoop].Color.color = verts[iLoop].vcolor;
            texture_verts[iLoop].TCoords.X = verts[iLoop].texture_x;
            texture_verts[iLoop].TCoords.Y = verts[iLoop].texture_y;

            BoundingBox.addInternalPoint(texture_verts[iLoop].Pos);
        }
        break;

    case EVT_TANGENTS:
        /* Vertex with a tangent and binormal vector, video::S3DVertexTangents.
        Usually used for tangent space normal mapping. */
        tangent_verts = (S3DVertexTangents *)mb->getVertices();

        for ( iLoop = 0; iLoop < iVertexCount; iLoop++ )
        {
            tangent_verts[iLoop].Pos.X = verts[iLoop].x;
            tangent_verts[iLoop].Pos.Y = verts[iLoop].y;
            tangent_verts[iLoop].Pos.Z = verts[iLoop].z;

            tangent_verts[iLoop].Normal.X = verts[iLoop].normal_x;
            tangent_verts[iLoop].Normal.Y = verts[iLoop].normal_y;
            tangent_verts[iLoop].Normal.Z = verts[iLoop].normal_z;

            tangent_verts[iLoop].Color.color = verts[iLoop].vcolor;
            tangent_verts[iLoop].TCoords.X = verts[iLoop].texture_x;
            tangent_verts[iLoop].TCoords.Y = verts[iLoop].texture_y;

            BoundingBox.addInternalPoint(tangent_verts[iLoop].Pos);
        }
        break;
    }

	// set the bounding box of the mesh buffer
    mb->setBoundingBox( BoundingBox );

	// add in the bounding boxes of other mesh buffers
	IMeshBuffer *mbuff;
	for ( iLoop = 0; iLoop < (int)imesh->getMeshBufferCount(); iLoop++ )
	{
		// dont add in our own box
		if ( iLoop != iMeshBuffer )
		{
			mbuff = imesh->getMeshBuffer( iLoop );
			BoundingBox.addInternalBox( mbuff->getBoundingBox());
		}
	}

    // now set the bounding box of the mesh
    imesh->setBoundingBox( BoundingBox );
}

/* ----------------------------------------------------------------------------
Change the color of the vertices in the mesh
*/
DLL_EXPORT void BBCALL iMeshVertexColors( IAnimatedMesh* mesh,
                                        unsigned int iFrame,
                                        unsigned int *vertexColor,
                                        unsigned int groupCount,
                                        unsigned int *startPos,
                                        unsigned int *endPos,
                                        unsigned int iMeshBuffer )
{
    unsigned int i;
    unsigned int j;
    S3DVertex *vertex;
    IMeshBuffer *mb = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer);

    switch ( mb->getVertexType())
    {
        // Standard vertex type used by the Irrlicht engine: video::S3DVertex.
        case EVT_STANDARD:
            vertex = (S3DVertex*)mb->getVertices();
            break;

        // Vertex with two texture coordinates, video::S3DVertex2TCoords.
        // Usually used for geometry with lightmaps or other special materials.
        case EVT_2TCOORDS:
            vertex = (S3DVertex2TCoords*)mb->getVertices();
            break;

        // Usually used for tangent space normal mapping.
        // Vertex with a tangent and binormal vector, video::S3DVertexTangents.
        case EVT_TANGENTS:
            vertex = (S3DVertexTangents*)mb->getVertices();
            break;
    }

    if (startPos != 0)
    {
        for ( i = 0; i < groupCount; i++ )
        {
            for ( j = startPos[i]; j <= endPos[i]; j++ )
            {
                vertex[j].Color.color = vertexColor[j];
            }
        }
    }
    else
    {
        if (groupCount == 0)
        {
            groupCount = mb->getVertexCount();
        }

        for ( i = 0; i < groupCount; i++ )
        {
            vertex[i].Color.color = vertexColor[i];
        }
    }
}

/* ----------------------------------------------------------------------------
Change the vertex coordinates in the mesh.
*/
DLL_EXPORT void BBCALL iMeshVertexCoords( IAnimatedMesh* mesh,
                                        unsigned int iFrame,
                                        IRR_VECTOR *vertexCoord,
                                        unsigned int groupCount,
                                        unsigned int *startPos,
                                        unsigned int *endPos,
                                        unsigned int iMeshBuffer )
{
    unsigned int i;
    unsigned int j;
    S3DVertex *vertex;
    IMeshBuffer *mb = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer);
    aabbox3df BoundingBox(0.0,0.0,0.0,0.0,0.0,0.0);
	IMesh * imesh = mesh->getMesh(iFrame);

    switch ( mb->getVertexType())
    {
        // Standard vertex type used by the Irrlicht engine: video::S3DVertex.
        case EVT_STANDARD:
            vertex = (S3DVertex*)mb->getVertices();
            break;

        // Vertex with two texture coordinates, video::S3DVertex2TCoords.
        // Usually used For geometry with lightmaps or other special materials.
        case EVT_2TCOORDS:
            vertex = (S3DVertex2TCoords *)mb->getVertices();
            break;

        // Usually used for tangent space normal mapping.
        // Vertex with a tangent and binormal vector, video::S3DVertexTangents.
        case EVT_TANGENTS:
            vertex = (S3DVertexTangents*)mb->getVertices();
            break;
    }

    if ( startPos != 0)
    {
        for ( i = 0; i < groupCount; i++ )
        {
            for ( j = startPos[i]; j <= endPos[i]; j++ )
            {
                vertex[j].Pos.X = vertexCoord[j].x;
                vertex[j].Pos.Y = vertexCoord[j].y;
                vertex[j].Pos.Z = vertexCoord[j].z;
                BoundingBox.addInternalPoint(vertex[j].Pos);
            }
        }
    }
    else
    {
        if (groupCount == 0)
        {
            groupCount = mb->getVertexCount();
        }
        for ( i = 0; i < groupCount; i++ )
        {
            vertex[i].Pos.X = vertexCoord[i].x;
            vertex[i].Pos.Y = vertexCoord[i].y;
            vertex[i].Pos.Z = vertexCoord[i].z;
            BoundingBox.addInternalPoint(vertex[i].Pos);
        }
    }

	// set the bounding box of the meshbuffer
    mb->setBoundingBox( BoundingBox );

	// add in the bounding boxes of other mesh buffers
	IMeshBuffer *mbuff;
	for ( i = 0; i < (int)imesh->getMeshBufferCount(); i++ )
	{
		// dont add in our own box
		if ( i != iMeshBuffer )
		{
			mbuff = imesh->getMeshBuffer( i );
			BoundingBox.addInternalBox( mbuff->getBoundingBox());
		}
	}

    // now set the bounding box of the mesh
    mesh->getMesh(iFrame)->setBoundingBox( BoundingBox );
}

/* ----------------------------------------------------------------------------
Change the color of the vertices in the mesh with a uniform color
*/
DLL_EXPORT void BBCALL iMeshVertexSingleColor(    IAnimatedMesh* mesh,
                                                unsigned int iFrame,
                                                unsigned int vertexColor,
                                                unsigned int groupCount,
                                                unsigned int *startPos,
                                                unsigned int *endPos,
                                                unsigned int iMeshBuffer )
{
    unsigned int i;
    unsigned int j;
    S3DVertex *vertex;
    IMeshBuffer *mb = mesh->getMesh(iFrame)->getMeshBuffer(iMeshBuffer);

    switch ( mb->getVertexType())
    {
        // Standard vertex type used by the Irrlicht engine: video::S3DVertex.
        case EVT_STANDARD:
            vertex = (S3DVertex*)mb->getVertices();
            break;

        // Vertex with two texture coordinates, video::S3DVertex2TCoords.
        // Usually used for geometry with lightmaps or other special materials.
        case EVT_2TCOORDS:
            vertex = (S3DVertex2TCoords*)mb->getVertices();
            break;

        // Usually used for tangent space normal mapping.
        // Vertex with a tangent and binormal vector, video::S3DVertexTangents.
        case EVT_TANGENTS:
            vertex = (S3DVertexTangents*)mb->getVertices();
            break;
    }

    if (startPos != 0)
    {
        for ( i = 0; i < groupCount; i++ )
        {
            for ( j = startPos[i]; j <= endPos[i]; j++ )
            {
                vertex[j].Color.color = vertexColor;
            }
        }
    }
    else
    {
        if (groupCount == 0)
        {
            groupCount = mb->getVertexCount();
        }

        for ( i = 0; i < groupCount; i++ )
        {
            vertex[i].Color.color = vertexColor;
        }
    }
}


/* ----------------------------------------------------------------------------
Gets the bounding box values of a mesh.
*/
DLL_EXPORT void BBCALL iMeshBoundingBox (
                                        void *mesh,
                                        f32 &minx,
                                        f32 &miny,
                                        f32 &minz,
                                        f32 &maxx,
                                        f32 &maxy,
                                        f32 &maxz )
{
    aabbox3d<f32> curbox = ((IMesh*)mesh)->getBoundingBox();
    minx = curbox.MinEdge.X;
    miny = curbox.MinEdge.Y;
    minz = curbox.MinEdge.Z;
    maxx = curbox.MaxEdge.X;
    maxy = curbox.MaxEdge.Y;
    maxz = curbox.MaxEdge.Z;
}


}