PageRenderTime 572ms CodeModel.GetById 181ms app.highlight 310ms RepoModel.GetById 73ms app.codeStats 0ms

/indra/newview/llvograss.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 759 lines | 580 code | 139 blank | 40 comment | 65 complexity | 325c7eda420e65d0729684f09b859341 MD5 | raw file
  1/** 
  2 * @file llvograss.cpp
  3 * @brief Not a blade, but a clump of grass
  4 *
  5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28
 29#include "llvograss.h"
 30
 31#include "imageids.h"
 32#include "llviewercontrol.h"
 33
 34#include "llagentcamera.h"
 35#include "llnotificationsutil.h"
 36#include "lldrawable.h"
 37#include "llface.h"
 38#include "llsky.h"
 39#include "llsurface.h"
 40#include "llsurfacepatch.h"
 41#include "llvosky.h"
 42#include "llviewercamera.h"
 43#include "llviewertexturelist.h"
 44#include "llviewerregion.h"
 45#include "pipeline.h"
 46#include "llspatialpartition.h"
 47#include "llworld.h"
 48#include "lldir.h"
 49#include "llxmltree.h"
 50#include "llvotree.h"
 51
 52const S32 GRASS_MAX_BLADES =	32;
 53const F32 GRASS_BLADE_BASE =	0.25f;			//  Width of grass at base
 54const F32 GRASS_BLADE_TOP =		0.25f;			//  Width of grass at top
 55const F32 GRASS_BLADE_HEIGHT =	0.5f;			// meters
 56const F32 GRASS_DISTRIBUTION_SD = 0.15f;		// empirically defined
 57
 58F32 exp_x[GRASS_MAX_BLADES];
 59F32 exp_y[GRASS_MAX_BLADES];
 60F32 rot_x[GRASS_MAX_BLADES];
 61F32 rot_y[GRASS_MAX_BLADES];
 62F32 dz_x [GRASS_MAX_BLADES];
 63F32 dz_y [GRASS_MAX_BLADES];
 64
 65F32 w_mod[GRASS_MAX_BLADES];					//  Factor to modulate wind movement by to randomize appearance
 66
 67LLVOGrass::SpeciesMap LLVOGrass::sSpeciesTable;
 68S32 LLVOGrass::sMaxGrassSpecies = 0;
 69
 70
 71LLVOGrass::LLVOGrass(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
 72:	LLAlphaObject(id, pcode, regionp)
 73{
 74	mPatch               = NULL;
 75	mLastPatchUpdateTime = 0;
 76	mGrassVel.clearVec();
 77	mGrassBend.clearVec();
 78	mbCanSelect          = TRUE;
 79
 80	mBladeWindAngle      = 35.f;
 81	mBWAOverlap          = 2.f;
 82
 83	setNumTEs(1);
 84
 85	setTEColor(0, LLColor4(1.0f, 1.0f, 1.0f, 1.f));
 86	mNumBlades = GRASS_MAX_BLADES;
 87}
 88
 89LLVOGrass::~LLVOGrass()
 90{
 91}
 92
 93
 94void LLVOGrass::updateSpecies()
 95{
 96	mSpecies = mState;
 97	
 98	if (!sSpeciesTable.count(mSpecies))
 99	{
100		llinfos << "Unknown grass type, substituting grass type." << llendl;
101		SpeciesMap::const_iterator it = sSpeciesTable.begin();
102		mSpecies = (*it).first;
103	}
104	setTEImage(0, LLViewerTextureManager::getFetchedTexture(sSpeciesTable[mSpecies]->mTextureID, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE));
105}
106
107
108void LLVOGrass::initClass()
109{
110	LLVector3 pos(0.0f, 0.0f, 0.0f);
111	//  Create nifty list of exponential distribution 0-1
112	F32 x = 0.f;
113	F32 y = 0.f;
114	F32 rot;
115	
116	std::string xml_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"grass.xml");
117	
118	LLXmlTree grass_def_grass;
119
120	if (!grass_def_grass.parseFile(xml_filename))
121	{
122		llerrs << "Failed to parse grass file." << llendl;
123		return;
124	}
125
126	LLXmlTreeNode* rootp = grass_def_grass.getRoot();
127
128	for (LLXmlTreeNode* grass_def = rootp->getFirstChild();
129		grass_def;
130		grass_def = rootp->getNextChild())
131	{
132		if (!grass_def->hasName("grass"))
133		{
134			llwarns << "Invalid grass definition node " << grass_def->getName() << llendl;
135			continue;
136		}
137		F32 F32_val;
138		LLUUID id;
139
140		BOOL success = TRUE;
141
142		S32 species;
143		static LLStdStringHandle species_id_string = LLXmlTree::addAttributeString("species_id");
144		if (!grass_def->getFastAttributeS32(species_id_string, species))
145		{
146			llwarns << "No species id defined" << llendl;
147			continue;
148		}
149
150		if (species < 0)
151		{
152			llwarns << "Invalid species id " << species << llendl;
153			continue;
154		}
155
156		GrassSpeciesData* newGrass = new GrassSpeciesData();
157
158
159		static LLStdStringHandle texture_id_string = LLXmlTree::addAttributeString("texture_id");
160		grass_def->getFastAttributeUUID(texture_id_string, id);
161		newGrass->mTextureID = id;
162
163		static LLStdStringHandle blade_sizex_string = LLXmlTree::addAttributeString("blade_size_x");
164		success &= grass_def->getFastAttributeF32(blade_sizex_string, F32_val);
165		newGrass->mBladeSizeX = F32_val;
166
167		static LLStdStringHandle blade_sizey_string = LLXmlTree::addAttributeString("blade_size_y");
168		success &= grass_def->getFastAttributeF32(blade_sizey_string, F32_val);
169		newGrass->mBladeSizeY = F32_val;
170
171		if (sSpeciesTable.count(species))
172		{
173			llinfos << "Grass species " << species << " already defined! Duplicate discarded." << llendl;
174			delete newGrass;
175			continue;
176		}
177		else
178		{
179			sSpeciesTable[species] = newGrass;
180		}
181
182		if (species >= sMaxGrassSpecies) sMaxGrassSpecies = species + 1;
183
184		if (!success)
185		{
186			std::string name;
187			static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
188			grass_def->getFastAttributeString(name_string, name);
189			llwarns << "Incomplete definition of grass " << name << llendl;
190		}
191	}
192
193	BOOL have_all_grass = TRUE;
194	std::string err;
195
196	for (S32 i=0;i<sMaxGrassSpecies;++i)
197	{
198		if (!sSpeciesTable.count(i))
199		{
200			err.append(llformat(" %d",i));
201			have_all_grass = FALSE;
202		}
203	}
204
205	if (!have_all_grass) 
206	{
207		LLSD args;
208		args["SPECIES"] = err;
209		LLNotificationsUtil::add("ErrorUndefinedGrasses", args);
210	}
211
212	for (S32 i = 0; i < GRASS_MAX_BLADES; ++i)
213	{
214		if (1)   //(i%2 == 0)			Uncomment for X blading
215		{
216			F32 u = sqrt(-2.0f * log(ll_frand()));
217			F32 v = 2.0f * F_PI * ll_frand();
218			
219			x = u * sin(v) * GRASS_DISTRIBUTION_SD;
220			y = u * cos(v) * GRASS_DISTRIBUTION_SD;
221
222			rot = ll_frand(F_PI);
223		}
224		else
225		{
226			rot += (F_PI*0.4f + ll_frand(0.2f*F_PI));
227		}
228
229		exp_x[i] = x;
230		exp_y[i] = y;
231		rot_x[i] = sin(rot);
232		rot_y[i] = cos(rot);
233		dz_x[i] = ll_frand(GRASS_BLADE_BASE * 0.25f);
234		dz_y[i] = ll_frand(GRASS_BLADE_BASE * 0.25f);
235		w_mod[i] = 0.5f + ll_frand();						//  Degree to which blade is moved by wind
236
237	}
238}
239
240void LLVOGrass::cleanupClass()
241{
242	for_each(sSpeciesTable.begin(), sSpeciesTable.end(), DeletePairedPointer());
243}
244
245U32 LLVOGrass::processUpdateMessage(LLMessageSystem *mesgsys,
246										  void **user_data,
247										  U32 block_num,
248										  const EObjectUpdateType update_type,
249										  LLDataPacker *dp)
250{
251	// Do base class updates...
252	U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
253
254	updateSpecies();
255
256	if (  (getVelocity().lengthSquared() > 0.f)
257		||(getAcceleration().lengthSquared() > 0.f)
258		||(getAngularVelocity().lengthSquared() > 0.f))
259	{
260		llinfos << "ACK! Moving grass!" << llendl;
261		setVelocity(LLVector3::zero);
262		setAcceleration(LLVector3::zero);
263		setAngularVelocity(LLVector3::zero);
264	}
265
266	if (mDrawable)
267	{
268		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
269	}
270
271	return retval;
272}
273
274BOOL LLVOGrass::isActive() const
275{
276	return TRUE;
277}
278
279BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
280{
281 	if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GRASS)))
282	{
283		return TRUE;
284	}
285	
286	if (!mDrawable)
287	{
288		// So drones work.
289		return TRUE;
290	}
291
292	if(LLVOTree::isTreeRenderingStopped()) //stop rendering grass
293	{
294		if(mNumBlades)
295		{
296			mNumBlades = 0 ;
297			gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
298		}
299		return TRUE ;
300	}
301	else if(!mNumBlades)//restart grass rendering
302	{
303		mNumBlades = GRASS_MAX_BLADES ;
304		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
305		
306		return TRUE ;
307	}
308
309	if (mPatch && (mLastPatchUpdateTime != mPatch->getLastUpdateTime()))
310	{
311		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
312	}
313
314	return TRUE;
315}
316
317
318void LLVOGrass::setPixelAreaAndAngle(LLAgent &agent)
319{
320	// This should be the camera's center, as soon as we move to all region-local.
321	LLVector3 relative_position = getPositionAgent() - gAgentCamera.getCameraPositionAgent();
322	F32 range = relative_position.length();
323
324	F32 max_scale = getMaxScale();
325
326	mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
327
328	// Compute pixels per meter at the given range
329	F32 pixels_per_meter = LLViewerCamera::getInstance()->getViewHeightInPixels() / (tan(LLViewerCamera::getInstance()->getView()) * range);
330
331	// Assume grass texture is a 5 meter by 5 meter sprite at the grass object's center
332	mPixelArea = (pixels_per_meter) * (pixels_per_meter) * 25.f;
333}
334
335
336// BUG could speed this up by caching the relative_position and range calculations
337void LLVOGrass::updateTextures()
338{
339	if (getTEImage(0))
340	{
341		if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
342		{
343			setDebugText(llformat("%4.0f", (F32) sqrt(mPixelArea)));
344		}
345		getTEImage(0)->addTextureStats(mPixelArea);
346	}
347}
348
349BOOL LLVOGrass::updateLOD()
350{
351	if (mDrawable->getNumFaces() <= 0)
352	{
353		return FALSE;
354	}
355	if(LLVOTree::isTreeRenderingStopped())
356	{
357		if(mNumBlades)
358		{
359			mNumBlades = 0 ;
360			gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
361		}
362		return TRUE ;
363	}
364	if(!mNumBlades)
365	{
366		mNumBlades = GRASS_MAX_BLADES;
367	}
368
369	LLFace* face = mDrawable->getFace(0);
370
371	F32 tan_angle = 0.f;
372	S32 num_blades = 0;
373
374	tan_angle = (mScale.mV[0]*mScale.mV[1])/mDrawable->mDistanceWRTCamera;
375	num_blades = llmin(GRASS_MAX_BLADES, lltrunc(tan_angle * 5));
376	num_blades = llmax(1, num_blades);
377	if (num_blades >= (mNumBlades << 1))
378	{
379		while (mNumBlades < num_blades)
380		{
381			mNumBlades <<= 1;
382		}
383
384		face->setSize(mNumBlades*8, mNumBlades*12);
385		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
386	}
387	else if (num_blades <= (mNumBlades >> 1))
388	{
389		while (mNumBlades > num_blades)
390		{
391			mNumBlades >>=1;
392		}
393
394		face->setSize(mNumBlades*8, mNumBlades*12);
395		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
396		return TRUE;
397	}
398
399	return FALSE;
400}
401
402LLDrawable* LLVOGrass::createDrawable(LLPipeline *pipeline)
403{
404	pipeline->allocDrawable(this);
405	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_GRASS);
406	
407	return mDrawable;
408}
409
410static LLFastTimer::DeclareTimer FTM_UPDATE_GRASS("Update Grass");
411
412BOOL LLVOGrass::updateGeometry(LLDrawable *drawable)
413{
414	LLFastTimer ftm(FTM_UPDATE_GRASS);
415
416	dirtySpatialGroup();
417
418	if(!mNumBlades)//stop rendering grass
419	{
420		if (mDrawable->getNumFaces() > 0)
421		{
422			LLFace* facep = mDrawable->getFace(0);
423			if(facep)
424			{
425				facep->setSize(0, 0);			
426			}
427		}
428	}
429	else
430	{		
431		plantBlades();
432	}
433	return TRUE;
434}
435
436void LLVOGrass::plantBlades()
437{
438	// It is possible that the species of a grass is not defined
439	// This is bad, but not the end of the world.
440	if (!sSpeciesTable.count(mSpecies))
441	{
442		llinfos << "Unknown grass species " << mSpecies << llendl;
443		return;
444	}
445	
446	if (mDrawable->getNumFaces() < 1)
447	{
448		mDrawable->setNumFaces(1, NULL, getTEImage(0));
449	}
450		
451	LLFace *face = mDrawable->getFace(0);
452
453	face->setTexture(getTEImage(0));
454	face->setState(LLFace::GLOBAL);
455	face->setSize(mNumBlades * 8, mNumBlades * 12);
456	face->setVertexBuffer(NULL);
457	face->setTEOffset(0);
458	face->mCenterLocal = mPosition + mRegionp->getOriginAgent();
459	
460	mDepth = (face->mCenterLocal - LLViewerCamera::getInstance()->getOrigin())*LLViewerCamera::getInstance()->getAtAxis();
461	mDrawable->setPosition(face->mCenterLocal);
462	mDrawable->movePartition();
463	LLPipeline::sCompiles++;
464}
465
466void LLVOGrass::getGeometry(S32 idx,
467								LLStrider<LLVector4a>& verticesp,
468								LLStrider<LLVector3>& normalsp, 
469								LLStrider<LLVector2>& texcoordsp,
470								LLStrider<LLColor4U>& colorsp, 
471								LLStrider<U16>& indicesp)
472{
473	if(!mNumBlades)//stop rendering grass
474	{
475		return ;
476	}
477
478	mPatch = mRegionp->getLand().resolvePatchRegion(getPositionRegion());
479	if (mPatch)
480		mLastPatchUpdateTime = mPatch->getLastUpdateTime();
481	
482	LLVector3 position;
483	// Create random blades of grass with gaussian distribution
484	F32 x,y,xf,yf,dzx,dzy;
485
486	LLColor4U color(255,255,255,255);
487
488	LLFace *face = mDrawable->getFace(idx);
489
490	F32 width  = sSpeciesTable[mSpecies]->mBladeSizeX;
491	F32 height = sSpeciesTable[mSpecies]->mBladeSizeY;
492
493	U32 index_offset = face->getGeomIndex();
494
495	for (S32 i = 0;  i < mNumBlades; i++)
496	{
497		x   = exp_x[i] * mScale.mV[VX];
498		y   = exp_y[i] * mScale.mV[VY];
499		xf  = rot_x[i] * GRASS_BLADE_BASE * width * w_mod[i];
500		yf  = rot_y[i] * GRASS_BLADE_BASE * width * w_mod[i];
501		dzx = dz_x [i];
502		dzy = dz_y [i];
503
504		LLVector3 v1,v2,v3;
505		F32 blade_height= GRASS_BLADE_HEIGHT * height * w_mod[i];
506
507		*texcoordsp++   = LLVector2(0, 0);
508		*texcoordsp++   = LLVector2(0, 0);
509		*texcoordsp++   = LLVector2(0, 0.98f);
510		*texcoordsp++   = LLVector2(0, 0.98f);
511		*texcoordsp++   = LLVector2(1, 0);
512		*texcoordsp++   = LLVector2(1, 0);
513		*texcoordsp++   = LLVector2(1, 0.98f);
514		*texcoordsp++   = LLVector2(1, 0.98f);
515
516		position.mV[0]  = mPosition.mV[VX] + x + xf;
517		position.mV[1]  = mPosition.mV[VY] + y + yf;
518		position.mV[2]  = mRegionp->getLand().resolveHeightRegion(position);
519		v1 = position + mRegionp->getOriginAgent();
520		(*verticesp++).load3(v1.mV);
521		(*verticesp++).load3(v1.mV);
522
523
524		position.mV[0] += dzx;
525		position.mV[1] += dzy;
526		position.mV[2] += blade_height;
527		v2 = position + mRegionp->getOriginAgent();
528		(*verticesp++).load3(v2.mV);
529		(*verticesp++).load3(v2.mV);
530
531		position.mV[0]  = mPosition.mV[VX] + x - xf;
532		position.mV[1]  = mPosition.mV[VY] + y - xf;
533		position.mV[2]  = mRegionp->getLand().resolveHeightRegion(position);
534		v3 = position + mRegionp->getOriginAgent();
535		(*verticesp++).load3(v3.mV);
536		(*verticesp++).load3(v3.mV);
537
538		LLVector3 normal1 = (v1-v2) % (v2-v3);
539		normal1.mV[VZ] = 0.75f;
540		normal1.normalize();
541		LLVector3 normal2 = -normal1;
542		normal2.mV[VZ] = -normal2.mV[VZ];
543
544		position.mV[0] += dzx;
545		position.mV[1] += dzy;
546		position.mV[2] += blade_height;
547		v1 = position + mRegionp->getOriginAgent();
548		(*verticesp++).load3(v1.mV);
549		(*verticesp++).load3(v1.mV);
550
551		*(normalsp++)   = normal1;
552		*(normalsp++)   = normal2;
553		*(normalsp++)   = normal1;
554		*(normalsp++)   = normal2;
555
556		*(normalsp++)   = normal1;
557		*(normalsp++)   = normal2;
558		*(normalsp++)   = normal1;
559		*(normalsp++)   = normal2;
560
561		*(colorsp++)   = color;
562		*(colorsp++)   = color;
563		*(colorsp++)   = color;
564		*(colorsp++)   = color;
565		*(colorsp++)   = color;
566		*(colorsp++)   = color;
567		*(colorsp++)   = color;
568		*(colorsp++)   = color;
569		
570		*indicesp++     = index_offset + 0;
571		*indicesp++     = index_offset + 2;
572		*indicesp++     = index_offset + 4;
573
574		*indicesp++     = index_offset + 2;
575		*indicesp++     = index_offset + 6;
576		*indicesp++     = index_offset + 4;
577
578		*indicesp++     = index_offset + 1;
579		*indicesp++     = index_offset + 5;
580		*indicesp++     = index_offset + 3;
581
582		*indicesp++     = index_offset + 3;
583		*indicesp++     = index_offset + 5;
584		*indicesp++     = index_offset + 7;
585		index_offset   += 8;
586	}
587
588	LLPipeline::sCompiles++;
589}
590
591U32 LLVOGrass::getPartitionType() const
592{
593	return LLViewerRegion::PARTITION_GRASS;
594}
595
596LLGrassPartition::LLGrassPartition()
597{
598	mDrawableType = LLPipeline::RENDER_TYPE_GRASS;
599	mPartitionType = LLViewerRegion::PARTITION_GRASS;
600	mLODPeriod = 16;
601	mDepthMask = TRUE;
602	mSlopRatio = 0.1f;
603	mRenderPass = LLRenderPass::PASS_GRASS;
604	mBufferUsage = GL_DYNAMIC_DRAW_ARB;
605}
606
607// virtual
608void LLVOGrass::updateDrawable(BOOL force_damped)
609{
610	// Force an immediate rebuild on any update
611	if (mDrawable.notNull())
612	{
613		mDrawable->updateXform(TRUE);
614		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
615	}
616	clearChanged(SHIFTED);
617}
618
619// virtual 
620BOOL LLVOGrass::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
621									  LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
622	
623{
624	BOOL ret = FALSE;
625	if (!mbCanSelect ||
626		mDrawable->isDead() || 
627		!gPipeline.hasRenderType(mDrawable->getRenderType()))
628	{
629		return FALSE;
630	}
631
632	LLVector3 dir = end-start;
633
634	mPatch = mRegionp->getLand().resolvePatchRegion(getPositionRegion());
635	
636	LLVector3 position;
637	// Create random blades of grass with gaussian distribution
638	F32 x,y,xf,yf,dzx,dzy;
639
640	LLColor4U color(255,255,255,255);
641
642	F32 width  = sSpeciesTable[mSpecies]->mBladeSizeX;
643	F32 height = sSpeciesTable[mSpecies]->mBladeSizeY;
644
645	LLVector2 tc[4];
646	LLVector3 v[4];
647	//LLVector3 n[4];
648
649	F32 closest_t = 1.f;
650
651	for (S32 i = 0;  i < mNumBlades; i++)
652	{
653		x   = exp_x[i] * mScale.mV[VX];
654		y   = exp_y[i] * mScale.mV[VY];
655		xf  = rot_x[i] * GRASS_BLADE_BASE * width * w_mod[i];
656		yf  = rot_y[i] * GRASS_BLADE_BASE * width * w_mod[i];
657		dzx = dz_x [i];
658		dzy = dz_y [i];
659
660		LLVector3 v1,v2,v3;
661		F32 blade_height= GRASS_BLADE_HEIGHT * height * w_mod[i];
662
663		tc[0]   = LLVector2(0, 0);
664		tc[1]   = LLVector2(0, 0.98f);
665		tc[2]   = LLVector2(1, 0);
666		tc[3]   = LLVector2(1, 0.98f);
667	
668		position.mV[0]  = mPosition.mV[VX] + x + xf;
669		position.mV[1]  = mPosition.mV[VY] + y + yf;
670		position.mV[2]  = mRegionp->getLand().resolveHeightRegion(position);
671		v[0]    = v1 = position + mRegionp->getOriginAgent();
672		
673
674
675		position.mV[0] += dzx;
676		position.mV[1] += dzy;
677		position.mV[2] += blade_height;
678		v[1]    = v2 = position + mRegionp->getOriginAgent();
679		
680		position.mV[0]  = mPosition.mV[VX] + x - xf;
681		position.mV[1]  = mPosition.mV[VY] + y - xf;
682		position.mV[2]  = mRegionp->getLand().resolveHeightRegion(position);
683		v[2]    = v3 = position + mRegionp->getOriginAgent();
684		
685		LLVector3 normal1 = (v1-v2) % (v2-v3);
686		normal1.normalize();
687		
688		position.mV[0] += dzx;
689		position.mV[1] += dzy;
690		position.mV[2] += blade_height;
691		v[3]    = v1 = position + mRegionp->getOriginAgent();
692	
693		F32 a,b,t;
694
695		BOOL hit = FALSE;
696
697
698		U32 idx0 = 0,idx1 = 0,idx2 = 0;
699
700		if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE))
701		{
702			hit = TRUE;
703			idx0 = 0; idx1 = 1; idx2 = 2;
704		}
705		else if (LLTriangleRayIntersect(v[1], v[3], v[2], start, dir, a, b, t, FALSE))
706		{
707			hit = TRUE;
708			idx0 = 1; idx1 = 3; idx2 = 2;
709		}
710		else if (LLTriangleRayIntersect(v[2], v[1], v[0], start, dir, a, b, t, FALSE))
711		{
712			normal1 = -normal1;
713			hit = TRUE;
714			idx0 = 2; idx1 = 1; idx2 = 0;
715		}
716		else if (LLTriangleRayIntersect(v[2], v[3], v[1], start, dir, a, b, t, FALSE))
717		{
718			normal1 = -normal1;
719			hit = TRUE;
720			idx0 = 2; idx1 = 3; idx2 = 1;
721		}
722
723		if (hit)
724		{
725			if (t >= 0.f &&
726				t <= 1.f &&
727				t < closest_t)
728			{
729
730				LLVector2 hit_tc = ((1.f - a - b)  * tc[idx0] +
731									  a              * tc[idx1] +
732									  b              * tc[idx2]);
733				if (pick_transparent ||
734					getTEImage(0)->getMask(hit_tc))
735				{
736					closest_t = t;
737					if (intersection != NULL)
738					{
739						*intersection = start+dir*closest_t;
740					}
741
742					if (tex_coord != NULL)
743					{
744						*tex_coord = hit_tc;
745					}
746
747					if (normal != NULL)
748					{
749						*normal    = normal1;
750					}
751					ret = TRUE;
752				}
753			}
754		}
755	}
756
757	return ret;
758}
759