/** 
 * @file llvotree.cpp
 * @brief LLVOTree class implementation
 *
 * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
 * $License$
 */

#include "llviewerprecompiledheaders.h"

#include "llvotree.h"

#include "llviewercontrol.h"
#include "lldir.h"
#include "llprimitive.h"
#include "lltree_common.h"
#include "llxmltree.h"
#include "material_codes.h"
#include "object_flags.h"

#include "llagent.h"
#include "llagparray.h"
#include "llcylinder.h"
#include "lldrawable.h"
#include "llface.h"
#include "llviewercamera.h"
#include "llviewerimagelist.h"
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
#include "llworld.h"
#include "noise.h"
#include "pipeline.h"
#include "llviewerwindow.h"

extern LLPipeline gPipeline;

LLGLuint mLeafDList;

S32 LLVOTree::sLODVertexOffset[4];
S32 LLVOTree::sLODVertexCount[4];
S32 LLVOTree::sLODIndexOffset[4];
S32 LLVOTree::sLODIndexCount[4];
S32 LLVOTree::sLODSlices[4] = {10, 5, 4, 3};
F32 LLVOTree::sLODAngles[4] = {30.f, 20.f, 15.f, 0.f};

F32 LLVOTree::sTreeFactor = 1.f;

LLVOTree::SpeciesMap LLVOTree::sSpeciesTable;
S32 LLVOTree::sMaxTreeSpecies = 0;

// Tree variables and functions

LLVOTree::LLVOTree(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp):
						LLViewerObject(id, pcode, regionp)
{
	mSpecies = 0;
	mFrameCount = 0;
	mWind = mRegionp->mWind.getVelocity(getPositionRegion());
}


LLVOTree::~LLVOTree()
{
	if (mData)
	{
		delete[] mData;
		mData = NULL;
	}
}

// static
void LLVOTree::initClass()
{
	std::string xml_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"trees.xml");
	
	LLXmlTree tree_def_tree;

	if (!tree_def_tree.parseFile(xml_filename))
	{
		llerrs << "Failed to parse tree file." << llendl;
	}

	LLXmlTreeNode* rootp = tree_def_tree.getRoot();

	for (LLXmlTreeNode* tree_def = rootp->getFirstChild();
		tree_def;
		tree_def = rootp->getNextChild())
		{
			if (!tree_def->hasName("tree"))
			{
				llwarns << "Invalid tree definition node " << tree_def->getName() << llendl;
				continue;
			}
			F32 F32_val;
			LLUUID id;
			S32 S32_val;

			BOOL success = TRUE;



			S32 species;
			static LLStdStringHandle species_id_string = LLXmlTree::addAttributeString("species_id");
			if (!tree_def->getFastAttributeS32(species_id_string, species))
			{
				llwarns << "No species id defined" << llendl;
				continue;
			}

			if (species < 0)
			{
				llwarns << "Invalid species id " << species << llendl;
				continue;
			}

			if (sSpeciesTable.count(species))
			{
				llwarns << "Tree species " << species << " already defined! Duplicate discarded." << llendl;
				continue;
			}

			TreeSpeciesData* newTree = new TreeSpeciesData();

			static LLStdStringHandle texture_id_string = LLXmlTree::addAttributeString("texture_id");
			success &= tree_def->getFastAttributeUUID(texture_id_string, id);
			newTree->mTextureID = id;
			
			static LLStdStringHandle droop_string = LLXmlTree::addAttributeString("droop");
			success &= tree_def->getFastAttributeF32(droop_string, F32_val);
			newTree->mDroop = F32_val;

			static LLStdStringHandle twist_string = LLXmlTree::addAttributeString("twist");
			success &= tree_def->getFastAttributeF32(twist_string, F32_val);
			newTree->mTwist = F32_val;
			
			static LLStdStringHandle branches_string = LLXmlTree::addAttributeString("branches");
			success &= tree_def->getFastAttributeF32(branches_string, F32_val);
			newTree->mBranches = F32_val;

			static LLStdStringHandle depth_string = LLXmlTree::addAttributeString("depth");
			success &= tree_def->getFastAttributeS32(depth_string, S32_val);
			newTree->mDepth = S32_val;

			static LLStdStringHandle scale_step_string = LLXmlTree::addAttributeString("scale_step");
			success &= tree_def->getFastAttributeF32(scale_step_string, F32_val);
			newTree->mScaleStep = F32_val;
			
			static LLStdStringHandle trunk_depth_string = LLXmlTree::addAttributeString("trunk_depth");
			success &= tree_def->getFastAttributeS32(trunk_depth_string, S32_val);
			newTree->mTrunkDepth = S32_val;
			
			static LLStdStringHandle branch_length_string = LLXmlTree::addAttributeString("branch_length");
			success &= tree_def->getFastAttributeF32(branch_length_string, F32_val);
			newTree->mBranchLength = F32_val;

			static LLStdStringHandle trunk_length_string = LLXmlTree::addAttributeString("trunk_length");
			success &= tree_def->getFastAttributeF32(trunk_length_string, F32_val);
			newTree->mTrunkLength = F32_val;

			static LLStdStringHandle leaf_scale_string = LLXmlTree::addAttributeString("leaf_scale");
			success &= tree_def->getFastAttributeF32(leaf_scale_string, F32_val);
			newTree->mLeafScale = F32_val;
			
			static LLStdStringHandle billboard_scale_string = LLXmlTree::addAttributeString("billboard_scale");
			success &= tree_def->getFastAttributeF32(billboard_scale_string, F32_val);
			newTree->mBillboardScale = F32_val;
			
			static LLStdStringHandle billboard_ratio_string = LLXmlTree::addAttributeString("billboard_ratio");
			success &= tree_def->getFastAttributeF32(billboard_ratio_string, F32_val);
			newTree->mBillboardRatio = F32_val;
			
			static LLStdStringHandle trunk_aspect_string = LLXmlTree::addAttributeString("trunk_aspect");
			success &= tree_def->getFastAttributeF32(trunk_aspect_string, F32_val);
			newTree->mTrunkAspect = F32_val;

			static LLStdStringHandle branch_aspect_string = LLXmlTree::addAttributeString("branch_aspect");
			success &= tree_def->getFastAttributeF32(branch_aspect_string, F32_val);
			newTree->mBranchAspect = F32_val;

			static LLStdStringHandle leaf_rotate_string = LLXmlTree::addAttributeString("leaf_rotate");
			success &= tree_def->getFastAttributeF32(leaf_rotate_string, F32_val);
			newTree->mRandomLeafRotate = F32_val;
			
			static LLStdStringHandle noise_mag_string = LLXmlTree::addAttributeString("noise_mag");
			success &= tree_def->getFastAttributeF32(noise_mag_string, F32_val);
			newTree->mNoiseMag = F32_val;

			static LLStdStringHandle noise_scale_string = LLXmlTree::addAttributeString("noise_scale");
			success &= tree_def->getFastAttributeF32(noise_scale_string, F32_val);
			newTree->mNoiseScale = F32_val;

			static LLStdStringHandle taper_string = LLXmlTree::addAttributeString("taper");
			success &= tree_def->getFastAttributeF32(taper_string, F32_val);
			newTree->mTaper = F32_val;

			static LLStdStringHandle repeat_z_string = LLXmlTree::addAttributeString("repeat_z");
			success &= tree_def->getFastAttributeF32(repeat_z_string, F32_val);
			newTree->mRepeatTrunkZ = F32_val;

			sSpeciesTable[species] = newTree;

			if (species >= sMaxTreeSpecies) sMaxTreeSpecies = species + 1;

			if (!success)
			{
				LLString name;
				static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
				tree_def->getFastAttributeString(name_string, name);
				llwarns << "Incomplete definition of tree " << name << llendl;
			}
		}
		
		BOOL have_all_trees = TRUE;
		LLString err;
		char buffer[10];

		for (S32 i=0;i<sMaxTreeSpecies;++i)
		{
			if (!sSpeciesTable.count(i))
			{
				snprintf(buffer,10," %d",i);
				err.append(buffer);
				have_all_trees = FALSE;
			}
		}

		if (!have_all_trees) 
		{
			LLStringBase<char>::format_map_t args;
			args["[SPECIES]"] = err;
			gViewerWindow->alertXml("ErrorUndefinedTrees", args );
		}
};

//static
void LLVOTree::cleanupClass()
{
	std::for_each(sSpeciesTable.begin(), sSpeciesTable.end(), DeletePairedPointer());
}

U32 LLVOTree::processUpdateMessage(LLMessageSystem *mesgsys,
										  void **user_data,
										  U32 block_num, EObjectUpdateType update_type,
										  LLDataPacker *dp)
{
	// Do base class updates...
	U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);

	if (  (getVelocity().magVecSquared() > 0.f)
		||(getAcceleration().magVecSquared() > 0.f)
		||(getAngularVelocity().magVecSquared() > 0.f))
	{
		llinfos << "ACK! Moving tree!" << llendl;
		setVelocity(LLVector3::zero);
		setAcceleration(LLVector3::zero);
		setAngularVelocity(LLVector3::zero);
	}

	if (update_type == OUT_TERSE_IMPROVED)
	{
		// Nothing else needs to be done for the terse message.
		return retval;
	}

	// 
	//  Load Instance-Specific data 
	//
	if (mData)
	{
		mSpecies = ((U8 *)mData)[0];
	}
	
	if (!sSpeciesTable.count(mSpecies))
	{
		if (sSpeciesTable.size())
		{
			SpeciesMap::const_iterator it = sSpeciesTable.begin();
			mSpecies = (*it).first;
		}
	}

	//
	//  Load Species-Specific data 
	//
	mTreeImagep = gImageList.getImage(sSpeciesTable[mSpecies]->mTextureID);
	if (mTreeImagep)
	{
		mTreeImagep->bindTexture(0);
		mTreeImagep->setClamp(TRUE, TRUE);
	}
	mBranchLength = sSpeciesTable[mSpecies]->mBranchLength;
	mTrunkLength = sSpeciesTable[mSpecies]->mTrunkLength;
	mLeafScale = sSpeciesTable[mSpecies]->mLeafScale;
	mDroop = sSpeciesTable[mSpecies]->mDroop;
	mTwist = sSpeciesTable[mSpecies]->mTwist;
	mBranches = sSpeciesTable[mSpecies]->mBranches;
	mDepth = sSpeciesTable[mSpecies]->mDepth;
	mScaleStep = sSpeciesTable[mSpecies]->mScaleStep;
	mTrunkDepth = sSpeciesTable[mSpecies]->mTrunkDepth;
	mBillboardScale = sSpeciesTable[mSpecies]->mBillboardScale;
	mBillboardRatio = sSpeciesTable[mSpecies]->mBillboardRatio;
	mTrunkAspect = sSpeciesTable[mSpecies]->mTrunkAspect;
	mBranchAspect = sSpeciesTable[mSpecies]->mBranchAspect;

	return retval;
}

BOOL LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
{
	const U16 FRAMES_PER_WIND_UPDATE = 20;				//  How many frames between wind update per tree
	const F32 TREE_WIND_SENSITIVITY = 0.005f;
	const F32 TREE_TRUNK_STIFFNESS = 0.1f;

 	if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TREE)))
	{
		return TRUE;
	}
	
	F32 mass_inv; 

	//  For all tree objects, update the trunk bending with the current wind 
	//  Walk sprite list in order away from viewer 
	if (!(mFrameCount % FRAMES_PER_WIND_UPDATE)) 
	{
		//  If needed, Get latest wind for this tree
		mWind = mRegionp->mWind.getVelocity(getPositionRegion());
	}
	mFrameCount++;

	mass_inv = 1.f/(5.f + mDepth*mBranches*0.2f);
	mTrunkVel += (mWind * mass_inv * TREE_WIND_SENSITIVITY);		//  Pull in direction of wind
	mTrunkVel -= (mTrunkBend * mass_inv * TREE_TRUNK_STIFFNESS);		//  Restoring force in direction of trunk 	
	mTrunkBend += mTrunkVel;
	mTrunkVel *= 0.99f;									//  Add damping

	if (mTrunkBend.magVec() > 1.f)
	{
		mTrunkBend.normVec();
	}

	if (mTrunkVel.magVec() > 1.f)
	{
		mTrunkVel.normVec();
	}

	return TRUE;
}


const F32 TREE_BLEND_MIN = 1.f;
const F32 TREE_BLEND_RANGE = 1.f;


void LLVOTree::render(LLAgent &agent)
{
}


void LLVOTree::setPixelAreaAndAngle(LLAgent &agent)
{
	// First calculate values as for any other object (for mAppAngle)
	LLViewerObject::setPixelAreaAndAngle(agent);

	// Re-calculate mPixelArea accurately
	
	// This should be the camera's center, as soon as we move to all region-local.
	LLVector3 relative_position = getPositionAgent() - agent.getCameraPositionAgent();
	F32 range = relative_position.magVec();				// ugh, square root

	F32 max_scale = mBillboardScale * getMaxScale();
	F32 area = max_scale * (max_scale*mBillboardRatio);

	// Compute pixels per meter at the given range
	F32 pixels_per_meter = gCamera->getViewHeightInPixels() / 
						   (tan(gCamera->getView()) * range);

	mPixelArea = (pixels_per_meter) * (pixels_per_meter) * area;
#if 0
	// mAppAngle is a bit of voodoo;
	// use the one calculated LLViewerObject::setPixelAreaAndAngle above
	// to avoid LOD miscalculations
	mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
#endif
}

void LLVOTree::updateTextures(LLAgent &agent)
{
	F32 texel_area_ratio = 1.f;
	F32 cos_angle = 1.f;
	if (mTreeImagep)
	{
		mTreeImagep->addTextureStats(mPixelArea, texel_area_ratio, cos_angle);
	}

}


LLDrawable* LLVOTree::createDrawable(LLPipeline *pipeline)
{
	pipeline->allocDrawable(this);
	mDrawable->setLit(FALSE);

	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_TREE);

	LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_TREE, mTreeImagep);

	// Just a placeholder for an actual object...
	LLFace *facep = mDrawable->addFace(poolp, mTreeImagep, TRUE);
	facep->setSize(1, 3);

	gPipeline.markMaterialed(mDrawable);

	updateRadius();

	return mDrawable;
}


// Yes, I know this is bad.  I'll clean this up soon. - djs 04/02/02
const S32 LEAF_INDICES = 24;
const S32 LEAF_VERTICES = 16;

BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
{
	U32 i, j;
	const S32 MAX_SLICES = 32;

	const F32 LEAF_LEFT = 0.52f;
	const F32 LEAF_RIGHT = 0.98f;
	const F32 LEAF_TOP = 1.0f;
	const F32 LEAF_BOTTOM = 0.52f;

	const F32 LEAF_WIDTH = 1.f;


	U32 slices = MAX_SLICES;

	S32 max_indices = LEAF_INDICES;
	S32 max_vertices = LEAF_VERTICES;
	S32 lod;

	LLFace *face = drawable->getFace(0);

	face->mCenterAgent = getPositionAgent();
	face->mCenterLocal = face->mCenterAgent;

	LLDrawPool *poolp = face->getPool();
	
	if (poolp->getVertexCount())
	{
		return TRUE;
	}

	if (!face->getDirty())
	{
		return FALSE;
	}

	poolp->setDirty();

	for (lod = 0; lod < 4; lod++)
	{
		slices = sLODSlices[lod];
		sLODVertexOffset[lod] = max_vertices;
		sLODVertexCount[lod] = slices*slices;
		sLODIndexOffset[lod] = max_indices;
		sLODIndexCount[lod] = (slices-1)*(slices-1)*6;
		max_indices += sLODIndexCount[lod];
		max_vertices += sLODVertexCount[lod];
	}

	LLStrider<LLVector3> vertices;
	LLStrider<LLVector3> normals;
	LLStrider<LLVector2> tex_coords;
	U32 *indicesp;

	face->setSize(max_vertices, max_indices);
	face->getGeometry(vertices, normals, tex_coords, indicesp);

	S32 vertex_count = 0;
	S32 index_count = 0;
	
	// First leaf
	for (i = 0; i < 4; i++)
	{
		*(normals++) =		LLVector3(0.f, 0.f, 1.f);
	}

	*(tex_coords++) =	LLVector2(LEAF_LEFT, LEAF_BOTTOM);
	*(vertices++) =		LLVector3(-0.5f*LEAF_WIDTH, 0.f, 0.f);
	vertex_count++;

	*(tex_coords++) =	LLVector2(LEAF_RIGHT, LEAF_TOP);
	*(vertices++) =		LLVector3(0.5f*LEAF_WIDTH, 0.f, 1.f);
	vertex_count++;

    *(tex_coords++) =	LLVector2(LEAF_LEFT, LEAF_TOP);
	*(vertices++) =		LLVector3(-0.5f*LEAF_WIDTH, 0.f, 1.f);
	vertex_count++;

	*(tex_coords++) =	LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
	*(vertices++) =		LLVector3(0.5f*LEAF_WIDTH, 0.f, 0.f);
	vertex_count++;


	*(indicesp++) = 0;
	index_count++;
	*(indicesp++) = 1;
	index_count++;
	*(indicesp++) = 2;
	index_count++;

	*(indicesp++) = 0;
	index_count++;
	*(indicesp++) = 3;
	index_count++;
	*(indicesp++) = 1;
	index_count++;

	// Same leaf, inverse winding/normals
	for (i = 0; i < 4; i++)
	{
		*(normals++) =		LLVector3(0.f, 0.f, 1.f);
	}

	*(tex_coords++) =	LLVector2(LEAF_LEFT, LEAF_BOTTOM);
	*(vertices++) =		LLVector3(-0.5f*LEAF_WIDTH, 0.f, 0.f);
	vertex_count++;

	//*(tex_coords++) =	LLVector2(1.f, 1.0f);
	*(tex_coords++) =	LLVector2(LEAF_RIGHT, LEAF_TOP);
	*(vertices++) =		LLVector3(0.5f*LEAF_WIDTH, 0.f, 1.f);
	vertex_count++;

	//*(tex_coords++) =	LLVector2(0.52f, 1.0f);
	*(tex_coords++) =	LLVector2(LEAF_LEFT, LEAF_TOP);
	*(vertices++) =		LLVector3(-0.5f*LEAF_WIDTH, 0.f, 1.f);
	vertex_count++;

	//*(tex_coords++) =	LLVector2(1.f, 0.52f);
	*(tex_coords++) =	LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
	*(vertices++) =		LLVector3(0.5f*LEAF_WIDTH, 0.f, 0.f);
	vertex_count++;

	*(indicesp++) = 4;
	index_count++;
	*(indicesp++) = 6;
	index_count++;
	*(indicesp++) = 5;
	index_count++;

	*(indicesp++) = 4;
	index_count++;
	*(indicesp++) = 5;
	index_count++;
	*(indicesp++) = 7;
	index_count++;


	for (i = 0; i < 4; i++)
	{
		*(normals++) =		LLVector3(0.f, 0.f, 1.f);
	}

	*(tex_coords++) =	LLVector2(LEAF_LEFT, LEAF_BOTTOM);
	*(vertices++) =		LLVector3(0.f, -0.5f*LEAF_WIDTH, 0.f);
	vertex_count++;

	*(tex_coords++) =	LLVector2(LEAF_RIGHT, LEAF_TOP);
	*(vertices++) =		LLVector3(0.f, 0.5f*LEAF_WIDTH, 1.f);
	vertex_count++;

    *(tex_coords++) =	LLVector2(LEAF_LEFT, LEAF_TOP);
	*(vertices++) =		LLVector3(0.f, -0.5f*LEAF_WIDTH, 1.f);
	vertex_count++;

	*(tex_coords++) =	LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
	*(vertices++) =		LLVector3(0.f, 0.5f*LEAF_WIDTH, 0.f);
	vertex_count++;

	*(indicesp++) = 8;
	index_count++;
	*(indicesp++) = 9;
	index_count++;
	*(indicesp++) = 10;
	index_count++;

	*(indicesp++) = 8;
	index_count++;
	*(indicesp++) = 11;
	index_count++;
	*(indicesp++) = 9;
	index_count++;

	for (i = 0; i < 4; i++)
	{
		*(normals++) =		LLVector3(0.f, 0.f, 1.f);
	}

	*(tex_coords++) =	LLVector2(LEAF_LEFT, LEAF_BOTTOM);
	*(vertices++) =		LLVector3(0.f, -0.5f*LEAF_WIDTH, 0.f);
	vertex_count++;

	*(tex_coords++) =	LLVector2(LEAF_RIGHT, LEAF_TOP);
	*(vertices++) =		LLVector3(0.f, 0.5f*LEAF_WIDTH, 1.f);
	vertex_count++;

    *(tex_coords++) =	LLVector2(LEAF_LEFT, LEAF_TOP);
	*(vertices++) =		LLVector3(0.f, -0.5f*LEAF_WIDTH, 1.f);
	vertex_count++;

	*(tex_coords++) =	LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
	*(vertices++) =		LLVector3(0.f, 0.5f*LEAF_WIDTH, 0.f);
	vertex_count++;


	*(indicesp++) = 12;
	index_count++;
	*(indicesp++) = 14;
	index_count++;
	*(indicesp++) = 13;
	index_count++;

	*(indicesp++) = 12;
	index_count++;
	*(indicesp++) = 13;
	index_count++;
	*(indicesp++) = 15;
	index_count++;

	// Generate geometry for the cylinders

	// Different LOD's

	// Generate the vertices
	// Generate the indices

	for (lod = 0; lod < 4; lod++)
	{
		slices = sLODSlices[lod];
		F32 base_radius = 0.65f;
		F32 top_radius = base_radius * sSpeciesTable[mSpecies]->mTaper;
		//llinfos << "Species " << ((U32) mSpecies) << ", taper = " << sSpeciesTable[mSpecies].mTaper << llendl;
		//llinfos << "Droop " << mDroop << ", branchlength: " << mBranchLength << llendl;
		F32 angle = 0;
		F32 angle_inc = 360.f/(slices-1);
		F32 z = 0.f;
		F32 z_inc = 1.f;
		if (slices > 3)
		{
			z_inc = 1.f/(slices - 3);
		}
		F32 radius = base_radius;

		F32 x1,y1;
		F32 noise_scale = sSpeciesTable[mSpecies]->mNoiseMag;
		LLVector3 nvec;

		const F32 cap_nudge = 0.1f;			// Height to 'peak' the caps on top/bottom of branch

		const S32 fractal_depth = 5;
		F32 nvec_scale = 1.f * sSpeciesTable[mSpecies]->mNoiseScale;
		F32 nvec_scalez = 4.f * sSpeciesTable[mSpecies]->mNoiseScale;

		F32 tex_z_repeat = sSpeciesTable[mSpecies]->mRepeatTrunkZ;

		F32 start_radius;
		F32 nangle = 0;
		F32 height = 1.f;
		F32 r0;

		for (i = 0; i < slices; i++)
		{
			if (i == 0) 
			{
				z = - cap_nudge;
				r0 = 0.0;
			}
			else if (i == (slices - 1))
			{
				z = 1.f + cap_nudge;//((i - 2) * z_inc) + cap_nudge;
				r0 = 0.0;
			}
			else  
			{
				z = (i - 1) * z_inc;
				r0 = base_radius + (top_radius - base_radius)*z;
			}

			for (j = 0; j < slices; j++)
			{
				if (slices - 1 == j)
				{
					angle = 0.f;
				}
				else
				{
					angle =  j*angle_inc;
				}
			
				nangle = angle;
				
				x1 = cos(angle * DEG_TO_RAD);
				y1 = sin(angle * DEG_TO_RAD);
				LLVector2 tc;
				// This isn't totally accurate.  Should compute based on slope as well.
				start_radius = r0 * (1.f + 1.2f*fabs(z - 0.66f*height)/height);
				nvec.setVec(	cos(nangle * DEG_TO_RAD)*start_radius*nvec_scale, 
								sin(nangle * DEG_TO_RAD)*start_radius*nvec_scale, 
								z*nvec_scalez); 
				// First and last slice at 0 radius (to bring in top/bottom of structure)
				radius = start_radius + turbulence3((F32*)&nvec.mV, (F32)fractal_depth)*noise_scale;

				if (slices - 1 == j)
				{
					// Not 0.5 for slight slop factor to avoid edges on leaves
					tc = LLVector2(0.490f, (1.f - z/2.f)*tex_z_repeat);
				}
				else
				{
					tc = LLVector2((angle/360.f)*0.5f, (1.f - z/2.f)*tex_z_repeat);
				}

				*(vertices++) =		LLVector3(x1*radius, y1*radius, z);
				*(normals++) =		LLVector3(x1, y1, 0.f);
				*(tex_coords++) = tc;
				vertex_count++;
			}
		}

		for (i = 0; i < (slices - 1); i++)
		{
			for (j = 0; j < (slices - 1); j++)
			{
				S32 x1_offset = j+1;
				if ((j+1) == slices)
				{
					x1_offset = 0;
				}
				// Generate the matching quads
				*(indicesp) = j + (i*slices) + sLODVertexOffset[lod];
				llassert(*(indicesp) < (U32)max_vertices);
				indicesp++;
				index_count++;
				*(indicesp) = x1_offset + ((i+1)*slices) + sLODVertexOffset[lod];
				llassert(*(indicesp) < (U32)max_vertices);
				indicesp++;
				index_count++;
				*(indicesp) = j + ((i+1)*slices) + sLODVertexOffset[lod];
				llassert(*(indicesp) < (U32)max_vertices);
				indicesp++;
				index_count++;

				*(indicesp) = j + (i*slices) + sLODVertexOffset[lod];
				llassert(*(indicesp) < (U32)max_vertices);
				indicesp++;
				index_count++;
				*(indicesp) = x1_offset + (i*slices) + sLODVertexOffset[lod];
				llassert(*(indicesp) < (U32)max_vertices);
				indicesp++;
				index_count++;
				*(indicesp) = x1_offset + ((i+1)*slices) + sLODVertexOffset[lod];
				llassert(*(indicesp) < (U32)max_vertices);
				indicesp++;
				index_count++;
			}
		}
		slices /= 2; 
	}

	llassert(vertex_count == max_vertices);
	llassert(index_count == max_indices);

	return TRUE;
}

void LLVOTree::drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop_level, U16 depth, U16 trunk_depth,  F32 scale, F32 twist, F32 droop,  F32 branches, F32 alpha)
{
	//
	//  Draws a tree by recursing, drawing branches and then a 'leaf' texture.
	//  If stop_level = -1, simply draws the whole tree as a billboarded texture
	//

	if (!draw_pool->getIndexCount())
	{
		return; // safety net
	}
	
	static F32 constant_twist;
	static F32 width = 0;

	//F32 length = ((scale == 1.f)? mTrunkLength:mBranchLength);
	//F32 aspect = ((scale == 1.f)? mTrunkAspect:mBranchAspect);
	F32 length = ((trunk_depth || (scale == 1.f))? mTrunkLength:mBranchLength);
	F32 aspect = ((trunk_depth || (scale == 1.f))? mTrunkAspect:mBranchAspect);
	
	constant_twist = 360.f/branches;

	if (stop_level >= 0)
	{
		//
		//  Draw the tree using recursion
		//
		if (depth > stop_level)
		{
			{
				llassert(sLODIndexCount[trunk_LOD] > 0);
				width = scale * length * aspect;
				glPushMatrix();
				glScalef(width,width,scale * length);
 				//glDrawElements(GL_TRIANGLES, sLODIndexCount[trunk_LOD], GL_UNSIGNED_INT, draw_pool.getRawIndices() + sLODIndexOffset[trunk_LOD]);
				glDrawRangeElements(GL_TRIANGLES,
									sLODVertexOffset[trunk_LOD],
									sLODVertexOffset[trunk_LOD] + sLODVertexCount[trunk_LOD]-1,
									sLODIndexCount[trunk_LOD],
									GL_UNSIGNED_INT,
									draw_pool->getRawIndices() + sLODIndexOffset[trunk_LOD]);
				stop_glerror();
				draw_pool->addIndicesDrawn(sLODIndexCount[trunk_LOD]);
				glPopMatrix();
			}
			
			// Recurse to create more branches
			for (S32 i=0; i < (S32)branches; i++) 
			{
				glPushMatrix();
				glTranslatef(0.f, 0.f, scale * length);
				glRotatef((constant_twist + ((i%2==0)?twist:-twist))*i, 0.f, 0.f, 1.f);
				glRotatef(droop, 0.f, 1.f, 0.f);
				glRotatef(20.f, 0.f, 0.f, 1.f);				// rotate 20deg about axis of new branch to add some random variation
				drawBranchPipeline(draw_pool, trunk_LOD, stop_level, depth - 1, 0, scale*mScaleStep, twist, droop, branches, alpha);
				glPopMatrix();
			}
			//  Recurse to continue trunk
			if (trunk_depth)
			{
				glPushMatrix();
				glTranslatef(0.f, 0.f, scale * length);
				glRotatef(70.5f, 0.f, 0.f, 1.f);					// rotate a bit around Z when ascending 
				drawBranchPipeline(draw_pool, trunk_LOD, stop_level, depth, trunk_depth-1, scale*mScaleStep, twist, droop, branches, alpha);
				glPopMatrix();
			}
		}
		else
		{
			//
			//  Draw leaves as two 90 deg crossed quads with leaf textures
			//
			{
				glPushMatrix();
				//glRotatef(llFrand(50.0), llFrand(1.0), llFrand(1.0), llFrand(1.0);
				//width = scale * (TREE_BRANCH_ASPECT + TREE_LEAF_ASPECT);
				glScalef(scale*mLeafScale, scale*mLeafScale, scale*mLeafScale);
				//glScalef(1.5f*width*mLeafScale,1,1.5f*scale*mLeafScale);
// 				glDrawElements(GL_TRIANGLES, LEAF_INDICES, GL_UNSIGNED_INT, draw_pool.getRawIndices());
				glDrawRangeElements(GL_TRIANGLES,
									0,
									LEAF_VERTICES-1,
									LEAF_INDICES,
									GL_UNSIGNED_INT,
									draw_pool->getRawIndices());
				stop_glerror();
				draw_pool->addIndicesDrawn(LEAF_INDICES);
				glPopMatrix();
			}
		}
	}
	else
	{
		//
		//  Draw the tree as a single billboard texture 
		//

		glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		glTranslatef(0.0, -0.5, 0.0);
		glMatrixMode(GL_MODELVIEW);
		{
			glPushMatrix();
			glScalef(mBillboardScale*mBillboardRatio, mBillboardScale*mBillboardRatio, mBillboardScale);
// 			glDrawElements(GL_TRIANGLES, LEAF_INDICES, GL_UNSIGNED_INT, draw_pool.getRawIndices());
			glDrawRangeElements(GL_TRIANGLES,
								0,
								LEAF_VERTICES-1,
								LEAF_INDICES,
								GL_UNSIGNED_INT,
								draw_pool->getRawIndices());
			stop_glerror();
			draw_pool->addIndicesDrawn(LEAF_INDICES);
			glPopMatrix();
		}
		glMatrixMode(GL_TEXTURE);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
	}
}

void LLVOTree::updateRadius()
{
	if (mDrawable.isNull())
	{
		return;
	}
		
	mDrawable->setRadius(32.0f);
}
