summaryrefslogtreecommitdiff
path: root/indra/llprimitive
diff options
context:
space:
mode:
authorRider Linden <rider@lindenlab.com>2015-09-29 14:16:58 -0700
committerRider Linden <rider@lindenlab.com>2015-09-29 14:16:58 -0700
commit2fe0c321095a8a97ad8d9033430f8cbd4ceddd14 (patch)
tree47f6552b8279e009aa0d6fe5f0310b4926e14601 /indra/llprimitive
parent83836d91178235e5c188020ac57c9a296343e83b (diff)
parent2d52b62e67cf46222396c724d00d2de874819197 (diff)
DRTVWR-409: merge DRTVWR-368
Diffstat (limited to 'indra/llprimitive')
-rwxr-xr-xindra/llprimitive/CMakeLists.txt10
-rw-r--r--indra/llprimitive/lldaeloader.cpp2436
-rw-r--r--indra/llprimitive/lldaeloader.h107
-rwxr-xr-xindra/llprimitive/llmodel.cpp1313
-rwxr-xr-xindra/llprimitive/llmodel.h129
-rw-r--r--indra/llprimitive/llmodelloader.cpp640
-rw-r--r--indra/llprimitive/llmodelloader.h212
-rwxr-xr-xindra/llprimitive/lltextureentry.h4
8 files changed, 3854 insertions, 997 deletions
diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt
index 3f258ee383..dd2e806dda 100755
--- a/indra/llprimitive/CMakeLists.txt
+++ b/indra/llprimitive/CMakeLists.txt
@@ -9,6 +9,7 @@ include(LLMessage)
include(LLCoreHttp)
include(LLXML)
include(LLPhysicsExtensions)
+include(LLCharacter)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
@@ -17,6 +18,7 @@ include_directories(
${LLXML_INCLUDE_DIRS}
${LIBS_PREBUILT_DIR}/include/collada
${LIBS_PREBUILT_DIR}/include/collada/1.4
+ ${LLCHARACTER_INCLUDE_DIRS}
)
include_directories(SYSTEM
${LLCOMMON_SYSTEM_INCLUDE_DIRS}
@@ -25,11 +27,13 @@ include_directories(SYSTEM
)
set(llprimitive_SOURCE_FILES
+ lldaeloader.cpp
llmaterialid.cpp
llmaterial.cpp
llmaterialtable.cpp
llmediaentry.cpp
llmodel.cpp
+ llmodelloader.cpp
llprimitive.cpp
llprimtexturelist.cpp
lltextureanim.cpp
@@ -41,16 +45,17 @@ set(llprimitive_SOURCE_FILES
set(llprimitive_HEADER_FILES
CMakeLists.txt
-
+ lldaeloader.h
legacy_object_types.h
- lllslconstants.h
llmaterial.h
llmaterialid.h
llmaterialtable.h
llmediaentry.h
llmodel.h
+ llmodelloader.h
llprimitive.h
llprimtexturelist.h
+ lllslconstants.h
lltextureanim.h
lltextureentry.h
lltreeparams.h
@@ -74,6 +79,7 @@ target_link_libraries(llprimitive
${LLCOREHTTP_LIBRARIES}
${LLXML_LIBRARIES}
${LLPHYSICSEXTENSIONS_LIBRARIES}
+ ${LLCHARACTER_LIBRARIES}
${BOOST_COROUTINE_LIBRARY}
${BOOST_CONTEXT_LIBRARY}
)
diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
new file mode 100644
index 0000000000..c1b74b1fd7
--- /dev/null
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -0,0 +1,2436 @@
+/**
+ * @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 <boost/lexical_cast.hpp>
+
+#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"
+};
+
+static const std::string lod_suffix[LLModel::NUM_LODS] =
+{
+ "_LOD0",
+ "_LOD1",
+ "_LOD2",
+ "",
+ "_PHYS",
+};
+
+const U32 LIMIT_MATERIALS_OUTPUT = 12;
+
+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))
+ {
+ return LLModel::BAD_ELEMENT;
+ }
+
+ if (!pos_source || !pos_source->getFloat_array())
+ {
+ LL_WARNS() << "Unable to process mesh without position data; invalid model; invalid model." << LL_ENDL;
+ 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)
+ {
+ if(v.getCount() == 0)
+ {
+ return LLModel::BAD_ELEMENT;
+ }
+
+ 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." << LL_ENDL;
+ 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)
+ {
+ 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;
+ }
+
+ 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 (!cv.getPosition().isFinite3())
+ {
+ LL_WARNS() << "Found NaN while loading position data from DAE-Model, invalid model." << LL_ENDL;
+ return LLModel::BAD_ELEMENT;
+ }
+ }
+
+ 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]);
+
+ if (!cv.getNormal().isFinite3())
+ {
+ LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL;
+ return LLModel::BAD_ELEMENT;
+ }
+ }
+
+ 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." << LL_ENDL;
+ 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)
+ {
+ 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;
+ }
+
+ 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,
+ U32 modelLimit)
+: LLModelLoader(
+ filename,
+ lod,
+ load_cb,
+ joint_lookup_func,
+ texture_load_func,
+ state_cb,
+ opaque_userdata,
+ jointMap,
+ jointsFromNodes),
+mGeneratedModelLimit(modelLimit)
+{
+}
+
+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)
+ {
+ LL_INFOS() <<" Error with dae - traditionally indicates a corrupt file."<<LL_ENDL;
+ setLoadState( ERROR_PARSING );
+ return false;
+ }
+ //Dom version
+ daeString domVersion = dae.getDomVersion();
+ std::string sldom(domVersion);
+ LL_INFOS()<<"Collada Importer Version: "<<sldom<<LL_ENDL;
+ //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;
+ }
+ LL_INFOS()<<"Dae version "<<colladaVersion[docVersion]<<LL_ENDL;
+
+
+ daeDatabase* db = dae.getDatabase();
+
+ daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH);
+
+ daeDocument* doc = dae.getDoc(mFilename);
+ if (!doc)
+ {
+ LL_WARNS() << "can't find internal doc" << LL_ENDL;
+ return false;
+ }
+
+ daeElement* root = doc->getDomRoot();
+ if (!root)
+ {
+ LL_WARNS() << "document has no root" << LL_ENDL;
+ 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)
+ {
+ LL_INFOS() << "Could not verify controller" << LL_ENDL;
+ 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();
+
+ U32 submodel_limit = count > 0 ? mGeneratedModelLimit/count : 0;
+ 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, submodel_limit);
+
+ std::vector<LLModel*>::iterator i;
+ i = models.begin();
+ while (i != models.end())
+ {
+ LLModel* mdl = *i;
+ if(mdl->getStatus() != LLModel::NO_ERRORS)
+ {
+ setLoadState(ERROR_MODEL + 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())
+ {
+ LLModel* mdl = *model_iter;
+ U32 material_count = mdl->mMaterialList.size();
+ LL_INFOS() << "Importing " << mdl->mLabel << " model with " << material_count << " material references" << LL_ENDL;
+ std::vector<std::string>::iterator mat_iter = mdl->mMaterialList.begin();
+ std::vector<std::string>::iterator end_iter = material_count > LIMIT_MATERIALS_OUTPUT
+ ? mat_iter + LIMIT_MATERIALS_OUTPUT
+ : mdl->mMaterialList.end();
+ while (mat_iter != end_iter)
+ {
+ LL_INFOS() << mdl->mLabel << " references " << (*mat_iter) << LL_ENDL;
+ 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++;
+ }
+ }
+ }
+ }
+ }
+
+ LL_INFOS()<< "Collada skins processed: " << count <<LL_ENDL;
+
+ daeElement* scene = root->getDescendant("visual_scene");
+
+ if (!scene)
+ {
+ LL_WARNS() << "document has no visual_scene" << LL_ENDL;
+ setLoadState( ERROR_PARSING );
+ return true;
+ }
+
+ setLoadState( DONE );
+
+ bool badElement = false;
+
+ processElement( scene, badElement, &dae );
+
+ if ( badElement )
+ {
+ LL_INFOS()<<"Scene could not be parsed"<<LL_ENDL;
+ 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 )
+ {
+ LL_WARNS()<<"No visual scene - unable to parse bone offsets "<<LL_ENDL;
+ 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() );
+ //LL_WARNS()<<"Joint "<< str <<LL_ENDL;
+
+ //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() )
+ {
+ LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL;
+ 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 )
+ {
+ LL_WARNS()<< "Partial jointmap found in asset - did you mean to just have a partial map?" << LL_ENDL;
+ }
+ }//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() )
+ {
+ //LL_INFOS()<<"joint "<<lookingForJoint.c_str()<<LL_ENDL;
+ LLMatrix4 jointTransform = mJointList[lookingForJoint];
+ LLJoint* pJoint = mJointLookupFunc(lookingForJoint,mOpaqueData);
+ if ( pJoint )
+ {
+ LLUUID fake_mesh_id;
+ fake_mesh_id.generate();
+ pJoint->addAttachmentPosOverride( jointTransform.getTranslation(), fake_mesh_id, "");
+ }
+ else
+ {
+ //Most likely an error in the asset.
+ LL_WARNS()<<"Tried to apply joint position from .dae, but it did not exist in the avatar rig." << LL_ENDL;
+ }
+ }
+ }
+ }
+ } //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
+ {
+ LL_WARNS()<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<" ] "<<LL_ENDL;
+ }
+ }
+
+ //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)
+ {
+ LL_ERRS() << "Invalid position array size." << LL_ENDL;
+ }
+
+ 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
+ {
+ LL_INFOS()<<"Node is NULL"<<LL_ENDL;
+ }
+
+ }
+}
+//-----------------------------------------------------------------------------
+// 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 )
+ {
+ LL_INFOS()<<"Created node is NULL"<<LL_ENDL;
+ return false;
+ }
+
+ return LLModelLoader::isNodeAJoint(pNode->getName());
+}
+//-----------------------------------------------------------------------------
+// verifyCount
+//-----------------------------------------------------------------------------
+bool LLDAELoader::verifyCount( int expected, int result )
+{
+ if ( expected != result )
+ {
+ LL_INFOS()<< "Error: (expected/got)"<<expected<<"/"<<result<<"verts"<<LL_ENDL;
+ 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 )
+ {
+ LL_INFOS()<<"Can't resolve skin source"<<LL_ENDL;
+ 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 )
+ {
+ LL_INFOS()<<"No vertices!"<<LL_ENDL;
+ 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
+ {
+ LL_WARNS()<<"Element is nonexistent - empty/unsupported node."<<LL_ENDL;
+ }
+}
+//-----------------------------------------------------------------------------
+// processJointNode()
+//-----------------------------------------------------------------------------
+void LLDAELoader::processJointNode( domNode* pNode, JointTransformMap& jointTransforms )
+{
+ if (pNode->getName() == NULL)
+ {
+ LL_WARNS() << "nameless node, can't process" << LL_ENDL;
+ return;
+ }
+
+ //LL_WARNS()<<"ProcessJointNode# Node:" <<pNode->getName()<<LL_ENDL;
+
+ //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() )
+ {
+ //LL_WARNS()<< "The found element is not a translate node" <<LL_ENDL;
+ daeSIDResolver jointResolver( pNode, "./matrix" );
+ domMatrix* pMatrix = daeSafeCast<domMatrix>( jointResolver.getElement() );
+ if ( pMatrix )
+ {
+ //LL_INFOS()<<"A matrix SID was however found!"<<LL_ENDL;
+ 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
+ {
+ LL_WARNS()<< "The found element is not translate or matrix node - most likely a corrupt export!" <<LL_ENDL;
+ }
+ }
+ 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;
+ }
+ LL_WARNS()<< "Could not find a child [" << name << "] for the element: \"" << pElement->getAttribute("id") << "\"" << LL_ENDL;
+ 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];
+ }
+ }
+
+ matrix_transform *= mTransform;
+ 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
+ LL_INFOS() << "Negative scale detected, unsupported transform. domInstance_geometry: " << getElementLabel(instance_geo) << LL_ENDL;
+ 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
+ LL_INFOS() << "Negative scale detected, unsupported post-normalization transform. domInstance_geometry: " << getElementLabel(instance_geo) << LL_ENDL;
+ badElement = true;
+ }
+
+ std::string label;
+
+ if (model->mLabel.empty())
+ {
+ label = getLodlessLabel(instance_geo);
+
+ llassert(!label.empty());
+
+ if (model->mSubmodelID)
+ {
+ label += (char)((int)'a' + model->mSubmodelID);
+ }
+
+ model->mLabel = label + lod_suffix[mLod];
+ }
+ else
+ {
+ // Don't change model's name if possible, it will play havoc with scenes that already use said model.
+ size_t ext_pos = getSuffixPosition(model->mLabel);
+ if (ext_pos != -1)
+ {
+ label = model->mLabel.substr(0, ext_pos);
+ }
+ else
+ {
+ label = model->mLabel;
+ }
+ }
+
+ mScene[transformation].push_back(LLModelInstance(model, label, transformation, materials));
+ stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform);
+ i++;
+ }
+ }
+ }
+ else
+ {
+ LL_INFOS()<<"Unable to resolve geometry URL."<<LL_ENDL;
+ 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();
+ std::string index_string;
+ if (parent)
+ {
+ // retrieve index to distinguish items inside same parent
+ size_t ind = 0;
+ parent->getChildren().find(element, ind);
+ index_string = "_" + boost::lexical_cast<std::string>(ind);
+
+ // if parent has a name or ID, use it
+ std::string name = parent->getAttribute("name");
+ if (!name.length())
+ {
+ name = std::string(parent->getID());
+ }
+
+ if (name.length())
+ {
+ // make sure that index won't mix up with pre-named lod extensions
+ size_t ext_pos = getSuffixPosition(name);
+
+ if (ext_pos == -1)
+ {
+ return name + index_string;
+ }
+ else
+ {
+ return name.insert(ext_pos, index_string);
+ }
+ }
+ }
+
+ // try to use our type
+ daeString element_name = element->getElementName();
+ if (element_name)
+ {
+ return std::string(element_name) + index_string;
+ }
+
+ // if all else fails, use "object"
+ return std::string("object") + index_string;
+}
+
+// static
+size_t LLDAELoader::getSuffixPosition(std::string label)
+{
+ if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1))
+ {
+ return label.rfind('_');
+ }
+ return -1;
+}
+
+// static
+std::string LLDAELoader::getLodlessLabel(daeElement *element)
+{
+ std::string label = getElementLabel(element);
+ size_t ext_pos = getSuffixPosition(label);
+ if (ext_pos != -1)
+ {
+ return label.substr(0, ext_pos);
+ }
+ return label;
+}
+
+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);
+ pModel->mStatus = status;
+ 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, U32 submodel_limit)
+{
+
+ 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);
+
+ std::string model_name = getLodlessLabel(mesh);
+ ret->mLabel = model_name + lod_suffix[mLod];
+
+ 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;
+
+ // remove all faces that definitely won't fit into one model and submodel limit
+ U32 face_limit = (submodel_limit + 1) * LL_SCULPT_MESH_MAX_FACES;
+ if (face_limit < volume_faces)
+ {
+ ret->setNumVolumeFaces(face_limit);
+ }
+
+ 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->mLabel = model_name + (char)((int)'a' + next->mSubmodelID) + lod_suffix[mLod];
+ next->getVolumeFaces() = remainder;
+ next->mNormalizedScale = ret->mNormalizedScale;
+ next->mNormalizedTranslation = ret->mNormalizedTranslation;
+ if ( ret->mMaterialList.size() > LL_SCULPT_MESH_MAX_FACES)
+ {
+ 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
+ {
+ LL_WARNS() << "no mesh found" << LL_ENDL;
+ }
+
+ return false;
+}
diff --git a/indra/llprimitive/lldaeloader.h b/indra/llprimitive/lldaeloader.h
new file mode 100644
index 0000000000..7d91a6063b
--- /dev/null
+++ b/indra/llprimitive/lldaeloader.h
@@ -0,0 +1,107 @@
+/**
+ * @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,
+ U32 modelLimit);
+ 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, U32 submodel_limit);
+
+ static std::string getElementLabel(daeElement *element);
+ static size_t getSuffixPosition(std::string label);
+ static std::string getLodlessLabel(daeElement *element);
+
+private:
+ U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
+
+};
+#endif // LL_LLDAELLOADER_H
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index ed903146ef..e494c55250 100755
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -31,18 +31,6 @@
#include "llconvexdecomposition.h"
#include "llsdserialize.h"
#include "llvector4a.h"
-#if LL_MSVC
-#pragma warning (disable : 4263)
-#pragma warning (disable : 4264)
-#endif
-#include "dae.h"
-#include "dae/daeErrorHandler.h"
-#include "dom/domConstants.h"
-#include "dom/domMesh.h"
-#if LL_MSVC
-#pragma warning (default : 4263)
-#pragma warning (default : 4264)
-#endif
#ifdef LL_USESYSTEMLIBS
# include <zlib.h>
@@ -50,8 +38,6 @@
# include "zlib/zlib.h"
#endif
-
-
std::string model_names[] =
{
"lowest_lod",
@@ -65,7 +51,7 @@ const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string);
LLModel::LLModel(LLVolumeParams& params, F32 detail)
: LLVolume(params, detail), mNormalizedScale(1,1,1), mNormalizedTranslation(0,0,0)
- , mPelvisOffset( 0.0f ), mStatus(NO_ERRORS)
+ , mPelvisOffset( 0.0f ), mStatus(NO_ERRORS), mSubmodelID(0)
{
mDecompID = -1;
mLocalID = -1;
@@ -79,939 +65,112 @@ LLModel::~LLModel()
}
}
-
-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)
+//static
+std::string LLModel::getStatusString(U32 status)
{
- idx_stride = 0;
+ const static std::string status_strings[(S32)INVALID_STATUS] = {"status_no_error", "status_vertex_number_overflow","bad_element"};
- for (U32 j = 0; j < inputs.getCount(); ++j)
+ if(status < INVALID_STATUS)
{
- 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)
+ if(status_strings[status] == std::string())
{
- //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();
+ //LL_ERRS() << "No valid status string for this status: " << (U32)status << LL_ENDL();
}
- 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 )
- {
- LL_WARNS() << "Could not find dom sources for basic geo data; invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
+ return status_strings[status] ;
}
- if (!pos_source)
- {
- LL_WARNS() << "Unable to process mesh without position data; invalid model; invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
-
- domPRef p = tri->getP();
- domListOfUInts& idx = p->getValue();
-
- domListOfFloats dummy ;
- domListOfFloats& v = (pos_source && pos_source->getFloat_array()) ? pos_source->getFloat_array()->getValue() : dummy ;
- domListOfFloats& tc = (tc_source && tc_source->getFloat_array()) ? tc_source->getFloat_array()->getValue() : dummy ;
- domListOfFloats& n = (norm_source && norm_source->getFloat_array()) ? norm_source->getFloat_array()->getValue() : dummy ;
+ //LL_ERRS() << "Invalid model status: " << (U32)status << LL_ENDL();
- LLVolumeFace::VertexMapData::PointMap point_map;
-
- U32 index_count = idx.getCount();
- U32 vertex_count = (pos_source && pos_source->getFloat_array()) ? v.getCount() : 0;
- U32 tc_count = (tc_source && tc_source->getFloat_array()) ? tc.getCount() : 0;
- U32 norm_count = (norm_source && norm_source->getFloat_array()) ? n.getCount(): 0;
+ return std::string() ;
+}
- if (vertex_count == 0)
- {
- LL_WARNS() << "Unable to process mesh with empty position array; invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
- face.mExtents[0].set(v[0], v[1], v[2]);
- face.mExtents[1].set(v[0], v[1], v[2]);
+void LLModel::offsetMesh( const LLVector3& pivotPoint )
+{
+ LLVector4a pivot( pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ] );
- for (U32 i = 0; i < index_count; i += idx_stride)
- {
- LLVolumeFace::VertexData cv;
- if (pos_source)
- {
- // guard against model data specifiying out of range indices or verts
- //
- if (((i + pos_offset) > index_count)
- || ((idx[i+pos_offset]*3+2) > vertex_count))
- {
- LL_WARNS() << "Out of range index data; invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
-
- 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 (!cv.getPosition().isFinite3())
- {
- LL_WARNS() << "Nan positional data, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
- }
-
- if (tc_source)
- {
- // guard against model data specifiying out of range indices or tcs
- //
-
- if (((i + tc_offset) > index_count)
- || ((idx[i+tc_offset]*2+1) > tc_count))
- {
- LL_WARNS() << "Out of range TC indices." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
-
- cv.mTexCoord.setVec(tc[idx[i+tc_offset]*2+0],
- tc[idx[i+tc_offset]*2+1]);
-
- if (!cv.mTexCoord.isFinite())
- {
- LL_WARNS() << "Found NaN while loading tex coords from DAE-Model, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
- }
-
- if (norm_source)
- {
- // guard against model data specifiying out of range indices or norms
- //
- if (((i + norm_offset) > index_count)
- || ((idx[i+norm_offset]*3+2) > norm_count))
- {
- LL_WARNS() << "Found out of range norm indices, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
-
- cv.setNormal(LLVector4a(n[idx[i+norm_offset]*3+0],
- n[idx[i+norm_offset]*3+1],
- n[idx[i+norm_offset]*3+2]));
-
- if (!cv.getNormal().isFinite3())
- {
- LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
- }
-
- 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)
- {
- if ((point_iter->second)[j] == cv)
- {
- found = TRUE;
- indices.push_back((point_iter->second)[j].mIndex);
- break;
- }
- }
- }
-
- if (!found)
- {
- update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
- verts.push_back(cv);
- if (verts.size() >= 65535)
- {
- //LL_ERRS() << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL;
- 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)
- {
- 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;
- }
-
- face = LLVolumeFace();
- point_map.clear();
- }
- }
-
- if (!verts.empty())
+ for (std::vector<LLVolumeFace>::iterator faceIt = mVolumeFaces.begin(); faceIt != mVolumeFaces.end(); )
{
- std::string material;
-
- if (tri->getMaterial())
- {
- material = std::string(tri->getMaterial());
- }
+ std::vector<LLVolumeFace>:: iterator currentFaceIt = faceIt++;
+ LLVolumeFace& face = *currentFaceIt;
+ LLVector4a *pos = (LLVector4a*) face.mPositions;
- 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)
+ for (U32 i=0; i<face.mNumVertices; ++i )
{
- //ll_aligned_free_16(new_face.mTexCoords);
- new_face.mTexCoords = NULL;
+ pos[i].add( pivot );
}
}
-
- return LLModel::NO_ERRORS ;
}
-LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolylistRef& poly)
+void LLModel::optimizeVolumeFaces()
{
- 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))
- {
- LL_WARNS() << "Could not get DOM sources for basic geo data, invalid model." << LL_ENDL;
- 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 index_count = idx.getCount();
- U32 vertex_count = pos_source ? v.getCount() : 0;
- U32 tc_count = tc_source ? tc.getCount() : 0;
- U32 norm_count = norm_source ? n.getCount() : 0;
-
- 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)
- {
- // guard against model data specifiying out of range indices or verts
- //
- if (((cur_idx + pos_offset) > index_count)
- || ((idx[cur_idx+pos_offset]*3+2) > vertex_count))
- {
- LL_WARNS() << "Out of range position indices, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
-
- 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 (!cv.getPosition().isFinite3())
- {
- LL_WARNS() << "Found NaN while loading positions from DAE-Model, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
-
- }
-
- if (tc_source)
- {
- // guard against model data specifiying out of range indices or tcs
- //
- if (((cur_idx + tc_offset) > index_count)
- || ((idx[cur_idx+tc_offset]*2+1) > tc_count))
- {
- LL_WARNS() << "Out of range TC indices, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
-
- cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0],
- tc[idx[cur_idx+tc_offset]*2+1]);
-
- if (!cv.mTexCoord.isFinite())
- {
- LL_WARNS() << "Found NaN while loading tex coords from DAE-Model, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
- }
-
- if (norm_source)
- {
- // guard against model data specifiying out of range indices or norms
- //
- if (((cur_idx + norm_offset) > index_count)
- || ((idx[cur_idx+norm_offset]*3+2) > norm_count))
- {
- LL_WARNS() << "Out of range norm indices, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
-
- 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]);
-
- if (!cv.getNormal().isFinite3())
- {
- LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
- }
-
- 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
- {
- 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)
- {
- //LL_ERRS() << "Attempted to write model exceeding 16-bit index buffer limitation." << LL_ENDL;
- 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
- {
- 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)
- {
- 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;
- }
-
- face = LLVolumeFace();
- verts.clear();
- indices.clear();
- point_map.clear();
- }
- }
- }
-
- if (!verts.empty())
+ for (U32 i = 0; i < getNumVolumeFaces(); ++i)
{
- 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;
- }
+ mVolumeFaces[i].optimize();
}
-
- return LLModel::NO_ERRORS ;
}
-LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& face_list, std::vector<std::string>& materials, domPolygonsRef& poly)
+struct MaterialBinding
{
- 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)
- {
- LL_WARNS() << "Could not find vertex source, invalid model." << LL_ENDL;
- 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)
- {
- LL_WARNS() << "Could not find DOM source, invalid model." << LL_ENDL;
- 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)
- {
- LL_WARNS() << "Could not find DOM source, invalid model." << LL_ENDL;
- 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)
- {
- LL_WARNS() << "Could not find DOM source, invalid model." << LL_ENDL;
- 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));
-
- if (!vert.getPosition().isFinite3())
- {
- LL_WARNS() << "Found NaN while loading position data from DAE-Model, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
- }
-
- //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));
-
- if (!vert.getNormal().isFinite3())
- {
- LL_WARNS() << "Found NaN while loading normals from DAE-Model, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
- }
- 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));
-
- if (!vert.mTexCoord.isFinite())
- {
- LL_WARNS() << "Found NaN while loading tex coords from DAE-Model, invalid model." << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
- }
- 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]];
- }
-
- // 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 ;
-}
+ int index;
+ std::string matName;
+};
-//static
-std::string LLModel::getStatusString(U32 status)
+struct MaterialSort
{
- const static std::string status_strings[(S32)INVALID_STATUS] = {"status_no_error", "status_vertex_number_overflow","bad_element"};
-
- if(status < INVALID_STATUS)
+ bool operator()(const MaterialBinding& lhs, const MaterialBinding& rhs)
{
- if(status_strings[status] == std::string())
- {
- LL_ERRS() << "No valid status string for this status: " << (U32)status << LL_ENDL ;
- }
- return status_strings[status] ;
+ return LLStringUtil::compareInsensitive(lhs.matName, rhs.matName) < 0;
}
+};
- LL_ERRS() << "Invalid model status: " << (U32)status << LL_ENDL ;
-
- return std::string() ;
-}
-
-void LLModel::addVolumeFacesFromDomMesh(domMesh* mesh)
+void LLModel::sortVolumeFacesByMaterialName()
{
- domTriangles_Array& tris = mesh->getTriangles_array();
-
- for (U32 i = 0; i < tris.getCount(); ++i)
- {
- domTrianglesRef& tri = tris.get(i);
+ std::vector<MaterialBinding> bindings;
+ bindings.resize(mVolumeFaces.size());
- mStatus = load_face_from_dom_triangles(mVolumeFaces, mMaterialList, tri);
-
- if(mStatus != NO_ERRORS)
+ for (int i = 0; i < bindings.size(); i++)
+ {
+ bindings[i].index = i;
+ if(i < mMaterialList.size())
{
- mVolumeFaces.clear() ;
- mMaterialList.clear() ;
- return ; //abort
+ bindings[i].matName = mMaterialList[i];
}
}
+ std::sort(bindings.begin(), bindings.end(), MaterialSort());
+ std::vector< LLVolumeFace > new_faces;
- domPolylist_Array& polys = mesh->getPolylist_array();
- for (U32 i = 0; i < polys.getCount(); ++i)
+ // remap the faces to be in the same order the mats now are...
+ //
+ new_faces.resize(bindings.size());
+ for (int i = 0; i < bindings.size(); i++)
{
- domPolylistRef& poly = polys.get(i);
- mStatus = load_face_from_dom_polylist(mVolumeFaces, mMaterialList, poly);
-
- if(mStatus != NO_ERRORS)
+ new_faces[i] = mVolumeFaces[bindings[i].index];
+ if(i < mMaterialList.size())
{
- mVolumeFaces.clear() ;
- mMaterialList.clear() ;
- return ; //abort
+ mMaterialList[i] = bindings[i].matName;
}
}
-
- domPolygons_Array& polygons = mesh->getPolygons_array();
-
- for (U32 i = 0; i < polygons.getCount(); ++i)
- {
- domPolygonsRef& poly = polygons.get(i);
- mStatus = load_face_from_dom_polygons(mVolumeFaces, mMaterialList, poly);
- if(mStatus != NO_ERRORS)
- {
- mVolumeFaces.clear() ;
- mMaterialList.clear() ;
- return ; //abort
- }
- }
-
+ mVolumeFaces = new_faces;
}
-BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh)
+void LLModel::trimVolumeFacesToSize(U32 new_count, LLVolume::face_list_t* remainder)
{
- if (mesh)
- {
- mVolumeFaces.clear();
- mMaterialList.clear();
-
- addVolumeFacesFromDomMesh(mesh);
-
- if (getNumVolumeFaces() > 0)
- {
- normalizeVolumeFaces();
- optimizeVolumeFaces();
-
- if (getNumVolumeFaces() > 0)
- {
- return TRUE;
- }
- }
- }
- else
- {
- LL_WARNS() << "no mesh found" << LL_ENDL;
- }
-
- return FALSE;
-}
+ llassert(new_count <= LL_SCULPT_MESH_MAX_FACES);
-void LLModel::offsetMesh( const LLVector3& pivotPoint )
-{
- LLVector4a pivot( pivotPoint[VX], pivotPoint[VY], pivotPoint[VZ] );
-
- for (std::vector<LLVolumeFace>::iterator faceIt = mVolumeFaces.begin(); faceIt != mVolumeFaces.end(); )
+ if (new_count && (getNumVolumeFaces() > new_count))
{
- std::vector<LLVolumeFace>:: iterator currentFaceIt = faceIt++;
- LLVolumeFace& face = *currentFaceIt;
- LLVector4a *pos = (LLVector4a*) face.mPositions;
-
- for (U32 i=0; i<face.mNumVertices; ++i )
+ // Copy out remaining volume faces for alternative handling, if provided
+ //
+ if (remainder)
{
- pos[i].add( pivot );
- }
- }
-}
+ (*remainder).assign(mVolumeFaces.begin() + new_count, mVolumeFaces.end());
+ }
-void LLModel::optimizeVolumeFaces()
-{
- for (U32 i = 0; i < getNumVolumeFaces(); ++i)
- {
- mVolumeFaces[i].optimize();
+ // Trim down to the final set of volume faces (now stuffed to the gills!)
+ //
+ mVolumeFaces.resize(new_count);
}
}
@@ -1025,11 +184,6 @@ void LLModel::optimizeVolumeFaces()
// within the unit cube.
void LLModel::normalizeVolumeFaces()
{
-
- // ensure we don't have too many faces
- if (mVolumeFaces.size() > LL_SCULPT_MESH_MAX_FACES)
- mVolumeFaces.resize(LL_SCULPT_MESH_MAX_FACES);
-
if (!mVolumeFaces.empty())
{
LLVector4a min, max;
@@ -1496,68 +650,10 @@ void LLModel::generateNormals(F32 angle_cutoff)
}
}
-//static
-std::string LLModel::getElementLabel(daeElement *element)
-{ // try to get a decent label for this 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");
-}
-
-//static
-LLModel* LLModel::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);
- ret->createVolumeFacesFromDomMesh(mesh);
- ret->mLabel = getElementLabel(mesh);
- return ret;
-}
std::string LLModel::getName() const
{
- if (!mRequestedLabel.empty())
- return mRequestedLabel;
- else
- return mLabel;
+ return mRequestedLabel.empty() ? mLabel : mRequestedLabel;
}
//static
@@ -1572,7 +668,8 @@ LLSD LLModel::writeModel(
BOOL upload_skin,
BOOL upload_joints,
BOOL nowrite,
- BOOL as_slm)
+ BOOL as_slm,
+ int submodel_id)
{
LLSD mdl;
@@ -1601,6 +698,14 @@ LLSD LLModel::writeModel(
model[LLModel::LOD_PHYSICS] = NULL;
}
}
+ else if (submodel_id)
+ {
+ const LLModel::Decomposition fake_decomp;
+ mdl["secondary"] = true;
+ mdl["submodel_id"] = submodel_id;
+ mdl["physics_convex"] = fake_decomp.asLLSD();
+ model[LLModel::LOD_PHYSICS] = NULL;
+ }
if (as_slm)
{ //save material list names
@@ -1612,7 +717,7 @@ LLSD LLModel::writeModel(
for (U32 idx = 0; idx < MODEL_NAMES_LENGTH; ++idx)
{
- if (model[idx] && model[idx]->getNumVolumeFaces() > 0)
+ if (model[idx] && (model[idx]->getNumVolumeFaces() > 0) && model[idx]->getVolumeFace(0).mPositions != NULL)
{
LLVector3 min_pos = LLVector3(model[idx]->getVolumeFace(0).mPositions[0].getF32ptr());
LLVector3 max_pos = min_pos;
@@ -1845,6 +950,11 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO
}
}
+ if (mdl.has("submodel_id"))
+ { //write out submodel id
+ header["submodel_id"] = (LLSD::Integer)mdl["submodel_id"];
+ }
+
std::string out[MODEL_NAMES_LENGTH];
for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++)
@@ -2028,7 +1138,9 @@ bool LLModel::loadModel(std::istream& is)
}
}
- static const std::string nm[] =
+ mSubmodelID = header.has("submodel_id") ? header["submodel_id"].asInteger() : false;
+
+ static const std::string lod_name[] =
{
"lowest_lod",
"low_lod",
@@ -2041,8 +1153,8 @@ bool LLModel::loadModel(std::istream& is)
S32 lod = llclamp((S32) mDetail, 0, MODEL_LODS);
- if (header[nm[lod]]["offset"].asInteger() == -1 ||
- header[nm[lod]]["size"].asInteger() == 0 )
+ if (header[lod_name[lod]]["offset"].asInteger() == -1 ||
+ header[lod_name[lod]]["size"].asInteger() == 0 )
{ //cannot load requested LOD
LL_WARNS() << "LoD data is invalid!" << LL_ENDL;
return false;
@@ -2051,23 +1163,23 @@ bool LLModel::loadModel(std::istream& is)
bool has_skin = header["skin"]["offset"].asInteger() >=0 &&
header["skin"]["size"].asInteger() > 0;
- if (lod == LLModel::LOD_HIGH)
+ if ((lod == LLModel::LOD_HIGH) && !mSubmodelID)
{ //try to load skin info and decomp info
std::ios::pos_type cur_pos = is.tellg();
loadSkinInfo(header, is);
is.seekg(cur_pos);
}
- if (lod == LLModel::LOD_HIGH || lod == LLModel::LOD_PHYSICS)
+ if ((lod == LLModel::LOD_HIGH || lod == LLModel::LOD_PHYSICS) && !mSubmodelID)
{
std::ios::pos_type cur_pos = is.tellg();
loadDecomposition(header, is);
is.seekg(cur_pos);
}
- is.seekg(header[nm[lod]]["offset"].asInteger(), std::ios_base::cur);
+ is.seekg(header[lod_name[lod]]["offset"].asInteger(), std::ios_base::cur);
- if (unpackVolumeFaces(is, header[nm[lod]]["size"].asInteger()))
+ if (unpackVolumeFaces(is, header[lod_name[lod]]["size"].asInteger()))
{
if (has_skin)
{
@@ -2133,8 +1245,10 @@ bool LLModel::isMaterialListSubset( LLModel* ref )
break;
}
}
+
if (!foundRef)
{
+ LL_INFOS() << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL;
return false;
}
}
@@ -2185,41 +1299,42 @@ bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCn
for (U32 i = 0; i < mMaterialList.size(); i++)
{
index_map[ref->mMaterialList[i]] = i;
- if (!reorder)
- { //if any material name does not match reference, we need to reorder
- reorder = ref->mMaterialList[i] != mMaterialList[i];
- }
+ //if any material name does not match reference, we need to reorder
+ reorder |= ref->mMaterialList[i] != mMaterialList[i];
base_mat.insert(ref->mMaterialList[i]);
cur_mat.insert(mMaterialList[i]);
}
- if (reorder &&
- base_mat == cur_mat) //don't reorder if material name sets don't match
+ if (reorder && (base_mat == cur_mat)) //don't reorder if material name sets don't match
{
std::vector<LLVolumeFace> new_face_list;
- new_face_list.resize(mVolumeFaces.size());
+ new_face_list.resize(mMaterialList.size());
std::vector<std::string> new_material_list;
- new_material_list.resize(mVolumeFaces.size());
+ new_material_list.resize(mMaterialList.size());
//rebuild face list so materials have the same order
//as the reference model
for (U32 i = 0; i < mMaterialList.size(); ++i)
{
U32 ref_idx = index_map[mMaterialList[i]];
- new_face_list[ref_idx] = mVolumeFaces[i];
+ if (i < mVolumeFaces.size())
+ {
+ new_face_list[ref_idx] = mVolumeFaces[i];
+ }
new_material_list[ref_idx] = mMaterialList[i];
}
llassert(new_material_list == ref->mMaterialList);
mVolumeFaces = new_face_list;
- }
- //override material list with reference model ordering
- mMaterialList = ref->mMaterialList;
+ //override material list with reference model ordering
+ mMaterialList = ref->mMaterialList;
+ }
+
return true;
}
@@ -2250,7 +1365,7 @@ bool LLModel::loadDecomposition(LLSD& header, std::istream& is)
S32 offset = header["physics_convex"]["offset"].asInteger();
S32 size = header["physics_convex"]["size"].asInteger();
- if (offset >= 0 && size > 0)
+ if (offset >= 0 && size > 0 && !mSubmodelID)
{
is.seekg(offset, std::ios_base::cur);
@@ -2658,3 +1773,227 @@ void LLModel::Decomposition::merge(const LLModel::Decomposition* rhs)
}
}
+bool ll_is_degenerate(const LLVector4a& a, const LLVector4a& b, const LLVector4a& c, F32 tolerance)
+{
+ // small area check
+ {
+ LLVector4a edge1; edge1.setSub( a, b );
+ LLVector4a edge2; edge2.setSub( a, c );
+ //////////////////////////////////////////////////////////////////////////
+ /// Linden Modified
+ //////////////////////////////////////////////////////////////////////////
+
+ // If no one edge is more than 10x longer than any other edge, we weaken
+ // the tolerance by a factor of 1e-4f.
+
+ LLVector4a edge3; edge3.setSub( c, b );
+ const F32 len1sq = edge1.dot3(edge1).getF32();
+ const F32 len2sq = edge2.dot3(edge2).getF32();
+ const F32 len3sq = edge3.dot3(edge3).getF32();
+ bool abOK = (len1sq <= 100.f * len2sq) && (len1sq <= 100.f * len3sq);
+ bool acOK = (len2sq <= 100.f * len1sq) && (len1sq <= 100.f * len3sq);
+ bool cbOK = (len3sq <= 100.f * len1sq) && (len1sq <= 100.f * len2sq);
+ if ( abOK && acOK && cbOK )
+ {
+ tolerance *= 1e-4f;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ /// End Modified
+ //////////////////////////////////////////////////////////////////////////
+
+ LLVector4a cross; cross.setCross3( edge1, edge2 );
+
+ LLVector4a edge1b; edge1b.setSub( b, a );
+ LLVector4a edge2b; edge2b.setSub( b, c );
+ LLVector4a crossb; crossb.setCross3( edge1b, edge2b );
+
+ if ( ( cross.dot3(cross).getF32() < tolerance ) || ( crossb.dot3(crossb).getF32() < tolerance ))
+ {
+ return true;
+ }
+ }
+
+ // point triangle distance check
+ {
+ LLVector4a Q; Q.setSub(a, b);
+ LLVector4a R; R.setSub(c, b);
+
+ const F32 QQ = dot3fpu(Q, Q);
+ const F32 RR = dot3fpu(R, R);
+ const F32 QR = dot3fpu(R, Q);
+
+ volatile F32 QQRR = QQ * RR;
+ volatile F32 QRQR = QR * QR;
+ F32 Det = (QQRR - QRQR);
+
+ if( Det == 0.0f )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool validate_face(const LLVolumeFace& face)
+{
+ for (U32 i = 0; i < face.mNumIndices; ++i)
+ {
+ if (face.mIndices[i] >= face.mNumVertices)
+ {
+ LL_WARNS() << "Face has invalid index." << LL_ENDL;
+ return false;
+ }
+ }
+
+ if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0)
+ {
+ LL_WARNS() << "Face has invalid number of indices." << LL_ENDL;
+ return false;
+ }
+
+ /*const LLVector4a scale(0.5f);
+
+ for (U32 i = 0; i < face.mNumIndices; i+=3)
+ {
+ U16 idx1 = face.mIndices[i];
+ U16 idx2 = face.mIndices[i+1];
+ U16 idx3 = face.mIndices[i+2];
+
+ LLVector4a v1; v1.setMul(face.mPositions[idx1], scale);
+ LLVector4a v2; v2.setMul(face.mPositions[idx2], scale);
+ LLVector4a v3; v3.setMul(face.mPositions[idx3], scale);
+
+ if (ll_is_degenerate(v1,v2,v3))
+ {
+ llwarns << "Degenerate face found!" << LL_ENDL;
+ return false;
+ }
+ }*/
+
+ return true;
+}
+
+bool validate_model(const LLModel* mdl)
+{
+ if (mdl->getNumVolumeFaces() == 0)
+ {
+ LL_WARNS() << "Model has no faces!" << LL_ENDL;
+ return false;
+ }
+
+ for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
+ {
+ if (mdl->getVolumeFace(i).mNumVertices == 0)
+ {
+ LL_WARNS() << "Face has no vertices." << LL_ENDL;
+ return false;
+ }
+
+ if (mdl->getVolumeFace(i).mNumIndices == 0)
+ {
+ LL_WARNS() << "Face has no indices." << LL_ENDL;
+ return false;
+ }
+
+ if (!validate_face(mdl->getVolumeFace(i)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+LLModelInstance::LLModelInstance(LLSD& data)
+ : LLModelInstanceBase()
+{
+ mLocalMeshID = data["mesh_id"].asInteger();
+ mLabel = data["label"].asString();
+ mTransform.setValue(data["transform"]);
+
+ for (U32 i = 0; i < data["material"].size(); ++i)
+ {
+ LLImportMaterial mat(data["material"][i]);
+ mMaterial[mat.mBinding] = mat;
+ }
+}
+
+
+LLSD LLModelInstance::asLLSD()
+{
+ LLSD ret;
+
+ ret["mesh_id"] = mModel->mLocalID;
+ ret["label"] = mLabel;
+ ret["transform"] = mTransform.getValue();
+
+ U32 i = 0;
+ for (std::map<std::string, LLImportMaterial>::iterator iter = mMaterial.begin(); iter != mMaterial.end(); ++iter)
+ {
+ ret["material"][i++] = iter->second.asLLSD();
+ }
+
+ return ret;
+}
+
+
+LLImportMaterial::~LLImportMaterial()
+{
+}
+
+LLImportMaterial::LLImportMaterial(LLSD& data)
+{
+ mDiffuseMapFilename = data["diffuse"]["filename"].asString();
+ mDiffuseMapLabel = data["diffuse"]["label"].asString();
+ mDiffuseColor.setValue(data["diffuse"]["color"]);
+ mFullbright = data["fullbright"].asBoolean();
+ mBinding = data["binding"].asString();
+}
+
+
+LLSD LLImportMaterial::asLLSD()
+{
+ LLSD ret;
+
+ ret["diffuse"]["filename"] = mDiffuseMapFilename;
+ ret["diffuse"]["label"] = mDiffuseMapLabel;
+ ret["diffuse"]["color"] = mDiffuseColor.getValue();
+ ret["fullbright"] = mFullbright;
+ ret["binding"] = mBinding;
+
+ return ret;
+}
+
+bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const
+{
+
+ if (mDiffuseMapID != rhs.mDiffuseMapID)
+ {
+ return mDiffuseMapID < rhs.mDiffuseMapID;
+ }
+
+ if (mDiffuseMapFilename != rhs.mDiffuseMapFilename)
+ {
+ return mDiffuseMapFilename < rhs.mDiffuseMapFilename;
+ }
+
+ if (mDiffuseMapLabel != rhs.mDiffuseMapLabel)
+ {
+ return mDiffuseMapLabel < rhs.mDiffuseMapLabel;
+ }
+
+ if (mDiffuseColor != rhs.mDiffuseColor)
+ {
+ return mDiffuseColor < rhs.mDiffuseColor;
+ }
+
+ if (mBinding != rhs.mBinding)
+ {
+ return mBinding < rhs.mBinding;
+ }
+
+ return mFullbright < rhs.mFullbright;
+}
+
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index aaafc55258..ae602c09df 100755
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -31,6 +31,7 @@
#include "llvolume.h"
#include "v4math.h"
#include "m4math.h"
+#include <queue>
class daeElement;
class domMesh;
@@ -138,15 +139,16 @@ public:
BOOL upload_skin,
BOOL upload_joints,
BOOL nowrite = FALSE,
- BOOL as_slm = FALSE);
+ BOOL as_slm = FALSE,
+ int submodel_id = 0);
static LLSD writeModelToStream(
std::ostream& ostr,
LLSD& mdl,
BOOL nowrite = FALSE, BOOL as_slm = FALSE);
+
+ void ClearFacesAndMaterials() { mVolumeFaces.clear(); mMaterialList.clear(); }
- static LLModel* loadModelFromDomMesh(domMesh* mesh);
- static std::string getElementLabel(daeElement* element);
std::string getName() const;
std::string getMetric() const {return mMetric;}
EModelStatus getStatus() const {return mStatus;}
@@ -169,20 +171,25 @@ public:
void addFace(const LLVolumeFace& face);
+ void sortVolumeFacesByMaterialName();
void normalizeVolumeFaces();
+ void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
void optimizeVolumeFaces();
void offsetMesh( const LLVector3& pivotPoint );
void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out);
LLVector3 getTransformedCenter(const LLMatrix4& mat);
-
+
//reorder face list based on mMaterialList in this and reference so
//order matches that of reference (material ordering touchup)
bool matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCnt );
bool isMaterialListSubset( LLModel* ref );
bool needToAddFaces( LLModel* ref, int& refFaceCnt, int& modelFaceCnt );
-
- std::vector<std::string> mMaterialList;
+ typedef std::vector<std::string> material_list;
+
+ material_list mMaterialList;
+
+ material_list& getMaterialList() { return mMaterialList; }
//data used for skin weights
class JointWeight
@@ -275,9 +282,115 @@ public:
Decomposition mPhysics;
EModelStatus mStatus ;
+
+ int mSubmodelID;
+};
+
+typedef std::vector<LLPointer<LLModel> > model_list;
+typedef std::queue<LLPointer<LLModel> > model_queue;
+
+class LLModelMaterialBase
+{
+public:
+ std::string mDiffuseMapFilename;
+ std::string mDiffuseMapLabel;
+ std::string mBinding;
+ LLColor4 mDiffuseColor;
+ bool mFullbright;
+
+ LLModelMaterialBase()
+ : mFullbright(false)
+ {
+ mDiffuseColor.set(1,1,1,1);
+ }
+};
+
+class LLImportMaterial : public LLModelMaterialBase
+{
+public:
+ friend class LLMeshUploadThread;
+ friend class LLModelPreview;
+
+ bool operator<(const LLImportMaterial &params) const;
+
+ LLImportMaterial() : LLModelMaterialBase()
+ {
+ mDiffuseColor.set(1,1,1,1);
+ }
+
+ LLImportMaterial(LLSD& data);
+ virtual ~LLImportMaterial();
+
+ LLSD asLLSD();
+
+ const LLUUID& getDiffuseMap() const { return mDiffuseMapID; }
+ void setDiffuseMap(const LLUUID& texId) { mDiffuseMapID = texId; }
+
protected:
- void addVolumeFacesFromDomMesh(domMesh* mesh);
- virtual BOOL createVolumeFacesFromDomMesh(domMesh *mesh);
+
+ LLUUID mDiffuseMapID;
+ void* mOpaqueData; // allow refs to viewer/platform-specific structs for each material
+ // currently only stores an LLPointer< LLViewerFetchedTexture > > to
+ // maintain refs to textures associated with each material for free
+ // ref counting.
};
+typedef std::map<std::string, LLImportMaterial> material_map;
+
+class LLModelInstanceBase
+{
+public:
+ LLPointer<LLModel> mModel;
+ LLPointer<LLModel> mLOD[5];
+ LLUUID mMeshID;
+
+ LLMatrix4 mTransform;
+ material_map mMaterial;
+
+ LLModelInstanceBase(LLModel* model, LLMatrix4& transform, material_map& materials)
+ : mModel(model), mTransform(transform), mMaterial(materials)
+ {
+ }
+
+ LLModelInstanceBase()
+ : mModel(NULL)
+ {
+ }
+};
+
+typedef std::vector<LLModelInstanceBase> model_instance_list;
+
+class LLModelInstance : public LLModelInstanceBase
+{
+public:
+ std::string mLabel;
+ LLUUID mMeshID;
+ S32 mLocalMeshID;
+
+ LLModelInstance(LLModel* model, const std::string& label, LLMatrix4& transform, material_map& materials)
+ : LLModelInstanceBase(model, transform, materials), mLabel(label)
+ {
+ mLocalMeshID = -1;
+ }
+
+ LLModelInstance(LLSD& data);
+
+ LLSD asLLSD();
+};
+
+#define LL_DEGENERACY_TOLERANCE 1e-7f
+
+inline F32 dot3fpu(const LLVector4a& a, const LLVector4a& b)
+{
+ volatile F32 p0 = a[0] * b[0];
+ volatile F32 p1 = a[1] * b[1];
+ volatile F32 p2 = a[2] * b[2];
+ return p0 + p1 + p2;
+}
+
+bool ll_is_degenerate(const LLVector4a& a, const LLVector4a& b, const LLVector4a& c, F32 tolerance = LL_DEGENERACY_TOLERANCE);
+
+bool validate_face(const LLVolumeFace& face);
+bool validate_model(const LLModel* mdl);
+
#endif //LL_LLMODEL_H
diff --git a/indra/llprimitive/llmodelloader.cpp b/indra/llprimitive/llmodelloader.cpp
new file mode 100644
index 0000000000..81d92d151b
--- /dev/null
+++ b/indra/llprimitive/llmodelloader.cpp
@@ -0,0 +1,640 @@
+/**
+ * @file llmodelloader.cpp
+ * @brief LLModelLoader class implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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$
+ */
+
+#include "llmodelloader.h"
+#include "llsdserialize.h"
+#include "lljoint.h"
+#include "llcallbacklist.h"
+
+#include "glh/glh_linear.h"
+#include "llmatrix4a.h"
+#include <boost/bind.hpp>
+
+std::list<LLModelLoader*> LLModelLoader::sActiveLoaderList;
+
+void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform)
+{
+ LLVector4a box[] =
+ {
+ LLVector4a(-1, 1,-1),
+ LLVector4a(-1, 1, 1),
+ LLVector4a(-1,-1,-1),
+ LLVector4a(-1,-1, 1),
+ LLVector4a( 1, 1,-1),
+ LLVector4a( 1, 1, 1),
+ LLVector4a( 1,-1,-1),
+ LLVector4a( 1,-1, 1),
+ };
+
+ for (S32 j = 0; j < model->getNumVolumeFaces(); ++j)
+ {
+ const LLVolumeFace& face = model->getVolumeFace(j);
+
+ LLVector4a center;
+ center.setAdd(face.mExtents[0], face.mExtents[1]);
+ center.mul(0.5f);
+ LLVector4a size;
+ size.setSub(face.mExtents[1],face.mExtents[0]);
+ size.mul(0.5f);
+
+ for (U32 i = 0; i < 8; i++)
+ {
+ LLVector4a t;
+ t.setMul(size, box[i]);
+ t.add(center);
+
+ LLVector4a v;
+
+ mat.affineTransform(t, v);
+
+ if (first_transform)
+ {
+ first_transform = FALSE;
+ min = max = v;
+ }
+ else
+ {
+ update_min_max(min, max, v);
+ }
+ }
+ }
+}
+
+void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform)
+{
+ LLVector4a mina, maxa;
+ LLMatrix4a mata;
+
+ mata.loadu(mat);
+ mina.load3(min.mV);
+ maxa.load3(max.mV);
+
+ stretch_extents(model, mata, mina, maxa, first_transform);
+
+ min.set(mina.getF32ptr());
+ max.set(maxa.getF32ptr());
+}
+
+//-----------------------------------------------------------------------------
+// LLModelLoader
+//-----------------------------------------------------------------------------
+LLModelLoader::LLModelLoader(
+ 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 )
+: mJointList( jointMap )
+, mJointsFromNode( jointsFromNodes )
+, LLThread("Model Loader")
+, mFilename(filename)
+, mLod(lod)
+, mFirstTransform(TRUE)
+, mNumOfFetchingTextures(0)
+, mLoadCallback(load_cb)
+, mJointLookupFunc(joint_lookup_func)
+, mTextureLoadFunc(texture_load_func)
+, mStateCallback(state_cb)
+, mOpaqueData(opaque_userdata)
+, mNoNormalize(false)
+, mNoOptimize(false)
+, mCacheOnlyHitIfRigged(false)
+{
+ mJointMap["mPelvis"] = "mPelvis";
+ mJointMap["mTorso"] = "mTorso";
+ mJointMap["mChest"] = "mChest";
+ mJointMap["mNeck"] = "mNeck";
+ mJointMap["mHead"] = "mHead";
+ mJointMap["mSkull"] = "mSkull";
+ mJointMap["mEyeRight"] = "mEyeRight";
+ mJointMap["mEyeLeft"] = "mEyeLeft";
+ mJointMap["mCollarLeft"] = "mCollarLeft";
+ mJointMap["mShoulderLeft"] = "mShoulderLeft";
+ mJointMap["mElbowLeft"] = "mElbowLeft";
+ mJointMap["mWristLeft"] = "mWristLeft";
+ mJointMap["mCollarRight"] = "mCollarRight";
+ mJointMap["mShoulderRight"] = "mShoulderRight";
+ mJointMap["mElbowRight"] = "mElbowRight";
+ mJointMap["mWristRight"] = "mWristRight";
+ mJointMap["mHipRight"] = "mHipRight";
+ mJointMap["mKneeRight"] = "mKneeRight";
+ mJointMap["mAnkleRight"] = "mAnkleRight";
+ mJointMap["mFootRight"] = "mFootRight";
+ mJointMap["mToeRight"] = "mToeRight";
+ mJointMap["mHipLeft"] = "mHipLeft";
+ mJointMap["mKneeLeft"] = "mKneeLeft";
+ mJointMap["mAnkleLeft"] = "mAnkleLeft";
+ mJointMap["mFootLeft"] = "mFootLeft";
+ mJointMap["mToeLeft"] = "mToeLeft";
+
+ mJointMap["avatar_mPelvis"] = "mPelvis";
+ mJointMap["avatar_mTorso"] = "mTorso";
+ mJointMap["avatar_mChest"] = "mChest";
+ mJointMap["avatar_mNeck"] = "mNeck";
+ mJointMap["avatar_mHead"] = "mHead";
+ mJointMap["avatar_mSkull"] = "mSkull";
+ mJointMap["avatar_mEyeRight"] = "mEyeRight";
+ mJointMap["avatar_mEyeLeft"] = "mEyeLeft";
+ mJointMap["avatar_mCollarLeft"] = "mCollarLeft";
+ mJointMap["avatar_mShoulderLeft"] = "mShoulderLeft";
+ mJointMap["avatar_mElbowLeft"] = "mElbowLeft";
+ mJointMap["avatar_mWristLeft"] = "mWristLeft";
+ mJointMap["avatar_mCollarRight"] = "mCollarRight";
+ mJointMap["avatar_mShoulderRight"] = "mShoulderRight";
+ mJointMap["avatar_mElbowRight"] = "mElbowRight";
+ mJointMap["avatar_mWristRight"] = "mWristRight";
+ mJointMap["avatar_mHipRight"] = "mHipRight";
+ mJointMap["avatar_mKneeRight"] = "mKneeRight";
+ mJointMap["avatar_mAnkleRight"] = "mAnkleRight";
+ mJointMap["avatar_mFootRight"] = "mFootRight";
+ mJointMap["avatar_mToeRight"] = "mToeRight";
+ mJointMap["avatar_mHipLeft"] = "mHipLeft";
+ mJointMap["avatar_mKneeLeft"] = "mKneeLeft";
+ mJointMap["avatar_mAnkleLeft"] = "mAnkleLeft";
+ mJointMap["avatar_mFootLeft"] = "mFootLeft";
+ mJointMap["avatar_mToeLeft"] = "mToeLeft";
+
+
+ mJointMap["hip"] = "mPelvis";
+ mJointMap["abdomen"] = "mTorso";
+ mJointMap["chest"] = "mChest";
+ mJointMap["neck"] = "mNeck";
+ mJointMap["head"] = "mHead";
+ mJointMap["figureHair"] = "mSkull";
+ mJointMap["lCollar"] = "mCollarLeft";
+ mJointMap["lShldr"] = "mShoulderLeft";
+ mJointMap["lForeArm"] = "mElbowLeft";
+ mJointMap["lHand"] = "mWristLeft";
+ mJointMap["rCollar"] = "mCollarRight";
+ mJointMap["rShldr"] = "mShoulderRight";
+ mJointMap["rForeArm"] = "mElbowRight";
+ mJointMap["rHand"] = "mWristRight";
+ mJointMap["rThigh"] = "mHipRight";
+ mJointMap["rShin"] = "mKneeRight";
+ mJointMap["rFoot"] = "mFootRight";
+ mJointMap["lThigh"] = "mHipLeft";
+ mJointMap["lShin"] = "mKneeLeft";
+ mJointMap["lFoot"] = "mFootLeft";
+
+ //move into joint mapper class
+ //1. joints for joint offset verification
+ mMasterJointList.push_front("mPelvis");
+ mMasterJointList.push_front("mTorso");
+ mMasterJointList.push_front("mChest");
+ mMasterJointList.push_front("mNeck");
+ mMasterJointList.push_front("mHead");
+ mMasterJointList.push_front("mCollarLeft");
+ mMasterJointList.push_front("mShoulderLeft");
+ mMasterJointList.push_front("mElbowLeft");
+ mMasterJointList.push_front("mWristLeft");
+ mMasterJointList.push_front("mCollarRight");
+ mMasterJointList.push_front("mShoulderRight");
+ mMasterJointList.push_front("mElbowRight");
+ mMasterJointList.push_front("mWristRight");
+ mMasterJointList.push_front("mHipRight");
+ mMasterJointList.push_front("mKneeRight");
+ mMasterJointList.push_front("mFootRight");
+ mMasterJointList.push_front("mHipLeft");
+ mMasterJointList.push_front("mKneeLeft");
+ mMasterJointList.push_front("mFootLeft");
+
+ //2. legacy joint list - used to verify rigs that will not be using joint offsets
+ mMasterLegacyJointList.push_front("mPelvis");
+ mMasterLegacyJointList.push_front("mTorso");
+ mMasterLegacyJointList.push_front("mChest");
+ mMasterLegacyJointList.push_front("mNeck");
+ mMasterLegacyJointList.push_front("mHead");
+ mMasterLegacyJointList.push_front("mHipRight");
+ mMasterLegacyJointList.push_front("mKneeRight");
+ mMasterLegacyJointList.push_front("mFootRight");
+ mMasterLegacyJointList.push_front("mHipLeft");
+ mMasterLegacyJointList.push_front("mKneeLeft");
+ mMasterLegacyJointList.push_front("mFootLeft");
+
+ assert_main_thread();
+ sActiveLoaderList.push_back(this) ;
+}
+
+LLModelLoader::~LLModelLoader()
+{
+ assert_main_thread();
+ sActiveLoaderList.remove(this);
+}
+
+void LLModelLoader::run()
+{
+ doLoadModel();
+ doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
+}
+
+bool LLModelLoader::doLoadModel()
+{
+ //first, look for a .slm file of the same name that was modified later
+ //than the .dae
+
+ if (mTrySLM)
+ {
+ std::string filename = mFilename;
+
+ std::string::size_type i = filename.rfind(".");
+ if (i != std::string::npos)
+ {
+ filename.replace(i, filename.size()-1, ".slm");
+ llstat slm_status;
+ if (LLFile::stat(filename, &slm_status) == 0)
+ { //slm file exists
+ llstat dae_status;
+ if (LLFile::stat(mFilename, &dae_status) != 0 ||
+ dae_status.st_mtime < slm_status.st_mtime)
+ {
+ if (loadFromSLM(filename))
+ { //slm successfully loaded, if this fails, fall through and
+ //try loading from dae
+
+ mLod = -1; //successfully loading from an slm implicitly sets all
+ //LoDs
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return OpenFile(mFilename);
+}
+
+void LLModelLoader::setLoadState(U32 state)
+{
+ mStateCallback(state, mOpaqueData);
+}
+
+bool LLModelLoader::loadFromSLM(const std::string& filename)
+{
+ //only need to populate mScene with data from slm
+ llstat stat;
+
+ if (LLFile::stat(filename, &stat))
+ { //file does not exist
+ return false;
+ }
+
+ S32 file_size = (S32) stat.st_size;
+
+ llifstream ifstream(filename.c_str(), std::ifstream::in | std::ifstream::binary);
+ LLSD data;
+ LLSDSerialize::fromBinary(data, ifstream, file_size);
+ ifstream.close();
+
+ //build model list for each LoD
+ model_list model[LLModel::NUM_LODS];
+
+ if (data["version"].asInteger() != SLM_SUPPORTED_VERSION)
+ { //unsupported version
+ return false;
+ }
+
+ LLSD& mesh = data["mesh"];
+
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
+
+ for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+ {
+ for (U32 i = 0; i < mesh.size(); ++i)
+ {
+ std::stringstream str(mesh[i].asString());
+ LLPointer<LLModel> loaded_model = new LLModel(volume_params, (F32) lod);
+ if (loaded_model->loadModel(str))
+ {
+ loaded_model->mLocalID = i;
+ model[lod].push_back(loaded_model);
+
+ if (lod == LLModel::LOD_HIGH)
+ {
+ if (!loaded_model->mSkinInfo.mJointNames.empty())
+ {
+ //check to see if rig is valid
+ critiqueRigForUploadApplicability( loaded_model->mSkinInfo.mJointNames );
+ }
+ else if (mCacheOnlyHitIfRigged)
+ {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ if (model[LLModel::LOD_HIGH].empty())
+ { //failed to load high lod
+ return false;
+ }
+
+ //load instance list
+ model_instance_list instance_list;
+
+ LLSD& instance = data["instance"];
+
+ for (U32 i = 0; i < instance.size(); ++i)
+ {
+ //deserialize instance list
+ instance_list.push_back(LLModelInstance(instance[i]));
+
+ //match up model instance pointers
+ S32 idx = instance_list[i].mLocalMeshID;
+ std::string instance_label = instance_list[i].mLabel;
+
+ for (U32 lod = 0; lod < LLModel::NUM_LODS; ++lod)
+ {
+ if (!model[lod].empty())
+ {
+ if (idx >= model[lod].size())
+ {
+ if (model[lod].size())
+ {
+ instance_list[i].mLOD[lod] = model[lod][0];
+ }
+ else
+ {
+ instance_list[i].mLOD[lod] = NULL;
+ }
+ continue;
+ }
+
+ if (model[lod][idx]
+ && model[lod][idx]->mLabel.empty()
+ && !instance_label.empty())
+ {
+ // restore model names
+ std::string name = instance_label;
+ switch (lod)
+ {
+ case LLModel::LOD_IMPOSTOR: name += "_LOD0"; break;
+ case LLModel::LOD_LOW: name += "_LOD1"; break;
+ case LLModel::LOD_MEDIUM: name += "_LOD2"; break;
+ case LLModel::LOD_PHYSICS: name += "_PHYS"; break;
+ case LLModel::LOD_HIGH: break;
+ }
+ model[lod][idx]->mLabel = name;
+ }
+
+ instance_list[i].mLOD[lod] = model[lod][idx];
+ }
+ }
+
+ if (!instance_list[i].mModel)
+ instance_list[i].mModel = model[LLModel::LOD_HIGH][idx];
+ }
+
+ // Set name for UI to use
+ std::string name = data["name"];
+ if (!name.empty())
+ {
+ model[LLModel::LOD_HIGH][0]->mRequestedLabel = name;
+ }
+
+
+ //convert instance_list to mScene
+ mFirstTransform = TRUE;
+ for (U32 i = 0; i < instance_list.size(); ++i)
+ {
+ LLModelInstance& cur_instance = instance_list[i];
+ mScene[cur_instance.mTransform].push_back(cur_instance);
+ stretch_extents(cur_instance.mModel, cur_instance.mTransform, mExtents[0], mExtents[1], mFirstTransform);
+ }
+
+ setLoadState( DONE );
+
+ return true;
+}
+
+//static
+bool LLModelLoader::isAlive(LLModelLoader* loader)
+{
+ if(!loader)
+ {
+ return false ;
+ }
+
+ std::list<LLModelLoader*>::iterator iter = sActiveLoaderList.begin() ;
+ for(; iter != sActiveLoaderList.end() && (*iter) != loader; ++iter) ;
+
+ return *iter == loader ;
+}
+
+void LLModelLoader::loadModelCallback()
+{
+ mLoadCallback(mScene,mModelList,mLod, mOpaqueData);
+
+ while (!isStopped())
+ { //wait until this thread is stopped before deleting self
+ apr_sleep(100);
+ }
+
+ //double check if "this" is valid before deleting it, in case it is aborted during running.
+ if(!isAlive(this))
+ {
+ return ;
+ }
+
+ delete this;
+}
+
+//-----------------------------------------------------------------------------
+// critiqueRigForUploadApplicability()
+//-----------------------------------------------------------------------------
+void LLModelLoader::critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset )
+{
+ critiqueJointToNodeMappingFromScene();
+
+ //Determines the following use cases for a rig:
+ //1. It is suitable for upload with skin weights & joint positions, or
+ //2. It is suitable for upload as standard av with just skin weights
+
+ bool isJointPositionUploadOK = isRigSuitableForJointPositionUpload( jointListFromAsset );
+ bool isRigLegacyOK = isRigLegacy( jointListFromAsset );
+
+ //It's OK that both could end up being true, both default to false
+ if ( isJointPositionUploadOK )
+ {
+ setRigValidForJointPositionUpload( true );
+ }
+
+ if ( isRigLegacyOK)
+ {
+ setLegacyRigValid( true );
+ }
+
+}
+//-----------------------------------------------------------------------------
+// critiqueJointToNodeMappingFromScene()
+//-----------------------------------------------------------------------------
+void LLModelLoader::critiqueJointToNodeMappingFromScene( void )
+{
+ //Do the actual nodes back the joint listing from the dae?
+ //if yes then this is a fully rigged asset, otherwise it's just a partial rig
+
+ JointSet::iterator jointsFromNodeIt = mJointsFromNode.begin();
+ JointSet::iterator jointsFromNodeEndIt = mJointsFromNode.end();
+ bool result = true;
+
+ if ( !mJointsFromNode.empty() )
+ {
+ for ( ;jointsFromNodeIt!=jointsFromNodeEndIt;++jointsFromNodeIt )
+ {
+ std::string name = *jointsFromNodeIt;
+ if ( mJointTransformMap.find( name ) != mJointTransformMap.end() )
+ {
+ continue;
+ }
+ else
+ {
+ LL_INFOS() <<"critiqueJointToNodeMappingFromScene is missing a: " << name << LL_ENDL;
+ result = false;
+ }
+ }
+ }
+ else
+ {
+ result = false;
+ }
+
+ //Determines the following use cases for a rig:
+ //1. Full av rig w/1-1 mapping from the scene and joint array
+ //2. Partial rig but w/o parity between the scene and joint array
+ if ( result )
+ {
+ setRigWithSceneParity( true );
+ }
+}
+//-----------------------------------------------------------------------------
+// isRigLegacy()
+//-----------------------------------------------------------------------------
+bool LLModelLoader::isRigLegacy( const std::vector<std::string> &jointListFromAsset )
+{
+ //No joints in asset
+ if ( jointListFromAsset.size() == 0 )
+ {
+ return false;
+ }
+
+ bool result = false;
+
+ JointSet :: const_iterator masterJointIt = mMasterLegacyJointList.begin();
+ JointSet :: const_iterator masterJointEndIt = mMasterLegacyJointList.end();
+
+ std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin();
+ std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end();
+
+ for ( ;masterJointIt!=masterJointEndIt;++masterJointIt )
+ {
+ result = false;
+ modelJointIt = jointListFromAsset.begin();
+
+ for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt )
+ {
+ if ( *masterJointIt == *modelJointIt )
+ {
+ result = true;
+ break;
+ }
+ }
+ if ( !result )
+ {
+ LL_INFOS() <<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< LL_ENDL;
+ break;
+ }
+ }
+ return result;
+}
+//-----------------------------------------------------------------------------
+// isRigSuitableForJointPositionUpload()
+//-----------------------------------------------------------------------------
+bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset )
+{
+ bool result = false;
+
+ JointSet :: const_iterator masterJointIt = mMasterJointList.begin();
+ JointSet :: const_iterator masterJointEndIt = mMasterJointList.end();
+
+ std::vector<std::string> :: const_iterator modelJointIt = jointListFromAsset.begin();
+ std::vector<std::string> :: const_iterator modelJointItEnd = jointListFromAsset.end();
+
+ for ( ;masterJointIt!=masterJointEndIt;++masterJointIt )
+ {
+ result = false;
+ modelJointIt = jointListFromAsset.begin();
+
+ for ( ;modelJointIt!=modelJointItEnd; ++modelJointIt )
+ {
+ if ( *masterJointIt == *modelJointIt )
+ {
+ result = true;
+ break;
+ }
+ }
+ if ( !result )
+ {
+ LL_INFOS() <<" Asset did not contain the joint (if you're u/l a fully rigged asset w/joint positions - it is required)." << *masterJointIt<< LL_ENDL;
+ break;
+ }
+ }
+ return result;
+}
+
+
+//called in the main thread
+void LLModelLoader::loadTextures()
+{
+ BOOL is_paused = isPaused() ;
+ pause() ; //pause the loader
+
+ for(scene::iterator iter = mScene.begin(); iter != mScene.end(); ++iter)
+ {
+ for(U32 i = 0 ; i < iter->second.size(); i++)
+ {
+ for(std::map<std::string, LLImportMaterial>::iterator j = iter->second[i].mMaterial.begin();
+ j != iter->second[i].mMaterial.end(); ++j)
+ {
+ LLImportMaterial& material = j->second;
+
+ if(!material.mDiffuseMapFilename.empty())
+ {
+ mNumOfFetchingTextures += mTextureLoadFunc(material, mOpaqueData);
+ }
+ }
+ }
+ }
+
+ if(!is_paused)
+ {
+ unpause() ;
+ }
+}
diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h
new file mode 100644
index 0000000000..bb4d06dca3
--- /dev/null
+++ b/indra/llprimitive/llmodelloader.h
@@ -0,0 +1,212 @@
+/**
+ * @file llmodelloader.h
+ * @brief LLModelLoader class definition
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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_LLMODELLOADER_H
+#define LL_LLMODELLOADER_H
+
+#include "llmodel.h"
+#include "llthread.h"
+#include <boost/function.hpp>
+#include <list>
+
+class LLJoint;
+
+typedef std::map<std::string, LLMatrix4> JointTransformMap;
+typedef std::map<std::string, LLMatrix4>:: iterator JointTransformMapIt;
+typedef std::map<std::string, std::string> JointMap;
+typedef std::deque<std::string> JointSet;
+
+const S32 SLM_SUPPORTED_VERSION = 3;
+const S32 NUM_LOD = 4;
+
+class LLModelLoader : public LLThread
+{
+public:
+
+ typedef std::map<std::string, LLImportMaterial> material_map;
+ typedef std::vector<LLPointer<LLModel > > model_list;
+ typedef std::vector<LLModelInstance> model_instance_list;
+ typedef std::map<LLMatrix4, model_instance_list > scene;
+
+ // Callback with loaded model data and loaded LoD
+ //
+ typedef boost::function<void (scene&,model_list&,S32,void*) > load_callback_t;
+
+ // Function to provide joint lookup by name
+ // (within preview avi skeleton, for example)
+ //
+ typedef boost::function<LLJoint* (const std::string&,void*) > joint_lookup_func_t;
+
+ // Func to load and associate material with all it's textures,
+ // returned value is the number of textures loaded
+ // intentionally non-const so func can modify material to
+ // store platform-specific data
+ //
+ typedef boost::function<U32 (LLImportMaterial&,void*) > texture_load_func_t;
+
+ // Callback to inform client of state changes
+ // during loading process (errors will be reported
+ // as state changes here as well)
+ //
+ typedef boost::function<void (U32,void*) > state_callback_t;
+
+ typedef enum
+ {
+ STARTING = 0,
+ READING_FILE,
+ CREATING_FACES,
+ GENERATING_VERTEX_BUFFERS,
+ GENERATING_LOD,
+ DONE,
+ ERROR_PARSING, //basically loading failed
+ ERROR_MATERIALS,
+ ERROR_PASSWORD_REQUIRED,
+ ERROR_NEED_MORE_MEMORY,
+ ERROR_INVALID_FILE,
+ ERROR_LOADER_SETUP,
+ ERROR_INVALID_PARAMETERS,
+ ERROR_OUT_OF_RANGE,
+ ERROR_FILE_VERSION_INVALID,
+ ERROR_MODEL // this error should always be last in this list, error code is passed as ERROR_MODEL+error_code
+ } eLoadState;
+
+ U32 mState;
+ std::string mFilename;
+
+ S32 mLod;
+
+ LLMatrix4 mTransform;
+ BOOL mFirstTransform;
+ LLVector3 mExtents[2];
+
+ bool mTrySLM;
+ bool mCacheOnlyHitIfRigged; // ignore cached SLM if it does not contain rig info (and we want rig info)
+
+ model_list mModelList;
+ scene mScene;
+
+ typedef std::queue<LLPointer<LLModel> > model_queue;
+
+ //queue of models that need a physics rep
+ model_queue mPhysicsQ;
+
+ //map of avatar joints as named in COLLADA assets to internal joint names
+ JointMap mJointMap;
+ JointTransformMap& mJointList;
+ JointSet& mJointsFromNode;
+
+ LLModelLoader(
+ 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 ~LLModelLoader() ;
+
+ virtual void setNoNormalize() { mNoNormalize = true; }
+ virtual void setNoOptimize() { mNoOptimize = true; }
+
+ virtual void run();
+
+ // Will try SLM or derived class OpenFile as appropriate
+ //
+ virtual bool doLoadModel();
+
+ // Derived classes need to provide their parsing of files here
+ //
+ virtual bool OpenFile(const std::string& filename) = 0;
+
+ bool loadFromSLM(const std::string& filename);
+
+ void loadModelCallback();
+ void loadTextures() ; //called in the main thread.
+ void setLoadState(U32 state);
+
+
+
+ S32 mNumOfFetchingTextures ; //updated in the main thread
+ bool areTexturesReady() { return !mNumOfFetchingTextures; } //called in the main thread.
+
+ bool verifyCount( int expected, int result );
+
+ //Determines the viability of an asset to be used as an avatar rig (w or w/o joint upload caps)
+ void critiqueRigForUploadApplicability( const std::vector<std::string> &jointListFromAsset );
+ void critiqueJointToNodeMappingFromScene( void );
+
+ //Determines if a rig is a legacy from the joint list
+ bool isRigLegacy( const std::vector<std::string> &jointListFromAsset );
+
+ //Determines if a rig is suitable for upload
+ bool isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset );
+
+ void setRigWithSceneParity( bool state ) { mRigParityWithScene = state; }
+ const bool getRigWithSceneParity( void ) const { return mRigParityWithScene; }
+
+ const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; }
+ void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; }
+
+ const bool isLegacyRigValid( void ) const { return mLegacyRigValid; }
+ void setLegacyRigValid( bool rigValid ) { mLegacyRigValid = rigValid; }
+
+ //-----------------------------------------------------------------------------
+ // isNodeAJoint()
+ //-----------------------------------------------------------------------------
+ bool isNodeAJoint(const char* name)
+ {
+ return mJointMap.find(name) != mJointMap.end();
+ }
+
+protected:
+
+ LLModelLoader::load_callback_t mLoadCallback;
+ LLModelLoader::joint_lookup_func_t mJointLookupFunc;
+ LLModelLoader::texture_load_func_t mTextureLoadFunc;
+ LLModelLoader::state_callback_t mStateCallback;
+ void* mOpaqueData;
+
+ bool mRigParityWithScene;
+ bool mRigValidJointUpload;
+ bool mLegacyRigValid;
+
+ bool mNoNormalize;
+ bool mNoOptimize;
+
+ JointSet mMasterJointList;
+ JointSet mMasterLegacyJointList;
+ JointTransformMap mJointTransformMap;
+
+ static std::list<LLModelLoader*> sActiveLoaderList;
+ static bool isAlive(LLModelLoader* loader) ;
+};
+class LLMatrix4a;
+void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform);
+void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform);
+
+#endif // LL_LLMODELLOADER_H
diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h
index 19edcaa27d..a40c3988f2 100755
--- a/indra/llprimitive/lltextureentry.h
+++ b/indra/llprimitive/lltextureentry.h
@@ -89,6 +89,10 @@ public:
bool operator==(const LLTextureEntry &rhs) const;
bool operator!=(const LLTextureEntry &rhs) const;
+
+ // Added to allow use with std::map
+ //
+ bool operator <(const LLTextureEntry &rhs) const;
LLSD asLLSD() const;
void asLLSD(LLSD& sd) const;