From 15235061e8a8f64dd94640d27eadfce23ccb76f6 Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Sat, 18 Jun 2011 01:02:03 -0500
Subject: SH-828 Fix for using uninitialized data when normals or texture
 coordinates are absent from collada file (can now upload meshes without
 normals or texture coordinates).

---
 indra/llmath/llvolume.cpp               | 141 ++++++++++---
 indra/llprimitive/llmodel.cpp           | 355 +++++++++++++++-----------------
 indra/newview/llfloatermodelpreview.cpp |  56 +++--
 3 files changed, 318 insertions(+), 234 deletions(-)

(limited to 'indra')

diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 8c81f27784..7c7c4306da 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -2379,11 +2379,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) && 
+		abs(rhs.mTexCoord[0]-mTexCoord[0]) < epsilon &&
+		abs(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
 		{
@@ -2499,38 +2504,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())
+				{
+					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
 				{
-					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;
+					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);
 				}
 			}
 
@@ -2656,6 +2675,25 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
 					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);
+				}
 			}
 		}
 	}
@@ -5696,8 +5734,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
@@ -5727,7 +5780,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)
@@ -5738,7 +5794,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)
@@ -5770,11 +5838,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);
 }
 
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index e0cfa07614..7c9dba9597 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -27,6 +27,7 @@
 #include "linden_common.h"
 
 #include "llmodel.h"
+#include "llmemory.h"
 #include "llconvexdecomposition.h"
 #include "llsdserialize.h"
 #include "llvector4a.h"
@@ -71,88 +72,10 @@ LLModel::~LLModel()
 	}
 }
 
-void load_face_from_dom_inputs(LLVolumeFace& face, const domInputLocalOffset_Array& inputs, U32 min_idx, U32 max_idx)
-{
-	for (U32 j = 0; j < inputs.getCount(); ++j)
-	{
-		if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0)
-		{ //found vertex array
-			const domURIFragmentType& uri = inputs[j]->getSource();
-			daeElementRef elem = uri.getElement();
-			domVertices* vertices = (domVertices*) elem.cast();
-
-			domInputLocal_Array& v_inp = vertices->getInput_array();
-			if (inputs[j]->getOffset() != 0)
-			{
-				llerrs << "Vertex array offset MUST be zero." << llendl;
-			}
-
-			for (U32 k = 0; k < v_inp.getCount(); ++k)
-			{
-				if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0)
-				{
-					const domURIFragmentType& uri = v_inp[k]->getSource();
-					
-					daeElementRef elem = uri.getElement();
-					domSource* src = (domSource*) elem.cast();
-
-					if (src->getTechnique_common()->getAccessor()->getStride() != 3)
-					{
-						llerrs << "Vertex array stride MUST be three." << llendl;
-					}
-
-					domListOfFloats& v = src->getFloat_array()->getValue();
-
-					LLVector4a min;
-					min.set(v[min_idx], v[min_idx+1], v[min_idx+2]);
-					LLVector4a max = min;
-
-					for (U32 j = min_idx; j <= max_idx; ++j)
-					{ //copy vertex array
-						face.mPositions[j-min_idx].set(v[j*3+0], v[j*3+1], v[j*3+2]);
-						update_min_max(min, max, face.mPositions[j-min_idx]);
-					}
-
-					face.mExtents[0] = min;
-					face.mExtents[1] = max;
-				}
-			}
-		}
-
-		if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0)
-		{
-			//found normal array for this triangle list
-			const domURIFragmentType& uri = inputs[j]->getSource();
-			daeElementRef elem = uri.getElement();
-			domSource* src = (domSource*) elem.cast();
-			domListOfFloats& n = src->getFloat_array()->getValue();
-			
-			for (U32 j = min_idx; j <= max_idx; ++j)
-			{
-				LLVector4a* norm = (LLVector4a*) face.mNormals + (j-min_idx);
-				norm->set(n[j*3+0], n[j*3+1], n[j*3+2]);
-				norm->normalize3();
-			}
-		}
-		else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0)
-		{ //found texCoords
-			const domURIFragmentType& uri = inputs[j]->getSource();
-			daeElementRef elem = uri.getElement();
-			domSource* src = (domSource*) elem.cast();
-			domListOfFloats& u = src->getFloat_array()->getValue();
-			
-			for (U32 j = min_idx; j <= max_idx; ++j)
-			{
-				face.mTexCoords[j-min_idx].setVec(u[j*2+0], u[j*2+1]);
-			}
-		}
-	}
-}
 
 bool get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S32& tc_offset, S32& norm_offset, S32 &idx_stride,
 					 domSource* &pos_source, domSource* &tc_source, domSource* &norm_source)
 {
-	
 	idx_stride = 0;
 
 	for (U32 j = 0; j < inputs.getCount(); ++j)
@@ -271,14 +194,13 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 			cv.mTexCoord.setVec(tc[idx[i+tc_offset]*2+0],
 								tc[idx[i+tc_offset]*2+1]);
 		}
-
+		
 		if (norm_source)
 		{
 			cv.setNormal(LLVector4a(n[idx[i+norm_offset]*3+0],
 								n[idx[i+norm_offset]*3+1],
 								n[idx[i+norm_offset]*3+2]));
 		}
-
 		
 		BOOL found = FALSE;
 			
@@ -329,10 +251,22 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 		{
 			face_list.push_back(face);
 			face_list.rbegin()->fillFromLegacyData(verts, indices);
+			LLVolumeFace& new_face = *face_list.rbegin();
+			if (!norm_source)
+			{
+				ll_aligned_free_16(new_face.mNormals);
+				new_face.mNormals = NULL;
+			}
+
+			if (!tc_source)
+			{
+				ll_aligned_free_16(face.mTexCoords);
+				new_face.mTexCoords = NULL;
+			}
+
 			face = LLVolumeFace();
 			point_map.clear();
 		}
-
 	}
 
 	if (!verts.empty())
@@ -348,6 +282,18 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
 		face_list.push_back(face);
 
 		face_list.rbegin()->fillFromLegacyData(verts, indices);
+		LLVolumeFace& new_face = *face_list.rbegin();
+		if (!norm_source)
+		{
+			ll_aligned_free_16(new_face.mNormals);
+			new_face.mNormals = NULL;
+		}
+
+		if (!tc_source)
+		{
+			ll_aligned_free_16(face.mTexCoords);
+			new_face.mTexCoords = NULL;
+		}
 	}
 
 	return LLModel::NO_ERRORS ;
@@ -433,14 +379,14 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 				cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0],
 									tc[idx[cur_idx+tc_offset]*2+1]);
 			}
-
+			
 			if (norm_source)
 			{
 				cv.getNormal().set(n[idx[cur_idx+norm_offset]*3+0],
 									n[idx[cur_idx+norm_offset]*3+1],
 									n[idx[cur_idx+norm_offset]*3+2]);
 			}
-
+			
 			cur_idx += idx_stride;
 			
 			BOOL found = FALSE;
@@ -524,6 +470,19 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 			{
 				face_list.push_back(face);
 				face_list.rbegin()->fillFromLegacyData(verts, indices);
+				LLVolumeFace& new_face = *face_list.rbegin();
+				if (!norm_source)
+				{
+					ll_aligned_free_16(new_face.mNormals);
+					new_face.mNormals = NULL;
+				}
+
+				if (!tc_source)
+				{
+					ll_aligned_free_16(face.mTexCoords);
+					new_face.mTexCoords = NULL;
+				}
+
 				face = LLVolumeFace();
 				verts.clear();
 				indices.clear();
@@ -540,10 +499,23 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
 		{
 			material = std::string(poly->getMaterial());
 		}
-		
+	
 		materials.push_back(material);
 		face_list.push_back(face);
 		face_list.rbegin()->fillFromLegacyData(verts, indices);
+
+		LLVolumeFace& new_face = *face_list.rbegin();
+		if (!norm_source)
+		{
+			ll_aligned_free_16(new_face.mNormals);
+			new_face.mNormals = NULL;
+		}
+
+		if (!tc_source)
+		{
+			ll_aligned_free_16(face.mTexCoords);
+			new_face.mTexCoords = NULL;
+		}
 	}
 
 	return LLModel::NO_ERRORS ;
@@ -557,7 +529,6 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac
 
 	const domInputLocalOffset_Array& inputs = poly->getInput_array();
 
-
 	S32 v_offset = -1;
 	S32 n_offset = -1;
 	S32 t_offset = -1;
@@ -662,15 +633,14 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac
 								n->get(n_idx+1),
 								n->get(n_idx+2));
 			}
-
+			
 			if (t)
 			{
 				U32 t_idx = idx[j*stride+t_offset]*2;
 				vert.mTexCoord.setVec(t->get(t_idx),
 								t->get(t_idx+1));								
 			}
-		
-			
+						
 			verts.push_back(vert);
 		}
 	}
@@ -733,6 +703,19 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac
 		materials.push_back(material);
 		face_list.push_back(face);
 		face_list.rbegin()->fillFromLegacyData(new_verts, indices);
+
+		LLVolumeFace& new_face = *face_list.rbegin();
+		if (!n)
+		{
+			ll_aligned_free_16(new_face.mNormals);
+			new_face.mNormals = NULL;
+		}
+
+		if (!t)
+		{
+			ll_aligned_free_16(face.mTexCoords);
+			new_face.mTexCoords = NULL;
+		}
 	}
 
 	return LLModel::NO_ERRORS ;
@@ -817,9 +800,9 @@ BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh)
 		
 		if (getNumVolumeFaces() > 0)
 		{
-			optimizeVolumeFaces();
 			normalizeVolumeFaces();
-
+			optimizeVolumeFaces();
+			
 			if (getNumVolumeFaces() > 0)
 			{
 				return TRUE;
@@ -853,81 +836,10 @@ void LLModel::offsetMesh( const LLVector3& pivotPoint )
 
 void LLModel::optimizeVolumeFaces()
 {
-#if 0 //VECTORIZE ?
-	for (std::vector<LLVolumeFace>::iterator iter = mVolumeFaces.begin(); iter != mVolumeFaces.end(); )
-	{
-		std::vector<LLVolumeFace>::iterator cur_iter = iter++;
-		LLVolumeFace& face = *cur_iter;
-
-		for (S32 i = 0; i < (S32) face.mNumIndices; i += 3)
-		{ //remove zero area triangles
-			U16 i0 = face.mIndices[i+0];
-			U16 i1 = face.mIndices[i+1];
-			U16 i2 = face.mIndices[i+2];
-
-			if (i0 == i1 || 
-				i1 == i2 || 
-				i0 == i2)
-			{ //duplicate index in triangle, remove triangle
-				face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3);
-				i -= 3;
-			}
-			else
-			{ 
-				LLVolumeFace::VertexData& v0 = face.mVertices[i0];
-				LLVolumeFace::VertexData& v1 = face.mVertices[i1];
-				LLVolumeFace::VertexData& v2 = face.mVertices[i2];
-
-				if (v0.mPosition == v1.mPosition ||
-					v1.mPosition == v2.mPosition ||
-					v2.mPosition == v0.mPosition)
-				{ //zero area triangle, delete
-					face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3);
-					i-=3;
-				}
-			}
-		}
-
-		//remove unreference vertices
-		std::vector<bool> ref;
-		ref.resize(face.mNumVertices);
-
-		for (U32 i = 0; i < ref.size(); ++i)
-		{
-			ref[i] = false;
-		}
-
-		for (U32 i = 0; i < face.mNumIndices; ++i)
-		{ 
-			ref[face.mIndices[i]] = true;
-		}
-
-		U32 unref_count = 0;
-		for (U32 i = 0; i < ref.size(); ++i)
-		{
-			if (!ref[i])
-			{
-				//vertex is unreferenced
-				face.mVertices.erase(face.mVertices.begin()+(i-unref_count));
-				U16 idx = (U16) (i-unref_count);
-
-				for (U32 j = 0; j < face.mNumIndices; ++j)
-				{ //decrement every index array value greater than idx
-					if (face.mIndices[j] > idx)
-					{
-						--face.mIndices[j];
-					}
-				}
-				++unref_count;
-			}
-		}
-
-		if (face.mVertices.empty() || face.mIndices.empty())
-		{ //face is empty, remove it
-			iter = mVolumeFaces.erase(cur_iter);
-		}
+	for (U32 i = 0; i < getNumVolumeFaces(); ++i)
+	{
+		mVolumeFaces[i].optimize();
 	}
-#endif
 }
 
 // Shrink the model to fit
@@ -962,6 +874,25 @@ void LLModel::normalizeVolumeFaces()
 
 			update_min_max(min, max, face.mExtents[0]);
 			update_min_max(min, max, face.mExtents[1]);
+
+			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);
+			}
 		}
 
 		// Now that we have the extents of the model
@@ -1029,8 +960,11 @@ void LLModel::normalizeVolumeFaces()
 			{
 			 	pos[j].add(trans);
 				pos[j].mul(scale);
-				norm[j].mul(inv_scale);
-				norm[j].normalize3();
+				if (norm && !norm[j].equals3(LLVector4a::getZero()))
+				{
+					norm[j].mul(inv_scale);
+					norm[j].normalize3();
+				}
 			}
 		}
 
@@ -1073,8 +1007,26 @@ void LLModel::setVolumeFaceData(
 	face.resizeIndices(num_indices);
 
 	LLVector4a::memcpyNonAliased16((F32*) face.mPositions, (F32*) pos.get(), num_verts*4*sizeof(F32));
-	LLVector4a::memcpyNonAliased16((F32*) face.mNormals, (F32*) norm.get(), num_verts*4*sizeof(F32));
-	LLVector4a::memcpyNonAliased16((F32*) face.mTexCoords, (F32*) tc.get(), num_verts*2*sizeof(F32));
+	if (norm.get())
+	{
+		LLVector4a::memcpyNonAliased16((F32*) face.mNormals, (F32*) norm.get(), num_verts*4*sizeof(F32));
+	}
+	else
+	{
+		ll_aligned_free_16(face.mNormals);
+		face.mNormals = NULL;
+	}
+
+	if (tc.get())
+	{
+		LLVector4a::memcpyNonAliased16((F32*) face.mTexCoords, (F32*) tc.get(), num_verts*2*sizeof(F32));
+	}
+	else
+	{
+		ll_aligned_free_16(face.mTexCoords);
+		face.mTexCoords = NULL;
+	}
+
 	U32 size = (num_indices*2+0xF)&~0xF;
 	LLVector4a::memcpyNonAliased16((F32*) face.mIndices, (F32*) ind.get(), size);
 }
@@ -1477,9 +1429,8 @@ LLSD LLModel::writeModel(
 				{ //for each vert
 		
 					F32* pos = face.mPositions[j].getF32ptr();
-					F32* norm = face.mNormals[j].getF32ptr();
-
-					//position + normal
+										
+					//position
 					for (U32 k = 0; k < 3; ++k)
 					{ //for each component
 						//convert to 16-bit normalized across domain
@@ -1489,29 +1440,41 @@ LLSD LLModel::writeModel(
 						//write to binary buffer
 						verts[vert_idx++] = buff[0];
 						verts[vert_idx++] = buff[1];
-						
-						//convert to 16-bit normalized
-						val = (U16) ((norm[k]+1.f)*0.5f*65535);
+					}
 
-						//write to binary buffer
-						normals[norm_idx++] = buff[0];
-						normals[norm_idx++] = buff[1];
+					if (face.mNormals)
+					{ //normals
+						F32* norm = face.mNormals[j].getF32ptr();
+
+						for (U32 k = 0; k < 3; ++k)
+						{ //for each component
+							//convert to 16-bit normalized
+							U16 val = (U16) ((norm[k]+1.f)*0.5f*65535);
+							U8* buff = (U8*) &val;
+
+							//write to binary buffer
+							normals[norm_idx++] = buff[0];
+							normals[norm_idx++] = buff[1];
+						}
 					}
 
+
 					F32* src_tc = (F32*) face.mTexCoords[j].mV;
 
 					//texcoord
-					for (U32 k = 0; k < 2; ++k)
-					{ //for each component
-						//convert to 16-bit normalized
-						U16 val = (U16) ((src_tc[k]-min_tc.mV[k])/tc_range.mV[k]*65535);
-
-						U8* buff = (U8*) &val;
-						//write to binary buffer
-						tc[tc_idx++] = buff[0];
-						tc[tc_idx++] = buff[1];
+					if (face.mTexCoords)
+					{
+						for (U32 k = 0; k < 2; ++k)
+						{ //for each component
+							//convert to 16-bit normalized
+							U16 val = (U16) ((src_tc[k]-min_tc.mV[k])/tc_range.mV[k]*65535);
+
+							U8* buff = (U8*) &val;
+							//write to binary buffer
+							tc[tc_idx++] = buff[0];
+							tc[tc_idx++] = buff[1];
+						}
 					}
-					
 				}
 
 				U32 idx_idx = 0;
@@ -1525,12 +1488,20 @@ LLSD LLModel::writeModel(
 				//write out face data
 				mdl[model_names[idx]][i]["PositionDomain"]["Min"] = min_pos.getValue();
 				mdl[model_names[idx]][i]["PositionDomain"]["Max"] = max_pos.getValue();
-				mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue();
-				mdl[model_names[idx]][i]["TexCoord0Domain"]["Max"] = max_tc.getValue();
-
 				mdl[model_names[idx]][i]["Position"] = verts;
-				mdl[model_names[idx]][i]["Normal"] = normals;
-				mdl[model_names[idx]][i]["TexCoord0"] = tc;
+				
+				if (face.mNormals)
+				{
+					mdl[model_names[idx]][i]["Normal"] = normals;
+				}
+
+				if (face.mTexCoords)
+				{
+					mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue();
+					mdl[model_names[idx]][i]["TexCoord0Domain"]["Max"] = max_tc.getValue();
+					mdl[model_names[idx]][i]["TexCoord0"] = tc;
+				}
+
 				mdl[model_names[idx]][i]["TriangleList"] = indices;
 
 				if (skinning)
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 866cc39eec..a4683b4874 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -3719,7 +3719,9 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
 			U32 tri_count = 0;
 			for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i)
 			{
-				mVertexBuffer[5][mdl][i]->setBuffer(type_mask);
+				LLVertexBuffer* buff = mVertexBuffer[5][mdl][i];
+				buff->setBuffer(type_mask & buff->getTypeMask());
+				
 				U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices();
 				if (num_indices > 2)
 				{
@@ -3841,6 +3843,8 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
 
 			for (GLint i = 0; i < patch_count; ++i)
 			{
+				type_mask = mVertexBuffer[5][base][i]->getTypeMask();
+
 				LLPointer<LLVertexBuffer> buff = new LLVertexBuffer(type_mask, 0);
 
 				if (sizes[i*2+1] > 0 && sizes[i*2] > 0)
@@ -3865,8 +3869,15 @@ void LLModelPreview::genLODs(S32 which_lod, U32 decimation, bool enforce_tri_lim
 				LLStrider<U16> index;
 
 				buff->getVertexStrider(pos);
-				buff->getNormalStrider(norm);
-				buff->getTexCoord0Strider(tc);
+				if (type_mask & LLVertexBuffer::MAP_NORMAL)
+				{
+					buff->getNormalStrider(norm);
+				}
+				if (type_mask & LLVertexBuffer::MAP_TEXCOORD0)
+				{
+					buff->getTexCoord0Strider(tc);
+				}
+
 				buff->getIndexStrider(index);
 
 				target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices());
@@ -4462,7 +4473,16 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
 
 			bool skinned = include_skin_weights && !mdl->mSkinWeights.empty();
 
-			U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
+			U32 mask = LLVertexBuffer::MAP_VERTEX;
+			
+			if (vf.mNormals)
+			{
+				mask |= LLVertexBuffer::MAP_NORMAL;
+			}
+			if (vf.mTexCoords)
+			{
+				mask |= LLVertexBuffer::MAP_TEXCOORD0;
+			}
 
 			if (skinned)
 			{
@@ -4480,8 +4500,6 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
 			LLStrider<LLVector4> weights_strider;
 
 			vb->getVertexStrider(vertex_strider);
-			vb->getNormalStrider(normal_strider);
-			vb->getTexCoord0Strider(tc_strider);
 			vb->getIndexStrider(index_strider);
 
 			if (skinned)
@@ -4490,8 +4508,18 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights)
 			}
 
 			LLVector4a::memcpyNonAliased16((F32*) vertex_strider.get(), (F32*) vf.mPositions, num_vertices*4*sizeof(F32));
-			LLVector4a::memcpyNonAliased16((F32*) tc_strider.get(), (F32*) vf.mTexCoords, num_vertices*2*sizeof(F32));
-			LLVector4a::memcpyNonAliased16((F32*) normal_strider.get(), (F32*) vf.mNormals, num_vertices*4*sizeof(F32));
+			
+			if (vf.mTexCoords)
+			{
+				vb->getTexCoord0Strider(tc_strider);
+				LLVector4a::memcpyNonAliased16((F32*) tc_strider.get(), (F32*) vf.mTexCoords, num_vertices*2*sizeof(F32));
+			}
+			
+			if (vf.mNormals)
+			{
+				vb->getNormalStrider(normal_strider);
+				LLVector4a::memcpyNonAliased16((F32*) normal_strider.get(), (F32*) vf.mNormals, num_vertices*4*sizeof(F32));
+			}
 
 			if (skinned)
 			{
@@ -4750,6 +4778,8 @@ BOOL LLModelPreview::render()
 	const F32 BRIGHTNESS = 0.9f;
 	gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS);
 
+	const U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0;
+
 	LLGLEnable normalize(GL_NORMALIZE);
 
 	if (!mBaseModel.empty() && mVertexBuffer[5].empty())
@@ -4798,8 +4828,8 @@ BOOL LLModelPreview::render()
 				for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i)
 				{
 					LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i];
-
-					buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+				
+					buffer->setBuffer(type_mask & buffer->getTypeMask());
 
 					if (textures)
 					{
@@ -4918,7 +4948,7 @@ BOOL LLModelPreview::render()
 						{
 							LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
 
-							buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+							buffer->setBuffer(type_mask & buffer->getTypeMask());
 
 							buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
 							gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
@@ -4984,7 +5014,7 @@ BOOL LLModelPreview::render()
 							{
 								LLVertexBuffer* buffer = mVertexBuffer[LLModel::LOD_PHYSICS][model][i];
 
-								buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+								buffer->setBuffer(type_mask & buffer->getTypeMask());
 
 								LLStrider<LLVector3> pos_strider; 
 								buffer->getVertexStrider(pos_strider, 0);
@@ -5109,7 +5139,7 @@ BOOL LLModelPreview::render()
 								position[j] = v;
 							}
 
-							buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0);
+							buffer->setBuffer(type_mask & buffer->getTypeMask());
 							glColor4fv(instance.mMaterial[i].mDiffuseColor.mV);
 							gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
 							buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0);
-- 
cgit v1.2.3