From 08ae21f52dbbe6245ac8deee0fdfd5df4d3dba53 Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Wed, 13 Mar 2013 17:07:22 -0500
Subject: [PATCH] MAINT-2410 Extra Particle Parameters -- viewer implementation

Reviewed by Kelly and Graham
---
 indra/llmessage/llpartdata.cpp       | 257 +++++++++++++---------
 indra/llmessage/llpartdata.h         |  22 +-
 indra/llrender/llvertexbuffer.cpp    |   9 +-
 indra/newview/lldrawpoolalpha.cpp    |  38 ++--
 indra/newview/llspatialpartition.cpp |   4 +-
 indra/newview/llspatialpartition.h   |   2 +
 indra/newview/llviewerobject.cpp     |  21 +-
 indra/newview/llviewerobject.h       |   8 +-
 indra/newview/llviewerpartsim.cpp    |  27 ++-
 indra/newview/llviewerpartsim.h      |   9 +-
 indra/newview/llviewerpartsource.cpp |  75 ++++++-
 indra/newview/llviewerpartsource.h   |   4 +-
 indra/newview/llvograss.cpp          |   7 +-
 indra/newview/llvograss.h            |   1 +
 indra/newview/llvopartgroup.cpp      | 305 +++++++++++++++++++--------
 indra/newview/llvopartgroup.h        |   6 +-
 indra/newview/llvovolume.cpp         |   6 +-
 17 files changed, 573 insertions(+), 228 deletions(-)

diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp
index c0fd3c95428..0f33a942952 100644
--- a/indra/llmessage/llpartdata.cpp
+++ b/indra/llmessage/llpartdata.cpp
@@ -37,9 +37,21 @@
 
 
 
-const S32 PS_PART_DATA_GLOW_BLEND_SIZE = 4;
-const S32 PS_PART_DATA_BLOCK_SIZE = 4 + 2 + 4 + 4 + 2 + 2; // 18
-const S32 PS_DATA_BLOCK_SIZE = 68 + PS_PART_DATA_BLOCK_SIZE + PS_PART_DATA_GLOW_BLEND_SIZE; // 68 + 18 + 4 = 90
+const S32 PS_PART_DATA_GLOW_SIZE = 2;
+const S32 PS_PART_DATA_BLEND_SIZE = 2;
+const S32 PS_LEGACY_PART_DATA_BLOCK_SIZE = 4 + 2 + 4 + 4 + 2 + 2; //18
+const S32 PS_SYS_DATA_BLOCK_SIZE = 68;
+const S32 PS_MAX_DATA_BLOCK_SIZE = PS_SYS_DATA_BLOCK_SIZE+
+									PS_LEGACY_PART_DATA_BLOCK_SIZE +
+									PS_PART_DATA_BLEND_SIZE +
+									PS_PART_DATA_GLOW_SIZE+
+									8; //two S32 size fields
+
+const S32 PS_LEGACY_DATA_BLOCK_SIZE = PS_SYS_DATA_BLOCK_SIZE + PS_LEGACY_PART_DATA_BLOCK_SIZE;
+
+
+const U32 PART_DATA_MASK = LLPartData::LL_PART_DATA_GLOW | LLPartData::LL_PART_DATA_BLEND;
+
 
 
 const F32 MAX_PART_SCALE = 4.f;
@@ -49,28 +61,9 @@ bool LLPartData::hasGlow() const
 	return mStartGlow > 0.f || mEndGlow > 0.f;
 }
 
-BOOL LLPartData::pack(LLDataPacker &dp)
+bool LLPartData::hasBlendFunc() const
 {
-	LLColor4U coloru;
-	dp.packU32(mFlags, "pdflags");
-	dp.packFixed(mMaxAge, "pdmaxage", FALSE, 8, 8);
-	coloru.setVec(mStartColor);
-	dp.packColor4U(coloru, "pdstartcolor");
-	coloru.setVec(mEndColor);
-	dp.packColor4U(coloru, "pdendcolor");
-	dp.packFixed(mStartScale.mV[0], "pdstartscalex", FALSE, 3, 5);
-	dp.packFixed(mStartScale.mV[1], "pdstartscaley", FALSE, 3, 5);
-	dp.packFixed(mEndScale.mV[0], "pdendscalex", FALSE, 3, 5);
-	dp.packFixed(mEndScale.mV[1], "pdendscaley", FALSE, 3, 5);
-
-	if (hasGlow() || hasBlendFunc())
-	{
-		dp.packU8(mStartGlow * 255,"pdstartglow");
-		dp.packU8(mEndGlow * 255,"pdendglow");
-		dp.packU8(mBlendFuncSource,"pdblendsource");
-		dp.packU8(mBlendFuncDest,"pdblenddest");
-	}
-	return TRUE;
+	return mBlendFuncSource != LLPartData::LL_PART_BF_SOURCE_ALPHA || mBlendFuncDest != LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA;
 }
 
 LLSD LLPartData::asLLSD() const
@@ -107,8 +100,17 @@ bool LLPartData::fromLLSD(LLSD& sd)
 	return true;
 }
 
+S32 LLPartData::getSize() const
+{
+	S32 size = PS_LEGACY_PART_DATA_BLOCK_SIZE;
+	if (hasGlow()) size += PS_PART_DATA_GLOW_SIZE;
+	if (hasBlendFunc()) size += PS_PART_DATA_BLEND_SIZE;
+
+	return size;
+}
 
-BOOL LLPartData::unpack(LLDataPacker &dp)
+
+BOOL LLPartData::unpackLegacy(LLDataPacker &dp)
 {
 	LLColor4U coloru;
 
@@ -124,7 +126,7 @@ BOOL LLPartData::unpack(LLDataPacker &dp)
 	dp.unpackFixed(mEndScale.mV[0], "pdendscalex", FALSE, 3, 5);
 	dp.unpackFixed(mEndScale.mV[1], "pdendscaley", FALSE, 3, 5);
 
-	if (dp.hasNext())
+	/*if (dp.hasNext())
 	{
 		U8 tmp_glow = 0;
 		dp.unpackU8(tmp_glow,"pdstartglow");
@@ -133,11 +135,71 @@ BOOL LLPartData::unpack(LLDataPacker &dp)
 		mEndGlow = tmp_glow / 255.f;
 		dp.unpackU8(mBlendFuncSource,"pdblendsource");
 		dp.unpackU8(mBlendFuncDest,"pdblenddest");
-	}
+	}*/
+	
+	mStartGlow = 0.f;
+	mEndGlow = 0.f;
+	mBlendFuncSource = LLPartData::LL_PART_BF_SOURCE_ALPHA;
+	mBlendFuncDest = LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA;
 
 	return TRUE;
 }
 
+BOOL LLPartData::unpack(LLDataPacker &dp)
+{
+	S32 size = 0;
+	dp.unpackS32(size, "partsize");
+
+	unpackLegacy(dp);
+	size -= PS_LEGACY_PART_DATA_BLOCK_SIZE;
+
+	if (mFlags & LL_PART_DATA_GLOW)
+	{
+		if (size < PS_PART_DATA_GLOW_SIZE) return FALSE;
+
+		U8 tmp_glow = 0;
+		dp.unpackU8(tmp_glow,"pdstartglow");
+		mStartGlow = tmp_glow / 255.f;
+		dp.unpackU8(tmp_glow,"pdendglow");
+		mEndGlow = tmp_glow / 255.f;
+
+		size -= PS_PART_DATA_GLOW_SIZE;
+	}
+	else
+	{
+		mStartGlow = 0.f;
+		mEndGlow = 0.f;
+	}
+
+	if (mFlags & LL_PART_DATA_BLEND)
+	{
+		if (size < PS_PART_DATA_BLEND_SIZE) return FALSE;
+		dp.unpackU8(mBlendFuncSource,"pdblendsource");
+		dp.unpackU8(mBlendFuncDest,"pdblenddest");
+		size -= PS_PART_DATA_BLEND_SIZE;
+	}
+	else
+	{
+		mBlendFuncSource = LLPartData::LL_PART_BF_SOURCE_ALPHA;
+		mBlendFuncDest = LLPartData::LL_PART_BF_ONE_MINUS_SOURCE_ALPHA;
+	}
+
+	if (size > 0)
+	{ //leftover bytes, unrecognized parameters
+		U8 feh = 0;
+		while (size > 0)
+		{ //read remaining bytes in block
+			dp.unpackU8(feh, "whippang");
+			size--;
+		}
+
+		//this particle system won't display properly, better to not show anything
+		return FALSE;
+	}
+
+
+	return TRUE;
+}
 
 void LLPartData::setFlags(const U32 flags)
 {
@@ -228,38 +290,7 @@ LLPartSysData::LLPartSysData()
 	mNumParticles = 0;
 }
 
-
-BOOL LLPartSysData::pack(LLDataPacker &dp)
-{
-	dp.packU32(mCRC, "pscrc");
-	dp.packU32(mFlags, "psflags");
-	dp.packU8(mPattern, "pspattern");
-	dp.packFixed(mMaxAge, "psmaxage", FALSE, 8, 8);
-	dp.packFixed(mStartAge, "psstartage", FALSE, 8, 8);
-	dp.packFixed(mInnerAngle, "psinnerangle", FALSE, 3, 5);
-	dp.packFixed(mOuterAngle, "psouterangle", FALSE, 3, 5);
-	dp.packFixed(mBurstRate, "psburstrate", FALSE, 8, 8);
-	dp.packFixed(mBurstRadius, "psburstradius", FALSE, 8, 8);
-	dp.packFixed(mBurstSpeedMin, "psburstspeedmin", FALSE, 8, 8);
-	dp.packFixed(mBurstSpeedMax, "psburstspeedmax", FALSE, 8, 8);
-	dp.packU8(mBurstPartCount, "psburstpartcount");
-
-	dp.packFixed(mAngularVelocity.mV[0], "psangvelx", TRUE, 8, 7);
-	dp.packFixed(mAngularVelocity.mV[1], "psangvely", TRUE, 8, 7);
-	dp.packFixed(mAngularVelocity.mV[2], "psangvelz", TRUE, 8, 7);
-
-	dp.packFixed(mPartAccel.mV[0], "psaccelx", TRUE, 8, 7);
-	dp.packFixed(mPartAccel.mV[1], "psaccely", TRUE, 8, 7);
-	dp.packFixed(mPartAccel.mV[2], "psaccelz", TRUE, 8, 7);
-
-	dp.packUUID(mPartImageID, "psuuid");
-	dp.packUUID(mTargetUUID, "pstargetuuid");
-	mPartData.pack(dp);
-	return TRUE;
-}
-
-
-BOOL LLPartSysData::unpack(LLDataPacker &dp)
+BOOL LLPartSysData::unpackSystem(LLDataPacker &dp)
 {
 	dp.unpackU32(mCRC, "pscrc");
 	dp.unpackU32(mFlags, "psflags");
@@ -285,10 +316,48 @@ BOOL LLPartSysData::unpack(LLDataPacker &dp)
 
 	dp.unpackUUID(mPartImageID, "psuuid");
 	dp.unpackUUID(mTargetUUID, "pstargetuuid");
-	mPartData.unpack(dp);
 	return TRUE;
 }
 
+BOOL LLPartSysData::unpackLegacy(LLDataPacker &dp)
+{
+	unpackSystem(dp);
+	mPartData.unpackLegacy(dp);
+
+	return TRUE;
+}
+
+BOOL LLPartSysData::unpack(LLDataPacker &dp)
+{
+	// syssize is currently unused.  Adding now when modifying the 'version to make extensible in the future
+	S32 size = 0;
+	dp.unpackS32(size, "syssize");
+	
+	if (size != PS_SYS_DATA_BLOCK_SIZE)
+	{ //unexpected size, this viewer doesn't know how to parse this particle system
+		
+		//skip to LLPartData block
+		U8 feh = 0;
+		
+		for (U32 i = 0; i < size; ++i)
+		{
+			dp.unpackU8(feh, "whippang");
+		}
+				
+		dp.unpackS32(size, "partsize");
+		//skip LLPartData block
+		for (U32 i = 0; i < size; ++i)
+		{
+			dp.unpackU8(feh, "whippang");
+		}
+		return FALSE;
+	}
+
+	unpackSystem(dp);
+	
+	return mPartData.unpack(dp);
+}
+
 std::ostream& operator<<(std::ostream& s, const LLPartSysData &data)
 {
 	s << "Flags: " << std::hex << data.mFlags;
@@ -306,7 +375,7 @@ std::ostream& operator<<(std::ostream& s, const LLPartSysData &data)
 
 BOOL LLPartSysData::isNullPS(const S32 block_num)
 {
-	U8 ps_data_block[PS_DATA_BLOCK_SIZE];
+	U8 ps_data_block[PS_MAX_DATA_BLOCK_SIZE];
 	U32 crc;
 
 	S32 size;
@@ -318,15 +387,27 @@ BOOL LLPartSysData::isNullPS(const S32 block_num)
 		return TRUE;
 	}
 	
-	gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, PS_DATA_BLOCK_SIZE, block_num, PS_DATA_BLOCK_SIZE);
-
-	if (size > PS_DATA_BLOCK_SIZE)
+	if (size > PS_MAX_DATA_BLOCK_SIZE)
 	{
 		//size is too big, newer particle version unsupported
 		return TRUE;
 	}
 
+	gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, size, block_num, PS_MAX_DATA_BLOCK_SIZE);
+
 	LLDataPackerBinaryBuffer dp(ps_data_block, size);
+	if (size > PS_LEGACY_DATA_BLOCK_SIZE)
+	{
+		// non legacy systems pack a size before the CRC
+		S32 tmp = 0;
+		dp.unpackS32(tmp, "syssize");
+
+		if (tmp > PS_SYS_DATA_BLOCK_SIZE)
+		{ //unknown system data block size, don't know how to parse it, treat as NULL
+			return TRUE;
+		}
+	}
+
 	dp.unpackU32(crc, "crc");
 
 	if (crc == 0)
@@ -336,53 +417,37 @@ BOOL LLPartSysData::isNullPS(const S32 block_num)
 	return FALSE;
 }
 
-
-//static
-BOOL LLPartSysData::packNull()
-{
-	U8 ps_data_block[PS_DATA_BLOCK_SIZE];
-	gMessageSystem->addBinaryData("PSBlock", ps_data_block, 0);
-	return TRUE;
-}
-
-
-BOOL LLPartSysData::packBlock()
-{
-	U8 ps_data_block[PS_DATA_BLOCK_SIZE];
-
-	LLDataPackerBinaryBuffer dp(ps_data_block, PS_DATA_BLOCK_SIZE);
-
-	pack(dp);
-
-	S32 size = dp.getCurrentSize();
-
-	// Add to message
-	gMessageSystem->addBinaryData("PSBlock", ps_data_block, size);
-
-	return TRUE;
-}                                         
-
-
 BOOL LLPartSysData::unpackBlock(const S32 block_num)
 {
-	U8 ps_data_block[PS_DATA_BLOCK_SIZE];
+	U8 ps_data_block[PS_MAX_DATA_BLOCK_SIZE];
 
 	// Check size of block
 	S32 size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock");
 
-	if (size > PS_DATA_BLOCK_SIZE)
+	if (size > PS_MAX_DATA_BLOCK_SIZE)
 	{
 		// Larger packets are newer and unsupported
 		return FALSE;
 	}
 
 	// Get from message
-	gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, size, block_num, PS_DATA_BLOCK_SIZE);
+	gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, size, block_num, PS_MAX_DATA_BLOCK_SIZE);
 
 	LLDataPackerBinaryBuffer dp(ps_data_block, size);
-	unpack(dp);
 
-	return TRUE;
+	if (size == PS_LEGACY_DATA_BLOCK_SIZE)
+	{
+		return unpackLegacy(dp);
+	}
+	else
+	{
+		return unpack(dp);
+	}
+}
+
+bool LLPartSysData::isLegacyCompatible() const
+{
+	return !mPartData.hasGlow() && !mPartData.hasBlendFunc();
 }
 
 void LLPartSysData::clampSourceParticleRate()
diff --git a/indra/llmessage/llpartdata.h b/indra/llmessage/llpartdata.h
index 19207d994d2..80a52b79ddf 100644
--- a/indra/llmessage/llpartdata.h
+++ b/indra/llmessage/llpartdata.h
@@ -88,7 +88,9 @@ class LLPartData
 		mParameter(0.f)
 	{
 	}
+	BOOL unpackLegacy(LLDataPacker &dp);
 	BOOL unpack(LLDataPacker &dp);
+
 	BOOL pack(LLDataPacker &dp);
 	LLSD asLLSD() const;
 	operator LLSD() const {return asLLSD(); }
@@ -117,6 +119,10 @@ class LLPartData
 		//LL_PART_RANDOM_VEL_MASK =		0x200,		// Particles have random velocity shifts"
 		//LL_PART_TRAIL_MASK =			0x400,		// Particles have historical "trails"
 
+		//sYSTEM SET FLAGS
+		LL_PART_DATA_GLOW =				0x10000,
+		LL_PART_DATA_BLEND =			0x20000,
+
 		// Viewer side use only!
 		LL_PART_HUD =					0x40000000,
 		LL_PART_DEAD_MASK =				0x80000000,
@@ -152,6 +158,9 @@ class LLPartData
 	friend class LLPartSysData;
 	friend class LLViewerPartSourceScript;
 
+private:
+	S32 getSize() const;
+
 	// These are public because I'm really lazy...
 public:
 	U32					mFlags;						// Particle state/interpolators in effect
@@ -178,15 +187,13 @@ class LLPartSysData
 	LLPartSysData();
 
 	BOOL unpack(LLDataPacker &dp);
-	BOOL pack(LLDataPacker &dp);
-
-	
+	BOOL unpackLegacy(LLDataPacker &dp);
 	BOOL unpackBlock(const S32 block_num);
-	BOOL packBlock();
-
-	static BOOL packNull();
+		
 	static BOOL isNullPS(const S32 block_num); // Returns FALSE if this is a "NULL" particle system (i.e. no system)
 
+	bool isLegacyCompatible() const;
+
 	// Different masks for effects on the source
 	enum
 	{
@@ -222,6 +229,9 @@ class LLPartSysData
 
 	S32 getdataBlockSize() const;
 	
+private:
+	BOOL unpackSystem(LLDataPacker &dp);
+
 public:
 	// Public because I'm lazy....
 
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index 1d257d84153..4460461e128 100644
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -2360,7 +2360,8 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
 		if (data_mask & MAP_COLOR)
 		{
 			S32 loc = TYPE_COLOR;
-			void* ptr = (void*)(base + mOffsets[TYPE_COLOR]);
+			//bind emissive instead of color pointer if emissive is present
+			void* ptr = (data_mask & MAP_EMISSIVE) ? (void*)(base + mOffsets[TYPE_EMISSIVE]) : (void*)(base + mOffsets[TYPE_COLOR]);
 			glVertexAttribPointerARB(loc, 4, GL_UNSIGNED_BYTE, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_COLOR], ptr);
 		}
 		if (data_mask & MAP_EMISSIVE)
@@ -2368,6 +2369,12 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
 			S32 loc = TYPE_EMISSIVE;
 			void* ptr = (void*)(base + mOffsets[TYPE_EMISSIVE]);
 			glVertexAttribPointerARB(loc, 4, GL_UNSIGNED_BYTE, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_EMISSIVE], ptr);
+
+			if (!(data_mask & MAP_COLOR))
+			{ //map emissive to color channel when color is not also being bound to avoid unnecessary shader swaps
+				loc = TYPE_COLOR;
+				glVertexAttribPointerARB(loc, 4, GL_UNSIGNED_BYTE, GL_TRUE, LLVertexBuffer::sTypeSize[TYPE_EMISSIVE], ptr);
+			}
 		}
 		if (data_mask & MAP_WEIGHT)
 		{
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
index 313b310e1e8..6fa16825df6 100644
--- a/indra/newview/lldrawpoolalpha.cpp
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -394,10 +394,15 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask)
 		if (group->mSpatialPartition->mRenderByGroup &&
 		    !group->isDead())
 		{
-			bool draw_glow_for_this_partition = mVertexShaderLevel > 0 && // no shaders = no glow.
-				// All particle systems seem to come off the wire with texture entries which claim that they glow.  This is probably a bug in the data.  Suppress.
-				group->mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_PARTICLE &&
-				group->mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_HUD_PARTICLE;
+			static LLFastTimer::DeclareTimer FTM_RENDER_ALPHA_GROUP_LOOP("Alpha Group");
+			LLFastTimer t(FTM_RENDER_ALPHA_GROUP_LOOP);
+
+			bool draw_glow_for_this_partition = mVertexShaderLevel > 0; // no shaders = no glow.
+
+			bool disable_cull = group->mSpatialPartition->mPartitionType == LLViewerRegion::PARTITION_PARTICLE ||
+				group->mSpatialPartition->mPartitionType == LLViewerRegion::PARTITION_HUD_PARTICLE;
+
+			LLGLDisable cull(disable_cull ? GL_CULL_FACE : 0);
 
 			LLSpatialGroup::drawmap_elem_t& draw_info = group->mDrawMap[LLRenderPass::PASS_ALPHA];
 
@@ -498,32 +503,31 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask)
 					}
 				}
 
-				params.mVertexBuffer->setBuffer(mask);
-				params.mVertexBuffer->drawRange(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
-				gPipeline.addTrianglesDrawn(params.mCount, params.mDrawMode);
+				static LLFastTimer::DeclareTimer FTM_RENDER_ALPHA_PUSH("Alpha Push Verts");
+				{
+					LLFastTimer t(FTM_RENDER_ALPHA_PUSH);
+					gGL.blendFunc((LLRender::eBlendFactor) params.mBlendFuncSrc, (LLRender::eBlendFactor) params.mBlendFuncDst, mAlphaSFactor, mAlphaDFactor);
+					params.mVertexBuffer->setBuffer(mask);
+					params.mVertexBuffer->drawRange(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
+					gPipeline.addTrianglesDrawn(params.mCount, params.mDrawMode);
+				}
 				
 				// If this alpha mesh has glow, then draw it a second time to add the destination-alpha (=glow).  Interleaving these state-changing calls could be expensive, but glow must be drawn Z-sorted with alpha.
 				if (current_shader && 
 					draw_glow_for_this_partition &&
 					params.mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_EMISSIVE))
 				{
+					static LLFastTimer::DeclareTimer FTM_RENDER_ALPHA_GLOW("Alpha Glow");
+					LLFastTimer t(FTM_RENDER_ALPHA_GLOW);
 					// install glow-accumulating blend mode
 					gGL.blendFunc(LLRender::BF_ZERO, LLRender::BF_ONE, // don't touch color
 						      LLRender::BF_ONE, LLRender::BF_ONE); // add to alpha (glow)
 
-					emissive_shader->bind();
-					
-					// glow doesn't use vertex colors from the mesh data
-					params.mVertexBuffer->setBuffer((mask & ~LLVertexBuffer::MAP_COLOR) | LLVertexBuffer::MAP_EMISSIVE);
+					params.mVertexBuffer->setBuffer(mask | LLVertexBuffer::MAP_EMISSIVE);
 					
 					// do the actual drawing, again
 					params.mVertexBuffer->drawRange(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
 					gPipeline.addTrianglesDrawn(params.mCount, params.mDrawMode);
-
-					// restore our alpha blend mode
-					gGL.blendFunc(mColorSFactor, mColorDFactor, mAlphaSFactor, mAlphaDFactor);
-
-					current_shader->bind();
 				}
 			
 				if (tex_setup)
@@ -536,6 +540,8 @@ void LLDrawPoolAlpha::renderAlpha(U32 mask)
 		}
 	}
 
+	gGL.setSceneBlendType(LLRender::BT_ALPHA);
+
 	LLVertexBuffer::unbind();	
 		
 	if (!light_enabled)
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index dd69172184c..1ec56eb5f8d 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -4654,7 +4654,9 @@ LLDrawInfo::LLDrawInfo(U16 start, U16 end, U32 count, U32 offset,
 	mGroup(NULL),
 	mFace(NULL),
 	mDistance(0.f),
-	mDrawMode(LLRender::TRIANGLES)
+	mDrawMode(LLRender::TRIANGLES),
+	mBlendFuncSrc(LLRender::BF_SOURCE_ALPHA),
+	mBlendFuncDst(LLRender::BF_ONE_MINUS_SOURCE_ALPHA)
 {
 	mVertexBuffer->validateRange(mStart, mEnd, mCount, mOffset);
 	
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index b5543c4a373..08e77855c41 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -119,6 +119,8 @@ class LLDrawInfo : public LLRefCount
 	LL_ALIGN_16(LLFace* mFace); //associated face
 	F32 mDistance;
 	U32 mDrawMode;
+	U32 mBlendFuncSrc;
+	U32 mBlendFuncDst;
 
 	struct CompareTexture
 	{
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index b1a60197a2c..09cc4a11217 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -1553,6 +1553,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 				dp->setPassFlags(value);
 				dp->unpackUUID(owner_id, "Owner");
 
+				mOwnerID = owner_id;
+
 				if (value & 0x80)
 				{
 					dp->unpackVector3(new_angv, "Omega");
@@ -1626,13 +1628,13 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
                 retval |= checkMediaURL(media_url);
 
 				//
-				// Unpack particle system data
+				// Unpack particle system data (legacy)
 				//
 				if (value & 0x8)
 				{
-					unpackParticleSource(*dp, owner_id);
+					unpackParticleSource(*dp, owner_id, true);
 				}
-				else
+				else if (!(value & 0x400))
 				{
 					deleteParticleSource();
 				}
@@ -1697,7 +1699,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 				// keep local flags and overwrite remote-controlled flags
 				mFlags = (mFlags & FLAGS_LOCAL) | flags;
 
-					// ...new objects that should come in selected need to be added to the selected list
+				// ...new objects that should come in selected need to be added to the selected list
 				mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0);
 			}
 			break;
@@ -4604,7 +4606,7 @@ void LLViewerObject::unpackParticleSource(const S32 block_num, const LLUUID& own
 	}
 }
 
-void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id)
+void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id, bool legacy)
 {
 	if (!mPartSourcep.isNull() && mPartSourcep->isDead())
 	{
@@ -4613,7 +4615,7 @@ void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_
 	if (mPartSourcep)
 	{
 		// If we've got one already, just update the existing source (or remove it)
-		if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, dp))
+		if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, dp, legacy))
 		{
 			mPartSourcep->setDead();
 			mPartSourcep = NULL;
@@ -4621,7 +4623,7 @@ void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_
 	}
 	else
 	{
-		LLPointer<LLViewerPartSourceScript> pss = LLViewerPartSourceScript::unpackPSS(this, NULL, dp);
+		LLPointer<LLViewerPartSourceScript> pss = LLViewerPartSourceScript::unpackPSS(this, NULL, dp, legacy);
 		//If the owner is muted, don't create the system
 		if(LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagParticles)) return;
 		// We need to be able to deal with a particle source that hasn't changed, but still got an update!
@@ -5467,6 +5469,11 @@ F32 LLAlphaObject::getPartSize(S32 idx)
 	return 0.f;
 }
 
+void LLAlphaObject::getBlendFunc(S32 face, U32& src, U32& dst)
+{
+
+}
+
 // virtual
 void LLStaticViewerObject::updateDrawable(BOOL force_damped)
 {
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 97cf0a4850c..cab617b745a 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -584,6 +584,7 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 	} EPhysicsShapeType;
 
 	LLUUID			mID;
+	LLUUID			mOwnerID; //null if unknown
 
 	// unique within region, not unique across regions
 	// Local ID = 0 is not used
@@ -662,7 +663,7 @@ class LLViewerObject : public LLPrimitive, public LLRefCount, public LLGLUpdate
 	BOOL isOnMap();
 
 	void unpackParticleSource(const S32 block_num, const LLUUID& owner_id);
-	void unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id);
+	void unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id, bool legacy);
 	void deleteParticleSource();
 	void setParticleSource(const LLPartSysData& particle_parameters, const LLUUID& owner_id);
 	
@@ -826,9 +827,12 @@ class LLAlphaObject : public LLViewerObject
 								LLStrider<LLVector4a>& verticesp,
 								LLStrider<LLVector3>& normalsp, 
 								LLStrider<LLVector2>& texcoordsp,
-								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<LLColor4U>& colorsp,
+								LLStrider<LLColor4U>& emissivep,
 								LLStrider<U16>& indicesp) = 0;
 
+	virtual void getBlendFunc(S32 face, U32& src, U32& dst);
+
 	F32 mDepth;
 };
 
diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp
index 61cdfd78187..21f1d2619c1 100644
--- a/indra/newview/llviewerpartsim.cpp
+++ b/indra/newview/llviewerpartsim.cpp
@@ -80,12 +80,31 @@ LLViewerPart::LLViewerPart() :
 	mImagep(NULL)
 {
 	mPartSourcep = NULL;
-
+	mParent = NULL;
+	mChild = NULL;
 	++LLViewerPartSim::sParticleCount2 ;
 }
 
 LLViewerPart::~LLViewerPart()
 {
+	if (mPartSourcep.notNull() && mPartSourcep->mLastPart == this)
+	{
+		mPartSourcep->mLastPart = NULL;
+	}
+
+	//patch up holes in the ribbon
+	if (mParent)
+	{
+		llassert(mParent->mChild == this);
+		mParent->mChild = mChild;
+	}
+
+	if (mChild)
+	{
+		llassert (mChild->mParent == this);
+		mChild->mParent = mParent;
+	}
+
 	mPartSourcep = NULL;
 
 	--LLViewerPartSim::sParticleCount2 ;
@@ -367,6 +386,9 @@ void LLViewerPartGroup::updateParticles(const F32 lastdt)
 			part->mScale += frac*part->mEndScale;
 		}
 
+		// Do glow interpolation
+		part->mGlow.mV[3] = (U8) (lerp(part->mStartGlow, part->mEndGlow, frac)*255.f);
+
 		// Set the last update time to now.
 		part->mLastUpdateTime = cur_time;
 
@@ -623,6 +645,9 @@ void LLViewerPartSim::updateSimulation()
 {
 	static LLFrameTimer update_timer;
 
+	//reset VBO cursor
+	LLVOPartGroup::sVBSlotCursor = 0;
+
 	const F32 dt = llmin(update_timer.getElapsedTimeAndResetF32(), 0.1f);
 
  	if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)))
diff --git a/indra/newview/llviewerpartsim.h b/indra/newview/llviewerpartsim.h
index c91fcf0691f..095de2060cf 100644
--- a/indra/newview/llviewerpartsim.h
+++ b/indra/newview/llviewerpartsim.h
@@ -65,15 +65,22 @@ class LLViewerPart : public LLPartData
 
 	LLVPCallback		mVPCallback;				// Callback function for more complicated behaviors
 	LLPointer<LLViewerPartSource> mPartSourcep;		// Particle source used for this object
-	
+
+	LLViewerPart*		mParent;					// particle to connect to if this is part of a particle ribbon
+	LLViewerPart*		mChild;						// child particle for clean reference destruction
 
 	// Current particle state (possibly used for rendering)
 	LLPointer<LLViewerTexture>	mImagep;
 	LLVector3		mPosAgent;
 	LLVector3		mVelocity;
 	LLVector3		mAccel;
+	LLVector3		mAxis;
 	LLColor4		mColor;
 	LLVector2		mScale;
+	F32				mStartGlow;
+	F32				mEndGlow;
+	LLColor4U		mGlow;
+
 
 	static U32		sNextPartID;
 };
diff --git a/indra/newview/llviewerpartsource.cpp b/indra/newview/llviewerpartsource.cpp
index b311f659fbd..8c49ce646db 100644
--- a/indra/newview/llviewerpartsource.cpp
+++ b/indra/newview/llviewerpartsource.cpp
@@ -52,6 +52,8 @@ LLViewerPartSource::LLViewerPartSource(const U32 type) :
 	static U32 id_seed = 0;
 	mID = ++id_seed;
 
+	mLastPart = NULL;
+
 	mDelay = 0 ;
 }
 
@@ -279,6 +281,22 @@ void LLViewerPartSourceScript::update(const F32 dt)
 			{
 				part->mFlags |= LLPartData::LL_PART_HUD;
 			}
+
+			if (part->mFlags & LLPartData::LL_PART_RIBBON_MASK && mLastPart)
+			{ //set previous particle's parent to this particle to chain ribbon together
+				mLastPart->mParent = part;
+				part->mChild = mLastPart;
+				part->mAxis = LLVector3(0,0,1);
+
+				if (mSourceObjectp.notNull())
+				{
+					LLQuaternion rot = mSourceObjectp->getRenderRotation();
+					part->mAxis *= rot;
+				}
+			}
+
+			mLastPart = part;
+
 			part->mMaxAge = mPartSysData.mPartData.mMaxAge;
 			part->mStartColor = mPartSysData.mPartData.mStartColor;
 			part->mEndColor = mPartSysData.mPartData.mEndColor;
@@ -290,6 +308,13 @@ void LLViewerPartSourceScript::update(const F32 dt)
 
 			part->mAccel = mPartSysData.mPartAccel;
 
+			part->mBlendFuncDest = mPartSysData.mPartData.mBlendFuncDest;
+			part->mBlendFuncSource = mPartSysData.mPartData.mBlendFuncSource;
+
+			part->mStartGlow = mPartSysData.mPartData.mStartGlow;
+			part->mEndGlow = mPartSysData.mPartData.mEndGlow;
+			part->mGlow = LLColor4U(0, 0, 0, (U8) (part->mStartGlow*255.f));
+			
 			if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_DROP)
 			{
 				part->mPosAgent = mPosAgent;
@@ -430,28 +455,51 @@ LLPointer<LLViewerPartSourceScript> LLViewerPartSourceScript::unpackPSS(LLViewer
 }
 
 
-LLPointer<LLViewerPartSourceScript> LLViewerPartSourceScript::unpackPSS(LLViewerObject *source_objp, LLPointer<LLViewerPartSourceScript> pssp, LLDataPacker &dp)
+LLPointer<LLViewerPartSourceScript> LLViewerPartSourceScript::unpackPSS(LLViewerObject *source_objp, LLPointer<LLViewerPartSourceScript> pssp, LLDataPacker &dp, bool legacy)
 {
 	if (!pssp)
 	{
 		LLPointer<LLViewerPartSourceScript> new_pssp = new LLViewerPartSourceScript(source_objp);
-		if (!new_pssp->mPartSysData.unpack(dp))
+		if (legacy)
 		{
-			return NULL;
+			if (!new_pssp->mPartSysData.unpackLegacy(dp))
+			{
+				return NULL;
+			}
+		}
+		else
+		{
+			if (!new_pssp->mPartSysData.unpack(dp))
+			{
+				return NULL;
+			}
 		}
+		
 		if (new_pssp->mPartSysData.mTargetUUID.notNull())
 		{
 			LLViewerObject *target_objp = gObjectList.findObject(new_pssp->mPartSysData.mTargetUUID);
 			new_pssp->setTargetObject(target_objp);
 		}
+		
 		return new_pssp;
 	}
 	else
 	{
-		if (!pssp->mPartSysData.unpack(dp))
+		if (legacy)
 		{
-			return NULL;
+			if (!pssp->mPartSysData.unpackLegacy(dp))
+			{
+				return NULL;
+			}
 		}
+		else
+		{
+			if (!pssp->mPartSysData.unpack(dp))
+			{
+				return NULL;
+			}
+		}
+
 		if (pssp->mPartSysData.mTargetUUID.notNull())
 		{
 			LLViewerObject *target_objp = gObjectList.findObject(pssp->mPartSysData.mTargetUUID);
@@ -569,6 +617,11 @@ void LLViewerPartSourceSpiral::update(const F32 dt)
 		part->mScale.mV[0] = 0.25f;
 		part->mScale.mV[1] = 0.25f;
 		part->mParameter = ll_frand(F_TWO_PI);
+		part->mBlendFuncDest = LLRender::BF_ONE_MINUS_SOURCE_ALPHA;
+		part->mBlendFuncSource = LLRender::BF_SOURCE_ALPHA;
+		part->mStartGlow = 0.f;
+		part->mEndGlow = 0.f;
+		part->mGlow = LLColor4U(0, 0, 0, 0);
 
 		LLViewerPartSim::getInstance()->addPart(part);
 	}
@@ -721,6 +774,12 @@ void LLViewerPartSourceBeam::update(const F32 dt)
 		part->mPosAgent = mPosAgent;
 		part->mVelocity = mTargetPosAgent - mPosAgent;
 
+		part->mBlendFuncDest = LLRender::BF_ONE_MINUS_SOURCE_ALPHA;
+		part->mBlendFuncSource = LLRender::BF_SOURCE_ALPHA;
+		part->mStartGlow = 0.f;
+		part->mEndGlow = 0.f;
+		part->mGlow = LLColor4U(0, 0, 0, 0);
+
 		LLViewerPartSim::getInstance()->addPart(part);
 	}
 }
@@ -825,6 +884,12 @@ void LLViewerPartSourceChat::update(const F32 dt)
 		part->mScale.mV[0] = 0.25f;
 		part->mScale.mV[1] = 0.25f;
 		part->mParameter = ll_frand(F_TWO_PI);
+		part->mBlendFuncDest = LLRender::BF_ONE_MINUS_SOURCE_ALPHA;
+		part->mBlendFuncSource = LLRender::BF_SOURCE_ALPHA;
+		part->mStartGlow = 0.f;
+		part->mEndGlow = 0.f;
+		part->mGlow = LLColor4U(0, 0, 0, 0);
+
 
 		LLViewerPartSim::getInstance()->addPart(part);
 	}
diff --git a/indra/newview/llviewerpartsource.h b/indra/newview/llviewerpartsource.h
index 28702d36a29..12e926173b4 100644
--- a/indra/newview/llviewerpartsource.h
+++ b/indra/newview/llviewerpartsource.h
@@ -76,6 +76,7 @@ class LLViewerPartSource : public LLRefCount
 	LLVector3	mLastUpdatePosAgent;
 	LLPointer<LLViewerObject>	mSourceObjectp;
 	U32 mID;
+	LLViewerPart* mLastPart; //last particle emitted (for making particle ribbons)
 
 protected:
 	U32			mType;
@@ -85,7 +86,6 @@ class LLViewerPartSource : public LLRefCount
 	F32			mLastPartTime;
 	LLUUID		mOwnerUUID;
 	LLPointer<LLViewerTexture>	mImagep;
-
 	// Particle information
 	U32			mPartFlags; // Flags for the particle
 	U32         mDelay ; //delay to start particles
@@ -114,7 +114,7 @@ class LLViewerPartSourceScript : public LLViewerPartSource
 
 	// Returns a new particle source to attach to an object...
 	static LLPointer<LLViewerPartSourceScript> unpackPSS(LLViewerObject *source_objp, LLPointer<LLViewerPartSourceScript> pssp, const S32 block_num);
-	static LLPointer<LLViewerPartSourceScript> unpackPSS(LLViewerObject *source_objp, LLPointer<LLViewerPartSourceScript> pssp, LLDataPacker &dp);
+	static LLPointer<LLViewerPartSourceScript> unpackPSS(LLViewerObject *source_objp, LLPointer<LLViewerPartSourceScript> pssp, LLDataPacker &dp, bool legacy);
 	static LLPointer<LLViewerPartSourceScript> createPSS(LLViewerObject *source_objp, const LLPartSysData& particle_parameters);
 
 	LLViewerTexture *getImage() const				{ return mImagep; }
diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp
index 4dca87652da..ed62abe4ef9 100644
--- a/indra/newview/llvograss.cpp
+++ b/indra/newview/llvograss.cpp
@@ -476,6 +476,7 @@ void LLVOGrass::getGeometry(S32 idx,
 								LLStrider<LLVector3>& normalsp, 
 								LLStrider<LLVector2>& texcoordsp,
 								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<LLColor4U>& emissivep,
 								LLStrider<U16>& indicesp)
 {
 	if(!mNumBlades)//stop rendering grass
@@ -708,7 +709,11 @@ void LLGrassPartition::getGeometry(LLSpatialGroup* group)
 		facep->setIndicesIndex(index_count);
 		facep->setVertexBuffer(buffer);
 		facep->setPoolType(LLDrawPool::POOL_ALPHA);
-		object->getGeometry(facep->getTEOffset(), verticesp, normalsp, texcoordsp, colorsp, indicesp);
+
+		//dummy parameter (unused by this implementation)
+		LLStrider<LLColor4U> emissivep;
+
+		object->getGeometry(facep->getTEOffset(), verticesp, normalsp, texcoordsp, colorsp, emissivep, indicesp);
 		
 		vertex_count += facep->getGeomCount();
 		index_count += facep->getIndicesCount();
diff --git a/indra/newview/llvograss.h b/indra/newview/llvograss.h
index b9835b8802a..1fe9990ceab 100644
--- a/indra/newview/llvograss.h
+++ b/indra/newview/llvograss.h
@@ -63,6 +63,7 @@ class LLVOGrass : public LLAlphaObject
 								LLStrider<LLVector3>& normalsp, 
 								LLStrider<LLVector2>& texcoordsp,
 								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<LLColor4U>& emissivep,
 								LLStrider<U16>& indicesp);
 
 	void updateFaceSize(S32 idx) { }
diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp
index 1a9769f09dc..53d67347d17 100644
--- a/indra/newview/llvopartgroup.cpp
+++ b/indra/newview/llvopartgroup.cpp
@@ -49,17 +49,11 @@ const F32 MAX_PART_LIFETIME = 120.f;
 extern U64 gFrameTime;
 
 LLPointer<LLVertexBuffer> LLVOPartGroup::sVB = NULL;
-S32 LLVOPartGroup::sVBSlotFree[];
-S32* LLVOPartGroup::sVBSlotCursor = NULL;
+S32 LLVOPartGroup::sVBSlotCursor = 0;
 
 void LLVOPartGroup::initClass()
 {
-	for (S32 i = 0; i < LL_MAX_PARTICLE_COUNT; ++i)
-	{
-		sVBSlotFree[i] = i;
-	}
-
-	sVBSlotCursor = sVBSlotFree;
+	
 }
 
 //static
@@ -122,36 +116,33 @@ void LLVOPartGroup::destroyGL()
 //static
 S32 LLVOPartGroup::findAvailableVBSlot()
 {
-	if (sVBSlotCursor >= sVBSlotFree+LL_MAX_PARTICLE_COUNT)
+	if (sVBSlotCursor >= LL_MAX_PARTICLE_COUNT)
 	{ //no more available slots
 		return -1;
 	}
 
-	S32 ret = *sVBSlotCursor;
-	sVBSlotCursor++;
-
-	return ret;
+	return sVBSlotCursor++;
 }
 
 bool ll_is_part_idx_allocated(S32 idx, S32* start, S32* end)
 {
-	while (start < end)
+	/*while (start < end)
 	{
 		if (*start == idx)
 		{ //not allocated (in free list)
 			return false;
 		}
 		++start;
-	}
+	}*/
 
 	//allocated (not in free list)
-	return true;
+	return false;
 }
 
 //static
 void LLVOPartGroup::freeVBSlot(S32 idx)
 {
-	llassert(idx < LL_MAX_PARTICLE_COUNT && idx >= 0);
+	/*llassert(idx < LL_MAX_PARTICLE_COUNT && idx >= 0);
 	llassert(sVBSlotCursor > sVBSlotFree);
 	llassert(ll_is_part_idx_allocated(idx, sVBSlotCursor, sVBSlotFree+LL_MAX_PARTICLE_COUNT));
 
@@ -159,7 +150,7 @@ void LLVOPartGroup::freeVBSlot(S32 idx)
 	{
 		sVBSlotCursor--;
 		*sVBSlotCursor = idx;
-	}
+	}*/
 }
 
 LLVOPartGroup::LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
@@ -189,6 +180,7 @@ F32 LLVOPartGroup::getBinRadius()
 void LLVOPartGroup::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
 {		
 	const LLVector3& pos_agent = getPositionAgent();
+
 	newMin.load3( (pos_agent - mScale).mV);
 	newMax.load3( (pos_agent + mScale).mV);
 	LLVector4a pos;
@@ -273,6 +265,16 @@ F32 LLVOPartGroup::getPartSize(S32 idx)
 	return 0.f;
 }
 
+void LLVOPartGroup::getBlendFunc(S32 idx, U32& src, U32& dst)
+{
+	if (idx < (S32) mViewerPartGroupp->mParticles.size())
+	{
+		LLViewerPart* part = mViewerPartGroupp->mParticles[idx];
+		src = part->mBlendFuncSource;
+		dst = part->mBlendFuncDest;
+	}
+}
+
 LLVector3 LLVOPartGroup::getCameraPosition() const
 {
 	return gAgentCamera.getCameraPositionAgent();
@@ -332,13 +334,42 @@ BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
 	mDepth = 0.f;
 	S32 i = 0 ;
 	LLVector3 camera_agent = getCameraPosition();
+	
+	F32 max_scale = 0.f;
+
+
 	for (i = 0 ; i < (S32)mViewerPartGroupp->mParticles.size(); i++)
 	{
 		const LLViewerPart *part = mViewerPartGroupp->mParticles[i];
 
+
+		//remember the largest particle
+		max_scale = llmax(max_scale, part->mScale.mV[0], part->mScale.mV[1]);
+
+		if (part->mFlags & LLPartData::LL_PART_RIBBON_MASK)
+		{ //include ribbon segment length in scale
+			const LLVector3* pos_agent = NULL;
+			if (part->mParent)
+			{
+				pos_agent = &(part->mParent->mPosAgent);
+			}
+			else if (part->mPartSourcep.notNull())
+			{
+				pos_agent = &(part->mPartSourcep->mPosAgent);
+			}
+
+			if (pos_agent)
+			{
+				F32 dist = (*pos_agent-part->mPosAgent).length();
+
+				max_scale = llmax(max_scale, dist);
+			}
+		}
+
 		LLVector3 part_pos_agent(part->mPosAgent);
 		LLVector3 at(part_pos_agent - camera_agent);
 
+		
 		F32 camera_dist_squared = at.lengthSquared();
 		F32 inv_camera_dist_squared;
 		if (camera_dist_squared > 1.f)
@@ -411,6 +442,9 @@ BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
 		facep->setSize(0, 0);
 	}
 
+	//record max scale (used to stretch bounding box for visibility culling)
+	mScale.set(max_scale, max_scale, max_scale);
+
 	mDrawable->movePartition();
 	LLPipeline::sCompiles++;
 	return TRUE;
@@ -478,74 +512,129 @@ BOOL LLVOPartGroup::lineSegmentIntersect(const LLVector3& start, const LLVector3
 void LLVOPartGroup::getGeometry(const LLViewerPart& part,
 								LLStrider<LLVector4a>& verticesp)
 {
-	LLVector4a part_pos_agent;
-	part_pos_agent.load3(part.mPosAgent.mV);
-	LLVector4a camera_agent;
-	camera_agent.load3(getCameraPosition().mV); 
-	LLVector4a at;
-	at.setSub(part_pos_agent, camera_agent);
-	LLVector4a up(0, 0, 1);
-	LLVector4a right;
-
-	right.setCross3(at, up);
-	right.normalize3fast();
-	up.setCross3(right, at);
-	up.normalize3fast();
-
-	if (part.mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK)
+	if (part.mFlags & LLPartData::LL_PART_RIBBON_MASK)
 	{
-		LLVector4a normvel;
-		normvel.load3(part.mVelocity.mV);
-		normvel.normalize3fast();
-		LLVector2 up_fracs;
-		up_fracs.mV[0] = normvel.dot3(right).getF32();
-		up_fracs.mV[1] = normvel.dot3(up).getF32();
-		up_fracs.normalize();
-		LLVector4a new_up;
-		LLVector4a new_right;
-
-		//new_up = up_fracs.mV[0] * right + up_fracs.mV[1]*up;
-		LLVector4a t = right;
-		t.mul(up_fracs.mV[0]);
-		new_up = up;
-		new_up.mul(up_fracs.mV[1]);
-		new_up.add(t);
-
-		//new_right = up_fracs.mV[1] * right - up_fracs.mV[0]*up;
-		t = right;
-		t.mul(up_fracs.mV[1]);
-		new_right = up;
-		new_right.mul(up_fracs.mV[0]);
-		t.sub(new_right);
-
-		up = new_up;
-		right = t;
-		up.normalize3fast();
-		right.normalize3fast();
+		LLVector4a axis, pos, paxis, ppos;
+		F32 scale, pscale;
+
+		pos.load3(part.mPosAgent.mV);
+		axis.load3(part.mAxis.mV);
+		scale = part.mScale.mV[0];
+		
+		if (part.mParent)
+		{
+			ppos.load3(part.mParent->mPosAgent.mV);
+			paxis.load3(part.mParent->mAxis.mV);
+			pscale = part.mParent->mScale.mV[0];
+		}
+		else
+		{ //use source object as position
+			
+			if (part.mPartSourcep->mSourceObjectp.notNull())
+			{
+				LLVector3 v = LLVector3(0,0,1);
+				v *= part.mPartSourcep->mSourceObjectp->getRenderRotation();
+				paxis.load3(v.mV);
+				ppos.load3(part.mPartSourcep->mPosAgent.mV);
+				pscale = part.mStartScale.mV[0];
+			}
+			else
+			{ //no source object, no parent, nothing to draw
+				ppos = pos;
+				pscale = scale;
+				paxis = axis;
+			}
+		}
+
+		LLVector4a p0, p1, p2, p3;
+
+		scale *= 0.5f;
+		pscale *= 0.5f;
+
+		axis.mul(scale);
+		paxis.mul(pscale);
+
+		p0.setAdd(pos, axis);
+		p1.setSub(pos,axis);
+		p2.setAdd(ppos, paxis);
+		p3.setSub(ppos, paxis);
+
+		(*verticesp++) = p2;
+		(*verticesp++) = p3;
+		(*verticesp++) = p0;
+		(*verticesp++) = p1;
 	}
+	else
+	{
+		LLVector4a part_pos_agent;
+		part_pos_agent.load3(part.mPosAgent.mV);
+		LLVector4a camera_agent;
+		camera_agent.load3(getCameraPosition().mV); 
+		LLVector4a at;
+		at.setSub(part_pos_agent, camera_agent);
+		LLVector4a up(0, 0, 1);
+		LLVector4a right;
+
+		right.setCross3(at, up);
+		right.normalize3fast();
+		up.setCross3(right, at);
+		up.normalize3fast();
 
-	right.mul(0.5f*part.mScale.mV[0]);
-	up.mul(0.5f*part.mScale.mV[1]);
+		if (part.mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK)
+		{
+			LLVector4a normvel;
+			normvel.load3(part.mVelocity.mV);
+			normvel.normalize3fast();
+			LLVector2 up_fracs;
+			up_fracs.mV[0] = normvel.dot3(right).getF32();
+			up_fracs.mV[1] = normvel.dot3(up).getF32();
+			up_fracs.normalize();
+			LLVector4a new_up;
+			LLVector4a new_right;
+
+			//new_up = up_fracs.mV[0] * right + up_fracs.mV[1]*up;
+			LLVector4a t = right;
+			t.mul(up_fracs.mV[0]);
+			new_up = up;
+			new_up.mul(up_fracs.mV[1]);
+			new_up.add(t);
+
+			//new_right = up_fracs.mV[1] * right - up_fracs.mV[0]*up;
+			t = right;
+			t.mul(up_fracs.mV[1]);
+			new_right = up;
+			new_right.mul(up_fracs.mV[0]);
+			t.sub(new_right);
+
+			up = new_up;
+			right = t;
+			up.normalize3fast();
+			right.normalize3fast();
+		}
 
+		right.mul(0.5f*part.mScale.mV[0]);
+		up.mul(0.5f*part.mScale.mV[1]);
 
-	//HACK -- the verticesp->mV[3] = 0.f here are to set the texture index to 0 (particles don't use texture batching, maybe they should)
-	// this works because there is actually a 4th float stored after the vertex position which is used as a texture index
-	// also, somebody please VECTORIZE THIS
 
-	LLVector4a ppapu;
-	LLVector4a ppamu;
+		//HACK -- the verticesp->mV[3] = 0.f here are to set the texture index to 0 (particles don't use texture batching, maybe they should)
+		// this works because there is actually a 4th float stored after the vertex position which is used as a texture index
+		// also, somebody please VECTORIZE THIS
 
-	ppapu.setAdd(part_pos_agent, up);
-	ppamu.setSub(part_pos_agent, up);
+		LLVector4a ppapu;
+		LLVector4a ppamu;
 
-	verticesp->setSub(ppapu, right);
-	(*verticesp++).getF32ptr()[3] = 0.f;
-	verticesp->setSub(ppamu, right);
-	(*verticesp++).getF32ptr()[3] = 0.f;
-	verticesp->setAdd(ppapu, right);
-	(*verticesp++).getF32ptr()[3] = 0.f;
-	verticesp->setAdd(ppamu, right);
-	(*verticesp++).getF32ptr()[3] = 0.f;
+		ppapu.setAdd(part_pos_agent, up);
+		ppamu.setSub(part_pos_agent, up);
+
+		verticesp->setSub(ppapu, right);
+		(*verticesp++).getF32ptr()[3] = 0.f;
+		verticesp->setSub(ppamu, right);
+		(*verticesp++).getF32ptr()[3] = 0.f;
+		verticesp->setAdd(ppapu, right);
+		(*verticesp++).getF32ptr()[3] = 0.f;
+		verticesp->setAdd(ppamu, right);
+		(*verticesp++).getF32ptr()[3] = 0.f;
+	}
 }
 
 
@@ -555,6 +644,7 @@ void LLVOPartGroup::getGeometry(S32 idx,
 								LLStrider<LLVector3>& normalsp, 
 								LLStrider<LLVector2>& texcoordsp,
 								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<LLColor4U>& emissivep,
 								LLStrider<U16>& indicesp)
 {
 	if (idx >= (S32) mViewerPartGroupp->mParticles.size())
@@ -566,10 +656,40 @@ void LLVOPartGroup::getGeometry(S32 idx,
 
 	getGeometry(part, verticesp);
 
-	*colorsp++ = part.mColor;
-	*colorsp++ = part.mColor;
-	*colorsp++ = part.mColor;
-	*colorsp++ = part.mColor;
+	LLColor4U pcolor;
+	LLColor4U color = part.mColor;
+
+	LLColor4U pglow;
+
+	if (part.mFlags & LLPartData::LL_PART_RIBBON_MASK)
+	{ //make sure color blends properly
+		if (part.mParent)
+		{
+			pglow = part.mParent->mGlow;
+			pcolor = part.mParent->mColor;
+		}
+		else 
+		{
+			pglow = LLColor4U(0, 0, 0, (U8) (255.f*part.mStartGlow));
+			pcolor = part.mStartColor;
+		}
+	}
+	else
+	{
+		pglow = part.mGlow;
+		pcolor = color;
+	}
+
+	*colorsp++ = pcolor;
+	*colorsp++ = pcolor;
+	*colorsp++ = color;
+	*colorsp++ = color;
+
+	*emissivep++ = pglow;
+	*emissivep++ = pglow;
+	*emissivep++ = part.mGlow;
+	*emissivep++ = part.mGlow;
+
 
 	if (!(part.mFlags & LLPartData::LL_PART_EMISSIVE_MASK))
 	{ //not fullbright, needs normal
@@ -712,10 +832,13 @@ void LLParticlePartition::getGeometry(LLSpatialGroup* group)
 	LLStrider<LLVector3> normalsp;
 	LLStrider<LLVector2> texcoordsp;
 	LLStrider<LLColor4U> colorsp;
+	LLStrider<LLColor4U> emissivep;
 
 	buffer->getVertexStrider(verticesp);
 	buffer->getNormalStrider(normalsp);
 	buffer->getColorStrider(colorsp);
+	buffer->getEmissiveStrider(emissivep);
+
 	
 	LLSpatialGroup::drawmap_elem_t& draw_vec = group->mDrawMap[mRenderPass];	
 
@@ -724,7 +847,7 @@ void LLParticlePartition::getGeometry(LLSpatialGroup* group)
 		LLFace* facep = *i;
 		LLAlphaObject* object = (LLAlphaObject*) facep->getViewerObject();
 
-		if (!facep->isState(LLFace::PARTICLE))
+		//if (!facep->isState(LLFace::PARTICLE))
 		{ //set the indices of this face
 			S32 idx = LLVOPartGroup::findAvailableVBSlot();
 			if (idx >= 0)
@@ -733,7 +856,7 @@ void LLParticlePartition::getGeometry(LLSpatialGroup* group)
 				facep->setIndicesIndex(idx*6);
 				facep->setVertexBuffer(LLVOPartGroup::sVB);
 				facep->setPoolType(LLDrawPool::POOL_ALPHA);
-				facep->setState(LLFace::PARTICLE);
+				//facep->setState(LLFace::PARTICLE);
 			}
 			else
 			{
@@ -748,8 +871,9 @@ void LLParticlePartition::getGeometry(LLSpatialGroup* group)
 		LLStrider<LLVector3> cur_norm = normalsp + geom_idx;
 		LLStrider<LLVector2> cur_tc = texcoordsp + geom_idx;
 		LLStrider<LLColor4U> cur_col = colorsp + geom_idx;
+		LLStrider<LLColor4U> cur_glow = emissivep + geom_idx;
 
-		object->getGeometry(facep->getTEOffset(), cur_vert, cur_norm, cur_tc, cur_col, cur_idx);
+		object->getGeometry(facep->getTEOffset(), cur_vert, cur_norm, cur_tc, cur_col, cur_glow, cur_idx);
 		
 		llassert(facep->getGeomCount() == 4);
 		llassert(facep->getIndicesCount() == 6);
@@ -765,9 +889,16 @@ void LLParticlePartition::getGeometry(LLSpatialGroup* group)
 
 		bool batched = false;
 	
+		U32 bf_src = LLRender::BF_SOURCE_ALPHA;
+		U32 bf_dst = LLRender::BF_ONE_MINUS_SOURCE_ALPHA;
+
+		object->getBlendFunc(facep->getTEOffset(), bf_src, bf_dst);
+
 		if (idx >= 0 &&
 			draw_vec[idx]->mTexture == facep->getTexture() &&
-			draw_vec[idx]->mFullbright == fullbright)
+			draw_vec[idx]->mFullbright == fullbright &&
+			draw_vec[idx]->mBlendFuncDst == bf_dst &&
+			draw_vec[idx]->mBlendFuncSrc == bf_src)
 		{
 			if (draw_vec[idx]->mEnd == facep->getGeomIndex()-1)
 			{
@@ -799,6 +930,8 @@ void LLParticlePartition::getGeometry(LLSpatialGroup* group)
 			info->mExtents[0] = group->mObjectExtents[0];
 			info->mExtents[1] = group->mObjectExtents[1];
 			info->mVSize = vsize;
+			info->mBlendFuncDst = bf_dst;
+			info->mBlendFuncSrc = bf_src;
 			draw_vec.push_back(info);
 			//for alpha sorting
 			facep->setDrawInfo(info);
diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h
index ce05a0282ef..3c2e0344a21 100644
--- a/indra/newview/llvopartgroup.h
+++ b/indra/newview/llvopartgroup.h
@@ -42,8 +42,7 @@ class LLVOPartGroup : public LLAlphaObject
 
 	//vertex buffer for holding all particles
 	static LLPointer<LLVertexBuffer> sVB;
-	static S32 sVBSlotFree[LL_MAX_PARTICLE_COUNT];
-	static S32* sVBSlotCursor;
+	static S32 sVBSlotCursor;
 
 	static void initClass();
 	static void restoreGL();
@@ -57,6 +56,7 @@ class LLVOPartGroup : public LLAlphaObject
 							LLVertexBuffer::MAP_NORMAL |
 							LLVertexBuffer::MAP_TEXCOORD0 |
 							LLVertexBuffer::MAP_COLOR |
+							LLVertexBuffer::MAP_EMISSIVE |
 							LLVertexBuffer::MAP_TEXTURE_INDEX
 	};
 
@@ -91,10 +91,12 @@ class LLVOPartGroup : public LLAlphaObject
 								LLStrider<LLVector3>& normalsp, 
 								LLStrider<LLVector2>& texcoordsp,
 								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<LLColor4U>& emissivep,
 								LLStrider<U16>& indicesp);
 
 	void updateFaceSize(S32 idx) { }
 	F32 getPartSize(S32 idx);
+	void getBlendFunc(S32 idx, U32& src, U32& dst);
 	LLUUID getPartOwner(S32 idx);
 	LLUUID getPartSource(S32 idx);
 
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 597fb035268..0df0e653ae2 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -384,7 +384,6 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
 	}
 	else
 	{
-		// CORY TO DO: Figure out how to get the value here
 		if (update_type != OUT_TERSE_IMPROVED)
 		{
 			LLVolumeParams volume_params;
@@ -453,6 +452,11 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
 				mFaceMappingChanged = TRUE;
 				mTexAnimMode = 0;
 			}
+
+			if (value & 0x400)
+			{ //particle system (new)
+				unpackParticleSource(*dp, mOwnerID, false);
+			}
 		}
 		else
 		{
-- 
GitLab