diff options
| -rw-r--r-- | indra/llprimitive/lldaeloader.cpp | 2326 | ||||
| -rw-r--r-- | indra/llprimitive/lldaeloader.h | 103 | 
2 files changed, 2429 insertions, 0 deletions
diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp new file mode 100644 index 0000000000..740ecf31c7 --- /dev/null +++ b/indra/llprimitive/lldaeloader.cpp @@ -0,0 +1,2326 @@ +/** + * @file lldaeloader.cpp + * @brief LLDAELoader class implementation + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#if LL_MSVC +#pragma warning (disable : 4263) +#pragma warning (disable : 4264) +#endif +#include "dae.h" +#include "dom/domAsset.h" +#include "dom/domBind_material.h" +#include "dom/domCOLLADA.h" +#include "dom/domConstants.h" +#include "dom/domController.h" +#include "dom/domEffect.h" +#include "dom/domGeometry.h" +#include "dom/domInstance_geometry.h" +#include "dom/domInstance_material.h" +#include "dom/domInstance_node.h" +#include "dom/domInstance_effect.h" +#include "dom/domMaterial.h" +#include "dom/domMatrix.h" +#include "dom/domNode.h" +#include "dom/domProfile_COMMON.h" +#include "dom/domRotate.h" +#include "dom/domScale.h" +#include "dom/domTranslate.h" +#include "dom/domVisual_scene.h" +#if LL_MSVC +#pragma warning (default : 4263) +#pragma warning (default : 4264) +#endif + +#include "lldaeloader.h" +#include "llsdserialize.h" +#include "lljoint.h" + +#include "glh/glh_linear.h" +#include "llmatrix4a.h" + +std::string colladaVersion[VERSIONTYPE_COUNT+1] =  +{ +	"1.4.0", +	"1.4.1", +	"Unsupported" +}; + +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) +	{ +		idx_stride = llmax((S32) inputs[j]->getOffset(), idx_stride); + +		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(); +			if ( !vertices ) +			{ +				return false; +			} + +			domInputLocal_Array& v_inp = vertices->getInput_array(); + + +			for (U32 k = 0; k < v_inp.getCount(); ++k) +			{ +				if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) +				{ +					pos_offset = inputs[j]->getOffset(); + +					const domURIFragmentType& uri = v_inp[k]->getSource(); +					daeElementRef elem = uri.getElement(); +					pos_source = (domSource*) elem.cast(); +				} + +				if (strcmp(COMMON_PROFILE_INPUT_NORMAL, v_inp[k]->getSemantic()) == 0) +				{ +					norm_offset = inputs[j]->getOffset(); + +					const domURIFragmentType& uri = v_inp[k]->getSource(); +					daeElementRef elem = uri.getElement(); +					norm_source = (domSource*) elem.cast(); +				} +			} +		} + +		if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0) +		{ +			//found normal array for this triangle list +			norm_offset = inputs[j]->getOffset(); +			const domURIFragmentType& uri = inputs[j]->getSource(); +			daeElementRef elem = uri.getElement(); +			norm_source = (domSource*) elem.cast(); +		} +		else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0) +		{ //found texCoords +			tc_offset = inputs[j]->getOffset(); +			const domURIFragmentType& uri = inputs[j]->getSource(); +			daeElementRef elem = uri.getElement(); +			tc_source = (domSource*) elem.cast(); +		} +	} + +	idx_stride += 1; + +	return true; +} + +LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domTrianglesRef& tri) +{ +	LLVolumeFace face; +	std::vector<LLVolumeFace::VertexData> verts; +	std::vector<U16> indices; +	 +	const domInputLocalOffset_Array& inputs = tri->getInput_array(); + +	S32 pos_offset = -1; +	S32 tc_offset = -1; +	S32 norm_offset = -1; + +	domSource* pos_source = NULL; +	domSource* tc_source = NULL; +	domSource* norm_source = NULL; + +	S32 idx_stride = 0; + +	if ( !get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source) || !pos_source ) +	{ +		return LLModel::BAD_ELEMENT; +	} + +	 +	domPRef p = tri->getP(); +	domListOfUInts& idx = p->getValue(); +	 +	domListOfFloats  dummy ; +	domListOfFloats& v = pos_source ? pos_source->getFloat_array()->getValue() : dummy ; +	domListOfFloats& tc = tc_source ? tc_source->getFloat_array()->getValue() : dummy ; +	domListOfFloats& n = norm_source ? norm_source->getFloat_array()->getValue() : dummy ; + +	if (pos_source) +	{ +		face.mExtents[0].set(v[0], v[1], v[2]); +		face.mExtents[1].set(v[0], v[1], v[2]); +	} +	 +	LLVolumeFace::VertexMapData::PointMap point_map; +	 +	for (U32 i = 0; i < idx.getCount(); i += idx_stride) +	{ +		LLVolumeFace::VertexData cv; +		if (pos_source) +		{ +			cv.setPosition(LLVector4a(v[idx[i+pos_offset]*3+0], +								v[idx[i+pos_offset]*3+1], +								v[idx[i+pos_offset]*3+2])); +		} + +		if (tc_source) +		{ +			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; +			 +		LLVolumeFace::VertexMapData::PointMap::iterator point_iter; +		point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr())); +		 +		if (point_iter != point_map.end()) +		{ +			for (U32 j = 0; j < point_iter->second.size(); ++j) +			{ +				// We have a matching loc +				// +				if ((point_iter->second)[j] == cv) +				{ +					U16 shared_index	= (point_iter->second)[j].mIndex; + +					// Don't share verts within the same tri, degenerate +					// +					if ((indices.size() % 3) && (indices[indices.size()-1] != shared_index)) +					{ +						found = true; +						indices.push_back(shared_index); +					} +					break; +				} +			} +		} + +		if (!found) +		{ +			update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); +			verts.push_back(cv); +			if (verts.size() >= 65535) +			{ +				//llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl; +				return LLModel::VERTEX_NUMBER_OVERFLOW ; +			} +			U16 index = (U16) (verts.size()-1); +			indices.push_back(index); + +			LLVolumeFace::VertexMapData d; +			d.setPosition(cv.getPosition()); +			d.mTexCoord = cv.mTexCoord; +			d.setNormal(cv.getNormal()); +			d.mIndex = index; +			if (point_iter != point_map.end()) +			{ +				point_iter->second.push_back(d); +			} +			else +			{ +				point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d); +			} +		} + +		if (indices.size()%3 == 0 && verts.size() >= 65532) +			{ +				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(new_face.mTexCoords); +					new_face.mTexCoords = NULL; +				} + +				face = LLVolumeFace(); +				point_map.clear(); +			} +	} + +	if (!verts.empty()) +	{ +		std::string material; + +		if (tri->getMaterial()) +		{ +			material = std::string(tri->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(new_face.mTexCoords); +			new_face.mTexCoords = NULL; +		} +	} + +	return LLModel::NO_ERRORS ; +} + +LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly) +{ +	domPRef p = poly->getP(); +	domListOfUInts& idx = p->getValue(); + +	if (idx.getCount() == 0) +	{ +		return LLModel::NO_ERRORS ; +	} + +	const domInputLocalOffset_Array& inputs = poly->getInput_array(); + + +	domListOfUInts& vcount = poly->getVcount()->getValue(); +	 +	S32 pos_offset = -1; +	S32 tc_offset = -1; +	S32 norm_offset = -1; + +	domSource* pos_source = NULL; +	domSource* tc_source = NULL; +	domSource* norm_source = NULL; + +	S32 idx_stride = 0; + +	if (!get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source)) +	{ +		return LLModel::BAD_ELEMENT; +	} + +	LLVolumeFace face; + +	std::vector<U16> indices; +	std::vector<LLVolumeFace::VertexData> verts; + +	domListOfFloats v; +	domListOfFloats tc; +	domListOfFloats n; + +	if (pos_source) +	{ +		v = pos_source->getFloat_array()->getValue(); +		face.mExtents[0].set(v[0], v[1], v[2]); +		face.mExtents[1].set(v[0], v[1], v[2]); +	} + +	if (tc_source) +	{ +		tc = tc_source->getFloat_array()->getValue(); +	} + +	if (norm_source) +	{ +		n = norm_source->getFloat_array()->getValue(); +	} +	 +	LLVolumeFace::VertexMapData::PointMap point_map; + +	U32 cur_idx = 0; +	for (U32 i = 0; i < vcount.getCount(); ++i) +	{ //for each polygon +		U32 first_index = 0; +		U32 last_index = 0; +		for (U32 j = 0; j < vcount[i]; ++j) +		{ //for each vertex + +			LLVolumeFace::VertexData cv; + +			if (pos_source) +			{ +				cv.getPosition().set(v[idx[cur_idx+pos_offset]*3+0], +									v[idx[cur_idx+pos_offset]*3+1], +									v[idx[cur_idx+pos_offset]*3+2]); +			} + +			if (tc_source) +			{ +				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; +				 +			LLVolumeFace::VertexMapData::PointMap::iterator point_iter; +			LLVector3 pos3(cv.getPosition().getF32ptr()); +			point_iter = point_map.find(pos3); +			 +			if (point_iter != point_map.end()) +			{ +				for (U32 k = 0; k < point_iter->second.size(); ++k) +				{ +					if ((point_iter->second)[k] == cv) +					{ +						found = TRUE; +						U32 index = (point_iter->second)[k].mIndex; +						if (j == 0) +						{ +							first_index = index; +						} +						else if (j == 1) +						{ +							last_index = index; +						} +						else +						{ +							// if these are the same, we have a very, very skinny triangle (coincident verts on one or more edges) +							// +							llassert((first_index != last_index) && (last_index != index) && (first_index != index)); +							indices.push_back(first_index); +							indices.push_back(last_index); +							indices.push_back(index); +							last_index = index; +						} + +						break; +					} +				} +			} + +			if (!found) +			{ +				update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); +				verts.push_back(cv); +				if (verts.size() >= 65535) +				{ +					//llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl; +					return LLModel::VERTEX_NUMBER_OVERFLOW ; +				} +				U16 index = (U16) (verts.size()-1); +			 +				if (j == 0) +				{ +					first_index = index; +				} +				else if (j == 1) +				{ +					last_index = index; +				} +				else +				{ +					// detect very skinny degenerate triangles with collapsed edges +					// +					llassert((first_index != last_index) && (last_index != index) && (first_index != index)); +					indices.push_back(first_index); +					indices.push_back(last_index); +					indices.push_back(index); +					last_index = index; +				}	 + +				LLVolumeFace::VertexMapData d; +				d.setPosition(cv.getPosition()); +				d.mTexCoord = cv.mTexCoord; +				d.setNormal(cv.getNormal()); +				d.mIndex = index; +				if (point_iter != point_map.end()) +				{ +					point_iter->second.push_back(d); +				} +				else +				{ +					point_map[pos3].push_back(d); +				} +			} + +			if (indices.size()%3 == 0 && indices.size() >= 65532) +			{ +				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(new_face.mTexCoords); +					new_face.mTexCoords = NULL; +				} + +				face = LLVolumeFace(); +				verts.clear(); +				indices.clear(); +				point_map.clear(); +			} +		} +	} + +	if (!verts.empty()) +	{ +		std::string material; + +		if (poly->getMaterial()) +		{ +			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(new_face.mTexCoords); +			new_face.mTexCoords = NULL; +		} +	} + +	return LLModel::NO_ERRORS ; +} + +LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolygonsRef& poly) +{ +	LLVolumeFace face; +	std::vector<U16> indices; +	std::vector<LLVolumeFace::VertexData> verts; + +	const domInputLocalOffset_Array& inputs = poly->getInput_array(); + +	S32 v_offset = -1; +	S32 n_offset = -1; +	S32 t_offset = -1; + +	domListOfFloats* v = NULL; +	domListOfFloats* n = NULL; +	domListOfFloats* t = NULL; +	 +	U32 stride = 0; +	for (U32 i = 0; i < inputs.getCount(); ++i) +	{ +		stride = llmax((U32) inputs[i]->getOffset()+1, stride); + +		if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0) +		{ //found vertex array +			v_offset = inputs[i]->getOffset(); + +			const domURIFragmentType& uri = inputs[i]->getSource(); +			daeElementRef elem = uri.getElement(); +			domVertices* vertices = (domVertices*) elem.cast(); +			if (!vertices) +			{ +				return LLModel::BAD_ELEMENT; +			} +			domInputLocal_Array& v_inp = vertices->getInput_array(); + +			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) +					{ +						return LLModel::BAD_ELEMENT; +					} +					v = &(src->getFloat_array()->getValue()); +				} +			} +		} +		else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[i]->getSemantic()) == 0) +		{ +			n_offset = inputs[i]->getOffset(); +			//found normal array for this triangle list +			const domURIFragmentType& uri = inputs[i]->getSource(); +			daeElementRef elem = uri.getElement(); +			domSource* src = (domSource*) elem.cast(); +			if (!src) +			{ +				return LLModel::BAD_ELEMENT; +			} +			n = &(src->getFloat_array()->getValue()); +		} +		else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[i]->getSemantic()) == 0 && inputs[i]->getSet() == 0) +		{ //found texCoords +			t_offset = inputs[i]->getOffset(); +			const domURIFragmentType& uri = inputs[i]->getSource(); +			daeElementRef elem = uri.getElement(); +			domSource* src = (domSource*) elem.cast(); +			if (!src) +			{ +				return LLModel::BAD_ELEMENT; +			} +			t = &(src->getFloat_array()->getValue()); +		} +	} + +	domP_Array& ps = poly->getP_array(); + +	//make a triangle list in <verts> +	for (U32 i = 0; i < ps.getCount(); ++i) +	{ //for each polygon +		domListOfUInts& idx = ps[i]->getValue(); +		for (U32 j = 0; j < idx.getCount()/stride; ++j) +		{ //for each vertex +			if (j > 2) +			{ +				U32 size = verts.size(); +				LLVolumeFace::VertexData v0 = verts[size-3]; +				LLVolumeFace::VertexData v1 = verts[size-1]; + +				verts.push_back(v0); +				verts.push_back(v1); +			} + +			LLVolumeFace::VertexData vert; + + +			if (v) +			{ +				U32 v_idx = idx[j*stride+v_offset]*3; +				v_idx = llclamp(v_idx, (U32) 0, (U32) v->getCount()); +				vert.getPosition().set(v->get(v_idx), +								v->get(v_idx+1), +								v->get(v_idx+2)); +			} +			 +			//bounds check n and t lookups because some FBX to DAE converters +			//use negative indices and empty arrays to indicate data does not exist +			//for a particular channel +			if (n && n->getCount() > 0) +			{ +				U32 n_idx = idx[j*stride+n_offset]*3; +				n_idx = llclamp(n_idx, (U32) 0, (U32) n->getCount()); +				vert.getNormal().set(n->get(n_idx), +								n->get(n_idx+1), +								n->get(n_idx+2)); +			} +			else +			{ +				vert.getNormal().clear(); +			} + +			 +			if (t && t->getCount() > 0) +			{ +				U32 t_idx = idx[j*stride+t_offset]*2; +				t_idx = llclamp(t_idx, (U32) 0, (U32) t->getCount()); +				vert.mTexCoord.setVec(t->get(t_idx), +								t->get(t_idx+1));								 +			} +			else +			{ +				vert.mTexCoord.clear(); +			} + +						 +			verts.push_back(vert); +		} +	} + +	if (verts.empty()) +	{ +		return LLModel::NO_ERRORS; +	} + +	face.mExtents[0] = verts[0].getPosition(); +	face.mExtents[1] = verts[0].getPosition(); +	 +	//create a map of unique vertices to indices +	std::map<LLVolumeFace::VertexData, U32> vert_idx; + +	U32 cur_idx = 0; +	for (U32 i = 0; i < verts.size(); ++i) +	{ +		std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.find(verts[i]); +		if (iter == vert_idx.end()) +		{ +			vert_idx[verts[i]] = cur_idx++; +		} +	} + +	//build vertex array from map +	std::vector<LLVolumeFace::VertexData> new_verts; +	new_verts.resize(vert_idx.size()); + +	for (std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter) +	{ +		new_verts[iter->second] = iter->first; +		update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition()); +	} + +	//build index array from map +	indices.resize(verts.size()); + +	for (U32 i = 0; i < verts.size(); ++i) +	{ +		indices[i] = vert_idx[verts[i]]; +		llassert(!i || (indices[i-1] != indices[i])); +	} + +	// DEBUG just build an expanded triangle list +	/*for (U32 i = 0; i < verts.size(); ++i) +	{ +		indices.push_back((U16) i); +		update_min_max(face.mExtents[0], face.mExtents[1], verts[i].getPosition()); +	}*/ + +    if (!new_verts.empty()) +	{ +		std::string material; + +		if (poly->getMaterial()) +		{ +			material = std::string(poly->getMaterial()); +		} + +		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(new_face.mTexCoords); +			new_face.mTexCoords = NULL; +		} +	} + +	return LLModel::NO_ERRORS ; +} + +//----------------------------------------------------------------------------- +// LLDAELoader +//----------------------------------------------------------------------------- +LLDAELoader::LLDAELoader( +	std::string				filename, +	S32						lod, +	load_callback_t		load_cb, +	joint_lookup_func_t	joint_lookup_func, +	texture_load_func_t	texture_load_func, +	state_callback_t		state_cb, +	void*						opaque_userdata, +	JointTransformMap&	jointMap, +	JointSet&				jointsFromNodes ) +: LLModelLoader( +		filename, +		lod, +		load_cb, +		joint_lookup_func, +		texture_load_func, +		state_cb, +		opaque_userdata, +		jointMap, +		jointsFromNodes) +{ +} + +LLDAELoader::~LLDAELoader() +{ +} + +struct ModelSort +{ +	bool operator()(const LLPointer< LLModel >& lhs, const LLPointer< LLModel >& rhs) +	{ +        if (lhs->mSubmodelID < rhs->mSubmodelID) +        { +            return true; +        } +		return LLStringUtil::compareInsensitive(lhs->mLabel, rhs->mLabel) < 0; +	} +}; + +bool LLDAELoader::OpenFile(const std::string& filename) +{ +	//no suitable slm exists, load from the .dae file +	DAE dae; +	domCOLLADA* dom = dae.open(filename); +	 +	if (!dom) +	{ +		llinfos<<" Error with dae - traditionally indicates a corrupt file."<<llendl; +		setLoadState( ERROR_PARSING ); +		return false; +	} +	//Dom version +	daeString domVersion = dae.getDomVersion(); +	std::string sldom(domVersion); +	llinfos<<"Collada Importer Version: "<<sldom<<llendl; +	//Dae version +	domVersionType docVersion = dom->getVersion(); +	//0=1.4 +	//1=1.4.1 +	//2=Currently unsupported, however may work +	if (docVersion > 1 )  +	{  +		docVersion = VERSIONTYPE_COUNT; +	} +	llinfos<<"Dae version "<<colladaVersion[docVersion]<<llendl; +	 +	 +	daeDatabase* db = dae.getDatabase(); +	 +	daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH); +	 +	daeDocument* doc = dae.getDoc(mFilename); +	if (!doc) +	{ +		llwarns << "can't find internal doc" << llendl; +		return false; +	} +	 +	daeElement* root = doc->getDomRoot(); +	if (!root) +	{ +		llwarns << "document has no root" << llendl; +		return false; +	} +	 +	//Verify some basic properties of the dae +	//1. Basic validity check on controller  +	U32 controllerCount = (int) db->getElementCount( NULL, "controller" ); +	bool result = false; +	for ( int i=0; i<controllerCount; ++i ) +	{ +		domController* pController = NULL; +		db->getElement( (daeElement**) &pController, i , NULL, "controller" ); +		result = verifyController( pController ); +		if (!result) +		{ +			setLoadState( ERROR_PARSING ); +			return true; +		} +	} + + +	//get unit scale +	mTransform.setIdentity(); +	 +	domAsset::domUnit* unit = daeSafeCast<domAsset::domUnit>(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID()))); + +	if (unit) +	{ +		F32 meter = unit->getMeter(); +		mTransform.mMatrix[0][0] = meter; +		mTransform.mMatrix[1][1] = meter; +		mTransform.mMatrix[2][2] = meter; +	} +	 +	//get up axis rotation +	LLMatrix4 rotation; +	 +	domUpAxisType up = UPAXISTYPE_Y_UP;  // default is Y_UP +	domAsset::domUp_axis* up_axis = +	daeSafeCast<domAsset::domUp_axis>(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID()))); +	 +	if (up_axis) +	{ +		up = up_axis->getValue(); +	} +	 +	if (up == UPAXISTYPE_X_UP) +	{ +		rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f); +	} +	else if (up == UPAXISTYPE_Y_UP) +	{ +		rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); +	} +	 +	rotation *= mTransform; +	mTransform = rotation; + +	mTransform.condition();	 + +	for (daeInt idx = 0; idx < count; ++idx) +	{ //build map of domEntities to LLModel +		domMesh* mesh = NULL; +		db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH); +		 +		if (mesh) +		{ + +			std::vector<LLModel*> models; + +			loadModelsFromDomMesh(mesh, models); + +			std::vector<LLModel*>::iterator i; +			i = models.begin(); +			while (i != models.end()) +			{ +				LLModel* mdl = *i; +				if(mdl->getStatus() != LLModel::NO_ERRORS) +				{ +					setLoadState(ERROR_PARSING + mdl->getStatus()) ; +					return false; //abort +				} + +				if (mdl && validate_model(mdl)) +				{ +					mModelList.push_back(mdl); +					mModelsMap[mesh].push_back(mdl); +				} +				i++; +			} +		} +	} + +	std::sort(mModelList.begin(), mModelList.end(), ModelSort()); + +	model_list::iterator model_iter = mModelList.begin(); +	while (model_iter != mModelList.end()) +	{ +		llinfos << "Importing " << (*model_iter)->mLabel << llendl; +		std::vector<std::string>::iterator mat_iter = (*model_iter)->mMaterialList.begin(); +		while (mat_iter != (*model_iter)->mMaterialList.end()) +		{ +			llinfos << (*model_iter)->mLabel << " references " << (*mat_iter) << llendl; +			mat_iter++; +		} +		model_iter++; +	} + +	count = db->getElementCount(NULL, COLLADA_TYPE_SKIN); +	for (daeInt idx = 0; idx < count; ++idx) +	{ //add skinned meshes as instances +		domSkin* skin = NULL; +		db->getElement((daeElement**) &skin, idx, NULL, COLLADA_TYPE_SKIN); +		 +		if (skin) +		{ +			domGeometry* geom = daeSafeCast<domGeometry>(skin->getSource().getElement()); +			 +			if (geom) +			{ +				domMesh* mesh = geom->getMesh(); +				if (mesh) +				{ +					std::vector< LLPointer< LLModel > >::iterator i = mModelsMap[mesh].begin(); +					while (i != mModelsMap[mesh].end()) +					{ +						LLPointer<LLModel> mdl = *i; +						LLDAELoader::processDomModel(mdl, &dae, root, mesh, skin); +						i++; +					} +				} +			} +		} +	} + +	daeElement* scene = root->getDescendant("visual_scene"); +	 +	if (!scene) +	{ +		llwarns << "document has no visual_scene" << llendl; +		setLoadState( ERROR_PARSING ); +		return true; +	} +	 +	setLoadState( DONE ); + +	bool badElement = false; +	 +	processElement( scene, badElement, &dae ); +	 +	if ( badElement ) +	{ +		setLoadState( ERROR_PARSING ); +	} +	 +	return true; +} + +void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, domMesh* mesh, domSkin* skin) +{ +	llassert(model && dae && mesh && skin); + +	if (model) +	{ +		LLVector3 mesh_scale_vector; +		LLVector3 mesh_translation_vector; +		model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + +		LLMatrix4 normalized_transformation; +		normalized_transformation.setTranslation(mesh_translation_vector); + +		LLMatrix4 mesh_scale; +		mesh_scale.initScale(mesh_scale_vector); +		mesh_scale *= normalized_transformation; +		normalized_transformation = mesh_scale; + +		glh::matrix4f inv_mat((F32*) normalized_transformation.mMatrix); +		inv_mat = inv_mat.inverse(); +		LLMatrix4 inverse_normalized_transformation(inv_mat.m); + +		domSkin::domBind_shape_matrix* bind_mat = skin->getBind_shape_matrix(); + +		if (bind_mat) +		{ //get bind shape matrix +			domFloat4x4& dom_value = bind_mat->getValue(); + +			LLMeshSkinInfo& skin_info = model->mSkinInfo; + +			for (int i = 0; i < 4; i++) +			{ +				for(int j = 0; j < 4; j++) +				{ +					skin_info.mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4]; +				} +			} + +			LLMatrix4 trans = normalized_transformation; +			trans *= skin_info.mBindShapeMatrix; +			skin_info.mBindShapeMatrix = trans;							 +		} + + +		//Some collada setup for accessing the skeleton +		daeElement* pElement = 0; +		dae->getDatabase()->getElement( &pElement, 0, 0, "skeleton" ); + +		//Try to get at the skeletal instance controller +		domInstance_controller::domSkeleton* pSkeleton = daeSafeCast<domInstance_controller::domSkeleton>( pElement ); +		bool missingSkeletonOrScene = false; + +		//If no skeleton, do a breadth-first search to get at specific joints +		bool rootNode = false; + +		//Need to test for a skeleton that does not have a root node +		//This occurs when your instance controller does not have an associated scene  +		if ( pSkeleton ) +		{ +			daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement(); +			if ( pSkeletonRootNode ) +			{ +				rootNode = true; +			} + +		} +		if ( !pSkeleton || !rootNode ) +		{ +			daeElement* pScene = root->getDescendant("visual_scene"); +			if ( !pScene ) +			{ +				llwarns<<"No visual scene - unable to parse bone offsets "<<llendl; +				missingSkeletonOrScene = true; +			} +			else +			{ +				//Get the children at this level +				daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren(); +				S32 childCount = children.getCount(); + +				//Process any children that are joints +				//Not all children are joints, some code be ambient lights, cameras, geometry etc.. +				for (S32 i = 0; i < childCount; ++i) +				{ +					domNode* pNode = daeSafeCast<domNode>(children[i]); +					if ( isNodeAJoint( pNode ) ) +					{ +						processJointNode( pNode, mJointList ); +					} +				} +			} +		} +		else +			//Has Skeleton +		{ +			//Get the root node of the skeleton +			daeElement* pSkeletonRootNode = pSkeleton->getValue().getElement(); +			if ( pSkeletonRootNode ) +			{ +				//Once we have the root node - start acccessing it's joint components +				const int jointCnt = mJointMap.size(); +				JointMap :: const_iterator jointIt = mJointMap.begin(); + +				//Loop over all the possible joints within the .dae - using the allowed joint list in the ctor. +				for ( int i=0; i<jointCnt; ++i, ++jointIt ) +				{ +					//Build a joint for the resolver to work with +					char str[64]={0}; +					sprintf(str,"./%s",(*jointIt).first.c_str() ); +					//llwarns<<"Joint "<< str <<llendl; + +					//Setup the resolver +					daeSIDResolver resolver( pSkeletonRootNode, str ); + +					//Look for the joint +					domNode* pJoint = daeSafeCast<domNode>( resolver.getElement() ); +					if ( pJoint ) +					{ +						//Pull out the translate id and store it in the jointTranslations map +						daeSIDResolver jointResolverA( pJoint, "./translate" ); +						domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() ); +						daeSIDResolver jointResolverB( pJoint, "./location" ); +						domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() ); + +						LLMatrix4 workingTransform; + +						//Translation via SID +						if ( pTranslateA ) +						{ +							extractTranslation( pTranslateA, workingTransform ); +						} +						else +							if ( pTranslateB ) +							{ +								extractTranslation( pTranslateB, workingTransform ); +							} +							else +							{ +								//Translation via child from element +								daeElement* pTranslateElement = getChildFromElement( pJoint, "translate" ); +								if ( pTranslateElement && pTranslateElement->typeID() != domTranslate::ID() ) +								{ +									llwarns<< "The found element is not a translate node" <<llendl; +									missingSkeletonOrScene = true; +								} +								else +									if ( pTranslateElement ) +									{ +										extractTranslationViaElement( pTranslateElement, workingTransform ); +									} +									else +									{ +										extractTranslationViaSID( pJoint, workingTransform ); +									} + +							} + +							//Store the joint transform w/respect to it's name. +							mJointList[(*jointIt).second.c_str()] = workingTransform; +					} +				} + +				//If anything failed in regards to extracting the skeleton, joints or translation id, +				//mention it +				if ( missingSkeletonOrScene  ) +				{ +					llwarns<< "Partial jointmap found in asset - did you mean to just have a partial map?" << llendl; +				} +			}//got skeleton? +		} + + +		domSkin::domJoints* joints = skin->getJoints(); + +		domInputLocal_Array& joint_input = joints->getInput_array(); + +		for (size_t i = 0; i < joint_input.getCount(); ++i) +		{ +			domInputLocal* input = joint_input.get(i); +			xsNMTOKEN semantic = input->getSemantic(); + +			if (strcmp(semantic, COMMON_PROFILE_INPUT_JOINT) == 0) +			{ //found joint source, fill model->mJointMap and model->mSkinInfo.mJointNames +				daeElement* elem = input->getSource().getElement(); + +				domSource* source = daeSafeCast<domSource>(elem); +				if (source) +				{ + + +					domName_array* names_source = source->getName_array(); + +					if (names_source) +					{ +						domListOfNames &names = names_source->getValue(); + +						for (size_t j = 0; j < names.getCount(); ++j) +						{ +							std::string name(names.get(j)); +							if (mJointMap.find(name) != mJointMap.end()) +							{ +								name = mJointMap[name]; +							} +							model->mSkinInfo.mJointNames.push_back(name); +							model->mSkinInfo.mJointMap[name] = j; +						} +					} +					else +					{ +						domIDREF_array* names_source = source->getIDREF_array(); +						if (names_source) +						{ +							xsIDREFS& names = names_source->getValue(); + +							for (size_t j = 0; j < names.getCount(); ++j) +							{ +								std::string name(names.get(j).getID()); +								if (mJointMap.find(name) != mJointMap.end()) +								{ +									name = mJointMap[name]; +								} +								model->mSkinInfo.mJointNames.push_back(name); +								model->mSkinInfo.mJointMap[name] = j; +							} +						} +					} +				} +			} +			else if (strcmp(semantic, COMMON_PROFILE_INPUT_INV_BIND_MATRIX) == 0) +			{ //found inv_bind_matrix array, fill model->mInvBindMatrix +				domSource* source = daeSafeCast<domSource>(input->getSource().getElement()); +				if (source) +				{ +					domFloat_array* t = source->getFloat_array(); +					if (t) +					{ +						domListOfFloats& transform = t->getValue(); +						S32 count = transform.getCount()/16; + +						for (S32 k = 0; k < count; ++k) +						{ +							LLMatrix4 mat; + +							for (int i = 0; i < 4; i++) +							{ +								for(int j = 0; j < 4; j++) +								{ +									mat.mMatrix[i][j] = transform[k*16 + i + j*4]; +								} +							} + +							model->mSkinInfo.mInvBindMatrix.push_back(mat);											 +						} +					} +				} +			} +		} + +		//Now that we've parsed the joint array, let's determine if we have a full rig +		//(which means we have all the joint sthat are required for an avatar versus +		//a skinned asset attached to a node in a file that contains an entire skeleton, +		//but does not use the skeleton).						 +		buildJointToNodeMappingFromScene( root ); +		critiqueRigForUploadApplicability( model->mSkinInfo.mJointNames ); + +		if ( !missingSkeletonOrScene ) +		{ +			//Set the joint translations on the avatar - if it's a full mapping +			//The joints are reset in the dtor +			if ( getRigWithSceneParity() ) +			{	 +				JointMap :: const_iterator masterJointIt = mJointMap.begin(); +				JointMap :: const_iterator masterJointItEnd = mJointMap.end(); +				for (;masterJointIt!=masterJointItEnd;++masterJointIt ) +				{ +					std::string lookingForJoint = (*masterJointIt).first.c_str(); + +					if ( mJointList.find( lookingForJoint ) != mJointList.end() ) +					{ +						//llinfos<<"joint "<<lookingForJoint.c_str()<<llendl; +						LLMatrix4 jointTransform = mJointList[lookingForJoint]; +						LLJoint* pJoint = mJointLookupFunc(lookingForJoint,mOpaqueData); +						if ( pJoint ) +						{    +							pJoint->storeCurrentXform( jointTransform.getTranslation() );												 +						} +						else +						{ +							//Most likely an error in the asset. +							llwarns<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << llendl; +						} +					} +				} +			} +		} //missingSkeletonOrScene + +		//We need to construct the alternate bind matrix (which contains the new joint positions) +		//in the same order as they were stored in the joint buffer. The joints associated +		//with the skeleton are not stored in the same order as they are in the exported joint buffer. +		//This remaps the skeletal joints to be in the same order as the joints stored in the model. +		std::vector<std::string> :: const_iterator jointIt  = model->mSkinInfo.mJointNames.begin(); +		const int jointCnt = model->mSkinInfo.mJointNames.size(); +		for ( int i=0; i<jointCnt; ++i, ++jointIt ) +		{ +			std::string lookingForJoint = (*jointIt).c_str(); +			//Look for the joint xform that we extracted from the skeleton, using the jointIt as the key +			//and store it in the alternate bind matrix +			if ( mJointList.find( lookingForJoint ) != mJointList.end() ) +			{ +				LLMatrix4 jointTransform = mJointList[lookingForJoint]; +				LLMatrix4 newInverse = model->mSkinInfo.mInvBindMatrix[i]; +				newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() ); +				model->mSkinInfo.mAlternateBindMatrix.push_back( newInverse ); +			} +			else +			{ +				llwarns<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<" ] "<<llendl; +			} +		} + +		//grab raw position array + +		domVertices* verts = mesh->getVertices(); +		if (verts) +		{ +			domInputLocal_Array& inputs = verts->getInput_array(); +			for (size_t i = 0; i < inputs.getCount() && model->mPosition.empty(); ++i) +			{ +				if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_POSITION) == 0) +				{ +					domSource* pos_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement()); +					if (pos_source) +					{ +						domFloat_array* pos_array = pos_source->getFloat_array(); +						if (pos_array) +						{ +							domListOfFloats& pos = pos_array->getValue(); + +							for (size_t j = 0; j < pos.getCount(); j += 3) +							{ +								if (pos.getCount() <= j+2) +								{ +									llerrs << "Invalid position array size." << llendl; +								} + +								LLVector3 v(pos[j], pos[j+1], pos[j+2]); + +								//transform from COLLADA space to volume space +								v = v * inverse_normalized_transformation; + +								model->mPosition.push_back(v); +							} +						} +					} +				} +			} +		} + +		//grab skin weights array +		domSkin::domVertex_weights* weights = skin->getVertex_weights(); +		if (weights) +		{ +			domInputLocalOffset_Array& inputs = weights->getInput_array(); +			domFloat_array* vertex_weights = NULL; +			for (size_t i = 0; i < inputs.getCount(); ++i) +			{ +				if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT) == 0) +				{ +					domSource* weight_source = daeSafeCast<domSource>(inputs[i]->getSource().getElement()); +					if (weight_source) +					{ +						vertex_weights = weight_source->getFloat_array(); +					} +				} +			} + +			if (vertex_weights) +			{ +				domListOfFloats& w = vertex_weights->getValue(); +				domListOfUInts& vcount = weights->getVcount()->getValue(); +				domListOfInts& v = weights->getV()->getValue(); + +				U32 c_idx = 0; +				for (size_t vc_idx = 0; vc_idx < vcount.getCount(); ++vc_idx) +				{ //for each vertex +					daeUInt count = vcount[vc_idx]; + +					//create list of weights that influence this vertex +					LLModel::weight_list weight_list; + +					for (daeUInt i = 0; i < count; ++i) +					{ //for each weight +						daeInt joint_idx = v[c_idx++]; +						daeInt weight_idx = v[c_idx++]; + +						if (joint_idx == -1) +						{ +							//ignore bindings to bind_shape_matrix +							continue; +						} + +						F32 weight_value = w[weight_idx]; + +						weight_list.push_back(LLModel::JointWeight(joint_idx, weight_value)); +					} + +					//sort by joint weight +					std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); + +					std::vector<LLModel::JointWeight> wght; + +					F32 total = 0.f; + +					for (U32 i = 0; i < llmin((U32) 4, (U32) weight_list.size()); ++i) +					{ //take up to 4 most significant weights +						if (weight_list[i].mWeight > 0.f) +						{ +							wght.push_back( weight_list[i] ); +							total += weight_list[i].mWeight; +						} +					} + +					F32 scale = 1.f/total; +					if (scale != 1.f) +					{ //normalize weights +						for (U32 i = 0; i < wght.size(); ++i) +						{ +							wght[i].mWeight *= scale; +						} +					} + +					model->mSkinWeights[model->mPosition[vc_idx]] = wght; +				} +			} + +		} + +		//add instance to scene for this model + +		LLMatrix4 transformation; +		transformation.initScale(mesh_scale_vector); +		transformation.setTranslation(mesh_translation_vector); +		transformation *= mTransform; + +		std::map<std::string, LLImportMaterial> materials; +		for (U32 i = 0; i < model->mMaterialList.size(); ++i) +		{ +			materials[model->mMaterialList[i]] = LLImportMaterial(); +		} +		mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials)); +		stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); +	} +} + +//----------------------------------------------------------------------------- +// buildJointToNodeMappingFromScene() +//----------------------------------------------------------------------------- +void LLDAELoader::buildJointToNodeMappingFromScene( daeElement* pRoot ) +{ +	daeElement* pScene = pRoot->getDescendant("visual_scene"); +	if ( pScene ) +	{ +		daeTArray< daeSmartRef<daeElement> > children = pScene->getChildren(); +		S32 childCount = children.getCount(); +		for (S32 i = 0; i < childCount; ++i) +		{ +			domNode* pNode = daeSafeCast<domNode>(children[i]); +			processJointToNodeMapping( pNode );			 +		} +	} +} +//----------------------------------------------------------------------------- +// processJointToNodeMapping() +//----------------------------------------------------------------------------- +void LLDAELoader::processJointToNodeMapping( domNode* pNode ) +{ +	if ( isNodeAJoint( pNode ) ) +	{ +		//1.Store the parent +		std::string nodeName = pNode->getName(); +		if ( !nodeName.empty() ) +		{ +			mJointsFromNode.push_front( pNode->getName() ); +		} +		//2. Handle the kiddo's +		processChildJoints( pNode ); +	} +	else +	{ +		//Determine if the're any children wrt to this failed node. +		//This occurs when an armature is exported and ends up being what essentially amounts to +		//as the root for the visual_scene +		if ( pNode )  +		{ +			processChildJoints( pNode ); +		} +		else  +		{ +			llinfos<<"Node is NULL"<<llendl; +		} + +	} +} +//----------------------------------------------------------------------------- +// processChildJoint() +//----------------------------------------------------------------------------- +void LLDAELoader::processChildJoints( domNode* pParentNode ) +{	 +	daeTArray< daeSmartRef<daeElement> > childOfChild = pParentNode->getChildren(); +	S32 childOfChildCount = childOfChild.getCount(); +	for (S32 i = 0; i < childOfChildCount; ++i) +	{ +		domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] ); +		if ( pChildNode ) +		{ +			processJointToNodeMapping( pChildNode ); +		} +	} +} + +//----------------------------------------------------------------------------- +// isNodeAJoint() +//----------------------------------------------------------------------------- +bool LLDAELoader::isNodeAJoint( domNode* pNode ) +{ +	if ( !pNode ) +	{ +		llinfos<<"Created node is NULL"<<llendl; +		return false; +	} +	 +	return LLModelLoader::isNodeAJoint(pNode->getName()); +} +//----------------------------------------------------------------------------- +// verifyCount +//----------------------------------------------------------------------------- +bool LLDAELoader::verifyCount( int expected, int result ) +{ +	if ( expected != result ) +	{ +		llinfos<< "Error: (expected/got)"<<expected<<"/"<<result<<"verts"<<llendl; +		return false; +	} +	return true; +} +//----------------------------------------------------------------------------- +// verifyController +//----------------------------------------------------------------------------- +bool LLDAELoader::verifyController( domController* pController ) +{	 + +	bool result = true; + +	domSkin* pSkin = pController->getSkin(); + +	if ( pSkin ) +	{ +		xsAnyURI & uri = pSkin->getSource(); +		domElement* pElement = uri.getElement(); + +		if ( !pElement ) +		{ +			llinfos<<"Can't resolve skin source"<<llendl; +			return false; +		} + +		daeString type_str = pElement->getTypeName(); +		if ( stricmp(type_str, "geometry") == 0 ) +		{	 +			//Skin is reference directly by geometry and get the vertex count from skin +			domSkin::domVertex_weights* pVertexWeights = pSkin->getVertex_weights(); +			U32 vertexWeightsCount = pVertexWeights->getCount(); +			domGeometry* pGeometry = (domGeometry*) (domElement*) uri.getElement(); +			domMesh* pMesh = pGeometry->getMesh();				 +			 +			if ( pMesh ) +			{ +				//Get vertex count from geometry +				domVertices* pVertices = pMesh->getVertices(); +				if ( !pVertices ) +				{  +					llinfos<<"No vertices!"<<llendl; +					return false; +				} + +				if ( pVertices ) +				{ +					xsAnyURI src = pVertices->getInput_array()[0]->getSource(); +					domSource* pSource = (domSource*) (domElement*) src.getElement(); +					U32 verticesCount = pSource->getTechnique_common()->getAccessor()->getCount(); +					result = verifyCount( verticesCount, vertexWeightsCount ); +					if ( !result ) +					{ +						return result; +					} +				} +			}	 + +			U32 vcountCount = (U32) pVertexWeights->getVcount()->getValue().getCount(); +			result = verifyCount( vcountCount, vertexWeightsCount );	 +			if ( !result ) +			{ +				return result; +			} + +			domInputLocalOffset_Array& inputs = pVertexWeights->getInput_array(); +			U32 sum = 0; +			for (size_t i=0; i<vcountCount; i++) +			{ +				sum += pVertexWeights->getVcount()->getValue()[i]; +			} +			result = verifyCount( sum * inputs.getCount(), (domInt) pVertexWeights->getV()->getValue().getCount() ); +		} +	} +	 +	return result; +} + +//----------------------------------------------------------------------------- +// extractTranslation() +//----------------------------------------------------------------------------- +void LLDAELoader::extractTranslation( domTranslate* pTranslate, LLMatrix4& transform ) +{ +	domFloat3 jointTrans = pTranslate->getValue(); +	LLVector3 singleJointTranslation( jointTrans[0], jointTrans[1], jointTrans[2] ); +	transform.setTranslation( singleJointTranslation ); +} +//----------------------------------------------------------------------------- +// extractTranslationViaElement() +//----------------------------------------------------------------------------- +void LLDAELoader::extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform ) +{ +	if ( pTranslateElement ) +	{ +		domTranslate* pTranslateChild = dynamic_cast<domTranslate*>( pTranslateElement ); +		domFloat3 translateChild = pTranslateChild->getValue(); +		LLVector3 singleJointTranslation( translateChild[0], translateChild[1], translateChild[2] ); +		transform.setTranslation( singleJointTranslation ); +	}	 +} +//----------------------------------------------------------------------------- +// extractTranslationViaSID() +//----------------------------------------------------------------------------- +void LLDAELoader::extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform ) +{ +	if ( pElement ) +	{	 +		daeSIDResolver resolver( pElement, "./transform" ); +		domMatrix* pMatrix = daeSafeCast<domMatrix>( resolver.getElement() ); +		//We are only extracting out the translational component atm +		LLMatrix4 workingTransform; +		if ( pMatrix ) +		{ +			domFloat4x4 domArray = pMatrix->getValue();									 +			for ( int i = 0; i < 4; i++ ) +			{ +				for( int j = 0; j < 4; j++ ) +				{ +					workingTransform.mMatrix[i][j] = domArray[i + j*4]; +				} +			} +			LLVector3 trans = workingTransform.getTranslation(); +			transform.setTranslation( trans );	 +		} +	} +	else +	{ +		llwarns<<"Element is nonexistent - empty/unsupported node."<<llendl; +	} +} +//----------------------------------------------------------------------------- +// processJointNode() +//----------------------------------------------------------------------------- +void LLDAELoader::processJointNode( domNode* pNode, JointTransformMap& jointTransforms ) +{ +	if (pNode->getName() == NULL) +	{ +		llwarns << "nameless node, can't process" << llendl; +		return; +	} + +	//llwarns<<"ProcessJointNode# Node:" <<pNode->getName()<<llendl; + +	//1. handle the incoming node - extract out translation via SID or element + +	LLMatrix4 workingTransform; + +	//Pull out the translate id and store it in the jointTranslations map +	daeSIDResolver jointResolverA( pNode, "./translate" ); +	domTranslate* pTranslateA = daeSafeCast<domTranslate>( jointResolverA.getElement() ); +	daeSIDResolver jointResolverB( pNode, "./location" ); +	domTranslate* pTranslateB = daeSafeCast<domTranslate>( jointResolverB.getElement() ); + +	//Translation via SID was successful +	if ( pTranslateA ) +	{ +		extractTranslation( pTranslateA, workingTransform ); +	} +	else +	if ( pTranslateB ) +	{ +		extractTranslation( pTranslateB, workingTransform ); +	} +	else +	{ +		//Translation via child from element +		daeElement* pTranslateElement = getChildFromElement( pNode, "translate" ); +		if ( !pTranslateElement || pTranslateElement->typeID() != domTranslate::ID() ) +		{ +			//llwarns<< "The found element is not a translate node" <<llendl; +			daeSIDResolver jointResolver( pNode, "./matrix" ); +			domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() ); +			if ( pMatrix ) +			{ +				//llinfos<<"A matrix SID was however found!"<<llendl; +				domFloat4x4 domArray = pMatrix->getValue();									 +				for ( int i = 0; i < 4; i++ ) +				{ +					for( int j = 0; j < 4; j++ ) +					{ +						workingTransform.mMatrix[i][j] = domArray[i + j*4]; +					} +				} +			} +			else +			{ +				llwarns<< "The found element is not translate or matrix node - most likely a corrupt export!" <<llendl; +			} +		} +		else +		{ +			extractTranslationViaElement( pTranslateElement, workingTransform ); +		} +	} + +	//Store the working transform relative to the nodes name. +	jointTransforms[ pNode->getName() ] = workingTransform; + +	//2. handle the nodes children + +	//Gather and handle the incoming nodes children +	daeTArray< daeSmartRef<daeElement> > childOfChild = pNode->getChildren(); +	S32 childOfChildCount = childOfChild.getCount(); + +	for (S32 i = 0; i < childOfChildCount; ++i) +	{ +		domNode* pChildNode = daeSafeCast<domNode>( childOfChild[i] ); +		if ( pChildNode ) +		{ +			processJointNode( pChildNode, jointTransforms ); +		} +	} +} +//----------------------------------------------------------------------------- +// getChildFromElement() +//----------------------------------------------------------------------------- +daeElement* LLDAELoader::getChildFromElement( daeElement* pElement, std::string const & name ) +{ +    daeElement* pChildOfElement = pElement->getChild( name.c_str() ); +	if ( pChildOfElement ) +	{ +		return pChildOfElement; +	} +	llwarns<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << llendl; +    return NULL; +} + +void LLDAELoader::processElement( daeElement* element, bool& badElement, DAE* dae ) +{ +	LLMatrix4 saved_transform; +	bool pushed_mat = false; + +	domNode* node = daeSafeCast<domNode>(element); +	if (node) +	{ +		pushed_mat = true; +		saved_transform = mTransform; +	} + +	domTranslate* translate = daeSafeCast<domTranslate>(element); +	if (translate) +	{ +		domFloat3 dom_value = translate->getValue(); + +		LLMatrix4 translation; +		translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2])); + +		translation *= mTransform; +		mTransform = translation; +		mTransform.condition(); +	} + +	domRotate* rotate = daeSafeCast<domRotate>(element); +	if (rotate) +	{ +		domFloat4 dom_value = rotate->getValue(); + +		LLMatrix4 rotation; +		rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0)); + +		rotation *= mTransform; +		mTransform = rotation; +		mTransform.condition(); +	} + +	domScale* scale = daeSafeCast<domScale>(element); +	if (scale) +	{ +		domFloat3 dom_value = scale->getValue(); + + +		LLVector3 scale_vector = LLVector3(dom_value[0], dom_value[1], dom_value[2]); +		scale_vector.abs(); // Set all values positive, since we don't currently support mirrored meshes +		LLMatrix4 scaling; +		scaling.initScale(scale_vector); + +		scaling *= mTransform; +		mTransform = scaling; +		mTransform.condition(); +	} + +	domMatrix* matrix = daeSafeCast<domMatrix>(element); +	if (matrix) +	{ +		domFloat4x4 dom_value = matrix->getValue(); + +		LLMatrix4 matrix_transform; + +		for (int i = 0; i < 4; i++) +		{ +			for(int j = 0; j < 4; j++) +			{ +				matrix_transform.mMatrix[i][j] = dom_value[i + j*4]; +			} +		} + +		mTransform *= matrix_transform; +		mTransform.condition(); +	} + +	domInstance_geometry* instance_geo = daeSafeCast<domInstance_geometry>(element); +	if (instance_geo) +	{ +		domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement()); +		if (geo) +		{ +			domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID()))); +			if (mesh) +			{ + +				std::vector< LLPointer< LLModel > >::iterator i = mModelsMap[mesh].begin();				 +				while (i != mModelsMap[mesh].end()) +				{ +					LLModel* model = *i; + +					LLMatrix4 transformation = mTransform; +				 +					if (mTransform.determinant() < 0) +					{ //negative scales are not supported +						llinfos << "Negative scale detected, unsupported transform.  domInstance_geometry: " << getElementLabel(instance_geo) << llendl; +						badElement = true; +					} + +					LLModelLoader::material_map materials = getMaterials(model, instance_geo, dae); + +					// adjust the transformation to compensate for mesh normalization +					LLVector3 mesh_scale_vector; +					LLVector3 mesh_translation_vector; +					model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + +					LLMatrix4 mesh_translation; +					mesh_translation.setTranslation(mesh_translation_vector); +					mesh_translation *= transformation; +					transformation = mesh_translation; +					 +					LLMatrix4 mesh_scale; +					mesh_scale.initScale(mesh_scale_vector); +					mesh_scale *= transformation; +					transformation = mesh_scale; + +					if (transformation.determinant() < 0) +					{ //negative scales are not supported +						llinfos << "Negative scale detected, unsupported post-normalization transform.  domInstance_geometry: " << getElementLabel(instance_geo) << llendl; +						badElement = true; +					} + +					std::string label = getElementLabel(instance_geo); +                    +                    llassert(!label.empty()); + +                    if (model->mSubmodelID) +                    { +                        // CHECK FOR _LODX and _PHYS here to ensure we bolt the submodel 'salt' at the right loc +                        // +                        if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1)) +                        { +                            std::string labelStart; +                            std::string markup; +                            size_t underscore_offset = label.rfind('_'); +                            if (underscore_offset != -1) +                            { +                                markup = label.substr(underscore_offset + 1, 4); +                                label.erase(label.begin() + underscore_offset, label.end()); +                                label +=(char)((int)'a' + model->mSubmodelID); +                                label += "_"; +                                label += markup; +                            } +                            else +                            { +                                label +=(char)((int)'a' + model->mSubmodelID); +                            }                             +                        } +                        else +                        { +                            label += (char)((int)'a' + model->mSubmodelID); +                        } +                    } + +                    model->mLabel = label; +                    +					mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials)); +					stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); +					i++; +				} +			} +		} +		else  +		{ +			llinfos<<"Unable to resolve geometry URL."<<llendl; +			badElement = true;			 +		} + +	}	 + +	domInstance_node* instance_node = daeSafeCast<domInstance_node>(element); +	if (instance_node) +	{ +		daeElement* instance = instance_node->getUrl().getElement(); +		if (instance) +		{ +			processElement(instance,badElement, dae); +		} +	} + +	//process children +	daeTArray< daeSmartRef<daeElement> > children = element->getChildren(); +	int childCount = children.getCount(); +	for (S32 i = 0; i < childCount; i++) +	{ +		processElement(children[i],badElement, dae); +	} + +	if (pushed_mat) +	{ //this element was a node, restore transform before processiing siblings +		mTransform = saved_transform; +	} +} + +std::map<std::string, LLImportMaterial> LLDAELoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo, DAE* dae) +{ +	std::map<std::string, LLImportMaterial> materials; +	for (int i = 0; i < model->mMaterialList.size(); i++) +	{ +		LLImportMaterial import_material; + +		domInstance_material* instance_mat = NULL; + +		domBind_material::domTechnique_common* technique = +		daeSafeCast<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID()))); + +		if (technique) +		{ +			daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>(); +			for (int j = 0; j < inst_materials.getCount(); j++) +			{ +				std::string symbol(inst_materials[j]->getSymbol()); + +				if (symbol == model->mMaterialList[i]) // found the binding +				{ +					instance_mat = inst_materials[j]; +					break; +				} +			} +		} + +		if (instance_mat) +		{ +			domMaterial* material = daeSafeCast<domMaterial>(instance_mat->getTarget().getElement()); +			if (material) +			{ +				domInstance_effect* instance_effect = +				daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID()))); +				if (instance_effect) +				{ +					domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement()); +					if (effect) +					{ +						domProfile_COMMON* profile = +						daeSafeCast<domProfile_COMMON>(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID()))); +						if (profile) +						{ +							import_material = profileToMaterial(profile, dae); +						} +					} +				} +			} +		} + +		import_material.mBinding = model->mMaterialList[i]; +		materials[model->mMaterialList[i]] = import_material; +	} + +	return materials; +} + +LLImportMaterial LLDAELoader::profileToMaterial(domProfile_COMMON* material, DAE* dae) +{ +	LLImportMaterial mat; +	mat.mFullbright = FALSE; + +	daeElement* diffuse = material->getDescendant("diffuse"); +	if (diffuse) +	{ +		domCommon_color_or_texture_type_complexType::domTexture* texture = +		daeSafeCast<domCommon_color_or_texture_type_complexType::domTexture>(diffuse->getDescendant("texture")); +		if (texture) +		{ +			domCommon_newparam_type_Array newparams = material->getNewparam_array(); +			if (newparams.getCount()) +			{ + +				for (S32 i = 0; i < newparams.getCount(); i++) +				{ +					domFx_surface_common* surface = newparams[i]->getSurface(); +					if (surface) +					{ +						domFx_surface_init_common* init = surface->getFx_surface_init_common(); +						if (init) +						{ +							domFx_surface_init_from_common_Array init_from = init->getInit_from_array(); + +							if (init_from.getCount() > i) +							{ +								domImage* image = daeSafeCast<domImage>(init_from[i]->getValue().getElement()); +								if (image) +								{ +									// we only support init_from now - embedded data will come later +									domImage::domInit_from* init = image->getInit_from(); +									if (init) +									{									 +										mat.mDiffuseMapFilename = cdom::uriToNativePath(init->getValue().str()); +										mat.mDiffuseMapLabel = getElementLabel(material); +									} +								} +							} +						} +					} +				} +			} +			else if (texture->getTexture()) +			{ +				domImage* image = NULL; +				dae->getDatabase()->getElement((daeElement**) &image, 0, texture->getTexture(), COLLADA_TYPE_IMAGE); +				if (image) +				{ +					// we only support init_from now - embedded data will come later +					domImage::domInit_from* init = image->getInit_from(); +					if (init) +					{ +						std::string image_path_value = cdom::uriToNativePath(init->getValue().str()); + +#if LL_WINDOWS +						// Work-around DOM tendency to resort to UNC names which are only confusing for downstream... +						// +						std::string::iterator i = image_path_value.begin(); +						while (*i == '\\') +							i++; +						mat.mDiffuseMapFilename.assign(i, image_path_value.end()); +#else +						mat.mDiffuseMapFilename = image_path_value; +#endif +						mat.mDiffuseMapLabel = getElementLabel(material); +					} +				} +			} +		} + +		domCommon_color_or_texture_type_complexType::domColor* color = +		daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(diffuse->getDescendant("color")); +		if (color) +		{ +			domFx_color_common domfx_color = color->getValue(); +			LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); +			mat.mDiffuseColor = value; +		} +	} + +	daeElement* emission = material->getDescendant("emission"); +	if (emission) +	{ +		LLColor4 emission_color = getDaeColor(emission); +		if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25) +		{ +			mat.mFullbright = TRUE; +		} +	} + +	return mat; +} + +// try to get a decent label for this element +std::string LLDAELoader::getElementLabel(daeElement *element) +{ +	// if we have a name attribute, use it +	std::string name = element->getAttribute("name"); +	if (name.length()) +	{ +		return name; +	} + +	// if we have an ID attribute, use it +	if (element->getID()) +	{ +		return std::string(element->getID()); +	} + +	// if we have a parent, use it +	daeElement* parent = element->getParent(); +	if (parent) +	{ +		// if parent has a name, use it +		std::string name = parent->getAttribute("name"); +		if (name.length()) +		{ +			return name; +		} + +		// if parent has an ID, use it +		if (parent->getID()) +		{ +			return std::string(parent->getID()); +		} +	} + +	// try to use our type +	daeString element_name = element->getElementName(); +	if (element_name) +	{ +		return std::string(element_name); +	} + +	// if all else fails, use "object" +	return std::string("object"); +} + +LLColor4 LLDAELoader::getDaeColor(daeElement* element) +{ +	LLColor4 value; +	domCommon_color_or_texture_type_complexType::domColor* color = +	daeSafeCast<domCommon_color_or_texture_type_complexType::domColor>(element->getDescendant("color")); +	if (color) +	{ +		domFx_color_common domfx_color = color->getValue(); +		value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); +	} + +	return value; +} + +bool LLDAELoader::addVolumeFacesFromDomMesh(LLModel* pModel,domMesh* mesh) +{ +	LLModel::EModelStatus status = LLModel::NO_ERRORS; +	domTriangles_Array& tris = mesh->getTriangles_array(); + +	for (U32 i = 0; i < tris.getCount(); ++i) +	{ +		domTrianglesRef& tri = tris.get(i); + +		status = load_face_from_dom_triangles(pModel->getVolumeFaces(), pModel->getMaterialList(), tri); + +		if(status != LLModel::NO_ERRORS) +		{ +			pModel->ClearFacesAndMaterials(); +			return false; +		} +	} + +	domPolylist_Array& polys = mesh->getPolylist_array(); +	for (U32 i = 0; i < polys.getCount(); ++i) +	{ +		domPolylistRef& poly = polys.get(i); +		status = load_face_from_dom_polylist(pModel->getVolumeFaces(), pModel->getMaterialList(), poly); + +		if(status != LLModel::NO_ERRORS) +		{ +			pModel->ClearFacesAndMaterials(); +			return false; +		} +	} + +	domPolygons_Array& polygons = mesh->getPolygons_array(); + +	for (U32 i = 0; i < polygons.getCount(); ++i) +	{ +		domPolygonsRef& poly = polygons.get(i); +		status = load_face_from_dom_polygons(pModel->getVolumeFaces(), pModel->getMaterialList(), poly); + +		if(status != LLModel::NO_ERRORS) +		{ +			pModel->ClearFacesAndMaterials(); +			return false; +		} +	} + +	return (status == LLModel::NO_ERRORS); +} + +//static  +LLModel* LLDAELoader::loadModelFromDomMesh(domMesh *mesh) +{ +	LLVolumeParams volume_params; +	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); +	LLModel* ret = new LLModel(volume_params, 0.f);  +	createVolumeFacesFromDomMesh(ret, mesh); +    if (ret->mLabel.empty()) +    { +	    ret->mLabel = getElementLabel(mesh); +    } +    return ret; +} + +//static diff version supports creating multiple models when material counts spill +// over the 8 face server-side limit +// +bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out) +{ + +	LLVolumeParams volume_params; +	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + +	models_out.clear(); + +	LLModel* ret = new LLModel(volume_params, 0.f);  + +    ret->mLabel = getElementLabel(mesh);     + +    llassert(!ret->mLabel.empty()); + +	// Like a monkey, ready to be shot into space +	// +	ret->ClearFacesAndMaterials(); + +	// Get the whole set of volume faces +	// +	addVolumeFacesFromDomMesh(ret, mesh); + +	U32 volume_faces = ret->getNumVolumeFaces(); + +	// Side-steps all manner of issues when splitting models +	// and matching lower LOD materials to base models +	// +	ret->sortVolumeFacesByMaterialName(); + +	bool normalized = false; + +    int submodelID = 0; +	LLVolume::face_list_t remainder; +	do  +	{ +		// Insure we do this once with the whole gang and not per-model +		// +		if (!normalized && !mNoNormalize) +		{			 +			normalized = true; +			ret->normalizeVolumeFaces(); +		} + +		ret->trimVolumeFacesToSize(LL_SCULPT_MESH_MAX_FACES, &remainder); + +		if (!mNoOptimize) +		{ +			ret->optimizeVolumeFaces(); +		} + +		volume_faces = remainder.size(); + +		models_out.push_back(ret); + +		// If we have left-over volume faces, create another model +		// to absorb them... +		// +		if (volume_faces) +		{ +			LLModel* next = new LLModel(volume_params, 0.f); +			next->mSubmodelID = ++submodelID; +			next->getVolumeFaces() = remainder; +			next->mNormalizedScale = ret->mNormalizedScale; +			next->mNormalizedTranslation = ret->mNormalizedTranslation; +			next->mMaterialList.assign(ret->mMaterialList.begin() + LL_SCULPT_MESH_MAX_FACES, ret->mMaterialList.end()); +			ret = next; +		} + +		remainder.clear(); + +	} while (volume_faces);	 + +	return true; +} + +bool LLDAELoader::createVolumeFacesFromDomMesh(LLModel* pModel, domMesh* mesh) +{ +	if (mesh) +	{ +		pModel->ClearFacesAndMaterials(); + +		addVolumeFacesFromDomMesh(pModel, mesh); + +		if (pModel->getNumVolumeFaces() > 0) +		{ +			pModel->normalizeVolumeFaces(); +			pModel->optimizeVolumeFaces(); + +			if (pModel->getNumVolumeFaces() > 0) +			{ +				return true; +			} +		} +	} +	else +	{	 +		llwarns << "no mesh found" << llendl; +	} + +	return false; +} diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h new file mode 100644 index 0000000000..e71c407909 --- /dev/null +++ b/indra/llprimitive/lldaeloader.h @@ -0,0 +1,103 @@ +/** + * @file lldaeloader.h + * @brief LLDAELoader class definition + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLDAELOADER_H +#define LL_LLDAELOADER_H + +#include "llmodelloader.h" + +class DAE; +class daeElement; +class domProfile_COMMON; +class domInstance_geometry; +class domNode; +class domTranslate; +class domController; +class domSkin; +class domMesh; + +class LLDAELoader : public LLModelLoader +{ +public: +	typedef std::map<std::string, LLImportMaterial>							material_map; +	typedef std::map<daeElement*, std::vector<LLPointer<LLModel> > >	dae_model_map; +	dae_model_map	mModelsMap; + +	LLDAELoader( +		std::string									filename, +		S32											lod,  +		LLModelLoader::load_callback_t		load_cb, +		LLModelLoader::joint_lookup_func_t	joint_lookup_func, +		LLModelLoader::texture_load_func_t	texture_load_func, +		LLModelLoader::state_callback_t		state_cb, +		void*											opaque_userdata, +		JointTransformMap&						jointMap, +		JointSet&									jointsFromNodes); +	virtual ~LLDAELoader() ; + +	virtual bool OpenFile(const std::string& filename); + +protected: + +	void processElement(daeElement* element, bool& badElement, DAE* dae); +	void processDomModel(LLModel* model, DAE* dae, daeElement* pRoot, domMesh* mesh, domSkin* skin); + +	material_map getMaterials(LLModel* model, domInstance_geometry* instance_geo, DAE* dae); +	LLImportMaterial profileToMaterial(domProfile_COMMON* material, DAE* dae);	 +	LLColor4 getDaeColor(daeElement* element); +	 +	daeElement* getChildFromElement( daeElement* pElement, std::string const & name ); +	 +	bool isNodeAJoint( domNode* pNode ); +	void processJointNode( domNode* pNode, std::map<std::string,LLMatrix4>& jointTransforms ); +	void extractTranslation( domTranslate* pTranslate, LLMatrix4& transform ); +	void extractTranslationViaElement( daeElement* pTranslateElement, LLMatrix4& transform ); +	void extractTranslationViaSID( daeElement* pElement, LLMatrix4& transform ); +	void buildJointToNodeMappingFromScene( daeElement* pRoot ); +	void processJointToNodeMapping( domNode* pNode ); +	void processChildJoints( domNode* pParentNode ); + +	bool verifyCount( int expected, int result ); + +	//Verify that a controller matches vertex counts +	bool verifyController( domController* pController ); + +	static bool addVolumeFacesFromDomMesh(LLModel* model, domMesh* mesh); +	static bool createVolumeFacesFromDomMesh(LLModel* model, domMesh *mesh); + +	static LLModel* loadModelFromDomMesh(domMesh* mesh); + +	// Loads a mesh breaking it into one or more models as necessary +	// to get around volume face limitations while retaining >8 materials +	// +	bool loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out); + +	static std::string getElementLabel(daeElement *element);	 + +private: + +}; +#endif  // LL_LLDAELLOADER_H  | 
