diff options
Diffstat (limited to 'indra/llmath')
| -rw-r--r-- | indra/llmath/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | indra/llmath/llcalc.cpp | 145 | ||||
| -rw-r--r-- | indra/llmath/llcalc.h | 83 | ||||
| -rw-r--r-- | indra/llmath/llcalcparser.cpp | 46 | ||||
| -rw-r--r-- | indra/llmath/llcalcparser.h | 174 | ||||
| -rw-r--r-- | indra/llmath/lloctree.h | 11 | ||||
| -rw-r--r-- | indra/llmath/llvolume.cpp | 527 | ||||
| -rw-r--r-- | indra/llmath/llvolume.h | 8 | 
8 files changed, 932 insertions, 66 deletions
| diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 9dadad7dd3..cd100cdf9f 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -12,6 +12,8 @@ include_directories(  set(llmath_SOURCE_FILES      llbbox.cpp      llbboxlocal.cpp +    llcalc.cpp +    llcalcparser.cpp      llcamera.cpp      llcoordframe.cpp      llline.cpp @@ -46,6 +48,8 @@ set(llmath_HEADER_FILES      coordframe.h      llbbox.h      llbboxlocal.h +    llcalc.h +    llcalcparser.h      llcamera.h      llcoord.h      llcoordframe.h diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp new file mode 100644 index 0000000000..597d0815fb --- /dev/null +++ b/indra/llmath/llcalc.cpp @@ -0,0 +1,145 @@ +/* + *  LLCalc.cpp + *  SecondLife + * + *  Created by Aimee Walton on 28/09/2008. + *  Copyright 2008 Aimee Walton. + * + */ + +#include "linden_common.h" + +#include "llcalc.h" + +#include "llcalcparser.h" +#include "llmath.h" + + +// Variable names for use in the build floater +const char* LLCalc::X_POS = "PX"; +const char* LLCalc::Y_POS = "PY"; +const char* LLCalc::Z_POS = "PZ"; +const char* LLCalc::X_SCALE = "SX"; +const char* LLCalc::Y_SCALE = "SY"; +const char* LLCalc::Z_SCALE = "SZ"; +const char* LLCalc::X_ROT = "RX"; +const char* LLCalc::Y_ROT = "RY"; +const char* LLCalc::Z_ROT = "RZ"; +const char* LLCalc::HOLLOW = "HLW"; +const char* LLCalc::CUT_BEGIN = "CB"; +const char* LLCalc::CUT_END = "CE"; +const char* LLCalc::PATH_BEGIN = "PB"; +const char* LLCalc::PATH_END = "PE"; +const char* LLCalc::TWIST_BEGIN = "TB"; +const char* LLCalc::TWIST_END = "TE"; +const char* LLCalc::X_SHEAR = "SHX"; +const char* LLCalc::Y_SHEAR = "SHY"; +const char* LLCalc::X_TAPER = "TPX"; +const char* LLCalc::Y_TAPER = "TPY"; +const char* LLCalc::RADIUS_OFFSET = "ROF"; +const char* LLCalc::REVOLUTIONS = "REV"; +const char* LLCalc::SKEW = "SKW"; +const char* LLCalc::X_HOLE = "HLX"; +const char* LLCalc::Y_HOLE = "HLY"; +const char* LLCalc::TEX_U_SCALE = "TSU"; +const char* LLCalc::TEX_V_SCALE = "TSV"; +const char* LLCalc::TEX_U_OFFSET = "TOU"; +const char* LLCalc::TEX_V_OFFSET = "TOV"; +const char* LLCalc::TEX_ROTATION = "TROT"; +const char* LLCalc::TEX_TRANSPARENCY = "TRNS"; +const char* LLCalc::TEX_GLOW = "GLOW"; + + +LLCalc* LLCalc::sInstance = NULL; + +LLCalc::LLCalc() : mLastErrorPos(0) +{ +	// Init table of constants +	mConstants["PI"] = F_PI; +	mConstants["TWO_PI"] = F_TWO_PI; +	mConstants["PI_BY_TWO"] = F_PI_BY_TWO; +	mConstants["SQRT_TWO_PI"] = F_SQRT_TWO_PI; +	mConstants["SQRT2"] = F_SQRT2; +	mConstants["SQRT3"] = F_SQRT3; +	mConstants["DEG_TO_RAD"] = DEG_TO_RAD; +	mConstants["RAD_TO_DEG"] = RAD_TO_DEG; +	mConstants["GRAVITY"] = GRAVITY; +} + +LLCalc::~LLCalc() +{ +} + +//static +void LLCalc::cleanUp() +{ +	delete sInstance; +	sInstance = NULL; +} + +//static +LLCalc* LLCalc::getInstance() +{ +    if (!sInstance)	sInstance = new LLCalc(); +	return sInstance; +} + +void LLCalc::setVar(const std::string& name, const F32& value) +{ +	mVariables[name] = value; +} + +void LLCalc::clearVar(const std::string& name) +{ +	mVariables.erase(name); +} + +void LLCalc::clearAllVariables() +{ +	mVariables.clear(); +} + +/* +void LLCalc::updateVariables(LLSD& vars) +{ +	LLSD::map_iterator cIt = vars.beginMap(); +	for(; cIt != vars.endMap(); cIt++) +	{ +		setVar(cIt->first, (F32)(LLSD::Real)cIt->second); +	} +} +*/ + +bool LLCalc::evalString(const std::string& expression, F32& result) +{ +	std::string expr_upper = expression; +	LLStringUtil::toUpper(expr_upper); +	 +	LLCalcParser calc(result, &mConstants, &mVariables); + +	mLastErrorPos = 0; +	std::string::iterator start = expr_upper.begin(); + 	parse_info<std::string::iterator> info; +	 +	try +	{ +		info = parse(start, expr_upper.end(), calc, space_p); +		lldebugs << "Math expression: " << expression << " = " << result << llendl; +	} +	catch(parser_error<std::string, std::string::iterator> &e) +	{ +		mLastErrorPos = e.where - expr_upper.begin(); +		 +		llinfos << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << llendl; +		return false; +	} +	 +	if (!info.full) +	{ +		mLastErrorPos = info.stop - expr_upper.begin(); +		llinfos << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << llendl; +		return false; +	} +	 +	return true; +} diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h new file mode 100644 index 0000000000..cc31950cb6 --- /dev/null +++ b/indra/llmath/llcalc.h @@ -0,0 +1,83 @@ +/* + *  LLCalc.h + *  SecondLife + * + *  Created by Aimee Walton on 28/09/2008. + *  Copyright 2008 Aimee Walton. + * + */ + +#ifndef LL_CALC_H +#define LL_CALC_H + +#include <map> +#include <string> + +class LLCalc +{ +public: +	LLCalc(); +	~LLCalc(); + +	// Variable name constants +	static const char* X_POS; +	static const char* Y_POS; +	static const char* Z_POS; +	static const char* X_SCALE; +	static const char* Y_SCALE; +	static const char* Z_SCALE; +	static const char* X_ROT; +	static const char* Y_ROT; +	static const char* Z_ROT; +	static const char* HOLLOW; +	static const char* CUT_BEGIN; +	static const char* CUT_END; +	static const char* PATH_BEGIN; +	static const char* PATH_END; +	static const char* TWIST_BEGIN; +	static const char* TWIST_END; +	static const char* X_SHEAR; +	static const char* Y_SHEAR; +	static const char* X_TAPER; +	static const char* Y_TAPER; +	static const char* RADIUS_OFFSET; +	static const char* REVOLUTIONS; +	static const char* SKEW; +	static const char* X_HOLE; +	static const char* Y_HOLE; +	static const char* TEX_U_SCALE; +	static const char* TEX_V_SCALE; +	static const char* TEX_U_OFFSET; +	static const char* TEX_V_OFFSET; +	static const char* TEX_ROTATION; +	static const char* TEX_TRANSPARENCY; +	static const char* TEX_GLOW; + +	void	setVar(const std::string& name, const F32& value); +	void	clearVar(const std::string& name); +	void	clearAllVariables(); +//	void	updateVariables(LLSD& vars); + +	bool	evalString(const std::string& expression, F32& result); +	std::string::size_type	getLastErrorPos()	{ return mLastErrorPos; } +	 +	static LLCalc* getInstance(); +	static void cleanUp(); + +	typedef	std::map<std::string, F32> calc_map_t; +	 +private: +	std::string::size_type	mLastErrorPos; +	 +	calc_map_t	mConstants; +	calc_map_t	mVariables; +	 +	// *TODO: Add support for storing user defined variables, and stored functions. +	//	Will need UI work, and a means to save them between sessions. +//	calc_map_t mUserVariables; +	 +	// "There shall be only one" +	static LLCalc*	sInstance; +}; + +#endif // LL_CALC_H diff --git a/indra/llmath/llcalcparser.cpp b/indra/llmath/llcalcparser.cpp new file mode 100644 index 0000000000..fd55376fa9 --- /dev/null +++ b/indra/llmath/llcalcparser.cpp @@ -0,0 +1,46 @@ +/* + *  LLCalcParser.cpp + *  SecondLife + * + *  Created by Aimee Walton on 28/09/2008. + *  Copyright 2008 Aimee Walton. + * + */ + +#include "linden_common.h" + +#include "llcalcparser.h" +using namespace boost::spirit::classic; + +F32 LLCalcParser::lookup(const std::string::iterator& start, const std::string::iterator& end) const +{ +	LLCalc::calc_map_t::iterator iter; + +	std::string name(start, end); +	 +	if (mConstants) +	{ +		iter = mConstants->find(name); +		if (iter != mConstants->end()) +		{ +			return (*iter).second; +		} +	} +	else +	{ +		// This should never happen! +		throw_(end, std::string("Missing constants table")); +	} +	 +	if (mVariables) +	{ +		iter = mVariables->find(name); +		if (iter != mVariables->end()) +		{ +			return (*iter).second; +		} +	} +	 +	throw_(end, std::string("Unknown symbol " + name)); +	return 0.f; +} diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h new file mode 100644 index 0000000000..600e173661 --- /dev/null +++ b/indra/llmath/llcalcparser.h @@ -0,0 +1,174 @@ +/* + *  LLCalcParser.h + *  SecondLife + * + *  Created by Aimee Walton on 28/09/2008. + *  Copyright 2008 Aimee Walton. + * + */ + +#ifndef LL_CALCPARSER_H +#define LL_CALCPARSER_H + +#include <boost/spirit/include/classic_attribute.hpp> +#include <boost/spirit/include/classic_core.hpp> +#include <boost/spirit/include/classic_error_handling.hpp> +#include <boost/spirit/include/classic_position_iterator.hpp> +#include <boost/spirit/include/phoenix1_binders.hpp> +#include <boost/spirit/include/classic_symbols.hpp> +using namespace boost::spirit::classic; + +#include "llcalc.h" +#include "llmath.h" + +struct LLCalcParser : grammar<LLCalcParser> +{ +	LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) : +		mResult(result), mConstants(constants), mVariables(vars) {}; +	 +	struct value_closure : closure<value_closure, F32> +	{ +		member1 value; +	}; +	 +	template <typename ScannerT> +	struct definition +	{ +		// Rule declarations +		rule<ScannerT> statement, identifier; +		rule<ScannerT, value_closure::context_t> expression, term, +			power,  +			unary_expr,  +			factor,  +			unary_func,  +			binary_func, +			group; + +		// start() should return the starting symbol +		rule<ScannerT> const& start() const { return statement; } +		 +		definition(LLCalcParser const& self) +		{ +			using namespace phoenix; +			 +			assertion<std::string> assert_domain("Domain error"); +//			assertion<std::string> assert_symbol("Unknown symbol"); +			assertion<std::string> assert_syntax("Syntax error"); +			 +			identifier = +				lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')] +			; +			 +			group = +				'(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')')) +			; + +			unary_func = +				((str_p("SIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sin)(self,arg1)]) | +				 (str_p("COS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_cos)(self,arg1)]) | +				 (str_p("TAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_tan)(self,arg1)]) | +				 (str_p("ASIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_asin)(self,arg1)]) | +				 (str_p("ACOS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_acos)(self,arg1)]) | +				 (str_p("ATAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_atan)(self,arg1)]) | +				 (str_p("SQRT") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sqrt)(self,arg1)]) | +				 (str_p("LOG") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_log)(self,arg1)]) | +				 (str_p("EXP") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_exp)(self,arg1)]) | +				 (str_p("ABS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_fabs)(self,arg1)]) | +				 (str_p("FLR") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_floor)(self,arg1)]) | +				 (str_p("CEIL") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_ceil)(self,arg1)]) +				) >> assert_syntax(ch_p(')')) +			; +			 +			binary_func = +				((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >> +				  expression[binary_func.value = bind(&LLCalcParser::_atan2)(self, binary_func.value, arg1)]) | +				 (str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >>  +				  expression[binary_func.value = bind(&LLCalcParser::_min)(self, binary_func.value, arg1)]) | +				 (str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >>  +				  expression[binary_func.value = bind(&LLCalcParser::_max)(self, binary_func.value, arg1)]) +				) >> assert_syntax(ch_p(')')) +			; +			 +			// *TODO: Localisation of the decimal point? +			// Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate +			// for the current locale. However to do that here could clash with using +			// the comma as a separator when passing arguments to functions. +			factor = +				(ureal_p[factor.value = arg1] | +				 group[factor.value = arg1] | +				 unary_func[factor.value = arg1] | +				 binary_func[factor.value = arg1] | +				 // Lookup throws an Unknown Symbol error if it is unknown, while this works fine, +				 // would be "neater" to handle symbol lookup from here with an assertive parser. +//				 constants_p[factor.value = arg1]| +				 identifier[factor.value = bind(&LLCalcParser::lookup)(self, arg1, arg2)] +				) >> +				// Detect and throw math errors. +				assert_domain(eps_p(bind(&LLCalcParser::checkNaN)(self, factor.value))) +			; + +			unary_expr = +				!ch_p('+') >> factor[unary_expr.value = arg1] | +				'-' >> factor[unary_expr.value = -arg1] +			; +			 +			power = +				unary_expr[power.value = arg1] >> +				*('^' >> assert_syntax(unary_expr[power.value = bind(&powf)(power.value, arg1)])) +			; +			 +			term = +				power[term.value = arg1] >> +				*(('*' >> assert_syntax(power[term.value *= arg1])) | +				  ('/' >> assert_syntax(power[term.value /= arg1])) | +				  ('%' >> assert_syntax(power[term.value = bind(&fmodf)(term.value, arg1)])) +				) +			; +			 +			expression = +				assert_syntax(term[expression.value = arg1]) >> +				*(('+' >> assert_syntax(term[expression.value += arg1])) | +				  ('-' >> assert_syntax(term[expression.value -= arg1])) +				) +			; + +			statement = +				!ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p) +			; +		} +	}; +	 +private: +	// Member functions for semantic actions +	F32	lookup(const std::string::iterator&, const std::string::iterator&) const; +	F32 _min(const F32& a, const F32& b) const { return llmin(a, b); } +	F32 _max(const F32& a, const F32& b) const { return llmax(a, b); } +	 +	bool checkNaN(const F32& a) const { return !llisnan(a); } +	 +	//FIX* non ambigious function fix making SIN() work for calc -Cryogenic Blitz +	F32 _sin(const F32& a) const { return sin(DEG_TO_RAD * a); } +	F32 _cos(const F32& a) const { return cos(DEG_TO_RAD * a); } +	F32 _tan(const F32& a) const { return tan(DEG_TO_RAD * a); } +	F32 _asin(const F32& a) const { return asin(a * RAD_TO_DEG); } +	F32 _acos(const F32& a) const { return acos(a * RAD_TO_DEG); } +	F32 _atan(const F32& a) const { return atan(a * RAD_TO_DEG); } +	F32 _sqrt(const F32& a) const { return sqrt(a); } +	F32 _log(const F32& a) const { return log(a); } +	F32 _exp(const F32& a) const { return exp(a); } +	F32 _fabs(const F32& a) const { return fabs(a); } +	F32 _floor(const F32& a) const { return llfloor(a); } +	F32 _ceil(const F32& a) const { return llceil(a); } + +	F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); } + + + +	LLCalc::calc_map_t* mConstants; +	LLCalc::calc_map_t* mVariables; +//	LLCalc::calc_map_t* mUserVariables; +	 +	F32&		mResult; +}; + +#endif // LL_CALCPARSER_H diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h index 179ad7ecfd..e5ca47da69 100644 --- a/indra/llmath/lloctree.h +++ b/indra/llmath/lloctree.h @@ -35,12 +35,14 @@  #define OCT_ERRS LL_WARNS("OctreeErrors") -#define LL_OCTREE_PARANOIA_CHECK 0 + +extern U32 gOctreeMaxCapacity; +/*#define LL_OCTREE_PARANOIA_CHECK 0  #if LL_DARWIN  #define LL_OCTREE_MAX_CAPACITY 32  #else  #define LL_OCTREE_MAX_CAPACITY 128 -#endif +#endif*/  template <class T> class LLOctreeNode; @@ -74,6 +76,7 @@ template <class T>  class LLOctreeNode : public LLTreeNode<T>  {  public: +  	typedef LLOctreeTraveler<T>									oct_traveler;  	typedef LLTreeTraveler<T>									tree_traveler;  	typedef typename std::set<LLPointer<T> >					element_list; @@ -294,8 +297,8 @@ public:  		//is it here?  		if (isInside(data->getPositionGroup()))  		{ -			if (((getElementCount() < LL_OCTREE_MAX_CAPACITY && contains(data->getBinRadius())) || -				(data->getBinRadius() > getSize()[0] &&	parent && parent->getElementCount() >= LL_OCTREE_MAX_CAPACITY)))  +			if ((getElementCount() < gOctreeMaxCapacity && contains(data->getBinRadius()) || +				(data->getBinRadius() > getSize()[0] &&	parent && parent->getElementCount() >= gOctreeMaxCapacity)))   			{ //it belongs here  #if LL_OCTREE_PARANOIA_CHECK  				//if this is a redundant insertion, error out (should never happen) diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index c504215ee5..2893e746e9 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -32,6 +32,7 @@  #if !LL_WINDOWS  #include <stdint.h>  #endif +#include <cmath>  #include "llerror.h"  #include "llmemtype.h" @@ -100,7 +101,7 @@ void assert_aligned(void* ptr, uintptr_t alignment)  	uintptr_t t = (uintptr_t) ptr;  	if (t%alignment != 0)  	{ -		llerrs << "WTF?" << llendl; +		llerrs << "Alignment check failed." << llendl;  	}  #endif  } @@ -361,7 +362,7 @@ public:  		}  		else  		{ -			llerrs << "WTF? Empty leaf" << llendl; +			llerrs << "Empty leaf" << llendl;  		}  		for (S32 i = 0; i < branch->getChildCount(); ++i) @@ -416,6 +417,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 +737,117 @@ 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 +	F32 hollow = params.getHollow(); + +	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 +1309,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 +1512,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)  { @@ -2128,11 +2380,16 @@ bool LLVolumeFace::VertexData::operator==(const LLVolumeFace::VertexData& rhs)co  bool LLVolumeFace::VertexData::compareNormal(const LLVolumeFace::VertexData& rhs, F32 angle_cutoff) const  {  	bool retval = false; -	if (rhs.mData[POSITION].equals3(mData[POSITION]) && rhs.mTexCoord == mTexCoord) + +	const F32 epsilon = 0.00001f; + +	if (rhs.mData[POSITION].equals3(mData[POSITION], epsilon) &&  +		fabs(rhs.mTexCoord[0]-mTexCoord[0]) < epsilon && +		fabs(rhs.mTexCoord[1]-mTexCoord[1]) < epsilon)  	{  		if (angle_cutoff > 1.f)  		{ -			retval = (mData[NORMAL].equals3(rhs.mData[NORMAL])); +			retval = (mData[NORMAL].equals3(rhs.mData[NORMAL], epsilon));  		}  		else  		{ @@ -2159,27 +2416,41 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)  		U32 face_count = mdl.size();  		if (face_count == 0) -		{ -			llerrs << "WTF?" << llendl; +		{ //no faces unpacked, treat as failed decode +			llwarns << "found no faces!" << llendl; +			return false;  		}  		mVolumeFaces.resize(face_count);  		for (U32 i = 0; i < face_count; ++i)  		{ +			LLVolumeFace& face = mVolumeFaces[i]; + +			if (mdl[i].has("NoGeometry")) +			{ //face has no geometry, continue +				face.resizeIndices(3); +				face.resizeVertices(1); +				memset(face.mPositions, 0, sizeof(LLVector4a)); +				memset(face.mNormals, 0, sizeof(LLVector4a)); +				memset(face.mTexCoords, 0, sizeof(LLVector2)); +				memset(face.mIndices, 0, sizeof(U16)*3); +				continue; +			} +  			LLSD::Binary pos = mdl[i]["Position"];  			LLSD::Binary norm = mdl[i]["Normal"];  			LLSD::Binary tc = mdl[i]["TexCoord0"];  			LLSD::Binary idx = mdl[i]["TriangleList"]; -			LLVolumeFace& face = mVolumeFaces[i]; +			  			//copy out indices  			face.resizeIndices(idx.size()/2);  			if (idx.empty() || face.mNumIndices < 3)  			{ //why is there an empty index list? -				llerrs <<"WTF?" << llendl; +				llwarns <<"Empty face present!" << llendl;  				continue;  			} @@ -2234,38 +2505,52 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)  			}  			{ -				U16* n = (U16*) &(norm[0]); -				for (U32 j = 0; j < num_verts; ++j) +				if (!norm.empty())  				{ -					norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]); -					norm_out->div(65535.f); -					norm_out->mul(2.f); -					norm_out->sub(1.f); -					norm_out++; -					n += 3; +					U16* n = (U16*) &(norm[0]); +					for (U32 j = 0; j < num_verts; ++j) +					{ +						norm_out->set((F32) n[0], (F32) n[1], (F32) n[2]); +						norm_out->div(65535.f); +						norm_out->mul(2.f); +						norm_out->sub(1.f); +						norm_out++; +						n += 3; +					} +				} +				else +				{ +					memset(norm_out, 0, sizeof(LLVector4a)*num_verts);  				}  			}  			{ -				U16* t = (U16*) &(tc[0]); -				for (U32 j = 0; j < num_verts; j+=2) +				if (!tc.empty())  				{ -					if (j < num_verts-1) -					{ -						tc_out->set((F32) t[0], (F32) t[1], (F32) t[2], (F32) t[3]); -					} -					else +					U16* t = (U16*) &(tc[0]); +					for (U32 j = 0; j < num_verts; j+=2)  					{ -						tc_out->set((F32) t[0], (F32) t[1], 0.f, 0.f); -					} +						if (j < num_verts-1) +						{ +							tc_out->set((F32) t[0], (F32) t[1], (F32) t[2], (F32) t[3]); +						} +						else +						{ +							tc_out->set((F32) t[0], (F32) t[1], 0.f, 0.f); +						} -					t += 4; +						t += 4; -					tc_out->div(65535.f); -					tc_out->mul(tc_range); -					tc_out->add(min_tc4); +						tc_out->div(65535.f); +						tc_out->mul(tc_range); +						tc_out->add(min_tc4); -					tc_out++; +						tc_out++; +					} +				} +				else +				{ +					memset(tc_out, 0, sizeof(LLVector2)*num_verts);  				}  			} @@ -2377,14 +2662,39 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)  			LLVector4a& min = face.mExtents[0];  			LLVector4a& max = face.mExtents[1]; -			min.clear(); -			max.clear(); -			min = max = face.mPositions[0]; - -			for (S32 i = 1; i < face.mNumVertices; ++i) +			if (face.mNumVertices < 3) +			{ //empty face, use a dummy 1cm (at 1m scale) bounding box +				min.splat(-0.005f); +				max.splat(0.005f); +			} +			else  			{ -				min.setMin(min, face.mPositions[i]); -				max.setMax(max, face.mPositions[i]); +				min = max = face.mPositions[0]; + +				for (S32 i = 1; i < face.mNumVertices; ++i) +				{ +					min.setMin(min, face.mPositions[i]); +					max.setMax(max, face.mPositions[i]); +				} + +				if (face.mTexCoords) +				{ +					LLVector2& min_tc = face.mTexCoordExtents[0]; +					LLVector2& max_tc = face.mTexCoordExtents[1]; + +					min_tc = face.mTexCoords[0]; +					max_tc = face.mTexCoords[0]; + +					for (U32 j = 1; j < face.mNumVertices; ++j) +					{ +						update_min_max(min_tc, max_tc, face.mTexCoords[j]); +					} +				} +				else +				{ +					face.mTexCoordExtents[0].set(0,0); +					face.mTexCoordExtents[1].set(1,1); +				}  			}  		}  	} @@ -2464,11 +2774,13 @@ void LLVolume::makeTetrahedron()  	n[2] = cv[2].getNormal();  	n += 3; -	tc[0] = cv[0].mTexCoord; -	tc[1] = cv[1].mTexCoord; -	tc[2] = cv[2].mTexCoord; -	tc += 3; - +	if(tc) +	{ +		tc[0] = cv[0].mTexCoord; +		tc[1] = cv[1].mTexCoord; +		tc[2] = cv[2].mTexCoord; +		tc += 3; +	}  	//side 2  	cv[0].setPosition(p[3]); @@ -2487,11 +2799,14 @@ void LLVolume::makeTetrahedron()  	n[2] = cv[2].getNormal();  	n += 3; -	tc[0] = cv[0].mTexCoord; -	tc[1] = cv[1].mTexCoord; -	tc[2] = cv[2].mTexCoord; -	tc += 3; -	 +	if(tc) +	{ +		tc[0] = cv[0].mTexCoord; +		tc[1] = cv[1].mTexCoord; +		tc[2] = cv[2].mTexCoord; +		tc += 3; +	} +  	//side 3  	cv[0].setPosition(p[3]);  	cv[1].setPosition(p[1]); @@ -2509,10 +2824,13 @@ void LLVolume::makeTetrahedron()  	n[2] = cv[2].getNormal();  	n += 3; -	tc[0] = cv[0].mTexCoord; -	tc[1] = cv[1].mTexCoord; -	tc[2] = cv[2].mTexCoord; -	tc += 3; +	if(tc) +	{ +		tc[0] = cv[0].mTexCoord; +		tc[1] = cv[1].mTexCoord; +		tc[2] = cv[2].mTexCoord; +		tc += 3; +	}  	//side 4  	cv[0].setPosition(p[2]); @@ -2531,10 +2849,13 @@ void LLVolume::makeTetrahedron()  	n[2] = cv[2].getNormal();  	n += 3; -	tc[0] = cv[0].mTexCoord; -	tc[1] = cv[1].mTexCoord; -	tc[2] = cv[2].mTexCoord; -	tc += 3; +	if(tc) +	{ +		tc[0] = cv[0].mTexCoord; +		tc[1] = cv[1].mTexCoord; +		tc[2] = cv[2].mTexCoord; +		tc += 3; +	}  	//set index buffer  	for (U16 i = 0; i < 12; i++) @@ -2980,7 +3301,11 @@ void LLVolume::sculpt(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components,  		// don't test lowest LOD to support legacy content DEV-33670  		if (mDetail > SCULPT_MIN_AREA_DETAIL)  		{ -			if (sculptGetSurfaceArea() < SCULPT_MIN_AREA) +			F32 area = sculptGetSurfaceArea(); + +			const F32 SCULPT_MAX_AREA = 384.f; + +			if (area < SCULPT_MIN_AREA || area > SCULPT_MAX_AREA)  			{  				data_is_empty = TRUE;  			} @@ -4064,6 +4389,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(); @@ -5220,6 +5562,8 @@ LLVolumeFace::LLVolumeFace() :  	mOctree(NULL)  {  	mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); +	mExtents[0].splat(-0.5f); +	mExtents[1].splat(0.5f);  	mCenter = mExtents+2;  } @@ -5280,7 +5624,16 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)  		LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) src.mPositions, vert_size);  		LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) src.mNormals, vert_size); -		LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) src.mTexCoords, tc_size); + +		if(src.mTexCoords) +		{ +			LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) src.mTexCoords, tc_size); +		} +		else +		{ +			ll_aligned_free_16(mTexCoords) ; +			mTexCoords = NULL ; +		}  		if (src.mBinormals) @@ -5402,8 +5755,23 @@ BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)  void LLVolumeFace::getVertexData(U16 index, LLVolumeFace::VertexData& cv)  {  	cv.setPosition(mPositions[index]); -	cv.setNormal(mNormals[index]); -	cv.mTexCoord = mTexCoords[index]; +	if (mNormals) +	{ +		cv.setNormal(mNormals[index]); +	} +	else +	{ +		cv.getNormal().clear(); +	} + +	if (mTexCoords) +	{ +		cv.mTexCoord = mTexCoords[index]; +	} +	else +	{ +		cv.mTexCoord.clear(); +	}  }  bool LLVolumeFace::VertexMapData::operator==(const LLVolumeFace::VertexData& rhs) const @@ -5433,7 +5801,10 @@ void LLVolumeFace::optimize(F32 angle_cutoff)  	LLVolumeFace new_face;  	//map of points to vector of vertices at that point -	VertexMapData::PointMap point_map; +	std::map<U64, std::vector<VertexMapData> > point_map; + +	LLVector4a range; +	range.setSub(mExtents[1],mExtents[0]);  	//remove redundant vertices  	for (U32 i = 0; i < mNumIndices; ++i) @@ -5444,7 +5815,19 @@ void LLVolumeFace::optimize(F32 angle_cutoff)  		getVertexData(index, cv);  		BOOL found = FALSE; -		VertexMapData::PointMap::iterator point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr())); + +		LLVector4a pos; +		pos.setSub(mPositions[index], mExtents[0]); +		pos.div(range); + +		U64 pos64 = 0; + +		pos64 = (U16) (pos[0]*65535); +		pos64 = pos64 | (((U64) (pos[1]*65535)) << 16); +		pos64 = pos64 | (((U64) (pos[2]*65535)) << 32); + +		std::map<U64, std::vector<VertexMapData> >::iterator point_iter = point_map.find(pos64); +		  		if (point_iter != point_map.end())  		{ //duplicate point might exist  			for (U32 j = 0; j < point_iter->second.size(); ++j) @@ -5476,11 +5859,26 @@ void LLVolumeFace::optimize(F32 angle_cutoff)  			}  			else  			{ -				point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d); +				point_map[pos64].push_back(d);  			}  		}  	} +	llassert(new_face.mNumIndices == mNumIndices); +	llassert(new_face.mNumVertices <= mNumVertices); + +	if (angle_cutoff > 1.f && !mNormals) +	{ +		ll_aligned_free_16(new_face.mNormals); +		new_face.mNormals = NULL; +	} + +	if (!mTexCoords) +	{ +		ll_aligned_free_16(new_face.mTexCoords); +		new_face.mTexCoords = NULL; +	} +  	swapData(new_face);  } @@ -5741,6 +6139,11 @@ void LLVolumeFace::cacheOptimize()  	LLVCacheLRU cache; +	if (mNumVertices < 3) +	{ //nothing to do +		return; +	} +  	//mapping of vertices to triangles and indices  	std::vector<LLVCacheVertexData> vertex_data; diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 01bfbd858b..f67f8f644d 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -690,6 +690,9 @@ public:  	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 @@ public:  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 @@ public:  	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 @@ public:  	// returns number of triangle indeces required for path/profile mesh  	S32 getNumTriangleIndices() const; +	static void getLoDTriangleCounts(const LLVolumeParams& params, S32* counts);  	S32 getNumTriangles() const; | 
