From de5f93ca52f207380baca6895b499cb7fcffead8 Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Thu, 2 Jun 2011 22:58:57 -0500
Subject: [PATCH] SH-1732 Add streaming cost calculation for mesh objects.

---
 indra/llmath/llvolume.cpp    | 273 +++++++++++++++++++++++++++++++++++
 indra/llmath/llvolume.h      |   8 +
 indra/newview/llvovolume.cpp |  20 ++-
 3 files changed, 297 insertions(+), 4 deletions(-)

diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index fbda824179f..fa94a7934e6 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -416,6 +416,70 @@ LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, BO
 	return face;
 }
 
+//static
+S32 LLProfile::getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
+{ // this is basically LLProfile::genNGon stripped down to only the operations that influence the number of points
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+	S32 np = 0;
+
+	// Generate an n-sided "circular" path.
+	// 0 is (1,0), and we go counter-clockwise along a circular path from there.
+	F32 t, t_step, t_first, t_fraction;
+	
+	F32 begin  = params.getBegin();
+	F32 end    = params.getEnd();
+
+	t_step = 1.0f / sides;
+	
+	t_first = floor(begin * sides) / (F32)sides;
+
+	// pt1 is the first point on the fractional face.
+	// Starting t and ang values for the first face
+	t = t_first;
+	
+	// Increment to the next point.
+	// pt2 is the end point on the fractional face
+	t += t_step;
+	
+	t_fraction = (begin - t_first)*sides;
+
+	// Only use if it's not almost exactly on an edge.
+	if (t_fraction < 0.9999f)
+	{
+		np++;
+	}
+
+	// There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
+	while (t < end)
+	{
+		// Iterate through all the integer steps of t.
+		np++;
+
+		t += t_step;
+	}
+
+	t_fraction = (end - (t - t_step))*sides;
+
+	// Find the fraction that we need to add to the end point.
+	t_fraction = (end - (t - t_step))*sides;
+	if (t_fraction > 0.0001f)
+	{
+		np++;
+	}
+
+	// If we're sliced, the profile is open.
+	if ((end - begin)*ang_scale < 0.99f)
+	{
+		if (params.getHollow() <= 0)
+		{
+			// put center point if not hollow.
+			np++;
+		}
+	}
+	
+	return np;
+}
+
 // What is the bevel parameter used for? - DJS 04/05/02
 // Bevel parameter is currently unused but presumedly would support
 // filleted and chamfered corners
@@ -672,6 +736,122 @@ LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, BOOL flat, F3
 	return face;
 }
 
+//static
+S32 LLProfile::getNumPoints(const LLProfileParams& params, BOOL path_open,F32 detail, S32 split,
+						 BOOL is_sculpted, S32 sculpt_size)
+{ // this is basically LLProfile::generate stripped down to only operations that influence the number of points
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+	
+	if (detail < MIN_LOD)
+	{
+		detail = MIN_LOD;
+	}
+
+	// Generate the face data
+	S32 i;
+	F32 begin = params.getBegin();
+	F32 end = params.getEnd();
+	F32 hollow = params.getHollow();
+
+	S32 face_num = 0;
+
+	S32 np = 0;
+
+	switch (params.getCurveType() & LL_PCODE_PROFILE_MASK)
+	{
+	case LL_PCODE_PROFILE_SQUARE:
+		{
+			np = getNumNGonPoints(params, 4,-0.375, 0, 1, split);
+		
+			if (hollow)
+			{
+				np *= 2;
+			}
+		}
+		break;
+	case  LL_PCODE_PROFILE_ISOTRI:
+	case  LL_PCODE_PROFILE_RIGHTTRI:
+	case  LL_PCODE_PROFILE_EQUALTRI:
+		{
+			np = getNumNGonPoints(params, 3,0, 0, 1, split);
+						
+			if (hollow)
+			{
+				np *= 2;
+			}
+		}
+		break;
+	case LL_PCODE_PROFILE_CIRCLE:
+		{
+			// If this has a square hollow, we should adjust the
+			// number of faces a bit so that the geometry lines up.
+			U8 hole_type=0;
+			F32 circle_detail = MIN_DETAIL_FACES * detail;
+			if (hollow)
+			{
+				hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
+				if (hole_type == LL_PCODE_HOLE_SQUARE)
+				{
+					// Snap to the next multiple of four sides,
+					// so that corners line up.
+					circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
+				}
+			}
+
+			S32 sides = (S32)circle_detail;
+
+			if (is_sculpted)
+				sides = sculpt_size;
+			
+			np = getNumNGonPoints(params, sides);
+			
+			if (hollow)
+			{
+				np *= 2;
+			}
+		}
+		break;
+	case LL_PCODE_PROFILE_CIRCLE_HALF:
+		{
+			// If this has a square hollow, we should adjust the
+			// number of faces a bit so that the geometry lines up.
+			U8 hole_type=0;
+			// Number of faces is cut in half because it's only a half-circle.
+			F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
+			if (hollow)
+			{
+				hole_type = params.getCurveType() & LL_PCODE_HOLE_MASK;
+				if (hole_type == LL_PCODE_HOLE_SQUARE)
+				{
+					// Snap to the next multiple of four sides (div 2),
+					// so that corners line up.
+					circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
+				}
+			}
+			np = getNumNGonPoints(params, llfloor(circle_detail), 0.5f, 0.f, 0.5f);
+			
+			if (hollow)
+			{
+				np *= 2;
+			}
+
+			// Special case for openness of sphere
+			if ((params.getEnd() - params.getBegin()) < 1.f)
+			{
+			}
+			else if (!hollow)
+			{
+				np++;
+			}
+		}
+		break;
+	default:
+	   break;
+	};
+
+	
+	return np;
+}
 
 
 BOOL LLProfile::generate(const LLProfileParams& params, BOOL path_open,F32 detail, S32 split,
@@ -1133,6 +1313,32 @@ LLPath::~LLPath()
 {
 }
 
+S32 LLPath::getNumNGonPoints(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
+{ //this is basically LLPath::genNGon stripped down to only operations that influence the number of points added
+	S32 ret = 0;
+
+	F32 step= 1.0f / sides;
+	F32 t	= params.getBegin();
+	ret = 1;
+	
+	t+=step;
+
+	// Snap to a quantized parameter, so that cut does not
+	// affect most sample points.
+	t = ((S32)(t * sides)) / (F32)sides;
+
+	// Run through the non-cut dependent points.
+	while (t < params.getEnd())
+	{
+		ret++;
+		t+=step;
+	}
+
+	ret++;
+
+	return ret;
+}
+
 void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
 {
 	// Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane.
@@ -1310,6 +1516,56 @@ const LLVector2 LLPathParams::getEndScale() const
 	return end_scale;
 }
 
+S32 LLPath::getNumPoints(const LLPathParams& params, F32 detail)
+{ // this is basically LLPath::generate stripped down to only the operations that influence the number of points
+	LLMemType m1(LLMemType::MTYPE_VOLUME);
+	
+	if (detail < MIN_LOD)
+	{
+		detail = MIN_LOD;
+	}
+
+	S32 np = 2; // hardcode for line
+
+	// Is this 0xf0 mask really necessary?  DK 03/02/05
+
+	switch (params.getCurveType() & 0xf0)
+	{
+	default:
+	case LL_PCODE_PATH_LINE:
+		{
+			// Take the begin/end twist into account for detail.
+			np    = llfloor(fabs(params.getTwistBegin() - params.getTwist()) * 3.5f * (detail-0.5f)) + 2;
+		}
+		break;
+
+	case LL_PCODE_PATH_CIRCLE:
+		{
+			// Increase the detail as the revolutions and twist increase.
+			F32 twist_mag = fabs(params.getTwistBegin() - params.getTwist());
+
+			S32 sides = (S32)llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * params.getRevolutions());
+
+			np = sides;
+		}
+		break;
+
+	case LL_PCODE_PATH_CIRCLE2:
+		{
+			//genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
+			np = getNumNGonPoints(params, llfloor(MIN_DETAIL_FACES * detail));
+		}
+		break;
+
+	case LL_PCODE_PATH_TEST:
+
+		np     = 5;
+		break;
+	};
+
+	return np;
+}
+
 BOOL LLPath::generate(const LLPathParams& params, F32 detail, S32 split,
 					  BOOL is_sculpted, S32 sculpt_size)
 {
@@ -4065,6 +4321,23 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 	return index;
 }
 
+void LLVolume::getLoDTriangleCounts(const LLVolumeParams& params, S32* counts)
+{ //attempt to approximate the number of triangles that will result from generating a volume LoD set for the 
+	//supplied LLVolumeParams -- inaccurate, but a close enough approximation for determining streaming cost
+	F32 detail[] = {1.f, 1.5f, 2.5f, 4.f};	
+	for (S32 i = 0; i < 4; i++)
+	{
+		S32 count = 0;
+		S32 path_points = LLPath::getNumPoints(params.getPathParams(), detail[i]);
+		S32 profile_points = LLProfile::getNumPoints(params.getProfileParams(), false, detail[i]);
+
+		count = (profile_points-1)*2*(path_points-1);
+		count += profile_points*2;
+
+		counts[i] = count;
+	}
+}
+
 S32 LLVolume::getNumTriangleIndices() const
 {
 	BOOL profile_open = getProfile().isOpen();
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index 01bfbd858be..f67f8f644df 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -690,6 +690,9 @@ class LLProfile
 	BOOL isFlat(S32 face) const							{ return (mFaces[face].mCount == 2); }
 	BOOL isOpen() const									{ return mOpen; }
 	void setDirty()										{ mDirty     = TRUE; }
+
+	static S32 getNumPoints(const LLProfileParams& params, BOOL path_open, F32 detail = 1.0f, S32 split = 0,
+				  BOOL is_sculpted = FALSE, S32 sculpt_size = 0);
 	BOOL generate(const LLProfileParams& params, BOOL path_open, F32 detail = 1.0f, S32 split = 0,
 				  BOOL is_sculpted = FALSE, S32 sculpt_size = 0);
 	BOOL isConcave() const								{ return mConcave; }
@@ -714,6 +717,7 @@ class LLProfile
 
 protected:
 	void genNormals(const LLProfileParams& params);
+	static S32 getNumNGonPoints(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0);
 	void genNGon(const LLProfileParams& params, S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0);
 
 	Face* addHole(const LLProfileParams& params, BOOL flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split = 0);
@@ -756,6 +760,9 @@ class LLPath
 
 	virtual ~LLPath();
 
+	static S32 getNumPoints(const LLPathParams& params, F32 detail);
+	static S32 getNumNGonPoints(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f);
+
 	void genNGon(const LLPathParams& params, S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f);
 	virtual BOOL generate(const LLPathParams& params, F32 detail=1.0f, S32 split = 0,
 						  BOOL is_sculpted = FALSE, S32 sculpt_size = 0);
@@ -981,6 +988,7 @@ class LLVolume : public LLRefCount
 
 	// returns number of triangle indeces required for path/profile mesh
 	S32 getNumTriangleIndices() const;
+	static void getLoDTriangleCounts(const LLVolumeParams& params, S32* counts);
 
 	S32 getNumTriangles() const;
 
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index c917a5fd9de..39e555f7812 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -3096,16 +3096,28 @@ U32 LLVOVolume::getRenderCost(std::set<LLUUID> &textures) const
 
 F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes)
 {
+	F32 radius = getScale().length();
+
 	if (isMesh())
 	{	
 		LLSD& header = gMeshRepo.getMeshHeader(getVolume()->getParams().getSculptID());
 
-		F32 radius = getScale().length();
-		
 		return LLMeshRepository::getStreamingCost(header, radius, bytes, visible_bytes, mLOD);
 	}
-		
-	return 0.f;
+	else
+	{
+		LLVolume* volume = getVolume();
+		S32 counts[4];
+		LLVolume::getLoDTriangleCounts(volume->getParams(), counts);
+
+		LLSD header;
+		header["lowest_lod"]["size"] = counts[0] * 10;
+		header["low_lod"]["size"] = counts[1] * 10;
+		header["medium_lod"]["size"] = counts[2] * 10;
+		header["high_lod"]["size"] = counts[3] * 10;
+
+		return LLMeshRepository::getStreamingCost(header, radius);
+	}	
 }
 
 U32 LLVOVolume::getTriangleCount()
-- 
GitLab