diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 21:25:21 +0200 |
---|---|---|
committer | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-05-22 22:40:26 +0300 |
commit | e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 (patch) | |
tree | 1bb897489ce524986f6196201c10ac0d8861aa5f /indra/llprimitive | |
parent | 069ea06848f766466f1a281144c82a0f2bd79f3a (diff) |
Fix line endlings
Diffstat (limited to 'indra/llprimitive')
-rw-r--r-- | indra/llprimitive/lldaeloader.cpp | 5198 | ||||
-rw-r--r-- | indra/llprimitive/llmaterial.cpp | 952 | ||||
-rw-r--r-- | indra/llprimitive/llmaterialtable.cpp | 1510 | ||||
-rw-r--r-- | indra/llprimitive/llmaterialtable.h | 380 | ||||
-rw-r--r-- | indra/llprimitive/llmodel.cpp | 4352 | ||||
-rw-r--r-- | indra/llprimitive/llmodel.h | 870 | ||||
-rw-r--r-- | indra/llprimitive/llmodelloader.cpp | 1010 | ||||
-rw-r--r-- | indra/llprimitive/llmodelloader.h | 442 | ||||
-rw-r--r-- | indra/llprimitive/llprimitive.cpp | 4826 | ||||
-rw-r--r-- | indra/llprimitive/llprimitive.h | 1588 | ||||
-rw-r--r-- | indra/llprimitive/lltextureanim.cpp | 478 | ||||
-rw-r--r-- | indra/llprimitive/lltextureanim.h | 144 | ||||
-rw-r--r-- | indra/llprimitive/llvolumemessage.cpp | 1114 | ||||
-rw-r--r-- | indra/llprimitive/tests/llprimitive_test.cpp | 542 |
14 files changed, 11703 insertions, 11703 deletions
diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 43e2bd88d3..de5a2fe1fa 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -1,2599 +1,2599 @@ -/**
- * @file lldaeloader.cpp
- * @brief LLDAELoader class implementation
- *
- * $LicenseInfo:firstyear=2013&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2013, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#if LL_MSVC
-#pragma warning (disable : 4263)
-#pragma warning (disable : 4264)
-#endif
-#include "dae.h"
-#include "dom/domAsset.h"
-#include "dom/domBind_material.h"
-#include "dom/domCOLLADA.h"
-#include "dom/domConstants.h"
-#include "dom/domController.h"
-#include "dom/domEffect.h"
-#include "dom/domGeometry.h"
-#include "dom/domInstance_geometry.h"
-#include "dom/domInstance_material.h"
-#include "dom/domInstance_node.h"
-#include "dom/domInstance_effect.h"
-#include "dom/domMaterial.h"
-#include "dom/domMatrix.h"
-#include "dom/domNode.h"
-#include "dom/domProfile_COMMON.h"
-#include "dom/domRotate.h"
-#include "dom/domScale.h"
-#include "dom/domTranslate.h"
-#include "dom/domVisual_scene.h"
-#if LL_MSVC
-#pragma warning (default : 4263)
-#pragma warning (default : 4264)
-#endif
-
-#include "lldaeloader.h"
-#include "llsdserialize.h"
-#include "lljoint.h"
-
-#include "glh/glh_linear.h"
-#include "llmatrix4a.h"
-
-#include <boost/regex.hpp>
-#include <boost/algorithm/string/replace.hpp>
-
-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,
- LLSD& log_msg)
-{
- 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))
- {
- LLSD args;
- args["Message"] = "ParsingErrorBadElement";
- log_msg.append(args);
- 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;
- LLSD args;
- args["Message"] = "ParsingErrorPositionInvalidModel";
- log_msg.append(args);
- 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;
- }
- // VFExtents change
- 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;
-
- if (idx_stride <= 0
- || (pos_source && pos_offset >= idx_stride)
- || (tc_source && tc_offset >= idx_stride)
- || (norm_source && norm_offset >= idx_stride))
- {
- // Looks like these offsets should fit inside idx_stride
- // Might be good idea to also check idx.getCount()%idx_stride != 0
- LL_WARNS() << "Invalid pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL;
- return LLModel::BAD_ELEMENT;
- }
-
- 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
- //
- U32 indx_size = indices.size();
- U32 verts_new_tri = indx_size % 3;
- if ((verts_new_tri < 1 || indices[indx_size - 1] != shared_index)
- && (verts_new_tri < 2 || indices[indx_size - 2] != shared_index))
- {
- found = true;
- indices.push_back(shared_index);
- }
- break;
- }
- }
- }
-
- if (!found)
- {
- // VFExtents change
- 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();
- // VFExtents change
- face.mExtents[0].set(v[0], v[1], v[2]);
- face.mExtents[1].set(v[0], v[1], v[2]);
-
- verts.clear();
- indices.clear();
- 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,
- LLSD& log_msg)
-{
- 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() << "Bad element." << LL_ENDL;
- LLSD args;
- args["Message"] = "ParsingErrorBadElement";
- log_msg.append(args);
- 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();
- // VFExtents change
- 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;
- bool log_tc_msg = true;
- 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;
- LLSD args;
- args["Message"] = "PositionNaN";
- log_msg.append(args);
- return LLModel::BAD_ELEMENT;
- }
- }
-
- if (tc_source)
- {
- U64 idx_x = idx[cur_idx + tc_offset] * 2 + 0;
- U64 idx_y = idx[cur_idx + tc_offset] * 2 + 1;
-
- if (idx_y < tc.getCount())
- {
- cv.mTexCoord.setVec(tc[idx_x], tc[idx_y]);
- }
- else if (log_tc_msg)
- {
- log_tc_msg = false;
- LL_WARNS() << "Texture coordinates data is not complete." << LL_ENDL;
- LLSD args;
- args["Message"] = "IncompleteTC";
- log_msg.append(args);
- }
- }
-
- 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;
- LLSD args;
- args["Message"] = "NormalsNaN";
- log_msg.append(args);
-
- 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)
- {
- // VFExtents change
- 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();
- // VFExtents change
- face.mExtents[0].set(v[0], v[1], v[2]);
- face.mExtents[1].set(v[0], v[1], v[2]);
- 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;
- }
- // VFExtents change
- 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++;
- }
- }
-
- // Viewer can only fit U16 vertices, shouldn't we do some checks here and return overflow if result has more?
- llassert(vert_idx.size() < U16_MAX);
-
- //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;
- // VFExtents change
- 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]];
- if (i % 3 != 0) // assumes GL_TRIANGLES, compare 0-1, 1-2, 3-4, 4-5 but not 2-3 or 5-6
- {
- // A faulty degenerate triangle detection (triangle with 0 area),
- // probably should be a warning and not an assert
- 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& jointTransformMap,
- JointNameSet& jointsFromNodes,
- std::map<std::string, std::string>& jointAliasMap,
- U32 maxJointsPerMesh,
- U32 modelLimit,
- bool preprocess)
-: LLModelLoader(
- filename,
- lod,
- load_cb,
- joint_lookup_func,
- texture_load_func,
- state_cb,
- opaque_userdata,
- jointTransformMap,
- jointsFromNodes,
- jointAliasMap,
- maxJointsPerMesh),
- mGeneratedModelLimit(modelLimit),
- mPreprocessDAE(preprocess)
-{
-}
-
-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)
-{
- setLoadState( READING_FILE );
-
- //no suitable slm exists, load from the .dae file
-
- // Collada expects file and folder names to be escaped
- // Note: cdom::nativePathToUri()
- const char* allowed =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789"
- "%-._~:\"|\\/";
- std::string uri_filename = LLURI::escape(filename, allowed);
-
- DAE dae;
- domCOLLADA* dom;
- if (mPreprocessDAE)
- {
- dom = dae.openFromMemory(uri_filename, preprocessDAE(filename).c_str());
- }
- else
- {
- LL_INFOS() << "Skipping dae preprocessing" << LL_ENDL;
- dom = dae.open(uri_filename);
- }
-
- if (!dom)
- {
- LL_INFOS() <<" Error with dae - traditionally indicates a corrupt file."<<LL_ENDL;
- LLSD args;
- args["Message"] = "ParsingErrorCorrupt";
- mWarningsArray.append(args);
- 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(uri_filename);
- if (!doc)
- {
- LL_WARNS() << "can't find internal doc" << LL_ENDL;
- LLSD args;
- args["Message"] = "ParsingErrorNoDoc";
- mWarningsArray.append(args);
- return false;
- }
-
- daeElement* root = doc->getDomRoot();
- if (!root)
- {
- LL_WARNS() << "document has no root" << LL_ENDL;
- LLSD args;
- args["Message"] = "ParsingErrorNoRoot";
- mWarningsArray.append(args);
- 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;
- LLSD args;
- args["Message"] = "ParsingErrorBadElement";
- mWarningsArray.append(args);
- 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;
- LLSD args;
- args["Message"] = "ParsingErrorNoScene";
- mWarningsArray.append(args);
- 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;
- LLSD args;
- args["Message"] = "ParsingErrorCantParseScene";
- mWarningsArray.append(args);
- setLoadState( ERROR_PARSING );
- }
-
- return true;
-}
-
-std::string LLDAELoader::preprocessDAE(std::string filename)
-{
- // Open a DAE file for some preprocessing (like removing space characters in IDs), see MAINT-5678
- llifstream inFile;
- inFile.open(filename.c_str(), std::ios_base::in);
- std::stringstream strStream;
- strStream << inFile.rdbuf();
- std::string buffer = strStream.str();
-
- LL_INFOS() << "Preprocessing dae file to remove spaces from the names, ids, etc." << LL_ENDL;
-
- try
- {
- boost::regex re("\"[\\w\\.@#$-]*(\\s[\\w\\.@#$-]*)+\"");
- boost::sregex_iterator next(buffer.begin(), buffer.end(), re);
- boost::sregex_iterator end;
- while (next != end)
- {
- boost::smatch match = *next;
- std::string s = match.str();
- LL_INFOS() << s << " found" << LL_ENDL;
- boost::replace_all(s, " ", "_");
- LL_INFOS() << "Replacing with " << s << LL_ENDL;
- boost::replace_all(buffer, match.str(), s);
- next++;
- }
- }
- catch (boost::regex_error &)
- {
- LL_INFOS() << "Regex error" << LL_ENDL;
- }
-
- return buffer;
-}
-
-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;
-
- LLMatrix4 mat;
- for (int i = 0; i < 4; i++)
- {
- for(int j = 0; j < 4; j++)
- {
- mat.mMatrix[i][j] = dom_value[i + j*4];
- }
- }
-
- skin_info.mBindShapeMatrix.loadu(mat);
-
- LLMatrix4a trans(normalized_transformation);
- matMul(trans, skin_info.mBindShapeMatrix, skin_info.mBindShapeMatrix);
- }
-
-
- //Some collada setup for accessing the skeleton
- U32 skeleton_count = dae->getDatabase()->getElementCount( NULL, "skeleton" );
- std::vector<domInstance_controller::domSkeleton*> skeletons;
- for (S32 i=0; i<skeleton_count; i++)
- {
- daeElement* pElement = 0;
- dae->getDatabase()->getElement( &pElement, i, 0, "skeleton" );
-
- //Try to get at the skeletal instance controller
- domInstance_controller::domSkeleton* pSkeleton = daeSafeCast<domInstance_controller::domSkeleton>( pElement );
- daeElement* pSkeletonRootNode = NULL;
- if (pSkeleton)
- {
- pSkeletonRootNode = pSkeleton->getValue().getElement();
- }
- if (pSkeleton && pSkeletonRootNode)
- {
- skeletons.push_back(pSkeleton);
- }
- }
- bool missingSkeletonOrScene = false;
-
- //If no skeleton, do a breadth-first search to get at specific joints
- if ( skeletons.size() == 0 )
- {
- 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 could be ambient lights, cameras, geometry etc..
- for (S32 i = 0; i < childCount; ++i)
- {
- domNode* pNode = daeSafeCast<domNode>(children[i]);
- if (pNode)
- {
- processJointNode( pNode, mJointList );
- }
- }
- }
- }
- else
- //Has one or more skeletons
- for (std::vector<domInstance_controller::domSkeleton*>::iterator skel_it = skeletons.begin();
- skel_it != skeletons.end(); ++skel_it)
- {
- domInstance_controller::domSkeleton* pSkeleton = *skel_it;
- //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 )
- {
- // FIXME this has a lot of overlap with processJointNode(), would be nice to refactor.
-
- //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 its 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.mJointNums.push_back(-1);
- }
- }
- 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.mJointNums.push_back(-1);
- }
- }
- }
- }
- }
- 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(LLMatrix4a(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 )
- {
- // FIXME: mesh_id is used to determine which mesh gets to
- // set the joint offset, in the event of a conflict. Since
- // we don't know the mesh id yet, we can't guarantee that
- // joint offsets will be applied with the same priority as
- // in the uploaded model. If the file contains multiple
- // meshes with conflicting joint offsets, preview may be
- // incorrect.
- LLUUID fake_mesh_id;
- fake_mesh_id.generate();
-
- //Set the joint translations on the avatar
- 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 )
- {
- const LLVector3& joint_pos = jointTransform.getTranslation();
- if (pJoint->aboveJointPosThreshold(joint_pos))
- {
- bool override_changed; // not used
- pJoint->addAttachmentPosOverride(joint_pos, fake_mesh_id, "", override_changed);
- if (model->mSkinInfo.mLockScaleIfJointPosition)
- {
- pJoint->addAttachmentScaleOverride(pJoint->getDefaultScale(), 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 (mJointMap.find(lookingForJoint) != mJointMap.end()
- && model->mSkinInfo.mInvBindMatrix.size() > i)
- {
- LLMatrix4 newInverse = LLMatrix4(model->mSkinInfo.mInvBindMatrix[i].getF32ptr());
- newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() );
- model->mSkinInfo.mAlternateBindMatrix.push_back( LLMatrix4a(newInverse) );
- }
- else
- {
- LL_DEBUGS("Mesh")<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<"] "<<LL_ENDL;
- }
- }
-
- U32 bind_count = model->mSkinInfo.mAlternateBindMatrix.size();
- if (bind_count > 0 && bind_count != jointCnt)
- {
- LL_WARNS("Mesh") << "Model " << model->mLabel << " has invalid joint bind matrix list." << 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 || !pNode->getName() )
- {
- LL_INFOS()<<"Created node is NULL or invalid"<<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 = static_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
- if (isNodeAJoint(pNode))
- {
- 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_DEBUGS("Mesh")<< "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;
- LLSD args;
- args["Message"] = "NegativeScaleTrans";
- args["LABEL"] = getElementLabel(instance_geo);
- mWarningsArray.append(args);
-
- 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;
- LLSD args;
- args["Message"] = "NegativeScaleNormTrans";
- args["LABEL"] = getElementLabel(instance_geo);
- mWarningsArray.append(args);
- 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;
- LLSD args;
- args["Message"] = "CantResolveGeometryUrl";
- mWarningsArray.append(args);
- 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);
-
- if (ind > 0)
- {
- index_string = "_" + std::to_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, LLSD& log_msg)
-{
- 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, log_msg);
- 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, log_msg);
-
- 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 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, mWarningsArray);
-
- 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);
-
- // remove unused/redundant vertices after normalizing
- if (!mNoOptimize)
- {
- ret->remapVolumeFaces();
- }
-
- 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;
-}
+/** + * @file lldaeloader.cpp + * @brief LLDAELoader class implementation + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2013, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if LL_MSVC +#pragma warning (disable : 4263) +#pragma warning (disable : 4264) +#endif +#include "dae.h" +#include "dom/domAsset.h" +#include "dom/domBind_material.h" +#include "dom/domCOLLADA.h" +#include "dom/domConstants.h" +#include "dom/domController.h" +#include "dom/domEffect.h" +#include "dom/domGeometry.h" +#include "dom/domInstance_geometry.h" +#include "dom/domInstance_material.h" +#include "dom/domInstance_node.h" +#include "dom/domInstance_effect.h" +#include "dom/domMaterial.h" +#include "dom/domMatrix.h" +#include "dom/domNode.h" +#include "dom/domProfile_COMMON.h" +#include "dom/domRotate.h" +#include "dom/domScale.h" +#include "dom/domTranslate.h" +#include "dom/domVisual_scene.h" +#if LL_MSVC +#pragma warning (default : 4263) +#pragma warning (default : 4264) +#endif + +#include "lldaeloader.h" +#include "llsdserialize.h" +#include "lljoint.h" + +#include "glh/glh_linear.h" +#include "llmatrix4a.h" + +#include <boost/regex.hpp> +#include <boost/algorithm/string/replace.hpp> + +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, + LLSD& log_msg) +{ + 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)) + { + LLSD args; + args["Message"] = "ParsingErrorBadElement"; + log_msg.append(args); + 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; + LLSD args; + args["Message"] = "ParsingErrorPositionInvalidModel"; + log_msg.append(args); + 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; + } + // VFExtents change + 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; + + if (idx_stride <= 0 + || (pos_source && pos_offset >= idx_stride) + || (tc_source && tc_offset >= idx_stride) + || (norm_source && norm_offset >= idx_stride)) + { + // Looks like these offsets should fit inside idx_stride + // Might be good idea to also check idx.getCount()%idx_stride != 0 + LL_WARNS() << "Invalid pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL; + return LLModel::BAD_ELEMENT; + } + + 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 + // + U32 indx_size = indices.size(); + U32 verts_new_tri = indx_size % 3; + if ((verts_new_tri < 1 || indices[indx_size - 1] != shared_index) + && (verts_new_tri < 2 || indices[indx_size - 2] != shared_index)) + { + found = true; + indices.push_back(shared_index); + } + break; + } + } + } + + if (!found) + { + // VFExtents change + 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(); + // VFExtents change + face.mExtents[0].set(v[0], v[1], v[2]); + face.mExtents[1].set(v[0], v[1], v[2]); + + verts.clear(); + indices.clear(); + 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, + LLSD& log_msg) +{ + 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() << "Bad element." << LL_ENDL; + LLSD args; + args["Message"] = "ParsingErrorBadElement"; + log_msg.append(args); + 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(); + // VFExtents change + 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; + bool log_tc_msg = true; + 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; + LLSD args; + args["Message"] = "PositionNaN"; + log_msg.append(args); + return LLModel::BAD_ELEMENT; + } + } + + if (tc_source) + { + U64 idx_x = idx[cur_idx + tc_offset] * 2 + 0; + U64 idx_y = idx[cur_idx + tc_offset] * 2 + 1; + + if (idx_y < tc.getCount()) + { + cv.mTexCoord.setVec(tc[idx_x], tc[idx_y]); + } + else if (log_tc_msg) + { + log_tc_msg = false; + LL_WARNS() << "Texture coordinates data is not complete." << LL_ENDL; + LLSD args; + args["Message"] = "IncompleteTC"; + log_msg.append(args); + } + } + + 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; + LLSD args; + args["Message"] = "NormalsNaN"; + log_msg.append(args); + + 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) + { + // VFExtents change + 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(); + // VFExtents change + face.mExtents[0].set(v[0], v[1], v[2]); + face.mExtents[1].set(v[0], v[1], v[2]); + 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; + } + // VFExtents change + 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++; + } + } + + // Viewer can only fit U16 vertices, shouldn't we do some checks here and return overflow if result has more? + llassert(vert_idx.size() < U16_MAX); + + //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; + // VFExtents change + 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]]; + if (i % 3 != 0) // assumes GL_TRIANGLES, compare 0-1, 1-2, 3-4, 4-5 but not 2-3 or 5-6 + { + // A faulty degenerate triangle detection (triangle with 0 area), + // probably should be a warning and not an assert + 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& jointTransformMap, + JointNameSet& jointsFromNodes, + std::map<std::string, std::string>& jointAliasMap, + U32 maxJointsPerMesh, + U32 modelLimit, + bool preprocess) +: LLModelLoader( + filename, + lod, + load_cb, + joint_lookup_func, + texture_load_func, + state_cb, + opaque_userdata, + jointTransformMap, + jointsFromNodes, + jointAliasMap, + maxJointsPerMesh), + mGeneratedModelLimit(modelLimit), + mPreprocessDAE(preprocess) +{ +} + +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) +{ + setLoadState( READING_FILE ); + + //no suitable slm exists, load from the .dae file + + // Collada expects file and folder names to be escaped + // Note: cdom::nativePathToUri() + const char* allowed = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "%-._~:\"|\\/"; + std::string uri_filename = LLURI::escape(filename, allowed); + + DAE dae; + domCOLLADA* dom; + if (mPreprocessDAE) + { + dom = dae.openFromMemory(uri_filename, preprocessDAE(filename).c_str()); + } + else + { + LL_INFOS() << "Skipping dae preprocessing" << LL_ENDL; + dom = dae.open(uri_filename); + } + + if (!dom) + { + LL_INFOS() <<" Error with dae - traditionally indicates a corrupt file."<<LL_ENDL; + LLSD args; + args["Message"] = "ParsingErrorCorrupt"; + mWarningsArray.append(args); + 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(uri_filename); + if (!doc) + { + LL_WARNS() << "can't find internal doc" << LL_ENDL; + LLSD args; + args["Message"] = "ParsingErrorNoDoc"; + mWarningsArray.append(args); + return false; + } + + daeElement* root = doc->getDomRoot(); + if (!root) + { + LL_WARNS() << "document has no root" << LL_ENDL; + LLSD args; + args["Message"] = "ParsingErrorNoRoot"; + mWarningsArray.append(args); + 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; + LLSD args; + args["Message"] = "ParsingErrorBadElement"; + mWarningsArray.append(args); + 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; + LLSD args; + args["Message"] = "ParsingErrorNoScene"; + mWarningsArray.append(args); + 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; + LLSD args; + args["Message"] = "ParsingErrorCantParseScene"; + mWarningsArray.append(args); + setLoadState( ERROR_PARSING ); + } + + return true; +} + +std::string LLDAELoader::preprocessDAE(std::string filename) +{ + // Open a DAE file for some preprocessing (like removing space characters in IDs), see MAINT-5678 + llifstream inFile; + inFile.open(filename.c_str(), std::ios_base::in); + std::stringstream strStream; + strStream << inFile.rdbuf(); + std::string buffer = strStream.str(); + + LL_INFOS() << "Preprocessing dae file to remove spaces from the names, ids, etc." << LL_ENDL; + + try + { + boost::regex re("\"[\\w\\.@#$-]*(\\s[\\w\\.@#$-]*)+\""); + boost::sregex_iterator next(buffer.begin(), buffer.end(), re); + boost::sregex_iterator end; + while (next != end) + { + boost::smatch match = *next; + std::string s = match.str(); + LL_INFOS() << s << " found" << LL_ENDL; + boost::replace_all(s, " ", "_"); + LL_INFOS() << "Replacing with " << s << LL_ENDL; + boost::replace_all(buffer, match.str(), s); + next++; + } + } + catch (boost::regex_error &) + { + LL_INFOS() << "Regex error" << LL_ENDL; + } + + return buffer; +} + +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; + + LLMatrix4 mat; + for (int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + mat.mMatrix[i][j] = dom_value[i + j*4]; + } + } + + skin_info.mBindShapeMatrix.loadu(mat); + + LLMatrix4a trans(normalized_transformation); + matMul(trans, skin_info.mBindShapeMatrix, skin_info.mBindShapeMatrix); + } + + + //Some collada setup for accessing the skeleton + U32 skeleton_count = dae->getDatabase()->getElementCount( NULL, "skeleton" ); + std::vector<domInstance_controller::domSkeleton*> skeletons; + for (S32 i=0; i<skeleton_count; i++) + { + daeElement* pElement = 0; + dae->getDatabase()->getElement( &pElement, i, 0, "skeleton" ); + + //Try to get at the skeletal instance controller + domInstance_controller::domSkeleton* pSkeleton = daeSafeCast<domInstance_controller::domSkeleton>( pElement ); + daeElement* pSkeletonRootNode = NULL; + if (pSkeleton) + { + pSkeletonRootNode = pSkeleton->getValue().getElement(); + } + if (pSkeleton && pSkeletonRootNode) + { + skeletons.push_back(pSkeleton); + } + } + bool missingSkeletonOrScene = false; + + //If no skeleton, do a breadth-first search to get at specific joints + if ( skeletons.size() == 0 ) + { + 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 could be ambient lights, cameras, geometry etc.. + for (S32 i = 0; i < childCount; ++i) + { + domNode* pNode = daeSafeCast<domNode>(children[i]); + if (pNode) + { + processJointNode( pNode, mJointList ); + } + } + } + } + else + //Has one or more skeletons + for (std::vector<domInstance_controller::domSkeleton*>::iterator skel_it = skeletons.begin(); + skel_it != skeletons.end(); ++skel_it) + { + domInstance_controller::domSkeleton* pSkeleton = *skel_it; + //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 ) + { + // FIXME this has a lot of overlap with processJointNode(), would be nice to refactor. + + //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 its 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.mJointNums.push_back(-1); + } + } + 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.mJointNums.push_back(-1); + } + } + } + } + } + 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(LLMatrix4a(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 ) + { + // FIXME: mesh_id is used to determine which mesh gets to + // set the joint offset, in the event of a conflict. Since + // we don't know the mesh id yet, we can't guarantee that + // joint offsets will be applied with the same priority as + // in the uploaded model. If the file contains multiple + // meshes with conflicting joint offsets, preview may be + // incorrect. + LLUUID fake_mesh_id; + fake_mesh_id.generate(); + + //Set the joint translations on the avatar + 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 ) + { + const LLVector3& joint_pos = jointTransform.getTranslation(); + if (pJoint->aboveJointPosThreshold(joint_pos)) + { + bool override_changed; // not used + pJoint->addAttachmentPosOverride(joint_pos, fake_mesh_id, "", override_changed); + if (model->mSkinInfo.mLockScaleIfJointPosition) + { + pJoint->addAttachmentScaleOverride(pJoint->getDefaultScale(), 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 (mJointMap.find(lookingForJoint) != mJointMap.end() + && model->mSkinInfo.mInvBindMatrix.size() > i) + { + LLMatrix4 newInverse = LLMatrix4(model->mSkinInfo.mInvBindMatrix[i].getF32ptr()); + newInverse.setTranslation( mJointList[lookingForJoint].getTranslation() ); + model->mSkinInfo.mAlternateBindMatrix.push_back( LLMatrix4a(newInverse) ); + } + else + { + LL_DEBUGS("Mesh")<<"Possibly misnamed/missing joint [" <<lookingForJoint.c_str()<<"] "<<LL_ENDL; + } + } + + U32 bind_count = model->mSkinInfo.mAlternateBindMatrix.size(); + if (bind_count > 0 && bind_count != jointCnt) + { + LL_WARNS("Mesh") << "Model " << model->mLabel << " has invalid joint bind matrix list." << 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 || !pNode->getName() ) + { + LL_INFOS()<<"Created node is NULL or invalid"<<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 = static_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 + if (isNodeAJoint(pNode)) + { + 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_DEBUGS("Mesh")<< "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; + LLSD args; + args["Message"] = "NegativeScaleTrans"; + args["LABEL"] = getElementLabel(instance_geo); + mWarningsArray.append(args); + + 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; + LLSD args; + args["Message"] = "NegativeScaleNormTrans"; + args["LABEL"] = getElementLabel(instance_geo); + mWarningsArray.append(args); + 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; + LLSD args; + args["Message"] = "CantResolveGeometryUrl"; + mWarningsArray.append(args); + 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); + + if (ind > 0) + { + index_string = "_" + std::to_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, LLSD& log_msg) +{ + 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, log_msg); + 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, log_msg); + + 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 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, mWarningsArray); + + 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); + + // remove unused/redundant vertices after normalizing + if (!mNoOptimize) + { + ret->remapVolumeFaces(); + } + + 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; +} diff --git a/indra/llprimitive/llmaterial.cpp b/indra/llprimitive/llmaterial.cpp index c1356fa5ff..fe1587d197 100644 --- a/indra/llprimitive/llmaterial.cpp +++ b/indra/llprimitive/llmaterial.cpp @@ -1,476 +1,476 @@ -/**
- * @file llmaterial.cpp
- * @brief Material definition
- *
- * $LicenseInfo:firstyear=2006&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 "linden_common.h"
-
-#include "llmaterial.h"
-#include "hbxxh.h"
-
-/**
- * Materials cap parameters
- */
-#define MATERIALS_CAP_NORMAL_MAP_FIELD "NormMap"
-#define MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD "NormOffsetX"
-#define MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD "NormOffsetY"
-#define MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD "NormRepeatX"
-#define MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD "NormRepeatY"
-#define MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD "NormRotation"
-
-#define MATERIALS_CAP_SPECULAR_MAP_FIELD "SpecMap"
-#define MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD "SpecOffsetX"
-#define MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD "SpecOffsetY"
-#define MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD "SpecRepeatX"
-#define MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD "SpecRepeatY"
-#define MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD "SpecRotation"
-
-#define MATERIALS_CAP_SPECULAR_COLOR_FIELD "SpecColor"
-#define MATERIALS_CAP_SPECULAR_EXP_FIELD "SpecExp"
-#define MATERIALS_CAP_ENV_INTENSITY_FIELD "EnvIntensity"
-#define MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD "AlphaMaskCutoff"
-#define MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD "DiffuseAlphaMode"
-
-const LLColor4U LLMaterial::DEFAULT_SPECULAR_LIGHT_COLOR(255,255,255,255);
-
-/**
- * Materials constants
- */
-
-const F32 MATERIALS_MULTIPLIER = 10000.f;
-
-/**
- * Helper functions
- */
-
-template<typename T> T getMaterialField(const LLSD& data, const std::string& field, const LLSD::Type field_type)
-{
- if ( (data.has(field)) && (field_type == data[field].type()) )
- {
- return (T)data[field];
- }
- LL_ERRS() << "Missing or mistyped field '" << field << "' in material definition" << LL_ENDL;
- return (T)LLSD();
-}
-
-// GCC didn't like the generic form above for some reason
-template<> LLUUID getMaterialField(const LLSD& data, const std::string& field, const LLSD::Type field_type)
-{
- if ( (data.has(field)) && (field_type == data[field].type()) )
- {
- return data[field].asUUID();
- }
- LL_ERRS() << "Missing or mistyped field '" << field << "' in material definition" << LL_ENDL;
- return LLUUID::null;
-}
-
-/**
- * LLMaterial class
- */
-
-const LLMaterial LLMaterial::null;
-
-LLMaterial::LLMaterial()
- : mNormalOffsetX(0.0f)
- , mNormalOffsetY(0.0f)
- , mNormalRepeatX(1.0f)
- , mNormalRepeatY(1.0f)
- , mNormalRotation(0.0f)
- , mSpecularOffsetX(0.0f)
- , mSpecularOffsetY(0.0f)
- , mSpecularRepeatX(1.0f)
- , mSpecularRepeatY(1.0f)
- , mSpecularRotation(0.0f)
- , mSpecularLightColor(LLMaterial::DEFAULT_SPECULAR_LIGHT_COLOR)
- , mSpecularLightExponent(LLMaterial::DEFAULT_SPECULAR_LIGHT_EXPONENT)
- , mEnvironmentIntensity(LLMaterial::DEFAULT_ENV_INTENSITY)
- , mDiffuseAlphaMode(LLMaterial::DIFFUSE_ALPHA_MODE_BLEND)
- , mAlphaMaskCutoff(0)
-{
-}
-
-LLMaterial::LLMaterial(const LLSD& material_data)
-{
- fromLLSD(material_data);
-}
-
-const LLUUID& LLMaterial::getNormalID() const
-{
- return mNormalID;
-}
-
-void LLMaterial::setNormalID(const LLUUID& normal_id)
-{
- mNormalID = normal_id;
-}
-
-void LLMaterial::getNormalOffset(F32& offset_x, F32& offset_y) const
-{
- offset_x = mNormalOffsetX;
- offset_y = mNormalOffsetY;
-}
-
-F32 LLMaterial::getNormalOffsetX() const
-{
- return mNormalOffsetX;
-}
-
-F32 LLMaterial::getNormalOffsetY() const
-{
- return mNormalOffsetY;
-}
-
-void LLMaterial::setNormalOffset(F32 offset_x, F32 offset_y)
-{
- mNormalOffsetX = offset_x;
- mNormalOffsetY = offset_y;
-}
-
-void LLMaterial::setNormalOffsetX(F32 offset_x)
-{
- mNormalOffsetX = offset_x;
-}
-
-void LLMaterial::setNormalOffsetY(F32 offset_y)
-{
- mNormalOffsetY = offset_y;
-}
-
-void LLMaterial::getNormalRepeat(F32& repeat_x, F32& repeat_y) const
-{
- repeat_x = mNormalRepeatX;
- repeat_y = mNormalRepeatY;
-}
-
-F32 LLMaterial::getNormalRepeatX() const
-{
- return mNormalRepeatX;
-}
-
-F32 LLMaterial::getNormalRepeatY() const
-{
- return mNormalRepeatY;
-}
-
-void LLMaterial::setNormalRepeat(F32 repeat_x, F32 repeat_y)
-{
- mNormalRepeatX = repeat_x;
- mNormalRepeatY = repeat_y;
-}
-
-void LLMaterial::setNormalRepeatX(F32 repeat_x)
-{
- mNormalRepeatX = repeat_x;
-}
-
-void LLMaterial::setNormalRepeatY(F32 repeat_y)
-{
- mNormalRepeatY = repeat_y;
-}
-
-F32 LLMaterial::getNormalRotation() const
-{
- return mNormalRotation;
-}
-
-void LLMaterial::setNormalRotation(F32 rot)
-{
- mNormalRotation = rot;
-}
-
-const LLUUID& LLMaterial::getSpecularID() const
-{
- return mSpecularID;
-}
-
-void LLMaterial::setSpecularID(const LLUUID& specular_id)
-{
- mSpecularID = specular_id;
-}
-
-void LLMaterial::getSpecularOffset(F32& offset_x, F32& offset_y) const
-{
- offset_x = mSpecularOffsetX;
- offset_y = mSpecularOffsetY;
-}
-
-F32 LLMaterial::getSpecularOffsetX() const
-{
- return mSpecularOffsetX;
-}
-
-F32 LLMaterial::getSpecularOffsetY() const
-{
- return mSpecularOffsetY;
-}
-
-void LLMaterial::setSpecularOffset(F32 offset_x, F32 offset_y)
-{
- mSpecularOffsetX = offset_x;
- mSpecularOffsetY = offset_y;
-}
-
-void LLMaterial::setSpecularOffsetX(F32 offset_x)
-{
- mSpecularOffsetX = offset_x;
-}
-
-void LLMaterial::setSpecularOffsetY(F32 offset_y)
-{
- mSpecularOffsetY = offset_y;
-}
-
-void LLMaterial::getSpecularRepeat(F32& repeat_x, F32& repeat_y) const
-{
- repeat_x = mSpecularRepeatX;
- repeat_y = mSpecularRepeatY;
-}
-
-F32 LLMaterial::getSpecularRepeatX() const
-{
- return mSpecularRepeatX;
-}
-
-F32 LLMaterial::getSpecularRepeatY() const
-{
- return mSpecularRepeatY;
-}
-
-void LLMaterial::setSpecularRepeat(F32 repeat_x, F32 repeat_y)
-{
- mSpecularRepeatX = repeat_x; mSpecularRepeatY = repeat_y;
-}
-
-void LLMaterial::setSpecularRepeatX(F32 repeat_x)
-{
- mSpecularRepeatX = repeat_x;
-}
-
-void LLMaterial::setSpecularRepeatY(F32 repeat_y)
-{
- mSpecularRepeatY = repeat_y;
-}
-
-F32 LLMaterial::getSpecularRotation() const
-{
- return mSpecularRotation;
-}
-
-void LLMaterial::setSpecularRotation(F32 rot)
-{
- mSpecularRotation = rot;
-}
-
-const LLColor4U LLMaterial::getSpecularLightColor() const
-{
- return mSpecularLightColor;
-}
-
-void LLMaterial::setSpecularLightColor(const LLColor4U& color)
-{
- mSpecularLightColor = color;
-}
-
-U8 LLMaterial::getSpecularLightExponent() const
-{
- return mSpecularLightExponent;
-}
-
-void LLMaterial::setSpecularLightExponent(U8 exponent)
-{
- mSpecularLightExponent = exponent;
-}
-
-U8 LLMaterial::getEnvironmentIntensity() const
-{
- return mEnvironmentIntensity;
-}
-
-void LLMaterial::setEnvironmentIntensity(U8 intensity)
-{
- mEnvironmentIntensity = intensity;
-}
-
-U8 LLMaterial::getDiffuseAlphaMode() const
-{
- return mDiffuseAlphaMode;
-}
-
-void LLMaterial::setDiffuseAlphaMode(U8 alpha_mode)
-{
- mDiffuseAlphaMode = alpha_mode;
-}
-
-U8 LLMaterial::getAlphaMaskCutoff() const
-{
- return mAlphaMaskCutoff;
-}
-
-void LLMaterial::setAlphaMaskCutoff(U8 cutoff)
-{
- mAlphaMaskCutoff = cutoff;
-}
-
-LLSD LLMaterial::asLLSD() const
-{
- LLSD material_data;
-
- S32 normalOffsetXInt = ll_round(mNormalOffsetX * MATERIALS_MULTIPLIER);
- S32 normalOffsetYInt = ll_round(mNormalOffsetY * MATERIALS_MULTIPLIER);
- S32 normalRotInt = ll_round(mNormalRotation * MATERIALS_MULTIPLIER);
-
- material_data[MATERIALS_CAP_NORMAL_MAP_FIELD] = mNormalID;
- material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD] = normalOffsetXInt;
- material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD] = normalOffsetYInt;
- material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD] = ll_round(mNormalRepeatX * MATERIALS_MULTIPLIER);
- material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD] = ll_round(mNormalRepeatY * MATERIALS_MULTIPLIER);
- material_data[MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD] = normalRotInt;
-
- material_data[MATERIALS_CAP_SPECULAR_MAP_FIELD] = mSpecularID;
-
- S32 specularOffsetXInt = ll_round(mSpecularOffsetX * MATERIALS_MULTIPLIER);
- S32 specularOffsetYInt = ll_round(mSpecularOffsetY * MATERIALS_MULTIPLIER);
- S32 specularRotInt = ll_round(mSpecularRotation * MATERIALS_MULTIPLIER);
-
- material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD] = specularOffsetXInt;
- material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD] = specularOffsetYInt;
- material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD] = ll_round(mSpecularRepeatX * MATERIALS_MULTIPLIER);
- material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD] = ll_round(mSpecularRepeatY * MATERIALS_MULTIPLIER);
- material_data[MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD] = specularRotInt;
-
- material_data[MATERIALS_CAP_SPECULAR_COLOR_FIELD] = mSpecularLightColor.getValue();
- material_data[MATERIALS_CAP_SPECULAR_EXP_FIELD] = mSpecularLightExponent;
- material_data[MATERIALS_CAP_ENV_INTENSITY_FIELD] = mEnvironmentIntensity;
- material_data[MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD] = mDiffuseAlphaMode;
- material_data[MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD] = mAlphaMaskCutoff;
-
- return material_data;
-}
-
-void LLMaterial::fromLLSD(const LLSD& material_data)
-{
- mNormalID = getMaterialField<LLSD::UUID>(material_data, MATERIALS_CAP_NORMAL_MAP_FIELD, LLSD::TypeUUID);
-
- S32 normalOffsetXInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD, LLSD::TypeInteger);
- S32 normalOffsetYInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD, LLSD::TypeInteger);
- S32 normalRotInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD, LLSD::TypeInteger);
- S32 normalRepeatXInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD, LLSD::TypeInteger);
- S32 normalRepeatYInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD, LLSD::TypeInteger);
-
- mNormalOffsetX = F32(normalOffsetXInt) / MATERIALS_MULTIPLIER;
- mNormalOffsetY = F32(normalOffsetYInt) / MATERIALS_MULTIPLIER;
- mNormalRotation = F32(normalRotInt) / MATERIALS_MULTIPLIER;
- mNormalRepeatX = F32(normalRepeatXInt) / MATERIALS_MULTIPLIER;
- mNormalRepeatY = F32(normalRepeatYInt) / MATERIALS_MULTIPLIER;
-
- mSpecularID = getMaterialField<LLSD::UUID>(material_data, MATERIALS_CAP_SPECULAR_MAP_FIELD, LLSD::TypeUUID);
-
- S32 specularOffsetXInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD, LLSD::TypeInteger);
- S32 specularOffsetYInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD, LLSD::TypeInteger);
- S32 specularRotInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD, LLSD::TypeInteger);
- S32 specularRepeatXInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD, LLSD::TypeInteger);
- S32 specularRepeatYInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD, LLSD::TypeInteger);
-
- mSpecularOffsetX = F32(specularOffsetXInt) / MATERIALS_MULTIPLIER;
- mSpecularOffsetY = F32(specularOffsetYInt) / MATERIALS_MULTIPLIER;
- mSpecularRotation = F32(specularRotInt) / MATERIALS_MULTIPLIER;
- mSpecularRepeatX = F32(specularRepeatXInt) / MATERIALS_MULTIPLIER;
- mSpecularRepeatY = F32(specularRepeatYInt) / MATERIALS_MULTIPLIER;
-
- mSpecularLightColor.setValue(getMaterialField<LLSD>(material_data, MATERIALS_CAP_SPECULAR_COLOR_FIELD, LLSD::TypeArray));
- mSpecularLightExponent = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_EXP_FIELD, LLSD::TypeInteger);
- mEnvironmentIntensity = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_ENV_INTENSITY_FIELD, LLSD::TypeInteger);
- mDiffuseAlphaMode = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD, LLSD::TypeInteger);
- mAlphaMaskCutoff = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD, LLSD::TypeInteger);
-}
-
-bool LLMaterial::isNull() const
-{
- return (*this == null);
-}
-
-bool LLMaterial::operator == (const LLMaterial& rhs) const
-{
- return
- (mNormalID == rhs.mNormalID) && (mNormalOffsetX == rhs.mNormalOffsetX) && (mNormalOffsetY == rhs.mNormalOffsetY) &&
- (mNormalRepeatX == rhs.mNormalRepeatX) && (mNormalRepeatY == rhs.mNormalRepeatY) && (mNormalRotation == rhs.mNormalRotation) &&
- (mSpecularID == rhs.mSpecularID) && (mSpecularOffsetX == rhs.mSpecularOffsetX) && (mSpecularOffsetY == rhs.mSpecularOffsetY) &&
- (mSpecularRepeatX == rhs.mSpecularRepeatX) && (mSpecularRepeatY == rhs.mSpecularRepeatY) && (mSpecularRotation == rhs.mSpecularRotation) &&
- (mSpecularLightColor == rhs.mSpecularLightColor) && (mSpecularLightExponent == rhs.mSpecularLightExponent) &&
- (mEnvironmentIntensity == rhs.mEnvironmentIntensity) && (mDiffuseAlphaMode == rhs.mDiffuseAlphaMode) && (mAlphaMaskCutoff == rhs.mAlphaMaskCutoff);
-}
-
-bool LLMaterial::operator != (const LLMaterial& rhs) const
-{
- return !(*this == rhs);
-}
-
-
-U32 LLMaterial::getShaderMask(U32 alpha_mode, bool is_alpha)
-{ //NEVER incorporate this value into the message system -- this function will vary depending on viewer implementation
-
- //two least significant bits are "diffuse alpha mode"
- U32 ret = alpha_mode;
- if (ret == DIFFUSE_ALPHA_MODE_DEFAULT)
- {
- ret = getDiffuseAlphaMode();
- if (ret == DIFFUSE_ALPHA_MODE_BLEND && !is_alpha)
- {
- ret = DIFFUSE_ALPHA_MODE_NONE;
- }
- }
-
- llassert(ret < SHADER_COUNT);
-
- //next bit is whether or not specular map is present
- const U32 SPEC_BIT = 0x4;
-
- if (getSpecularID().notNull())
- {
- ret |= SPEC_BIT;
- }
-
- llassert(ret < SHADER_COUNT);
-
- //next bit is whether or not normal map is present
- const U32 NORM_BIT = 0x8;
- if (getNormalID().notNull())
- {
- ret |= NORM_BIT;
- }
-
- llassert(ret < SHADER_COUNT);
-
- return ret;
-}
-
-LLUUID LLMaterial::getHash() const
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- // HACK - hash the bytes of this LLMaterial, but trim off the S32 in LLRefCount
- LLUUID id;
- HBXXH128::digest(id, (unsigned char*)this + sizeof(LLRefCount), sizeof(*this) - sizeof(LLRefCount));
- return id;
-}
-
+/** + * @file llmaterial.cpp + * @brief Material definition + * + * $LicenseInfo:firstyear=2006&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 "linden_common.h" + +#include "llmaterial.h" +#include "hbxxh.h" + +/** + * Materials cap parameters + */ +#define MATERIALS_CAP_NORMAL_MAP_FIELD "NormMap" +#define MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD "NormOffsetX" +#define MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD "NormOffsetY" +#define MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD "NormRepeatX" +#define MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD "NormRepeatY" +#define MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD "NormRotation" + +#define MATERIALS_CAP_SPECULAR_MAP_FIELD "SpecMap" +#define MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD "SpecOffsetX" +#define MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD "SpecOffsetY" +#define MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD "SpecRepeatX" +#define MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD "SpecRepeatY" +#define MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD "SpecRotation" + +#define MATERIALS_CAP_SPECULAR_COLOR_FIELD "SpecColor" +#define MATERIALS_CAP_SPECULAR_EXP_FIELD "SpecExp" +#define MATERIALS_CAP_ENV_INTENSITY_FIELD "EnvIntensity" +#define MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD "AlphaMaskCutoff" +#define MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD "DiffuseAlphaMode" + +const LLColor4U LLMaterial::DEFAULT_SPECULAR_LIGHT_COLOR(255,255,255,255); + +/** + * Materials constants + */ + +const F32 MATERIALS_MULTIPLIER = 10000.f; + +/** + * Helper functions + */ + +template<typename T> T getMaterialField(const LLSD& data, const std::string& field, const LLSD::Type field_type) +{ + if ( (data.has(field)) && (field_type == data[field].type()) ) + { + return (T)data[field]; + } + LL_ERRS() << "Missing or mistyped field '" << field << "' in material definition" << LL_ENDL; + return (T)LLSD(); +} + +// GCC didn't like the generic form above for some reason +template<> LLUUID getMaterialField(const LLSD& data, const std::string& field, const LLSD::Type field_type) +{ + if ( (data.has(field)) && (field_type == data[field].type()) ) + { + return data[field].asUUID(); + } + LL_ERRS() << "Missing or mistyped field '" << field << "' in material definition" << LL_ENDL; + return LLUUID::null; +} + +/** + * LLMaterial class + */ + +const LLMaterial LLMaterial::null; + +LLMaterial::LLMaterial() + : mNormalOffsetX(0.0f) + , mNormalOffsetY(0.0f) + , mNormalRepeatX(1.0f) + , mNormalRepeatY(1.0f) + , mNormalRotation(0.0f) + , mSpecularOffsetX(0.0f) + , mSpecularOffsetY(0.0f) + , mSpecularRepeatX(1.0f) + , mSpecularRepeatY(1.0f) + , mSpecularRotation(0.0f) + , mSpecularLightColor(LLMaterial::DEFAULT_SPECULAR_LIGHT_COLOR) + , mSpecularLightExponent(LLMaterial::DEFAULT_SPECULAR_LIGHT_EXPONENT) + , mEnvironmentIntensity(LLMaterial::DEFAULT_ENV_INTENSITY) + , mDiffuseAlphaMode(LLMaterial::DIFFUSE_ALPHA_MODE_BLEND) + , mAlphaMaskCutoff(0) +{ +} + +LLMaterial::LLMaterial(const LLSD& material_data) +{ + fromLLSD(material_data); +} + +const LLUUID& LLMaterial::getNormalID() const +{ + return mNormalID; +} + +void LLMaterial::setNormalID(const LLUUID& normal_id) +{ + mNormalID = normal_id; +} + +void LLMaterial::getNormalOffset(F32& offset_x, F32& offset_y) const +{ + offset_x = mNormalOffsetX; + offset_y = mNormalOffsetY; +} + +F32 LLMaterial::getNormalOffsetX() const +{ + return mNormalOffsetX; +} + +F32 LLMaterial::getNormalOffsetY() const +{ + return mNormalOffsetY; +} + +void LLMaterial::setNormalOffset(F32 offset_x, F32 offset_y) +{ + mNormalOffsetX = offset_x; + mNormalOffsetY = offset_y; +} + +void LLMaterial::setNormalOffsetX(F32 offset_x) +{ + mNormalOffsetX = offset_x; +} + +void LLMaterial::setNormalOffsetY(F32 offset_y) +{ + mNormalOffsetY = offset_y; +} + +void LLMaterial::getNormalRepeat(F32& repeat_x, F32& repeat_y) const +{ + repeat_x = mNormalRepeatX; + repeat_y = mNormalRepeatY; +} + +F32 LLMaterial::getNormalRepeatX() const +{ + return mNormalRepeatX; +} + +F32 LLMaterial::getNormalRepeatY() const +{ + return mNormalRepeatY; +} + +void LLMaterial::setNormalRepeat(F32 repeat_x, F32 repeat_y) +{ + mNormalRepeatX = repeat_x; + mNormalRepeatY = repeat_y; +} + +void LLMaterial::setNormalRepeatX(F32 repeat_x) +{ + mNormalRepeatX = repeat_x; +} + +void LLMaterial::setNormalRepeatY(F32 repeat_y) +{ + mNormalRepeatY = repeat_y; +} + +F32 LLMaterial::getNormalRotation() const +{ + return mNormalRotation; +} + +void LLMaterial::setNormalRotation(F32 rot) +{ + mNormalRotation = rot; +} + +const LLUUID& LLMaterial::getSpecularID() const +{ + return mSpecularID; +} + +void LLMaterial::setSpecularID(const LLUUID& specular_id) +{ + mSpecularID = specular_id; +} + +void LLMaterial::getSpecularOffset(F32& offset_x, F32& offset_y) const +{ + offset_x = mSpecularOffsetX; + offset_y = mSpecularOffsetY; +} + +F32 LLMaterial::getSpecularOffsetX() const +{ + return mSpecularOffsetX; +} + +F32 LLMaterial::getSpecularOffsetY() const +{ + return mSpecularOffsetY; +} + +void LLMaterial::setSpecularOffset(F32 offset_x, F32 offset_y) +{ + mSpecularOffsetX = offset_x; + mSpecularOffsetY = offset_y; +} + +void LLMaterial::setSpecularOffsetX(F32 offset_x) +{ + mSpecularOffsetX = offset_x; +} + +void LLMaterial::setSpecularOffsetY(F32 offset_y) +{ + mSpecularOffsetY = offset_y; +} + +void LLMaterial::getSpecularRepeat(F32& repeat_x, F32& repeat_y) const +{ + repeat_x = mSpecularRepeatX; + repeat_y = mSpecularRepeatY; +} + +F32 LLMaterial::getSpecularRepeatX() const +{ + return mSpecularRepeatX; +} + +F32 LLMaterial::getSpecularRepeatY() const +{ + return mSpecularRepeatY; +} + +void LLMaterial::setSpecularRepeat(F32 repeat_x, F32 repeat_y) +{ + mSpecularRepeatX = repeat_x; mSpecularRepeatY = repeat_y; +} + +void LLMaterial::setSpecularRepeatX(F32 repeat_x) +{ + mSpecularRepeatX = repeat_x; +} + +void LLMaterial::setSpecularRepeatY(F32 repeat_y) +{ + mSpecularRepeatY = repeat_y; +} + +F32 LLMaterial::getSpecularRotation() const +{ + return mSpecularRotation; +} + +void LLMaterial::setSpecularRotation(F32 rot) +{ + mSpecularRotation = rot; +} + +const LLColor4U LLMaterial::getSpecularLightColor() const +{ + return mSpecularLightColor; +} + +void LLMaterial::setSpecularLightColor(const LLColor4U& color) +{ + mSpecularLightColor = color; +} + +U8 LLMaterial::getSpecularLightExponent() const +{ + return mSpecularLightExponent; +} + +void LLMaterial::setSpecularLightExponent(U8 exponent) +{ + mSpecularLightExponent = exponent; +} + +U8 LLMaterial::getEnvironmentIntensity() const +{ + return mEnvironmentIntensity; +} + +void LLMaterial::setEnvironmentIntensity(U8 intensity) +{ + mEnvironmentIntensity = intensity; +} + +U8 LLMaterial::getDiffuseAlphaMode() const +{ + return mDiffuseAlphaMode; +} + +void LLMaterial::setDiffuseAlphaMode(U8 alpha_mode) +{ + mDiffuseAlphaMode = alpha_mode; +} + +U8 LLMaterial::getAlphaMaskCutoff() const +{ + return mAlphaMaskCutoff; +} + +void LLMaterial::setAlphaMaskCutoff(U8 cutoff) +{ + mAlphaMaskCutoff = cutoff; +} + +LLSD LLMaterial::asLLSD() const +{ + LLSD material_data; + + S32 normalOffsetXInt = ll_round(mNormalOffsetX * MATERIALS_MULTIPLIER); + S32 normalOffsetYInt = ll_round(mNormalOffsetY * MATERIALS_MULTIPLIER); + S32 normalRotInt = ll_round(mNormalRotation * MATERIALS_MULTIPLIER); + + material_data[MATERIALS_CAP_NORMAL_MAP_FIELD] = mNormalID; + material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD] = normalOffsetXInt; + material_data[MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD] = normalOffsetYInt; + material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD] = ll_round(mNormalRepeatX * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD] = ll_round(mNormalRepeatY * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD] = normalRotInt; + + material_data[MATERIALS_CAP_SPECULAR_MAP_FIELD] = mSpecularID; + + S32 specularOffsetXInt = ll_round(mSpecularOffsetX * MATERIALS_MULTIPLIER); + S32 specularOffsetYInt = ll_round(mSpecularOffsetY * MATERIALS_MULTIPLIER); + S32 specularRotInt = ll_round(mSpecularRotation * MATERIALS_MULTIPLIER); + + material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD] = specularOffsetXInt; + material_data[MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD] = specularOffsetYInt; + material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD] = ll_round(mSpecularRepeatX * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD] = ll_round(mSpecularRepeatY * MATERIALS_MULTIPLIER); + material_data[MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD] = specularRotInt; + + material_data[MATERIALS_CAP_SPECULAR_COLOR_FIELD] = mSpecularLightColor.getValue(); + material_data[MATERIALS_CAP_SPECULAR_EXP_FIELD] = mSpecularLightExponent; + material_data[MATERIALS_CAP_ENV_INTENSITY_FIELD] = mEnvironmentIntensity; + material_data[MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD] = mDiffuseAlphaMode; + material_data[MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD] = mAlphaMaskCutoff; + + return material_data; +} + +void LLMaterial::fromLLSD(const LLSD& material_data) +{ + mNormalID = getMaterialField<LLSD::UUID>(material_data, MATERIALS_CAP_NORMAL_MAP_FIELD, LLSD::TypeUUID); + + S32 normalOffsetXInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_OFFSET_X_FIELD, LLSD::TypeInteger); + S32 normalOffsetYInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_OFFSET_Y_FIELD, LLSD::TypeInteger); + S32 normalRotInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_ROTATION_FIELD, LLSD::TypeInteger); + S32 normalRepeatXInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_REPEAT_X_FIELD, LLSD::TypeInteger); + S32 normalRepeatYInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_NORMAL_MAP_REPEAT_Y_FIELD, LLSD::TypeInteger); + + mNormalOffsetX = F32(normalOffsetXInt) / MATERIALS_MULTIPLIER; + mNormalOffsetY = F32(normalOffsetYInt) / MATERIALS_MULTIPLIER; + mNormalRotation = F32(normalRotInt) / MATERIALS_MULTIPLIER; + mNormalRepeatX = F32(normalRepeatXInt) / MATERIALS_MULTIPLIER; + mNormalRepeatY = F32(normalRepeatYInt) / MATERIALS_MULTIPLIER; + + mSpecularID = getMaterialField<LLSD::UUID>(material_data, MATERIALS_CAP_SPECULAR_MAP_FIELD, LLSD::TypeUUID); + + S32 specularOffsetXInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_OFFSET_X_FIELD, LLSD::TypeInteger); + S32 specularOffsetYInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_OFFSET_Y_FIELD, LLSD::TypeInteger); + S32 specularRotInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_ROTATION_FIELD, LLSD::TypeInteger); + S32 specularRepeatXInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_REPEAT_X_FIELD, LLSD::TypeInteger); + S32 specularRepeatYInt = getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_MAP_REPEAT_Y_FIELD, LLSD::TypeInteger); + + mSpecularOffsetX = F32(specularOffsetXInt) / MATERIALS_MULTIPLIER; + mSpecularOffsetY = F32(specularOffsetYInt) / MATERIALS_MULTIPLIER; + mSpecularRotation = F32(specularRotInt) / MATERIALS_MULTIPLIER; + mSpecularRepeatX = F32(specularRepeatXInt) / MATERIALS_MULTIPLIER; + mSpecularRepeatY = F32(specularRepeatYInt) / MATERIALS_MULTIPLIER; + + mSpecularLightColor.setValue(getMaterialField<LLSD>(material_data, MATERIALS_CAP_SPECULAR_COLOR_FIELD, LLSD::TypeArray)); + mSpecularLightExponent = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_SPECULAR_EXP_FIELD, LLSD::TypeInteger); + mEnvironmentIntensity = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_ENV_INTENSITY_FIELD, LLSD::TypeInteger); + mDiffuseAlphaMode = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_DIFFUSE_ALPHA_MODE_FIELD, LLSD::TypeInteger); + mAlphaMaskCutoff = (U8)getMaterialField<LLSD::Integer>(material_data, MATERIALS_CAP_ALPHA_MASK_CUTOFF_FIELD, LLSD::TypeInteger); +} + +bool LLMaterial::isNull() const +{ + return (*this == null); +} + +bool LLMaterial::operator == (const LLMaterial& rhs) const +{ + return + (mNormalID == rhs.mNormalID) && (mNormalOffsetX == rhs.mNormalOffsetX) && (mNormalOffsetY == rhs.mNormalOffsetY) && + (mNormalRepeatX == rhs.mNormalRepeatX) && (mNormalRepeatY == rhs.mNormalRepeatY) && (mNormalRotation == rhs.mNormalRotation) && + (mSpecularID == rhs.mSpecularID) && (mSpecularOffsetX == rhs.mSpecularOffsetX) && (mSpecularOffsetY == rhs.mSpecularOffsetY) && + (mSpecularRepeatX == rhs.mSpecularRepeatX) && (mSpecularRepeatY == rhs.mSpecularRepeatY) && (mSpecularRotation == rhs.mSpecularRotation) && + (mSpecularLightColor == rhs.mSpecularLightColor) && (mSpecularLightExponent == rhs.mSpecularLightExponent) && + (mEnvironmentIntensity == rhs.mEnvironmentIntensity) && (mDiffuseAlphaMode == rhs.mDiffuseAlphaMode) && (mAlphaMaskCutoff == rhs.mAlphaMaskCutoff); +} + +bool LLMaterial::operator != (const LLMaterial& rhs) const +{ + return !(*this == rhs); +} + + +U32 LLMaterial::getShaderMask(U32 alpha_mode, bool is_alpha) +{ //NEVER incorporate this value into the message system -- this function will vary depending on viewer implementation + + //two least significant bits are "diffuse alpha mode" + U32 ret = alpha_mode; + if (ret == DIFFUSE_ALPHA_MODE_DEFAULT) + { + ret = getDiffuseAlphaMode(); + if (ret == DIFFUSE_ALPHA_MODE_BLEND && !is_alpha) + { + ret = DIFFUSE_ALPHA_MODE_NONE; + } + } + + llassert(ret < SHADER_COUNT); + + //next bit is whether or not specular map is present + const U32 SPEC_BIT = 0x4; + + if (getSpecularID().notNull()) + { + ret |= SPEC_BIT; + } + + llassert(ret < SHADER_COUNT); + + //next bit is whether or not normal map is present + const U32 NORM_BIT = 0x8; + if (getNormalID().notNull()) + { + ret |= NORM_BIT; + } + + llassert(ret < SHADER_COUNT); + + return ret; +} + +LLUUID LLMaterial::getHash() const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + // HACK - hash the bytes of this LLMaterial, but trim off the S32 in LLRefCount + LLUUID id; + HBXXH128::digest(id, (unsigned char*)this + sizeof(LLRefCount), sizeof(*this) - sizeof(LLRefCount)); + return id; +} + diff --git a/indra/llprimitive/llmaterialtable.cpp b/indra/llprimitive/llmaterialtable.cpp index ee92a59c3e..44c97d63e7 100644 --- a/indra/llprimitive/llmaterialtable.cpp +++ b/indra/llprimitive/llmaterialtable.cpp @@ -1,755 +1,755 @@ -/**
- * @file llmaterialtable.cpp
- * @brief Table of material names and IDs for viewer
- *
- * $LicenseInfo:firstyear=2001&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 "linden_common.h"
-
-#include "llmaterialtable.h"
-#include "indra_constants.h"
-#include "llstl.h"
-#include "material_codes.h"
-#include "sound_ids.h"
-
-LLMaterialTable LLMaterialTable::basic(1);
-
-/*
- Old Havok 1 constants
-
-// these are the approximately correct friction values for various materials
-// however Havok1's friction dynamics are not very correct, so the effective
-// friction coefficients that result from these numbers are approximately
-// 25-50% too low, more incorrect for the lower values.
-F32 const LLMaterialTable::FRICTION_MIN = 0.2f;
-F32 const LLMaterialTable::FRICTION_GLASS = 0.2f; // borosilicate glass
-F32 const LLMaterialTable::FRICTION_LIGHT = 0.2f; //
-F32 const LLMaterialTable::FRICTION_METAL = 0.3f; // steel
-F32 const LLMaterialTable::FRICTION_PLASTIC = 0.4f; // HDPE
-F32 const LLMaterialTable::FRICTION_WOOD = 0.6f; // southern pine
-F32 const LLMaterialTable::FRICTION_FLESH = 0.60f; // saltwater
-F32 const LLMaterialTable::FRICTION_LAND = 0.78f; // dirt
-F32 const LLMaterialTable::FRICTION_STONE = 0.8f; // concrete
-F32 const LLMaterialTable::FRICTION_RUBBER = 0.9f; //
-F32 const LLMaterialTable::FRICTION_MAX = 0.95f; //
-*/
-
-// #if LL_CURRENT_HAVOK_VERSION == LL_HAVOK_VERSION_460
-// Havok4 has more correct friction dynamics, however here we have to use
-// the "incorrect" equivalents for the legacy Havok1 behavior
-F32 const LLMaterialTable::FRICTION_MIN = 0.15f;
-F32 const LLMaterialTable::FRICTION_GLASS = 0.13f; // borosilicate glass
-F32 const LLMaterialTable::FRICTION_LIGHT = 0.14f; //
-F32 const LLMaterialTable::FRICTION_METAL = 0.22f; // steel
-F32 const LLMaterialTable::FRICTION_PLASTIC = 0.3f; // HDPE
-F32 const LLMaterialTable::FRICTION_WOOD = 0.44f; // southern pine
-F32 const LLMaterialTable::FRICTION_FLESH = 0.46f; // saltwater
-F32 const LLMaterialTable::FRICTION_LAND = 0.58f; // dirt
-F32 const LLMaterialTable::FRICTION_STONE = 0.6f; // concrete
-F32 const LLMaterialTable::FRICTION_RUBBER = 0.67f; //
-F32 const LLMaterialTable::FRICTION_MAX = 0.71f; //
-// #endif
-
-F32 const LLMaterialTable::RESTITUTION_MIN = 0.02f;
-F32 const LLMaterialTable::RESTITUTION_LAND = LLMaterialTable::RESTITUTION_MIN;
-F32 const LLMaterialTable::RESTITUTION_FLESH = 0.2f; // saltwater
-F32 const LLMaterialTable::RESTITUTION_STONE = 0.4f; // concrete
-F32 const LLMaterialTable::RESTITUTION_METAL = 0.4f; // steel
-F32 const LLMaterialTable::RESTITUTION_WOOD = 0.5f; // southern pine
-F32 const LLMaterialTable::RESTITUTION_GLASS = 0.7f; // borosilicate glass
-F32 const LLMaterialTable::RESTITUTION_PLASTIC = 0.7f; // HDPE
-F32 const LLMaterialTable::RESTITUTION_LIGHT = 0.7f; //
-F32 const LLMaterialTable::RESTITUTION_RUBBER = 0.9f; //
-F32 const LLMaterialTable::RESTITUTION_MAX = 0.95f;
-
-F32 const LLMaterialTable::DEFAULT_FRICTION = 0.5f;
-F32 const LLMaterialTable::DEFAULT_RESTITUTION = 0.4f;
-
-LLMaterialTable::LLMaterialTable()
- : mCollisionSoundMatrix(NULL),
- mSlidingSoundMatrix(NULL),
- mRollingSoundMatrix(NULL)
-{
-}
-
-LLMaterialTable::LLMaterialTable(U8 isBasic)
-{
- initBasicTable();
-}
-
-LLMaterialTable::~LLMaterialTable()
-{
- if (mCollisionSoundMatrix)
- {
- delete [] mCollisionSoundMatrix;
- mCollisionSoundMatrix = NULL;
- }
-
- if (mSlidingSoundMatrix)
- {
- delete [] mSlidingSoundMatrix;
- mSlidingSoundMatrix = NULL;
- }
-
- if (mRollingSoundMatrix)
- {
- delete [] mRollingSoundMatrix;
- mRollingSoundMatrix = NULL;
- }
-
- for_each(mMaterialInfoList.begin(), mMaterialInfoList.end(), DeletePointer());
- mMaterialInfoList.clear();
-}
-
-void LLMaterialTable::initTableTransNames(std::map<std::string, std::string> namemap)
-{
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- std::string name = infop->mName;
- infop->mName = namemap[name];
- }
-}
-
-void LLMaterialTable::initBasicTable()
-{
- // *TODO: Translate
- add(LL_MCODE_STONE,std::string("Stone"), LL_DEFAULT_STONE_UUID);
- add(LL_MCODE_METAL,std::string("Metal"), LL_DEFAULT_METAL_UUID);
- add(LL_MCODE_GLASS,std::string("Glass"), LL_DEFAULT_GLASS_UUID);
- add(LL_MCODE_WOOD,std::string("Wood"), LL_DEFAULT_WOOD_UUID);
- add(LL_MCODE_FLESH,std::string("Flesh"), LL_DEFAULT_FLESH_UUID);
- add(LL_MCODE_PLASTIC,std::string("Plastic"), LL_DEFAULT_PLASTIC_UUID);
- add(LL_MCODE_RUBBER,std::string("Rubber"), LL_DEFAULT_RUBBER_UUID);
- add(LL_MCODE_LIGHT,std::string("Light"), LL_DEFAULT_LIGHT_UUID);
-
- // specify densities for these materials. . .
- // these were taken from http://www.mcelwee.net/html/densities_of_various_materials.html
-
- addDensity(LL_MCODE_STONE,30.f);
- addDensity(LL_MCODE_METAL,50.f);
- addDensity(LL_MCODE_GLASS,20.f);
- addDensity(LL_MCODE_WOOD,10.f);
- addDensity(LL_MCODE_FLESH,10.f);
- addDensity(LL_MCODE_PLASTIC,5.f);
- addDensity(LL_MCODE_RUBBER,0.5f); //
- addDensity(LL_MCODE_LIGHT,20.f); //
-
- // add damage and energy values
- addDamageAndEnergy(LL_MCODE_STONE, 1.f, 1.f, 1.f); // concrete
- addDamageAndEnergy(LL_MCODE_METAL, 1.f, 1.f, 1.f); // steel
- addDamageAndEnergy(LL_MCODE_GLASS, 1.f, 1.f, 1.f); // borosilicate glass
- addDamageAndEnergy(LL_MCODE_WOOD, 1.f, 1.f, 1.f); // southern pine
- addDamageAndEnergy(LL_MCODE_FLESH, 1.f, 1.f, 1.f); // saltwater
- addDamageAndEnergy(LL_MCODE_PLASTIC, 1.f, 1.f, 1.f); // HDPE
- addDamageAndEnergy(LL_MCODE_RUBBER, 1.f, 1.f, 1.f); //
- addDamageAndEnergy(LL_MCODE_LIGHT, 1.f, 1.f, 1.f); //
-
- addFriction(LL_MCODE_STONE,0.8f); // concrete
- addFriction(LL_MCODE_METAL,0.3f); // steel
- addFriction(LL_MCODE_GLASS,0.2f); // borosilicate glass
- addFriction(LL_MCODE_WOOD,0.6f); // southern pine
- addFriction(LL_MCODE_FLESH,0.9f); // saltwater
- addFriction(LL_MCODE_PLASTIC,0.4f); // HDPE
- addFriction(LL_MCODE_RUBBER,0.9f); //
- addFriction(LL_MCODE_LIGHT,0.2f); //
-
- addRestitution(LL_MCODE_STONE,0.4f); // concrete
- addRestitution(LL_MCODE_METAL,0.4f); // steel
- addRestitution(LL_MCODE_GLASS,0.7f); // borosilicate glass
- addRestitution(LL_MCODE_WOOD,0.5f); // southern pine
- addRestitution(LL_MCODE_FLESH,0.3f); // saltwater
- addRestitution(LL_MCODE_PLASTIC,0.7f); // HDPE
- addRestitution(LL_MCODE_RUBBER,0.9f); //
- addRestitution(LL_MCODE_LIGHT,0.7f); //
-
- addShatterSound(LL_MCODE_STONE,LLUUID("ea296329-0f09-4993-af1b-e6784bab1dc9"));
- addShatterSound(LL_MCODE_METAL,LLUUID("d1375446-1c4d-470b-9135-30132433b678"));
- addShatterSound(LL_MCODE_GLASS,LLUUID("85cda060-b393-48e6-81c8-2cfdfb275351"));
- addShatterSound(LL_MCODE_WOOD,LLUUID("6f00669f-15e0-4793-a63e-c03f62fee43a"));
- addShatterSound(LL_MCODE_FLESH,LLUUID("2d8c6f51-149e-4e23-8413-93a379b42b67"));
- addShatterSound(LL_MCODE_PLASTIC,LLUUID("d55c7f3c-e1c3-4ddc-9eff-9ef805d9190e"));
- addShatterSound(LL_MCODE_RUBBER,LLUUID("212b6d1e-8d9c-4986-b3aa-f3c6df8d987d"));
- addShatterSound(LL_MCODE_LIGHT,LLUUID("d55c7f3c-e1c3-4ddc-9eff-9ef805d9190e"));
-
- // CollisionSounds
- mCollisionSoundMatrix = new LLUUID[LL_MCODE_END*LL_MCODE_END];
- if (mCollisionSoundMatrix)
- {
- addCollisionSound(LL_MCODE_STONE, LL_MCODE_STONE, SND_STONE_STONE);
- addCollisionSound(LL_MCODE_STONE, LL_MCODE_METAL, SND_STONE_METAL);
- addCollisionSound(LL_MCODE_STONE, LL_MCODE_GLASS, SND_STONE_GLASS);
- addCollisionSound(LL_MCODE_STONE, LL_MCODE_WOOD, SND_STONE_WOOD);
- addCollisionSound(LL_MCODE_STONE, LL_MCODE_FLESH, SND_STONE_FLESH);
- addCollisionSound(LL_MCODE_STONE, LL_MCODE_PLASTIC, SND_STONE_PLASTIC);
- addCollisionSound(LL_MCODE_STONE, LL_MCODE_RUBBER, SND_STONE_RUBBER);
- addCollisionSound(LL_MCODE_STONE, LL_MCODE_LIGHT, SND_STONE_PLASTIC);
-
- addCollisionSound(LL_MCODE_METAL, LL_MCODE_METAL, SND_METAL_METAL);
- addCollisionSound(LL_MCODE_METAL, LL_MCODE_GLASS, SND_METAL_GLASS);
- addCollisionSound(LL_MCODE_METAL, LL_MCODE_WOOD, SND_METAL_WOOD);
- addCollisionSound(LL_MCODE_METAL, LL_MCODE_FLESH, SND_METAL_FLESH);
- addCollisionSound(LL_MCODE_METAL, LL_MCODE_PLASTIC, SND_METAL_PLASTIC);
- addCollisionSound(LL_MCODE_METAL, LL_MCODE_LIGHT, SND_METAL_PLASTIC);
- addCollisionSound(LL_MCODE_METAL, LL_MCODE_RUBBER, SND_METAL_RUBBER);
-
- addCollisionSound(LL_MCODE_GLASS, LL_MCODE_GLASS, SND_GLASS_GLASS);
- addCollisionSound(LL_MCODE_GLASS, LL_MCODE_WOOD, SND_GLASS_WOOD);
- addCollisionSound(LL_MCODE_GLASS, LL_MCODE_FLESH, SND_GLASS_FLESH);
- addCollisionSound(LL_MCODE_GLASS, LL_MCODE_PLASTIC, SND_GLASS_PLASTIC);
- addCollisionSound(LL_MCODE_GLASS, LL_MCODE_RUBBER, SND_GLASS_RUBBER);
- addCollisionSound(LL_MCODE_GLASS, LL_MCODE_LIGHT, SND_GLASS_PLASTIC);
-
- addCollisionSound(LL_MCODE_WOOD, LL_MCODE_WOOD, SND_WOOD_WOOD);
- addCollisionSound(LL_MCODE_WOOD, LL_MCODE_FLESH, SND_WOOD_FLESH);
- addCollisionSound(LL_MCODE_WOOD, LL_MCODE_PLASTIC, SND_WOOD_PLASTIC);
- addCollisionSound(LL_MCODE_WOOD, LL_MCODE_RUBBER, SND_WOOD_RUBBER);
- addCollisionSound(LL_MCODE_WOOD, LL_MCODE_LIGHT, SND_WOOD_PLASTIC);
-
- addCollisionSound(LL_MCODE_FLESH, LL_MCODE_FLESH, SND_FLESH_FLESH);
- addCollisionSound(LL_MCODE_FLESH, LL_MCODE_PLASTIC, SND_FLESH_PLASTIC);
- addCollisionSound(LL_MCODE_FLESH, LL_MCODE_RUBBER, SND_FLESH_RUBBER);
- addCollisionSound(LL_MCODE_FLESH, LL_MCODE_LIGHT, SND_FLESH_PLASTIC);
-
- addCollisionSound(LL_MCODE_RUBBER, LL_MCODE_RUBBER, SND_RUBBER_RUBBER);
- addCollisionSound(LL_MCODE_RUBBER, LL_MCODE_PLASTIC, SND_RUBBER_PLASTIC);
- addCollisionSound(LL_MCODE_RUBBER, LL_MCODE_LIGHT, SND_RUBBER_PLASTIC);
-
- addCollisionSound(LL_MCODE_PLASTIC, LL_MCODE_PLASTIC, SND_PLASTIC_PLASTIC);
- addCollisionSound(LL_MCODE_PLASTIC, LL_MCODE_LIGHT, SND_PLASTIC_PLASTIC);
-
- addCollisionSound(LL_MCODE_LIGHT, LL_MCODE_LIGHT, SND_PLASTIC_PLASTIC);
- }
-
- // Sliding Sounds
- mSlidingSoundMatrix = new LLUUID[LL_MCODE_END*LL_MCODE_END];
- if (mSlidingSoundMatrix)
- {
- addSlidingSound(LL_MCODE_STONE, LL_MCODE_STONE, SND_SLIDE_STONE_STONE);
- addSlidingSound(LL_MCODE_STONE, LL_MCODE_METAL, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_STONE, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_STONE, LL_MCODE_WOOD, SND_SLIDE_STONE_WOOD);
- addSlidingSound(LL_MCODE_STONE, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_STONE, LL_MCODE_PLASTIC, SND_SLIDE_STONE_PLASTIC);
- addSlidingSound(LL_MCODE_STONE, LL_MCODE_RUBBER, SND_SLIDE_STONE_RUBBER);
- addSlidingSound(LL_MCODE_STONE, LL_MCODE_LIGHT, SND_SLIDE_STONE_PLASTIC);
-
- addSlidingSound(LL_MCODE_METAL, LL_MCODE_METAL, SND_SLIDE_METAL_METAL);
- addSlidingSound(LL_MCODE_METAL, LL_MCODE_GLASS, SND_SLIDE_METAL_GLASS);
- addSlidingSound(LL_MCODE_METAL, LL_MCODE_WOOD, SND_SLIDE_METAL_WOOD);
- addSlidingSound(LL_MCODE_METAL, LL_MCODE_FLESH, SND_SLIDE_METAL_FLESH);
- addSlidingSound(LL_MCODE_METAL, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_METAL, LL_MCODE_RUBBER, SND_SLIDE_METAL_RUBBER);
- addSlidingSound(LL_MCODE_METAL, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
-
- addSlidingSound(LL_MCODE_GLASS, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_GLASS, LL_MCODE_WOOD, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_GLASS, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_GLASS, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_GLASS, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_GLASS, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
-
- addSlidingSound(LL_MCODE_WOOD, LL_MCODE_WOOD, SND_SLIDE_WOOD_WOOD);
- addSlidingSound(LL_MCODE_WOOD, LL_MCODE_FLESH, SND_SLIDE_WOOD_FLESH);
- addSlidingSound(LL_MCODE_WOOD, LL_MCODE_PLASTIC, SND_SLIDE_WOOD_PLASTIC);
- addSlidingSound(LL_MCODE_WOOD, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_WOOD, LL_MCODE_LIGHT, SND_SLIDE_WOOD_PLASTIC);
-
- addSlidingSound(LL_MCODE_FLESH, LL_MCODE_FLESH, SND_SLIDE_FLESH_FLESH);
- addSlidingSound(LL_MCODE_FLESH, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_FLESH, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_FLESH, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
-
- addSlidingSound(LL_MCODE_RUBBER, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_RUBBER, LL_MCODE_PLASTIC, SND_SLIDE_RUBBER_PLASTIC);
- addSlidingSound(LL_MCODE_RUBBER, LL_MCODE_LIGHT, SND_SLIDE_RUBBER_PLASTIC);
-
- addSlidingSound(LL_MCODE_PLASTIC, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
- addSlidingSound(LL_MCODE_PLASTIC, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
-
- addSlidingSound(LL_MCODE_LIGHT, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
- }
-
- // Rolling Sounds
- mRollingSoundMatrix = new LLUUID[LL_MCODE_END*LL_MCODE_END];
- if (mRollingSoundMatrix)
- {
- addRollingSound(LL_MCODE_STONE, LL_MCODE_STONE, SND_ROLL_STONE_STONE);
- addRollingSound(LL_MCODE_STONE, LL_MCODE_METAL, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_STONE, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_STONE, LL_MCODE_WOOD, SND_ROLL_STONE_WOOD);
- addRollingSound(LL_MCODE_STONE, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_STONE, LL_MCODE_PLASTIC, SND_ROLL_STONE_PLASTIC);
- addRollingSound(LL_MCODE_STONE, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_STONE, LL_MCODE_LIGHT, SND_ROLL_STONE_PLASTIC);
-
- addRollingSound(LL_MCODE_METAL, LL_MCODE_METAL, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_METAL, LL_MCODE_GLASS, SND_ROLL_METAL_GLASS);
- addRollingSound(LL_MCODE_METAL, LL_MCODE_WOOD, SND_ROLL_METAL_WOOD);
- addRollingSound(LL_MCODE_METAL, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_METAL, LL_MCODE_PLASTIC, SND_ROLL_METAL_WOOD);
- addRollingSound(LL_MCODE_METAL, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_METAL, LL_MCODE_LIGHT, SND_ROLL_METAL_WOOD);
-
- addRollingSound(LL_MCODE_GLASS, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_GLASS, LL_MCODE_WOOD, SND_ROLL_GLASS_WOOD);
- addRollingSound(LL_MCODE_GLASS, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_GLASS, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_GLASS, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_GLASS, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
-
- addRollingSound(LL_MCODE_WOOD, LL_MCODE_WOOD, SND_ROLL_WOOD_WOOD);
- addRollingSound(LL_MCODE_WOOD, LL_MCODE_FLESH, SND_ROLL_WOOD_FLESH);
- addRollingSound(LL_MCODE_WOOD, LL_MCODE_PLASTIC, SND_ROLL_WOOD_PLASTIC);
- addRollingSound(LL_MCODE_WOOD, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_WOOD, LL_MCODE_LIGHT, SND_ROLL_WOOD_PLASTIC);
-
- addRollingSound(LL_MCODE_FLESH, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_FLESH, LL_MCODE_PLASTIC, SND_ROLL_FLESH_PLASTIC);
- addRollingSound(LL_MCODE_FLESH, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_FLESH, LL_MCODE_LIGHT, SND_ROLL_FLESH_PLASTIC);
-
- addRollingSound(LL_MCODE_RUBBER, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_RUBBER, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
- addRollingSound(LL_MCODE_RUBBER, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
-
- addRollingSound(LL_MCODE_PLASTIC, LL_MCODE_PLASTIC, SND_ROLL_PLASTIC_PLASTIC);
- addRollingSound(LL_MCODE_PLASTIC, LL_MCODE_LIGHT, SND_ROLL_PLASTIC_PLASTIC);
-
- addRollingSound(LL_MCODE_LIGHT, LL_MCODE_LIGHT, SND_ROLL_PLASTIC_PLASTIC);
- }
-}
-
-bool LLMaterialTable::add(U8 mcode, const std::string& name, const LLUUID &uuid)
-{
- LLMaterialInfo *infop;
-
- infop = new LLMaterialInfo(mcode,name,uuid);
- if (!infop) return false;
-
- // Add at the end so the order in menus matches the order in this
- // file. JNC 11.30.01
- mMaterialInfoList.push_back(infop);
-
- return true;
-}
-
-bool LLMaterialTable::addCollisionSound(U8 mcode, U8 mcode2, const LLUUID &uuid)
-{
- if (mCollisionSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
- {
- mCollisionSoundMatrix[mcode * LL_MCODE_END + mcode2] = uuid;
- if (mcode != mcode2)
- {
- mCollisionSoundMatrix[mcode2 * LL_MCODE_END + mcode] = uuid;
- }
- }
- return true;
-}
-
-bool LLMaterialTable::addSlidingSound(U8 mcode, U8 mcode2, const LLUUID &uuid)
-{
- if (mSlidingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
- {
- mSlidingSoundMatrix[mcode * LL_MCODE_END + mcode2] = uuid;
- if (mcode != mcode2)
- {
- mSlidingSoundMatrix[mcode2 * LL_MCODE_END + mcode] = uuid;
- }
- }
- return true;
-}
-
-bool LLMaterialTable::addRollingSound(U8 mcode, U8 mcode2, const LLUUID &uuid)
-{
- if (mRollingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
- {
- mRollingSoundMatrix[mcode * LL_MCODE_END + mcode2] = uuid;
- if (mcode != mcode2)
- {
- mRollingSoundMatrix[mcode2 * LL_MCODE_END + mcode] = uuid;
- }
- }
- return true;
-}
-
-bool LLMaterialTable::addShatterSound(U8 mcode, const LLUUID &uuid)
-{
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- infop->mShatterSoundID = uuid;
- return true;
- }
- }
-
- return false;
-}
-
-bool LLMaterialTable::addDensity(U8 mcode, const F32 &density)
-{
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- infop->mDensity = density;
- return true;
- }
- }
-
- return false;
-}
-
-bool LLMaterialTable::addRestitution(U8 mcode, const F32 &restitution)
-{
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- infop->mRestitution = restitution;
- return true;
- }
- }
-
- return false;
-}
-
-bool LLMaterialTable::addFriction(U8 mcode, const F32 &friction)
-{
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- infop->mFriction = friction;
- return true;
- }
- }
-
- return false;
-}
-
-bool LLMaterialTable::addDamageAndEnergy(U8 mcode, const F32 &hp_mod, const F32 &damage_mod, const F32 &ep_mod)
-{
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- infop->mHPModifier = hp_mod;
- infop->mDamageModifier = damage_mod;
- infop->mEPModifier = ep_mod;
- return true;
- }
- }
-
- return false;
-}
-
-LLUUID LLMaterialTable::getDefaultTextureID(const std::string& name)
-{
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (name == infop->mName)
- {
- return infop->mDefaultTextureID;
- }
- }
-
- return LLUUID::null;
-}
-
-
-LLUUID LLMaterialTable::getDefaultTextureID(U8 mcode)
-{
- mcode &= LL_MCODE_MASK;
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- return infop->mDefaultTextureID;
- }
- }
-
- return LLUUID::null;
-}
-
-
-U8 LLMaterialTable::getMCode(const std::string& name)
-{
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (name == infop->mName)
- {
- return infop->mMCode;
- }
- }
-
- return 0;
-}
-
-
-std::string LLMaterialTable::getName(U8 mcode)
-{
- mcode &= LL_MCODE_MASK;
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- return infop->mName;
- }
- }
-
- return std::string();
-}
-
-
-LLUUID LLMaterialTable::getCollisionSoundUUID(U8 mcode, U8 mcode2)
-{
- mcode &= LL_MCODE_MASK;
- mcode2 &= LL_MCODE_MASK;
-
- //LL_INFOS() << "code 1: " << ((U32) mcode) << " code 2:" << ((U32) mcode2) << LL_ENDL;
- if (mCollisionSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
- {
- return(mCollisionSoundMatrix[mcode * LL_MCODE_END + mcode2]);
- }
- else
- {
- //LL_INFOS() << "Null Sound" << LL_ENDL;
- return(SND_NULL);
- }
-}
-
-bool LLMaterialTable::isCollisionSound(const LLUUID &uuid)
-{
- for (U8 i = 0; i < LL_MCODE_END; i++)
- {
- for (U8 j = 0; j < LL_MCODE_END; j++)
- {
- i &= LL_MCODE_MASK;
- j &= LL_MCODE_MASK;
- if (mCollisionSoundMatrix[i * LL_MCODE_END + j] == uuid)
- {
- return true;
- }
- }
- }
- return false;
-}
-
-LLUUID LLMaterialTable::getSlidingSoundUUID(U8 mcode, U8 mcode2)
-{
- mcode &= LL_MCODE_MASK;
- mcode2 &= LL_MCODE_MASK;
-
- if (mSlidingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
- {
- return(mSlidingSoundMatrix[mcode * LL_MCODE_END + mcode2]);
- }
- else
- {
- return(SND_NULL);
- }
-}
-
-LLUUID LLMaterialTable::getRollingSoundUUID(U8 mcode, U8 mcode2)
-{
- mcode &= LL_MCODE_MASK;
- mcode2 &= LL_MCODE_MASK;
-
- if (mRollingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
- {
- return(mRollingSoundMatrix[mcode * LL_MCODE_END + mcode2]);
- }
- else
- {
- return(SND_NULL);
- }
-}
-
-LLUUID LLMaterialTable::getGroundCollisionSoundUUID(U8 mcode)
-{
- // Create material appropriate sounds for collisions with the ground
- // For now, simply return a single sound for all materials
- return SND_STONE_DIRT_02;
-}
-
-LLUUID LLMaterialTable::getGroundSlidingSoundUUID(U8 mcode)
-{
- // Create material-specific sound for sliding on ground
- // For now, just return a single sound
- return SND_SLIDE_STONE_STONE_01;
-}
-
-LLUUID LLMaterialTable::getGroundRollingSoundUUID(U8 mcode)
-{
- // Create material-specific sound for rolling on ground
- // For now, just return a single sound
- return SND_SLIDE_STONE_STONE_01;
-}
-
-LLUUID LLMaterialTable::getCollisionParticleUUID(U8 mcode, U8 mcode2)
-{
- // Returns an appropriate UUID to use as sprite at collision betweeen objects
- // For now, just return a single image
- return IMG_SHOT;
-}
-
-LLUUID LLMaterialTable::getGroundCollisionParticleUUID(U8 mcode)
-{
- // Returns an appropriate
- // For now, just return a single sound
- return IMG_SMOKE_POOF;
-}
-
-
-F32 LLMaterialTable::getDensity(U8 mcode)
-{
- mcode &= LL_MCODE_MASK;
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- return infop->mDensity;
- }
- }
-
- return 0.f;
-}
-
-F32 LLMaterialTable::getRestitution(U8 mcode)
-{
- mcode &= LL_MCODE_MASK;
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- return infop->mRestitution;
- }
- }
-
- return LLMaterialTable::DEFAULT_RESTITUTION;
-}
-
-F32 LLMaterialTable::getFriction(U8 mcode)
-{
- mcode &= LL_MCODE_MASK;
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- return infop->mFriction;
- }
- }
-
- return LLMaterialTable::DEFAULT_FRICTION;
-}
-
-F32 LLMaterialTable::getHPMod(U8 mcode)
-{
- mcode &= LL_MCODE_MASK;
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- return infop->mHPModifier;
- }
- }
-
- return 1.f;
-}
-
-F32 LLMaterialTable::getDamageMod(U8 mcode)
-{
- mcode &= LL_MCODE_MASK;
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- return infop->mDamageModifier;
- }
- }
-
- return 1.f;
-}
-
-F32 LLMaterialTable::getEPMod(U8 mcode)
-{
- mcode &= LL_MCODE_MASK;
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- return infop->mEPModifier;
- }
- }
-
- return 1.f;
-}
-
-LLUUID LLMaterialTable::getShatterSoundUUID(U8 mcode)
-{
- mcode &= LL_MCODE_MASK;
- for (info_list_t::iterator iter = mMaterialInfoList.begin();
- iter != mMaterialInfoList.end(); ++iter)
- {
- LLMaterialInfo *infop = *iter;
- if (mcode == infop->mMCode)
- {
- return infop->mShatterSoundID;
- }
- }
-
- return SND_NULL;
-}
+/** + * @file llmaterialtable.cpp + * @brief Table of material names and IDs for viewer + * + * $LicenseInfo:firstyear=2001&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 "linden_common.h" + +#include "llmaterialtable.h" +#include "indra_constants.h" +#include "llstl.h" +#include "material_codes.h" +#include "sound_ids.h" + +LLMaterialTable LLMaterialTable::basic(1); + +/* + Old Havok 1 constants + +// these are the approximately correct friction values for various materials +// however Havok1's friction dynamics are not very correct, so the effective +// friction coefficients that result from these numbers are approximately +// 25-50% too low, more incorrect for the lower values. +F32 const LLMaterialTable::FRICTION_MIN = 0.2f; +F32 const LLMaterialTable::FRICTION_GLASS = 0.2f; // borosilicate glass +F32 const LLMaterialTable::FRICTION_LIGHT = 0.2f; // +F32 const LLMaterialTable::FRICTION_METAL = 0.3f; // steel +F32 const LLMaterialTable::FRICTION_PLASTIC = 0.4f; // HDPE +F32 const LLMaterialTable::FRICTION_WOOD = 0.6f; // southern pine +F32 const LLMaterialTable::FRICTION_FLESH = 0.60f; // saltwater +F32 const LLMaterialTable::FRICTION_LAND = 0.78f; // dirt +F32 const LLMaterialTable::FRICTION_STONE = 0.8f; // concrete +F32 const LLMaterialTable::FRICTION_RUBBER = 0.9f; // +F32 const LLMaterialTable::FRICTION_MAX = 0.95f; // +*/ + +// #if LL_CURRENT_HAVOK_VERSION == LL_HAVOK_VERSION_460 +// Havok4 has more correct friction dynamics, however here we have to use +// the "incorrect" equivalents for the legacy Havok1 behavior +F32 const LLMaterialTable::FRICTION_MIN = 0.15f; +F32 const LLMaterialTable::FRICTION_GLASS = 0.13f; // borosilicate glass +F32 const LLMaterialTable::FRICTION_LIGHT = 0.14f; // +F32 const LLMaterialTable::FRICTION_METAL = 0.22f; // steel +F32 const LLMaterialTable::FRICTION_PLASTIC = 0.3f; // HDPE +F32 const LLMaterialTable::FRICTION_WOOD = 0.44f; // southern pine +F32 const LLMaterialTable::FRICTION_FLESH = 0.46f; // saltwater +F32 const LLMaterialTable::FRICTION_LAND = 0.58f; // dirt +F32 const LLMaterialTable::FRICTION_STONE = 0.6f; // concrete +F32 const LLMaterialTable::FRICTION_RUBBER = 0.67f; // +F32 const LLMaterialTable::FRICTION_MAX = 0.71f; // +// #endif + +F32 const LLMaterialTable::RESTITUTION_MIN = 0.02f; +F32 const LLMaterialTable::RESTITUTION_LAND = LLMaterialTable::RESTITUTION_MIN; +F32 const LLMaterialTable::RESTITUTION_FLESH = 0.2f; // saltwater +F32 const LLMaterialTable::RESTITUTION_STONE = 0.4f; // concrete +F32 const LLMaterialTable::RESTITUTION_METAL = 0.4f; // steel +F32 const LLMaterialTable::RESTITUTION_WOOD = 0.5f; // southern pine +F32 const LLMaterialTable::RESTITUTION_GLASS = 0.7f; // borosilicate glass +F32 const LLMaterialTable::RESTITUTION_PLASTIC = 0.7f; // HDPE +F32 const LLMaterialTable::RESTITUTION_LIGHT = 0.7f; // +F32 const LLMaterialTable::RESTITUTION_RUBBER = 0.9f; // +F32 const LLMaterialTable::RESTITUTION_MAX = 0.95f; + +F32 const LLMaterialTable::DEFAULT_FRICTION = 0.5f; +F32 const LLMaterialTable::DEFAULT_RESTITUTION = 0.4f; + +LLMaterialTable::LLMaterialTable() + : mCollisionSoundMatrix(NULL), + mSlidingSoundMatrix(NULL), + mRollingSoundMatrix(NULL) +{ +} + +LLMaterialTable::LLMaterialTable(U8 isBasic) +{ + initBasicTable(); +} + +LLMaterialTable::~LLMaterialTable() +{ + if (mCollisionSoundMatrix) + { + delete [] mCollisionSoundMatrix; + mCollisionSoundMatrix = NULL; + } + + if (mSlidingSoundMatrix) + { + delete [] mSlidingSoundMatrix; + mSlidingSoundMatrix = NULL; + } + + if (mRollingSoundMatrix) + { + delete [] mRollingSoundMatrix; + mRollingSoundMatrix = NULL; + } + + for_each(mMaterialInfoList.begin(), mMaterialInfoList.end(), DeletePointer()); + mMaterialInfoList.clear(); +} + +void LLMaterialTable::initTableTransNames(std::map<std::string, std::string> namemap) +{ + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + std::string name = infop->mName; + infop->mName = namemap[name]; + } +} + +void LLMaterialTable::initBasicTable() +{ + // *TODO: Translate + add(LL_MCODE_STONE,std::string("Stone"), LL_DEFAULT_STONE_UUID); + add(LL_MCODE_METAL,std::string("Metal"), LL_DEFAULT_METAL_UUID); + add(LL_MCODE_GLASS,std::string("Glass"), LL_DEFAULT_GLASS_UUID); + add(LL_MCODE_WOOD,std::string("Wood"), LL_DEFAULT_WOOD_UUID); + add(LL_MCODE_FLESH,std::string("Flesh"), LL_DEFAULT_FLESH_UUID); + add(LL_MCODE_PLASTIC,std::string("Plastic"), LL_DEFAULT_PLASTIC_UUID); + add(LL_MCODE_RUBBER,std::string("Rubber"), LL_DEFAULT_RUBBER_UUID); + add(LL_MCODE_LIGHT,std::string("Light"), LL_DEFAULT_LIGHT_UUID); + + // specify densities for these materials. . . + // these were taken from http://www.mcelwee.net/html/densities_of_various_materials.html + + addDensity(LL_MCODE_STONE,30.f); + addDensity(LL_MCODE_METAL,50.f); + addDensity(LL_MCODE_GLASS,20.f); + addDensity(LL_MCODE_WOOD,10.f); + addDensity(LL_MCODE_FLESH,10.f); + addDensity(LL_MCODE_PLASTIC,5.f); + addDensity(LL_MCODE_RUBBER,0.5f); // + addDensity(LL_MCODE_LIGHT,20.f); // + + // add damage and energy values + addDamageAndEnergy(LL_MCODE_STONE, 1.f, 1.f, 1.f); // concrete + addDamageAndEnergy(LL_MCODE_METAL, 1.f, 1.f, 1.f); // steel + addDamageAndEnergy(LL_MCODE_GLASS, 1.f, 1.f, 1.f); // borosilicate glass + addDamageAndEnergy(LL_MCODE_WOOD, 1.f, 1.f, 1.f); // southern pine + addDamageAndEnergy(LL_MCODE_FLESH, 1.f, 1.f, 1.f); // saltwater + addDamageAndEnergy(LL_MCODE_PLASTIC, 1.f, 1.f, 1.f); // HDPE + addDamageAndEnergy(LL_MCODE_RUBBER, 1.f, 1.f, 1.f); // + addDamageAndEnergy(LL_MCODE_LIGHT, 1.f, 1.f, 1.f); // + + addFriction(LL_MCODE_STONE,0.8f); // concrete + addFriction(LL_MCODE_METAL,0.3f); // steel + addFriction(LL_MCODE_GLASS,0.2f); // borosilicate glass + addFriction(LL_MCODE_WOOD,0.6f); // southern pine + addFriction(LL_MCODE_FLESH,0.9f); // saltwater + addFriction(LL_MCODE_PLASTIC,0.4f); // HDPE + addFriction(LL_MCODE_RUBBER,0.9f); // + addFriction(LL_MCODE_LIGHT,0.2f); // + + addRestitution(LL_MCODE_STONE,0.4f); // concrete + addRestitution(LL_MCODE_METAL,0.4f); // steel + addRestitution(LL_MCODE_GLASS,0.7f); // borosilicate glass + addRestitution(LL_MCODE_WOOD,0.5f); // southern pine + addRestitution(LL_MCODE_FLESH,0.3f); // saltwater + addRestitution(LL_MCODE_PLASTIC,0.7f); // HDPE + addRestitution(LL_MCODE_RUBBER,0.9f); // + addRestitution(LL_MCODE_LIGHT,0.7f); // + + addShatterSound(LL_MCODE_STONE,LLUUID("ea296329-0f09-4993-af1b-e6784bab1dc9")); + addShatterSound(LL_MCODE_METAL,LLUUID("d1375446-1c4d-470b-9135-30132433b678")); + addShatterSound(LL_MCODE_GLASS,LLUUID("85cda060-b393-48e6-81c8-2cfdfb275351")); + addShatterSound(LL_MCODE_WOOD,LLUUID("6f00669f-15e0-4793-a63e-c03f62fee43a")); + addShatterSound(LL_MCODE_FLESH,LLUUID("2d8c6f51-149e-4e23-8413-93a379b42b67")); + addShatterSound(LL_MCODE_PLASTIC,LLUUID("d55c7f3c-e1c3-4ddc-9eff-9ef805d9190e")); + addShatterSound(LL_MCODE_RUBBER,LLUUID("212b6d1e-8d9c-4986-b3aa-f3c6df8d987d")); + addShatterSound(LL_MCODE_LIGHT,LLUUID("d55c7f3c-e1c3-4ddc-9eff-9ef805d9190e")); + + // CollisionSounds + mCollisionSoundMatrix = new LLUUID[LL_MCODE_END*LL_MCODE_END]; + if (mCollisionSoundMatrix) + { + addCollisionSound(LL_MCODE_STONE, LL_MCODE_STONE, SND_STONE_STONE); + addCollisionSound(LL_MCODE_STONE, LL_MCODE_METAL, SND_STONE_METAL); + addCollisionSound(LL_MCODE_STONE, LL_MCODE_GLASS, SND_STONE_GLASS); + addCollisionSound(LL_MCODE_STONE, LL_MCODE_WOOD, SND_STONE_WOOD); + addCollisionSound(LL_MCODE_STONE, LL_MCODE_FLESH, SND_STONE_FLESH); + addCollisionSound(LL_MCODE_STONE, LL_MCODE_PLASTIC, SND_STONE_PLASTIC); + addCollisionSound(LL_MCODE_STONE, LL_MCODE_RUBBER, SND_STONE_RUBBER); + addCollisionSound(LL_MCODE_STONE, LL_MCODE_LIGHT, SND_STONE_PLASTIC); + + addCollisionSound(LL_MCODE_METAL, LL_MCODE_METAL, SND_METAL_METAL); + addCollisionSound(LL_MCODE_METAL, LL_MCODE_GLASS, SND_METAL_GLASS); + addCollisionSound(LL_MCODE_METAL, LL_MCODE_WOOD, SND_METAL_WOOD); + addCollisionSound(LL_MCODE_METAL, LL_MCODE_FLESH, SND_METAL_FLESH); + addCollisionSound(LL_MCODE_METAL, LL_MCODE_PLASTIC, SND_METAL_PLASTIC); + addCollisionSound(LL_MCODE_METAL, LL_MCODE_LIGHT, SND_METAL_PLASTIC); + addCollisionSound(LL_MCODE_METAL, LL_MCODE_RUBBER, SND_METAL_RUBBER); + + addCollisionSound(LL_MCODE_GLASS, LL_MCODE_GLASS, SND_GLASS_GLASS); + addCollisionSound(LL_MCODE_GLASS, LL_MCODE_WOOD, SND_GLASS_WOOD); + addCollisionSound(LL_MCODE_GLASS, LL_MCODE_FLESH, SND_GLASS_FLESH); + addCollisionSound(LL_MCODE_GLASS, LL_MCODE_PLASTIC, SND_GLASS_PLASTIC); + addCollisionSound(LL_MCODE_GLASS, LL_MCODE_RUBBER, SND_GLASS_RUBBER); + addCollisionSound(LL_MCODE_GLASS, LL_MCODE_LIGHT, SND_GLASS_PLASTIC); + + addCollisionSound(LL_MCODE_WOOD, LL_MCODE_WOOD, SND_WOOD_WOOD); + addCollisionSound(LL_MCODE_WOOD, LL_MCODE_FLESH, SND_WOOD_FLESH); + addCollisionSound(LL_MCODE_WOOD, LL_MCODE_PLASTIC, SND_WOOD_PLASTIC); + addCollisionSound(LL_MCODE_WOOD, LL_MCODE_RUBBER, SND_WOOD_RUBBER); + addCollisionSound(LL_MCODE_WOOD, LL_MCODE_LIGHT, SND_WOOD_PLASTIC); + + addCollisionSound(LL_MCODE_FLESH, LL_MCODE_FLESH, SND_FLESH_FLESH); + addCollisionSound(LL_MCODE_FLESH, LL_MCODE_PLASTIC, SND_FLESH_PLASTIC); + addCollisionSound(LL_MCODE_FLESH, LL_MCODE_RUBBER, SND_FLESH_RUBBER); + addCollisionSound(LL_MCODE_FLESH, LL_MCODE_LIGHT, SND_FLESH_PLASTIC); + + addCollisionSound(LL_MCODE_RUBBER, LL_MCODE_RUBBER, SND_RUBBER_RUBBER); + addCollisionSound(LL_MCODE_RUBBER, LL_MCODE_PLASTIC, SND_RUBBER_PLASTIC); + addCollisionSound(LL_MCODE_RUBBER, LL_MCODE_LIGHT, SND_RUBBER_PLASTIC); + + addCollisionSound(LL_MCODE_PLASTIC, LL_MCODE_PLASTIC, SND_PLASTIC_PLASTIC); + addCollisionSound(LL_MCODE_PLASTIC, LL_MCODE_LIGHT, SND_PLASTIC_PLASTIC); + + addCollisionSound(LL_MCODE_LIGHT, LL_MCODE_LIGHT, SND_PLASTIC_PLASTIC); + } + + // Sliding Sounds + mSlidingSoundMatrix = new LLUUID[LL_MCODE_END*LL_MCODE_END]; + if (mSlidingSoundMatrix) + { + addSlidingSound(LL_MCODE_STONE, LL_MCODE_STONE, SND_SLIDE_STONE_STONE); + addSlidingSound(LL_MCODE_STONE, LL_MCODE_METAL, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_STONE, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_STONE, LL_MCODE_WOOD, SND_SLIDE_STONE_WOOD); + addSlidingSound(LL_MCODE_STONE, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_STONE, LL_MCODE_PLASTIC, SND_SLIDE_STONE_PLASTIC); + addSlidingSound(LL_MCODE_STONE, LL_MCODE_RUBBER, SND_SLIDE_STONE_RUBBER); + addSlidingSound(LL_MCODE_STONE, LL_MCODE_LIGHT, SND_SLIDE_STONE_PLASTIC); + + addSlidingSound(LL_MCODE_METAL, LL_MCODE_METAL, SND_SLIDE_METAL_METAL); + addSlidingSound(LL_MCODE_METAL, LL_MCODE_GLASS, SND_SLIDE_METAL_GLASS); + addSlidingSound(LL_MCODE_METAL, LL_MCODE_WOOD, SND_SLIDE_METAL_WOOD); + addSlidingSound(LL_MCODE_METAL, LL_MCODE_FLESH, SND_SLIDE_METAL_FLESH); + addSlidingSound(LL_MCODE_METAL, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_METAL, LL_MCODE_RUBBER, SND_SLIDE_METAL_RUBBER); + addSlidingSound(LL_MCODE_METAL, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01); + + addSlidingSound(LL_MCODE_GLASS, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_GLASS, LL_MCODE_WOOD, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_GLASS, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_GLASS, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_GLASS, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_GLASS, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01); + + addSlidingSound(LL_MCODE_WOOD, LL_MCODE_WOOD, SND_SLIDE_WOOD_WOOD); + addSlidingSound(LL_MCODE_WOOD, LL_MCODE_FLESH, SND_SLIDE_WOOD_FLESH); + addSlidingSound(LL_MCODE_WOOD, LL_MCODE_PLASTIC, SND_SLIDE_WOOD_PLASTIC); + addSlidingSound(LL_MCODE_WOOD, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_WOOD, LL_MCODE_LIGHT, SND_SLIDE_WOOD_PLASTIC); + + addSlidingSound(LL_MCODE_FLESH, LL_MCODE_FLESH, SND_SLIDE_FLESH_FLESH); + addSlidingSound(LL_MCODE_FLESH, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_FLESH, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_FLESH, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01); + + addSlidingSound(LL_MCODE_RUBBER, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_RUBBER, LL_MCODE_PLASTIC, SND_SLIDE_RUBBER_PLASTIC); + addSlidingSound(LL_MCODE_RUBBER, LL_MCODE_LIGHT, SND_SLIDE_RUBBER_PLASTIC); + + addSlidingSound(LL_MCODE_PLASTIC, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01); + addSlidingSound(LL_MCODE_PLASTIC, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01); + + addSlidingSound(LL_MCODE_LIGHT, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01); + } + + // Rolling Sounds + mRollingSoundMatrix = new LLUUID[LL_MCODE_END*LL_MCODE_END]; + if (mRollingSoundMatrix) + { + addRollingSound(LL_MCODE_STONE, LL_MCODE_STONE, SND_ROLL_STONE_STONE); + addRollingSound(LL_MCODE_STONE, LL_MCODE_METAL, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_STONE, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_STONE, LL_MCODE_WOOD, SND_ROLL_STONE_WOOD); + addRollingSound(LL_MCODE_STONE, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_STONE, LL_MCODE_PLASTIC, SND_ROLL_STONE_PLASTIC); + addRollingSound(LL_MCODE_STONE, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_STONE, LL_MCODE_LIGHT, SND_ROLL_STONE_PLASTIC); + + addRollingSound(LL_MCODE_METAL, LL_MCODE_METAL, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_METAL, LL_MCODE_GLASS, SND_ROLL_METAL_GLASS); + addRollingSound(LL_MCODE_METAL, LL_MCODE_WOOD, SND_ROLL_METAL_WOOD); + addRollingSound(LL_MCODE_METAL, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_METAL, LL_MCODE_PLASTIC, SND_ROLL_METAL_WOOD); + addRollingSound(LL_MCODE_METAL, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_METAL, LL_MCODE_LIGHT, SND_ROLL_METAL_WOOD); + + addRollingSound(LL_MCODE_GLASS, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_GLASS, LL_MCODE_WOOD, SND_ROLL_GLASS_WOOD); + addRollingSound(LL_MCODE_GLASS, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_GLASS, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_GLASS, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_GLASS, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01); + + addRollingSound(LL_MCODE_WOOD, LL_MCODE_WOOD, SND_ROLL_WOOD_WOOD); + addRollingSound(LL_MCODE_WOOD, LL_MCODE_FLESH, SND_ROLL_WOOD_FLESH); + addRollingSound(LL_MCODE_WOOD, LL_MCODE_PLASTIC, SND_ROLL_WOOD_PLASTIC); + addRollingSound(LL_MCODE_WOOD, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_WOOD, LL_MCODE_LIGHT, SND_ROLL_WOOD_PLASTIC); + + addRollingSound(LL_MCODE_FLESH, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_FLESH, LL_MCODE_PLASTIC, SND_ROLL_FLESH_PLASTIC); + addRollingSound(LL_MCODE_FLESH, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_FLESH, LL_MCODE_LIGHT, SND_ROLL_FLESH_PLASTIC); + + addRollingSound(LL_MCODE_RUBBER, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_RUBBER, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01); + addRollingSound(LL_MCODE_RUBBER, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01); + + addRollingSound(LL_MCODE_PLASTIC, LL_MCODE_PLASTIC, SND_ROLL_PLASTIC_PLASTIC); + addRollingSound(LL_MCODE_PLASTIC, LL_MCODE_LIGHT, SND_ROLL_PLASTIC_PLASTIC); + + addRollingSound(LL_MCODE_LIGHT, LL_MCODE_LIGHT, SND_ROLL_PLASTIC_PLASTIC); + } +} + +bool LLMaterialTable::add(U8 mcode, const std::string& name, const LLUUID &uuid) +{ + LLMaterialInfo *infop; + + infop = new LLMaterialInfo(mcode,name,uuid); + if (!infop) return false; + + // Add at the end so the order in menus matches the order in this + // file. JNC 11.30.01 + mMaterialInfoList.push_back(infop); + + return true; +} + +bool LLMaterialTable::addCollisionSound(U8 mcode, U8 mcode2, const LLUUID &uuid) +{ + if (mCollisionSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END)) + { + mCollisionSoundMatrix[mcode * LL_MCODE_END + mcode2] = uuid; + if (mcode != mcode2) + { + mCollisionSoundMatrix[mcode2 * LL_MCODE_END + mcode] = uuid; + } + } + return true; +} + +bool LLMaterialTable::addSlidingSound(U8 mcode, U8 mcode2, const LLUUID &uuid) +{ + if (mSlidingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END)) + { + mSlidingSoundMatrix[mcode * LL_MCODE_END + mcode2] = uuid; + if (mcode != mcode2) + { + mSlidingSoundMatrix[mcode2 * LL_MCODE_END + mcode] = uuid; + } + } + return true; +} + +bool LLMaterialTable::addRollingSound(U8 mcode, U8 mcode2, const LLUUID &uuid) +{ + if (mRollingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END)) + { + mRollingSoundMatrix[mcode * LL_MCODE_END + mcode2] = uuid; + if (mcode != mcode2) + { + mRollingSoundMatrix[mcode2 * LL_MCODE_END + mcode] = uuid; + } + } + return true; +} + +bool LLMaterialTable::addShatterSound(U8 mcode, const LLUUID &uuid) +{ + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + infop->mShatterSoundID = uuid; + return true; + } + } + + return false; +} + +bool LLMaterialTable::addDensity(U8 mcode, const F32 &density) +{ + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + infop->mDensity = density; + return true; + } + } + + return false; +} + +bool LLMaterialTable::addRestitution(U8 mcode, const F32 &restitution) +{ + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + infop->mRestitution = restitution; + return true; + } + } + + return false; +} + +bool LLMaterialTable::addFriction(U8 mcode, const F32 &friction) +{ + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + infop->mFriction = friction; + return true; + } + } + + return false; +} + +bool LLMaterialTable::addDamageAndEnergy(U8 mcode, const F32 &hp_mod, const F32 &damage_mod, const F32 &ep_mod) +{ + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + infop->mHPModifier = hp_mod; + infop->mDamageModifier = damage_mod; + infop->mEPModifier = ep_mod; + return true; + } + } + + return false; +} + +LLUUID LLMaterialTable::getDefaultTextureID(const std::string& name) +{ + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (name == infop->mName) + { + return infop->mDefaultTextureID; + } + } + + return LLUUID::null; +} + + +LLUUID LLMaterialTable::getDefaultTextureID(U8 mcode) +{ + mcode &= LL_MCODE_MASK; + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + return infop->mDefaultTextureID; + } + } + + return LLUUID::null; +} + + +U8 LLMaterialTable::getMCode(const std::string& name) +{ + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (name == infop->mName) + { + return infop->mMCode; + } + } + + return 0; +} + + +std::string LLMaterialTable::getName(U8 mcode) +{ + mcode &= LL_MCODE_MASK; + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + return infop->mName; + } + } + + return std::string(); +} + + +LLUUID LLMaterialTable::getCollisionSoundUUID(U8 mcode, U8 mcode2) +{ + mcode &= LL_MCODE_MASK; + mcode2 &= LL_MCODE_MASK; + + //LL_INFOS() << "code 1: " << ((U32) mcode) << " code 2:" << ((U32) mcode2) << LL_ENDL; + if (mCollisionSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END)) + { + return(mCollisionSoundMatrix[mcode * LL_MCODE_END + mcode2]); + } + else + { + //LL_INFOS() << "Null Sound" << LL_ENDL; + return(SND_NULL); + } +} + +bool LLMaterialTable::isCollisionSound(const LLUUID &uuid) +{ + for (U8 i = 0; i < LL_MCODE_END; i++) + { + for (U8 j = 0; j < LL_MCODE_END; j++) + { + i &= LL_MCODE_MASK; + j &= LL_MCODE_MASK; + if (mCollisionSoundMatrix[i * LL_MCODE_END + j] == uuid) + { + return true; + } + } + } + return false; +} + +LLUUID LLMaterialTable::getSlidingSoundUUID(U8 mcode, U8 mcode2) +{ + mcode &= LL_MCODE_MASK; + mcode2 &= LL_MCODE_MASK; + + if (mSlidingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END)) + { + return(mSlidingSoundMatrix[mcode * LL_MCODE_END + mcode2]); + } + else + { + return(SND_NULL); + } +} + +LLUUID LLMaterialTable::getRollingSoundUUID(U8 mcode, U8 mcode2) +{ + mcode &= LL_MCODE_MASK; + mcode2 &= LL_MCODE_MASK; + + if (mRollingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END)) + { + return(mRollingSoundMatrix[mcode * LL_MCODE_END + mcode2]); + } + else + { + return(SND_NULL); + } +} + +LLUUID LLMaterialTable::getGroundCollisionSoundUUID(U8 mcode) +{ + // Create material appropriate sounds for collisions with the ground + // For now, simply return a single sound for all materials + return SND_STONE_DIRT_02; +} + +LLUUID LLMaterialTable::getGroundSlidingSoundUUID(U8 mcode) +{ + // Create material-specific sound for sliding on ground + // For now, just return a single sound + return SND_SLIDE_STONE_STONE_01; +} + +LLUUID LLMaterialTable::getGroundRollingSoundUUID(U8 mcode) +{ + // Create material-specific sound for rolling on ground + // For now, just return a single sound + return SND_SLIDE_STONE_STONE_01; +} + +LLUUID LLMaterialTable::getCollisionParticleUUID(U8 mcode, U8 mcode2) +{ + // Returns an appropriate UUID to use as sprite at collision betweeen objects + // For now, just return a single image + return IMG_SHOT; +} + +LLUUID LLMaterialTable::getGroundCollisionParticleUUID(U8 mcode) +{ + // Returns an appropriate + // For now, just return a single sound + return IMG_SMOKE_POOF; +} + + +F32 LLMaterialTable::getDensity(U8 mcode) +{ + mcode &= LL_MCODE_MASK; + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + return infop->mDensity; + } + } + + return 0.f; +} + +F32 LLMaterialTable::getRestitution(U8 mcode) +{ + mcode &= LL_MCODE_MASK; + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + return infop->mRestitution; + } + } + + return LLMaterialTable::DEFAULT_RESTITUTION; +} + +F32 LLMaterialTable::getFriction(U8 mcode) +{ + mcode &= LL_MCODE_MASK; + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + return infop->mFriction; + } + } + + return LLMaterialTable::DEFAULT_FRICTION; +} + +F32 LLMaterialTable::getHPMod(U8 mcode) +{ + mcode &= LL_MCODE_MASK; + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + return infop->mHPModifier; + } + } + + return 1.f; +} + +F32 LLMaterialTable::getDamageMod(U8 mcode) +{ + mcode &= LL_MCODE_MASK; + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + return infop->mDamageModifier; + } + } + + return 1.f; +} + +F32 LLMaterialTable::getEPMod(U8 mcode) +{ + mcode &= LL_MCODE_MASK; + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + return infop->mEPModifier; + } + } + + return 1.f; +} + +LLUUID LLMaterialTable::getShatterSoundUUID(U8 mcode) +{ + mcode &= LL_MCODE_MASK; + for (info_list_t::iterator iter = mMaterialInfoList.begin(); + iter != mMaterialInfoList.end(); ++iter) + { + LLMaterialInfo *infop = *iter; + if (mcode == infop->mMCode) + { + return infop->mShatterSoundID; + } + } + + return SND_NULL; +} diff --git a/indra/llprimitive/llmaterialtable.h b/indra/llprimitive/llmaterialtable.h index 915001e5cf..7d1d5c7d34 100644 --- a/indra/llprimitive/llmaterialtable.h +++ b/indra/llprimitive/llmaterialtable.h @@ -1,190 +1,190 @@ -/**
- * @file llmaterialtable.h
- * @brief Table of material information for the viewer UI
- *
- * $LicenseInfo:firstyear=2001&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_LLMATERIALTABLE_H
-#define LL_LLMATERIALTABLE_H
-
-#include "lluuid.h"
-#include "llstring.h"
-
-#include <list>
-
-class LLMaterialInfo;
-
-const U32 LLMATERIAL_INFO_NAME_LENGTH = 256;
-
-// We've moved toward more reasonable mass values for the Havok4 engine.
-// The LEGACY_DEFAULT_OBJECT_DENSITY is used to maintain support for
-// legacy scripts code (llGetMass()) and script energy consumption.
-const F32 DEFAULT_OBJECT_DENSITY = 1000.0f; // per m^3
-const F32 LEGACY_DEFAULT_OBJECT_DENSITY = 10.0f;
-
-// Avatars density depends on the collision shape used. The approximate
-// legacy volumes of avatars are:
-// Body_Length Body_Width Body_Fat Leg_Length Volume(m^3)
-// -------------------------------------------------------
-// min | min | min | min | 0.123 |
-// max | max | max | max | 0.208 |
-//
-// Either the avatar shape must be tweaked to match those volumes
-// or the DEFAULT_AVATAR_DENSITY must be adjusted to achieve the
-// legacy mass.
-//
-// The current density appears to be low because the mass and
-// inertia are computed as if the avatar were a cylinder which
-// has more volume than the actual collision shape of the avatar.
-// See the physics engine mass properties code for more info.
-const F32 DEFAULT_AVATAR_DENSITY = 445.3f; // was 444.24f;
-
-
-class LLMaterialTable
-{
-public:
- static const F32 FRICTION_MIN;
- static const F32 FRICTION_GLASS;
- static const F32 FRICTION_LIGHT;
- static const F32 FRICTION_METAL;
- static const F32 FRICTION_PLASTIC;
- static const F32 FRICTION_WOOD;
- static const F32 FRICTION_LAND;
- static const F32 FRICTION_STONE;
- static const F32 FRICTION_FLESH;
- static const F32 FRICTION_RUBBER;
- static const F32 FRICTION_MAX;
-
- static const F32 RESTITUTION_MIN;
- static const F32 RESTITUTION_LAND;
- static const F32 RESTITUTION_FLESH;
- static const F32 RESTITUTION_STONE;
- static const F32 RESTITUTION_METAL;
- static const F32 RESTITUTION_WOOD;
- static const F32 RESTITUTION_GLASS;
- static const F32 RESTITUTION_PLASTIC;
- static const F32 RESTITUTION_LIGHT;
- static const F32 RESTITUTION_RUBBER;
- static const F32 RESTITUTION_MAX;
-
- typedef std::list<LLMaterialInfo*> info_list_t;
- info_list_t mMaterialInfoList;
-
- LLUUID *mCollisionSoundMatrix;
- LLUUID *mSlidingSoundMatrix;
- LLUUID *mRollingSoundMatrix;
-
- static const F32 DEFAULT_FRICTION;
- static const F32 DEFAULT_RESTITUTION;
-
-public:
- LLMaterialTable();
- LLMaterialTable(U8); // cheat with an overloaded constructor to build our basic table
- ~LLMaterialTable();
-
- void initBasicTable();
-
- void initTableTransNames(std::map<std::string, std::string> namemap);
-
- bool add(U8 mcode, const std::string& name, const LLUUID &uuid);
- bool addCollisionSound(U8 mcode, U8 mcode2, const LLUUID &uuid);
- bool addSlidingSound(U8 mcode, U8 mcode2, const LLUUID &uuid);
- bool addRollingSound(U8 mcode, U8 mcode2, const LLUUID &uuid);
- bool addShatterSound(U8 mcode, const LLUUID &uuid);
- bool addDensity(U8 mcode, const F32 &density);
- bool addFriction(U8 mcode, const F32 &friction);
- bool addRestitution(U8 mcode, const F32 &restitution);
- bool addDamageAndEnergy(U8 mcode, const F32 &hp_mod, const F32 &damage_mod, const F32 &ep_mod);
-
- LLUUID getDefaultTextureID(const std::string& name); // LLUUID::null if not found
- LLUUID getDefaultTextureID(U8 mcode); // LLUUID::null if not found
- U8 getMCode(const std::string& name); // 0 if not found
- std::string getName(U8 mcode);
-
- F32 getDensity(U8 mcode); // kg/m^3, 0 if not found
- F32 getFriction(U8 mcode); // physics values
- F32 getRestitution(U8 mcode); // physics values
- F32 getHPMod(U8 mcode);
- F32 getDamageMod(U8 mcode);
- F32 getEPMod(U8 mcode);
-
- bool isCollisionSound(const LLUUID &uuid);
-
- LLUUID getCollisionSoundUUID(U8 mcode, U8 mcode2);
- LLUUID getSlidingSoundUUID(U8 mcode, U8 mcode2);
- LLUUID getRollingSoundUUID(U8 mcode, U8 mcode2);
- LLUUID getShatterSoundUUID(U8 mcode); // LLUUID::null if not found
-
- LLUUID getGroundCollisionSoundUUID(U8 mcode);
- LLUUID getGroundSlidingSoundUUID(U8 mcode);
- LLUUID getGroundRollingSoundUUID(U8 mcode);
- LLUUID getCollisionParticleUUID(U8 mcode, U8 mcode2);
- LLUUID getGroundCollisionParticleUUID(U8 mcode);
-
- static LLMaterialTable basic;
-};
-
-
-class LLMaterialInfo
-{
-public:
- U8 mMCode;
- std::string mName;
- LLUUID mDefaultTextureID;
- LLUUID mShatterSoundID;
- F32 mDensity; // kg/m^3
- F32 mFriction;
- F32 mRestitution;
-
- // damage and energy constants
- F32 mHPModifier; // modifier on mass based HP total
- F32 mDamageModifier; // modifier on KE based damage
- F32 mEPModifier; // modifier on mass based EP total
-
- LLMaterialInfo(U8 mcode, const std::string& name, const LLUUID &uuid)
- {
- init(mcode,name,uuid);
- };
-
- void init(U8 mcode, const std::string& name, const LLUUID &uuid)
- {
- mDensity = 1000.f; // default to 1000.0 (water)
- mFriction = LLMaterialTable::DEFAULT_FRICTION;
- mRestitution = LLMaterialTable::DEFAULT_RESTITUTION;
- mHPModifier = 1.f;
- mDamageModifier = 1.f;
- mEPModifier = 1.f;
-
- mMCode = mcode;
- mName = name;
- mDefaultTextureID = uuid;
- };
-
- ~LLMaterialInfo()
- {
- };
-
-};
-
-#endif
-
+/** + * @file llmaterialtable.h + * @brief Table of material information for the viewer UI + * + * $LicenseInfo:firstyear=2001&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_LLMATERIALTABLE_H +#define LL_LLMATERIALTABLE_H + +#include "lluuid.h" +#include "llstring.h" + +#include <list> + +class LLMaterialInfo; + +const U32 LLMATERIAL_INFO_NAME_LENGTH = 256; + +// We've moved toward more reasonable mass values for the Havok4 engine. +// The LEGACY_DEFAULT_OBJECT_DENSITY is used to maintain support for +// legacy scripts code (llGetMass()) and script energy consumption. +const F32 DEFAULT_OBJECT_DENSITY = 1000.0f; // per m^3 +const F32 LEGACY_DEFAULT_OBJECT_DENSITY = 10.0f; + +// Avatars density depends on the collision shape used. The approximate +// legacy volumes of avatars are: +// Body_Length Body_Width Body_Fat Leg_Length Volume(m^3) +// ------------------------------------------------------- +// min | min | min | min | 0.123 | +// max | max | max | max | 0.208 | +// +// Either the avatar shape must be tweaked to match those volumes +// or the DEFAULT_AVATAR_DENSITY must be adjusted to achieve the +// legacy mass. +// +// The current density appears to be low because the mass and +// inertia are computed as if the avatar were a cylinder which +// has more volume than the actual collision shape of the avatar. +// See the physics engine mass properties code for more info. +const F32 DEFAULT_AVATAR_DENSITY = 445.3f; // was 444.24f; + + +class LLMaterialTable +{ +public: + static const F32 FRICTION_MIN; + static const F32 FRICTION_GLASS; + static const F32 FRICTION_LIGHT; + static const F32 FRICTION_METAL; + static const F32 FRICTION_PLASTIC; + static const F32 FRICTION_WOOD; + static const F32 FRICTION_LAND; + static const F32 FRICTION_STONE; + static const F32 FRICTION_FLESH; + static const F32 FRICTION_RUBBER; + static const F32 FRICTION_MAX; + + static const F32 RESTITUTION_MIN; + static const F32 RESTITUTION_LAND; + static const F32 RESTITUTION_FLESH; + static const F32 RESTITUTION_STONE; + static const F32 RESTITUTION_METAL; + static const F32 RESTITUTION_WOOD; + static const F32 RESTITUTION_GLASS; + static const F32 RESTITUTION_PLASTIC; + static const F32 RESTITUTION_LIGHT; + static const F32 RESTITUTION_RUBBER; + static const F32 RESTITUTION_MAX; + + typedef std::list<LLMaterialInfo*> info_list_t; + info_list_t mMaterialInfoList; + + LLUUID *mCollisionSoundMatrix; + LLUUID *mSlidingSoundMatrix; + LLUUID *mRollingSoundMatrix; + + static const F32 DEFAULT_FRICTION; + static const F32 DEFAULT_RESTITUTION; + +public: + LLMaterialTable(); + LLMaterialTable(U8); // cheat with an overloaded constructor to build our basic table + ~LLMaterialTable(); + + void initBasicTable(); + + void initTableTransNames(std::map<std::string, std::string> namemap); + + bool add(U8 mcode, const std::string& name, const LLUUID &uuid); + bool addCollisionSound(U8 mcode, U8 mcode2, const LLUUID &uuid); + bool addSlidingSound(U8 mcode, U8 mcode2, const LLUUID &uuid); + bool addRollingSound(U8 mcode, U8 mcode2, const LLUUID &uuid); + bool addShatterSound(U8 mcode, const LLUUID &uuid); + bool addDensity(U8 mcode, const F32 &density); + bool addFriction(U8 mcode, const F32 &friction); + bool addRestitution(U8 mcode, const F32 &restitution); + bool addDamageAndEnergy(U8 mcode, const F32 &hp_mod, const F32 &damage_mod, const F32 &ep_mod); + + LLUUID getDefaultTextureID(const std::string& name); // LLUUID::null if not found + LLUUID getDefaultTextureID(U8 mcode); // LLUUID::null if not found + U8 getMCode(const std::string& name); // 0 if not found + std::string getName(U8 mcode); + + F32 getDensity(U8 mcode); // kg/m^3, 0 if not found + F32 getFriction(U8 mcode); // physics values + F32 getRestitution(U8 mcode); // physics values + F32 getHPMod(U8 mcode); + F32 getDamageMod(U8 mcode); + F32 getEPMod(U8 mcode); + + bool isCollisionSound(const LLUUID &uuid); + + LLUUID getCollisionSoundUUID(U8 mcode, U8 mcode2); + LLUUID getSlidingSoundUUID(U8 mcode, U8 mcode2); + LLUUID getRollingSoundUUID(U8 mcode, U8 mcode2); + LLUUID getShatterSoundUUID(U8 mcode); // LLUUID::null if not found + + LLUUID getGroundCollisionSoundUUID(U8 mcode); + LLUUID getGroundSlidingSoundUUID(U8 mcode); + LLUUID getGroundRollingSoundUUID(U8 mcode); + LLUUID getCollisionParticleUUID(U8 mcode, U8 mcode2); + LLUUID getGroundCollisionParticleUUID(U8 mcode); + + static LLMaterialTable basic; +}; + + +class LLMaterialInfo +{ +public: + U8 mMCode; + std::string mName; + LLUUID mDefaultTextureID; + LLUUID mShatterSoundID; + F32 mDensity; // kg/m^3 + F32 mFriction; + F32 mRestitution; + + // damage and energy constants + F32 mHPModifier; // modifier on mass based HP total + F32 mDamageModifier; // modifier on KE based damage + F32 mEPModifier; // modifier on mass based EP total + + LLMaterialInfo(U8 mcode, const std::string& name, const LLUUID &uuid) + { + init(mcode,name,uuid); + }; + + void init(U8 mcode, const std::string& name, const LLUUID &uuid) + { + mDensity = 1000.f; // default to 1000.0 (water) + mFriction = LLMaterialTable::DEFAULT_FRICTION; + mRestitution = LLMaterialTable::DEFAULT_RESTITUTION; + mHPModifier = 1.f; + mDamageModifier = 1.f; + mEPModifier = 1.f; + + mMCode = mcode; + mName = name; + mDefaultTextureID = uuid; + }; + + ~LLMaterialInfo() + { + }; + +}; + +#endif + diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 0710e3186b..37917c0c04 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -1,2176 +1,2176 @@ -/**
- * @file llmodel.cpp
- * @brief Model handling implementation
- *
- * $LicenseInfo:firstyear=2001&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 "linden_common.h"
-
-#include "llmodel.h"
-#include "llmemory.h"
-#include "llconvexdecomposition.h"
-#include "llsdserialize.h"
-#include "llvector4a.h"
-#include "hbxxh.h"
-
-#ifdef LL_USESYSTEMLIBS
-# include <zlib.h>
-#else
-# include "zlib-ng/zlib.h"
-#endif
-
-std::string model_names[] =
-{
- "lowest_lod",
- "low_lod",
- "medium_lod",
- "high_lod",
- "physics_mesh"
-};
-
-const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string);
-
-LLModel::LLModel(const LLVolumeParams& params, F32 detail)
- : LLVolume(params, detail),
- mNormalizedScale(1,1,1),
- mNormalizedTranslation(0, 0, 0),
- mPelvisOffset( 0.0f ),
- mStatus(NO_ERRORS),
- mSubmodelID(0)
-{
- mDecompID = -1;
- mLocalID = -1;
-}
-
-LLModel::~LLModel()
-{
- if (mDecompID >= 0)
- {
- LLConvexDecomposition::getInstance()->deleteDecomposition(mDecompID);
- }
- mPhysics.mMesh.clear();
-}
-
-//static
-std::string LLModel::getStatusString(U32 status)
-{
- const static std::string status_strings[(S32)INVALID_STATUS] = {"status_no_error", "status_vertex_number_overflow","bad_element"};
-
- if(status < INVALID_STATUS)
- {
- if(status_strings[status] == std::string())
- {
- //LL_ERRS() << "No valid status string for this status: " << (U32)status << LL_ENDL();
- }
- return status_strings[status] ;
- }
-
- //LL_ERRS() << "Invalid model status: " << (U32)status << LL_ENDL();
-
- return std::string() ;
-}
-
-
-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(); )
- {
- std::vector<LLVolumeFace>:: iterator currentFaceIt = faceIt++;
- LLVolumeFace& face = *currentFaceIt;
- LLVector4a *pos = (LLVector4a*) face.mPositions;
-
- for (U32 i=0; i<face.mNumVertices; ++i )
- {
- pos[i].add( pivot );
- }
- }
-}
-
-void LLModel::remapVolumeFaces()
-{
- for (U32 i = 0; i < getNumVolumeFaces(); ++i)
- {
- mVolumeFaces[i].remap();
- }
-}
-
-void LLModel::optimizeVolumeFaces()
-{
- for (U32 i = 0; i < getNumVolumeFaces(); ++i)
- {
- mVolumeFaces[i].optimize();
- }
-}
-
-struct MaterialBinding
-{
- int index;
- std::string matName;
-};
-
-struct MaterialSort
-{
- bool operator()(const MaterialBinding& lhs, const MaterialBinding& rhs)
- {
- return LLStringUtil::compareInsensitive(lhs.matName, rhs.matName) < 0;
- }
-};
-
-void LLModel::sortVolumeFacesByMaterialName()
-{
- std::vector<MaterialBinding> bindings;
- bindings.resize(mVolumeFaces.size());
-
- for (int i = 0; i < bindings.size(); i++)
- {
- bindings[i].index = i;
- if(i < mMaterialList.size())
- {
- bindings[i].matName = mMaterialList[i];
- }
- }
- std::sort(bindings.begin(), bindings.end(), MaterialSort());
- std::vector< LLVolumeFace > new_faces;
-
- // 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++)
- {
- new_faces[i] = mVolumeFaces[bindings[i].index];
- if(i < mMaterialList.size())
- {
- mMaterialList[i] = bindings[i].matName;
- }
- }
-
- mVolumeFaces = new_faces;
-}
-
-void LLModel::trimVolumeFacesToSize(U32 new_count, LLVolume::face_list_t* remainder)
-{
- llassert(new_count <= LL_SCULPT_MESH_MAX_FACES);
-
- if (new_count && (getNumVolumeFaces() > new_count))
- {
- // Copy out remaining volume faces for alternative handling, if provided
- //
- if (remainder)
- {
- (*remainder).assign(mVolumeFaces.begin() + new_count, mVolumeFaces.end());
- }
-
- // Trim down to the final set of volume faces (now stuffed to the gills!)
- //
- mVolumeFaces.resize(new_count);
- }
-}
-
-// Shrink the model to fit
-// on a 1x1x1 cube centered at the origin.
-// The positions and extents
-// multiplied by mNormalizedScale
-// and offset by mNormalizedTranslation
-// to be the "original" extents and position.
-// Also, the positions will fit
-// within the unit cube.
-void LLModel::normalizeVolumeFaces()
-{
- if (!mVolumeFaces.empty())
- {
- LLVector4a min, max;
-
- // For all of the volume faces
- // in the model, loop over
- // them and see what the extents
- // of the volume along each axis.
- min = mVolumeFaces[0].mExtents[0];
- max = mVolumeFaces[0].mExtents[1];
-
- for (U32 i = 1; i < mVolumeFaces.size(); ++i)
- {
- LLVolumeFace& face = mVolumeFaces[i];
-
- update_min_max(min, max, face.mExtents[0]);
- update_min_max(min, max, face.mExtents[1]);
-
- if (face.mTexCoords)
- {
- LLVector2& min_tc = face.mTexCoordExtents[0];
- LLVector2& max_tc = face.mTexCoordExtents[1];
-
- min_tc = face.mTexCoords[0];
- max_tc = face.mTexCoords[0];
-
- for (U32 j = 1; j < face.mNumVertices; ++j)
- {
- update_min_max(min_tc, max_tc, face.mTexCoords[j]);
- }
- }
- else
- {
- face.mTexCoordExtents[0].set(0,0);
- face.mTexCoordExtents[1].set(1,1);
- }
- }
-
- // Now that we have the extents of the model
- // we can compute the offset needed to center
- // the model at the origin.
-
- // Compute center of the model
- // and make it negative to get translation
- // needed to center at origin.
- LLVector4a trans;
- trans.setAdd(min, max);
- trans.mul(-0.5f);
-
- // Compute the total size along all
- // axes of the model.
- LLVector4a size;
- size.setSub(max, min);
-
- // Prevent division by zero.
- F32 x = size[0];
- F32 y = size[1];
- F32 z = size[2];
- F32 w = size[3];
- if (fabs(x)<F_APPROXIMATELY_ZERO)
- {
- x = 1.0;
- }
- if (fabs(y)<F_APPROXIMATELY_ZERO)
- {
- y = 1.0;
- }
- if (fabs(z)<F_APPROXIMATELY_ZERO)
- {
- z = 1.0;
- }
- size.set(x,y,z,w);
-
- // Compute scale as reciprocal of size
- LLVector4a scale;
- scale.splat(1.f);
- scale.div(size);
-
- LLVector4a inv_scale(1.f);
- inv_scale.div(scale);
-
- for (U32 i = 0; i < mVolumeFaces.size(); ++i)
- {
- LLVolumeFace& face = mVolumeFaces[i];
-
- // We shrink the extents so
- // that they fall within
- // the unit cube.
- // VFExtents change
- face.mExtents[0].add(trans);
- face.mExtents[0].mul(scale);
-
- face.mExtents[1].add(trans);
- face.mExtents[1].mul(scale);
-
- // For all the positions, we scale
- // the positions to fit within the unit cube.
- LLVector4a* pos = (LLVector4a*) face.mPositions;
- LLVector4a* norm = (LLVector4a*) face.mNormals;
- LLVector4a* t = (LLVector4a*)face.mTangents;
-
- for (U32 j = 0; j < face.mNumVertices; ++j)
- {
- pos[j].add(trans);
- pos[j].mul(scale);
- if (norm && !norm[j].equals3(LLVector4a::getZero()))
- {
- norm[j].mul(inv_scale);
- norm[j].normalize3();
- }
-
- if (t)
- {
- F32 w = t[j].getF32ptr()[3];
- t[j].mul(inv_scale);
- t[j].normalize3();
- t[j].getF32ptr()[3] = w;
- }
- }
- }
-
- // mNormalizedScale is the scale at which
- // we would need to multiply the model
- // by to get the original size of the
- // model instead of the normalized size.
- LLVector4a normalized_scale;
- normalized_scale.splat(1.f);
- normalized_scale.div(scale);
- mNormalizedScale.set(normalized_scale.getF32ptr());
- mNormalizedTranslation.set(trans.getF32ptr());
- mNormalizedTranslation *= -1.f;
-
- // remember normalized scale so original dimensions can be recovered for mesh processing (i.e. tangent generation)
- for (auto& face : mVolumeFaces)
- {
- face.mNormalizedScale = mNormalizedScale;
- }
- }
-}
-
-void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out)
-{
- scale_out = mNormalizedScale;
- translation_out = mNormalizedTranslation;
-}
-
-LLVector3 LLModel::getTransformedCenter(const LLMatrix4& mat)
-{
- LLVector3 ret;
-
- if (!mVolumeFaces.empty())
- {
- LLMatrix4a m;
- m.loadu(mat);
-
- LLVector4a minv,maxv;
-
- LLVector4a t;
- m.affineTransform(mVolumeFaces[0].mPositions[0], t);
- minv = maxv = t;
-
- for (S32 i = 0; i < mVolumeFaces.size(); ++i)
- {
- LLVolumeFace& face = mVolumeFaces[i];
-
- for (U32 j = 0; j < face.mNumVertices; ++j)
- {
- m.affineTransform(face.mPositions[j],t);
- update_min_max(minv, maxv, t);
- }
- }
-
- minv.add(maxv);
- minv.mul(0.5f);
-
- ret.set(minv.getF32ptr());
- }
-
- return ret;
-}
-
-
-
-void LLModel::setNumVolumeFaces(S32 count)
-{
- mVolumeFaces.resize(count);
-}
-
-void LLModel::setVolumeFaceData(
- S32 f,
- LLStrider<LLVector3> pos,
- LLStrider<LLVector3> norm,
- LLStrider<LLVector2> tc,
- LLStrider<U16> ind,
- U32 num_verts,
- U32 num_indices)
-{
- llassert(num_indices % 3 == 0);
-
- LLVolumeFace& face = mVolumeFaces[f];
-
- face.resizeVertices(num_verts);
- face.resizeIndices(num_indices);
-
- LLVector4a::memcpyNonAliased16((F32*) face.mPositions, (F32*) pos.get(), num_verts*4*sizeof(F32));
- if (norm.get())
- {
- LLVector4a::memcpyNonAliased16((F32*) face.mNormals, (F32*) norm.get(), num_verts*4*sizeof(F32));
- }
- else
- {
- //ll_aligned_free_16(face.mNormals);
- face.mNormals = NULL;
- }
-
- if (tc.get())
- {
- U32 tex_size = (num_verts*2*sizeof(F32)+0xF)&~0xF;
- LLVector4a::memcpyNonAliased16((F32*) face.mTexCoords, (F32*) tc.get(), tex_size);
- }
- else
- {
- //ll_aligned_free_16(face.mTexCoords);
- face.mTexCoords = NULL;
- }
-
- U32 size = (num_indices*2+0xF)&~0xF;
- LLVector4a::memcpyNonAliased16((F32*) face.mIndices, (F32*) ind.get(), size);
-}
-
-void LLModel::addFace(const LLVolumeFace& face)
-{
- if (face.mNumVertices == 0)
- {
- LL_ERRS() << "Cannot add empty face." << LL_ENDL;
- }
-
- mVolumeFaces.push_back(face);
-
- if (mVolumeFaces.size() > MAX_MODEL_FACES)
- {
- LL_ERRS() << "Model prims cannot have more than " << MAX_MODEL_FACES << " faces!" << LL_ENDL;
- }
-}
-
-
-void LLModel::generateNormals(F32 angle_cutoff)
-{
- //generate normals for all faces by:
- // 1 - Create faceted copy of face with no texture coordinates
- // 2 - Weld vertices in faceted copy that are shared between triangles with less than "angle_cutoff" difference between normals
- // 3 - Generate smoothed set of normals based on welding results
- // 4 - Create faceted copy of face with texture coordinates
- // 5 - Copy smoothed normals to faceted copy, using closest normal to triangle normal where more than one normal exists for a given position
- // 6 - Remove redundant vertices from new faceted (now smooth) copy
-
- angle_cutoff = cosf(angle_cutoff);
- for (U32 j = 0; j < mVolumeFaces.size(); ++j)
- {
- LLVolumeFace& vol_face = mVolumeFaces[j];
-
- if (vol_face.mNumIndices > 65535)
- {
- LL_WARNS("MESHSKININFO") << "Too many vertices for normal generation to work." << LL_ENDL;
- continue;
- }
-
- //create faceted copy of current face with no texture coordinates (step 1)
- LLVolumeFace faceted;
-
- LLVector4a* src_pos = (LLVector4a*) vol_face.mPositions;
- //LLVector4a* src_norm = (LLVector4a*) vol_face.mNormals;
-
-
- faceted.resizeVertices(vol_face.mNumIndices);
- faceted.resizeIndices(vol_face.mNumIndices);
- //bake out triangles into temporary face, clearing texture coordinates
- for (U32 i = 0; i < vol_face.mNumIndices; ++i)
- {
- U32 idx = vol_face.mIndices[i];
-
- faceted.mPositions[i] = src_pos[idx];
- faceted.mTexCoords[i] = LLVector2(0,0);
- faceted.mIndices[i] = i;
- }
-
- //generate normals for temporary face
- for (U32 i = 0; i < faceted.mNumIndices; i += 3)
- { //for each triangle
- U16 i0 = faceted.mIndices[i+0];
- U16 i1 = faceted.mIndices[i+1];
- U16 i2 = faceted.mIndices[i+2];
-
- LLVector4a& p0 = faceted.mPositions[i0];
- LLVector4a& p1 = faceted.mPositions[i1];
- LLVector4a& p2 = faceted.mPositions[i2];
-
- LLVector4a& n0 = faceted.mNormals[i0];
- LLVector4a& n1 = faceted.mNormals[i1];
- LLVector4a& n2 = faceted.mNormals[i2];
-
- LLVector4a lhs, rhs;
- lhs.setSub(p1, p0);
- rhs.setSub(p2, p0);
-
- n0.setCross3(lhs, rhs);
- n0.normalize3();
- n1 = n0;
- n2 = n0;
- }
-
- //weld vertices in temporary face, respecting angle_cutoff (step 2)
- faceted.optimize(angle_cutoff);
-
- //generate normals for welded face based on new topology (step 3)
-
- for (U32 i = 0; i < faceted.mNumVertices; i++)
- {
- faceted.mNormals[i].clear();
- }
-
- for (U32 i = 0; i < faceted.mNumIndices; i += 3)
- { //for each triangle
- U16 i0 = faceted.mIndices[i+0];
- U16 i1 = faceted.mIndices[i+1];
- U16 i2 = faceted.mIndices[i+2];
-
- LLVector4a& p0 = faceted.mPositions[i0];
- LLVector4a& p1 = faceted.mPositions[i1];
- LLVector4a& p2 = faceted.mPositions[i2];
-
- LLVector4a& n0 = faceted.mNormals[i0];
- LLVector4a& n1 = faceted.mNormals[i1];
- LLVector4a& n2 = faceted.mNormals[i2];
-
- LLVector4a lhs, rhs;
- lhs.setSub(p1, p0);
- rhs.setSub(p2, p0);
-
- LLVector4a n;
- n.setCross3(lhs, rhs);
-
- n0.add(n);
- n1.add(n);
- n2.add(n);
- }
-
- //normalize normals and build point map
- LLVolumeFace::VertexMapData::PointMap point_map;
-
- for (U32 i = 0; i < faceted.mNumVertices; ++i)
- {
- faceted.mNormals[i].normalize3();
-
- LLVolumeFace::VertexMapData v;
- v.setPosition(faceted.mPositions[i]);
- v.setNormal(faceted.mNormals[i]);
-
- point_map[LLVector3(v.getPosition().getF32ptr())].push_back(v);
- }
-
- //create faceted copy of current face with texture coordinates (step 4)
- LLVolumeFace new_face;
-
- //bake out triangles into new face
- new_face.resizeIndices(vol_face.mNumIndices);
- new_face.resizeVertices(vol_face.mNumIndices);
-
- for (U32 i = 0; i < vol_face.mNumIndices; ++i)
- {
- U32 idx = vol_face.mIndices[i];
- LLVolumeFace::VertexData v;
- new_face.mPositions[i] = vol_face.mPositions[idx];
- new_face.mNormals[i].clear();
- new_face.mIndices[i] = i;
- }
-
- if (vol_face.mTexCoords)
- {
- for (U32 i = 0; i < vol_face.mNumIndices; i++)
- {
- U32 idx = vol_face.mIndices[i];
- new_face.mTexCoords[i] = vol_face.mTexCoords[idx];
- }
- }
- else
- {
- //ll_aligned_free_16(new_face.mTexCoords);
- new_face.mTexCoords = NULL;
- }
-
- //generate normals for new face
- for (U32 i = 0; i < new_face.mNumIndices; i += 3)
- { //for each triangle
- U16 i0 = new_face.mIndices[i+0];
- U16 i1 = new_face.mIndices[i+1];
- U16 i2 = new_face.mIndices[i+2];
-
- LLVector4a& p0 = new_face.mPositions[i0];
- LLVector4a& p1 = new_face.mPositions[i1];
- LLVector4a& p2 = new_face.mPositions[i2];
-
- LLVector4a& n0 = new_face.mNormals[i0];
- LLVector4a& n1 = new_face.mNormals[i1];
- LLVector4a& n2 = new_face.mNormals[i2];
-
- LLVector4a lhs, rhs;
- lhs.setSub(p1, p0);
- rhs.setSub(p2, p0);
-
- n0.setCross3(lhs, rhs);
- n0.normalize3();
- n1 = n0;
- n2 = n0;
- }
-
- //swap out normals in new_face with best match from point map (step 5)
- for (U32 i = 0; i < new_face.mNumVertices; ++i)
- {
- //LLVolumeFace::VertexData v = new_face.mVertices[i];
-
- LLVector4a ref_norm = new_face.mNormals[i];
-
- LLVolumeFace::VertexMapData::PointMap::iterator iter = point_map.find(LLVector3(new_face.mPositions[i].getF32ptr()));
-
- if (iter != point_map.end())
- {
- F32 best = -2.f;
- for (U32 k = 0; k < iter->second.size(); ++k)
- {
- LLVector4a& n = iter->second[k].getNormal();
-
- F32 cur = n.dot3(ref_norm).getF32();
-
- if (cur > best)
- {
- best = cur;
- new_face.mNormals[i] = n;
- }
- }
- }
- }
-
- //remove redundant vertices from new face (step 6)
- new_face.optimize();
-
- mVolumeFaces[j] = new_face;
- }
-}
-
-
-std::string LLModel::getName() const
-{
- return mRequestedLabel.empty() ? mLabel : mRequestedLabel;
-}
-
-//static
-LLSD LLModel::writeModel(
- std::ostream& ostr,
- LLModel* physics,
- LLModel* high,
- LLModel* medium,
- LLModel* low,
- LLModel* impostor,
- const LLModel::Decomposition& decomp,
- bool upload_skin,
- bool upload_joints,
- bool lock_scale_if_joint_position,
- bool nowrite,
- bool as_slm,
- int submodel_id)
-{
- LLSD mdl;
-
- LLModel* model[] =
- {
- impostor,
- low,
- medium,
- high,
- physics
- };
-
- bool skinning = upload_skin && high && !high->mSkinWeights.empty();
-
- if (skinning)
- { //write skinning block
- mdl["skin"] = high->mSkinInfo.asLLSD(upload_joints, lock_scale_if_joint_position);
- }
-
- if (!decomp.mBaseHull.empty() ||
- !decomp.mHull.empty())
- {
- mdl["physics_convex"] = decomp.asLLSD();
- if (!decomp.mHull.empty() && !as_slm)
- { //convex decomposition exists, physics mesh will not be used (unless this is an slm file)
- 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
- for (U32 i = 0; i < high->mMaterialList.size(); ++i)
- {
- mdl["material_list"][i] = high->mMaterialList[i];
- }
- }
-
- for (U32 idx = 0; idx < MODEL_NAMES_LENGTH; ++idx)
- {
- 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;
-
- //find position domain
- for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i)
- { //for each face
- const LLVolumeFace& face = model[idx]->getVolumeFace(i);
- for (U32 j = 0; j < face.mNumVertices; ++j)
- {
- update_min_max(min_pos, max_pos, face.mPositions[j].getF32ptr());
- }
- }
-
- LLVector3 pos_range = max_pos - min_pos;
-
- for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i)
- { //for each face
- const LLVolumeFace& face = model[idx]->getVolumeFace(i);
- if (face.mNumVertices < 3)
- { //don't export an empty face
- mdl[model_names[idx]][i]["NoGeometry"] = true;
- continue;
- }
- LLSD::Binary verts(face.mNumVertices*3*2);
- LLSD::Binary tc(face.mNumVertices*2*2);
- LLSD::Binary normals(face.mNumVertices*3*2);
- LLSD::Binary tangents(face.mNumVertices * 4 * 2);
- LLSD::Binary indices(face.mNumIndices*2);
-
- U32 vert_idx = 0;
- U32 norm_idx = 0;
- //U32 tan_idx = 0;
- U32 tc_idx = 0;
-
- LLVector2* ftc = (LLVector2*) face.mTexCoords;
- LLVector2 min_tc;
- LLVector2 max_tc;
-
- if (ftc)
- {
- min_tc = ftc[0];
- max_tc = min_tc;
-
- //get texture coordinate domain
- for (U32 j = 0; j < face.mNumVertices; ++j)
- {
- update_min_max(min_tc, max_tc, ftc[j]);
- }
- }
-
- LLVector2 tc_range = max_tc - min_tc;
-
- for (U32 j = 0; j < face.mNumVertices; ++j)
- { //for each vert
-
- F32* pos = face.mPositions[j].getF32ptr();
-
- //position
- for (U32 k = 0; k < 3; ++k)
- { //for each component
- //convert to 16-bit normalized across domain
- U16 val = (U16) (((pos[k]-min_pos.mV[k])/pos_range.mV[k])*65535);
-
- U8* buff = (U8*) &val;
- //write to binary buffer
- verts[vert_idx++] = buff[0];
- verts[vert_idx++] = buff[1];
- }
-
- if (face.mNormals)
- { //normals
- F32* norm = face.mNormals[j].getF32ptr();
-
- for (U32 k = 0; k < 3; ++k)
- { //for each component
- //convert to 16-bit normalized
- U16 val = (U16) ((norm[k]+1.f)*0.5f*65535);
- U8* buff = (U8*) &val;
-
- //write to binary buffer
- normals[norm_idx++] = buff[0];
- normals[norm_idx++] = buff[1];
- }
- }
-
-#if 0 // keep this code for now in case we want to support transporting tangents with mesh assets
- if (face.mTangents)
- { //normals
- F32* tangent = face.mTangents[j].getF32ptr();
-
- for (U32 k = 0; k < 4; ++k)
- { //for each component
- //convert to 16-bit normalized
- U16 val = (U16)((tangent[k] + 1.f) * 0.5f * 65535);
- U8* buff = (U8*)&val;
-
- //write to binary buffer
- tangents[tan_idx++] = buff[0];
- tangents[tan_idx++] = buff[1];
- }
- }
-#endif
-
- //texcoord
- if (face.mTexCoords)
- {
- F32* src_tc = (F32*) face.mTexCoords[j].mV;
-
- for (U32 k = 0; k < 2; ++k)
- { //for each component
- //convert to 16-bit normalized
- U16 val = (U16) ((src_tc[k]-min_tc.mV[k])/tc_range.mV[k]*65535);
-
- U8* buff = (U8*) &val;
- //write to binary buffer
- tc[tc_idx++] = buff[0];
- tc[tc_idx++] = buff[1];
- }
- }
- }
-
- U32 idx_idx = 0;
- for (U32 j = 0; j < face.mNumIndices; ++j)
- {
- U8* buff = (U8*) &(face.mIndices[j]);
- indices[idx_idx++] = buff[0];
- indices[idx_idx++] = buff[1];
- }
-
- //write out face data
- mdl[model_names[idx]][i]["PositionDomain"]["Min"] = min_pos.getValue();
- mdl[model_names[idx]][i]["PositionDomain"]["Max"] = max_pos.getValue();
- mdl[model_names[idx]][i]["NormalizedScale"] = face.mNormalizedScale.getValue();
-
- mdl[model_names[idx]][i]["Position"] = verts;
-
- if (face.mNormals)
- {
- mdl[model_names[idx]][i]["Normal"] = normals;
- }
-
-#if 0 // keep this code for now in case we decide to transport tangents with mesh assets
- if (face.mTangents)
- {
- mdl[model_names[idx]][i]["Tangent"] = tangents;
- }
-#endif
-
- if (face.mTexCoords)
- {
- mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue();
- mdl[model_names[idx]][i]["TexCoord0Domain"]["Max"] = max_tc.getValue();
- mdl[model_names[idx]][i]["TexCoord0"] = tc;
- }
-
- mdl[model_names[idx]][i]["TriangleList"] = indices;
-
- if (skinning)
- {
- if (!model[idx]->mSkinWeights.empty())
- {
- //write out skin weights
-
- //each influence list entry is up to 4 24-bit values
- // first 8 bits is bone index
- // last 16 bits is bone influence weight
- // a bone index of 0xFF signifies no more influences for this vertex
-
- std::stringstream ostr;
- for (U32 j = 0; j < face.mNumVertices; ++j)
- {
- LLVector3 pos(face.mPositions[j].getF32ptr());
-
- weight_list& weights = model[idx]->getJointInfluences(pos);
-
- S32 count = 0;
- for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter)
- {
- // Note joint index cannot exceed 255.
- if (iter->mJointIdx < 255 && iter->mJointIdx >= 0)
- {
- U8 idx = (U8)iter->mJointIdx;
- ostr.write((const char*)&idx, 1);
-
- U16 influence = (U16)(iter->mWeight * 65535);
- ostr.write((const char*)&influence, 2);
-
- ++count;
- }
- }
- U8 end_list = 0xFF;
- if (count < 4)
- {
- ostr.write((const char*)&end_list, 1);
- }
- }
-
- //copy ostr to binary buffer
- std::string data = ostr.str();
- const U8* buff = (U8*)data.data();
- U32 bytes = data.size();
-
- LLSD::Binary w(bytes);
- for (U32 j = 0; j < bytes; ++j)
- {
- w[j] = buff[j];
- }
-
- mdl[model_names[idx]][i]["Weights"] = w;
- }
- else
- {
- if (idx == LLModel::LOD_PHYSICS)
- {
- // Ex: using "bounding box"
- LL_DEBUGS("MESHSKININFO") << "Using physics model without skin weights" << LL_ENDL;
- }
- else
- {
- LL_WARNS("MESHSKININFO") << "Attempting to use skinning without having skin weights" << LL_ENDL;
- }
- }
- }
- }
- }
- }
-
- return writeModelToStream(ostr, mdl, nowrite, as_slm);
-}
-
-LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, bool nowrite, bool as_slm)
-{
- std::string::size_type cur_offset = 0;
-
- LLSD header;
-
- if (as_slm && mdl.has("material_list"))
- { //save material binding names to header
- header["material_list"] = mdl["material_list"];
- }
-
- std::string skin;
-
- if (mdl.has("skin"))
- { //write out skin block
- skin = zip_llsd(mdl["skin"]);
-
- U32 size = skin.size();
- if (size > 0)
- {
- header["skin"]["offset"] = (LLSD::Integer) cur_offset;
- header["skin"]["size"] = (LLSD::Integer) size;
- cur_offset += size;
- }
- }
-
- std::string decomposition;
-
- if (mdl.has("physics_convex"))
- { //write out convex decomposition
- decomposition = zip_llsd(mdl["physics_convex"]);
-
- U32 size = decomposition.size();
- if (size > 0)
- {
- header["physics_convex"]["offset"] = (LLSD::Integer) cur_offset;
- header["physics_convex"]["size"] = (LLSD::Integer) size;
- cur_offset += size;
- }
- }
-
- 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++)
- {
- if (mdl.has(model_names[i]))
- {
- out[i] = zip_llsd(mdl[model_names[i]]);
-
- U32 size = out[i].size();
-
- header[model_names[i]]["offset"] = (LLSD::Integer) cur_offset;
- header[model_names[i]]["size"] = (LLSD::Integer) size;
- cur_offset += size;
- }
- }
-
- if (!nowrite)
- {
- LLSDSerialize::toBinary(header, ostr);
-
- if (!skin.empty())
- { //write skin block
- ostr.write((const char*) skin.data(), header["skin"]["size"].asInteger());
- }
-
- if (!decomposition.empty())
- { //write decomposition block
- ostr.write((const char*) decomposition.data(), header["physics_convex"]["size"].asInteger());
- }
-
- for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++)
- {
- if (!out[i].empty())
- {
- ostr.write((const char*) out[i].data(), header[model_names[i]]["size"].asInteger());
- }
- }
- }
-
- return header;
-}
-
-LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos)
-{
- //1. If a vertex has been weighted then we'll find it via pos and return its weight list
- weight_map::iterator iterPos = mSkinWeights.begin();
- weight_map::iterator iterEnd = mSkinWeights.end();
-
- if (mSkinWeights.empty())
- {
- // function calls iter->second on all return paths
- // everything that calls this function should precheck that there is data.
- LL_ERRS() << "called getJointInfluences with empty weights list" << LL_ENDL;
- }
-
- for ( ; iterPos!=iterEnd; ++iterPos )
- {
- if ( jointPositionalLookup( iterPos->first, pos ) )
- {
- return iterPos->second;
- }
- }
-
- //2. Otherwise we'll use the older implementation
- weight_map::iterator iter = mSkinWeights.find(pos);
-
- if (iter != mSkinWeights.end())
- {
- if ((iter->first - pos).magVec() > 0.1f)
- {
- LL_ERRS() << "Couldn't find weight list." << LL_ENDL;
- }
-
- return iter->second;
- }
- else
- { //no exact match found, get closest point
- const F32 epsilon = 1e-5f;
- weight_map::iterator iter_up = mSkinWeights.lower_bound(pos);
- weight_map::iterator iter_down = iter_up;
- weight_map::iterator best = iter_up;
- if (iter_up != mSkinWeights.end())
- {
- iter_down = ++iter_up;
- }
- else
- {
- // Assumes that there is at least one element
- --best;
- }
-
- F32 min_dist = (iter->first - pos).magVec();
-
- bool done = false;
- while (!done)
- { //search up and down mSkinWeights from lower bound of pos until a
- //match is found within epsilon. If no match is found within epsilon,
- //return closest match
- done = true;
- if (iter_up != mSkinWeights.end() && ++iter_up != mSkinWeights.end())
- {
- done = false;
- F32 dist = (iter_up->first - pos).magVec();
-
- if (dist < epsilon)
- {
- return iter_up->second;
- }
-
- if (dist < min_dist)
- {
- best = iter_up;
- min_dist = dist;
- }
- }
-
- if (iter_down != mSkinWeights.begin() && --iter_down != mSkinWeights.begin())
- {
- done = false;
-
- F32 dist = (iter_down->first - pos).magVec();
-
- if (dist < epsilon)
- {
- return iter_down->second;
- }
-
- if (dist < min_dist)
- {
- best = iter_down;
- min_dist = dist;
- }
-
- }
- }
-
- return best->second;
- }
-}
-
-void LLModel::setConvexHullDecomposition(
- const LLModel::convex_hull_decomposition& decomp)
-{
- mPhysics.mHull = decomp;
- mPhysics.mMesh.clear();
- updateHullCenters();
-}
-
-void LLModel::updateHullCenters()
-{
- mHullCenter.resize(mPhysics.mHull.size());
- mHullPoints = 0;
- mCenterOfHullCenters.clear();
-
- for (U32 i = 0; i < mPhysics.mHull.size(); ++i)
- {
- LLVector3 cur_center;
-
- for (U32 j = 0; j < mPhysics.mHull[i].size(); ++j)
- {
- cur_center += mPhysics.mHull[i][j];
- }
- mCenterOfHullCenters += cur_center;
- cur_center *= 1.f/mPhysics.mHull[i].size();
- mHullCenter[i] = cur_center;
- mHullPoints += mPhysics.mHull[i].size();
- }
-
- if (mHullPoints > 0)
- {
- mCenterOfHullCenters *= 1.f / mHullPoints;
- llassert(mPhysics.hasHullList());
- }
-}
-
-bool LLModel::loadModel(std::istream& is)
-{
- mSculptLevel = -1; // default is an error occured
-
- LLSD header;
- {
- if (!LLSDSerialize::fromBinary(header, is, 1024*1024*1024))
- {
- LL_WARNS("MESHSKININFO") << "Mesh header parse error. Not a valid mesh asset!" << LL_ENDL;
- return false;
- }
- }
-
- if (header.has("material_list"))
- { //load material list names
- mMaterialList.clear();
- for (U32 i = 0; i < header["material_list"].size(); ++i)
- {
- mMaterialList.push_back(header["material_list"][i].asString());
- }
- }
-
- mSubmodelID = header.has("submodel_id") ? header["submodel_id"].asInteger() : false;
-
- static const std::string lod_name[] =
- {
- "lowest_lod",
- "low_lod",
- "medium_lod",
- "high_lod",
- "physics_mesh",
- };
-
- const S32 MODEL_LODS = 5;
-
- S32 lod = llclamp((S32) mDetail, 0, MODEL_LODS);
-
- if (header[lod_name[lod]]["offset"].asInteger() == -1 ||
- header[lod_name[lod]]["size"].asInteger() == 0 )
- { //cannot load requested LOD
- LL_WARNS("MESHSKININFO") << "LoD data is invalid!" << LL_ENDL;
- return false;
- }
-
- bool has_skin = header["skin"]["offset"].asInteger() >=0 &&
- header["skin"]["size"].asInteger() > 0;
-
- 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) && !mSubmodelID)
- {
- std::ios::pos_type cur_pos = is.tellg();
- loadDecomposition(header, is);
- is.seekg(cur_pos);
- }
-
- is.seekg(header[lod_name[lod]]["offset"].asInteger(), std::ios_base::cur);
-
- if (unpackVolumeFaces(is, header[lod_name[lod]]["size"].asInteger()))
- {
- if (has_skin)
- {
- //build out mSkinWeight from face info
- for (S32 i = 0; i < getNumVolumeFaces(); ++i)
- {
- const LLVolumeFace& face = getVolumeFace(i);
-
- if (face.mWeights)
- {
- for (S32 j = 0; j < face.mNumVertices; ++j)
- {
- LLVector4a& w = face.mWeights[j];
-
- std::vector<JointWeight> wght;
-
- for (S32 k = 0; k < 4; ++k)
- {
- S32 idx = (S32) w[k];
- F32 f = w[k] - idx;
- if (f > 0.f)
- {
- wght.push_back(JointWeight(idx, f));
- }
- }
-
- if (!wght.empty())
- {
- LLVector3 pos(face.mPositions[j].getF32ptr());
- mSkinWeights[pos] = wght;
- }
- }
- }
- }
- }
- return true;
- }
- else
- {
- LL_WARNS("MESHSKININFO") << "unpackVolumeFaces failed!" << LL_ENDL;
- }
-
- return false;
-}
-
-bool LLModel::isMaterialListSubset( LLModel* ref )
-{
- int refCnt = ref->mMaterialList.size();
- int modelCnt = mMaterialList.size();
-
- for (U32 src = 0; src < modelCnt; ++src)
- {
- bool foundRef = false;
-
- for (U32 dst = 0; dst < refCnt; ++dst)
- {
- //LL_INFOS()<<mMaterialList[src]<<" "<<ref->mMaterialList[dst]<<LL_ENDL;
- foundRef = mMaterialList[src] == ref->mMaterialList[dst];
-
- if ( foundRef )
- {
- break;
- }
- }
-
- if (!foundRef)
- {
- LL_INFOS("MESHSKININFO") << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL;
- return false;
- }
- }
-
- return true;
-}
-
-bool LLModel::needToAddFaces( LLModel* ref, int& refFaceCnt, int& modelFaceCnt )
-{
- bool changed = false;
- if ( refFaceCnt< modelFaceCnt )
- {
- refFaceCnt += modelFaceCnt - refFaceCnt;
- changed = true;
- }
- else
- if ( modelFaceCnt < refFaceCnt )
- {
- modelFaceCnt += refFaceCnt - modelFaceCnt;
- changed = true;
- }
-
- return changed;
-}
-
-bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCnt )
-{
- //Is this a subset?
- //LODs cannot currently add new materials, e.g.
- //1. ref = a,b,c lod1 = d,e => This is not permitted
- //2. ref = a,b,c lod1 = c => This would be permitted
-
- bool isASubset = isMaterialListSubset( ref );
- if ( !isASubset )
- {
- LL_INFOS("MESHSKININFO")<<"Material of model is not a subset of reference."<<LL_ENDL;
- return false;
- }
-
- if (mMaterialList.size() > ref->mMaterialList.size())
- {
- LL_INFOS("MESHSKININFO") << "Material of model has more materials than a reference." << LL_ENDL;
- // We passed isMaterialListSubset, so materials are a subset, but subset isn't supposed to be
- // larger than original and if we keep going, reordering will cause a crash
- return false;
- }
-
- std::map<std::string, U32> index_map;
-
- //build a map of material slot names to face indexes
- bool reorder = false;
-
- std::set<std::string> base_mat;
- std::set<std::string> cur_mat;
-
- for (U32 i = 0; i < mMaterialList.size(); i++)
- {
- index_map[ref->mMaterialList[i]] = 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
- {
- std::vector<LLVolumeFace> new_face_list;
- new_face_list.resize(mMaterialList.size());
-
- std::vector<std::string> new_material_list;
- 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]];
-
- 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;
- }
-
- return true;
-}
-
-bool LLModel::loadSkinInfo(LLSD& header, std::istream &is)
-{
- S32 offset = header["skin"]["offset"].asInteger();
- S32 size = header["skin"]["size"].asInteger();
-
- if (offset >= 0 && size > 0)
- {
- is.seekg(offset, std::ios_base::cur);
-
- LLSD skin_data;
-
- if (LLUZipHelper::unzip_llsd(skin_data, is, size) == LLUZipHelper::ZR_OK)
- {
- mSkinInfo.fromLLSD(skin_data);
- return true;
- }
- }
-
- return false;
-}
-
-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 && !mSubmodelID)
- {
- is.seekg(offset, std::ios_base::cur);
-
- LLSD data;
-
- if (LLUZipHelper::unzip_llsd(data, is, size) == LLUZipHelper::ZR_OK)
- {
- mPhysics.fromLLSD(data);
- updateHullCenters();
- }
- }
-
- return true;
-}
-
-LLMeshSkinInfo::LLMeshSkinInfo():
- mPelvisOffset(0.0),
- mLockScaleIfJointPosition(false),
- mInvalidJointsScrubbed(false),
- mJointNumsInitialized(false)
-{
-}
-
-LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin):
- mPelvisOffset(0.0),
- mLockScaleIfJointPosition(false),
- mInvalidJointsScrubbed(false),
- mJointNumsInitialized(false)
-{
- fromLLSD(skin);
-}
-
-LLMeshSkinInfo::LLMeshSkinInfo(const LLUUID& mesh_id, LLSD& skin) :
- mMeshID(mesh_id),
- mPelvisOffset(0.0),
- mLockScaleIfJointPosition(false),
- mInvalidJointsScrubbed(false),
- mJointNumsInitialized(false)
-{
- fromLLSD(skin);
-}
-
-void LLMeshSkinInfo::fromLLSD(LLSD& skin)
-{
- if (skin.has("joint_names"))
- {
- for (U32 i = 0; i < skin["joint_names"].size(); ++i)
- {
- mJointNames.push_back(skin["joint_names"][i]);
- mJointNums.push_back(-1);
- }
- }
-
- if (skin.has("inverse_bind_matrix"))
- {
- for (U32 i = 0; i < skin["inverse_bind_matrix"].size(); ++i)
- {
- LLMatrix4 mat;
- for (U32 j = 0; j < 4; j++)
- {
- for (U32 k = 0; k < 4; k++)
- {
- mat.mMatrix[j][k] = skin["inverse_bind_matrix"][i][j*4+k].asReal();
- }
- }
-
- mInvBindMatrix.push_back(LLMatrix4a(mat));
- }
-
- if (mJointNames.size() != mInvBindMatrix.size())
- {
- LL_WARNS("MESHSKININFO") << "Joints vs bind matrix count mismatch. Dropping joint bindings." << LL_ENDL;
- mJointNames.clear();
- mJointNums.clear();
- mInvBindMatrix.clear();
- }
- }
-
- if (skin.has("bind_shape_matrix"))
- {
- LLMatrix4 mat;
- for (U32 j = 0; j < 4; j++)
- {
- for (U32 k = 0; k < 4; k++)
- {
- mat.mMatrix[j][k] = skin["bind_shape_matrix"][j*4+k].asReal();
- }
- }
- mBindShapeMatrix.loadu(mat);
- }
-
- if (skin.has("alt_inverse_bind_matrix"))
- {
- for (U32 i = 0; i < skin["alt_inverse_bind_matrix"].size(); ++i)
- {
- LLMatrix4 mat;
- for (U32 j = 0; j < 4; j++)
- {
- for (U32 k = 0; k < 4; k++)
- {
- mat.mMatrix[j][k] = skin["alt_inverse_bind_matrix"][i][j*4+k].asReal();
- }
- }
-
- mAlternateBindMatrix.push_back(LLMatrix4a(mat));
- }
- }
-
- if (skin.has("pelvis_offset"))
- {
- mPelvisOffset = skin["pelvis_offset"].asReal();
- }
-
- if (skin.has("lock_scale_if_joint_position"))
- {
- mLockScaleIfJointPosition = skin["lock_scale_if_joint_position"].asBoolean();
- }
- else
- {
- mLockScaleIfJointPosition = false;
- }
-
- updateHash();
-}
-
-LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_position) const
-{
- LLSD ret;
-
- for (U32 i = 0; i < mJointNames.size(); ++i)
- {
- ret["joint_names"][i] = mJointNames[i];
-
- for (U32 j = 0; j < 4; j++)
- {
- for (U32 k = 0; k < 4; k++)
- {
- ret["inverse_bind_matrix"][i][j*4+k] = mInvBindMatrix[i].mMatrix[j][k];
- }
- }
- }
-
- for (U32 i = 0; i < 4; i++)
- {
- for (U32 j = 0; j < 4; j++)
- {
- ret["bind_shape_matrix"][i*4+j] = mBindShapeMatrix.mMatrix[i][j];
- }
- }
-
- if ( include_joints && mAlternateBindMatrix.size() > 0 )
- {
- for (U32 i = 0; i < mJointNames.size(); ++i)
- {
- for (U32 j = 0; j < 4; j++)
- {
- for (U32 k = 0; k < 4; k++)
- {
- ret["alt_inverse_bind_matrix"][i][j*4+k] = mAlternateBindMatrix[i].mMatrix[j][k];
- }
- }
- }
-
- if (lock_scale_if_joint_position)
- {
- ret["lock_scale_if_joint_position"] = lock_scale_if_joint_position;
- }
-
- ret["pelvis_offset"] = mPelvisOffset;
- }
-
- return ret;
-}
-
-void LLMeshSkinInfo::updateHash()
-{
- // get hash of data relevant to render batches
- HBXXH64 hash;
-
- //mJointNames
- for (auto& name : mJointNames)
- {
- hash.update(name);
- }
-
- //mJointNums
- hash.update((const void*)mJointNums.data(), sizeof(S32) * mJointNums.size());
-
- //mInvBindMatrix
- F32* src = mInvBindMatrix[0].getF32ptr();
-
- for (size_t i = 0, count = mInvBindMatrix.size() * 16; i < count; ++i)
- {
- S32 t = llround(src[i] * 10000.f);
- hash.update((const void*)&t, sizeof(S32));
- }
- //hash.update((const void*)mInvBindMatrix.data(), sizeof(LLMatrix4a) * mInvBindMatrix.size());
-
- mHash = hash.digest();
-}
-
-U32 LLMeshSkinInfo::sizeBytes() const
-{
- U32 res = sizeof(LLUUID); // mMeshID
-
- res += sizeof(std::vector<std::string>) + sizeof(std::string) * mJointNames.size();
- for (U32 i = 0; i < mJointNames.size(); ++i)
- {
- res += mJointNames[i].size(); // actual size, not capacity
- }
-
- res += sizeof(std::vector<S32>) + sizeof(S32) * mJointNums.size();
- res += sizeof(std::vector<LLMatrix4>) + 16 * sizeof(float) * mInvBindMatrix.size();
- res += sizeof(std::vector<LLMatrix4>) + 16 * sizeof(float) * mAlternateBindMatrix.size();
- res += 16 * sizeof(float); //mBindShapeMatrix
- res += sizeof(float) + 3 * sizeof(bool);
-
- return res;
-}
-
-LLModel::Decomposition::Decomposition(LLSD& data)
-{
- fromLLSD(data);
-}
-
-void LLModel::Decomposition::fromLLSD(LLSD& decomp)
-{
- if (decomp.has("HullList") && decomp.has("Positions"))
- {
- // updated for const-correctness. gcc is picky about this type of thing - Nyx
- const LLSD::Binary& hulls = decomp["HullList"].asBinary();
- const LLSD::Binary& position = decomp["Positions"].asBinary();
-
- U16* p = (U16*) &position[0];
-
- mHull.resize(hulls.size());
-
- LLVector3 min;
- LLVector3 max;
- LLVector3 range;
-
- if (decomp.has("Min"))
- {
- min.setValue(decomp["Min"]);
- max.setValue(decomp["Max"]);
- }
- else
- {
- min.set(-0.5f, -0.5f, -0.5f);
- max.set(0.5f, 0.5f, 0.5f);
- }
-
- range = max-min;
-
- for (U32 i = 0; i < hulls.size(); ++i)
- {
- U16 count = (hulls[i] == 0) ? 256 : hulls[i];
-
- std::set<U64> valid;
-
- //must have at least 4 points
- //llassert(count > 3);
-
- for (U32 j = 0; j < count; ++j)
- {
- U64 test = (U64) p[0] | ((U64) p[1] << 16) | ((U64) p[2] << 32);
- //point must be unique
- //llassert(valid.find(test) == valid.end());
- valid.insert(test);
-
- mHull[i].push_back(LLVector3(
- (F32) p[0]/65535.f*range.mV[0]+min.mV[0],
- (F32) p[1]/65535.f*range.mV[1]+min.mV[1],
- (F32) p[2]/65535.f*range.mV[2]+min.mV[2]));
- p += 3;
-
-
- }
-
- //each hull must contain at least 4 unique points
- //llassert(valid.size() > 3);
- }
- }
-
- if (decomp.has("BoundingVerts"))
- {
- const LLSD::Binary& position = decomp["BoundingVerts"].asBinary();
-
- U16* p = (U16*) &position[0];
-
- LLVector3 min;
- LLVector3 max;
- LLVector3 range;
-
- if (decomp.has("Min"))
- {
- min.setValue(decomp["Min"]);
- max.setValue(decomp["Max"]);
- }
- else
- {
- min.set(-0.5f, -0.5f, -0.5f);
- max.set(0.5f, 0.5f, 0.5f);
- }
-
- range = max-min;
-
- U16 count = (U16)(position.size()/6);
-
- for (U32 j = 0; j < count; ++j)
- {
- mBaseHull.push_back(LLVector3(
- (F32) p[0]/65535.f*range.mV[0]+min.mV[0],
- (F32) p[1]/65535.f*range.mV[1]+min.mV[1],
- (F32) p[2]/65535.f*range.mV[2]+min.mV[2]));
- p += 3;
- }
- }
- else
- {
- //empty base hull mesh to indicate decomposition has been loaded
- //but contains no base hull
- mBaseHullMesh.clear();
- }
-}
-
-U32 LLModel::Decomposition::sizeBytes() const
-{
- U32 res = sizeof(LLUUID); // mMeshID
-
- res += sizeof(LLModel::convex_hull_decomposition) + sizeof(std::vector<LLVector3>) * mHull.size();
- for (U32 i = 0; i < mHull.size(); ++i)
- {
- res += mHull[i].size() * sizeof(LLVector3);
- }
-
- res += sizeof(LLModel::hull) + sizeof(LLVector3) * mBaseHull.size();
-
- res += sizeof(std::vector<LLModel::PhysicsMesh>) + sizeof(std::vector<LLModel::PhysicsMesh>) * mMesh.size();
- for (U32 i = 0; i < mMesh.size(); ++i)
- {
- res += mMesh[i].sizeBytes();
- }
-
- res += sizeof(std::vector<LLModel::PhysicsMesh>) * 2;
- res += mBaseHullMesh.sizeBytes() + mPhysicsShapeMesh.sizeBytes();
-
- return res;
-}
-
-bool LLModel::Decomposition::hasHullList() const
-{
- return !mHull.empty() ;
-}
-
-LLSD LLModel::Decomposition::asLLSD() const
-{
- LLSD ret;
-
- if (mBaseHull.empty() && mHull.empty())
- { //nothing to write
- return ret;
- }
-
- //write decomposition block
- // ["physics_convex"]["HullList"] -- list of 8 bit integers, each entry represents a hull with specified number of points
- // ["physics_convex"]["Position"] -- list of 16-bit integers to be decoded to given domain, encoded 3D points
- // ["physics_convex"]["BoundingVerts"] -- list of 16-bit integers to be decoded to given domain, encoded 3D points representing a single hull approximation of given shape
-
- //get minimum and maximum
- LLVector3 min;
-
- if (mHull.empty())
- {
- min = mBaseHull[0];
- }
- else
- {
- min = mHull[0][0];
- }
-
- LLVector3 max = min;
-
- LLSD::Binary hulls(mHull.size());
-
- U32 total = 0;
-
- for (U32 i = 0; i < mHull.size(); ++i)
- {
- U32 size = mHull[i].size();
- total += size;
- hulls[i] = (U8) (size);
-
- for (U32 j = 0; j < mHull[i].size(); ++j)
- {
- update_min_max(min, max, mHull[i][j]);
- }
- }
-
- for (U32 i = 0; i < mBaseHull.size(); ++i)
- {
- update_min_max(min, max, mBaseHull[i]);
- }
-
- ret["Min"] = min.getValue();
- ret["Max"] = max.getValue();
-
- LLVector3 range = max-min;
-
- if (!hulls.empty())
- {
- ret["HullList"] = hulls;
- }
-
- if (total > 0)
- {
- LLSD::Binary p(total*3*2);
-
- U32 vert_idx = 0;
-
- for (U32 i = 0; i < mHull.size(); ++i)
- {
- std::set<U64> valid;
-
- llassert(!mHull[i].empty());
-
- for (U32 j = 0; j < mHull[i].size(); ++j)
- {
- U64 test = 0;
- const F32* src = mHull[i][j].mV;
-
- for (U32 k = 0; k < 3; k++)
- {
- //convert to 16-bit normalized across domain
- U16 val = (U16) (((src[k]-min.mV[k])/range.mV[k])*65535);
-
- if(valid.size() < 3)
- {
- switch (k)
- {
- case 0: test = test | (U64) val; break;
- case 1: test = test | ((U64) val << 16); break;
- case 2: test = test | ((U64) val << 32); break;
- };
-
- valid.insert(test);
- }
-
- U8* buff = (U8*) &val;
- //write to binary buffer
- p[vert_idx++] = buff[0];
- p[vert_idx++] = buff[1];
-
- //makes sure we haven't run off the end of the array
- llassert(vert_idx <= p.size());
- }
- }
-
- //must have at least 3 unique points
- llassert(valid.size() > 2);
- }
-
- ret["Positions"] = p;
- }
-
- //llassert(!mBaseHull.empty());
-
- if (!mBaseHull.empty())
- {
- LLSD::Binary p(mBaseHull.size()*3*2);
-
- U32 vert_idx = 0;
- for (U32 j = 0; j < mBaseHull.size(); ++j)
- {
- const F32* v = mBaseHull[j].mV;
-
- for (U32 k = 0; k < 3; k++)
- {
- //convert to 16-bit normalized across domain
- U16 val = (U16) (((v[k]-min.mV[k])/range.mV[k])*65535);
-
- U8* buff = (U8*) &val;
- //write to binary buffer
- p[vert_idx++] = buff[0];
- p[vert_idx++] = buff[1];
-
- if (vert_idx > p.size())
- {
- LL_ERRS() << "Index out of bounds" << LL_ENDL;
- }
- }
- }
-
- ret["BoundingVerts"] = p;
- }
-
- return ret;
-}
-
-void LLModel::Decomposition::merge(const LLModel::Decomposition* rhs)
-{
- if (!rhs)
- {
- return;
- }
-
- if (mMeshID != rhs->mMeshID)
- {
- LL_ERRS() << "Attempted to merge with decomposition of some other mesh." << LL_ENDL;
- }
-
- if (mBaseHull.empty())
- { //take base hull and decomposition from rhs
- mHull = rhs->mHull;
- mBaseHull = rhs->mBaseHull;
- mMesh = rhs->mMesh;
- mBaseHullMesh = rhs->mBaseHullMesh;
- }
-
- if (mPhysicsShapeMesh.empty())
- { //take physics shape mesh from rhs
- mPhysicsShapeMesh = rhs->mPhysicsShapeMesh;
- }
-}
-
-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("MESHSKININFO") << "Face has invalid index." << LL_ENDL;
- return false;
- }
- }
-
- if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0)
- {
- LL_WARNS("MESHSKININFO") << "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("MESHSKININFO") << "Model has no faces!" << LL_ENDL;
- return false;
- }
-
- for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i)
- {
- if (mdl->getVolumeFace(i).mNumVertices == 0)
- {
- LL_WARNS("MESHSKININFO") << "Face has no vertices." << LL_ENDL;
- return false;
- }
-
- if (mdl->getVolumeFace(i).mNumIndices == 0)
- {
- LL_WARNS("MESHSKININFO") << "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;
-}
-
+/** + * @file llmodel.cpp + * @brief Model handling implementation + * + * $LicenseInfo:firstyear=2001&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 "linden_common.h" + +#include "llmodel.h" +#include "llmemory.h" +#include "llconvexdecomposition.h" +#include "llsdserialize.h" +#include "llvector4a.h" +#include "hbxxh.h" + +#ifdef LL_USESYSTEMLIBS +# include <zlib.h> +#else +# include "zlib-ng/zlib.h" +#endif + +std::string model_names[] = +{ + "lowest_lod", + "low_lod", + "medium_lod", + "high_lod", + "physics_mesh" +}; + +const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string); + +LLModel::LLModel(const LLVolumeParams& params, F32 detail) + : LLVolume(params, detail), + mNormalizedScale(1,1,1), + mNormalizedTranslation(0, 0, 0), + mPelvisOffset( 0.0f ), + mStatus(NO_ERRORS), + mSubmodelID(0) +{ + mDecompID = -1; + mLocalID = -1; +} + +LLModel::~LLModel() +{ + if (mDecompID >= 0) + { + LLConvexDecomposition::getInstance()->deleteDecomposition(mDecompID); + } + mPhysics.mMesh.clear(); +} + +//static +std::string LLModel::getStatusString(U32 status) +{ + const static std::string status_strings[(S32)INVALID_STATUS] = {"status_no_error", "status_vertex_number_overflow","bad_element"}; + + if(status < INVALID_STATUS) + { + if(status_strings[status] == std::string()) + { + //LL_ERRS() << "No valid status string for this status: " << (U32)status << LL_ENDL(); + } + return status_strings[status] ; + } + + //LL_ERRS() << "Invalid model status: " << (U32)status << LL_ENDL(); + + return std::string() ; +} + + +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(); ) + { + std::vector<LLVolumeFace>:: iterator currentFaceIt = faceIt++; + LLVolumeFace& face = *currentFaceIt; + LLVector4a *pos = (LLVector4a*) face.mPositions; + + for (U32 i=0; i<face.mNumVertices; ++i ) + { + pos[i].add( pivot ); + } + } +} + +void LLModel::remapVolumeFaces() +{ + for (U32 i = 0; i < getNumVolumeFaces(); ++i) + { + mVolumeFaces[i].remap(); + } +} + +void LLModel::optimizeVolumeFaces() +{ + for (U32 i = 0; i < getNumVolumeFaces(); ++i) + { + mVolumeFaces[i].optimize(); + } +} + +struct MaterialBinding +{ + int index; + std::string matName; +}; + +struct MaterialSort +{ + bool operator()(const MaterialBinding& lhs, const MaterialBinding& rhs) + { + return LLStringUtil::compareInsensitive(lhs.matName, rhs.matName) < 0; + } +}; + +void LLModel::sortVolumeFacesByMaterialName() +{ + std::vector<MaterialBinding> bindings; + bindings.resize(mVolumeFaces.size()); + + for (int i = 0; i < bindings.size(); i++) + { + bindings[i].index = i; + if(i < mMaterialList.size()) + { + bindings[i].matName = mMaterialList[i]; + } + } + std::sort(bindings.begin(), bindings.end(), MaterialSort()); + std::vector< LLVolumeFace > new_faces; + + // 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++) + { + new_faces[i] = mVolumeFaces[bindings[i].index]; + if(i < mMaterialList.size()) + { + mMaterialList[i] = bindings[i].matName; + } + } + + mVolumeFaces = new_faces; +} + +void LLModel::trimVolumeFacesToSize(U32 new_count, LLVolume::face_list_t* remainder) +{ + llassert(new_count <= LL_SCULPT_MESH_MAX_FACES); + + if (new_count && (getNumVolumeFaces() > new_count)) + { + // Copy out remaining volume faces for alternative handling, if provided + // + if (remainder) + { + (*remainder).assign(mVolumeFaces.begin() + new_count, mVolumeFaces.end()); + } + + // Trim down to the final set of volume faces (now stuffed to the gills!) + // + mVolumeFaces.resize(new_count); + } +} + +// Shrink the model to fit +// on a 1x1x1 cube centered at the origin. +// The positions and extents +// multiplied by mNormalizedScale +// and offset by mNormalizedTranslation +// to be the "original" extents and position. +// Also, the positions will fit +// within the unit cube. +void LLModel::normalizeVolumeFaces() +{ + if (!mVolumeFaces.empty()) + { + LLVector4a min, max; + + // For all of the volume faces + // in the model, loop over + // them and see what the extents + // of the volume along each axis. + min = mVolumeFaces[0].mExtents[0]; + max = mVolumeFaces[0].mExtents[1]; + + for (U32 i = 1; i < mVolumeFaces.size(); ++i) + { + LLVolumeFace& face = mVolumeFaces[i]; + + update_min_max(min, max, face.mExtents[0]); + update_min_max(min, max, face.mExtents[1]); + + if (face.mTexCoords) + { + LLVector2& min_tc = face.mTexCoordExtents[0]; + LLVector2& max_tc = face.mTexCoordExtents[1]; + + min_tc = face.mTexCoords[0]; + max_tc = face.mTexCoords[0]; + + for (U32 j = 1; j < face.mNumVertices; ++j) + { + update_min_max(min_tc, max_tc, face.mTexCoords[j]); + } + } + else + { + face.mTexCoordExtents[0].set(0,0); + face.mTexCoordExtents[1].set(1,1); + } + } + + // Now that we have the extents of the model + // we can compute the offset needed to center + // the model at the origin. + + // Compute center of the model + // and make it negative to get translation + // needed to center at origin. + LLVector4a trans; + trans.setAdd(min, max); + trans.mul(-0.5f); + + // Compute the total size along all + // axes of the model. + LLVector4a size; + size.setSub(max, min); + + // Prevent division by zero. + F32 x = size[0]; + F32 y = size[1]; + F32 z = size[2]; + F32 w = size[3]; + if (fabs(x)<F_APPROXIMATELY_ZERO) + { + x = 1.0; + } + if (fabs(y)<F_APPROXIMATELY_ZERO) + { + y = 1.0; + } + if (fabs(z)<F_APPROXIMATELY_ZERO) + { + z = 1.0; + } + size.set(x,y,z,w); + + // Compute scale as reciprocal of size + LLVector4a scale; + scale.splat(1.f); + scale.div(size); + + LLVector4a inv_scale(1.f); + inv_scale.div(scale); + + for (U32 i = 0; i < mVolumeFaces.size(); ++i) + { + LLVolumeFace& face = mVolumeFaces[i]; + + // We shrink the extents so + // that they fall within + // the unit cube. + // VFExtents change + face.mExtents[0].add(trans); + face.mExtents[0].mul(scale); + + face.mExtents[1].add(trans); + face.mExtents[1].mul(scale); + + // For all the positions, we scale + // the positions to fit within the unit cube. + LLVector4a* pos = (LLVector4a*) face.mPositions; + LLVector4a* norm = (LLVector4a*) face.mNormals; + LLVector4a* t = (LLVector4a*)face.mTangents; + + for (U32 j = 0; j < face.mNumVertices; ++j) + { + pos[j].add(trans); + pos[j].mul(scale); + if (norm && !norm[j].equals3(LLVector4a::getZero())) + { + norm[j].mul(inv_scale); + norm[j].normalize3(); + } + + if (t) + { + F32 w = t[j].getF32ptr()[3]; + t[j].mul(inv_scale); + t[j].normalize3(); + t[j].getF32ptr()[3] = w; + } + } + } + + // mNormalizedScale is the scale at which + // we would need to multiply the model + // by to get the original size of the + // model instead of the normalized size. + LLVector4a normalized_scale; + normalized_scale.splat(1.f); + normalized_scale.div(scale); + mNormalizedScale.set(normalized_scale.getF32ptr()); + mNormalizedTranslation.set(trans.getF32ptr()); + mNormalizedTranslation *= -1.f; + + // remember normalized scale so original dimensions can be recovered for mesh processing (i.e. tangent generation) + for (auto& face : mVolumeFaces) + { + face.mNormalizedScale = mNormalizedScale; + } + } +} + +void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) +{ + scale_out = mNormalizedScale; + translation_out = mNormalizedTranslation; +} + +LLVector3 LLModel::getTransformedCenter(const LLMatrix4& mat) +{ + LLVector3 ret; + + if (!mVolumeFaces.empty()) + { + LLMatrix4a m; + m.loadu(mat); + + LLVector4a minv,maxv; + + LLVector4a t; + m.affineTransform(mVolumeFaces[0].mPositions[0], t); + minv = maxv = t; + + for (S32 i = 0; i < mVolumeFaces.size(); ++i) + { + LLVolumeFace& face = mVolumeFaces[i]; + + for (U32 j = 0; j < face.mNumVertices; ++j) + { + m.affineTransform(face.mPositions[j],t); + update_min_max(minv, maxv, t); + } + } + + minv.add(maxv); + minv.mul(0.5f); + + ret.set(minv.getF32ptr()); + } + + return ret; +} + + + +void LLModel::setNumVolumeFaces(S32 count) +{ + mVolumeFaces.resize(count); +} + +void LLModel::setVolumeFaceData( + S32 f, + LLStrider<LLVector3> pos, + LLStrider<LLVector3> norm, + LLStrider<LLVector2> tc, + LLStrider<U16> ind, + U32 num_verts, + U32 num_indices) +{ + llassert(num_indices % 3 == 0); + + LLVolumeFace& face = mVolumeFaces[f]; + + face.resizeVertices(num_verts); + face.resizeIndices(num_indices); + + LLVector4a::memcpyNonAliased16((F32*) face.mPositions, (F32*) pos.get(), num_verts*4*sizeof(F32)); + if (norm.get()) + { + LLVector4a::memcpyNonAliased16((F32*) face.mNormals, (F32*) norm.get(), num_verts*4*sizeof(F32)); + } + else + { + //ll_aligned_free_16(face.mNormals); + face.mNormals = NULL; + } + + if (tc.get()) + { + U32 tex_size = (num_verts*2*sizeof(F32)+0xF)&~0xF; + LLVector4a::memcpyNonAliased16((F32*) face.mTexCoords, (F32*) tc.get(), tex_size); + } + else + { + //ll_aligned_free_16(face.mTexCoords); + face.mTexCoords = NULL; + } + + U32 size = (num_indices*2+0xF)&~0xF; + LLVector4a::memcpyNonAliased16((F32*) face.mIndices, (F32*) ind.get(), size); +} + +void LLModel::addFace(const LLVolumeFace& face) +{ + if (face.mNumVertices == 0) + { + LL_ERRS() << "Cannot add empty face." << LL_ENDL; + } + + mVolumeFaces.push_back(face); + + if (mVolumeFaces.size() > MAX_MODEL_FACES) + { + LL_ERRS() << "Model prims cannot have more than " << MAX_MODEL_FACES << " faces!" << LL_ENDL; + } +} + + +void LLModel::generateNormals(F32 angle_cutoff) +{ + //generate normals for all faces by: + // 1 - Create faceted copy of face with no texture coordinates + // 2 - Weld vertices in faceted copy that are shared between triangles with less than "angle_cutoff" difference between normals + // 3 - Generate smoothed set of normals based on welding results + // 4 - Create faceted copy of face with texture coordinates + // 5 - Copy smoothed normals to faceted copy, using closest normal to triangle normal where more than one normal exists for a given position + // 6 - Remove redundant vertices from new faceted (now smooth) copy + + angle_cutoff = cosf(angle_cutoff); + for (U32 j = 0; j < mVolumeFaces.size(); ++j) + { + LLVolumeFace& vol_face = mVolumeFaces[j]; + + if (vol_face.mNumIndices > 65535) + { + LL_WARNS("MESHSKININFO") << "Too many vertices for normal generation to work." << LL_ENDL; + continue; + } + + //create faceted copy of current face with no texture coordinates (step 1) + LLVolumeFace faceted; + + LLVector4a* src_pos = (LLVector4a*) vol_face.mPositions; + //LLVector4a* src_norm = (LLVector4a*) vol_face.mNormals; + + + faceted.resizeVertices(vol_face.mNumIndices); + faceted.resizeIndices(vol_face.mNumIndices); + //bake out triangles into temporary face, clearing texture coordinates + for (U32 i = 0; i < vol_face.mNumIndices; ++i) + { + U32 idx = vol_face.mIndices[i]; + + faceted.mPositions[i] = src_pos[idx]; + faceted.mTexCoords[i] = LLVector2(0,0); + faceted.mIndices[i] = i; + } + + //generate normals for temporary face + for (U32 i = 0; i < faceted.mNumIndices; i += 3) + { //for each triangle + U16 i0 = faceted.mIndices[i+0]; + U16 i1 = faceted.mIndices[i+1]; + U16 i2 = faceted.mIndices[i+2]; + + LLVector4a& p0 = faceted.mPositions[i0]; + LLVector4a& p1 = faceted.mPositions[i1]; + LLVector4a& p2 = faceted.mPositions[i2]; + + LLVector4a& n0 = faceted.mNormals[i0]; + LLVector4a& n1 = faceted.mNormals[i1]; + LLVector4a& n2 = faceted.mNormals[i2]; + + LLVector4a lhs, rhs; + lhs.setSub(p1, p0); + rhs.setSub(p2, p0); + + n0.setCross3(lhs, rhs); + n0.normalize3(); + n1 = n0; + n2 = n0; + } + + //weld vertices in temporary face, respecting angle_cutoff (step 2) + faceted.optimize(angle_cutoff); + + //generate normals for welded face based on new topology (step 3) + + for (U32 i = 0; i < faceted.mNumVertices; i++) + { + faceted.mNormals[i].clear(); + } + + for (U32 i = 0; i < faceted.mNumIndices; i += 3) + { //for each triangle + U16 i0 = faceted.mIndices[i+0]; + U16 i1 = faceted.mIndices[i+1]; + U16 i2 = faceted.mIndices[i+2]; + + LLVector4a& p0 = faceted.mPositions[i0]; + LLVector4a& p1 = faceted.mPositions[i1]; + LLVector4a& p2 = faceted.mPositions[i2]; + + LLVector4a& n0 = faceted.mNormals[i0]; + LLVector4a& n1 = faceted.mNormals[i1]; + LLVector4a& n2 = faceted.mNormals[i2]; + + LLVector4a lhs, rhs; + lhs.setSub(p1, p0); + rhs.setSub(p2, p0); + + LLVector4a n; + n.setCross3(lhs, rhs); + + n0.add(n); + n1.add(n); + n2.add(n); + } + + //normalize normals and build point map + LLVolumeFace::VertexMapData::PointMap point_map; + + for (U32 i = 0; i < faceted.mNumVertices; ++i) + { + faceted.mNormals[i].normalize3(); + + LLVolumeFace::VertexMapData v; + v.setPosition(faceted.mPositions[i]); + v.setNormal(faceted.mNormals[i]); + + point_map[LLVector3(v.getPosition().getF32ptr())].push_back(v); + } + + //create faceted copy of current face with texture coordinates (step 4) + LLVolumeFace new_face; + + //bake out triangles into new face + new_face.resizeIndices(vol_face.mNumIndices); + new_face.resizeVertices(vol_face.mNumIndices); + + for (U32 i = 0; i < vol_face.mNumIndices; ++i) + { + U32 idx = vol_face.mIndices[i]; + LLVolumeFace::VertexData v; + new_face.mPositions[i] = vol_face.mPositions[idx]; + new_face.mNormals[i].clear(); + new_face.mIndices[i] = i; + } + + if (vol_face.mTexCoords) + { + for (U32 i = 0; i < vol_face.mNumIndices; i++) + { + U32 idx = vol_face.mIndices[i]; + new_face.mTexCoords[i] = vol_face.mTexCoords[idx]; + } + } + else + { + //ll_aligned_free_16(new_face.mTexCoords); + new_face.mTexCoords = NULL; + } + + //generate normals for new face + for (U32 i = 0; i < new_face.mNumIndices; i += 3) + { //for each triangle + U16 i0 = new_face.mIndices[i+0]; + U16 i1 = new_face.mIndices[i+1]; + U16 i2 = new_face.mIndices[i+2]; + + LLVector4a& p0 = new_face.mPositions[i0]; + LLVector4a& p1 = new_face.mPositions[i1]; + LLVector4a& p2 = new_face.mPositions[i2]; + + LLVector4a& n0 = new_face.mNormals[i0]; + LLVector4a& n1 = new_face.mNormals[i1]; + LLVector4a& n2 = new_face.mNormals[i2]; + + LLVector4a lhs, rhs; + lhs.setSub(p1, p0); + rhs.setSub(p2, p0); + + n0.setCross3(lhs, rhs); + n0.normalize3(); + n1 = n0; + n2 = n0; + } + + //swap out normals in new_face with best match from point map (step 5) + for (U32 i = 0; i < new_face.mNumVertices; ++i) + { + //LLVolumeFace::VertexData v = new_face.mVertices[i]; + + LLVector4a ref_norm = new_face.mNormals[i]; + + LLVolumeFace::VertexMapData::PointMap::iterator iter = point_map.find(LLVector3(new_face.mPositions[i].getF32ptr())); + + if (iter != point_map.end()) + { + F32 best = -2.f; + for (U32 k = 0; k < iter->second.size(); ++k) + { + LLVector4a& n = iter->second[k].getNormal(); + + F32 cur = n.dot3(ref_norm).getF32(); + + if (cur > best) + { + best = cur; + new_face.mNormals[i] = n; + } + } + } + } + + //remove redundant vertices from new face (step 6) + new_face.optimize(); + + mVolumeFaces[j] = new_face; + } +} + + +std::string LLModel::getName() const +{ + return mRequestedLabel.empty() ? mLabel : mRequestedLabel; +} + +//static +LLSD LLModel::writeModel( + std::ostream& ostr, + LLModel* physics, + LLModel* high, + LLModel* medium, + LLModel* low, + LLModel* impostor, + const LLModel::Decomposition& decomp, + bool upload_skin, + bool upload_joints, + bool lock_scale_if_joint_position, + bool nowrite, + bool as_slm, + int submodel_id) +{ + LLSD mdl; + + LLModel* model[] = + { + impostor, + low, + medium, + high, + physics + }; + + bool skinning = upload_skin && high && !high->mSkinWeights.empty(); + + if (skinning) + { //write skinning block + mdl["skin"] = high->mSkinInfo.asLLSD(upload_joints, lock_scale_if_joint_position); + } + + if (!decomp.mBaseHull.empty() || + !decomp.mHull.empty()) + { + mdl["physics_convex"] = decomp.asLLSD(); + if (!decomp.mHull.empty() && !as_slm) + { //convex decomposition exists, physics mesh will not be used (unless this is an slm file) + 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 + for (U32 i = 0; i < high->mMaterialList.size(); ++i) + { + mdl["material_list"][i] = high->mMaterialList[i]; + } + } + + for (U32 idx = 0; idx < MODEL_NAMES_LENGTH; ++idx) + { + 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; + + //find position domain + for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i) + { //for each face + const LLVolumeFace& face = model[idx]->getVolumeFace(i); + for (U32 j = 0; j < face.mNumVertices; ++j) + { + update_min_max(min_pos, max_pos, face.mPositions[j].getF32ptr()); + } + } + + LLVector3 pos_range = max_pos - min_pos; + + for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i) + { //for each face + const LLVolumeFace& face = model[idx]->getVolumeFace(i); + if (face.mNumVertices < 3) + { //don't export an empty face + mdl[model_names[idx]][i]["NoGeometry"] = true; + continue; + } + LLSD::Binary verts(face.mNumVertices*3*2); + LLSD::Binary tc(face.mNumVertices*2*2); + LLSD::Binary normals(face.mNumVertices*3*2); + LLSD::Binary tangents(face.mNumVertices * 4 * 2); + LLSD::Binary indices(face.mNumIndices*2); + + U32 vert_idx = 0; + U32 norm_idx = 0; + //U32 tan_idx = 0; + U32 tc_idx = 0; + + LLVector2* ftc = (LLVector2*) face.mTexCoords; + LLVector2 min_tc; + LLVector2 max_tc; + + if (ftc) + { + min_tc = ftc[0]; + max_tc = min_tc; + + //get texture coordinate domain + for (U32 j = 0; j < face.mNumVertices; ++j) + { + update_min_max(min_tc, max_tc, ftc[j]); + } + } + + LLVector2 tc_range = max_tc - min_tc; + + for (U32 j = 0; j < face.mNumVertices; ++j) + { //for each vert + + F32* pos = face.mPositions[j].getF32ptr(); + + //position + for (U32 k = 0; k < 3; ++k) + { //for each component + //convert to 16-bit normalized across domain + U16 val = (U16) (((pos[k]-min_pos.mV[k])/pos_range.mV[k])*65535); + + U8* buff = (U8*) &val; + //write to binary buffer + verts[vert_idx++] = buff[0]; + verts[vert_idx++] = buff[1]; + } + + if (face.mNormals) + { //normals + F32* norm = face.mNormals[j].getF32ptr(); + + for (U32 k = 0; k < 3; ++k) + { //for each component + //convert to 16-bit normalized + U16 val = (U16) ((norm[k]+1.f)*0.5f*65535); + U8* buff = (U8*) &val; + + //write to binary buffer + normals[norm_idx++] = buff[0]; + normals[norm_idx++] = buff[1]; + } + } + +#if 0 // keep this code for now in case we want to support transporting tangents with mesh assets + if (face.mTangents) + { //normals + F32* tangent = face.mTangents[j].getF32ptr(); + + for (U32 k = 0; k < 4; ++k) + { //for each component + //convert to 16-bit normalized + U16 val = (U16)((tangent[k] + 1.f) * 0.5f * 65535); + U8* buff = (U8*)&val; + + //write to binary buffer + tangents[tan_idx++] = buff[0]; + tangents[tan_idx++] = buff[1]; + } + } +#endif + + //texcoord + if (face.mTexCoords) + { + F32* src_tc = (F32*) face.mTexCoords[j].mV; + + for (U32 k = 0; k < 2; ++k) + { //for each component + //convert to 16-bit normalized + U16 val = (U16) ((src_tc[k]-min_tc.mV[k])/tc_range.mV[k]*65535); + + U8* buff = (U8*) &val; + //write to binary buffer + tc[tc_idx++] = buff[0]; + tc[tc_idx++] = buff[1]; + } + } + } + + U32 idx_idx = 0; + for (U32 j = 0; j < face.mNumIndices; ++j) + { + U8* buff = (U8*) &(face.mIndices[j]); + indices[idx_idx++] = buff[0]; + indices[idx_idx++] = buff[1]; + } + + //write out face data + mdl[model_names[idx]][i]["PositionDomain"]["Min"] = min_pos.getValue(); + mdl[model_names[idx]][i]["PositionDomain"]["Max"] = max_pos.getValue(); + mdl[model_names[idx]][i]["NormalizedScale"] = face.mNormalizedScale.getValue(); + + mdl[model_names[idx]][i]["Position"] = verts; + + if (face.mNormals) + { + mdl[model_names[idx]][i]["Normal"] = normals; + } + +#if 0 // keep this code for now in case we decide to transport tangents with mesh assets + if (face.mTangents) + { + mdl[model_names[idx]][i]["Tangent"] = tangents; + } +#endif + + if (face.mTexCoords) + { + mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue(); + mdl[model_names[idx]][i]["TexCoord0Domain"]["Max"] = max_tc.getValue(); + mdl[model_names[idx]][i]["TexCoord0"] = tc; + } + + mdl[model_names[idx]][i]["TriangleList"] = indices; + + if (skinning) + { + if (!model[idx]->mSkinWeights.empty()) + { + //write out skin weights + + //each influence list entry is up to 4 24-bit values + // first 8 bits is bone index + // last 16 bits is bone influence weight + // a bone index of 0xFF signifies no more influences for this vertex + + std::stringstream ostr; + for (U32 j = 0; j < face.mNumVertices; ++j) + { + LLVector3 pos(face.mPositions[j].getF32ptr()); + + weight_list& weights = model[idx]->getJointInfluences(pos); + + S32 count = 0; + for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter) + { + // Note joint index cannot exceed 255. + if (iter->mJointIdx < 255 && iter->mJointIdx >= 0) + { + U8 idx = (U8)iter->mJointIdx; + ostr.write((const char*)&idx, 1); + + U16 influence = (U16)(iter->mWeight * 65535); + ostr.write((const char*)&influence, 2); + + ++count; + } + } + U8 end_list = 0xFF; + if (count < 4) + { + ostr.write((const char*)&end_list, 1); + } + } + + //copy ostr to binary buffer + std::string data = ostr.str(); + const U8* buff = (U8*)data.data(); + U32 bytes = data.size(); + + LLSD::Binary w(bytes); + for (U32 j = 0; j < bytes; ++j) + { + w[j] = buff[j]; + } + + mdl[model_names[idx]][i]["Weights"] = w; + } + else + { + if (idx == LLModel::LOD_PHYSICS) + { + // Ex: using "bounding box" + LL_DEBUGS("MESHSKININFO") << "Using physics model without skin weights" << LL_ENDL; + } + else + { + LL_WARNS("MESHSKININFO") << "Attempting to use skinning without having skin weights" << LL_ENDL; + } + } + } + } + } + } + + return writeModelToStream(ostr, mdl, nowrite, as_slm); +} + +LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, bool nowrite, bool as_slm) +{ + std::string::size_type cur_offset = 0; + + LLSD header; + + if (as_slm && mdl.has("material_list")) + { //save material binding names to header + header["material_list"] = mdl["material_list"]; + } + + std::string skin; + + if (mdl.has("skin")) + { //write out skin block + skin = zip_llsd(mdl["skin"]); + + U32 size = skin.size(); + if (size > 0) + { + header["skin"]["offset"] = (LLSD::Integer) cur_offset; + header["skin"]["size"] = (LLSD::Integer) size; + cur_offset += size; + } + } + + std::string decomposition; + + if (mdl.has("physics_convex")) + { //write out convex decomposition + decomposition = zip_llsd(mdl["physics_convex"]); + + U32 size = decomposition.size(); + if (size > 0) + { + header["physics_convex"]["offset"] = (LLSD::Integer) cur_offset; + header["physics_convex"]["size"] = (LLSD::Integer) size; + cur_offset += size; + } + } + + 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++) + { + if (mdl.has(model_names[i])) + { + out[i] = zip_llsd(mdl[model_names[i]]); + + U32 size = out[i].size(); + + header[model_names[i]]["offset"] = (LLSD::Integer) cur_offset; + header[model_names[i]]["size"] = (LLSD::Integer) size; + cur_offset += size; + } + } + + if (!nowrite) + { + LLSDSerialize::toBinary(header, ostr); + + if (!skin.empty()) + { //write skin block + ostr.write((const char*) skin.data(), header["skin"]["size"].asInteger()); + } + + if (!decomposition.empty()) + { //write decomposition block + ostr.write((const char*) decomposition.data(), header["physics_convex"]["size"].asInteger()); + } + + for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++) + { + if (!out[i].empty()) + { + ostr.write((const char*) out[i].data(), header[model_names[i]]["size"].asInteger()); + } + } + } + + return header; +} + +LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos) +{ + //1. If a vertex has been weighted then we'll find it via pos and return its weight list + weight_map::iterator iterPos = mSkinWeights.begin(); + weight_map::iterator iterEnd = mSkinWeights.end(); + + if (mSkinWeights.empty()) + { + // function calls iter->second on all return paths + // everything that calls this function should precheck that there is data. + LL_ERRS() << "called getJointInfluences with empty weights list" << LL_ENDL; + } + + for ( ; iterPos!=iterEnd; ++iterPos ) + { + if ( jointPositionalLookup( iterPos->first, pos ) ) + { + return iterPos->second; + } + } + + //2. Otherwise we'll use the older implementation + weight_map::iterator iter = mSkinWeights.find(pos); + + if (iter != mSkinWeights.end()) + { + if ((iter->first - pos).magVec() > 0.1f) + { + LL_ERRS() << "Couldn't find weight list." << LL_ENDL; + } + + return iter->second; + } + else + { //no exact match found, get closest point + const F32 epsilon = 1e-5f; + weight_map::iterator iter_up = mSkinWeights.lower_bound(pos); + weight_map::iterator iter_down = iter_up; + weight_map::iterator best = iter_up; + if (iter_up != mSkinWeights.end()) + { + iter_down = ++iter_up; + } + else + { + // Assumes that there is at least one element + --best; + } + + F32 min_dist = (iter->first - pos).magVec(); + + bool done = false; + while (!done) + { //search up and down mSkinWeights from lower bound of pos until a + //match is found within epsilon. If no match is found within epsilon, + //return closest match + done = true; + if (iter_up != mSkinWeights.end() && ++iter_up != mSkinWeights.end()) + { + done = false; + F32 dist = (iter_up->first - pos).magVec(); + + if (dist < epsilon) + { + return iter_up->second; + } + + if (dist < min_dist) + { + best = iter_up; + min_dist = dist; + } + } + + if (iter_down != mSkinWeights.begin() && --iter_down != mSkinWeights.begin()) + { + done = false; + + F32 dist = (iter_down->first - pos).magVec(); + + if (dist < epsilon) + { + return iter_down->second; + } + + if (dist < min_dist) + { + best = iter_down; + min_dist = dist; + } + + } + } + + return best->second; + } +} + +void LLModel::setConvexHullDecomposition( + const LLModel::convex_hull_decomposition& decomp) +{ + mPhysics.mHull = decomp; + mPhysics.mMesh.clear(); + updateHullCenters(); +} + +void LLModel::updateHullCenters() +{ + mHullCenter.resize(mPhysics.mHull.size()); + mHullPoints = 0; + mCenterOfHullCenters.clear(); + + for (U32 i = 0; i < mPhysics.mHull.size(); ++i) + { + LLVector3 cur_center; + + for (U32 j = 0; j < mPhysics.mHull[i].size(); ++j) + { + cur_center += mPhysics.mHull[i][j]; + } + mCenterOfHullCenters += cur_center; + cur_center *= 1.f/mPhysics.mHull[i].size(); + mHullCenter[i] = cur_center; + mHullPoints += mPhysics.mHull[i].size(); + } + + if (mHullPoints > 0) + { + mCenterOfHullCenters *= 1.f / mHullPoints; + llassert(mPhysics.hasHullList()); + } +} + +bool LLModel::loadModel(std::istream& is) +{ + mSculptLevel = -1; // default is an error occured + + LLSD header; + { + if (!LLSDSerialize::fromBinary(header, is, 1024*1024*1024)) + { + LL_WARNS("MESHSKININFO") << "Mesh header parse error. Not a valid mesh asset!" << LL_ENDL; + return false; + } + } + + if (header.has("material_list")) + { //load material list names + mMaterialList.clear(); + for (U32 i = 0; i < header["material_list"].size(); ++i) + { + mMaterialList.push_back(header["material_list"][i].asString()); + } + } + + mSubmodelID = header.has("submodel_id") ? header["submodel_id"].asInteger() : false; + + static const std::string lod_name[] = + { + "lowest_lod", + "low_lod", + "medium_lod", + "high_lod", + "physics_mesh", + }; + + const S32 MODEL_LODS = 5; + + S32 lod = llclamp((S32) mDetail, 0, MODEL_LODS); + + if (header[lod_name[lod]]["offset"].asInteger() == -1 || + header[lod_name[lod]]["size"].asInteger() == 0 ) + { //cannot load requested LOD + LL_WARNS("MESHSKININFO") << "LoD data is invalid!" << LL_ENDL; + return false; + } + + bool has_skin = header["skin"]["offset"].asInteger() >=0 && + header["skin"]["size"].asInteger() > 0; + + 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) && !mSubmodelID) + { + std::ios::pos_type cur_pos = is.tellg(); + loadDecomposition(header, is); + is.seekg(cur_pos); + } + + is.seekg(header[lod_name[lod]]["offset"].asInteger(), std::ios_base::cur); + + if (unpackVolumeFaces(is, header[lod_name[lod]]["size"].asInteger())) + { + if (has_skin) + { + //build out mSkinWeight from face info + for (S32 i = 0; i < getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = getVolumeFace(i); + + if (face.mWeights) + { + for (S32 j = 0; j < face.mNumVertices; ++j) + { + LLVector4a& w = face.mWeights[j]; + + std::vector<JointWeight> wght; + + for (S32 k = 0; k < 4; ++k) + { + S32 idx = (S32) w[k]; + F32 f = w[k] - idx; + if (f > 0.f) + { + wght.push_back(JointWeight(idx, f)); + } + } + + if (!wght.empty()) + { + LLVector3 pos(face.mPositions[j].getF32ptr()); + mSkinWeights[pos] = wght; + } + } + } + } + } + return true; + } + else + { + LL_WARNS("MESHSKININFO") << "unpackVolumeFaces failed!" << LL_ENDL; + } + + return false; +} + +bool LLModel::isMaterialListSubset( LLModel* ref ) +{ + int refCnt = ref->mMaterialList.size(); + int modelCnt = mMaterialList.size(); + + for (U32 src = 0; src < modelCnt; ++src) + { + bool foundRef = false; + + for (U32 dst = 0; dst < refCnt; ++dst) + { + //LL_INFOS()<<mMaterialList[src]<<" "<<ref->mMaterialList[dst]<<LL_ENDL; + foundRef = mMaterialList[src] == ref->mMaterialList[dst]; + + if ( foundRef ) + { + break; + } + } + + if (!foundRef) + { + LL_INFOS("MESHSKININFO") << "Could not find material " << mMaterialList[src] << " in reference model " << ref->mLabel << LL_ENDL; + return false; + } + } + + return true; +} + +bool LLModel::needToAddFaces( LLModel* ref, int& refFaceCnt, int& modelFaceCnt ) +{ + bool changed = false; + if ( refFaceCnt< modelFaceCnt ) + { + refFaceCnt += modelFaceCnt - refFaceCnt; + changed = true; + } + else + if ( modelFaceCnt < refFaceCnt ) + { + modelFaceCnt += refFaceCnt - modelFaceCnt; + changed = true; + } + + return changed; +} + +bool LLModel::matchMaterialOrder(LLModel* ref, int& refFaceCnt, int& modelFaceCnt ) +{ + //Is this a subset? + //LODs cannot currently add new materials, e.g. + //1. ref = a,b,c lod1 = d,e => This is not permitted + //2. ref = a,b,c lod1 = c => This would be permitted + + bool isASubset = isMaterialListSubset( ref ); + if ( !isASubset ) + { + LL_INFOS("MESHSKININFO")<<"Material of model is not a subset of reference."<<LL_ENDL; + return false; + } + + if (mMaterialList.size() > ref->mMaterialList.size()) + { + LL_INFOS("MESHSKININFO") << "Material of model has more materials than a reference." << LL_ENDL; + // We passed isMaterialListSubset, so materials are a subset, but subset isn't supposed to be + // larger than original and if we keep going, reordering will cause a crash + return false; + } + + std::map<std::string, U32> index_map; + + //build a map of material slot names to face indexes + bool reorder = false; + + std::set<std::string> base_mat; + std::set<std::string> cur_mat; + + for (U32 i = 0; i < mMaterialList.size(); i++) + { + index_map[ref->mMaterialList[i]] = 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 + { + std::vector<LLVolumeFace> new_face_list; + new_face_list.resize(mMaterialList.size()); + + std::vector<std::string> new_material_list; + 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]]; + + 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; + } + + return true; +} + +bool LLModel::loadSkinInfo(LLSD& header, std::istream &is) +{ + S32 offset = header["skin"]["offset"].asInteger(); + S32 size = header["skin"]["size"].asInteger(); + + if (offset >= 0 && size > 0) + { + is.seekg(offset, std::ios_base::cur); + + LLSD skin_data; + + if (LLUZipHelper::unzip_llsd(skin_data, is, size) == LLUZipHelper::ZR_OK) + { + mSkinInfo.fromLLSD(skin_data); + return true; + } + } + + return false; +} + +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 && !mSubmodelID) + { + is.seekg(offset, std::ios_base::cur); + + LLSD data; + + if (LLUZipHelper::unzip_llsd(data, is, size) == LLUZipHelper::ZR_OK) + { + mPhysics.fromLLSD(data); + updateHullCenters(); + } + } + + return true; +} + +LLMeshSkinInfo::LLMeshSkinInfo(): + mPelvisOffset(0.0), + mLockScaleIfJointPosition(false), + mInvalidJointsScrubbed(false), + mJointNumsInitialized(false) +{ +} + +LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin): + mPelvisOffset(0.0), + mLockScaleIfJointPosition(false), + mInvalidJointsScrubbed(false), + mJointNumsInitialized(false) +{ + fromLLSD(skin); +} + +LLMeshSkinInfo::LLMeshSkinInfo(const LLUUID& mesh_id, LLSD& skin) : + mMeshID(mesh_id), + mPelvisOffset(0.0), + mLockScaleIfJointPosition(false), + mInvalidJointsScrubbed(false), + mJointNumsInitialized(false) +{ + fromLLSD(skin); +} + +void LLMeshSkinInfo::fromLLSD(LLSD& skin) +{ + if (skin.has("joint_names")) + { + for (U32 i = 0; i < skin["joint_names"].size(); ++i) + { + mJointNames.push_back(skin["joint_names"][i]); + mJointNums.push_back(-1); + } + } + + if (skin.has("inverse_bind_matrix")) + { + for (U32 i = 0; i < skin["inverse_bind_matrix"].size(); ++i) + { + LLMatrix4 mat; + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + mat.mMatrix[j][k] = skin["inverse_bind_matrix"][i][j*4+k].asReal(); + } + } + + mInvBindMatrix.push_back(LLMatrix4a(mat)); + } + + if (mJointNames.size() != mInvBindMatrix.size()) + { + LL_WARNS("MESHSKININFO") << "Joints vs bind matrix count mismatch. Dropping joint bindings." << LL_ENDL; + mJointNames.clear(); + mJointNums.clear(); + mInvBindMatrix.clear(); + } + } + + if (skin.has("bind_shape_matrix")) + { + LLMatrix4 mat; + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + mat.mMatrix[j][k] = skin["bind_shape_matrix"][j*4+k].asReal(); + } + } + mBindShapeMatrix.loadu(mat); + } + + if (skin.has("alt_inverse_bind_matrix")) + { + for (U32 i = 0; i < skin["alt_inverse_bind_matrix"].size(); ++i) + { + LLMatrix4 mat; + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + mat.mMatrix[j][k] = skin["alt_inverse_bind_matrix"][i][j*4+k].asReal(); + } + } + + mAlternateBindMatrix.push_back(LLMatrix4a(mat)); + } + } + + if (skin.has("pelvis_offset")) + { + mPelvisOffset = skin["pelvis_offset"].asReal(); + } + + if (skin.has("lock_scale_if_joint_position")) + { + mLockScaleIfJointPosition = skin["lock_scale_if_joint_position"].asBoolean(); + } + else + { + mLockScaleIfJointPosition = false; + } + + updateHash(); +} + +LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_position) const +{ + LLSD ret; + + for (U32 i = 0; i < mJointNames.size(); ++i) + { + ret["joint_names"][i] = mJointNames[i]; + + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + ret["inverse_bind_matrix"][i][j*4+k] = mInvBindMatrix[i].mMatrix[j][k]; + } + } + } + + for (U32 i = 0; i < 4; i++) + { + for (U32 j = 0; j < 4; j++) + { + ret["bind_shape_matrix"][i*4+j] = mBindShapeMatrix.mMatrix[i][j]; + } + } + + if ( include_joints && mAlternateBindMatrix.size() > 0 ) + { + for (U32 i = 0; i < mJointNames.size(); ++i) + { + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + ret["alt_inverse_bind_matrix"][i][j*4+k] = mAlternateBindMatrix[i].mMatrix[j][k]; + } + } + } + + if (lock_scale_if_joint_position) + { + ret["lock_scale_if_joint_position"] = lock_scale_if_joint_position; + } + + ret["pelvis_offset"] = mPelvisOffset; + } + + return ret; +} + +void LLMeshSkinInfo::updateHash() +{ + // get hash of data relevant to render batches + HBXXH64 hash; + + //mJointNames + for (auto& name : mJointNames) + { + hash.update(name); + } + + //mJointNums + hash.update((const void*)mJointNums.data(), sizeof(S32) * mJointNums.size()); + + //mInvBindMatrix + F32* src = mInvBindMatrix[0].getF32ptr(); + + for (size_t i = 0, count = mInvBindMatrix.size() * 16; i < count; ++i) + { + S32 t = llround(src[i] * 10000.f); + hash.update((const void*)&t, sizeof(S32)); + } + //hash.update((const void*)mInvBindMatrix.data(), sizeof(LLMatrix4a) * mInvBindMatrix.size()); + + mHash = hash.digest(); +} + +U32 LLMeshSkinInfo::sizeBytes() const +{ + U32 res = sizeof(LLUUID); // mMeshID + + res += sizeof(std::vector<std::string>) + sizeof(std::string) * mJointNames.size(); + for (U32 i = 0; i < mJointNames.size(); ++i) + { + res += mJointNames[i].size(); // actual size, not capacity + } + + res += sizeof(std::vector<S32>) + sizeof(S32) * mJointNums.size(); + res += sizeof(std::vector<LLMatrix4>) + 16 * sizeof(float) * mInvBindMatrix.size(); + res += sizeof(std::vector<LLMatrix4>) + 16 * sizeof(float) * mAlternateBindMatrix.size(); + res += 16 * sizeof(float); //mBindShapeMatrix + res += sizeof(float) + 3 * sizeof(bool); + + return res; +} + +LLModel::Decomposition::Decomposition(LLSD& data) +{ + fromLLSD(data); +} + +void LLModel::Decomposition::fromLLSD(LLSD& decomp) +{ + if (decomp.has("HullList") && decomp.has("Positions")) + { + // updated for const-correctness. gcc is picky about this type of thing - Nyx + const LLSD::Binary& hulls = decomp["HullList"].asBinary(); + const LLSD::Binary& position = decomp["Positions"].asBinary(); + + U16* p = (U16*) &position[0]; + + mHull.resize(hulls.size()); + + LLVector3 min; + LLVector3 max; + LLVector3 range; + + if (decomp.has("Min")) + { + min.setValue(decomp["Min"]); + max.setValue(decomp["Max"]); + } + else + { + min.set(-0.5f, -0.5f, -0.5f); + max.set(0.5f, 0.5f, 0.5f); + } + + range = max-min; + + for (U32 i = 0; i < hulls.size(); ++i) + { + U16 count = (hulls[i] == 0) ? 256 : hulls[i]; + + std::set<U64> valid; + + //must have at least 4 points + //llassert(count > 3); + + for (U32 j = 0; j < count; ++j) + { + U64 test = (U64) p[0] | ((U64) p[1] << 16) | ((U64) p[2] << 32); + //point must be unique + //llassert(valid.find(test) == valid.end()); + valid.insert(test); + + mHull[i].push_back(LLVector3( + (F32) p[0]/65535.f*range.mV[0]+min.mV[0], + (F32) p[1]/65535.f*range.mV[1]+min.mV[1], + (F32) p[2]/65535.f*range.mV[2]+min.mV[2])); + p += 3; + + + } + + //each hull must contain at least 4 unique points + //llassert(valid.size() > 3); + } + } + + if (decomp.has("BoundingVerts")) + { + const LLSD::Binary& position = decomp["BoundingVerts"].asBinary(); + + U16* p = (U16*) &position[0]; + + LLVector3 min; + LLVector3 max; + LLVector3 range; + + if (decomp.has("Min")) + { + min.setValue(decomp["Min"]); + max.setValue(decomp["Max"]); + } + else + { + min.set(-0.5f, -0.5f, -0.5f); + max.set(0.5f, 0.5f, 0.5f); + } + + range = max-min; + + U16 count = (U16)(position.size()/6); + + for (U32 j = 0; j < count; ++j) + { + mBaseHull.push_back(LLVector3( + (F32) p[0]/65535.f*range.mV[0]+min.mV[0], + (F32) p[1]/65535.f*range.mV[1]+min.mV[1], + (F32) p[2]/65535.f*range.mV[2]+min.mV[2])); + p += 3; + } + } + else + { + //empty base hull mesh to indicate decomposition has been loaded + //but contains no base hull + mBaseHullMesh.clear(); + } +} + +U32 LLModel::Decomposition::sizeBytes() const +{ + U32 res = sizeof(LLUUID); // mMeshID + + res += sizeof(LLModel::convex_hull_decomposition) + sizeof(std::vector<LLVector3>) * mHull.size(); + for (U32 i = 0; i < mHull.size(); ++i) + { + res += mHull[i].size() * sizeof(LLVector3); + } + + res += sizeof(LLModel::hull) + sizeof(LLVector3) * mBaseHull.size(); + + res += sizeof(std::vector<LLModel::PhysicsMesh>) + sizeof(std::vector<LLModel::PhysicsMesh>) * mMesh.size(); + for (U32 i = 0; i < mMesh.size(); ++i) + { + res += mMesh[i].sizeBytes(); + } + + res += sizeof(std::vector<LLModel::PhysicsMesh>) * 2; + res += mBaseHullMesh.sizeBytes() + mPhysicsShapeMesh.sizeBytes(); + + return res; +} + +bool LLModel::Decomposition::hasHullList() const +{ + return !mHull.empty() ; +} + +LLSD LLModel::Decomposition::asLLSD() const +{ + LLSD ret; + + if (mBaseHull.empty() && mHull.empty()) + { //nothing to write + return ret; + } + + //write decomposition block + // ["physics_convex"]["HullList"] -- list of 8 bit integers, each entry represents a hull with specified number of points + // ["physics_convex"]["Position"] -- list of 16-bit integers to be decoded to given domain, encoded 3D points + // ["physics_convex"]["BoundingVerts"] -- list of 16-bit integers to be decoded to given domain, encoded 3D points representing a single hull approximation of given shape + + //get minimum and maximum + LLVector3 min; + + if (mHull.empty()) + { + min = mBaseHull[0]; + } + else + { + min = mHull[0][0]; + } + + LLVector3 max = min; + + LLSD::Binary hulls(mHull.size()); + + U32 total = 0; + + for (U32 i = 0; i < mHull.size(); ++i) + { + U32 size = mHull[i].size(); + total += size; + hulls[i] = (U8) (size); + + for (U32 j = 0; j < mHull[i].size(); ++j) + { + update_min_max(min, max, mHull[i][j]); + } + } + + for (U32 i = 0; i < mBaseHull.size(); ++i) + { + update_min_max(min, max, mBaseHull[i]); + } + + ret["Min"] = min.getValue(); + ret["Max"] = max.getValue(); + + LLVector3 range = max-min; + + if (!hulls.empty()) + { + ret["HullList"] = hulls; + } + + if (total > 0) + { + LLSD::Binary p(total*3*2); + + U32 vert_idx = 0; + + for (U32 i = 0; i < mHull.size(); ++i) + { + std::set<U64> valid; + + llassert(!mHull[i].empty()); + + for (U32 j = 0; j < mHull[i].size(); ++j) + { + U64 test = 0; + const F32* src = mHull[i][j].mV; + + for (U32 k = 0; k < 3; k++) + { + //convert to 16-bit normalized across domain + U16 val = (U16) (((src[k]-min.mV[k])/range.mV[k])*65535); + + if(valid.size() < 3) + { + switch (k) + { + case 0: test = test | (U64) val; break; + case 1: test = test | ((U64) val << 16); break; + case 2: test = test | ((U64) val << 32); break; + }; + + valid.insert(test); + } + + U8* buff = (U8*) &val; + //write to binary buffer + p[vert_idx++] = buff[0]; + p[vert_idx++] = buff[1]; + + //makes sure we haven't run off the end of the array + llassert(vert_idx <= p.size()); + } + } + + //must have at least 3 unique points + llassert(valid.size() > 2); + } + + ret["Positions"] = p; + } + + //llassert(!mBaseHull.empty()); + + if (!mBaseHull.empty()) + { + LLSD::Binary p(mBaseHull.size()*3*2); + + U32 vert_idx = 0; + for (U32 j = 0; j < mBaseHull.size(); ++j) + { + const F32* v = mBaseHull[j].mV; + + for (U32 k = 0; k < 3; k++) + { + //convert to 16-bit normalized across domain + U16 val = (U16) (((v[k]-min.mV[k])/range.mV[k])*65535); + + U8* buff = (U8*) &val; + //write to binary buffer + p[vert_idx++] = buff[0]; + p[vert_idx++] = buff[1]; + + if (vert_idx > p.size()) + { + LL_ERRS() << "Index out of bounds" << LL_ENDL; + } + } + } + + ret["BoundingVerts"] = p; + } + + return ret; +} + +void LLModel::Decomposition::merge(const LLModel::Decomposition* rhs) +{ + if (!rhs) + { + return; + } + + if (mMeshID != rhs->mMeshID) + { + LL_ERRS() << "Attempted to merge with decomposition of some other mesh." << LL_ENDL; + } + + if (mBaseHull.empty()) + { //take base hull and decomposition from rhs + mHull = rhs->mHull; + mBaseHull = rhs->mBaseHull; + mMesh = rhs->mMesh; + mBaseHullMesh = rhs->mBaseHullMesh; + } + + if (mPhysicsShapeMesh.empty()) + { //take physics shape mesh from rhs + mPhysicsShapeMesh = rhs->mPhysicsShapeMesh; + } +} + +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("MESHSKININFO") << "Face has invalid index." << LL_ENDL; + return false; + } + } + + if (face.mNumIndices % 3 != 0 || face.mNumIndices == 0) + { + LL_WARNS("MESHSKININFO") << "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("MESHSKININFO") << "Model has no faces!" << LL_ENDL; + return false; + } + + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + if (mdl->getVolumeFace(i).mNumVertices == 0) + { + LL_WARNS("MESHSKININFO") << "Face has no vertices." << LL_ENDL; + return false; + } + + if (mdl->getVolumeFace(i).mNumIndices == 0) + { + LL_WARNS("MESHSKININFO") << "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 9f3479f26b..ae195525ef 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -1,435 +1,435 @@ -/**
- * @file llmodel.h
- * @brief Model handling class definitions
- *
- * $LicenseInfo:firstyear=2001&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_LLMODEL_H
-#define LL_LLMODEL_H
-
-#include "llpointer.h"
-#include "llvolume.h"
-#include "v4math.h"
-#include "m4math.h"
-#include <queue>
-
-#include <boost/align/aligned_allocator.hpp>
-
-class daeElement;
-class domMesh;
-
-#define MAX_MODEL_FACES 8
-
-LL_ALIGN_PREFIX(16)
-class LLMeshSkinInfo : public LLRefCount
-{
- LL_ALIGN_NEW
-public:
- LLMeshSkinInfo();
- LLMeshSkinInfo(LLSD& data);
- LLMeshSkinInfo(const LLUUID& mesh_id, LLSD& data);
- void fromLLSD(LLSD& data);
- LLSD asLLSD(bool include_joints, bool lock_scale_if_joint_position) const;
- void updateHash();
- U32 sizeBytes() const;
-
- LLUUID mMeshID;
- std::vector<std::string> mJointNames;
- mutable std::vector<S32> mJointNums;
- typedef std::vector<LLMatrix4a, boost::alignment::aligned_allocator<LLMatrix4a, 16>> matrix_list_t;
- matrix_list_t mInvBindMatrix;
-
- // bones/joints position overrides
- matrix_list_t mAlternateBindMatrix;
-
- LL_ALIGN_16(LLMatrix4a mBindShapeMatrix);
-
- float mPelvisOffset;
- bool mLockScaleIfJointPosition;
- bool mInvalidJointsScrubbed;
- bool mJointNumsInitialized;
- U64 mHash = 0;
-} LL_ALIGN_POSTFIX(16);
-
-LL_ALIGN_PREFIX(16)
-class LLModel : public LLVolume
-{
- LL_ALIGN_NEW
-public:
-
- enum
- {
- LOD_IMPOSTOR = 0,
- LOD_LOW,
- LOD_MEDIUM,
- LOD_HIGH,
- LOD_PHYSICS,
- NUM_LODS
- };
-
- enum EModelStatus
- {
- NO_ERRORS = 0,
- VERTEX_NUMBER_OVERFLOW, //vertex number is >= 65535.
- BAD_ELEMENT,
- INVALID_STATUS
- } ;
-
- //convex_hull_decomposition is a vector of convex hulls
- //each convex hull is a set of points
- typedef std::vector<std::vector<LLVector3> > convex_hull_decomposition;
- typedef std::vector<LLVector3> hull;
-
- class PhysicsMesh
- {
- public:
- std::vector<LLVector3> mPositions;
- std::vector<LLVector3> mNormals;
-
- ~PhysicsMesh() {}
-
- void clear()
- {
- mPositions.clear();
- mNormals.clear();
- }
-
- bool empty() const
- {
- return mPositions.empty();
- }
-
- U32 sizeBytes() const
- {
- U32 res = sizeof(std::vector<LLVector3>) * 2;
- res += sizeof(LLVector3) * mPositions.size();
- res += sizeof(LLVector3) * mNormals.size();
- return res;
- }
- };
-
- class Decomposition
- {
- public:
- Decomposition() { }
- Decomposition(LLSD& data);
- ~Decomposition() { }
- void fromLLSD(LLSD& data);
- LLSD asLLSD() const;
- bool hasHullList() const;
- U32 sizeBytes() const;
-
- void merge(const Decomposition* rhs);
-
- LLUUID mMeshID;
- LLModel::convex_hull_decomposition mHull;
- LLModel::hull mBaseHull;
-
- std::vector<LLModel::PhysicsMesh> mMesh;
- LLModel::PhysicsMesh mBaseHullMesh;
- LLModel::PhysicsMesh mPhysicsShapeMesh;
- };
-
- LLModel(const LLVolumeParams& params, F32 detail);
- ~LLModel();
-
- bool loadModel(std::istream& is);
- bool loadSkinInfo(LLSD& header, std::istream& is);
- bool loadDecomposition(LLSD& header, std::istream& is);
-
- static LLSD writeModel(
- std::ostream& ostr,
- LLModel* physics,
- LLModel* high,
- LLModel* medium,
- LLModel* low,
- LLModel* imposotr,
- const LLModel::Decomposition& decomp,
- bool upload_skin,
- bool upload_joints,
- bool lock_scale_if_joint_position,
- bool nowrite = 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(); }
-
- std::string getName() const;
- EModelStatus getStatus() const {return mStatus;}
- static std::string getStatusString(U32 status) ;
-
- void setNumVolumeFaces(S32 count);
- void setVolumeFaceData(
- S32 f,
- LLStrider<LLVector3> pos,
- LLStrider<LLVector3> norm,
- LLStrider<LLVector2> tc,
- LLStrider<U16> ind,
- U32 num_verts,
- U32 num_indices);
-
- void generateNormals(F32 angle_cutoff);
-
- 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 remapVolumeFaces();
- 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 );
-
- typedef std::vector<std::string> material_list;
-
- material_list mMaterialList;
-
- material_list& getMaterialList() { return mMaterialList; }
-
- //data used for skin weights
- class JointWeight
- {
- public:
- S32 mJointIdx;
- F32 mWeight;
-
- JointWeight()
- {
- mJointIdx = 0;
- mWeight = 0.f;
- }
-
- JointWeight(S32 idx, F32 weight)
- : mJointIdx(idx), mWeight(weight)
- {
- }
-
- bool operator<(const JointWeight& rhs) const
- {
- if (mWeight == rhs.mWeight)
- {
- return mJointIdx < rhs.mJointIdx;
- }
-
- return mWeight < rhs.mWeight;
- }
-
- };
-
- struct CompareWeightGreater
- {
- bool operator()(const JointWeight& lhs, const JointWeight& rhs)
- {
- return rhs < lhs; // strongest = first
- }
- };
-
- //Are the doubles the same w/in epsilon specified tolerance
- bool areEqual( double a, double b )
- {
- const float epsilon = 1e-5f;
- return fabs(a - b) < epsilon;
- }
-
- //Make sure that we return false for any values that are within the tolerance for equivalence
- bool jointPositionalLookup( const LLVector3& a, const LLVector3& b )
- {
- const float epsilon = 1e-5f;
- return (a - b).length() < epsilon;
- }
-
- //copy of position array for this model -- mPosition[idx].mV[X,Y,Z]
- std::vector<LLVector3> mPosition;
-
- //map of positions to skin weights --- mSkinWeights[pos].mV[0..4] == <joint_index>.<weight>
- //joint_index corresponds to mJointList
- typedef std::vector<JointWeight> weight_list;
- typedef std::map<LLVector3, weight_list > weight_map;
- weight_map mSkinWeights;
-
- //get list of weight influences closest to given position
- weight_list& getJointInfluences(const LLVector3& pos);
-
- LLMeshSkinInfo mSkinInfo;
-
- std::string mRequestedLabel; // name requested in UI, if any.
- std::string mLabel; // name computed from dae.
-
- LLVector3 mNormalizedScale;
- LLVector3 mNormalizedTranslation;
-
- float mPelvisOffset;
- // convex hull decomposition
- S32 mDecompID;
-
- void setConvexHullDecomposition(
- const convex_hull_decomposition& decomp);
- void updateHullCenters();
-
- LLVector3 mCenterOfHullCenters;
- std::vector<LLVector3> mHullCenter;
- U32 mHullPoints;
-
- //ID for storing this model in a .slm file
- S32 mLocalID;
-
- Decomposition mPhysics;
-
- EModelStatus mStatus ;
-
- // A model/object can only have 8 faces, spillover faces will
- // be moved to new model/object and assigned a submodel id.
- int mSubmodelID;
-} LL_ALIGN_POSTFIX(16);
-
-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 ¶ms) 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:
-
- 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[LLModel::NUM_LODS];
- LLUUID mMeshID;
-
- LLMatrix4 mTransform;
- material_map mMaterial;
-
- LLModelInstanceBase(LLModel* model, const LLMatrix4& transform, const material_map& materials)
- : mModel(model), mTransform(transform), mMaterial(materials)
- {
- }
-
- LLModelInstanceBase()
- : mModel(NULL)
- {
- }
-
- virtual ~LLModelInstanceBase()
- {
- mModel = NULL;
- for (int j = 0; j < LLModel::NUM_LODS; ++j)
- {
- mLOD[j] = 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, const LLMatrix4& transform, const material_map& materials)
- : LLModelInstanceBase(model, transform, materials), mLabel(label)
- {
- mLocalMeshID = -1;
- }
-
- LLModelInstance(LLSD& data);
-
- ~LLModelInstance() {}
-
- 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
+/** + * @file llmodel.h + * @brief Model handling class definitions + * + * $LicenseInfo:firstyear=2001&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_LLMODEL_H +#define LL_LLMODEL_H + +#include "llpointer.h" +#include "llvolume.h" +#include "v4math.h" +#include "m4math.h" +#include <queue> + +#include <boost/align/aligned_allocator.hpp> + +class daeElement; +class domMesh; + +#define MAX_MODEL_FACES 8 + +LL_ALIGN_PREFIX(16) +class LLMeshSkinInfo : public LLRefCount +{ + LL_ALIGN_NEW +public: + LLMeshSkinInfo(); + LLMeshSkinInfo(LLSD& data); + LLMeshSkinInfo(const LLUUID& mesh_id, LLSD& data); + void fromLLSD(LLSD& data); + LLSD asLLSD(bool include_joints, bool lock_scale_if_joint_position) const; + void updateHash(); + U32 sizeBytes() const; + + LLUUID mMeshID; + std::vector<std::string> mJointNames; + mutable std::vector<S32> mJointNums; + typedef std::vector<LLMatrix4a, boost::alignment::aligned_allocator<LLMatrix4a, 16>> matrix_list_t; + matrix_list_t mInvBindMatrix; + + // bones/joints position overrides + matrix_list_t mAlternateBindMatrix; + + LL_ALIGN_16(LLMatrix4a mBindShapeMatrix); + + float mPelvisOffset; + bool mLockScaleIfJointPosition; + bool mInvalidJointsScrubbed; + bool mJointNumsInitialized; + U64 mHash = 0; +} LL_ALIGN_POSTFIX(16); + +LL_ALIGN_PREFIX(16) +class LLModel : public LLVolume +{ + LL_ALIGN_NEW +public: + + enum + { + LOD_IMPOSTOR = 0, + LOD_LOW, + LOD_MEDIUM, + LOD_HIGH, + LOD_PHYSICS, + NUM_LODS + }; + + enum EModelStatus + { + NO_ERRORS = 0, + VERTEX_NUMBER_OVERFLOW, //vertex number is >= 65535. + BAD_ELEMENT, + INVALID_STATUS + } ; + + //convex_hull_decomposition is a vector of convex hulls + //each convex hull is a set of points + typedef std::vector<std::vector<LLVector3> > convex_hull_decomposition; + typedef std::vector<LLVector3> hull; + + class PhysicsMesh + { + public: + std::vector<LLVector3> mPositions; + std::vector<LLVector3> mNormals; + + ~PhysicsMesh() {} + + void clear() + { + mPositions.clear(); + mNormals.clear(); + } + + bool empty() const + { + return mPositions.empty(); + } + + U32 sizeBytes() const + { + U32 res = sizeof(std::vector<LLVector3>) * 2; + res += sizeof(LLVector3) * mPositions.size(); + res += sizeof(LLVector3) * mNormals.size(); + return res; + } + }; + + class Decomposition + { + public: + Decomposition() { } + Decomposition(LLSD& data); + ~Decomposition() { } + void fromLLSD(LLSD& data); + LLSD asLLSD() const; + bool hasHullList() const; + U32 sizeBytes() const; + + void merge(const Decomposition* rhs); + + LLUUID mMeshID; + LLModel::convex_hull_decomposition mHull; + LLModel::hull mBaseHull; + + std::vector<LLModel::PhysicsMesh> mMesh; + LLModel::PhysicsMesh mBaseHullMesh; + LLModel::PhysicsMesh mPhysicsShapeMesh; + }; + + LLModel(const LLVolumeParams& params, F32 detail); + ~LLModel(); + + bool loadModel(std::istream& is); + bool loadSkinInfo(LLSD& header, std::istream& is); + bool loadDecomposition(LLSD& header, std::istream& is); + + static LLSD writeModel( + std::ostream& ostr, + LLModel* physics, + LLModel* high, + LLModel* medium, + LLModel* low, + LLModel* imposotr, + const LLModel::Decomposition& decomp, + bool upload_skin, + bool upload_joints, + bool lock_scale_if_joint_position, + bool nowrite = 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(); } + + std::string getName() const; + EModelStatus getStatus() const {return mStatus;} + static std::string getStatusString(U32 status) ; + + void setNumVolumeFaces(S32 count); + void setVolumeFaceData( + S32 f, + LLStrider<LLVector3> pos, + LLStrider<LLVector3> norm, + LLStrider<LLVector2> tc, + LLStrider<U16> ind, + U32 num_verts, + U32 num_indices); + + void generateNormals(F32 angle_cutoff); + + 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 remapVolumeFaces(); + 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 ); + + typedef std::vector<std::string> material_list; + + material_list mMaterialList; + + material_list& getMaterialList() { return mMaterialList; } + + //data used for skin weights + class JointWeight + { + public: + S32 mJointIdx; + F32 mWeight; + + JointWeight() + { + mJointIdx = 0; + mWeight = 0.f; + } + + JointWeight(S32 idx, F32 weight) + : mJointIdx(idx), mWeight(weight) + { + } + + bool operator<(const JointWeight& rhs) const + { + if (mWeight == rhs.mWeight) + { + return mJointIdx < rhs.mJointIdx; + } + + return mWeight < rhs.mWeight; + } + + }; + + struct CompareWeightGreater + { + bool operator()(const JointWeight& lhs, const JointWeight& rhs) + { + return rhs < lhs; // strongest = first + } + }; + + //Are the doubles the same w/in epsilon specified tolerance + bool areEqual( double a, double b ) + { + const float epsilon = 1e-5f; + return fabs(a - b) < epsilon; + } + + //Make sure that we return false for any values that are within the tolerance for equivalence + bool jointPositionalLookup( const LLVector3& a, const LLVector3& b ) + { + const float epsilon = 1e-5f; + return (a - b).length() < epsilon; + } + + //copy of position array for this model -- mPosition[idx].mV[X,Y,Z] + std::vector<LLVector3> mPosition; + + //map of positions to skin weights --- mSkinWeights[pos].mV[0..4] == <joint_index>.<weight> + //joint_index corresponds to mJointList + typedef std::vector<JointWeight> weight_list; + typedef std::map<LLVector3, weight_list > weight_map; + weight_map mSkinWeights; + + //get list of weight influences closest to given position + weight_list& getJointInfluences(const LLVector3& pos); + + LLMeshSkinInfo mSkinInfo; + + std::string mRequestedLabel; // name requested in UI, if any. + std::string mLabel; // name computed from dae. + + LLVector3 mNormalizedScale; + LLVector3 mNormalizedTranslation; + + float mPelvisOffset; + // convex hull decomposition + S32 mDecompID; + + void setConvexHullDecomposition( + const convex_hull_decomposition& decomp); + void updateHullCenters(); + + LLVector3 mCenterOfHullCenters; + std::vector<LLVector3> mHullCenter; + U32 mHullPoints; + + //ID for storing this model in a .slm file + S32 mLocalID; + + Decomposition mPhysics; + + EModelStatus mStatus ; + + // A model/object can only have 8 faces, spillover faces will + // be moved to new model/object and assigned a submodel id. + int mSubmodelID; +} LL_ALIGN_POSTFIX(16); + +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 ¶ms) 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: + + 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[LLModel::NUM_LODS]; + LLUUID mMeshID; + + LLMatrix4 mTransform; + material_map mMaterial; + + LLModelInstanceBase(LLModel* model, const LLMatrix4& transform, const material_map& materials) + : mModel(model), mTransform(transform), mMaterial(materials) + { + } + + LLModelInstanceBase() + : mModel(NULL) + { + } + + virtual ~LLModelInstanceBase() + { + mModel = NULL; + for (int j = 0; j < LLModel::NUM_LODS; ++j) + { + mLOD[j] = 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, const LLMatrix4& transform, const material_map& materials) + : LLModelInstanceBase(model, transform, materials), mLabel(label) + { + mLocalMeshID = -1; + } + + LLModelInstance(LLSD& data); + + ~LLModelInstance() {} + + 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 index 1e94b5bf92..9a194567d2 100644 --- a/indra/llprimitive/llmodelloader.cpp +++ b/indra/llprimitive/llmodelloader.cpp @@ -1,505 +1,505 @@ -/**
- * @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 "llapp.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& jointTransformMap,
- JointNameSet& jointsFromNodes,
- JointMap& legalJointNamesMap,
- U32 maxJointsPerMesh)
-: mJointList( jointTransformMap )
-, mJointsFromNode( jointsFromNodes )
-, LLThread("Model Loader")
-, mFilename(filename)
-, mLod(lod)
-, mTrySLM(false)
-, mFirstTransform(true)
-, mNumOfFetchingTextures(0)
-, mLoadCallback(load_cb)
-, mJointLookupFunc(joint_lookup_func)
-, mTextureLoadFunc(texture_load_func)
-, mStateCallback(state_cb)
-, mOpaqueData(opaque_userdata)
-, mRigValidJointUpload(true)
-, mLegacyRigFlags(0)
-, mNoNormalize(false)
-, mNoOptimize(false)
-, mCacheOnlyHitIfRigged(false)
-, mMaxJointsPerMesh(maxJointsPerMesh)
-, mJointMap(legalJointNamesMap)
-{
- assert_main_thread();
- sActiveLoaderList.push_back(this) ;
- mWarningsArray = LLSD::emptyArray();
-}
-
-LLModelLoader::~LLModelLoader()
-{
- assert_main_thread();
- sActiveLoaderList.remove(this);
-}
-
-void LLModelLoader::run()
-{
- mWarningsArray.clear();
- doLoadModel();
- doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
-}
-
-// static
-bool LLModelLoader::getSLMFilename(const std::string& model_filename, std::string& slm_filename)
-{
- slm_filename = model_filename;
-
- std::string::size_type i = model_filename.rfind(".");
- if (i != std::string::npos)
- {
- slm_filename.resize(i, '\0');
- slm_filename.append(".slm");
- return true;
- }
- else
- {
- return false;
- }
-}
-
-bool LLModelLoader::doLoadModel()
-{
- //first, look for a .slm file of the same name that was modified later
- //than the specified model file
-
- if (mTrySLM)
- {
- std::string slm_filename;
- if (getSLMFilename(mFilename, slm_filename))
- {
- llstat slm_status;
- if (LLFile::stat(slm_filename, &slm_status) == 0)
- { //slm file exists
- llstat model_file_status;
- if (LLFile::stat(mFilename, &model_file_status) != 0 ||
- model_file_status.st_mtime < slm_status.st_mtime)
- {
- if (loadFromSLM(slm_filename))
- { //slm successfully loaded, if this fails, fall through and
- //try loading from model file
-
- 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()
-{
- if (!LLApp::isExiting())
- {
- 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 )
-{
- //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 );
- U32 legacy_rig_flags = determineRigLegacyFlags( jointListFromAsset );
-
- // It's OK that both could end up being true.
-
- // Both start out as true and are forced to false if any mesh in
- // the model file is not vald by that criterion. Note that a file
- // can contain multiple meshes.
- if ( !isJointPositionUploadOK )
- {
- // This starts out true, becomes false if false for any loaded
- // mesh.
- setRigValidForJointPositionUpload( false );
- }
-
- legacy_rig_flags |= getLegacyRigFlags();
- // This starts as 0, changes if any loaded mesh has issues
- setLegacyRigFlags(legacy_rig_flags);
-
-}
-
-//-----------------------------------------------------------------------------
-// determineRigLegacyFlags()
-//-----------------------------------------------------------------------------
-U32 LLModelLoader::determineRigLegacyFlags( const std::vector<std::string> &jointListFromAsset )
-{
- //No joints in asset
- if ( jointListFromAsset.size() == 0 )
- {
- return false;
- }
-
- // Too many joints in asset
- if (jointListFromAsset.size()>mMaxJointsPerMesh)
- {
- LL_WARNS() << "Rigged to " << jointListFromAsset.size() << " joints, max is " << mMaxJointsPerMesh << LL_ENDL;
- LL_WARNS() << "Skinning disabled due to too many joints" << LL_ENDL;
- LLSD args;
- args["Message"] = "TooManyJoint";
- args["[JOINTS]"] = LLSD::Integer(jointListFromAsset.size());
- args["[MAX]"] = LLSD::Integer(mMaxJointsPerMesh);
- mWarningsArray.append(args);
- return LEGACY_RIG_FLAG_TOO_MANY_JOINTS;
- }
-
- // Unknown joints in asset
- S32 unknown_joint_count = 0;
- for (std::vector<std::string>::const_iterator it = jointListFromAsset.begin();
- it != jointListFromAsset.end(); ++it)
- {
- if (mJointMap.find(*it)==mJointMap.end())
- {
- LL_WARNS() << "Rigged to unrecognized joint name " << *it << LL_ENDL;
- LLSD args;
- args["Message"] = "UnrecognizedJoint";
- args["[NAME]"] = *it;
- mWarningsArray.append(args);
- unknown_joint_count++;
- }
- }
- if (unknown_joint_count>0)
- {
- LL_WARNS() << "Skinning disabled due to unknown joints" << LL_ENDL;
- LLSD args;
- args["Message"] = "UnknownJoints";
- args["[COUNT]"] = LLSD::Integer(unknown_joint_count);
- mWarningsArray.append(args);
- return LEGACY_RIG_FLAG_UNKNOWN_JOINT;
- }
-
- return LEGACY_RIG_OK;
-}
-//-----------------------------------------------------------------------------
-// isRigSuitableForJointPositionUpload()
-//-----------------------------------------------------------------------------
-bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset )
-{
- return true;
-}
-
-
-//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() ;
- }
-}
+/** + * @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 "llapp.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& jointTransformMap, + JointNameSet& jointsFromNodes, + JointMap& legalJointNamesMap, + U32 maxJointsPerMesh) +: mJointList( jointTransformMap ) +, mJointsFromNode( jointsFromNodes ) +, LLThread("Model Loader") +, mFilename(filename) +, mLod(lod) +, mTrySLM(false) +, mFirstTransform(true) +, mNumOfFetchingTextures(0) +, mLoadCallback(load_cb) +, mJointLookupFunc(joint_lookup_func) +, mTextureLoadFunc(texture_load_func) +, mStateCallback(state_cb) +, mOpaqueData(opaque_userdata) +, mRigValidJointUpload(true) +, mLegacyRigFlags(0) +, mNoNormalize(false) +, mNoOptimize(false) +, mCacheOnlyHitIfRigged(false) +, mMaxJointsPerMesh(maxJointsPerMesh) +, mJointMap(legalJointNamesMap) +{ + assert_main_thread(); + sActiveLoaderList.push_back(this) ; + mWarningsArray = LLSD::emptyArray(); +} + +LLModelLoader::~LLModelLoader() +{ + assert_main_thread(); + sActiveLoaderList.remove(this); +} + +void LLModelLoader::run() +{ + mWarningsArray.clear(); + doLoadModel(); + doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this)); +} + +// static +bool LLModelLoader::getSLMFilename(const std::string& model_filename, std::string& slm_filename) +{ + slm_filename = model_filename; + + std::string::size_type i = model_filename.rfind("."); + if (i != std::string::npos) + { + slm_filename.resize(i, '\0'); + slm_filename.append(".slm"); + return true; + } + else + { + return false; + } +} + +bool LLModelLoader::doLoadModel() +{ + //first, look for a .slm file of the same name that was modified later + //than the specified model file + + if (mTrySLM) + { + std::string slm_filename; + if (getSLMFilename(mFilename, slm_filename)) + { + llstat slm_status; + if (LLFile::stat(slm_filename, &slm_status) == 0) + { //slm file exists + llstat model_file_status; + if (LLFile::stat(mFilename, &model_file_status) != 0 || + model_file_status.st_mtime < slm_status.st_mtime) + { + if (loadFromSLM(slm_filename)) + { //slm successfully loaded, if this fails, fall through and + //try loading from model file + + 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() +{ + if (!LLApp::isExiting()) + { + 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 ) +{ + //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 ); + U32 legacy_rig_flags = determineRigLegacyFlags( jointListFromAsset ); + + // It's OK that both could end up being true. + + // Both start out as true and are forced to false if any mesh in + // the model file is not vald by that criterion. Note that a file + // can contain multiple meshes. + if ( !isJointPositionUploadOK ) + { + // This starts out true, becomes false if false for any loaded + // mesh. + setRigValidForJointPositionUpload( false ); + } + + legacy_rig_flags |= getLegacyRigFlags(); + // This starts as 0, changes if any loaded mesh has issues + setLegacyRigFlags(legacy_rig_flags); + +} + +//----------------------------------------------------------------------------- +// determineRigLegacyFlags() +//----------------------------------------------------------------------------- +U32 LLModelLoader::determineRigLegacyFlags( const std::vector<std::string> &jointListFromAsset ) +{ + //No joints in asset + if ( jointListFromAsset.size() == 0 ) + { + return false; + } + + // Too many joints in asset + if (jointListFromAsset.size()>mMaxJointsPerMesh) + { + LL_WARNS() << "Rigged to " << jointListFromAsset.size() << " joints, max is " << mMaxJointsPerMesh << LL_ENDL; + LL_WARNS() << "Skinning disabled due to too many joints" << LL_ENDL; + LLSD args; + args["Message"] = "TooManyJoint"; + args["[JOINTS]"] = LLSD::Integer(jointListFromAsset.size()); + args["[MAX]"] = LLSD::Integer(mMaxJointsPerMesh); + mWarningsArray.append(args); + return LEGACY_RIG_FLAG_TOO_MANY_JOINTS; + } + + // Unknown joints in asset + S32 unknown_joint_count = 0; + for (std::vector<std::string>::const_iterator it = jointListFromAsset.begin(); + it != jointListFromAsset.end(); ++it) + { + if (mJointMap.find(*it)==mJointMap.end()) + { + LL_WARNS() << "Rigged to unrecognized joint name " << *it << LL_ENDL; + LLSD args; + args["Message"] = "UnrecognizedJoint"; + args["[NAME]"] = *it; + mWarningsArray.append(args); + unknown_joint_count++; + } + } + if (unknown_joint_count>0) + { + LL_WARNS() << "Skinning disabled due to unknown joints" << LL_ENDL; + LLSD args; + args["Message"] = "UnknownJoints"; + args["[COUNT]"] = LLSD::Integer(unknown_joint_count); + mWarningsArray.append(args); + return LEGACY_RIG_FLAG_UNKNOWN_JOINT; + } + + return LEGACY_RIG_OK; +} +//----------------------------------------------------------------------------- +// isRigSuitableForJointPositionUpload() +//----------------------------------------------------------------------------- +bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset ) +{ + return true; +} + + +//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 index 2de3d85959..83bd2f5bbd 100644 --- a/indra/llprimitive/llmodelloader.h +++ b/indra/llprimitive/llmodelloader.h @@ -1,221 +1,221 @@ -/**
- * @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> JointNameSet;
-
-const S32 SLM_SUPPORTED_VERSION = 3;
-const S32 NUM_LOD = 4;
-
-const U32 LEGACY_RIG_OK = 0;
-const U32 LEGACY_RIG_FLAG_TOO_MANY_JOINTS = 1;
-const U32 LEGACY_RIG_FLAG_UNKNOWN_JOINT = 2;
-
-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,
- WARNING_BIND_SHAPE_ORIENTATION,
- 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;
- JointNameSet& mJointsFromNode;
- U32 mMaxJointsPerMesh;
-
- 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& jointTransformMap,
- JointNameSet& jointsFromNodes,
- JointMap& legalJointNamesMap,
- U32 maxJointsPerMesh);
- virtual ~LLModelLoader() ;
-
- virtual void setNoNormalize() { mNoNormalize = true; }
- virtual void setNoOptimize() { mNoOptimize = true; }
-
- virtual void run();
-
- static bool getSLMFilename(const std::string& model_filename, std::string& slm_filename);
-
- // 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 );
-
- //Determines if a rig is a legacy from the joint list
- U32 determineRigLegacyFlags( const std::vector<std::string> &jointListFromAsset );
-
- //Determines if a rig is suitable for upload
- bool isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset );
-
- const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; }
- void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; }
-
- const bool isLegacyRigValid(void) const { return mLegacyRigFlags == 0; }
- U32 getLegacyRigFlags() const { return mLegacyRigFlags; }
- void setLegacyRigFlags( U32 rigFlags ) { mLegacyRigFlags = rigFlags; }
-
- //-----------------------------------------------------------------------------
- // isNodeAJoint()
- //-----------------------------------------------------------------------------
- bool isNodeAJoint(const char* name)
- {
- return name != NULL && mJointMap.find(name) != mJointMap.end();
- }
-
- const LLSD logOut() const { return mWarningsArray; }
- void clearLog() { mWarningsArray.clear(); }
-
-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 mRigValidJointUpload;
- U32 mLegacyRigFlags;
-
- bool mNoNormalize;
- bool mNoOptimize;
-
- JointTransformMap mJointTransformMap;
-
- LLSD mWarningsArray; // preview floater will pull logs from here
-
- 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
+/** + * @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> JointNameSet; + +const S32 SLM_SUPPORTED_VERSION = 3; +const S32 NUM_LOD = 4; + +const U32 LEGACY_RIG_OK = 0; +const U32 LEGACY_RIG_FLAG_TOO_MANY_JOINTS = 1; +const U32 LEGACY_RIG_FLAG_UNKNOWN_JOINT = 2; + +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, + WARNING_BIND_SHAPE_ORIENTATION, + 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; + JointNameSet& mJointsFromNode; + U32 mMaxJointsPerMesh; + + 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& jointTransformMap, + JointNameSet& jointsFromNodes, + JointMap& legalJointNamesMap, + U32 maxJointsPerMesh); + virtual ~LLModelLoader() ; + + virtual void setNoNormalize() { mNoNormalize = true; } + virtual void setNoOptimize() { mNoOptimize = true; } + + virtual void run(); + + static bool getSLMFilename(const std::string& model_filename, std::string& slm_filename); + + // 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 ); + + //Determines if a rig is a legacy from the joint list + U32 determineRigLegacyFlags( const std::vector<std::string> &jointListFromAsset ); + + //Determines if a rig is suitable for upload + bool isRigSuitableForJointPositionUpload( const std::vector<std::string> &jointListFromAsset ); + + const bool isRigValidForJointPositionUpload( void ) const { return mRigValidJointUpload; } + void setRigValidForJointPositionUpload( bool rigValid ) { mRigValidJointUpload = rigValid; } + + const bool isLegacyRigValid(void) const { return mLegacyRigFlags == 0; } + U32 getLegacyRigFlags() const { return mLegacyRigFlags; } + void setLegacyRigFlags( U32 rigFlags ) { mLegacyRigFlags = rigFlags; } + + //----------------------------------------------------------------------------- + // isNodeAJoint() + //----------------------------------------------------------------------------- + bool isNodeAJoint(const char* name) + { + return name != NULL && mJointMap.find(name) != mJointMap.end(); + } + + const LLSD logOut() const { return mWarningsArray; } + void clearLog() { mWarningsArray.clear(); } + +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 mRigValidJointUpload; + U32 mLegacyRigFlags; + + bool mNoNormalize; + bool mNoOptimize; + + JointTransformMap mJointTransformMap; + + LLSD mWarningsArray; // preview floater will pull logs from here + + 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/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index 0ef9804ab0..267ccd6a76 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1,2413 +1,2413 @@ -/**
- * @file llprimitive.cpp
- * @brief LLPrimitive base class
- *
- * $LicenseInfo:firstyear=2001&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 "linden_common.h"
-
-#include "material_codes.h"
-#include "llerror.h"
-#include "message.h"
-#include "llprimitive.h"
-#include "llvolume.h"
-#include "legacy_object_types.h"
-#include "v4coloru.h"
-#include "llvolumemgr.h"
-#include "llstring.h"
-#include "lldatapacker.h"
-#include "llsdutil_math.h"
-#include "llprimtexturelist.h"
-#include "llmaterialid.h"
-#include "llsdutil.h"
-
-/**
- * exported constants
- */
-
-const F32 OBJECT_CUT_MIN = 0.f;
-const F32 OBJECT_CUT_MAX = 1.f;
-const F32 OBJECT_CUT_INC = 0.05f;
-const F32 OBJECT_MIN_CUT_INC = 0.02f;
-const F32 OBJECT_ROTATION_PRECISION = 0.05f;
-
-const F32 OBJECT_TWIST_MIN = -360.f;
-const F32 OBJECT_TWIST_MAX = 360.f;
-const F32 OBJECT_TWIST_INC = 18.f;
-
-// This is used for linear paths,
-// since twist is used in a slightly different manner.
-const F32 OBJECT_TWIST_LINEAR_MIN = -180.f;
-const F32 OBJECT_TWIST_LINEAR_MAX = 180.f;
-const F32 OBJECT_TWIST_LINEAR_INC = 9.f;
-
-const F32 OBJECT_MIN_HOLE_SIZE = 0.05f;
-const F32 OBJECT_MAX_HOLE_SIZE_X = 1.0f;
-const F32 OBJECT_MAX_HOLE_SIZE_Y = 0.5f;
-
-// Revolutions parameters.
-const F32 OBJECT_REV_MIN = 1.0f;
-const F32 OBJECT_REV_MAX = 4.0f;
-const F32 OBJECT_REV_INC = 0.1f;
-
-// lights
-const F32 LIGHT_MIN_RADIUS = 0.0f;
-const F32 LIGHT_DEFAULT_RADIUS = 5.0f;
-const F32 LIGHT_MAX_RADIUS = 20.0f;
-const F32 LIGHT_MIN_FALLOFF = 0.0f;
-const F32 LIGHT_DEFAULT_FALLOFF = 1.0f;
-const F32 LIGHT_MAX_FALLOFF = 2.0f;
-const F32 LIGHT_MIN_CUTOFF = 0.0f;
-const F32 LIGHT_DEFAULT_CUTOFF = 0.0f;
-const F32 LIGHT_MAX_CUTOFF = 180.f;
-
-// reflection probes
-const F32 REFLECTION_PROBE_MIN_AMBIANCE = 0.f;
-const F32 REFLECTION_PROBE_MAX_AMBIANCE = 100.f;
-const F32 REFLECTION_PROBE_DEFAULT_AMBIANCE = 0.f;
-// *NOTE: Clip distances are clamped in LLCamera::setNear. The max clip
-// distance is currently limited by the skybox
-const F32 REFLECTION_PROBE_MIN_CLIP_DISTANCE = 0.f;
-const F32 REFLECTION_PROBE_MAX_CLIP_DISTANCE = 1024.f;
-const F32 REFLECTION_PROBE_DEFAULT_CLIP_DISTANCE = 0.f;
-
-// "Tension" => [0,10], increments of 0.1
-const F32 FLEXIBLE_OBJECT_MIN_TENSION = 0.0f;
-const F32 FLEXIBLE_OBJECT_DEFAULT_TENSION = 1.0f;
-const F32 FLEXIBLE_OBJECT_MAX_TENSION = 10.0f;
-
-// "Drag" => [0,10], increments of 0.1
-const F32 FLEXIBLE_OBJECT_MIN_AIR_FRICTION = 0.0f;
-const F32 FLEXIBLE_OBJECT_DEFAULT_AIR_FRICTION = 2.0f;
-const F32 FLEXIBLE_OBJECT_MAX_AIR_FRICTION = 10.0f;
-
-// "Gravity" = [-10,10], increments of 0.1
-const F32 FLEXIBLE_OBJECT_MIN_GRAVITY = -10.0f;
-const F32 FLEXIBLE_OBJECT_DEFAULT_GRAVITY = 0.3f;
-const F32 FLEXIBLE_OBJECT_MAX_GRAVITY = 10.0f;
-
-// "Wind" = [0,10], increments of 0.1
-const F32 FLEXIBLE_OBJECT_MIN_WIND_SENSITIVITY = 0.0f;
-const F32 FLEXIBLE_OBJECT_DEFAULT_WIND_SENSITIVITY = 0.0f;
-const F32 FLEXIBLE_OBJECT_MAX_WIND_SENSITIVITY = 10.0f;
-
-// I'll explain later...
-const F32 FLEXIBLE_OBJECT_MAX_INTERNAL_TENSION_FORCE = 0.99f;
-
-const F32 FLEXIBLE_OBJECT_DEFAULT_LENGTH = 1.0f;
-const bool FLEXIBLE_OBJECT_DEFAULT_USING_COLLISION_SPHERE = false;
-const bool FLEXIBLE_OBJECT_DEFAULT_RENDERING_COLLISION_SPHERE = false;
-
-const LLUUID SCULPT_DEFAULT_TEXTURE("be293869-d0d9-0a69-5989-ad27f1946fd4"); // old inverted texture: "7595d345-a24c-e7ef-f0bd-78793792133e";
-
-// Texture rotations are sent over the wire as a S16. This is used to scale the actual float
-// value to a S16. Don't use 7FFF as it introduces some odd rounding with 180 since it
-// can't be divided by 2. See DEV-19108
-const F32 TEXTURE_ROTATION_PACK_FACTOR = ((F32) 0x08000);
-
-struct material_id_type // originally from llrendermaterialtable
-{
- material_id_type()
- {
- memset((void*)m_value, 0, sizeof(m_value));
- }
-
- bool operator==(const material_id_type& other) const
- {
- return (memcmp(m_value, other.m_value, sizeof(m_value)) == 0);
- }
-
- bool operator!=(const material_id_type& other) const
- {
- return !operator==(other);
- }
-
- bool isNull() const
- {
- return (memcmp(m_value, s_null_id, sizeof(m_value)) == 0);
- }
-
- U8 m_value[MATERIAL_ID_SIZE]; // server side this is MD5RAW_BYTES
-
- static const U8 s_null_id[MATERIAL_ID_SIZE];
-};
-
-const U8 material_id_type::s_null_id[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
-
-//static
-// LEGACY: by default we use the LLVolumeMgr::gVolumeMgr global
-// TODO -- eliminate this global from the codebase!
-LLVolumeMgr* LLPrimitive::sVolumeManager = NULL;
-
-// static
-void LLPrimitive::setVolumeManager( LLVolumeMgr* volume_manager )
-{
- if ( !volume_manager || sVolumeManager )
- {
- LL_ERRS() << "LLPrimitive::sVolumeManager attempting to be set to NULL or it already has been set." << LL_ENDL;
- }
- sVolumeManager = volume_manager;
-}
-
-// static
-bool LLPrimitive::cleanupVolumeManager()
-{
- bool res = false;
- if (sVolumeManager)
- {
- res = sVolumeManager->cleanup();
- delete sVolumeManager;
- sVolumeManager = NULL;
- }
- return res;
-}
-
-
-//===============================================================
-LLPrimitive::LLPrimitive()
-: mTextureList(),
- mNumTEs(0),
- mMiscFlags(0),
- mNumBumpmapTEs(0)
-{
- mPrimitiveCode = 0;
-
- mMaterial = LL_MCODE_STONE;
- mVolumep = NULL;
-
- mChanged = UNCHANGED;
-
- mPosition.setVec(0.f,0.f,0.f);
- mVelocity.setVec(0.f,0.f,0.f);
- mAcceleration.setVec(0.f,0.f,0.f);
-
- mRotation.loadIdentity();
- mAngularVelocity.setVec(0.f,0.f,0.f);
-
- mScale.setVec(1.f,1.f,1.f);
-}
-
-//===============================================================
-LLPrimitive::~LLPrimitive()
-{
- clearTextureList();
- // Cleanup handled by volume manager
- if (mVolumep && sVolumeManager)
- {
- sVolumeManager->unrefVolume(mVolumep);
- }
- mVolumep = NULL;
-}
-
-void LLPrimitive::clearTextureList()
-{
-}
-
-//===============================================================
-// static
-LLPrimitive *LLPrimitive::createPrimitive(LLPCode p_code)
-{
- LLPrimitive *retval = new LLPrimitive();
-
- if (retval)
- {
- retval->init_primitive(p_code);
- }
- else
- {
- LL_ERRS() << "primitive allocation failed" << LL_ENDL;
- }
-
- return retval;
-}
-
-//===============================================================
-void LLPrimitive::init_primitive(LLPCode p_code)
-{
- clearTextureList();
- mPrimitiveCode = p_code;
-}
-
-void LLPrimitive::setPCode(const U8 p_code)
-{
- mPrimitiveCode = p_code;
-}
-
-//===============================================================
-LLTextureEntry* LLPrimitive::getTE(const U8 index) const
-{
- return mTextureList.getTexture(index);
-}
-
-//===============================================================
-void LLPrimitive::setNumTEs(const U8 num_tes)
-{
- mTextureList.setSize(num_tes);
-}
-
-//===============================================================
-void LLPrimitive::setAllTETextures(const LLUUID &tex_id)
-{
- mTextureList.setAllIDs(tex_id);
-}
-
-//===============================================================
-void LLPrimitive::setTE(const U8 index, const LLTextureEntry& te)
-{
- if(mTextureList.copyTexture(index, te) != TEM_CHANGE_NONE && te.getBumpmap() > 0)
- {
- mNumBumpmapTEs++;
- }
-}
-
-S32 LLPrimitive::setTETexture(const U8 index, const LLUUID &id)
-{
- return mTextureList.setID(index, id);
-}
-
-S32 LLPrimitive::setTEColor(const U8 index, const LLColor4 &color)
-{
- return mTextureList.setColor(index, color);
-}
-
-S32 LLPrimitive::setTEColor(const U8 index, const LLColor3 &color)
-{
- return mTextureList.setColor(index, color);
-}
-
-S32 LLPrimitive::setTEAlpha(const U8 index, const F32 alpha)
-{
- return mTextureList.setAlpha(index, alpha);
-}
-
-//===============================================================
-S32 LLPrimitive::setTEScale(const U8 index, const F32 s, const F32 t)
-{
- return mTextureList.setScale(index, s, t);
-}
-
-
-// BUG: slow - done this way because texture entries have some
-// voodoo related to texture coords
-S32 LLPrimitive::setTEScaleS(const U8 index, const F32 s)
-{
- return mTextureList.setScaleS(index, s);
-}
-
-
-// BUG: slow - done this way because texture entries have some
-// voodoo related to texture coords
-S32 LLPrimitive::setTEScaleT(const U8 index, const F32 t)
-{
- return mTextureList.setScaleT(index, t);
-}
-
-
-//===============================================================
-S32 LLPrimitive::setTEOffset(const U8 index, const F32 s, const F32 t)
-{
- return mTextureList.setOffset(index, s, t);
-}
-
-
-// BUG: slow - done this way because texture entries have some
-// voodoo related to texture coords
-S32 LLPrimitive::setTEOffsetS(const U8 index, const F32 s)
-{
- return mTextureList.setOffsetS(index, s);
-}
-
-
-// BUG: slow - done this way because texture entries have some
-// voodoo related to texture coords
-S32 LLPrimitive::setTEOffsetT(const U8 index, const F32 t)
-{
- return mTextureList.setOffsetT(index, t);
-}
-
-
-//===============================================================
-S32 LLPrimitive::setTERotation(const U8 index, const F32 r)
-{
- return mTextureList.setRotation(index, r);
-}
-
-S32 LLPrimitive::setTEMaterialID(const U8 index, const LLMaterialID& pMaterialID)
-{
- return mTextureList.setMaterialID(index, pMaterialID);
-}
-
-S32 LLPrimitive::setTEMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams)
-{
- return mTextureList.setMaterialParams(index, pMaterialParams);
-}
-
-LLMaterialPtr LLPrimitive::getTEMaterialParams(const U8 index)
-{
- return mTextureList.getMaterialParams(index);
-}
-
-//===============================================================
-S32 LLPrimitive::setTEBumpShinyFullbright(const U8 index, const U8 bump)
-{
- updateNumBumpmap(index, bump);
- return mTextureList.setBumpShinyFullbright(index, bump);
-}
-
-S32 LLPrimitive::setTEMediaTexGen(const U8 index, const U8 media)
-{
- return mTextureList.setMediaTexGen(index, media);
-}
-
-S32 LLPrimitive::setTEBumpmap(const U8 index, const U8 bump)
-{
- updateNumBumpmap(index, bump);
- return mTextureList.setBumpMap(index, bump);
-}
-
-S32 LLPrimitive::setTEBumpShiny(const U8 index, const U8 bump_shiny)
-{
- updateNumBumpmap(index, bump_shiny);
- return mTextureList.setBumpShiny(index, bump_shiny);
-}
-
-S32 LLPrimitive::setTETexGen(const U8 index, const U8 texgen)
-{
- return mTextureList.setTexGen(index, texgen);
-}
-
-S32 LLPrimitive::setTEShiny(const U8 index, const U8 shiny)
-{
- return mTextureList.setShiny(index, shiny);
-}
-
-S32 LLPrimitive::setTEFullbright(const U8 index, const U8 fullbright)
-{
- return mTextureList.setFullbright(index, fullbright);
-}
-
-S32 LLPrimitive::setTEMediaFlags(const U8 index, const U8 media_flags)
-{
- return mTextureList.setMediaFlags(index, media_flags);
-}
-
-S32 LLPrimitive::setTEGlow(const U8 index, const F32 glow)
-{
- return mTextureList.setGlow(index, glow);
-}
-
-void LLPrimitive::setAllTESelected(bool sel)
-{
- for (int i = 0, cnt = getNumTEs(); i < cnt; i++)
- {
- setTESelected(i, sel);
- }
-}
-
-void LLPrimitive::setTESelected(const U8 te, bool sel)
-{
- LLTextureEntry* tep = getTE(te);
- if ( (tep) && (tep->setSelected(sel)) && (!sel) && (tep->hasPendingMaterialUpdate()) )
- {
- LLMaterialID material_id = tep->getMaterialID();
- setTEMaterialID(te, material_id);
- }
-}
-
-LLPCode LLPrimitive::legacyToPCode(const U8 legacy)
-{
- // TODO: Should this default to something valid?
- // Maybe volume?
- LLPCode pcode = 0;
-
- switch (legacy)
- {
- /*
- case BOX:
- pcode = LL_PCODE_CUBE;
- break;
- case CYLINDER:
- pcode = LL_PCODE_CYLINDER;
- break;
- case CONE:
- pcode = LL_PCODE_CONE;
- break;
- case HALF_CONE:
- pcode = LL_PCODE_CONE_HEMI;
- break;
- case HALF_CYLINDER:
- pcode = LL_PCODE_CYLINDER_HEMI;
- break;
- case HALF_SPHERE:
- pcode = LL_PCODE_SPHERE_HEMI;
- break;
- case PRISM:
- pcode = LL_PCODE_PRISM;
- break;
- case PYRAMID:
- pcode = LL_PCODE_PYRAMID;
- break;
- case SPHERE:
- pcode = LL_PCODE_SPHERE;
- break;
- case TETRAHEDRON:
- pcode = LL_PCODE_TETRAHEDRON;
- break;
- case DEMON:
- pcode = LL_PCODE_LEGACY_DEMON;
- break;
- case LSL_TEST:
- pcode = LL_PCODE_LEGACY_LSL_TEST;
- break;
- case ORACLE:
- pcode = LL_PCODE_LEGACY_ORACLE;
- break;
- case TEXTBUBBLE:
- pcode = LL_PCODE_LEGACY_TEXT_BUBBLE;
- break;
- case ATOR:
- pcode = LL_PCODE_LEGACY_ATOR;
- break;
- case BASIC_SHOT:
- pcode = LL_PCODE_LEGACY_SHOT;
- break;
- case BIG_SHOT:
- pcode = LL_PCODE_LEGACY_SHOT_BIG;
- break;
- case BIRD:
- pcode = LL_PCODE_LEGACY_BIRD;
- break;
- case ROCK:
- pcode = LL_PCODE_LEGACY_ROCK;
- break;
- case SMOKE:
- pcode = LL_PCODE_LEGACY_SMOKE;
- break;
- case SPARK:
- pcode = LL_PCODE_LEGACY_SPARK;
- break;
- */
- case PRIMITIVE_VOLUME:
- pcode = LL_PCODE_VOLUME;
- break;
- case GRASS:
- pcode = LL_PCODE_LEGACY_GRASS;
- break;
- case PART_SYS:
- pcode = LL_PCODE_LEGACY_PART_SYS;
- break;
- case PLAYER:
- pcode = LL_PCODE_LEGACY_AVATAR;
- break;
- case TREE:
- pcode = LL_PCODE_LEGACY_TREE;
- break;
- case TREE_NEW:
- pcode = LL_PCODE_TREE_NEW;
- break;
- default:
- LL_WARNS() << "Unknown legacy code " << legacy << " [" << (S32)legacy << "]!" << LL_ENDL;
- }
-
- return pcode;
-}
-
-U8 LLPrimitive::pCodeToLegacy(const LLPCode pcode)
-{
- U8 legacy;
- switch (pcode)
- {
-/*
- case LL_PCODE_CUBE:
- legacy = BOX;
- break;
- case LL_PCODE_CYLINDER:
- legacy = CYLINDER;
- break;
- case LL_PCODE_CONE:
- legacy = CONE;
- break;
- case LL_PCODE_CONE_HEMI:
- legacy = HALF_CONE;
- break;
- case LL_PCODE_CYLINDER_HEMI:
- legacy = HALF_CYLINDER;
- break;
- case LL_PCODE_SPHERE_HEMI:
- legacy = HALF_SPHERE;
- break;
- case LL_PCODE_PRISM:
- legacy = PRISM;
- break;
- case LL_PCODE_PYRAMID:
- legacy = PYRAMID;
- break;
- case LL_PCODE_SPHERE:
- legacy = SPHERE;
- break;
- case LL_PCODE_TETRAHEDRON:
- legacy = TETRAHEDRON;
- break;
- case LL_PCODE_LEGACY_ATOR:
- legacy = ATOR;
- break;
- case LL_PCODE_LEGACY_SHOT:
- legacy = BASIC_SHOT;
- break;
- case LL_PCODE_LEGACY_SHOT_BIG:
- legacy = BIG_SHOT;
- break;
- case LL_PCODE_LEGACY_BIRD:
- legacy = BIRD;
- break;
- case LL_PCODE_LEGACY_DEMON:
- legacy = DEMON;
- break;
- case LL_PCODE_LEGACY_LSL_TEST:
- legacy = LSL_TEST;
- break;
- case LL_PCODE_LEGACY_ORACLE:
- legacy = ORACLE;
- break;
- case LL_PCODE_LEGACY_ROCK:
- legacy = ROCK;
- break;
- case LL_PCODE_LEGACY_TEXT_BUBBLE:
- legacy = TEXTBUBBLE;
- break;
- case LL_PCODE_LEGACY_SMOKE:
- legacy = SMOKE;
- break;
- case LL_PCODE_LEGACY_SPARK:
- legacy = SPARK;
- break;
-*/
- case LL_PCODE_VOLUME:
- legacy = PRIMITIVE_VOLUME;
- break;
- case LL_PCODE_LEGACY_GRASS:
- legacy = GRASS;
- break;
- case LL_PCODE_LEGACY_PART_SYS:
- legacy = PART_SYS;
- break;
- case LL_PCODE_LEGACY_AVATAR:
- legacy = PLAYER;
- break;
- case LL_PCODE_LEGACY_TREE:
- legacy = TREE;
- break;
- case LL_PCODE_TREE_NEW:
- legacy = TREE_NEW;
- break;
- default:
- LL_WARNS() << "Unknown pcode " << (S32)pcode << ":" << pcode << "!" << LL_ENDL;
- return 0;
- }
- return legacy;
-}
-
-
-// static
-// Don't crash or LL_ERRS() here! This function is used for debug strings.
-std::string LLPrimitive::pCodeToString(const LLPCode pcode)
-{
- std::string pcode_string;
-
- U8 base_code = pcode & LL_PCODE_BASE_MASK;
- if (!pcode)
- {
- pcode_string = "null";
- }
- else if ((base_code) == LL_PCODE_LEGACY)
- {
- // It's a legacy object
- switch (pcode)
- {
- case LL_PCODE_LEGACY_GRASS:
- pcode_string = "grass";
- break;
- case LL_PCODE_LEGACY_PART_SYS:
- pcode_string = "particle system";
- break;
- case LL_PCODE_LEGACY_AVATAR:
- pcode_string = "avatar";
- break;
- case LL_PCODE_LEGACY_TEXT_BUBBLE:
- pcode_string = "text bubble";
- break;
- case LL_PCODE_LEGACY_TREE:
- pcode_string = "tree";
- break;
- case LL_PCODE_TREE_NEW:
- pcode_string = "tree_new";
- break;
- default:
- pcode_string = llformat( "unknown legacy pcode %i",(U32)pcode);
- }
- }
- else
- {
- std::string shape;
- std::string mask;
- if (base_code == LL_PCODE_CUBE)
- {
- shape = "cube";
- }
- else if (base_code == LL_PCODE_CYLINDER)
- {
- shape = "cylinder";
- }
- else if (base_code == LL_PCODE_CONE)
- {
- shape = "cone";
- }
- else if (base_code == LL_PCODE_PRISM)
- {
- shape = "prism";
- }
- else if (base_code == LL_PCODE_PYRAMID)
- {
- shape = "pyramid";
- }
- else if (base_code == LL_PCODE_SPHERE)
- {
- shape = "sphere";
- }
- else if (base_code == LL_PCODE_TETRAHEDRON)
- {
- shape = "tetrahedron";
- }
- else if (base_code == LL_PCODE_VOLUME)
- {
- shape = "volume";
- }
- else if (base_code == LL_PCODE_APP)
- {
- shape = "app";
- }
- else
- {
- LL_WARNS() << "Unknown base mask for pcode: " << base_code << LL_ENDL;
- }
-
- U8 mask_code = pcode & (~LL_PCODE_BASE_MASK);
- if (base_code == LL_PCODE_APP)
- {
- mask = llformat( "%x", mask_code);
- }
- else if (mask_code & LL_PCODE_HEMI_MASK)
- {
- mask = "hemi";
- }
- else
- {
- mask = llformat( "%x", mask_code);
- }
-
- if (mask[0])
- {
- pcode_string = llformat( "%s-%s", shape.c_str(), mask.c_str());
- }
- else
- {
- pcode_string = llformat( "%s", shape.c_str());
- }
- }
-
- return pcode_string;
-}
-
-
-void LLPrimitive::copyTEs(const LLPrimitive *primitivep)
-{
- U32 i;
- if (primitivep->getExpectedNumTEs() != getExpectedNumTEs())
- {
- LL_WARNS() << "Primitives don't have same expected number of TE's" << LL_ENDL;
- }
- U32 num_tes = llmin(primitivep->getExpectedNumTEs(), getExpectedNumTEs());
- if (mTextureList.size() < getExpectedNumTEs())
- {
- mTextureList.setSize(getExpectedNumTEs());
- }
- for (i = 0; i < num_tes; i++)
- {
- mTextureList.copyTexture(i, *(primitivep->getTE(i)));
- }
-}
-
-S32 face_index_from_id(LLFaceID face_ID, const std::vector<LLProfile::Face>& faceArray)
-{
- S32 i;
- for (i = 0; i < (S32)faceArray.size(); i++)
- {
- if (faceArray[i].mFaceID == face_ID)
- {
- return i;
- }
- }
- return -1;
-}
-
-bool LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume)
-{
- if (NO_LOD == detail)
- {
- // build the new object
- setChanged(GEOMETRY);
- sVolumeManager->unrefVolume(mVolumep);
- mVolumep = new LLVolume(volume_params, 1, true, true);
- setNumTEs(mVolumep->getNumFaces());
- return false;
- }
-
- LLVolume *volumep;
- if (unique_volume)
- {
- F32 volume_detail = LLVolumeLODGroup::getVolumeScaleFromDetail(detail);
- if (mVolumep.notNull() && volume_params == mVolumep->getParams() && (volume_detail == mVolumep->getDetail()))
- {
- return false;
- }
- volumep = new LLVolume(volume_params, volume_detail, false, true);
- }
- else
- {
- if (mVolumep.notNull())
- {
- F32 volume_detail = LLVolumeLODGroup::getVolumeScaleFromDetail(detail);
- if (volume_params == mVolumep->getParams() && (volume_detail == mVolumep->getDetail()))
- {
- return false;
- }
- }
-
- volumep = sVolumeManager->refVolume(volume_params, detail);
- if (volumep == mVolumep)
- {
- sVolumeManager->unrefVolume( volumep ); // LLVolumeMgr::refVolume() creates a reference, but we don't need a second one.
- return true;
- }
- }
-
- setChanged(GEOMETRY);
-
-
- if (!mVolumep)
- {
- mVolumep = volumep;
- //mFaceMask = mVolumep->generateFaceMask();
- setNumTEs(mVolumep->getNumFaces());
- return true;
- }
-
-#if 0
- // #if 0'd out by davep
- // this is a lot of cruft to set texture entry values that just stay the same for LOD switch
- // or immediately get overridden by an object update message, also crashes occasionally
- U32 old_face_mask = mVolumep->mFaceMask;
-
- S32 face_bit = 0;
- S32 cur_mask = 0;
-
- // Grab copies of the old faces from the original shape, ordered by type.
- // We will use these to figure out what old texture info gets mapped to new
- // faces in the new shape.
- std::vector<LLProfile::Face> old_faces;
- for (S32 face = 0; face < mVolumep->getNumFaces(); face++)
- {
- old_faces.push_back(mVolumep->getProfile().mFaces[face]);
- }
-
- // Copy the old texture info off to the side, but not in the order in which
- // they live in the mTextureList, rather in order of ther "face id" which
- // is the corresponding value of LLVolueParams::LLProfile::mFaces::mIndex.
- //
- // Hence, some elements of old_tes::mEntryList will be invalid. It is
- // initialized to a size of 9 (max number of possible faces on a volume?)
- // and only the ones with valid types are filled in.
- LLPrimTextureList old_tes;
- old_tes.setSize(9);
- for (face_bit = 0; face_bit < 9; face_bit++)
- {
- cur_mask = 0x1 << face_bit;
- if (old_face_mask & cur_mask)
- {
- S32 te_index = face_index_from_id(cur_mask, old_faces);
- old_tes.copyTexture(face_bit, *(getTE(te_index)));
- //LL_INFOS() << face_bit << ":" << te_index << ":" << old_tes[face_bit].getID() << LL_ENDL;
- }
- }
-
-
- // build the new object
- sVolumeManager->unrefVolume(mVolumep);
- mVolumep = volumep;
-
- U32 new_face_mask = mVolumep->mFaceMask;
- S32 i;
-
- if (old_face_mask == new_face_mask)
- {
- // nothing to do
- return true;
- }
-
- if (mVolumep->getNumFaces() == 0 && new_face_mask != 0)
- {
- LL_WARNS() << "Object with 0 faces found...INCORRECT!" << LL_ENDL;
- setNumTEs(mVolumep->getNumFaces());
- return true;
- }
-
- // initialize face_mapping
- S32 face_mapping[9];
- for (face_bit = 0; face_bit < 9; face_bit++)
- {
- face_mapping[face_bit] = face_bit;
- }
-
- // The new shape may have more faces than the original, but we can't just
- // add them to the end -- the ordering matters and it may be that we must
- // insert the new faces in the middle of the list. When we add a face it
- // will pick up the texture/color info of one of the old faces an so we
- // now figure out which old face info gets mapped to each new face, and
- // store in the face_mapping lookup table.
- for (face_bit = 0; face_bit < 9; face_bit++)
- {
- cur_mask = 0x1 << face_bit;
- if (!(new_face_mask & cur_mask))
- {
- // Face doesn't exist in new map.
- face_mapping[face_bit] = -1;
- continue;
- }
- else if (old_face_mask & cur_mask)
- {
- // Face exists in new and old map.
- face_mapping[face_bit] = face_bit;
- continue;
- }
-
- // OK, how we've got a mismatch, where we have to fill a new face with one from
- // the old face.
- if (cur_mask & (LL_FACE_PATH_BEGIN | LL_FACE_PATH_END | LL_FACE_INNER_SIDE))
- {
- // It's a top/bottom/hollow interior face.
- if (old_face_mask & LL_FACE_PATH_END)
- {
- face_mapping[face_bit] = 1;
- continue;
- }
- else
- {
- S32 cur_outer_mask = LL_FACE_OUTER_SIDE_0;
- for (i = 0; i < 4; i++)
- {
- if (old_face_mask & cur_outer_mask)
- {
- face_mapping[face_bit] = 5 + i;
- break;
- }
- cur_outer_mask <<= 1;
- }
- if (i == 4)
- {
- LL_WARNS() << "No path end or outer face in volume!" << LL_ENDL;
- }
- continue;
- }
- }
-
- if (cur_mask & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END))
- {
- // A cut slice. Use the hollow interior if we have it.
- if (old_face_mask & LL_FACE_INNER_SIDE)
- {
- face_mapping[face_bit] = 2;
- continue;
- }
-
- // No interior, use the bottom face.
- // Could figure out which of the outer faces was nearest, but that would be harder.
- if (old_face_mask & LL_FACE_PATH_END)
- {
- face_mapping[face_bit] = 1;
- continue;
- }
- else
- {
- S32 cur_outer_mask = LL_FACE_OUTER_SIDE_0;
- for (i = 0; i < 4; i++)
- {
- if (old_face_mask & cur_outer_mask)
- {
- face_mapping[face_bit] = 5 + i;
- break;
- }
- cur_outer_mask <<= 1;
- }
- if (i == 4)
- {
- LL_WARNS() << "No path end or outer face in volume!" << LL_ENDL;
- }
- continue;
- }
- }
-
- // OK, the face that's missing is an outer face...
- // Pull from the nearest adjacent outer face (there's always guaranteed to be one...
- S32 cur_outer = face_bit - 5;
- S32 min_dist = 5;
- S32 min_outer_bit = -1;
- S32 i;
- for (i = 0; i < 4; i++)
- {
- if (old_face_mask & (LL_FACE_OUTER_SIDE_0 << i))
- {
- S32 dist = abs(i - cur_outer);
- if (dist < min_dist)
- {
- min_dist = dist;
- min_outer_bit = i + 5;
- }
- }
- }
- if (-1 == min_outer_bit)
- {
- LL_INFOS() << (LLVolume *)mVolumep << LL_ENDL;
- LL_WARNS() << "Bad! No outer faces, impossible!" << LL_ENDL;
- }
- face_mapping[face_bit] = min_outer_bit;
- }
-
-
- setNumTEs(mVolumep->getNumFaces());
- for (face_bit = 0; face_bit < 9; face_bit++)
- {
- // For each possible face type on the new shape we check to see if that
- // face exists and if it does we create a texture entry that is a copy
- // of one of the originals. Since the originals might not have a
- // matching face, we use the face_mapping lookup table to figure out
- // which face information to copy.
- cur_mask = 0x1 << face_bit;
- if (new_face_mask & cur_mask)
- {
- if (-1 == face_mapping[face_bit])
- {
- LL_WARNS() << "No mapping from old face to new face!" << LL_ENDL;
- }
-
- S32 te_num = face_index_from_id(cur_mask, mVolumep->getProfile().mFaces);
- setTE(te_num, *(old_tes.getTexture(face_mapping[face_bit])));
- }
- }
-#else
- // build the new object
- sVolumeManager->unrefVolume(mVolumep);
- mVolumep = volumep;
-
- setNumTEs(mVolumep->getNumFaces());
-#endif
- return true;
-}
-
-bool LLPrimitive::setMaterial(U8 material)
-{
- if (material != mMaterial)
- {
- mMaterial = material;
- return true;
- }
- else
- {
- return false;
- }
-}
-
-S32 LLPrimitive::packTEField(U8 *cur_ptr, U8 *data_ptr, U8 data_size, U8 last_face_index, EMsgVariableType type) const
-{
- S32 face_index;
- S32 i;
- U64 exception_faces;
- U8 *start_loc = cur_ptr;
-
- htolememcpy(cur_ptr,data_ptr + (last_face_index * data_size), type, data_size);
- cur_ptr += data_size;
-
- for (face_index = last_face_index-1; face_index >= 0; face_index--)
- {
- bool already_sent = false;
- for (i = face_index+1; i <= last_face_index; i++)
- {
- if (!memcmp(data_ptr+(data_size *face_index), data_ptr+(data_size *i), data_size))
- {
- already_sent = true;
- break;
- }
- }
-
- if (!already_sent)
- {
- exception_faces = 0;
- for (i = face_index; i >= 0; i--)
- {
- if (!memcmp(data_ptr+(data_size *face_index), data_ptr+(data_size *i), data_size))
- {
- exception_faces |= ((U64)1 << i);
- }
- }
-
- //assign exception faces to cur_ptr
- if (exception_faces >= ((U64)0x1 << 7))
- {
- if (exception_faces >= ((U64)0x1 << 14))
- {
- if (exception_faces >= ((U64)0x1 << 21))
- {
- if (exception_faces >= ((U64)0x1 << 28))
- {
- if (exception_faces >= ((U64)0x1 << 35))
- {
- if (exception_faces >= ((U64)0x1 << 42))
- {
- if (exception_faces >= ((U64)0x1 << 49))
- {
- *cur_ptr++ = (U8)(((exception_faces >> 49) & 0x7F) | 0x80);
- }
- *cur_ptr++ = (U8)(((exception_faces >> 42) & 0x7F) | 0x80);
- }
- *cur_ptr++ = (U8)(((exception_faces >> 35) & 0x7F) | 0x80);
- }
- *cur_ptr++ = (U8)(((exception_faces >> 28) & 0x7F) | 0x80);
- }
- *cur_ptr++ = (U8)(((exception_faces >> 21) & 0x7F) | 0x80);
- }
- *cur_ptr++ = (U8)(((exception_faces >> 14) & 0x7F) | 0x80);
- }
- *cur_ptr++ = (U8)(((exception_faces >> 7) & 0x7F) | 0x80);
- }
-
-
- *cur_ptr++ = (U8)(exception_faces & 0x7F);
-
- htolememcpy(cur_ptr,data_ptr + (face_index * data_size), type, data_size);
- cur_ptr += data_size;
- }
- }
- return (S32)(cur_ptr - start_loc);
-}
-
-namespace
-{
- template< typename T >
- bool unpack_TEField(T dest[], U8 dest_count, U8 * &source, U8 *source_end, EMsgVariableType type)
- {
- const size_t size(sizeof(T));
-
- LL_DEBUGS("TEXTUREENTRY") << "Request to read items of size " << size << " with swizzle " << type << " froum buffer sized " << (source_end - source) << LL_ENDL;
-
- if ((source + size + 1) > source_end)
- {
- // we add 1 above to take into account the byte that we know must follow the value.
- LL_WARNS("TEXTUREENTRY") << "Buffer exhausted! Requires " << size << " + 1 bytes for default, " << (source_end - source) << " bytes remaning." << LL_ENDL;
- source = source_end;
- return false;
- }
-
- // Extract the default value and fill the array.
- htolememcpy(dest, source, type, size);
- source += size;
- for (S32 idx = 1; idx < dest_count; ++idx)
- {
- dest[idx] = dest[0];
- }
-
- while (source < source_end)
- {
- U64 index_flags(0);
- U8 sbit(0);
-
- // Unpack the variable length bitfield. Each bit represents whether the following
- // value will be placed at the corresponding array index.
- do
- {
- if (source >= source_end)
- {
- LL_WARNS("TEXTUREENTRY") << "Buffer exhausted! Reading index flags." << LL_ENDL;
- source = source_end;
- return false;
- }
-
- sbit = *source++;
- index_flags <<= 7; // original code had this after?
- index_flags |= (sbit & 0x7F);
- } while (sbit & 0x80);
-
- if (!index_flags)
- { // We've hit the terminating 0 byte.
- break;
- }
-
- if ((source + size + 1) > source_end)
- {
- // we add 1 above to take into account the byte that we know must follow the value.
- LL_WARNS("TEXTUREENTRY") << "Buffer exhausted! Requires " << size << " + 1 bytes for default, " << (source_end - source) << " bytes remaning." << LL_ENDL;
- source = source_end;
- return false;
- }
-
- // get the value for the indexs.
- T value;
- htolememcpy(&value, source, type, size);
- source += size;
-
- for (S32 idx = 0; idx < dest_count; idx++)
- {
- if (index_flags & 1ULL << idx)
- {
- dest[idx] = value;
- }
- }
-
- }
- return true;
- }
-}
-
-
-
-// Pack information about all texture entries into container:
-// { TextureEntry Variable 2 }
-// Includes information about image ID, color, scale S,T, offset S,T and rotation
-bool LLPrimitive::packTEMessage(LLMessageSystem *mesgsys) const
-{
- const U32 MAX_TES = 45;
-
- U8 image_ids[MAX_TES*16];
- U8 colors[MAX_TES*4];
- F32 scale_s[MAX_TES];
- F32 scale_t[MAX_TES];
- S16 offset_s[MAX_TES];
- S16 offset_t[MAX_TES];
- S16 image_rot[MAX_TES];
- U8 bump[MAX_TES];
- U8 media_flags[MAX_TES];
- U8 glow[MAX_TES];
- U8 material_data[MAX_TES*16];
-
- const U32 MAX_TE_BUFFER = 4096;
- U8 packed_buffer[MAX_TE_BUFFER];
- U8 *cur_ptr = packed_buffer;
-
- S32 last_face_index = llmin((U32) getNumTEs(), MAX_TES) - 1;
-
- if (last_face_index > -1)
- {
- // ...if we hit the front, send one image id
- S8 face_index;
- LLColor4U coloru;
- for (face_index = 0; face_index <= last_face_index; face_index++)
- {
- // Directly sending image_ids is not safe!
- memcpy(&image_ids[face_index*16],getTE(face_index)->getID().mData,16); /* Flawfinder: ignore */
-
- // Cast LLColor4 to LLColor4U
- coloru.setVec( getTE(face_index)->getColor() );
-
- // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f)
- // as all zeros. However, the subtraction and addition must be done in unsigned
- // byte space, not in float space, otherwise off-by-one errors occur. JC
- colors[4*face_index] = 255 - coloru.mV[0];
- colors[4*face_index + 1] = 255 - coloru.mV[1];
- colors[4*face_index + 2] = 255 - coloru.mV[2];
- colors[4*face_index + 3] = 255 - coloru.mV[3];
-
- const LLTextureEntry* te = getTE(face_index);
- scale_s[face_index] = (F32) te->mScaleS;
- scale_t[face_index] = (F32) te->mScaleT;
- offset_s[face_index] = (S16) ll_round((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ;
- offset_t[face_index] = (S16) ll_round((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ;
- image_rot[face_index] = (S16) ll_round(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR));
- bump[face_index] = te->getBumpShinyFullbright();
- media_flags[face_index] = te->getMediaTexGen();
- glow[face_index] = (U8) ll_round((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF));
-
- // Directly sending material_ids is not safe!
- memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */
- }
-
- cur_ptr += packTEField(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)scale_s, 4 ,last_face_index, MVT_F32);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)scale_t, 4 ,last_face_index, MVT_F32);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)glow, 1 ,last_face_index, MVT_U8);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)material_data, 16, last_face_index, MVT_LLUUID);
- }
- mesgsys->addBinaryDataFast(_PREHASH_TextureEntry, packed_buffer, (S32)(cur_ptr - packed_buffer));
-
- return true;
-}
-
-
-bool LLPrimitive::packTEMessage(LLDataPacker &dp) const
-{
- const U32 MAX_TES = 45;
-
- U8 image_ids[MAX_TES*16];
- U8 colors[MAX_TES*4];
- F32 scale_s[MAX_TES];
- F32 scale_t[MAX_TES];
- S16 offset_s[MAX_TES];
- S16 offset_t[MAX_TES];
- S16 image_rot[MAX_TES];
- U8 bump[MAX_TES];
- U8 media_flags[MAX_TES];
- U8 glow[MAX_TES];
- U8 material_data[MAX_TES*16];
-
- const U32 MAX_TE_BUFFER = 4096;
- U8 packed_buffer[MAX_TE_BUFFER];
- U8 *cur_ptr = packed_buffer;
-
- S32 last_face_index = getNumTEs() - 1;
-
- if (last_face_index > -1)
- {
- // ...if we hit the front, send one image id
- S8 face_index;
- LLColor4U coloru;
- for (face_index = 0; face_index <= last_face_index; face_index++)
- {
- // Directly sending image_ids is not safe!
- memcpy(&image_ids[face_index*16],getTE(face_index)->getID().mData,16); /* Flawfinder: ignore */
-
- // Cast LLColor4 to LLColor4U
- coloru.setVec( getTE(face_index)->getColor() );
-
- // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f)
- // as all zeros. However, the subtraction and addition must be done in unsigned
- // byte space, not in float space, otherwise off-by-one errors occur. JC
- colors[4*face_index] = 255 - coloru.mV[0];
- colors[4*face_index + 1] = 255 - coloru.mV[1];
- colors[4*face_index + 2] = 255 - coloru.mV[2];
- colors[4*face_index + 3] = 255 - coloru.mV[3];
-
- const LLTextureEntry* te = getTE(face_index);
- scale_s[face_index] = (F32) te->mScaleS;
- scale_t[face_index] = (F32) te->mScaleT;
- offset_s[face_index] = (S16) ll_round((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ;
- offset_t[face_index] = (S16) ll_round((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ;
- image_rot[face_index] = (S16) ll_round(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR));
- bump[face_index] = te->getBumpShinyFullbright();
- media_flags[face_index] = te->getMediaTexGen();
- glow[face_index] = (U8) ll_round((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF));
-
- // Directly sending material_ids is not safe!
- memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */
- }
-
- cur_ptr += packTEField(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)scale_s, 4 ,last_face_index, MVT_F32);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)scale_t, 4 ,last_face_index, MVT_F32);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)glow, 1 ,last_face_index, MVT_U8);
- *cur_ptr++ = 0;
- cur_ptr += packTEField(cur_ptr, (U8 *)material_data, 16, last_face_index, MVT_LLUUID);
- }
-
- dp.packBinaryData(packed_buffer, (S32)(cur_ptr - packed_buffer), "TextureEntry");
- return true;
-}
-
-S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec)
-{
- S32 retval = 0;
- // temp buffer for material ID processing
- // data will end up in tec.material_id[]
- material_id_type material_data[LLTEContents::MAX_TES];
-
- if (block_num < 0)
- {
- tec.size = mesgsys->getSizeFast(block_name, _PREHASH_TextureEntry);
- }
- else
- {
- tec.size = mesgsys->getSizeFast(block_name, block_num, _PREHASH_TextureEntry);
- }
-
- if (tec.size == 0)
- {
- tec.face_count = 0;
- return retval;
- }
- else if (tec.size >= LLTEContents::MAX_TE_BUFFER)
- {
- LL_WARNS("TEXTUREENTRY") << "Excessive buffer size detected in Texture Entry! Truncating." << LL_ENDL;
- tec.size = LLTEContents::MAX_TE_BUFFER - 1;
- }
-
- // if block_num < 0 ask for block 0
- mesgsys->getBinaryDataFast(block_name, _PREHASH_TextureEntry, tec.packed_buffer, 0, std::max(block_num, 0), LLTEContents::MAX_TE_BUFFER - 1);
-
- // The last field is not zero terminated.
- // Rather than special case the upack functions. Just make it 0x00 terminated.
- tec.packed_buffer[tec.size] = 0x00;
- ++tec.size;
-
- tec.face_count = llmin((U32)getNumTEs(),(U32)LLTEContents::MAX_TES);
-
- U8 *cur_ptr = tec.packed_buffer;
- LL_DEBUGS("TEXTUREENTRY") << "Texture Entry with buffere sized: " << tec.size << LL_ENDL;
- U8 *buffer_end = tec.packed_buffer + tec.size;
-
- if (!( unpack_TEField<LLUUID>(tec.image_data, tec.face_count, cur_ptr, buffer_end, MVT_LLUUID) &&
- unpack_TEField<LLColor4U>(tec.colors, tec.face_count, cur_ptr, buffer_end, MVT_U8) &&
- unpack_TEField<F32>(tec.scale_s, tec.face_count, cur_ptr, buffer_end, MVT_F32) &&
- unpack_TEField<F32>(tec.scale_t, tec.face_count, cur_ptr, buffer_end, MVT_F32) &&
- unpack_TEField<S16>(tec.offset_s, tec.face_count, cur_ptr, buffer_end, MVT_S16) &&
- unpack_TEField<S16>(tec.offset_t, tec.face_count, cur_ptr, buffer_end, MVT_S16) &&
- unpack_TEField<S16>(tec.image_rot, tec.face_count, cur_ptr, buffer_end, MVT_S16) &&
- unpack_TEField<U8>(tec.bump, tec.face_count, cur_ptr, buffer_end, MVT_U8) &&
- unpack_TEField<U8>(tec.media_flags, tec.face_count, cur_ptr, buffer_end, MVT_U8) &&
- unpack_TEField<U8>(tec.glow, tec.face_count, cur_ptr, buffer_end, MVT_U8)))
- {
- LL_WARNS("TEXTUREENTRY") << "Failure parsing Texture Entry Message due to malformed TE Field! Dropping changes on the floor. " << LL_ENDL;
- return 0;
- }
-
- if (cur_ptr >= buffer_end || !unpack_TEField<material_id_type>(material_data, tec.face_count, cur_ptr, buffer_end, MVT_LLUUID))
- {
- memset((void*)material_data, 0, sizeof(material_data));
- }
-
- for (U32 i = 0; i < tec.face_count; i++)
- {
- tec.material_ids[i].set(&(material_data[i]));
- }
-
- retval = 1;
- return retval;
- }
-
-S32 LLPrimitive::applyParsedTEMessage(LLTEContents& tec)
-{
- S32 retval = 0;
-
- LLColor4 color;
- for (U32 i = 0; i < tec.face_count; i++)
- {
- LLUUID& req_id = ((LLUUID*)tec.image_data)[i];
- retval |= setTETexture(i, req_id);
- retval |= setTEScale(i, tec.scale_s[i], tec.scale_t[i]);
- retval |= setTEOffset(i, (F32)tec.offset_s[i] / (F32)0x7FFF, (F32) tec.offset_t[i] / (F32) 0x7FFF);
- retval |= setTERotation(i, ((F32)tec.image_rot[i] / TEXTURE_ROTATION_PACK_FACTOR) * F_TWO_PI);
- retval |= setTEBumpShinyFullbright(i, tec.bump[i]);
- retval |= setTEMediaTexGen(i, tec.media_flags[i]);
- retval |= setTEGlow(i, (F32)tec.glow[i] / (F32)0xFF);
- retval |= setTEMaterialID(i, tec.material_ids[i]);
-
- // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f)
- // as all zeros. However, the subtraction and addition must be done in unsigned
- // byte space, not in float space, otherwise off-by-one errors occur. JC
- color.mV[VRED] = F32(255 - tec.colors[i].mV[VRED]) / 255.f;
- color.mV[VGREEN] = F32(255 - tec.colors[i].mV[VGREEN]) / 255.f;
- color.mV[VBLUE] = F32(255 - tec.colors[i].mV[VBLUE]) / 255.f;
- color.mV[VALPHA] = F32(255 - tec.colors[i].mV[VALPHA]) / 255.f;
-
- retval |= setTEColor(i, color);
- }
-
- return retval;
-}
-
-S32 LLPrimitive::unpackTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num)
-{
- LLTEContents tec;
- S32 retval = parseTEMessage(mesgsys, block_name, block_num, tec);
- if (!retval)
- return retval;
- return applyParsedTEMessage(tec);
-}
-
-S32 LLPrimitive::unpackTEMessage(LLDataPacker &dp)
-{
- // use a negative block_num to indicate a single-block read (a non-variable block)
- S32 retval = 0;
- constexpr U32 MAX_TES = 45;
-
- // Avoid construction of 32 UUIDs per call
- static LLMaterialID material_ids[MAX_TES];
-
- constexpr U32 MAX_TE_BUFFER = 4096;
- U8 packed_buffer[MAX_TE_BUFFER];
- memset((void*)packed_buffer, 0, MAX_TE_BUFFER);
-
- LLUUID image_data[MAX_TES];
- LLColor4U colors[MAX_TES];
- F32 scale_s[MAX_TES];
- F32 scale_t[MAX_TES];
- S16 offset_s[MAX_TES];
- S16 offset_t[MAX_TES];
- S16 image_rot[MAX_TES];
- U8 bump[MAX_TES];
- U8 media_flags[MAX_TES];
- U8 glow[MAX_TES];
- material_id_type material_data[MAX_TES];
-
- memset((void*)scale_s, 0, sizeof(scale_s));
- memset((void*)scale_t, 0, sizeof(scale_t));
- memset((void*)offset_s, 0, sizeof(offset_s));
- memset((void*)offset_t, 0, sizeof(offset_t));
- memset((void*)image_rot, 0, sizeof(image_rot));
- memset((void*)bump, 0, sizeof(bump));
- memset((void*)media_flags, 0, sizeof(media_flags));
- memset((void*)glow, 0, sizeof(glow));
-
- S32 size;
- U32 face_count = 0;
-
- if (!dp.unpackBinaryData(packed_buffer, size, "TextureEntry"))
- {
- retval = TEM_INVALID;
- LL_WARNS() << "Bad texture entry block! Abort!" << LL_ENDL;
- return retval;
- }
-
- if (size == 0)
- {
- return retval;
- }
- else if (size >= MAX_TE_BUFFER)
- {
- LL_WARNS("TEXTUREENTRY") << "Excessive buffer size detected in Texture Entry! Truncating." << LL_ENDL;
- size = MAX_TE_BUFFER - 1;
- }
-
- // The last field is not zero terminated.
- // Rather than special case the upack functions. Just make it 0x00 terminated.
- packed_buffer[size] = 0x00;
- ++size;
- face_count = llmin((U32) getNumTEs(), MAX_TES);
- U32 i;
-
- U8 *cur_ptr = packed_buffer;
- LL_DEBUGS("TEXTUREENTRY") << "Texture Entry with buffer sized: " << size << LL_ENDL;
- U8 *buffer_end = packed_buffer + size;
-
- if (!( unpack_TEField<LLUUID>(image_data, face_count, cur_ptr, buffer_end, MVT_LLUUID) &&
- unpack_TEField<LLColor4U>(colors, face_count, cur_ptr, buffer_end, MVT_U8) &&
- unpack_TEField<F32>(scale_s, face_count, cur_ptr, buffer_end, MVT_F32) &&
- unpack_TEField<F32>(scale_t, face_count, cur_ptr, buffer_end, MVT_F32) &&
- unpack_TEField<S16>(offset_s, face_count, cur_ptr, buffer_end, MVT_S16) &&
- unpack_TEField<S16>(offset_t, face_count, cur_ptr, buffer_end, MVT_S16) &&
- unpack_TEField<S16>(image_rot, face_count, cur_ptr, buffer_end, MVT_S16) &&
- unpack_TEField<U8>(bump, face_count, cur_ptr, buffer_end, MVT_U8) &&
- unpack_TEField<U8>(media_flags, face_count, cur_ptr, buffer_end, MVT_U8) &&
- unpack_TEField<U8>(glow, face_count, cur_ptr, buffer_end, MVT_U8)))
- {
- LL_WARNS("TEXTUREENTRY") << "Failure parsing Texture Entry Message due to malformed TE Field! Dropping changes on the floor. " << LL_ENDL;
- return 0;
- }
-
- if (cur_ptr >= buffer_end || !unpack_TEField<material_id_type>(material_data, face_count, cur_ptr, buffer_end, MVT_LLUUID))
- {
- memset((void*)material_data, 0, sizeof(material_data));
- }
-
- for (i = 0; i < face_count; i++)
- {
- material_ids[i].set(&(material_data[i]));
- }
-
- LLColor4 color;
- for (i = 0; i < face_count; i++)
- {
- retval |= setTETexture(i, ((LLUUID*)image_data)[i]);
- retval |= setTEScale(i, scale_s[i], scale_t[i]);
- retval |= setTEOffset(i, (F32)offset_s[i] / (F32)0x7FFF, (F32) offset_t[i] / (F32) 0x7FFF);
- retval |= setTERotation(i, ((F32)image_rot[i] / TEXTURE_ROTATION_PACK_FACTOR) * F_TWO_PI);
- retval |= setTEBumpShinyFullbright(i, bump[i]);
- retval |= setTEMediaTexGen(i, media_flags[i]);
- retval |= setTEGlow(i, (F32)glow[i] / (F32)0xFF);
- retval |= setTEMaterialID(i, material_ids[i]);
-
- // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f)
- // as all zeros. However, the subtraction and addition must be done in unsigned
- // byte space, not in float space, otherwise off-by-one errors occur. JC
- color.mV[VRED] = F32(255 - colors[i].mV[VRED]) / 255.f;
- color.mV[VGREEN] = F32(255 - colors[i].mV[VGREEN]) / 255.f;
- color.mV[VBLUE] = F32(255 - colors[i].mV[VBLUE]) / 255.f;
- color.mV[VALPHA] = F32(255 - colors[i].mV[VALPHA]) / 255.f;
-
- retval |= setTEColor(i, color);
- }
-
- return retval;
-}
-
-U8 LLPrimitive::getExpectedNumTEs() const
-{
- U8 expected_face_count = 0;
- if (mVolumep)
- {
- expected_face_count = mVolumep->getNumFaces();
- }
- return expected_face_count;
-}
-
-void LLPrimitive::copyTextureList(const LLPrimTextureList& other_list)
-{
- mTextureList.copy(other_list);
-}
-
-void LLPrimitive::takeTextureList(LLPrimTextureList& other_list)
-{
- mTextureList.take(other_list);
-}
-
-void LLPrimitive::updateNumBumpmap(const U8 index, const U8 bump)
-{
- LLTextureEntry* te = getTE(index);
- if(!te)
- {
- return;
- }
-
- U8 old_bump = te->getBumpmap();
- if(old_bump > 0)
- {
- mNumBumpmapTEs--;
- }
- if((bump & TEM_BUMP_MASK) > 0)
- {
- mNumBumpmapTEs++;
- }
-
- return;
-}
-//============================================================================
-
-// Moved from llselectmgr.cpp
-// BUG: Only works for boxes.
-// Face numbering for flex boxes as of 1.14.2
-
-// static
-bool LLPrimitive::getTESTAxes(const U8 face, U32* s_axis, U32* t_axis)
-{
- if (face == 0)
- {
- *s_axis = VX; *t_axis = VY;
- return true;
- }
- else if (face == 1)
- {
- *s_axis = VX; *t_axis = VZ;
- return true;
- }
- else if (face == 2)
- {
- *s_axis = VY; *t_axis = VZ;
- return true;
- }
- else if (face == 3)
- {
- *s_axis = VX; *t_axis = VZ;
- return true;
- }
- else if (face == 4)
- {
- *s_axis = VY; *t_axis = VZ;
- return true;
- }
- else if (face >= 5)
- {
- *s_axis = VX; *t_axis = VY;
- return true;
- }
- else
- {
- // unknown face
- return false;
- }
-}
-
-//============================================================================
-
-//static
-bool LLNetworkData::isValid(U16 param_type, U32 size)
-{
- // ew - better mechanism needed
-
- switch(param_type)
- {
- case PARAMS_FLEXIBLE:
- return (size == 16);
- case PARAMS_LIGHT:
- return (size == 16);
- case PARAMS_SCULPT:
- return (size == 17);
- case PARAMS_LIGHT_IMAGE:
- return (size == 28);
- case PARAMS_EXTENDED_MESH:
- return (size == 4);
- case PARAMS_RENDER_MATERIAL:
- return (size > 1);
- case PARAMS_REFLECTION_PROBE:
- return (size == 9);
- }
-
- return false;
-}
-
-//============================================================================
-
-LLLightParams::LLLightParams()
-{
- mColor.setToWhite();
- mRadius = 10.f;
- mCutoff = 0.0f;
- mFalloff = 0.75f;
-
- mType = PARAMS_LIGHT;
-}
-
-bool LLLightParams::pack(LLDataPacker &dp) const
-{
- LLColor4U color4u(mColor);
- dp.packColor4U(color4u, "color");
- dp.packF32(mRadius, "radius");
- dp.packF32(mCutoff, "cutoff");
- dp.packF32(mFalloff, "falloff");
- return true;
-}
-
-bool LLLightParams::unpack(LLDataPacker &dp)
-{
- LLColor4U color;
- dp.unpackColor4U(color, "color");
- setLinearColor(LLColor4(color));
-
- F32 radius;
- dp.unpackF32(radius, "radius");
- setRadius(radius);
-
- F32 cutoff;
- dp.unpackF32(cutoff, "cutoff");
- setCutoff(cutoff);
-
- F32 falloff;
- dp.unpackF32(falloff, "falloff");
- setFalloff(falloff);
-
- return true;
-}
-
-bool LLLightParams::operator==(const LLNetworkData& data) const
-{
- if (data.mType != PARAMS_LIGHT)
- {
- return false;
- }
- const LLLightParams *param = (const LLLightParams*)&data;
- if (param->mColor != mColor ||
- param->mRadius != mRadius ||
- param->mCutoff != mCutoff ||
- param->mFalloff != mFalloff)
- {
- return false;
- }
- return true;
-}
-
-void LLLightParams::copy(const LLNetworkData& data)
-{
- const LLLightParams *param = (LLLightParams*)&data;
- mType = param->mType;
- mColor = param->mColor;
- mRadius = param->mRadius;
- mCutoff = param->mCutoff;
- mFalloff = param->mFalloff;
-}
-
-LLSD LLLightParams::asLLSD() const
-{
- LLSD sd;
-
- sd["color"] = ll_sd_from_color4(getLinearColor());
- sd["radius"] = getRadius();
- sd["falloff"] = getFalloff();
- sd["cutoff"] = getCutoff();
-
- return sd;
-}
-
-bool LLLightParams::fromLLSD(LLSD& sd)
-{
- const char *w;
- w = "color";
- if (sd.has(w))
- {
- setLinearColor( ll_color4_from_sd(sd["color"]) );
- } else goto fail;
- w = "radius";
- if (sd.has(w))
- {
- setRadius( (F32)sd[w].asReal() );
- } else goto fail;
- w = "falloff";
- if (sd.has(w))
- {
- setFalloff( (F32)sd[w].asReal() );
- } else goto fail;
- w = "cutoff";
- if (sd.has(w))
- {
- setCutoff( (F32)sd[w].asReal() );
- } else goto fail;
-
- return true;
- fail:
- return false;
-}
-
-//============================================================================
-
-//============================================================================
-
-LLReflectionProbeParams::LLReflectionProbeParams()
-{
- mType = PARAMS_REFLECTION_PROBE;
-}
-
-bool LLReflectionProbeParams::pack(LLDataPacker &dp) const
-{
- dp.packF32(mAmbiance, "ambiance");
- dp.packF32(mClipDistance, "clip_distance");
- dp.packU8(mFlags, "flags");
- return true;
-}
-
-bool LLReflectionProbeParams::unpack(LLDataPacker &dp)
-{
- F32 ambiance;
- F32 clip_distance;
-
- dp.unpackF32(ambiance, "ambiance");
- setAmbiance(ambiance);
-
- dp.unpackF32(clip_distance, "clip_distance");
- setClipDistance(clip_distance);
-
- dp.unpackU8(mFlags, "flags");
-
- return true;
-}
-
-bool LLReflectionProbeParams::operator==(const LLNetworkData& data) const
-{
- if (data.mType != PARAMS_REFLECTION_PROBE)
- {
- return false;
- }
- const LLReflectionProbeParams *param = (const LLReflectionProbeParams*)&data;
- if (param->mAmbiance != mAmbiance)
- {
- return false;
- }
- if (param->mClipDistance != mClipDistance)
- {
- return false;
- }
- if (param->mFlags != mFlags)
- {
- return false;
- }
- return true;
-}
-
-void LLReflectionProbeParams::copy(const LLNetworkData& data)
-{
- const LLReflectionProbeParams *param = (LLReflectionProbeParams*)&data;
- mType = param->mType;
- mAmbiance = param->mAmbiance;
- mClipDistance = param->mClipDistance;
- mFlags = param->mFlags;
-}
-
-LLSD LLReflectionProbeParams::asLLSD() const
-{
- LLSD sd;
- sd["ambiance"] = getAmbiance();
- sd["clip_distance"] = getClipDistance();
- sd["flags"] = mFlags;
- return sd;
-}
-
-bool LLReflectionProbeParams::fromLLSD(LLSD& sd)
-{
- if (!sd.has("ambiance") ||
- !sd.has("clip_distance") ||
- !sd.has("flags"))
- {
- return false;
- }
-
- setAmbiance((F32)sd["ambiance"].asReal());
- setClipDistance((F32)sd["clip_distance"].asReal());
- mFlags = (U8) sd["flags"].asInteger();
-
- return true;
-}
-
-void LLReflectionProbeParams::setIsBox(bool is_box)
-{
- if (is_box)
- {
- mFlags |= FLAG_BOX_VOLUME;
- }
- else
- {
- mFlags &= ~FLAG_BOX_VOLUME;
- }
-}
-
-void LLReflectionProbeParams::setIsDynamic(bool is_dynamic)
-{
- if (is_dynamic)
- {
- mFlags |= FLAG_DYNAMIC;
- }
- else
- {
- mFlags &= ~FLAG_DYNAMIC;
- }
-}
-
-//============================================================================
-LLFlexibleObjectData::LLFlexibleObjectData()
-{
- mSimulateLOD = FLEXIBLE_OBJECT_DEFAULT_NUM_SECTIONS;
- mGravity = FLEXIBLE_OBJECT_DEFAULT_GRAVITY;
- mAirFriction = FLEXIBLE_OBJECT_DEFAULT_AIR_FRICTION;
- mWindSensitivity = FLEXIBLE_OBJECT_DEFAULT_WIND_SENSITIVITY;
- mTension = FLEXIBLE_OBJECT_DEFAULT_TENSION;
- //mUsingCollisionSphere = FLEXIBLE_OBJECT_DEFAULT_USING_COLLISION_SPHERE;
- //mRenderingCollisionSphere = FLEXIBLE_OBJECT_DEFAULT_RENDERING_COLLISION_SPHERE;
- mUserForce = LLVector3(0.f, 0.f, 0.f);
-
- mType = PARAMS_FLEXIBLE;
-}
-
-bool LLFlexibleObjectData::pack(LLDataPacker &dp) const
-{
- // Custom, uber-svelte pack "softness" in upper bits of tension & drag
- U8 bit1 = (mSimulateLOD & 2) << 6;
- U8 bit2 = (mSimulateLOD & 1) << 7;
- dp.packU8((U8)(mTension*10.01f) + bit1, "tension");
- dp.packU8((U8)(mAirFriction*10.01f) + bit2, "drag");
- dp.packU8((U8)((mGravity+10.f)*10.01f), "gravity");
- dp.packU8((U8)(mWindSensitivity*10.01f), "wind");
- dp.packVector3(mUserForce, "userforce");
- return true;
-}
-
-bool LLFlexibleObjectData::unpack(LLDataPacker &dp)
-{
- U8 tension, friction, gravity, wind;
- U8 bit1, bit2;
- dp.unpackU8(tension, "tension"); bit1 = (tension >> 6) & 2;
- mTension = ((F32)(tension&0x7f))/10.f;
- dp.unpackU8(friction, "drag"); bit2 = (friction >> 7) & 1;
- mAirFriction = ((F32)(friction&0x7f))/10.f;
- mSimulateLOD = bit1 | bit2;
- dp.unpackU8(gravity, "gravity"); mGravity = ((F32)gravity)/10.f - 10.f;
- dp.unpackU8(wind, "wind"); mWindSensitivity = ((F32)wind)/10.f;
- if (dp.hasNext())
- {
- dp.unpackVector3(mUserForce, "userforce");
- }
- else
- {
- mUserForce.setVec(0.f, 0.f, 0.f);
- }
- return true;
-}
-
-bool LLFlexibleObjectData::operator==(const LLNetworkData& data) const
-{
- if (data.mType != PARAMS_FLEXIBLE)
- {
- return false;
- }
- LLFlexibleObjectData *flex_data = (LLFlexibleObjectData*)&data;
- return (mSimulateLOD == flex_data->mSimulateLOD &&
- mGravity == flex_data->mGravity &&
- mAirFriction == flex_data->mAirFriction &&
- mWindSensitivity == flex_data->mWindSensitivity &&
- mTension == flex_data->mTension &&
- mUserForce == flex_data->mUserForce);
- //mUsingCollisionSphere == flex_data->mUsingCollisionSphere &&
- //mRenderingCollisionSphere == flex_data->mRenderingCollisionSphere
-}
-
-void LLFlexibleObjectData::copy(const LLNetworkData& data)
-{
- const LLFlexibleObjectData *flex_data = (LLFlexibleObjectData*)&data;
- mSimulateLOD = flex_data->mSimulateLOD;
- mGravity = flex_data->mGravity;
- mAirFriction = flex_data->mAirFriction;
- mWindSensitivity = flex_data->mWindSensitivity;
- mTension = flex_data->mTension;
- mUserForce = flex_data->mUserForce;
- //mUsingCollisionSphere = flex_data->mUsingCollisionSphere;
- //mRenderingCollisionSphere = flex_data->mRenderingCollisionSphere;
-}
-
-LLSD LLFlexibleObjectData::asLLSD() const
-{
- LLSD sd;
-
- sd["air_friction"] = getAirFriction();
- sd["gravity"] = getGravity();
- sd["simulate_lod"] = getSimulateLOD();
- sd["tension"] = getTension();
- sd["user_force"] = getUserForce().getValue();
- sd["wind_sensitivity"] = getWindSensitivity();
-
- return sd;
-}
-
-bool LLFlexibleObjectData::fromLLSD(LLSD& sd)
-{
- const char *w;
- w = "air_friction";
- if (sd.has(w))
- {
- setAirFriction( (F32)sd[w].asReal() );
- } else goto fail;
- w = "gravity";
- if (sd.has(w))
- {
- setGravity( (F32)sd[w].asReal() );
- } else goto fail;
- w = "simulate_lod";
- if (sd.has(w))
- {
- setSimulateLOD( sd[w].asInteger() );
- } else goto fail;
- w = "tension";
- if (sd.has(w))
- {
- setTension( (F32)sd[w].asReal() );
- } else goto fail;
- w = "user_force";
- if (sd.has(w))
- {
- LLVector3 user_force = ll_vector3_from_sd(sd[w], 0);
- setUserForce( user_force );
- } else goto fail;
- w = "wind_sensitivity";
- if (sd.has(w))
- {
- setWindSensitivity( (F32)sd[w].asReal() );
- } else goto fail;
-
- return true;
- fail:
- return false;
-}
-
-//============================================================================
-
-LLSculptParams::LLSculptParams()
-{
- mType = PARAMS_SCULPT;
- mSculptTexture = SCULPT_DEFAULT_TEXTURE;
- mSculptType = LL_SCULPT_TYPE_SPHERE;
-}
-
-bool LLSculptParams::pack(LLDataPacker &dp) const
-{
- dp.packUUID(mSculptTexture, "texture");
- dp.packU8(mSculptType, "type");
-
- return true;
-}
-
-bool LLSculptParams::unpack(LLDataPacker &dp)
-{
- U8 type;
- LLUUID id;
- dp.unpackUUID(id, "texture");
- dp.unpackU8(type, "type");
-
- setSculptTexture(id, type);
- return true;
-}
-
-bool LLSculptParams::operator==(const LLNetworkData& data) const
-{
- if (data.mType != PARAMS_SCULPT)
- {
- return false;
- }
-
- const LLSculptParams *param = (const LLSculptParams*)&data;
- if ( (param->mSculptTexture != mSculptTexture) ||
- (param->mSculptType != mSculptType) )
-
- {
- return false;
- }
-
- return true;
-}
-
-void LLSculptParams::copy(const LLNetworkData& data)
-{
- const LLSculptParams *param = (LLSculptParams*)&data;
- setSculptTexture(param->mSculptTexture, param->mSculptType);
-}
-
-
-
-LLSD LLSculptParams::asLLSD() const
-{
- LLSD sd;
-
- sd["texture"] = mSculptTexture;
- sd["type"] = mSculptType;
-
- return sd;
-}
-
-bool LLSculptParams::fromLLSD(LLSD& sd)
-{
- const char *w;
- U8 type;
- w = "type";
- if (sd.has(w))
- {
- type = sd[w].asInteger();
- }
- else return false;
-
- w = "texture";
- if (sd.has(w))
- {
- setSculptTexture(sd[w], type);
- }
- else return false;
-
- return true;
-}
-
-void LLSculptParams::setSculptTexture(const LLUUID& texture_id, U8 sculpt_type)
-{
- U8 type = sculpt_type & LL_SCULPT_TYPE_MASK;
- U8 flags = sculpt_type & LL_SCULPT_FLAG_MASK;
- if (sculpt_type != (type | flags) || type > LL_SCULPT_TYPE_MAX)
- {
- mSculptTexture = SCULPT_DEFAULT_TEXTURE;
- mSculptType = LL_SCULPT_TYPE_SPHERE;
- }
- else
- {
- mSculptTexture = texture_id;
- mSculptType = sculpt_type;
- }
-}
-
-//============================================================================
-
-LLLightImageParams::LLLightImageParams()
-{
- mType = PARAMS_LIGHT_IMAGE;
- mParams.setVec(F_PI*0.5f, 0.f, 0.f);
-}
-
-bool LLLightImageParams::pack(LLDataPacker &dp) const
-{
- dp.packUUID(mLightTexture, "texture");
- dp.packVector3(mParams, "params");
-
- return true;
-}
-
-bool LLLightImageParams::unpack(LLDataPacker &dp)
-{
- dp.unpackUUID(mLightTexture, "texture");
- dp.unpackVector3(mParams, "params");
-
- return true;
-}
-
-bool LLLightImageParams::operator==(const LLNetworkData& data) const
-{
- if (data.mType != PARAMS_LIGHT_IMAGE)
- {
- return false;
- }
-
- const LLLightImageParams *param = (const LLLightImageParams*)&data;
- if ( (param->mLightTexture != mLightTexture) )
- {
- return false;
- }
-
- if ( (param->mParams != mParams ) )
- {
- return false;
- }
-
- return true;
-}
-
-void LLLightImageParams::copy(const LLNetworkData& data)
-{
- const LLLightImageParams *param = (LLLightImageParams*)&data;
- mLightTexture = param->mLightTexture;
- mParams = param->mParams;
-}
-
-
-
-LLSD LLLightImageParams::asLLSD() const
-{
- LLSD sd;
-
- sd["texture"] = mLightTexture;
- sd["params"] = mParams.getValue();
-
- return sd;
-}
-
-bool LLLightImageParams::fromLLSD(LLSD& sd)
-{
- if (sd.has("texture"))
- {
- setLightTexture( sd["texture"] );
- setParams( LLVector3( sd["params"] ) );
- return true;
- }
-
- return false;
-}
-
-//============================================================================
-
-LLExtendedMeshParams::LLExtendedMeshParams()
-{
- mType = PARAMS_EXTENDED_MESH;
- mFlags = 0;
-}
-
-bool LLExtendedMeshParams::pack(LLDataPacker &dp) const
-{
- dp.packU32(mFlags, "flags");
-
- return true;
-}
-
-bool LLExtendedMeshParams::unpack(LLDataPacker &dp)
-{
- dp.unpackU32(mFlags, "flags");
-
- return true;
-}
-
-bool LLExtendedMeshParams::operator==(const LLNetworkData& data) const
-{
- if (data.mType != PARAMS_EXTENDED_MESH)
- {
- return false;
- }
-
- const LLExtendedMeshParams *param = (const LLExtendedMeshParams*)&data;
- if ( (param->mFlags != mFlags) )
- {
- return false;
- }
-
- return true;
-}
-
-void LLExtendedMeshParams::copy(const LLNetworkData& data)
-{
- const LLExtendedMeshParams *param = (LLExtendedMeshParams*)&data;
- mFlags = param->mFlags;
-}
-
-LLSD LLExtendedMeshParams::asLLSD() const
-{
- LLSD sd;
-
- sd["flags"] = LLSD::Integer(mFlags);
-
- return sd;
-}
-
-bool LLExtendedMeshParams::fromLLSD(LLSD& sd)
-{
- if (sd.has("flags"))
- {
- setFlags( sd["flags"].asInteger());
- return true;
- }
-
- return false;
-}
-
-//============================================================================
-
-LLRenderMaterialParams::LLRenderMaterialParams()
-{
- mType = PARAMS_RENDER_MATERIAL;
-}
-
-bool LLRenderMaterialParams::pack(LLDataPacker& dp) const
-{
- U8 count = (U8)llmin((S32)mEntries.size(), 14); //limited to 255 bytes, no more than 14 material ids
-
- dp.packU8(count, "count");
- for (auto& entry : mEntries)
- {
- dp.packU8(entry.te_idx, "te_idx");
- dp.packUUID(entry.id, "id");
- }
-
- return true;
-}
-
-bool LLRenderMaterialParams::unpack(LLDataPacker& dp)
-{
- U8 count;
- dp.unpackU8(count, "count");
- mEntries.resize(count);
- for (auto& entry : mEntries)
- {
- dp.unpackU8(entry.te_idx, "te_idx");
- dp.unpackUUID(entry.id, "te_id");
- }
-
- return true;
-}
-
-bool LLRenderMaterialParams::operator==(const LLNetworkData& data) const
-{
- if (data.mType != PARAMS_RENDER_MATERIAL)
- {
- return false;
- }
-
- const LLRenderMaterialParams& param = static_cast<const LLRenderMaterialParams&>(data);
-
- if (param.mEntries.size() != mEntries.size())
- {
- return false;
- }
-
- for (auto& entry : mEntries)
- {
- if (param.getMaterial(entry.te_idx) != entry.id)
- {
- return false;
- }
- }
-
- return true;
-}
-
-void LLRenderMaterialParams::copy(const LLNetworkData& data)
-{
- llassert_always(data.mType == PARAMS_RENDER_MATERIAL);
- const LLRenderMaterialParams& param = static_cast<const LLRenderMaterialParams&>(data);
- mEntries = param.mEntries;
-}
-
-
-void LLRenderMaterialParams::setMaterial(U8 te, const LLUUID& id)
-{
- for (int i = 0; i < mEntries.size(); ++i)
- {
- if (mEntries[i].te_idx == te)
- {
- if (id.isNull())
- {
- mEntries.erase(mEntries.begin() + i);
- }
- else
- {
- mEntries[i].id = id;
- }
- return;
- }
- }
-
- mEntries.push_back({ te, id });
-}
-
-const LLUUID& LLRenderMaterialParams::getMaterial(U8 te) const
-{
- for (int i = 0; i < mEntries.size(); ++i)
- {
- if (mEntries[i].te_idx == te)
- {
- return mEntries[i].id;
- }
- }
-
- return LLUUID::null;
-}
-
+/** + * @file llprimitive.cpp + * @brief LLPrimitive base class + * + * $LicenseInfo:firstyear=2001&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 "linden_common.h" + +#include "material_codes.h" +#include "llerror.h" +#include "message.h" +#include "llprimitive.h" +#include "llvolume.h" +#include "legacy_object_types.h" +#include "v4coloru.h" +#include "llvolumemgr.h" +#include "llstring.h" +#include "lldatapacker.h" +#include "llsdutil_math.h" +#include "llprimtexturelist.h" +#include "llmaterialid.h" +#include "llsdutil.h" + +/** + * exported constants + */ + +const F32 OBJECT_CUT_MIN = 0.f; +const F32 OBJECT_CUT_MAX = 1.f; +const F32 OBJECT_CUT_INC = 0.05f; +const F32 OBJECT_MIN_CUT_INC = 0.02f; +const F32 OBJECT_ROTATION_PRECISION = 0.05f; + +const F32 OBJECT_TWIST_MIN = -360.f; +const F32 OBJECT_TWIST_MAX = 360.f; +const F32 OBJECT_TWIST_INC = 18.f; + +// This is used for linear paths, +// since twist is used in a slightly different manner. +const F32 OBJECT_TWIST_LINEAR_MIN = -180.f; +const F32 OBJECT_TWIST_LINEAR_MAX = 180.f; +const F32 OBJECT_TWIST_LINEAR_INC = 9.f; + +const F32 OBJECT_MIN_HOLE_SIZE = 0.05f; +const F32 OBJECT_MAX_HOLE_SIZE_X = 1.0f; +const F32 OBJECT_MAX_HOLE_SIZE_Y = 0.5f; + +// Revolutions parameters. +const F32 OBJECT_REV_MIN = 1.0f; +const F32 OBJECT_REV_MAX = 4.0f; +const F32 OBJECT_REV_INC = 0.1f; + +// lights +const F32 LIGHT_MIN_RADIUS = 0.0f; +const F32 LIGHT_DEFAULT_RADIUS = 5.0f; +const F32 LIGHT_MAX_RADIUS = 20.0f; +const F32 LIGHT_MIN_FALLOFF = 0.0f; +const F32 LIGHT_DEFAULT_FALLOFF = 1.0f; +const F32 LIGHT_MAX_FALLOFF = 2.0f; +const F32 LIGHT_MIN_CUTOFF = 0.0f; +const F32 LIGHT_DEFAULT_CUTOFF = 0.0f; +const F32 LIGHT_MAX_CUTOFF = 180.f; + +// reflection probes +const F32 REFLECTION_PROBE_MIN_AMBIANCE = 0.f; +const F32 REFLECTION_PROBE_MAX_AMBIANCE = 100.f; +const F32 REFLECTION_PROBE_DEFAULT_AMBIANCE = 0.f; +// *NOTE: Clip distances are clamped in LLCamera::setNear. The max clip +// distance is currently limited by the skybox +const F32 REFLECTION_PROBE_MIN_CLIP_DISTANCE = 0.f; +const F32 REFLECTION_PROBE_MAX_CLIP_DISTANCE = 1024.f; +const F32 REFLECTION_PROBE_DEFAULT_CLIP_DISTANCE = 0.f; + +// "Tension" => [0,10], increments of 0.1 +const F32 FLEXIBLE_OBJECT_MIN_TENSION = 0.0f; +const F32 FLEXIBLE_OBJECT_DEFAULT_TENSION = 1.0f; +const F32 FLEXIBLE_OBJECT_MAX_TENSION = 10.0f; + +// "Drag" => [0,10], increments of 0.1 +const F32 FLEXIBLE_OBJECT_MIN_AIR_FRICTION = 0.0f; +const F32 FLEXIBLE_OBJECT_DEFAULT_AIR_FRICTION = 2.0f; +const F32 FLEXIBLE_OBJECT_MAX_AIR_FRICTION = 10.0f; + +// "Gravity" = [-10,10], increments of 0.1 +const F32 FLEXIBLE_OBJECT_MIN_GRAVITY = -10.0f; +const F32 FLEXIBLE_OBJECT_DEFAULT_GRAVITY = 0.3f; +const F32 FLEXIBLE_OBJECT_MAX_GRAVITY = 10.0f; + +// "Wind" = [0,10], increments of 0.1 +const F32 FLEXIBLE_OBJECT_MIN_WIND_SENSITIVITY = 0.0f; +const F32 FLEXIBLE_OBJECT_DEFAULT_WIND_SENSITIVITY = 0.0f; +const F32 FLEXIBLE_OBJECT_MAX_WIND_SENSITIVITY = 10.0f; + +// I'll explain later... +const F32 FLEXIBLE_OBJECT_MAX_INTERNAL_TENSION_FORCE = 0.99f; + +const F32 FLEXIBLE_OBJECT_DEFAULT_LENGTH = 1.0f; +const bool FLEXIBLE_OBJECT_DEFAULT_USING_COLLISION_SPHERE = false; +const bool FLEXIBLE_OBJECT_DEFAULT_RENDERING_COLLISION_SPHERE = false; + +const LLUUID SCULPT_DEFAULT_TEXTURE("be293869-d0d9-0a69-5989-ad27f1946fd4"); // old inverted texture: "7595d345-a24c-e7ef-f0bd-78793792133e"; + +// Texture rotations are sent over the wire as a S16. This is used to scale the actual float +// value to a S16. Don't use 7FFF as it introduces some odd rounding with 180 since it +// can't be divided by 2. See DEV-19108 +const F32 TEXTURE_ROTATION_PACK_FACTOR = ((F32) 0x08000); + +struct material_id_type // originally from llrendermaterialtable +{ + material_id_type() + { + memset((void*)m_value, 0, sizeof(m_value)); + } + + bool operator==(const material_id_type& other) const + { + return (memcmp(m_value, other.m_value, sizeof(m_value)) == 0); + } + + bool operator!=(const material_id_type& other) const + { + return !operator==(other); + } + + bool isNull() const + { + return (memcmp(m_value, s_null_id, sizeof(m_value)) == 0); + } + + U8 m_value[MATERIAL_ID_SIZE]; // server side this is MD5RAW_BYTES + + static const U8 s_null_id[MATERIAL_ID_SIZE]; +}; + +const U8 material_id_type::s_null_id[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + +//static +// LEGACY: by default we use the LLVolumeMgr::gVolumeMgr global +// TODO -- eliminate this global from the codebase! +LLVolumeMgr* LLPrimitive::sVolumeManager = NULL; + +// static +void LLPrimitive::setVolumeManager( LLVolumeMgr* volume_manager ) +{ + if ( !volume_manager || sVolumeManager ) + { + LL_ERRS() << "LLPrimitive::sVolumeManager attempting to be set to NULL or it already has been set." << LL_ENDL; + } + sVolumeManager = volume_manager; +} + +// static +bool LLPrimitive::cleanupVolumeManager() +{ + bool res = false; + if (sVolumeManager) + { + res = sVolumeManager->cleanup(); + delete sVolumeManager; + sVolumeManager = NULL; + } + return res; +} + + +//=============================================================== +LLPrimitive::LLPrimitive() +: mTextureList(), + mNumTEs(0), + mMiscFlags(0), + mNumBumpmapTEs(0) +{ + mPrimitiveCode = 0; + + mMaterial = LL_MCODE_STONE; + mVolumep = NULL; + + mChanged = UNCHANGED; + + mPosition.setVec(0.f,0.f,0.f); + mVelocity.setVec(0.f,0.f,0.f); + mAcceleration.setVec(0.f,0.f,0.f); + + mRotation.loadIdentity(); + mAngularVelocity.setVec(0.f,0.f,0.f); + + mScale.setVec(1.f,1.f,1.f); +} + +//=============================================================== +LLPrimitive::~LLPrimitive() +{ + clearTextureList(); + // Cleanup handled by volume manager + if (mVolumep && sVolumeManager) + { + sVolumeManager->unrefVolume(mVolumep); + } + mVolumep = NULL; +} + +void LLPrimitive::clearTextureList() +{ +} + +//=============================================================== +// static +LLPrimitive *LLPrimitive::createPrimitive(LLPCode p_code) +{ + LLPrimitive *retval = new LLPrimitive(); + + if (retval) + { + retval->init_primitive(p_code); + } + else + { + LL_ERRS() << "primitive allocation failed" << LL_ENDL; + } + + return retval; +} + +//=============================================================== +void LLPrimitive::init_primitive(LLPCode p_code) +{ + clearTextureList(); + mPrimitiveCode = p_code; +} + +void LLPrimitive::setPCode(const U8 p_code) +{ + mPrimitiveCode = p_code; +} + +//=============================================================== +LLTextureEntry* LLPrimitive::getTE(const U8 index) const +{ + return mTextureList.getTexture(index); +} + +//=============================================================== +void LLPrimitive::setNumTEs(const U8 num_tes) +{ + mTextureList.setSize(num_tes); +} + +//=============================================================== +void LLPrimitive::setAllTETextures(const LLUUID &tex_id) +{ + mTextureList.setAllIDs(tex_id); +} + +//=============================================================== +void LLPrimitive::setTE(const U8 index, const LLTextureEntry& te) +{ + if(mTextureList.copyTexture(index, te) != TEM_CHANGE_NONE && te.getBumpmap() > 0) + { + mNumBumpmapTEs++; + } +} + +S32 LLPrimitive::setTETexture(const U8 index, const LLUUID &id) +{ + return mTextureList.setID(index, id); +} + +S32 LLPrimitive::setTEColor(const U8 index, const LLColor4 &color) +{ + return mTextureList.setColor(index, color); +} + +S32 LLPrimitive::setTEColor(const U8 index, const LLColor3 &color) +{ + return mTextureList.setColor(index, color); +} + +S32 LLPrimitive::setTEAlpha(const U8 index, const F32 alpha) +{ + return mTextureList.setAlpha(index, alpha); +} + +//=============================================================== +S32 LLPrimitive::setTEScale(const U8 index, const F32 s, const F32 t) +{ + return mTextureList.setScale(index, s, t); +} + + +// BUG: slow - done this way because texture entries have some +// voodoo related to texture coords +S32 LLPrimitive::setTEScaleS(const U8 index, const F32 s) +{ + return mTextureList.setScaleS(index, s); +} + + +// BUG: slow - done this way because texture entries have some +// voodoo related to texture coords +S32 LLPrimitive::setTEScaleT(const U8 index, const F32 t) +{ + return mTextureList.setScaleT(index, t); +} + + +//=============================================================== +S32 LLPrimitive::setTEOffset(const U8 index, const F32 s, const F32 t) +{ + return mTextureList.setOffset(index, s, t); +} + + +// BUG: slow - done this way because texture entries have some +// voodoo related to texture coords +S32 LLPrimitive::setTEOffsetS(const U8 index, const F32 s) +{ + return mTextureList.setOffsetS(index, s); +} + + +// BUG: slow - done this way because texture entries have some +// voodoo related to texture coords +S32 LLPrimitive::setTEOffsetT(const U8 index, const F32 t) +{ + return mTextureList.setOffsetT(index, t); +} + + +//=============================================================== +S32 LLPrimitive::setTERotation(const U8 index, const F32 r) +{ + return mTextureList.setRotation(index, r); +} + +S32 LLPrimitive::setTEMaterialID(const U8 index, const LLMaterialID& pMaterialID) +{ + return mTextureList.setMaterialID(index, pMaterialID); +} + +S32 LLPrimitive::setTEMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams) +{ + return mTextureList.setMaterialParams(index, pMaterialParams); +} + +LLMaterialPtr LLPrimitive::getTEMaterialParams(const U8 index) +{ + return mTextureList.getMaterialParams(index); +} + +//=============================================================== +S32 LLPrimitive::setTEBumpShinyFullbright(const U8 index, const U8 bump) +{ + updateNumBumpmap(index, bump); + return mTextureList.setBumpShinyFullbright(index, bump); +} + +S32 LLPrimitive::setTEMediaTexGen(const U8 index, const U8 media) +{ + return mTextureList.setMediaTexGen(index, media); +} + +S32 LLPrimitive::setTEBumpmap(const U8 index, const U8 bump) +{ + updateNumBumpmap(index, bump); + return mTextureList.setBumpMap(index, bump); +} + +S32 LLPrimitive::setTEBumpShiny(const U8 index, const U8 bump_shiny) +{ + updateNumBumpmap(index, bump_shiny); + return mTextureList.setBumpShiny(index, bump_shiny); +} + +S32 LLPrimitive::setTETexGen(const U8 index, const U8 texgen) +{ + return mTextureList.setTexGen(index, texgen); +} + +S32 LLPrimitive::setTEShiny(const U8 index, const U8 shiny) +{ + return mTextureList.setShiny(index, shiny); +} + +S32 LLPrimitive::setTEFullbright(const U8 index, const U8 fullbright) +{ + return mTextureList.setFullbright(index, fullbright); +} + +S32 LLPrimitive::setTEMediaFlags(const U8 index, const U8 media_flags) +{ + return mTextureList.setMediaFlags(index, media_flags); +} + +S32 LLPrimitive::setTEGlow(const U8 index, const F32 glow) +{ + return mTextureList.setGlow(index, glow); +} + +void LLPrimitive::setAllTESelected(bool sel) +{ + for (int i = 0, cnt = getNumTEs(); i < cnt; i++) + { + setTESelected(i, sel); + } +} + +void LLPrimitive::setTESelected(const U8 te, bool sel) +{ + LLTextureEntry* tep = getTE(te); + if ( (tep) && (tep->setSelected(sel)) && (!sel) && (tep->hasPendingMaterialUpdate()) ) + { + LLMaterialID material_id = tep->getMaterialID(); + setTEMaterialID(te, material_id); + } +} + +LLPCode LLPrimitive::legacyToPCode(const U8 legacy) +{ + // TODO: Should this default to something valid? + // Maybe volume? + LLPCode pcode = 0; + + switch (legacy) + { + /* + case BOX: + pcode = LL_PCODE_CUBE; + break; + case CYLINDER: + pcode = LL_PCODE_CYLINDER; + break; + case CONE: + pcode = LL_PCODE_CONE; + break; + case HALF_CONE: + pcode = LL_PCODE_CONE_HEMI; + break; + case HALF_CYLINDER: + pcode = LL_PCODE_CYLINDER_HEMI; + break; + case HALF_SPHERE: + pcode = LL_PCODE_SPHERE_HEMI; + break; + case PRISM: + pcode = LL_PCODE_PRISM; + break; + case PYRAMID: + pcode = LL_PCODE_PYRAMID; + break; + case SPHERE: + pcode = LL_PCODE_SPHERE; + break; + case TETRAHEDRON: + pcode = LL_PCODE_TETRAHEDRON; + break; + case DEMON: + pcode = LL_PCODE_LEGACY_DEMON; + break; + case LSL_TEST: + pcode = LL_PCODE_LEGACY_LSL_TEST; + break; + case ORACLE: + pcode = LL_PCODE_LEGACY_ORACLE; + break; + case TEXTBUBBLE: + pcode = LL_PCODE_LEGACY_TEXT_BUBBLE; + break; + case ATOR: + pcode = LL_PCODE_LEGACY_ATOR; + break; + case BASIC_SHOT: + pcode = LL_PCODE_LEGACY_SHOT; + break; + case BIG_SHOT: + pcode = LL_PCODE_LEGACY_SHOT_BIG; + break; + case BIRD: + pcode = LL_PCODE_LEGACY_BIRD; + break; + case ROCK: + pcode = LL_PCODE_LEGACY_ROCK; + break; + case SMOKE: + pcode = LL_PCODE_LEGACY_SMOKE; + break; + case SPARK: + pcode = LL_PCODE_LEGACY_SPARK; + break; + */ + case PRIMITIVE_VOLUME: + pcode = LL_PCODE_VOLUME; + break; + case GRASS: + pcode = LL_PCODE_LEGACY_GRASS; + break; + case PART_SYS: + pcode = LL_PCODE_LEGACY_PART_SYS; + break; + case PLAYER: + pcode = LL_PCODE_LEGACY_AVATAR; + break; + case TREE: + pcode = LL_PCODE_LEGACY_TREE; + break; + case TREE_NEW: + pcode = LL_PCODE_TREE_NEW; + break; + default: + LL_WARNS() << "Unknown legacy code " << legacy << " [" << (S32)legacy << "]!" << LL_ENDL; + } + + return pcode; +} + +U8 LLPrimitive::pCodeToLegacy(const LLPCode pcode) +{ + U8 legacy; + switch (pcode) + { +/* + case LL_PCODE_CUBE: + legacy = BOX; + break; + case LL_PCODE_CYLINDER: + legacy = CYLINDER; + break; + case LL_PCODE_CONE: + legacy = CONE; + break; + case LL_PCODE_CONE_HEMI: + legacy = HALF_CONE; + break; + case LL_PCODE_CYLINDER_HEMI: + legacy = HALF_CYLINDER; + break; + case LL_PCODE_SPHERE_HEMI: + legacy = HALF_SPHERE; + break; + case LL_PCODE_PRISM: + legacy = PRISM; + break; + case LL_PCODE_PYRAMID: + legacy = PYRAMID; + break; + case LL_PCODE_SPHERE: + legacy = SPHERE; + break; + case LL_PCODE_TETRAHEDRON: + legacy = TETRAHEDRON; + break; + case LL_PCODE_LEGACY_ATOR: + legacy = ATOR; + break; + case LL_PCODE_LEGACY_SHOT: + legacy = BASIC_SHOT; + break; + case LL_PCODE_LEGACY_SHOT_BIG: + legacy = BIG_SHOT; + break; + case LL_PCODE_LEGACY_BIRD: + legacy = BIRD; + break; + case LL_PCODE_LEGACY_DEMON: + legacy = DEMON; + break; + case LL_PCODE_LEGACY_LSL_TEST: + legacy = LSL_TEST; + break; + case LL_PCODE_LEGACY_ORACLE: + legacy = ORACLE; + break; + case LL_PCODE_LEGACY_ROCK: + legacy = ROCK; + break; + case LL_PCODE_LEGACY_TEXT_BUBBLE: + legacy = TEXTBUBBLE; + break; + case LL_PCODE_LEGACY_SMOKE: + legacy = SMOKE; + break; + case LL_PCODE_LEGACY_SPARK: + legacy = SPARK; + break; +*/ + case LL_PCODE_VOLUME: + legacy = PRIMITIVE_VOLUME; + break; + case LL_PCODE_LEGACY_GRASS: + legacy = GRASS; + break; + case LL_PCODE_LEGACY_PART_SYS: + legacy = PART_SYS; + break; + case LL_PCODE_LEGACY_AVATAR: + legacy = PLAYER; + break; + case LL_PCODE_LEGACY_TREE: + legacy = TREE; + break; + case LL_PCODE_TREE_NEW: + legacy = TREE_NEW; + break; + default: + LL_WARNS() << "Unknown pcode " << (S32)pcode << ":" << pcode << "!" << LL_ENDL; + return 0; + } + return legacy; +} + + +// static +// Don't crash or LL_ERRS() here! This function is used for debug strings. +std::string LLPrimitive::pCodeToString(const LLPCode pcode) +{ + std::string pcode_string; + + U8 base_code = pcode & LL_PCODE_BASE_MASK; + if (!pcode) + { + pcode_string = "null"; + } + else if ((base_code) == LL_PCODE_LEGACY) + { + // It's a legacy object + switch (pcode) + { + case LL_PCODE_LEGACY_GRASS: + pcode_string = "grass"; + break; + case LL_PCODE_LEGACY_PART_SYS: + pcode_string = "particle system"; + break; + case LL_PCODE_LEGACY_AVATAR: + pcode_string = "avatar"; + break; + case LL_PCODE_LEGACY_TEXT_BUBBLE: + pcode_string = "text bubble"; + break; + case LL_PCODE_LEGACY_TREE: + pcode_string = "tree"; + break; + case LL_PCODE_TREE_NEW: + pcode_string = "tree_new"; + break; + default: + pcode_string = llformat( "unknown legacy pcode %i",(U32)pcode); + } + } + else + { + std::string shape; + std::string mask; + if (base_code == LL_PCODE_CUBE) + { + shape = "cube"; + } + else if (base_code == LL_PCODE_CYLINDER) + { + shape = "cylinder"; + } + else if (base_code == LL_PCODE_CONE) + { + shape = "cone"; + } + else if (base_code == LL_PCODE_PRISM) + { + shape = "prism"; + } + else if (base_code == LL_PCODE_PYRAMID) + { + shape = "pyramid"; + } + else if (base_code == LL_PCODE_SPHERE) + { + shape = "sphere"; + } + else if (base_code == LL_PCODE_TETRAHEDRON) + { + shape = "tetrahedron"; + } + else if (base_code == LL_PCODE_VOLUME) + { + shape = "volume"; + } + else if (base_code == LL_PCODE_APP) + { + shape = "app"; + } + else + { + LL_WARNS() << "Unknown base mask for pcode: " << base_code << LL_ENDL; + } + + U8 mask_code = pcode & (~LL_PCODE_BASE_MASK); + if (base_code == LL_PCODE_APP) + { + mask = llformat( "%x", mask_code); + } + else if (mask_code & LL_PCODE_HEMI_MASK) + { + mask = "hemi"; + } + else + { + mask = llformat( "%x", mask_code); + } + + if (mask[0]) + { + pcode_string = llformat( "%s-%s", shape.c_str(), mask.c_str()); + } + else + { + pcode_string = llformat( "%s", shape.c_str()); + } + } + + return pcode_string; +} + + +void LLPrimitive::copyTEs(const LLPrimitive *primitivep) +{ + U32 i; + if (primitivep->getExpectedNumTEs() != getExpectedNumTEs()) + { + LL_WARNS() << "Primitives don't have same expected number of TE's" << LL_ENDL; + } + U32 num_tes = llmin(primitivep->getExpectedNumTEs(), getExpectedNumTEs()); + if (mTextureList.size() < getExpectedNumTEs()) + { + mTextureList.setSize(getExpectedNumTEs()); + } + for (i = 0; i < num_tes; i++) + { + mTextureList.copyTexture(i, *(primitivep->getTE(i))); + } +} + +S32 face_index_from_id(LLFaceID face_ID, const std::vector<LLProfile::Face>& faceArray) +{ + S32 i; + for (i = 0; i < (S32)faceArray.size(); i++) + { + if (faceArray[i].mFaceID == face_ID) + { + return i; + } + } + return -1; +} + +bool LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume) +{ + if (NO_LOD == detail) + { + // build the new object + setChanged(GEOMETRY); + sVolumeManager->unrefVolume(mVolumep); + mVolumep = new LLVolume(volume_params, 1, true, true); + setNumTEs(mVolumep->getNumFaces()); + return false; + } + + LLVolume *volumep; + if (unique_volume) + { + F32 volume_detail = LLVolumeLODGroup::getVolumeScaleFromDetail(detail); + if (mVolumep.notNull() && volume_params == mVolumep->getParams() && (volume_detail == mVolumep->getDetail())) + { + return false; + } + volumep = new LLVolume(volume_params, volume_detail, false, true); + } + else + { + if (mVolumep.notNull()) + { + F32 volume_detail = LLVolumeLODGroup::getVolumeScaleFromDetail(detail); + if (volume_params == mVolumep->getParams() && (volume_detail == mVolumep->getDetail())) + { + return false; + } + } + + volumep = sVolumeManager->refVolume(volume_params, detail); + if (volumep == mVolumep) + { + sVolumeManager->unrefVolume( volumep ); // LLVolumeMgr::refVolume() creates a reference, but we don't need a second one. + return true; + } + } + + setChanged(GEOMETRY); + + + if (!mVolumep) + { + mVolumep = volumep; + //mFaceMask = mVolumep->generateFaceMask(); + setNumTEs(mVolumep->getNumFaces()); + return true; + } + +#if 0 + // #if 0'd out by davep + // this is a lot of cruft to set texture entry values that just stay the same for LOD switch + // or immediately get overridden by an object update message, also crashes occasionally + U32 old_face_mask = mVolumep->mFaceMask; + + S32 face_bit = 0; + S32 cur_mask = 0; + + // Grab copies of the old faces from the original shape, ordered by type. + // We will use these to figure out what old texture info gets mapped to new + // faces in the new shape. + std::vector<LLProfile::Face> old_faces; + for (S32 face = 0; face < mVolumep->getNumFaces(); face++) + { + old_faces.push_back(mVolumep->getProfile().mFaces[face]); + } + + // Copy the old texture info off to the side, but not in the order in which + // they live in the mTextureList, rather in order of ther "face id" which + // is the corresponding value of LLVolueParams::LLProfile::mFaces::mIndex. + // + // Hence, some elements of old_tes::mEntryList will be invalid. It is + // initialized to a size of 9 (max number of possible faces on a volume?) + // and only the ones with valid types are filled in. + LLPrimTextureList old_tes; + old_tes.setSize(9); + for (face_bit = 0; face_bit < 9; face_bit++) + { + cur_mask = 0x1 << face_bit; + if (old_face_mask & cur_mask) + { + S32 te_index = face_index_from_id(cur_mask, old_faces); + old_tes.copyTexture(face_bit, *(getTE(te_index))); + //LL_INFOS() << face_bit << ":" << te_index << ":" << old_tes[face_bit].getID() << LL_ENDL; + } + } + + + // build the new object + sVolumeManager->unrefVolume(mVolumep); + mVolumep = volumep; + + U32 new_face_mask = mVolumep->mFaceMask; + S32 i; + + if (old_face_mask == new_face_mask) + { + // nothing to do + return true; + } + + if (mVolumep->getNumFaces() == 0 && new_face_mask != 0) + { + LL_WARNS() << "Object with 0 faces found...INCORRECT!" << LL_ENDL; + setNumTEs(mVolumep->getNumFaces()); + return true; + } + + // initialize face_mapping + S32 face_mapping[9]; + for (face_bit = 0; face_bit < 9; face_bit++) + { + face_mapping[face_bit] = face_bit; + } + + // The new shape may have more faces than the original, but we can't just + // add them to the end -- the ordering matters and it may be that we must + // insert the new faces in the middle of the list. When we add a face it + // will pick up the texture/color info of one of the old faces an so we + // now figure out which old face info gets mapped to each new face, and + // store in the face_mapping lookup table. + for (face_bit = 0; face_bit < 9; face_bit++) + { + cur_mask = 0x1 << face_bit; + if (!(new_face_mask & cur_mask)) + { + // Face doesn't exist in new map. + face_mapping[face_bit] = -1; + continue; + } + else if (old_face_mask & cur_mask) + { + // Face exists in new and old map. + face_mapping[face_bit] = face_bit; + continue; + } + + // OK, how we've got a mismatch, where we have to fill a new face with one from + // the old face. + if (cur_mask & (LL_FACE_PATH_BEGIN | LL_FACE_PATH_END | LL_FACE_INNER_SIDE)) + { + // It's a top/bottom/hollow interior face. + if (old_face_mask & LL_FACE_PATH_END) + { + face_mapping[face_bit] = 1; + continue; + } + else + { + S32 cur_outer_mask = LL_FACE_OUTER_SIDE_0; + for (i = 0; i < 4; i++) + { + if (old_face_mask & cur_outer_mask) + { + face_mapping[face_bit] = 5 + i; + break; + } + cur_outer_mask <<= 1; + } + if (i == 4) + { + LL_WARNS() << "No path end or outer face in volume!" << LL_ENDL; + } + continue; + } + } + + if (cur_mask & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END)) + { + // A cut slice. Use the hollow interior if we have it. + if (old_face_mask & LL_FACE_INNER_SIDE) + { + face_mapping[face_bit] = 2; + continue; + } + + // No interior, use the bottom face. + // Could figure out which of the outer faces was nearest, but that would be harder. + if (old_face_mask & LL_FACE_PATH_END) + { + face_mapping[face_bit] = 1; + continue; + } + else + { + S32 cur_outer_mask = LL_FACE_OUTER_SIDE_0; + for (i = 0; i < 4; i++) + { + if (old_face_mask & cur_outer_mask) + { + face_mapping[face_bit] = 5 + i; + break; + } + cur_outer_mask <<= 1; + } + if (i == 4) + { + LL_WARNS() << "No path end or outer face in volume!" << LL_ENDL; + } + continue; + } + } + + // OK, the face that's missing is an outer face... + // Pull from the nearest adjacent outer face (there's always guaranteed to be one... + S32 cur_outer = face_bit - 5; + S32 min_dist = 5; + S32 min_outer_bit = -1; + S32 i; + for (i = 0; i < 4; i++) + { + if (old_face_mask & (LL_FACE_OUTER_SIDE_0 << i)) + { + S32 dist = abs(i - cur_outer); + if (dist < min_dist) + { + min_dist = dist; + min_outer_bit = i + 5; + } + } + } + if (-1 == min_outer_bit) + { + LL_INFOS() << (LLVolume *)mVolumep << LL_ENDL; + LL_WARNS() << "Bad! No outer faces, impossible!" << LL_ENDL; + } + face_mapping[face_bit] = min_outer_bit; + } + + + setNumTEs(mVolumep->getNumFaces()); + for (face_bit = 0; face_bit < 9; face_bit++) + { + // For each possible face type on the new shape we check to see if that + // face exists and if it does we create a texture entry that is a copy + // of one of the originals. Since the originals might not have a + // matching face, we use the face_mapping lookup table to figure out + // which face information to copy. + cur_mask = 0x1 << face_bit; + if (new_face_mask & cur_mask) + { + if (-1 == face_mapping[face_bit]) + { + LL_WARNS() << "No mapping from old face to new face!" << LL_ENDL; + } + + S32 te_num = face_index_from_id(cur_mask, mVolumep->getProfile().mFaces); + setTE(te_num, *(old_tes.getTexture(face_mapping[face_bit]))); + } + } +#else + // build the new object + sVolumeManager->unrefVolume(mVolumep); + mVolumep = volumep; + + setNumTEs(mVolumep->getNumFaces()); +#endif + return true; +} + +bool LLPrimitive::setMaterial(U8 material) +{ + if (material != mMaterial) + { + mMaterial = material; + return true; + } + else + { + return false; + } +} + +S32 LLPrimitive::packTEField(U8 *cur_ptr, U8 *data_ptr, U8 data_size, U8 last_face_index, EMsgVariableType type) const +{ + S32 face_index; + S32 i; + U64 exception_faces; + U8 *start_loc = cur_ptr; + + htolememcpy(cur_ptr,data_ptr + (last_face_index * data_size), type, data_size); + cur_ptr += data_size; + + for (face_index = last_face_index-1; face_index >= 0; face_index--) + { + bool already_sent = false; + for (i = face_index+1; i <= last_face_index; i++) + { + if (!memcmp(data_ptr+(data_size *face_index), data_ptr+(data_size *i), data_size)) + { + already_sent = true; + break; + } + } + + if (!already_sent) + { + exception_faces = 0; + for (i = face_index; i >= 0; i--) + { + if (!memcmp(data_ptr+(data_size *face_index), data_ptr+(data_size *i), data_size)) + { + exception_faces |= ((U64)1 << i); + } + } + + //assign exception faces to cur_ptr + if (exception_faces >= ((U64)0x1 << 7)) + { + if (exception_faces >= ((U64)0x1 << 14)) + { + if (exception_faces >= ((U64)0x1 << 21)) + { + if (exception_faces >= ((U64)0x1 << 28)) + { + if (exception_faces >= ((U64)0x1 << 35)) + { + if (exception_faces >= ((U64)0x1 << 42)) + { + if (exception_faces >= ((U64)0x1 << 49)) + { + *cur_ptr++ = (U8)(((exception_faces >> 49) & 0x7F) | 0x80); + } + *cur_ptr++ = (U8)(((exception_faces >> 42) & 0x7F) | 0x80); + } + *cur_ptr++ = (U8)(((exception_faces >> 35) & 0x7F) | 0x80); + } + *cur_ptr++ = (U8)(((exception_faces >> 28) & 0x7F) | 0x80); + } + *cur_ptr++ = (U8)(((exception_faces >> 21) & 0x7F) | 0x80); + } + *cur_ptr++ = (U8)(((exception_faces >> 14) & 0x7F) | 0x80); + } + *cur_ptr++ = (U8)(((exception_faces >> 7) & 0x7F) | 0x80); + } + + + *cur_ptr++ = (U8)(exception_faces & 0x7F); + + htolememcpy(cur_ptr,data_ptr + (face_index * data_size), type, data_size); + cur_ptr += data_size; + } + } + return (S32)(cur_ptr - start_loc); +} + +namespace +{ + template< typename T > + bool unpack_TEField(T dest[], U8 dest_count, U8 * &source, U8 *source_end, EMsgVariableType type) + { + const size_t size(sizeof(T)); + + LL_DEBUGS("TEXTUREENTRY") << "Request to read items of size " << size << " with swizzle " << type << " froum buffer sized " << (source_end - source) << LL_ENDL; + + if ((source + size + 1) > source_end) + { + // we add 1 above to take into account the byte that we know must follow the value. + LL_WARNS("TEXTUREENTRY") << "Buffer exhausted! Requires " << size << " + 1 bytes for default, " << (source_end - source) << " bytes remaning." << LL_ENDL; + source = source_end; + return false; + } + + // Extract the default value and fill the array. + htolememcpy(dest, source, type, size); + source += size; + for (S32 idx = 1; idx < dest_count; ++idx) + { + dest[idx] = dest[0]; + } + + while (source < source_end) + { + U64 index_flags(0); + U8 sbit(0); + + // Unpack the variable length bitfield. Each bit represents whether the following + // value will be placed at the corresponding array index. + do + { + if (source >= source_end) + { + LL_WARNS("TEXTUREENTRY") << "Buffer exhausted! Reading index flags." << LL_ENDL; + source = source_end; + return false; + } + + sbit = *source++; + index_flags <<= 7; // original code had this after? + index_flags |= (sbit & 0x7F); + } while (sbit & 0x80); + + if (!index_flags) + { // We've hit the terminating 0 byte. + break; + } + + if ((source + size + 1) > source_end) + { + // we add 1 above to take into account the byte that we know must follow the value. + LL_WARNS("TEXTUREENTRY") << "Buffer exhausted! Requires " << size << " + 1 bytes for default, " << (source_end - source) << " bytes remaning." << LL_ENDL; + source = source_end; + return false; + } + + // get the value for the indexs. + T value; + htolememcpy(&value, source, type, size); + source += size; + + for (S32 idx = 0; idx < dest_count; idx++) + { + if (index_flags & 1ULL << idx) + { + dest[idx] = value; + } + } + + } + return true; + } +} + + + +// Pack information about all texture entries into container: +// { TextureEntry Variable 2 } +// Includes information about image ID, color, scale S,T, offset S,T and rotation +bool LLPrimitive::packTEMessage(LLMessageSystem *mesgsys) const +{ + const U32 MAX_TES = 45; + + U8 image_ids[MAX_TES*16]; + U8 colors[MAX_TES*4]; + F32 scale_s[MAX_TES]; + F32 scale_t[MAX_TES]; + S16 offset_s[MAX_TES]; + S16 offset_t[MAX_TES]; + S16 image_rot[MAX_TES]; + U8 bump[MAX_TES]; + U8 media_flags[MAX_TES]; + U8 glow[MAX_TES]; + U8 material_data[MAX_TES*16]; + + const U32 MAX_TE_BUFFER = 4096; + U8 packed_buffer[MAX_TE_BUFFER]; + U8 *cur_ptr = packed_buffer; + + S32 last_face_index = llmin((U32) getNumTEs(), MAX_TES) - 1; + + if (last_face_index > -1) + { + // ...if we hit the front, send one image id + S8 face_index; + LLColor4U coloru; + for (face_index = 0; face_index <= last_face_index; face_index++) + { + // Directly sending image_ids is not safe! + memcpy(&image_ids[face_index*16],getTE(face_index)->getID().mData,16); /* Flawfinder: ignore */ + + // Cast LLColor4 to LLColor4U + coloru.setVec( getTE(face_index)->getColor() ); + + // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f) + // as all zeros. However, the subtraction and addition must be done in unsigned + // byte space, not in float space, otherwise off-by-one errors occur. JC + colors[4*face_index] = 255 - coloru.mV[0]; + colors[4*face_index + 1] = 255 - coloru.mV[1]; + colors[4*face_index + 2] = 255 - coloru.mV[2]; + colors[4*face_index + 3] = 255 - coloru.mV[3]; + + const LLTextureEntry* te = getTE(face_index); + scale_s[face_index] = (F32) te->mScaleS; + scale_t[face_index] = (F32) te->mScaleT; + offset_s[face_index] = (S16) ll_round((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; + offset_t[face_index] = (S16) ll_round((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; + image_rot[face_index] = (S16) ll_round(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); + bump[face_index] = te->getBumpShinyFullbright(); + media_flags[face_index] = te->getMediaTexGen(); + glow[face_index] = (U8) ll_round((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); + + // Directly sending material_ids is not safe! + memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */ + } + + cur_ptr += packTEField(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)scale_s, 4 ,last_face_index, MVT_F32); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)scale_t, 4 ,last_face_index, MVT_F32); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)glow, 1 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)material_data, 16, last_face_index, MVT_LLUUID); + } + mesgsys->addBinaryDataFast(_PREHASH_TextureEntry, packed_buffer, (S32)(cur_ptr - packed_buffer)); + + return true; +} + + +bool LLPrimitive::packTEMessage(LLDataPacker &dp) const +{ + const U32 MAX_TES = 45; + + U8 image_ids[MAX_TES*16]; + U8 colors[MAX_TES*4]; + F32 scale_s[MAX_TES]; + F32 scale_t[MAX_TES]; + S16 offset_s[MAX_TES]; + S16 offset_t[MAX_TES]; + S16 image_rot[MAX_TES]; + U8 bump[MAX_TES]; + U8 media_flags[MAX_TES]; + U8 glow[MAX_TES]; + U8 material_data[MAX_TES*16]; + + const U32 MAX_TE_BUFFER = 4096; + U8 packed_buffer[MAX_TE_BUFFER]; + U8 *cur_ptr = packed_buffer; + + S32 last_face_index = getNumTEs() - 1; + + if (last_face_index > -1) + { + // ...if we hit the front, send one image id + S8 face_index; + LLColor4U coloru; + for (face_index = 0; face_index <= last_face_index; face_index++) + { + // Directly sending image_ids is not safe! + memcpy(&image_ids[face_index*16],getTE(face_index)->getID().mData,16); /* Flawfinder: ignore */ + + // Cast LLColor4 to LLColor4U + coloru.setVec( getTE(face_index)->getColor() ); + + // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f) + // as all zeros. However, the subtraction and addition must be done in unsigned + // byte space, not in float space, otherwise off-by-one errors occur. JC + colors[4*face_index] = 255 - coloru.mV[0]; + colors[4*face_index + 1] = 255 - coloru.mV[1]; + colors[4*face_index + 2] = 255 - coloru.mV[2]; + colors[4*face_index + 3] = 255 - coloru.mV[3]; + + const LLTextureEntry* te = getTE(face_index); + scale_s[face_index] = (F32) te->mScaleS; + scale_t[face_index] = (F32) te->mScaleT; + offset_s[face_index] = (S16) ll_round((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; + offset_t[face_index] = (S16) ll_round((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; + image_rot[face_index] = (S16) ll_round(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); + bump[face_index] = te->getBumpShinyFullbright(); + media_flags[face_index] = te->getMediaTexGen(); + glow[face_index] = (U8) ll_round((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); + + // Directly sending material_ids is not safe! + memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */ + } + + cur_ptr += packTEField(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)scale_s, 4 ,last_face_index, MVT_F32); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)scale_t, 4 ,last_face_index, MVT_F32); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)glow, 1 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += packTEField(cur_ptr, (U8 *)material_data, 16, last_face_index, MVT_LLUUID); + } + + dp.packBinaryData(packed_buffer, (S32)(cur_ptr - packed_buffer), "TextureEntry"); + return true; +} + +S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec) +{ + S32 retval = 0; + // temp buffer for material ID processing + // data will end up in tec.material_id[] + material_id_type material_data[LLTEContents::MAX_TES]; + + if (block_num < 0) + { + tec.size = mesgsys->getSizeFast(block_name, _PREHASH_TextureEntry); + } + else + { + tec.size = mesgsys->getSizeFast(block_name, block_num, _PREHASH_TextureEntry); + } + + if (tec.size == 0) + { + tec.face_count = 0; + return retval; + } + else if (tec.size >= LLTEContents::MAX_TE_BUFFER) + { + LL_WARNS("TEXTUREENTRY") << "Excessive buffer size detected in Texture Entry! Truncating." << LL_ENDL; + tec.size = LLTEContents::MAX_TE_BUFFER - 1; + } + + // if block_num < 0 ask for block 0 + mesgsys->getBinaryDataFast(block_name, _PREHASH_TextureEntry, tec.packed_buffer, 0, std::max(block_num, 0), LLTEContents::MAX_TE_BUFFER - 1); + + // The last field is not zero terminated. + // Rather than special case the upack functions. Just make it 0x00 terminated. + tec.packed_buffer[tec.size] = 0x00; + ++tec.size; + + tec.face_count = llmin((U32)getNumTEs(),(U32)LLTEContents::MAX_TES); + + U8 *cur_ptr = tec.packed_buffer; + LL_DEBUGS("TEXTUREENTRY") << "Texture Entry with buffere sized: " << tec.size << LL_ENDL; + U8 *buffer_end = tec.packed_buffer + tec.size; + + if (!( unpack_TEField<LLUUID>(tec.image_data, tec.face_count, cur_ptr, buffer_end, MVT_LLUUID) && + unpack_TEField<LLColor4U>(tec.colors, tec.face_count, cur_ptr, buffer_end, MVT_U8) && + unpack_TEField<F32>(tec.scale_s, tec.face_count, cur_ptr, buffer_end, MVT_F32) && + unpack_TEField<F32>(tec.scale_t, tec.face_count, cur_ptr, buffer_end, MVT_F32) && + unpack_TEField<S16>(tec.offset_s, tec.face_count, cur_ptr, buffer_end, MVT_S16) && + unpack_TEField<S16>(tec.offset_t, tec.face_count, cur_ptr, buffer_end, MVT_S16) && + unpack_TEField<S16>(tec.image_rot, tec.face_count, cur_ptr, buffer_end, MVT_S16) && + unpack_TEField<U8>(tec.bump, tec.face_count, cur_ptr, buffer_end, MVT_U8) && + unpack_TEField<U8>(tec.media_flags, tec.face_count, cur_ptr, buffer_end, MVT_U8) && + unpack_TEField<U8>(tec.glow, tec.face_count, cur_ptr, buffer_end, MVT_U8))) + { + LL_WARNS("TEXTUREENTRY") << "Failure parsing Texture Entry Message due to malformed TE Field! Dropping changes on the floor. " << LL_ENDL; + return 0; + } + + if (cur_ptr >= buffer_end || !unpack_TEField<material_id_type>(material_data, tec.face_count, cur_ptr, buffer_end, MVT_LLUUID)) + { + memset((void*)material_data, 0, sizeof(material_data)); + } + + for (U32 i = 0; i < tec.face_count; i++) + { + tec.material_ids[i].set(&(material_data[i])); + } + + retval = 1; + return retval; + } + +S32 LLPrimitive::applyParsedTEMessage(LLTEContents& tec) +{ + S32 retval = 0; + + LLColor4 color; + for (U32 i = 0; i < tec.face_count; i++) + { + LLUUID& req_id = ((LLUUID*)tec.image_data)[i]; + retval |= setTETexture(i, req_id); + retval |= setTEScale(i, tec.scale_s[i], tec.scale_t[i]); + retval |= setTEOffset(i, (F32)tec.offset_s[i] / (F32)0x7FFF, (F32) tec.offset_t[i] / (F32) 0x7FFF); + retval |= setTERotation(i, ((F32)tec.image_rot[i] / TEXTURE_ROTATION_PACK_FACTOR) * F_TWO_PI); + retval |= setTEBumpShinyFullbright(i, tec.bump[i]); + retval |= setTEMediaTexGen(i, tec.media_flags[i]); + retval |= setTEGlow(i, (F32)tec.glow[i] / (F32)0xFF); + retval |= setTEMaterialID(i, tec.material_ids[i]); + + // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f) + // as all zeros. However, the subtraction and addition must be done in unsigned + // byte space, not in float space, otherwise off-by-one errors occur. JC + color.mV[VRED] = F32(255 - tec.colors[i].mV[VRED]) / 255.f; + color.mV[VGREEN] = F32(255 - tec.colors[i].mV[VGREEN]) / 255.f; + color.mV[VBLUE] = F32(255 - tec.colors[i].mV[VBLUE]) / 255.f; + color.mV[VALPHA] = F32(255 - tec.colors[i].mV[VALPHA]) / 255.f; + + retval |= setTEColor(i, color); + } + + return retval; +} + +S32 LLPrimitive::unpackTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num) +{ + LLTEContents tec; + S32 retval = parseTEMessage(mesgsys, block_name, block_num, tec); + if (!retval) + return retval; + return applyParsedTEMessage(tec); +} + +S32 LLPrimitive::unpackTEMessage(LLDataPacker &dp) +{ + // use a negative block_num to indicate a single-block read (a non-variable block) + S32 retval = 0; + constexpr U32 MAX_TES = 45; + + // Avoid construction of 32 UUIDs per call + static LLMaterialID material_ids[MAX_TES]; + + constexpr U32 MAX_TE_BUFFER = 4096; + U8 packed_buffer[MAX_TE_BUFFER]; + memset((void*)packed_buffer, 0, MAX_TE_BUFFER); + + LLUUID image_data[MAX_TES]; + LLColor4U colors[MAX_TES]; + F32 scale_s[MAX_TES]; + F32 scale_t[MAX_TES]; + S16 offset_s[MAX_TES]; + S16 offset_t[MAX_TES]; + S16 image_rot[MAX_TES]; + U8 bump[MAX_TES]; + U8 media_flags[MAX_TES]; + U8 glow[MAX_TES]; + material_id_type material_data[MAX_TES]; + + memset((void*)scale_s, 0, sizeof(scale_s)); + memset((void*)scale_t, 0, sizeof(scale_t)); + memset((void*)offset_s, 0, sizeof(offset_s)); + memset((void*)offset_t, 0, sizeof(offset_t)); + memset((void*)image_rot, 0, sizeof(image_rot)); + memset((void*)bump, 0, sizeof(bump)); + memset((void*)media_flags, 0, sizeof(media_flags)); + memset((void*)glow, 0, sizeof(glow)); + + S32 size; + U32 face_count = 0; + + if (!dp.unpackBinaryData(packed_buffer, size, "TextureEntry")) + { + retval = TEM_INVALID; + LL_WARNS() << "Bad texture entry block! Abort!" << LL_ENDL; + return retval; + } + + if (size == 0) + { + return retval; + } + else if (size >= MAX_TE_BUFFER) + { + LL_WARNS("TEXTUREENTRY") << "Excessive buffer size detected in Texture Entry! Truncating." << LL_ENDL; + size = MAX_TE_BUFFER - 1; + } + + // The last field is not zero terminated. + // Rather than special case the upack functions. Just make it 0x00 terminated. + packed_buffer[size] = 0x00; + ++size; + face_count = llmin((U32) getNumTEs(), MAX_TES); + U32 i; + + U8 *cur_ptr = packed_buffer; + LL_DEBUGS("TEXTUREENTRY") << "Texture Entry with buffer sized: " << size << LL_ENDL; + U8 *buffer_end = packed_buffer + size; + + if (!( unpack_TEField<LLUUID>(image_data, face_count, cur_ptr, buffer_end, MVT_LLUUID) && + unpack_TEField<LLColor4U>(colors, face_count, cur_ptr, buffer_end, MVT_U8) && + unpack_TEField<F32>(scale_s, face_count, cur_ptr, buffer_end, MVT_F32) && + unpack_TEField<F32>(scale_t, face_count, cur_ptr, buffer_end, MVT_F32) && + unpack_TEField<S16>(offset_s, face_count, cur_ptr, buffer_end, MVT_S16) && + unpack_TEField<S16>(offset_t, face_count, cur_ptr, buffer_end, MVT_S16) && + unpack_TEField<S16>(image_rot, face_count, cur_ptr, buffer_end, MVT_S16) && + unpack_TEField<U8>(bump, face_count, cur_ptr, buffer_end, MVT_U8) && + unpack_TEField<U8>(media_flags, face_count, cur_ptr, buffer_end, MVT_U8) && + unpack_TEField<U8>(glow, face_count, cur_ptr, buffer_end, MVT_U8))) + { + LL_WARNS("TEXTUREENTRY") << "Failure parsing Texture Entry Message due to malformed TE Field! Dropping changes on the floor. " << LL_ENDL; + return 0; + } + + if (cur_ptr >= buffer_end || !unpack_TEField<material_id_type>(material_data, face_count, cur_ptr, buffer_end, MVT_LLUUID)) + { + memset((void*)material_data, 0, sizeof(material_data)); + } + + for (i = 0; i < face_count; i++) + { + material_ids[i].set(&(material_data[i])); + } + + LLColor4 color; + for (i = 0; i < face_count; i++) + { + retval |= setTETexture(i, ((LLUUID*)image_data)[i]); + retval |= setTEScale(i, scale_s[i], scale_t[i]); + retval |= setTEOffset(i, (F32)offset_s[i] / (F32)0x7FFF, (F32) offset_t[i] / (F32) 0x7FFF); + retval |= setTERotation(i, ((F32)image_rot[i] / TEXTURE_ROTATION_PACK_FACTOR) * F_TWO_PI); + retval |= setTEBumpShinyFullbright(i, bump[i]); + retval |= setTEMediaTexGen(i, media_flags[i]); + retval |= setTEGlow(i, (F32)glow[i] / (F32)0xFF); + retval |= setTEMaterialID(i, material_ids[i]); + + // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f) + // as all zeros. However, the subtraction and addition must be done in unsigned + // byte space, not in float space, otherwise off-by-one errors occur. JC + color.mV[VRED] = F32(255 - colors[i].mV[VRED]) / 255.f; + color.mV[VGREEN] = F32(255 - colors[i].mV[VGREEN]) / 255.f; + color.mV[VBLUE] = F32(255 - colors[i].mV[VBLUE]) / 255.f; + color.mV[VALPHA] = F32(255 - colors[i].mV[VALPHA]) / 255.f; + + retval |= setTEColor(i, color); + } + + return retval; +} + +U8 LLPrimitive::getExpectedNumTEs() const +{ + U8 expected_face_count = 0; + if (mVolumep) + { + expected_face_count = mVolumep->getNumFaces(); + } + return expected_face_count; +} + +void LLPrimitive::copyTextureList(const LLPrimTextureList& other_list) +{ + mTextureList.copy(other_list); +} + +void LLPrimitive::takeTextureList(LLPrimTextureList& other_list) +{ + mTextureList.take(other_list); +} + +void LLPrimitive::updateNumBumpmap(const U8 index, const U8 bump) +{ + LLTextureEntry* te = getTE(index); + if(!te) + { + return; + } + + U8 old_bump = te->getBumpmap(); + if(old_bump > 0) + { + mNumBumpmapTEs--; + } + if((bump & TEM_BUMP_MASK) > 0) + { + mNumBumpmapTEs++; + } + + return; +} +//============================================================================ + +// Moved from llselectmgr.cpp +// BUG: Only works for boxes. +// Face numbering for flex boxes as of 1.14.2 + +// static +bool LLPrimitive::getTESTAxes(const U8 face, U32* s_axis, U32* t_axis) +{ + if (face == 0) + { + *s_axis = VX; *t_axis = VY; + return true; + } + else if (face == 1) + { + *s_axis = VX; *t_axis = VZ; + return true; + } + else if (face == 2) + { + *s_axis = VY; *t_axis = VZ; + return true; + } + else if (face == 3) + { + *s_axis = VX; *t_axis = VZ; + return true; + } + else if (face == 4) + { + *s_axis = VY; *t_axis = VZ; + return true; + } + else if (face >= 5) + { + *s_axis = VX; *t_axis = VY; + return true; + } + else + { + // unknown face + return false; + } +} + +//============================================================================ + +//static +bool LLNetworkData::isValid(U16 param_type, U32 size) +{ + // ew - better mechanism needed + + switch(param_type) + { + case PARAMS_FLEXIBLE: + return (size == 16); + case PARAMS_LIGHT: + return (size == 16); + case PARAMS_SCULPT: + return (size == 17); + case PARAMS_LIGHT_IMAGE: + return (size == 28); + case PARAMS_EXTENDED_MESH: + return (size == 4); + case PARAMS_RENDER_MATERIAL: + return (size > 1); + case PARAMS_REFLECTION_PROBE: + return (size == 9); + } + + return false; +} + +//============================================================================ + +LLLightParams::LLLightParams() +{ + mColor.setToWhite(); + mRadius = 10.f; + mCutoff = 0.0f; + mFalloff = 0.75f; + + mType = PARAMS_LIGHT; +} + +bool LLLightParams::pack(LLDataPacker &dp) const +{ + LLColor4U color4u(mColor); + dp.packColor4U(color4u, "color"); + dp.packF32(mRadius, "radius"); + dp.packF32(mCutoff, "cutoff"); + dp.packF32(mFalloff, "falloff"); + return true; +} + +bool LLLightParams::unpack(LLDataPacker &dp) +{ + LLColor4U color; + dp.unpackColor4U(color, "color"); + setLinearColor(LLColor4(color)); + + F32 radius; + dp.unpackF32(radius, "radius"); + setRadius(radius); + + F32 cutoff; + dp.unpackF32(cutoff, "cutoff"); + setCutoff(cutoff); + + F32 falloff; + dp.unpackF32(falloff, "falloff"); + setFalloff(falloff); + + return true; +} + +bool LLLightParams::operator==(const LLNetworkData& data) const +{ + if (data.mType != PARAMS_LIGHT) + { + return false; + } + const LLLightParams *param = (const LLLightParams*)&data; + if (param->mColor != mColor || + param->mRadius != mRadius || + param->mCutoff != mCutoff || + param->mFalloff != mFalloff) + { + return false; + } + return true; +} + +void LLLightParams::copy(const LLNetworkData& data) +{ + const LLLightParams *param = (LLLightParams*)&data; + mType = param->mType; + mColor = param->mColor; + mRadius = param->mRadius; + mCutoff = param->mCutoff; + mFalloff = param->mFalloff; +} + +LLSD LLLightParams::asLLSD() const +{ + LLSD sd; + + sd["color"] = ll_sd_from_color4(getLinearColor()); + sd["radius"] = getRadius(); + sd["falloff"] = getFalloff(); + sd["cutoff"] = getCutoff(); + + return sd; +} + +bool LLLightParams::fromLLSD(LLSD& sd) +{ + const char *w; + w = "color"; + if (sd.has(w)) + { + setLinearColor( ll_color4_from_sd(sd["color"]) ); + } else goto fail; + w = "radius"; + if (sd.has(w)) + { + setRadius( (F32)sd[w].asReal() ); + } else goto fail; + w = "falloff"; + if (sd.has(w)) + { + setFalloff( (F32)sd[w].asReal() ); + } else goto fail; + w = "cutoff"; + if (sd.has(w)) + { + setCutoff( (F32)sd[w].asReal() ); + } else goto fail; + + return true; + fail: + return false; +} + +//============================================================================ + +//============================================================================ + +LLReflectionProbeParams::LLReflectionProbeParams() +{ + mType = PARAMS_REFLECTION_PROBE; +} + +bool LLReflectionProbeParams::pack(LLDataPacker &dp) const +{ + dp.packF32(mAmbiance, "ambiance"); + dp.packF32(mClipDistance, "clip_distance"); + dp.packU8(mFlags, "flags"); + return true; +} + +bool LLReflectionProbeParams::unpack(LLDataPacker &dp) +{ + F32 ambiance; + F32 clip_distance; + + dp.unpackF32(ambiance, "ambiance"); + setAmbiance(ambiance); + + dp.unpackF32(clip_distance, "clip_distance"); + setClipDistance(clip_distance); + + dp.unpackU8(mFlags, "flags"); + + return true; +} + +bool LLReflectionProbeParams::operator==(const LLNetworkData& data) const +{ + if (data.mType != PARAMS_REFLECTION_PROBE) + { + return false; + } + const LLReflectionProbeParams *param = (const LLReflectionProbeParams*)&data; + if (param->mAmbiance != mAmbiance) + { + return false; + } + if (param->mClipDistance != mClipDistance) + { + return false; + } + if (param->mFlags != mFlags) + { + return false; + } + return true; +} + +void LLReflectionProbeParams::copy(const LLNetworkData& data) +{ + const LLReflectionProbeParams *param = (LLReflectionProbeParams*)&data; + mType = param->mType; + mAmbiance = param->mAmbiance; + mClipDistance = param->mClipDistance; + mFlags = param->mFlags; +} + +LLSD LLReflectionProbeParams::asLLSD() const +{ + LLSD sd; + sd["ambiance"] = getAmbiance(); + sd["clip_distance"] = getClipDistance(); + sd["flags"] = mFlags; + return sd; +} + +bool LLReflectionProbeParams::fromLLSD(LLSD& sd) +{ + if (!sd.has("ambiance") || + !sd.has("clip_distance") || + !sd.has("flags")) + { + return false; + } + + setAmbiance((F32)sd["ambiance"].asReal()); + setClipDistance((F32)sd["clip_distance"].asReal()); + mFlags = (U8) sd["flags"].asInteger(); + + return true; +} + +void LLReflectionProbeParams::setIsBox(bool is_box) +{ + if (is_box) + { + mFlags |= FLAG_BOX_VOLUME; + } + else + { + mFlags &= ~FLAG_BOX_VOLUME; + } +} + +void LLReflectionProbeParams::setIsDynamic(bool is_dynamic) +{ + if (is_dynamic) + { + mFlags |= FLAG_DYNAMIC; + } + else + { + mFlags &= ~FLAG_DYNAMIC; + } +} + +//============================================================================ +LLFlexibleObjectData::LLFlexibleObjectData() +{ + mSimulateLOD = FLEXIBLE_OBJECT_DEFAULT_NUM_SECTIONS; + mGravity = FLEXIBLE_OBJECT_DEFAULT_GRAVITY; + mAirFriction = FLEXIBLE_OBJECT_DEFAULT_AIR_FRICTION; + mWindSensitivity = FLEXIBLE_OBJECT_DEFAULT_WIND_SENSITIVITY; + mTension = FLEXIBLE_OBJECT_DEFAULT_TENSION; + //mUsingCollisionSphere = FLEXIBLE_OBJECT_DEFAULT_USING_COLLISION_SPHERE; + //mRenderingCollisionSphere = FLEXIBLE_OBJECT_DEFAULT_RENDERING_COLLISION_SPHERE; + mUserForce = LLVector3(0.f, 0.f, 0.f); + + mType = PARAMS_FLEXIBLE; +} + +bool LLFlexibleObjectData::pack(LLDataPacker &dp) const +{ + // Custom, uber-svelte pack "softness" in upper bits of tension & drag + U8 bit1 = (mSimulateLOD & 2) << 6; + U8 bit2 = (mSimulateLOD & 1) << 7; + dp.packU8((U8)(mTension*10.01f) + bit1, "tension"); + dp.packU8((U8)(mAirFriction*10.01f) + bit2, "drag"); + dp.packU8((U8)((mGravity+10.f)*10.01f), "gravity"); + dp.packU8((U8)(mWindSensitivity*10.01f), "wind"); + dp.packVector3(mUserForce, "userforce"); + return true; +} + +bool LLFlexibleObjectData::unpack(LLDataPacker &dp) +{ + U8 tension, friction, gravity, wind; + U8 bit1, bit2; + dp.unpackU8(tension, "tension"); bit1 = (tension >> 6) & 2; + mTension = ((F32)(tension&0x7f))/10.f; + dp.unpackU8(friction, "drag"); bit2 = (friction >> 7) & 1; + mAirFriction = ((F32)(friction&0x7f))/10.f; + mSimulateLOD = bit1 | bit2; + dp.unpackU8(gravity, "gravity"); mGravity = ((F32)gravity)/10.f - 10.f; + dp.unpackU8(wind, "wind"); mWindSensitivity = ((F32)wind)/10.f; + if (dp.hasNext()) + { + dp.unpackVector3(mUserForce, "userforce"); + } + else + { + mUserForce.setVec(0.f, 0.f, 0.f); + } + return true; +} + +bool LLFlexibleObjectData::operator==(const LLNetworkData& data) const +{ + if (data.mType != PARAMS_FLEXIBLE) + { + return false; + } + LLFlexibleObjectData *flex_data = (LLFlexibleObjectData*)&data; + return (mSimulateLOD == flex_data->mSimulateLOD && + mGravity == flex_data->mGravity && + mAirFriction == flex_data->mAirFriction && + mWindSensitivity == flex_data->mWindSensitivity && + mTension == flex_data->mTension && + mUserForce == flex_data->mUserForce); + //mUsingCollisionSphere == flex_data->mUsingCollisionSphere && + //mRenderingCollisionSphere == flex_data->mRenderingCollisionSphere +} + +void LLFlexibleObjectData::copy(const LLNetworkData& data) +{ + const LLFlexibleObjectData *flex_data = (LLFlexibleObjectData*)&data; + mSimulateLOD = flex_data->mSimulateLOD; + mGravity = flex_data->mGravity; + mAirFriction = flex_data->mAirFriction; + mWindSensitivity = flex_data->mWindSensitivity; + mTension = flex_data->mTension; + mUserForce = flex_data->mUserForce; + //mUsingCollisionSphere = flex_data->mUsingCollisionSphere; + //mRenderingCollisionSphere = flex_data->mRenderingCollisionSphere; +} + +LLSD LLFlexibleObjectData::asLLSD() const +{ + LLSD sd; + + sd["air_friction"] = getAirFriction(); + sd["gravity"] = getGravity(); + sd["simulate_lod"] = getSimulateLOD(); + sd["tension"] = getTension(); + sd["user_force"] = getUserForce().getValue(); + sd["wind_sensitivity"] = getWindSensitivity(); + + return sd; +} + +bool LLFlexibleObjectData::fromLLSD(LLSD& sd) +{ + const char *w; + w = "air_friction"; + if (sd.has(w)) + { + setAirFriction( (F32)sd[w].asReal() ); + } else goto fail; + w = "gravity"; + if (sd.has(w)) + { + setGravity( (F32)sd[w].asReal() ); + } else goto fail; + w = "simulate_lod"; + if (sd.has(w)) + { + setSimulateLOD( sd[w].asInteger() ); + } else goto fail; + w = "tension"; + if (sd.has(w)) + { + setTension( (F32)sd[w].asReal() ); + } else goto fail; + w = "user_force"; + if (sd.has(w)) + { + LLVector3 user_force = ll_vector3_from_sd(sd[w], 0); + setUserForce( user_force ); + } else goto fail; + w = "wind_sensitivity"; + if (sd.has(w)) + { + setWindSensitivity( (F32)sd[w].asReal() ); + } else goto fail; + + return true; + fail: + return false; +} + +//============================================================================ + +LLSculptParams::LLSculptParams() +{ + mType = PARAMS_SCULPT; + mSculptTexture = SCULPT_DEFAULT_TEXTURE; + mSculptType = LL_SCULPT_TYPE_SPHERE; +} + +bool LLSculptParams::pack(LLDataPacker &dp) const +{ + dp.packUUID(mSculptTexture, "texture"); + dp.packU8(mSculptType, "type"); + + return true; +} + +bool LLSculptParams::unpack(LLDataPacker &dp) +{ + U8 type; + LLUUID id; + dp.unpackUUID(id, "texture"); + dp.unpackU8(type, "type"); + + setSculptTexture(id, type); + return true; +} + +bool LLSculptParams::operator==(const LLNetworkData& data) const +{ + if (data.mType != PARAMS_SCULPT) + { + return false; + } + + const LLSculptParams *param = (const LLSculptParams*)&data; + if ( (param->mSculptTexture != mSculptTexture) || + (param->mSculptType != mSculptType) ) + + { + return false; + } + + return true; +} + +void LLSculptParams::copy(const LLNetworkData& data) +{ + const LLSculptParams *param = (LLSculptParams*)&data; + setSculptTexture(param->mSculptTexture, param->mSculptType); +} + + + +LLSD LLSculptParams::asLLSD() const +{ + LLSD sd; + + sd["texture"] = mSculptTexture; + sd["type"] = mSculptType; + + return sd; +} + +bool LLSculptParams::fromLLSD(LLSD& sd) +{ + const char *w; + U8 type; + w = "type"; + if (sd.has(w)) + { + type = sd[w].asInteger(); + } + else return false; + + w = "texture"; + if (sd.has(w)) + { + setSculptTexture(sd[w], type); + } + else return false; + + return true; +} + +void LLSculptParams::setSculptTexture(const LLUUID& texture_id, U8 sculpt_type) +{ + U8 type = sculpt_type & LL_SCULPT_TYPE_MASK; + U8 flags = sculpt_type & LL_SCULPT_FLAG_MASK; + if (sculpt_type != (type | flags) || type > LL_SCULPT_TYPE_MAX) + { + mSculptTexture = SCULPT_DEFAULT_TEXTURE; + mSculptType = LL_SCULPT_TYPE_SPHERE; + } + else + { + mSculptTexture = texture_id; + mSculptType = sculpt_type; + } +} + +//============================================================================ + +LLLightImageParams::LLLightImageParams() +{ + mType = PARAMS_LIGHT_IMAGE; + mParams.setVec(F_PI*0.5f, 0.f, 0.f); +} + +bool LLLightImageParams::pack(LLDataPacker &dp) const +{ + dp.packUUID(mLightTexture, "texture"); + dp.packVector3(mParams, "params"); + + return true; +} + +bool LLLightImageParams::unpack(LLDataPacker &dp) +{ + dp.unpackUUID(mLightTexture, "texture"); + dp.unpackVector3(mParams, "params"); + + return true; +} + +bool LLLightImageParams::operator==(const LLNetworkData& data) const +{ + if (data.mType != PARAMS_LIGHT_IMAGE) + { + return false; + } + + const LLLightImageParams *param = (const LLLightImageParams*)&data; + if ( (param->mLightTexture != mLightTexture) ) + { + return false; + } + + if ( (param->mParams != mParams ) ) + { + return false; + } + + return true; +} + +void LLLightImageParams::copy(const LLNetworkData& data) +{ + const LLLightImageParams *param = (LLLightImageParams*)&data; + mLightTexture = param->mLightTexture; + mParams = param->mParams; +} + + + +LLSD LLLightImageParams::asLLSD() const +{ + LLSD sd; + + sd["texture"] = mLightTexture; + sd["params"] = mParams.getValue(); + + return sd; +} + +bool LLLightImageParams::fromLLSD(LLSD& sd) +{ + if (sd.has("texture")) + { + setLightTexture( sd["texture"] ); + setParams( LLVector3( sd["params"] ) ); + return true; + } + + return false; +} + +//============================================================================ + +LLExtendedMeshParams::LLExtendedMeshParams() +{ + mType = PARAMS_EXTENDED_MESH; + mFlags = 0; +} + +bool LLExtendedMeshParams::pack(LLDataPacker &dp) const +{ + dp.packU32(mFlags, "flags"); + + return true; +} + +bool LLExtendedMeshParams::unpack(LLDataPacker &dp) +{ + dp.unpackU32(mFlags, "flags"); + + return true; +} + +bool LLExtendedMeshParams::operator==(const LLNetworkData& data) const +{ + if (data.mType != PARAMS_EXTENDED_MESH) + { + return false; + } + + const LLExtendedMeshParams *param = (const LLExtendedMeshParams*)&data; + if ( (param->mFlags != mFlags) ) + { + return false; + } + + return true; +} + +void LLExtendedMeshParams::copy(const LLNetworkData& data) +{ + const LLExtendedMeshParams *param = (LLExtendedMeshParams*)&data; + mFlags = param->mFlags; +} + +LLSD LLExtendedMeshParams::asLLSD() const +{ + LLSD sd; + + sd["flags"] = LLSD::Integer(mFlags); + + return sd; +} + +bool LLExtendedMeshParams::fromLLSD(LLSD& sd) +{ + if (sd.has("flags")) + { + setFlags( sd["flags"].asInteger()); + return true; + } + + return false; +} + +//============================================================================ + +LLRenderMaterialParams::LLRenderMaterialParams() +{ + mType = PARAMS_RENDER_MATERIAL; +} + +bool LLRenderMaterialParams::pack(LLDataPacker& dp) const +{ + U8 count = (U8)llmin((S32)mEntries.size(), 14); //limited to 255 bytes, no more than 14 material ids + + dp.packU8(count, "count"); + for (auto& entry : mEntries) + { + dp.packU8(entry.te_idx, "te_idx"); + dp.packUUID(entry.id, "id"); + } + + return true; +} + +bool LLRenderMaterialParams::unpack(LLDataPacker& dp) +{ + U8 count; + dp.unpackU8(count, "count"); + mEntries.resize(count); + for (auto& entry : mEntries) + { + dp.unpackU8(entry.te_idx, "te_idx"); + dp.unpackUUID(entry.id, "te_id"); + } + + return true; +} + +bool LLRenderMaterialParams::operator==(const LLNetworkData& data) const +{ + if (data.mType != PARAMS_RENDER_MATERIAL) + { + return false; + } + + const LLRenderMaterialParams& param = static_cast<const LLRenderMaterialParams&>(data); + + if (param.mEntries.size() != mEntries.size()) + { + return false; + } + + for (auto& entry : mEntries) + { + if (param.getMaterial(entry.te_idx) != entry.id) + { + return false; + } + } + + return true; +} + +void LLRenderMaterialParams::copy(const LLNetworkData& data) +{ + llassert_always(data.mType == PARAMS_RENDER_MATERIAL); + const LLRenderMaterialParams& param = static_cast<const LLRenderMaterialParams&>(data); + mEntries = param.mEntries; +} + + +void LLRenderMaterialParams::setMaterial(U8 te, const LLUUID& id) +{ + for (int i = 0; i < mEntries.size(); ++i) + { + if (mEntries[i].te_idx == te) + { + if (id.isNull()) + { + mEntries.erase(mEntries.begin() + i); + } + else + { + mEntries[i].id = id; + } + return; + } + } + + mEntries.push_back({ te, id }); +} + +const LLUUID& LLRenderMaterialParams::getMaterial(U8 te) const +{ + for (int i = 0; i < mEntries.size(); ++i) + { + if (mEntries[i].te_idx == te) + { + return mEntries[i].id; + } + } + + return LLUUID::null; +} + diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index 1cbd6b242f..ce27dad346 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -1,794 +1,794 @@ -/**
- * @file llprimitive.h
- * @brief LLPrimitive base class
- *
- * $LicenseInfo:firstyear=2001&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_LLPRIMITIVE_H
-#define LL_LLPRIMITIVE_H
-
-#include "lluuid.h"
-#include "v3math.h"
-#include "xform.h"
-#include "message.h"
-#include "llpointer.h"
-#include "llvolume.h"
-#include "lltextureentry.h"
-#include "llprimtexturelist.h"
-
-// Moved to stdtypes.h --JC
-// typedef U8 LLPCode;
-class LLMessageSystem;
-class LLVolumeParams;
-class LLColor4;
-class LLColor3;
-class LLMaterialID;
-class LLTextureEntry;
-class LLDataPacker;
-class LLVolumeMgr;
-
-enum LLGeomType // NOTE: same vals as GL Ids
-{
- LLInvalid = 0,
- LLLineLoop = 2,
- LLLineStrip = 3,
- LLTriangles = 4,
- LLTriStrip = 5,
- LLTriFan = 6,
- LLQuads = 7,
- LLQuadStrip = 8
-};
-
-class LLVolume;
-
-/**
- * exported constants
- */
-extern const F32 OBJECT_CUT_MIN;
-extern const F32 OBJECT_CUT_MAX;
-extern const F32 OBJECT_CUT_INC;
-extern const F32 OBJECT_MIN_CUT_INC;
-extern const F32 OBJECT_ROTATION_PRECISION;
-
-extern const F32 OBJECT_TWIST_MIN;
-extern const F32 OBJECT_TWIST_MAX;
-extern const F32 OBJECT_TWIST_INC;
-
-// This is used for linear paths,
-// since twist is used in a slightly different manner.
-extern const F32 OBJECT_TWIST_LINEAR_MIN;
-extern const F32 OBJECT_TWIST_LINEAR_MAX;
-extern const F32 OBJECT_TWIST_LINEAR_INC;
-
-extern const F32 OBJECT_MIN_HOLE_SIZE;
-extern const F32 OBJECT_MAX_HOLE_SIZE_X;
-extern const F32 OBJECT_MAX_HOLE_SIZE_Y;
-
-// Revolutions parameters.
-extern const F32 OBJECT_REV_MIN;
-extern const F32 OBJECT_REV_MAX;
-extern const F32 OBJECT_REV_INC;
-
-extern const LLUUID SCULPT_DEFAULT_TEXTURE;
-
-//============================================================================
-
-// TomY: Base class for things that pack & unpack themselves
-class LLNetworkData
-{
-public:
- // Extra parameter IDs
- enum
- {
- PARAMS_FLEXIBLE = 0x10,
- PARAMS_LIGHT = 0x20,
- PARAMS_SCULPT = 0x30,
- PARAMS_LIGHT_IMAGE = 0x40,
- PARAMS_RESERVED = 0x50, // Used on server-side
- PARAMS_MESH = 0x60,
- PARAMS_EXTENDED_MESH = 0x70,
- PARAMS_RENDER_MATERIAL = 0x80,
- PARAMS_REFLECTION_PROBE = 0x90,
- };
-
-public:
- U16 mType;
- virtual ~LLNetworkData() {};
- virtual bool pack(LLDataPacker &dp) const = 0;
- virtual bool unpack(LLDataPacker &dp) = 0;
- virtual bool operator==(const LLNetworkData& data) const = 0;
- virtual void copy(const LLNetworkData& data) = 0;
- static bool isValid(U16 param_type, U32 size);
-};
-
-extern const F32 LIGHT_MIN_RADIUS;
-extern const F32 LIGHT_DEFAULT_RADIUS;
-extern const F32 LIGHT_MAX_RADIUS;
-extern const F32 LIGHT_MIN_FALLOFF;
-extern const F32 LIGHT_DEFAULT_FALLOFF;
-extern const F32 LIGHT_MAX_FALLOFF;
-extern const F32 LIGHT_MIN_CUTOFF;
-extern const F32 LIGHT_DEFAULT_CUTOFF;
-extern const F32 LIGHT_MAX_CUTOFF;
-
-class LLLightParams : public LLNetworkData
-{
-private:
- LLColor4 mColor; // linear color (not gamma corrected), alpha = intensity
- F32 mRadius;
- F32 mFalloff;
- F32 mCutoff;
-
-public:
- LLLightParams();
- /*virtual*/ bool pack(LLDataPacker &dp) const;
- /*virtual*/ bool unpack(LLDataPacker &dp);
- /*virtual*/ bool operator==(const LLNetworkData& data) const;
- /*virtual*/ void copy(const LLNetworkData& data);
- // LLSD implementations here are provided by Eddy Stryker.
- // NOTE: there are currently unused in protocols
- LLSD asLLSD() const;
- operator LLSD() const { return asLLSD(); }
- bool fromLLSD(LLSD& sd);
-
- // set the color by gamma corrected color value
- // color - gamma corrected color value (directly taken from an on-screen color swatch)
- void setSRGBColor(const LLColor4& color) { setLinearColor(linearColor4(color)); }
-
- // set the color by linear color value
- // color - linear color value (value as it appears in shaders)
- void setLinearColor(const LLColor4& color) { mColor = color; mColor.clamp(); }
- void setRadius(F32 radius) { mRadius = llclamp(radius, LIGHT_MIN_RADIUS, LIGHT_MAX_RADIUS); }
- void setFalloff(F32 falloff) { mFalloff = llclamp(falloff, LIGHT_MIN_FALLOFF, LIGHT_MAX_FALLOFF); }
- void setCutoff(F32 cutoff) { mCutoff = llclamp(cutoff, LIGHT_MIN_CUTOFF, LIGHT_MAX_CUTOFF); }
-
- // get the linear space color of this light. This value can be fed directly to shaders
- LLColor4 getLinearColor() const { return mColor; }
- // get the sRGB (gamma corrected) color of this light, this is the value that should be displayed in the UI
- LLColor4 getSRGBColor() const { return srgbColor4(mColor); }
-
- F32 getRadius() const { return mRadius; }
- F32 getFalloff() const { return mFalloff; }
- F32 getCutoff() const { return mCutoff; }
-};
-
-extern const F32 REFLECTION_PROBE_MIN_AMBIANCE;
-extern const F32 REFLECTION_PROBE_MAX_AMBIANCE;
-extern const F32 REFLECTION_PROBE_DEFAULT_AMBIANCE;
-extern const F32 REFLECTION_PROBE_MIN_CLIP_DISTANCE;
-extern const F32 REFLECTION_PROBE_MAX_CLIP_DISTANCE;
-extern const F32 REFLECTION_PROBE_DEFAULT_CLIP_DISTANCE;
-
-class LLReflectionProbeParams : public LLNetworkData
-{
-public:
- enum EFlags : U8
- {
- FLAG_BOX_VOLUME = 0x01, // use a box influence volume
- FLAG_DYNAMIC = 0x02, // render dynamic objects (avatars) into this Reflection Probe
- };
-
-protected:
- F32 mAmbiance = REFLECTION_PROBE_DEFAULT_AMBIANCE;
- F32 mClipDistance = REFLECTION_PROBE_DEFAULT_CLIP_DISTANCE;
- U8 mFlags = 0;
-
-public:
- LLReflectionProbeParams();
- /*virtual*/ bool pack(LLDataPacker& dp) const;
- /*virtual*/ bool unpack(LLDataPacker& dp);
- /*virtual*/ bool operator==(const LLNetworkData& data) const;
- /*virtual*/ void copy(const LLNetworkData& data);
- // LLSD implementations here are provided by Eddy Stryker.
- // NOTE: there are currently unused in protocols
- LLSD asLLSD() const;
- operator LLSD() const { return asLLSD(); }
- bool fromLLSD(LLSD& sd);
-
- void setAmbiance(F32 ambiance) { mAmbiance = llclamp(ambiance, REFLECTION_PROBE_MIN_AMBIANCE, REFLECTION_PROBE_MAX_AMBIANCE); }
- void setClipDistance(F32 distance) { mClipDistance = llclamp(distance, REFLECTION_PROBE_MIN_CLIP_DISTANCE, REFLECTION_PROBE_MAX_CLIP_DISTANCE); }
- void setIsBox(bool is_box);
- void setIsDynamic(bool is_dynamic);
-
- F32 getAmbiance() const { return mAmbiance; }
- F32 getClipDistance() const { return mClipDistance; }
- bool getIsBox() const { return (mFlags & FLAG_BOX_VOLUME) != 0; }
- bool getIsDynamic() const { return (mFlags & FLAG_DYNAMIC) != 0; }
-};
-
-//-------------------------------------------------
-// This structure is also used in the part of the
-// code that creates new flexible objects.
-//-------------------------------------------------
-
-// These were made into enums so that they could be used as fixed size
-// array bounds.
-enum EFlexibleObjectConst
-{
- // "Softness" => [0,3], increments of 1
- // Represents powers of 2: 0 -> 1, 3 -> 8
- FLEXIBLE_OBJECT_MIN_SECTIONS = 0,
- FLEXIBLE_OBJECT_DEFAULT_NUM_SECTIONS = 2,
- FLEXIBLE_OBJECT_MAX_SECTIONS = 3
-};
-
-// "Tension" => [0,10], increments of 0.1
-extern const F32 FLEXIBLE_OBJECT_MIN_TENSION;
-extern const F32 FLEXIBLE_OBJECT_DEFAULT_TENSION;
-extern const F32 FLEXIBLE_OBJECT_MAX_TENSION;
-
-// "Drag" => [0,10], increments of 0.1
-extern const F32 FLEXIBLE_OBJECT_MIN_AIR_FRICTION;
-extern const F32 FLEXIBLE_OBJECT_DEFAULT_AIR_FRICTION;
-extern const F32 FLEXIBLE_OBJECT_MAX_AIR_FRICTION;
-
-// "Gravity" = [-10,10], increments of 0.1
-extern const F32 FLEXIBLE_OBJECT_MIN_GRAVITY;
-extern const F32 FLEXIBLE_OBJECT_DEFAULT_GRAVITY;
-extern const F32 FLEXIBLE_OBJECT_MAX_GRAVITY;
-
-// "Wind" = [0,10], increments of 0.1
-extern const F32 FLEXIBLE_OBJECT_MIN_WIND_SENSITIVITY;
-extern const F32 FLEXIBLE_OBJECT_DEFAULT_WIND_SENSITIVITY;
-extern const F32 FLEXIBLE_OBJECT_MAX_WIND_SENSITIVITY;
-
-extern const F32 FLEXIBLE_OBJECT_MAX_INTERNAL_TENSION_FORCE;
-
-extern const F32 FLEXIBLE_OBJECT_DEFAULT_LENGTH;
-extern const bool FLEXIBLE_OBJECT_DEFAULT_USING_COLLISION_SPHERE;
-extern const bool FLEXIBLE_OBJECT_DEFAULT_RENDERING_COLLISION_SPHERE;
-
-
-class LLFlexibleObjectData : public LLNetworkData
-{
-protected:
- S32 mSimulateLOD; // 2^n = number of simulated sections
- F32 mGravity;
- F32 mAirFriction; // higher is more stable, but too much looks like it's underwater
- F32 mWindSensitivity; // interacts with tension, air friction, and gravity
- F32 mTension; //interacts in complex ways with other parameters
- LLVector3 mUserForce; // custom user-defined force vector
- //bool mUsingCollisionSphere;
- //bool mRenderingCollisionSphere;
-
-public:
- void setSimulateLOD(S32 lod) { mSimulateLOD = llclamp(lod, (S32)FLEXIBLE_OBJECT_MIN_SECTIONS, (S32)FLEXIBLE_OBJECT_MAX_SECTIONS); }
- void setGravity(F32 gravity) { mGravity = llclamp(gravity, FLEXIBLE_OBJECT_MIN_GRAVITY, FLEXIBLE_OBJECT_MAX_GRAVITY); }
- void setAirFriction(F32 friction) { mAirFriction = llclamp(friction, FLEXIBLE_OBJECT_MIN_AIR_FRICTION, FLEXIBLE_OBJECT_MAX_AIR_FRICTION); }
- void setWindSensitivity(F32 wind) { mWindSensitivity = llclamp(wind, FLEXIBLE_OBJECT_MIN_WIND_SENSITIVITY, FLEXIBLE_OBJECT_MAX_WIND_SENSITIVITY); }
- void setTension(F32 tension) { mTension = llclamp(tension, FLEXIBLE_OBJECT_MIN_TENSION, FLEXIBLE_OBJECT_MAX_TENSION); }
- void setUserForce(LLVector3 &force) { mUserForce = force; }
-
- S32 getSimulateLOD() const { return mSimulateLOD; }
- F32 getGravity() const { return mGravity; }
- F32 getAirFriction() const { return mAirFriction; }
- F32 getWindSensitivity() const { return mWindSensitivity; }
- F32 getTension() const { return mTension; }
- LLVector3 getUserForce() const { return mUserForce; }
-
- //------ the constructor for the structure ------------
- LLFlexibleObjectData();
- bool pack(LLDataPacker &dp) const;
- bool unpack(LLDataPacker &dp);
- bool operator==(const LLNetworkData& data) const;
- void copy(const LLNetworkData& data);
- LLSD asLLSD() const;
- operator LLSD() const { return asLLSD(); }
- bool fromLLSD(LLSD& sd);
-};// end of attributes structure
-
-
-
-class LLSculptParams : public LLNetworkData
-{
-protected:
- LLUUID mSculptTexture;
- U8 mSculptType;
-
-public:
- LLSculptParams();
- /*virtual*/ bool pack(LLDataPacker &dp) const;
- /*virtual*/ bool unpack(LLDataPacker &dp);
- /*virtual*/ bool operator==(const LLNetworkData& data) const;
- /*virtual*/ void copy(const LLNetworkData& data);
- LLSD asLLSD() const;
- operator LLSD() const { return asLLSD(); }
- bool fromLLSD(LLSD& sd);
-
- void setSculptTexture(const LLUUID& texture_id, U8 sculpt_type);
- LLUUID getSculptTexture() const { return mSculptTexture; }
- U8 getSculptType() const { return mSculptType; }
-};
-
-class LLLightImageParams : public LLNetworkData
-{
-protected:
- LLUUID mLightTexture;
- LLVector3 mParams;
-
-public:
- LLLightImageParams();
- /*virtual*/ bool pack(LLDataPacker &dp) const;
- /*virtual*/ bool unpack(LLDataPacker &dp);
- /*virtual*/ bool operator==(const LLNetworkData& data) const;
- /*virtual*/ void copy(const LLNetworkData& data);
- LLSD asLLSD() const;
- operator LLSD() const { return asLLSD(); }
- bool fromLLSD(LLSD& sd);
-
- void setLightTexture(const LLUUID& id) { mLightTexture = id; }
- LLUUID getLightTexture() const { return mLightTexture; }
- bool isLightSpotlight() const { return mLightTexture.notNull(); }
- void setParams(const LLVector3& params) { mParams = params; }
- LLVector3 getParams() const { return mParams; }
-
-};
-
-class LLExtendedMeshParams : public LLNetworkData
-{
-protected:
- U32 mFlags;
-
-public:
- static const U32 ANIMATED_MESH_ENABLED_FLAG = 0x1 << 0;
-
- LLExtendedMeshParams();
- /*virtual*/ bool pack(LLDataPacker &dp) const;
- /*virtual*/ bool unpack(LLDataPacker &dp);
- /*virtual*/ bool operator==(const LLNetworkData& data) const;
- /*virtual*/ void copy(const LLNetworkData& data);
- LLSD asLLSD() const;
- operator LLSD() const { return asLLSD(); }
- bool fromLLSD(LLSD& sd);
-
- void setFlags(const U32& flags) { mFlags = flags; }
- U32 getFlags() const { return mFlags; }
-
-};
-
-class LLRenderMaterialParams : public LLNetworkData
-{
-private:
- struct Entry
- {
- U8 te_idx;
- LLUUID id;
- };
- std::vector< Entry > mEntries;
-
-public:
- LLRenderMaterialParams();
- bool pack(LLDataPacker& dp) const override;
- bool unpack(LLDataPacker& dp) override;
- bool operator==(const LLNetworkData& data) const override;
- void copy(const LLNetworkData& data) override;
-
- void setMaterial(U8 te_idx, const LLUUID& id);
- const LLUUID& getMaterial(U8 te_idx) const;
-
- bool isEmpty() { return mEntries.empty(); }
-};
-
-
-// This code is not naming-standards compliant. Leaving it like this for
-// now to make the connection to code in
-// bool packTEMessage(LLDataPacker &dp) const;
-// more obvious. This should be refactored to remove the duplication, at which
-// point we can fix the names as well.
-// - Vir
-struct LLTEContents
-{
- static const U32 MAX_TES = 45;
-
- LLUUID image_data[MAX_TES];
- LLColor4U colors[MAX_TES];
- F32 scale_s[MAX_TES];
- F32 scale_t[MAX_TES];
- S16 offset_s[MAX_TES];
- S16 offset_t[MAX_TES];
- S16 image_rot[MAX_TES];
- U8 bump[MAX_TES];
- U8 media_flags[MAX_TES];
- U8 glow[MAX_TES];
- LLMaterialID material_ids[MAX_TES];
-
- static const U32 MAX_TE_BUFFER = 4096;
- U8 packed_buffer[MAX_TE_BUFFER];
-
- U32 size;
- U32 face_count;
-};
-
-class LLPrimitive : public LLXform
-{
-public:
-
- // HACK for removing LLPrimitive's dependency on gVolumeMgr global.
- // If a different LLVolumeManager is instantiated and set early enough
- // then the LLPrimitive class will use it instead of gVolumeMgr.
- static LLVolumeMgr* getVolumeManager() { return sVolumeManager; }
- static void setVolumeManager( LLVolumeMgr* volume_manager);
- static bool cleanupVolumeManager();
-
- // these flags influence how the RigidBody representation is built
- static const U32 PRIM_FLAG_PHANTOM = 0x1 << 0;
- static const U32 PRIM_FLAG_VOLUME_DETECT = 0x1 << 1;
- static const U32 PRIM_FLAG_DYNAMIC = 0x1 << 2;
- static const U32 PRIM_FLAG_AVATAR = 0x1 << 3;
- static const U32 PRIM_FLAG_SCULPT = 0x1 << 4;
- // not used yet, but soon
- static const U32 PRIM_FLAG_COLLISION_CALLBACK = 0x1 << 5;
- static const U32 PRIM_FLAG_CONVEX = 0x1 << 6;
- static const U32 PRIM_FLAG_DEFAULT_VOLUME = 0x1 << 7;
- static const U32 PRIM_FLAG_SITTING = 0x1 << 8;
- static const U32 PRIM_FLAG_SITTING_ON_GROUND = 0x1 << 9; // Set along with PRIM_FLAG_SITTING
-
- LLPrimitive();
- virtual ~LLPrimitive();
-
- void clearTextureList();
-
- static LLPrimitive *createPrimitive(LLPCode p_code);
- void init_primitive(LLPCode p_code);
-
- void setPCode(const LLPCode pcode);
- const LLVolume *getVolumeConst() const { return mVolumep; } // HACK for Windoze confusion about ostream operator in LLVolume
- LLVolume *getVolume() const { return mVolumep; }
- virtual bool setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume = false);
-
- // Modify texture entry properties
- inline bool validTE(const U8 te_num) const;
- LLTextureEntry* getTE(const U8 te_num) const;
-
- virtual void setNumTEs(const U8 num_tes);
- virtual void setAllTESelected(bool sel);
- virtual void setAllTETextures(const LLUUID &tex_id);
- virtual void setTE(const U8 index, const LLTextureEntry& te);
- virtual S32 setTEColor(const U8 te, const LLColor4 &color);
- virtual S32 setTEColor(const U8 te, const LLColor3 &color);
- virtual S32 setTEAlpha(const U8 te, const F32 alpha);
- virtual S32 setTETexture(const U8 te, const LLUUID &tex_id);
- virtual S32 setTEScale (const U8 te, const F32 s, const F32 t);
- virtual S32 setTEScaleS(const U8 te, const F32 s);
- virtual S32 setTEScaleT(const U8 te, const F32 t);
- virtual S32 setTEOffset (const U8 te, const F32 s, const F32 t);
- virtual S32 setTEOffsetS(const U8 te, const F32 s);
- virtual S32 setTEOffsetT(const U8 te, const F32 t);
- virtual S32 setTERotation(const U8 te, const F32 r);
- virtual S32 setTEBumpShinyFullbright(const U8 te, const U8 bump);
- virtual S32 setTEBumpShiny(const U8 te, const U8 bump);
- virtual S32 setTEMediaTexGen(const U8 te, const U8 media);
- virtual S32 setTEBumpmap(const U8 te, const U8 bump);
- virtual S32 setTETexGen(const U8 te, const U8 texgen);
- virtual S32 setTEShiny(const U8 te, const U8 shiny);
- virtual S32 setTEFullbright(const U8 te, const U8 fullbright);
- virtual S32 setTEMediaFlags(const U8 te, const U8 flags);
- virtual S32 setTEGlow(const U8 te, const F32 glow);
- virtual S32 setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID);
- virtual S32 setTEMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams);
- virtual bool setMaterial(const U8 material); // returns true if material changed
- virtual void setTESelected(const U8 te, bool sel);
-
- LLMaterialPtr getTEMaterialParams(const U8 index);
-
- void copyTEs(const LLPrimitive *primitive);
- S32 packTEField(U8 *cur_ptr, U8 *data_ptr, U8 data_size, U8 last_face_index, EMsgVariableType type) const;
- bool packTEMessage(LLMessageSystem *mesgsys) const;
- bool packTEMessage(LLDataPacker &dp) const;
- S32 unpackTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num); // Variable num of blocks
- S32 unpackTEMessage(LLDataPacker &dp);
- S32 parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec);
- S32 applyParsedTEMessage(LLTEContents& tec);
-
-#ifdef CHECK_FOR_FINITE
- inline void setPosition(const LLVector3& pos);
- inline void setPosition(const F32 x, const F32 y, const F32 z);
- inline void addPosition(const LLVector3& pos);
-
- inline void setAngularVelocity(const LLVector3& avel);
- inline void setAngularVelocity(const F32 x, const F32 y, const F32 z);
- inline void setVelocity(const LLVector3& vel);
- inline void setVelocity(const F32 x, const F32 y, const F32 z);
- inline void setVelocityX(const F32 x);
- inline void setVelocityY(const F32 y);
- inline void setVelocityZ(const F32 z);
- inline void addVelocity(const LLVector3& vel);
- inline void setAcceleration(const LLVector3& accel);
- inline void setAcceleration(const F32 x, const F32 y, const F32 z);
-#else
- // Don't override the base LLXForm operators.
- // Special case for setPosition. If not check-for-finite, fall through to LLXform method.
- // void setPosition(F32 x, F32 y, F32 z)
- // void setPosition(LLVector3)
-
- void setAngularVelocity(const LLVector3& avel) { mAngularVelocity = avel; }
- void setAngularVelocity(const F32 x, const F32 y, const F32 z) { mAngularVelocity.setVec(x,y,z); }
- void setVelocity(const LLVector3& vel) { mVelocity = vel; }
- void setVelocity(const F32 x, const F32 y, const F32 z) { mVelocity.setVec(x,y,z); }
- void setVelocityX(const F32 x) { mVelocity.mV[VX] = x; }
- void setVelocityY(const F32 y) { mVelocity.mV[VY] = y; }
- void setVelocityZ(const F32 z) { mVelocity.mV[VZ] = z; }
- void addVelocity(const LLVector3& vel) { mVelocity += vel; }
- void setAcceleration(const LLVector3& accel) { mAcceleration = accel; }
- void setAcceleration(const F32 x, const F32 y, const F32 z) { mAcceleration.setVec(x,y,z); }
-#endif
-
- LLPCode getPCode() const { return mPrimitiveCode; }
- std::string getPCodeString() const { return pCodeToString(mPrimitiveCode); }
- const LLVector3& getAngularVelocity() const { return mAngularVelocity; }
- const LLVector3& getVelocity() const { return mVelocity; }
- const LLVector3& getAcceleration() const { return mAcceleration; }
- U8 getNumTEs() const { return mTextureList.size(); }
- U8 getExpectedNumTEs() const;
-
- U8 getMaterial() const { return mMaterial; }
-
- void setVolumeType(const U8 code);
- U8 getVolumeType();
-
- // clears existing textures
- // copies the contents of other_list into mEntryList
- void copyTextureList(const LLPrimTextureList& other_list);
-
- // clears existing textures
- // takes the contents of other_list and clears other_list
- void takeTextureList(LLPrimTextureList& other_list);
-
- inline bool isAvatar() const;
- inline bool isSittingAvatar() const;
- inline bool isSittingAvatarOnGround() const;
- inline bool hasBumpmap() const { return mNumBumpmapTEs > 0;}
-
- void setFlags(U32 flags) { mMiscFlags = flags; }
- void addFlags(U32 flags) { mMiscFlags |= flags; }
- void removeFlags(U32 flags) { mMiscFlags &= ~flags; }
- U32 getFlags() const { return mMiscFlags; }
- bool checkFlags(U32 flags) const { return (mMiscFlags & flags) != 0; }
-
- static std::string pCodeToString(const LLPCode pcode);
- static LLPCode legacyToPCode(const U8 legacy);
- static U8 pCodeToLegacy(const LLPCode pcode);
- static bool getTESTAxes(const U8 face, U32* s_axis, U32* t_axis);
-
- bool hasRenderMaterialParams() const;
-
- inline static bool isPrimitive(const LLPCode pcode);
- inline static bool isApp(const LLPCode pcode);
-
-private:
- void updateNumBumpmap(const U8 index, const U8 bump);
-
-protected:
- LLPCode mPrimitiveCode; // Primitive code
- LLVector3 mVelocity; // how fast are we moving?
- LLVector3 mAcceleration; // are we under constant acceleration?
- LLVector3 mAngularVelocity; // angular velocity
- LLPointer<LLVolume> mVolumep;
- LLPrimTextureList mTextureList; // list of texture GUIDs, scales, offsets
- U8 mMaterial; // Material code
- U8 mNumTEs; // # of faces on the primitve
- U8 mNumBumpmapTEs; // number of bumpmap TEs.
- U32 mMiscFlags; // home for misc bools
-
-public:
- static LLVolumeMgr* sVolumeManager;
-
- enum
- {
- NO_LOD = -1
- };
-};
-
-inline bool LLPrimitive::isAvatar() const
-{
- return LL_PCODE_LEGACY_AVATAR == mPrimitiveCode;
-}
-
-inline bool LLPrimitive::isSittingAvatar() const
-{
- // this is only used server-side
- return isAvatar() && checkFlags(PRIM_FLAG_SITTING | PRIM_FLAG_SITTING_ON_GROUND);
-}
-
-inline bool LLPrimitive::isSittingAvatarOnGround() const
-{
- // this is only used server-side
- return isAvatar() && checkFlags(PRIM_FLAG_SITTING_ON_GROUND);
-}
-
-// static
-inline bool LLPrimitive::isPrimitive(const LLPCode pcode)
-{
- LLPCode base_type = pcode & LL_PCODE_BASE_MASK;
-
- if (base_type && (base_type < LL_PCODE_APP))
- {
- return true;
- }
- return false;
-}
-
-// static
-inline bool LLPrimitive::isApp(const LLPCode pcode)
-{
- LLPCode base_type = pcode & LL_PCODE_BASE_MASK;
-
- return (base_type == LL_PCODE_APP);
-}
-
-
-#ifdef CHECK_FOR_FINITE
-// Special case for setPosition. If not check-for-finite, fall through to LLXform method.
-void LLPrimitive::setPosition(const F32 x, const F32 y, const F32 z)
-{
- if (llfinite(x) && llfinite(y) && llfinite(z))
- {
- LLXform::setPosition(x, y, z);
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setPosition(x,y,z) for " << pCodeToString(mPrimitiveCode) << LL_ENDL;
- }
-}
-
-// Special case for setPosition. If not check-for-finite, fall through to LLXform method.
-void LLPrimitive::setPosition(const LLVector3& pos)
-{
- if (pos.isFinite())
- {
- LLXform::setPosition(pos);
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setPosition(LLVector3) for " << pCodeToString(mPrimitiveCode) << LL_ENDL;
- }
-}
-
-void LLPrimitive::setAngularVelocity(const LLVector3& avel)
-{
- if (avel.isFinite())
- {
- mAngularVelocity = avel;
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setAngularVelocity" << LL_ENDL;
- }
-}
-
-void LLPrimitive::setAngularVelocity(const F32 x, const F32 y, const F32 z)
-{
- if (llfinite(x) && llfinite(y) && llfinite(z))
- {
- mAngularVelocity.setVec(x,y,z);
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setAngularVelocity" << LL_ENDL;
- }
-}
-
-void LLPrimitive::setVelocity(const LLVector3& vel)
-{
- if (vel.isFinite())
- {
- mVelocity = vel;
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setVelocity(LLVector3) for " << pCodeToString(mPrimitiveCode) << LL_ENDL;
- }
-}
-
-void LLPrimitive::setVelocity(const F32 x, const F32 y, const F32 z)
-{
- if (llfinite(x) && llfinite(y) && llfinite(z))
- {
- mVelocity.setVec(x,y,z);
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setVelocity(F32,F32,F32) for " << pCodeToString(mPrimitiveCode) << LL_ENDL;
- }
-}
-
-void LLPrimitive::setVelocityX(const F32 x)
-{
- if (llfinite(x))
- {
- mVelocity.mV[VX] = x;
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setVelocityX" << LL_ENDL;
- }
-}
-
-void LLPrimitive::setVelocityY(const F32 y)
-{
- if (llfinite(y))
- {
- mVelocity.mV[VY] = y;
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setVelocityY" << LL_ENDL;
- }
-}
-
-void LLPrimitive::setVelocityZ(const F32 z)
-{
- if (llfinite(z))
- {
- mVelocity.mV[VZ] = z;
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setVelocityZ" << LL_ENDL;
- }
-}
-
-void LLPrimitive::addVelocity(const LLVector3& vel)
-{
- if (vel.isFinite())
- {
- mVelocity += vel;
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::addVelocity" << LL_ENDL;
- }
-}
-
-void LLPrimitive::setAcceleration(const LLVector3& accel)
-{
- if (accel.isFinite())
- {
- mAcceleration = accel;
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setAcceleration(LLVector3) for " << pCodeToString(mPrimitiveCode) << LL_ENDL;
- }
-}
-
-void LLPrimitive::setAcceleration(const F32 x, const F32 y, const F32 z)
-{
- if (llfinite(x) && llfinite(y) && llfinite(z))
- {
- mAcceleration.setVec(x,y,z);
- }
- else
- {
- LL_ERRS() << "Non Finite in LLPrimitive::setAcceleration(F32,F32,F32) for " << pCodeToString(mPrimitiveCode) << LL_ENDL;
- }
-}
-#endif // CHECK_FOR_FINITE
-
-inline bool LLPrimitive::validTE(const U8 te_num) const
-{
- return (mNumTEs && te_num < mNumTEs);
-}
-
-#endif
-
+/** + * @file llprimitive.h + * @brief LLPrimitive base class + * + * $LicenseInfo:firstyear=2001&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_LLPRIMITIVE_H +#define LL_LLPRIMITIVE_H + +#include "lluuid.h" +#include "v3math.h" +#include "xform.h" +#include "message.h" +#include "llpointer.h" +#include "llvolume.h" +#include "lltextureentry.h" +#include "llprimtexturelist.h" + +// Moved to stdtypes.h --JC +// typedef U8 LLPCode; +class LLMessageSystem; +class LLVolumeParams; +class LLColor4; +class LLColor3; +class LLMaterialID; +class LLTextureEntry; +class LLDataPacker; +class LLVolumeMgr; + +enum LLGeomType // NOTE: same vals as GL Ids +{ + LLInvalid = 0, + LLLineLoop = 2, + LLLineStrip = 3, + LLTriangles = 4, + LLTriStrip = 5, + LLTriFan = 6, + LLQuads = 7, + LLQuadStrip = 8 +}; + +class LLVolume; + +/** + * exported constants + */ +extern const F32 OBJECT_CUT_MIN; +extern const F32 OBJECT_CUT_MAX; +extern const F32 OBJECT_CUT_INC; +extern const F32 OBJECT_MIN_CUT_INC; +extern const F32 OBJECT_ROTATION_PRECISION; + +extern const F32 OBJECT_TWIST_MIN; +extern const F32 OBJECT_TWIST_MAX; +extern const F32 OBJECT_TWIST_INC; + +// This is used for linear paths, +// since twist is used in a slightly different manner. +extern const F32 OBJECT_TWIST_LINEAR_MIN; +extern const F32 OBJECT_TWIST_LINEAR_MAX; +extern const F32 OBJECT_TWIST_LINEAR_INC; + +extern const F32 OBJECT_MIN_HOLE_SIZE; +extern const F32 OBJECT_MAX_HOLE_SIZE_X; +extern const F32 OBJECT_MAX_HOLE_SIZE_Y; + +// Revolutions parameters. +extern const F32 OBJECT_REV_MIN; +extern const F32 OBJECT_REV_MAX; +extern const F32 OBJECT_REV_INC; + +extern const LLUUID SCULPT_DEFAULT_TEXTURE; + +//============================================================================ + +// TomY: Base class for things that pack & unpack themselves +class LLNetworkData +{ +public: + // Extra parameter IDs + enum + { + PARAMS_FLEXIBLE = 0x10, + PARAMS_LIGHT = 0x20, + PARAMS_SCULPT = 0x30, + PARAMS_LIGHT_IMAGE = 0x40, + PARAMS_RESERVED = 0x50, // Used on server-side + PARAMS_MESH = 0x60, + PARAMS_EXTENDED_MESH = 0x70, + PARAMS_RENDER_MATERIAL = 0x80, + PARAMS_REFLECTION_PROBE = 0x90, + }; + +public: + U16 mType; + virtual ~LLNetworkData() {}; + virtual bool pack(LLDataPacker &dp) const = 0; + virtual bool unpack(LLDataPacker &dp) = 0; + virtual bool operator==(const LLNetworkData& data) const = 0; + virtual void copy(const LLNetworkData& data) = 0; + static bool isValid(U16 param_type, U32 size); +}; + +extern const F32 LIGHT_MIN_RADIUS; +extern const F32 LIGHT_DEFAULT_RADIUS; +extern const F32 LIGHT_MAX_RADIUS; +extern const F32 LIGHT_MIN_FALLOFF; +extern const F32 LIGHT_DEFAULT_FALLOFF; +extern const F32 LIGHT_MAX_FALLOFF; +extern const F32 LIGHT_MIN_CUTOFF; +extern const F32 LIGHT_DEFAULT_CUTOFF; +extern const F32 LIGHT_MAX_CUTOFF; + +class LLLightParams : public LLNetworkData +{ +private: + LLColor4 mColor; // linear color (not gamma corrected), alpha = intensity + F32 mRadius; + F32 mFalloff; + F32 mCutoff; + +public: + LLLightParams(); + /*virtual*/ bool pack(LLDataPacker &dp) const; + /*virtual*/ bool unpack(LLDataPacker &dp); + /*virtual*/ bool operator==(const LLNetworkData& data) const; + /*virtual*/ void copy(const LLNetworkData& data); + // LLSD implementations here are provided by Eddy Stryker. + // NOTE: there are currently unused in protocols + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); + + // set the color by gamma corrected color value + // color - gamma corrected color value (directly taken from an on-screen color swatch) + void setSRGBColor(const LLColor4& color) { setLinearColor(linearColor4(color)); } + + // set the color by linear color value + // color - linear color value (value as it appears in shaders) + void setLinearColor(const LLColor4& color) { mColor = color; mColor.clamp(); } + void setRadius(F32 radius) { mRadius = llclamp(radius, LIGHT_MIN_RADIUS, LIGHT_MAX_RADIUS); } + void setFalloff(F32 falloff) { mFalloff = llclamp(falloff, LIGHT_MIN_FALLOFF, LIGHT_MAX_FALLOFF); } + void setCutoff(F32 cutoff) { mCutoff = llclamp(cutoff, LIGHT_MIN_CUTOFF, LIGHT_MAX_CUTOFF); } + + // get the linear space color of this light. This value can be fed directly to shaders + LLColor4 getLinearColor() const { return mColor; } + // get the sRGB (gamma corrected) color of this light, this is the value that should be displayed in the UI + LLColor4 getSRGBColor() const { return srgbColor4(mColor); } + + F32 getRadius() const { return mRadius; } + F32 getFalloff() const { return mFalloff; } + F32 getCutoff() const { return mCutoff; } +}; + +extern const F32 REFLECTION_PROBE_MIN_AMBIANCE; +extern const F32 REFLECTION_PROBE_MAX_AMBIANCE; +extern const F32 REFLECTION_PROBE_DEFAULT_AMBIANCE; +extern const F32 REFLECTION_PROBE_MIN_CLIP_DISTANCE; +extern const F32 REFLECTION_PROBE_MAX_CLIP_DISTANCE; +extern const F32 REFLECTION_PROBE_DEFAULT_CLIP_DISTANCE; + +class LLReflectionProbeParams : public LLNetworkData +{ +public: + enum EFlags : U8 + { + FLAG_BOX_VOLUME = 0x01, // use a box influence volume + FLAG_DYNAMIC = 0x02, // render dynamic objects (avatars) into this Reflection Probe + }; + +protected: + F32 mAmbiance = REFLECTION_PROBE_DEFAULT_AMBIANCE; + F32 mClipDistance = REFLECTION_PROBE_DEFAULT_CLIP_DISTANCE; + U8 mFlags = 0; + +public: + LLReflectionProbeParams(); + /*virtual*/ bool pack(LLDataPacker& dp) const; + /*virtual*/ bool unpack(LLDataPacker& dp); + /*virtual*/ bool operator==(const LLNetworkData& data) const; + /*virtual*/ void copy(const LLNetworkData& data); + // LLSD implementations here are provided by Eddy Stryker. + // NOTE: there are currently unused in protocols + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); + + void setAmbiance(F32 ambiance) { mAmbiance = llclamp(ambiance, REFLECTION_PROBE_MIN_AMBIANCE, REFLECTION_PROBE_MAX_AMBIANCE); } + void setClipDistance(F32 distance) { mClipDistance = llclamp(distance, REFLECTION_PROBE_MIN_CLIP_DISTANCE, REFLECTION_PROBE_MAX_CLIP_DISTANCE); } + void setIsBox(bool is_box); + void setIsDynamic(bool is_dynamic); + + F32 getAmbiance() const { return mAmbiance; } + F32 getClipDistance() const { return mClipDistance; } + bool getIsBox() const { return (mFlags & FLAG_BOX_VOLUME) != 0; } + bool getIsDynamic() const { return (mFlags & FLAG_DYNAMIC) != 0; } +}; + +//------------------------------------------------- +// This structure is also used in the part of the +// code that creates new flexible objects. +//------------------------------------------------- + +// These were made into enums so that they could be used as fixed size +// array bounds. +enum EFlexibleObjectConst +{ + // "Softness" => [0,3], increments of 1 + // Represents powers of 2: 0 -> 1, 3 -> 8 + FLEXIBLE_OBJECT_MIN_SECTIONS = 0, + FLEXIBLE_OBJECT_DEFAULT_NUM_SECTIONS = 2, + FLEXIBLE_OBJECT_MAX_SECTIONS = 3 +}; + +// "Tension" => [0,10], increments of 0.1 +extern const F32 FLEXIBLE_OBJECT_MIN_TENSION; +extern const F32 FLEXIBLE_OBJECT_DEFAULT_TENSION; +extern const F32 FLEXIBLE_OBJECT_MAX_TENSION; + +// "Drag" => [0,10], increments of 0.1 +extern const F32 FLEXIBLE_OBJECT_MIN_AIR_FRICTION; +extern const F32 FLEXIBLE_OBJECT_DEFAULT_AIR_FRICTION; +extern const F32 FLEXIBLE_OBJECT_MAX_AIR_FRICTION; + +// "Gravity" = [-10,10], increments of 0.1 +extern const F32 FLEXIBLE_OBJECT_MIN_GRAVITY; +extern const F32 FLEXIBLE_OBJECT_DEFAULT_GRAVITY; +extern const F32 FLEXIBLE_OBJECT_MAX_GRAVITY; + +// "Wind" = [0,10], increments of 0.1 +extern const F32 FLEXIBLE_OBJECT_MIN_WIND_SENSITIVITY; +extern const F32 FLEXIBLE_OBJECT_DEFAULT_WIND_SENSITIVITY; +extern const F32 FLEXIBLE_OBJECT_MAX_WIND_SENSITIVITY; + +extern const F32 FLEXIBLE_OBJECT_MAX_INTERNAL_TENSION_FORCE; + +extern const F32 FLEXIBLE_OBJECT_DEFAULT_LENGTH; +extern const bool FLEXIBLE_OBJECT_DEFAULT_USING_COLLISION_SPHERE; +extern const bool FLEXIBLE_OBJECT_DEFAULT_RENDERING_COLLISION_SPHERE; + + +class LLFlexibleObjectData : public LLNetworkData +{ +protected: + S32 mSimulateLOD; // 2^n = number of simulated sections + F32 mGravity; + F32 mAirFriction; // higher is more stable, but too much looks like it's underwater + F32 mWindSensitivity; // interacts with tension, air friction, and gravity + F32 mTension; //interacts in complex ways with other parameters + LLVector3 mUserForce; // custom user-defined force vector + //bool mUsingCollisionSphere; + //bool mRenderingCollisionSphere; + +public: + void setSimulateLOD(S32 lod) { mSimulateLOD = llclamp(lod, (S32)FLEXIBLE_OBJECT_MIN_SECTIONS, (S32)FLEXIBLE_OBJECT_MAX_SECTIONS); } + void setGravity(F32 gravity) { mGravity = llclamp(gravity, FLEXIBLE_OBJECT_MIN_GRAVITY, FLEXIBLE_OBJECT_MAX_GRAVITY); } + void setAirFriction(F32 friction) { mAirFriction = llclamp(friction, FLEXIBLE_OBJECT_MIN_AIR_FRICTION, FLEXIBLE_OBJECT_MAX_AIR_FRICTION); } + void setWindSensitivity(F32 wind) { mWindSensitivity = llclamp(wind, FLEXIBLE_OBJECT_MIN_WIND_SENSITIVITY, FLEXIBLE_OBJECT_MAX_WIND_SENSITIVITY); } + void setTension(F32 tension) { mTension = llclamp(tension, FLEXIBLE_OBJECT_MIN_TENSION, FLEXIBLE_OBJECT_MAX_TENSION); } + void setUserForce(LLVector3 &force) { mUserForce = force; } + + S32 getSimulateLOD() const { return mSimulateLOD; } + F32 getGravity() const { return mGravity; } + F32 getAirFriction() const { return mAirFriction; } + F32 getWindSensitivity() const { return mWindSensitivity; } + F32 getTension() const { return mTension; } + LLVector3 getUserForce() const { return mUserForce; } + + //------ the constructor for the structure ------------ + LLFlexibleObjectData(); + bool pack(LLDataPacker &dp) const; + bool unpack(LLDataPacker &dp); + bool operator==(const LLNetworkData& data) const; + void copy(const LLNetworkData& data); + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); +};// end of attributes structure + + + +class LLSculptParams : public LLNetworkData +{ +protected: + LLUUID mSculptTexture; + U8 mSculptType; + +public: + LLSculptParams(); + /*virtual*/ bool pack(LLDataPacker &dp) const; + /*virtual*/ bool unpack(LLDataPacker &dp); + /*virtual*/ bool operator==(const LLNetworkData& data) const; + /*virtual*/ void copy(const LLNetworkData& data); + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); + + void setSculptTexture(const LLUUID& texture_id, U8 sculpt_type); + LLUUID getSculptTexture() const { return mSculptTexture; } + U8 getSculptType() const { return mSculptType; } +}; + +class LLLightImageParams : public LLNetworkData +{ +protected: + LLUUID mLightTexture; + LLVector3 mParams; + +public: + LLLightImageParams(); + /*virtual*/ bool pack(LLDataPacker &dp) const; + /*virtual*/ bool unpack(LLDataPacker &dp); + /*virtual*/ bool operator==(const LLNetworkData& data) const; + /*virtual*/ void copy(const LLNetworkData& data); + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); + + void setLightTexture(const LLUUID& id) { mLightTexture = id; } + LLUUID getLightTexture() const { return mLightTexture; } + bool isLightSpotlight() const { return mLightTexture.notNull(); } + void setParams(const LLVector3& params) { mParams = params; } + LLVector3 getParams() const { return mParams; } + +}; + +class LLExtendedMeshParams : public LLNetworkData +{ +protected: + U32 mFlags; + +public: + static const U32 ANIMATED_MESH_ENABLED_FLAG = 0x1 << 0; + + LLExtendedMeshParams(); + /*virtual*/ bool pack(LLDataPacker &dp) const; + /*virtual*/ bool unpack(LLDataPacker &dp); + /*virtual*/ bool operator==(const LLNetworkData& data) const; + /*virtual*/ void copy(const LLNetworkData& data); + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); + + void setFlags(const U32& flags) { mFlags = flags; } + U32 getFlags() const { return mFlags; } + +}; + +class LLRenderMaterialParams : public LLNetworkData +{ +private: + struct Entry + { + U8 te_idx; + LLUUID id; + }; + std::vector< Entry > mEntries; + +public: + LLRenderMaterialParams(); + bool pack(LLDataPacker& dp) const override; + bool unpack(LLDataPacker& dp) override; + bool operator==(const LLNetworkData& data) const override; + void copy(const LLNetworkData& data) override; + + void setMaterial(U8 te_idx, const LLUUID& id); + const LLUUID& getMaterial(U8 te_idx) const; + + bool isEmpty() { return mEntries.empty(); } +}; + + +// This code is not naming-standards compliant. Leaving it like this for +// now to make the connection to code in +// bool packTEMessage(LLDataPacker &dp) const; +// more obvious. This should be refactored to remove the duplication, at which +// point we can fix the names as well. +// - Vir +struct LLTEContents +{ + static const U32 MAX_TES = 45; + + LLUUID image_data[MAX_TES]; + LLColor4U colors[MAX_TES]; + F32 scale_s[MAX_TES]; + F32 scale_t[MAX_TES]; + S16 offset_s[MAX_TES]; + S16 offset_t[MAX_TES]; + S16 image_rot[MAX_TES]; + U8 bump[MAX_TES]; + U8 media_flags[MAX_TES]; + U8 glow[MAX_TES]; + LLMaterialID material_ids[MAX_TES]; + + static const U32 MAX_TE_BUFFER = 4096; + U8 packed_buffer[MAX_TE_BUFFER]; + + U32 size; + U32 face_count; +}; + +class LLPrimitive : public LLXform +{ +public: + + // HACK for removing LLPrimitive's dependency on gVolumeMgr global. + // If a different LLVolumeManager is instantiated and set early enough + // then the LLPrimitive class will use it instead of gVolumeMgr. + static LLVolumeMgr* getVolumeManager() { return sVolumeManager; } + static void setVolumeManager( LLVolumeMgr* volume_manager); + static bool cleanupVolumeManager(); + + // these flags influence how the RigidBody representation is built + static const U32 PRIM_FLAG_PHANTOM = 0x1 << 0; + static const U32 PRIM_FLAG_VOLUME_DETECT = 0x1 << 1; + static const U32 PRIM_FLAG_DYNAMIC = 0x1 << 2; + static const U32 PRIM_FLAG_AVATAR = 0x1 << 3; + static const U32 PRIM_FLAG_SCULPT = 0x1 << 4; + // not used yet, but soon + static const U32 PRIM_FLAG_COLLISION_CALLBACK = 0x1 << 5; + static const U32 PRIM_FLAG_CONVEX = 0x1 << 6; + static const U32 PRIM_FLAG_DEFAULT_VOLUME = 0x1 << 7; + static const U32 PRIM_FLAG_SITTING = 0x1 << 8; + static const U32 PRIM_FLAG_SITTING_ON_GROUND = 0x1 << 9; // Set along with PRIM_FLAG_SITTING + + LLPrimitive(); + virtual ~LLPrimitive(); + + void clearTextureList(); + + static LLPrimitive *createPrimitive(LLPCode p_code); + void init_primitive(LLPCode p_code); + + void setPCode(const LLPCode pcode); + const LLVolume *getVolumeConst() const { return mVolumep; } // HACK for Windoze confusion about ostream operator in LLVolume + LLVolume *getVolume() const { return mVolumep; } + virtual bool setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume = false); + + // Modify texture entry properties + inline bool validTE(const U8 te_num) const; + LLTextureEntry* getTE(const U8 te_num) const; + + virtual void setNumTEs(const U8 num_tes); + virtual void setAllTESelected(bool sel); + virtual void setAllTETextures(const LLUUID &tex_id); + virtual void setTE(const U8 index, const LLTextureEntry& te); + virtual S32 setTEColor(const U8 te, const LLColor4 &color); + virtual S32 setTEColor(const U8 te, const LLColor3 &color); + virtual S32 setTEAlpha(const U8 te, const F32 alpha); + virtual S32 setTETexture(const U8 te, const LLUUID &tex_id); + virtual S32 setTEScale (const U8 te, const F32 s, const F32 t); + virtual S32 setTEScaleS(const U8 te, const F32 s); + virtual S32 setTEScaleT(const U8 te, const F32 t); + virtual S32 setTEOffset (const U8 te, const F32 s, const F32 t); + virtual S32 setTEOffsetS(const U8 te, const F32 s); + virtual S32 setTEOffsetT(const U8 te, const F32 t); + virtual S32 setTERotation(const U8 te, const F32 r); + virtual S32 setTEBumpShinyFullbright(const U8 te, const U8 bump); + virtual S32 setTEBumpShiny(const U8 te, const U8 bump); + virtual S32 setTEMediaTexGen(const U8 te, const U8 media); + virtual S32 setTEBumpmap(const U8 te, const U8 bump); + virtual S32 setTETexGen(const U8 te, const U8 texgen); + virtual S32 setTEShiny(const U8 te, const U8 shiny); + virtual S32 setTEFullbright(const U8 te, const U8 fullbright); + virtual S32 setTEMediaFlags(const U8 te, const U8 flags); + virtual S32 setTEGlow(const U8 te, const F32 glow); + virtual S32 setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID); + virtual S32 setTEMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams); + virtual bool setMaterial(const U8 material); // returns true if material changed + virtual void setTESelected(const U8 te, bool sel); + + LLMaterialPtr getTEMaterialParams(const U8 index); + + void copyTEs(const LLPrimitive *primitive); + S32 packTEField(U8 *cur_ptr, U8 *data_ptr, U8 data_size, U8 last_face_index, EMsgVariableType type) const; + bool packTEMessage(LLMessageSystem *mesgsys) const; + bool packTEMessage(LLDataPacker &dp) const; + S32 unpackTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num); // Variable num of blocks + S32 unpackTEMessage(LLDataPacker &dp); + S32 parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec); + S32 applyParsedTEMessage(LLTEContents& tec); + +#ifdef CHECK_FOR_FINITE + inline void setPosition(const LLVector3& pos); + inline void setPosition(const F32 x, const F32 y, const F32 z); + inline void addPosition(const LLVector3& pos); + + inline void setAngularVelocity(const LLVector3& avel); + inline void setAngularVelocity(const F32 x, const F32 y, const F32 z); + inline void setVelocity(const LLVector3& vel); + inline void setVelocity(const F32 x, const F32 y, const F32 z); + inline void setVelocityX(const F32 x); + inline void setVelocityY(const F32 y); + inline void setVelocityZ(const F32 z); + inline void addVelocity(const LLVector3& vel); + inline void setAcceleration(const LLVector3& accel); + inline void setAcceleration(const F32 x, const F32 y, const F32 z); +#else + // Don't override the base LLXForm operators. + // Special case for setPosition. If not check-for-finite, fall through to LLXform method. + // void setPosition(F32 x, F32 y, F32 z) + // void setPosition(LLVector3) + + void setAngularVelocity(const LLVector3& avel) { mAngularVelocity = avel; } + void setAngularVelocity(const F32 x, const F32 y, const F32 z) { mAngularVelocity.setVec(x,y,z); } + void setVelocity(const LLVector3& vel) { mVelocity = vel; } + void setVelocity(const F32 x, const F32 y, const F32 z) { mVelocity.setVec(x,y,z); } + void setVelocityX(const F32 x) { mVelocity.mV[VX] = x; } + void setVelocityY(const F32 y) { mVelocity.mV[VY] = y; } + void setVelocityZ(const F32 z) { mVelocity.mV[VZ] = z; } + void addVelocity(const LLVector3& vel) { mVelocity += vel; } + void setAcceleration(const LLVector3& accel) { mAcceleration = accel; } + void setAcceleration(const F32 x, const F32 y, const F32 z) { mAcceleration.setVec(x,y,z); } +#endif + + LLPCode getPCode() const { return mPrimitiveCode; } + std::string getPCodeString() const { return pCodeToString(mPrimitiveCode); } + const LLVector3& getAngularVelocity() const { return mAngularVelocity; } + const LLVector3& getVelocity() const { return mVelocity; } + const LLVector3& getAcceleration() const { return mAcceleration; } + U8 getNumTEs() const { return mTextureList.size(); } + U8 getExpectedNumTEs() const; + + U8 getMaterial() const { return mMaterial; } + + void setVolumeType(const U8 code); + U8 getVolumeType(); + + // clears existing textures + // copies the contents of other_list into mEntryList + void copyTextureList(const LLPrimTextureList& other_list); + + // clears existing textures + // takes the contents of other_list and clears other_list + void takeTextureList(LLPrimTextureList& other_list); + + inline bool isAvatar() const; + inline bool isSittingAvatar() const; + inline bool isSittingAvatarOnGround() const; + inline bool hasBumpmap() const { return mNumBumpmapTEs > 0;} + + void setFlags(U32 flags) { mMiscFlags = flags; } + void addFlags(U32 flags) { mMiscFlags |= flags; } + void removeFlags(U32 flags) { mMiscFlags &= ~flags; } + U32 getFlags() const { return mMiscFlags; } + bool checkFlags(U32 flags) const { return (mMiscFlags & flags) != 0; } + + static std::string pCodeToString(const LLPCode pcode); + static LLPCode legacyToPCode(const U8 legacy); + static U8 pCodeToLegacy(const LLPCode pcode); + static bool getTESTAxes(const U8 face, U32* s_axis, U32* t_axis); + + bool hasRenderMaterialParams() const; + + inline static bool isPrimitive(const LLPCode pcode); + inline static bool isApp(const LLPCode pcode); + +private: + void updateNumBumpmap(const U8 index, const U8 bump); + +protected: + LLPCode mPrimitiveCode; // Primitive code + LLVector3 mVelocity; // how fast are we moving? + LLVector3 mAcceleration; // are we under constant acceleration? + LLVector3 mAngularVelocity; // angular velocity + LLPointer<LLVolume> mVolumep; + LLPrimTextureList mTextureList; // list of texture GUIDs, scales, offsets + U8 mMaterial; // Material code + U8 mNumTEs; // # of faces on the primitve + U8 mNumBumpmapTEs; // number of bumpmap TEs. + U32 mMiscFlags; // home for misc bools + +public: + static LLVolumeMgr* sVolumeManager; + + enum + { + NO_LOD = -1 + }; +}; + +inline bool LLPrimitive::isAvatar() const +{ + return LL_PCODE_LEGACY_AVATAR == mPrimitiveCode; +} + +inline bool LLPrimitive::isSittingAvatar() const +{ + // this is only used server-side + return isAvatar() && checkFlags(PRIM_FLAG_SITTING | PRIM_FLAG_SITTING_ON_GROUND); +} + +inline bool LLPrimitive::isSittingAvatarOnGround() const +{ + // this is only used server-side + return isAvatar() && checkFlags(PRIM_FLAG_SITTING_ON_GROUND); +} + +// static +inline bool LLPrimitive::isPrimitive(const LLPCode pcode) +{ + LLPCode base_type = pcode & LL_PCODE_BASE_MASK; + + if (base_type && (base_type < LL_PCODE_APP)) + { + return true; + } + return false; +} + +// static +inline bool LLPrimitive::isApp(const LLPCode pcode) +{ + LLPCode base_type = pcode & LL_PCODE_BASE_MASK; + + return (base_type == LL_PCODE_APP); +} + + +#ifdef CHECK_FOR_FINITE +// Special case for setPosition. If not check-for-finite, fall through to LLXform method. +void LLPrimitive::setPosition(const F32 x, const F32 y, const F32 z) +{ + if (llfinite(x) && llfinite(y) && llfinite(z)) + { + LLXform::setPosition(x, y, z); + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setPosition(x,y,z) for " << pCodeToString(mPrimitiveCode) << LL_ENDL; + } +} + +// Special case for setPosition. If not check-for-finite, fall through to LLXform method. +void LLPrimitive::setPosition(const LLVector3& pos) +{ + if (pos.isFinite()) + { + LLXform::setPosition(pos); + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setPosition(LLVector3) for " << pCodeToString(mPrimitiveCode) << LL_ENDL; + } +} + +void LLPrimitive::setAngularVelocity(const LLVector3& avel) +{ + if (avel.isFinite()) + { + mAngularVelocity = avel; + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setAngularVelocity" << LL_ENDL; + } +} + +void LLPrimitive::setAngularVelocity(const F32 x, const F32 y, const F32 z) +{ + if (llfinite(x) && llfinite(y) && llfinite(z)) + { + mAngularVelocity.setVec(x,y,z); + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setAngularVelocity" << LL_ENDL; + } +} + +void LLPrimitive::setVelocity(const LLVector3& vel) +{ + if (vel.isFinite()) + { + mVelocity = vel; + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setVelocity(LLVector3) for " << pCodeToString(mPrimitiveCode) << LL_ENDL; + } +} + +void LLPrimitive::setVelocity(const F32 x, const F32 y, const F32 z) +{ + if (llfinite(x) && llfinite(y) && llfinite(z)) + { + mVelocity.setVec(x,y,z); + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setVelocity(F32,F32,F32) for " << pCodeToString(mPrimitiveCode) << LL_ENDL; + } +} + +void LLPrimitive::setVelocityX(const F32 x) +{ + if (llfinite(x)) + { + mVelocity.mV[VX] = x; + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setVelocityX" << LL_ENDL; + } +} + +void LLPrimitive::setVelocityY(const F32 y) +{ + if (llfinite(y)) + { + mVelocity.mV[VY] = y; + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setVelocityY" << LL_ENDL; + } +} + +void LLPrimitive::setVelocityZ(const F32 z) +{ + if (llfinite(z)) + { + mVelocity.mV[VZ] = z; + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setVelocityZ" << LL_ENDL; + } +} + +void LLPrimitive::addVelocity(const LLVector3& vel) +{ + if (vel.isFinite()) + { + mVelocity += vel; + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::addVelocity" << LL_ENDL; + } +} + +void LLPrimitive::setAcceleration(const LLVector3& accel) +{ + if (accel.isFinite()) + { + mAcceleration = accel; + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setAcceleration(LLVector3) for " << pCodeToString(mPrimitiveCode) << LL_ENDL; + } +} + +void LLPrimitive::setAcceleration(const F32 x, const F32 y, const F32 z) +{ + if (llfinite(x) && llfinite(y) && llfinite(z)) + { + mAcceleration.setVec(x,y,z); + } + else + { + LL_ERRS() << "Non Finite in LLPrimitive::setAcceleration(F32,F32,F32) for " << pCodeToString(mPrimitiveCode) << LL_ENDL; + } +} +#endif // CHECK_FOR_FINITE + +inline bool LLPrimitive::validTE(const U8 te_num) const +{ + return (mNumTEs && te_num < mNumTEs); +} + +#endif + diff --git a/indra/llprimitive/lltextureanim.cpp b/indra/llprimitive/lltextureanim.cpp index 56ae178a3b..579538075a 100644 --- a/indra/llprimitive/lltextureanim.cpp +++ b/indra/llprimitive/lltextureanim.cpp @@ -1,239 +1,239 @@ -/**
- * @file lltextureanim.cpp
- * @brief LLTextureAnim base class
- *
- * $LicenseInfo:firstyear=2001&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 "linden_common.h"
-
-#include "lltextureanim.h"
-#include "message.h"
-#include "lldatapacker.h"
-
-const S32 TA_BLOCK_SIZE = 16;
-
-LLTextureAnim::LLTextureAnim()
-{
- reset();
-}
-
-
-LLTextureAnim::~LLTextureAnim()
-{
-}
-
-
-void LLTextureAnim::reset()
-{
- mMode = 0;
- mFace = -1;
- mSizeX = 4;
- mSizeY = 4;
- mStart = 0.f;
- mLength = 0.f;
- mRate = 1.f;
-}
-
-bool LLTextureAnim::equals(const LLTextureAnim &other) const
-{
- if (mMode != other.mMode)
- {
- return false;
- }
- if (mFace != other.mFace)
- {
- return false;
- }
- if (mSizeX != other.mSizeX)
- {
- return false;
- }
- if (mSizeY != other.mSizeY)
- {
- return false;
- }
- if (mStart != other.mStart)
- {
- return false;
- }
- if (mLength != other.mLength)
- {
- return false;
- }
- if (mRate != other.mRate)
- {
- return false;
- }
-
- return true;
-}
-void LLTextureAnim::packTAMessage(LLMessageSystem *mesgsys) const
-{
- U8 data[TA_BLOCK_SIZE];
- data[0] = mMode;
- data[1] = mFace;
- data[2] = mSizeX;
- data[3] = mSizeY;
- htolememcpy(data + 4, &mStart, MVT_F32, sizeof(F32));
- htolememcpy(data + 8, &mLength, MVT_F32, sizeof(F32));
- htolememcpy(data + 12, &mRate, MVT_F32, sizeof(F32));
-
- mesgsys->addBinaryDataFast(_PREHASH_TextureAnim, data, TA_BLOCK_SIZE);
-}
-
-
-void LLTextureAnim::packTAMessage(LLDataPacker &dp) const
-{
- U8 data[TA_BLOCK_SIZE];
- data[0] = mMode;
- data[1] = mFace;
- data[2] = mSizeX;
- data[3] = mSizeY;
- htolememcpy(data + 4, &mStart, MVT_F32, sizeof(F32));
- htolememcpy(data + 8, &mLength, MVT_F32, sizeof(F32));
- htolememcpy(data + 12, &mRate, MVT_F32, sizeof(F32));
-
- dp.packBinaryData(data, TA_BLOCK_SIZE, "TextureAnimation");
-}
-
-
-void LLTextureAnim::unpackTAMessage(LLMessageSystem *mesgsys, const S32 block_num)
-{
- S32 size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureAnim);
-
- if (size != TA_BLOCK_SIZE)
- {
- if (size)
- {
- LL_WARNS() << "Bad size " << size << " for TA block, ignoring." << LL_ENDL;
- }
- mMode = 0;
- return;
- }
-
- U8 data[TA_BLOCK_SIZE];
- mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureAnim, data, TA_BLOCK_SIZE, block_num);
-
- mMode = data[0];
- mFace = data[1];
- if (mMode & LLTextureAnim::SMOOTH)
- {
- mSizeX = llmax((U8)0, data[2]);
- mSizeY = llmax((U8)0, data[3]);
- }
- else
- {
- mSizeX = llmax((U8)1, data[2]);
- mSizeY = llmax((U8)1, data[3]);
- }
- htolememcpy(&mStart, data + 4, MVT_F32, sizeof(F32));
- htolememcpy(&mLength, data + 8, MVT_F32, sizeof(F32));
- htolememcpy(&mRate, data + 12, MVT_F32, sizeof(F32));
-}
-
-void LLTextureAnim::unpackTAMessage(LLDataPacker &dp)
-{
- S32 size;
- U8 data[TA_BLOCK_SIZE];
- dp.unpackBinaryData(data, size, "TextureAnimation");
- if (size != TA_BLOCK_SIZE)
- {
- if (size)
- {
- LL_WARNS() << "Bad size " << size << " for TA block, ignoring." << LL_ENDL;
- }
- mMode = 0;
- return;
- }
-
- mMode = data[0];
- mFace = data[1];
- mSizeX = data[2];
- mSizeY = data[3];
- htolememcpy(&mStart, data + 4, MVT_F32, sizeof(F32));
- htolememcpy(&mLength, data + 8, MVT_F32, sizeof(F32));
- htolememcpy(&mRate, data + 12, MVT_F32, sizeof(F32));
-}
-
-LLSD LLTextureAnim::asLLSD() const
-{
- LLSD sd;
- sd["mode"] = mMode;
- sd["face"] = mFace;
- sd["sizeX"] = mSizeX;
- sd["sizeY"] = mSizeY;
- sd["start"] = mStart;
- sd["length"] = mLength;
- sd["rate"] = mRate;
- return sd;
-}
-
-bool LLTextureAnim::fromLLSD(LLSD& sd)
-{
- const char *w;
- w = "mode";
- if (sd.has(w))
- {
- mMode = (U8)sd[w].asInteger();
- } else goto fail;
-
- w = "face";
- if (sd.has(w))
- {
- mFace = (S8)sd[w].asInteger();
- } else goto fail;
-
- w = "sizeX";
- if (sd.has(w))
- {
- mSizeX = (U8)sd[w].asInteger();
- } else goto fail;
-
- w = "sizeY";
- if (sd.has(w))
- {
- mSizeY = (U8)sd[w].asInteger();
- } else goto fail;
-
- w = "start";
- if (sd.has(w))
- {
- mStart = (F32)sd[w].asReal();
- } else goto fail;
-
- w = "length";
- if (sd.has(w))
- {
- mLength = (F32)sd[w].asReal();
- } else goto fail;
-
- w = "rate";
- if (sd.has(w))
- {
- mRate = (F32)sd[w].asReal();
- } else goto fail;
-
- return true;
-fail:
- return false;
-}
+/** + * @file lltextureanim.cpp + * @brief LLTextureAnim base class + * + * $LicenseInfo:firstyear=2001&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 "linden_common.h" + +#include "lltextureanim.h" +#include "message.h" +#include "lldatapacker.h" + +const S32 TA_BLOCK_SIZE = 16; + +LLTextureAnim::LLTextureAnim() +{ + reset(); +} + + +LLTextureAnim::~LLTextureAnim() +{ +} + + +void LLTextureAnim::reset() +{ + mMode = 0; + mFace = -1; + mSizeX = 4; + mSizeY = 4; + mStart = 0.f; + mLength = 0.f; + mRate = 1.f; +} + +bool LLTextureAnim::equals(const LLTextureAnim &other) const +{ + if (mMode != other.mMode) + { + return false; + } + if (mFace != other.mFace) + { + return false; + } + if (mSizeX != other.mSizeX) + { + return false; + } + if (mSizeY != other.mSizeY) + { + return false; + } + if (mStart != other.mStart) + { + return false; + } + if (mLength != other.mLength) + { + return false; + } + if (mRate != other.mRate) + { + return false; + } + + return true; +} +void LLTextureAnim::packTAMessage(LLMessageSystem *mesgsys) const +{ + U8 data[TA_BLOCK_SIZE]; + data[0] = mMode; + data[1] = mFace; + data[2] = mSizeX; + data[3] = mSizeY; + htolememcpy(data + 4, &mStart, MVT_F32, sizeof(F32)); + htolememcpy(data + 8, &mLength, MVT_F32, sizeof(F32)); + htolememcpy(data + 12, &mRate, MVT_F32, sizeof(F32)); + + mesgsys->addBinaryDataFast(_PREHASH_TextureAnim, data, TA_BLOCK_SIZE); +} + + +void LLTextureAnim::packTAMessage(LLDataPacker &dp) const +{ + U8 data[TA_BLOCK_SIZE]; + data[0] = mMode; + data[1] = mFace; + data[2] = mSizeX; + data[3] = mSizeY; + htolememcpy(data + 4, &mStart, MVT_F32, sizeof(F32)); + htolememcpy(data + 8, &mLength, MVT_F32, sizeof(F32)); + htolememcpy(data + 12, &mRate, MVT_F32, sizeof(F32)); + + dp.packBinaryData(data, TA_BLOCK_SIZE, "TextureAnimation"); +} + + +void LLTextureAnim::unpackTAMessage(LLMessageSystem *mesgsys, const S32 block_num) +{ + S32 size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureAnim); + + if (size != TA_BLOCK_SIZE) + { + if (size) + { + LL_WARNS() << "Bad size " << size << " for TA block, ignoring." << LL_ENDL; + } + mMode = 0; + return; + } + + U8 data[TA_BLOCK_SIZE]; + mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureAnim, data, TA_BLOCK_SIZE, block_num); + + mMode = data[0]; + mFace = data[1]; + if (mMode & LLTextureAnim::SMOOTH) + { + mSizeX = llmax((U8)0, data[2]); + mSizeY = llmax((U8)0, data[3]); + } + else + { + mSizeX = llmax((U8)1, data[2]); + mSizeY = llmax((U8)1, data[3]); + } + htolememcpy(&mStart, data + 4, MVT_F32, sizeof(F32)); + htolememcpy(&mLength, data + 8, MVT_F32, sizeof(F32)); + htolememcpy(&mRate, data + 12, MVT_F32, sizeof(F32)); +} + +void LLTextureAnim::unpackTAMessage(LLDataPacker &dp) +{ + S32 size; + U8 data[TA_BLOCK_SIZE]; + dp.unpackBinaryData(data, size, "TextureAnimation"); + if (size != TA_BLOCK_SIZE) + { + if (size) + { + LL_WARNS() << "Bad size " << size << " for TA block, ignoring." << LL_ENDL; + } + mMode = 0; + return; + } + + mMode = data[0]; + mFace = data[1]; + mSizeX = data[2]; + mSizeY = data[3]; + htolememcpy(&mStart, data + 4, MVT_F32, sizeof(F32)); + htolememcpy(&mLength, data + 8, MVT_F32, sizeof(F32)); + htolememcpy(&mRate, data + 12, MVT_F32, sizeof(F32)); +} + +LLSD LLTextureAnim::asLLSD() const +{ + LLSD sd; + sd["mode"] = mMode; + sd["face"] = mFace; + sd["sizeX"] = mSizeX; + sd["sizeY"] = mSizeY; + sd["start"] = mStart; + sd["length"] = mLength; + sd["rate"] = mRate; + return sd; +} + +bool LLTextureAnim::fromLLSD(LLSD& sd) +{ + const char *w; + w = "mode"; + if (sd.has(w)) + { + mMode = (U8)sd[w].asInteger(); + } else goto fail; + + w = "face"; + if (sd.has(w)) + { + mFace = (S8)sd[w].asInteger(); + } else goto fail; + + w = "sizeX"; + if (sd.has(w)) + { + mSizeX = (U8)sd[w].asInteger(); + } else goto fail; + + w = "sizeY"; + if (sd.has(w)) + { + mSizeY = (U8)sd[w].asInteger(); + } else goto fail; + + w = "start"; + if (sd.has(w)) + { + mStart = (F32)sd[w].asReal(); + } else goto fail; + + w = "length"; + if (sd.has(w)) + { + mLength = (F32)sd[w].asReal(); + } else goto fail; + + w = "rate"; + if (sd.has(w)) + { + mRate = (F32)sd[w].asReal(); + } else goto fail; + + return true; +fail: + return false; +} diff --git a/indra/llprimitive/lltextureanim.h b/indra/llprimitive/lltextureanim.h index ad03c5d601..2d10153043 100644 --- a/indra/llprimitive/lltextureanim.h +++ b/indra/llprimitive/lltextureanim.h @@ -1,72 +1,72 @@ -/**
- * @file lltextureanim.h
- * @brief LLTextureAnim base class
- *
- * $LicenseInfo:firstyear=2001&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_LLTEXTUREANIM_H
-#define LL_LLTEXTUREANIM_H
-
-#include "stdtypes.h"
-#include "llsd.h"
-
-class LLMessageSystem;
-class LLDataPacker;
-
-class LLTextureAnim
-{
-public:
- LLTextureAnim();
- virtual ~LLTextureAnim();
-
- virtual void reset();
- void packTAMessage(LLMessageSystem *mesgsys) const;
- void packTAMessage(LLDataPacker &dp) const;
- void unpackTAMessage(LLMessageSystem *mesgsys, const S32 block_num);
- void unpackTAMessage(LLDataPacker &dp);
- bool equals(const LLTextureAnim &other) const;
- LLSD asLLSD() const;
- operator LLSD() const { return asLLSD(); }
- bool fromLLSD(LLSD& sd);
-
- enum
- {
- ON = 0x01,
- LOOP = 0x02,
- REVERSE = 0x04,
- PING_PONG = 0x08,
- SMOOTH = 0x10,
- ROTATE = 0x20,
- SCALE = 0x40,
- };
-
-public:
- U8 mMode;
- S8 mFace;
- U8 mSizeX;
- U8 mSizeY;
- F32 mStart;
- F32 mLength;
- F32 mRate; // Rate in frames per second.
-};
-#endif
+/** + * @file lltextureanim.h + * @brief LLTextureAnim base class + * + * $LicenseInfo:firstyear=2001&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_LLTEXTUREANIM_H +#define LL_LLTEXTUREANIM_H + +#include "stdtypes.h" +#include "llsd.h" + +class LLMessageSystem; +class LLDataPacker; + +class LLTextureAnim +{ +public: + LLTextureAnim(); + virtual ~LLTextureAnim(); + + virtual void reset(); + void packTAMessage(LLMessageSystem *mesgsys) const; + void packTAMessage(LLDataPacker &dp) const; + void unpackTAMessage(LLMessageSystem *mesgsys, const S32 block_num); + void unpackTAMessage(LLDataPacker &dp); + bool equals(const LLTextureAnim &other) const; + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); + + enum + { + ON = 0x01, + LOOP = 0x02, + REVERSE = 0x04, + PING_PONG = 0x08, + SMOOTH = 0x10, + ROTATE = 0x20, + SCALE = 0x40, + }; + +public: + U8 mMode; + S8 mFace; + U8 mSizeX; + U8 mSizeY; + F32 mStart; + F32 mLength; + F32 mRate; // Rate in frames per second. +}; +#endif diff --git a/indra/llprimitive/llvolumemessage.cpp b/indra/llprimitive/llvolumemessage.cpp index 5f5fa502dd..aa4bd711ab 100644 --- a/indra/llprimitive/llvolumemessage.cpp +++ b/indra/llprimitive/llvolumemessage.cpp @@ -1,557 +1,557 @@ -/**
- * @file llvolumemessage.cpp
- * @brief LLVolumeMessage base class
- *
- * $LicenseInfo:firstyear=2001&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 "linden_common.h"
-
-#include "message.h"
-#include "llvolumemessage.h"
-#include "lldatapacker.h"
-
-//============================================================================
-
-// LLVolumeMessage is just a wrapper class; all members are static
-
-//============================================================================
-
-bool LLVolumeMessage::packProfileParams(
- const LLProfileParams* params,
- LLMessageSystem *mesgsys)
-{
- // Default to cylinder
- static LLProfileParams defaultparams(LL_PCODE_PROFILE_CIRCLE, U16(0), U16(0), U16(0));
-
- if (!params)
- params = &defaultparams;
-
- U8 tempU8;
- U16 tempU16;
-
- tempU8 = params->getCurveType();
- mesgsys->addU8Fast(_PREHASH_ProfileCurve, tempU8);
-
- tempU16 = (U16) ll_round( params->getBegin() / CUT_QUANTA);
- mesgsys->addU16Fast(_PREHASH_ProfileBegin, tempU16);
-
- tempU16 = 50000 - (U16) ll_round(params->getEnd() / CUT_QUANTA);
- mesgsys->addU16Fast(_PREHASH_ProfileEnd, tempU16);
-
- tempU16 = (U16) ll_round(params->getHollow() / HOLLOW_QUANTA);
- mesgsys->addU16Fast(_PREHASH_ProfileHollow, tempU16);
-
- return true;
-}
-
-bool LLVolumeMessage::packProfileParams(
- const LLProfileParams* params,
- LLDataPacker &dp)
-{
- // Default to cylinder
- static LLProfileParams defaultparams(LL_PCODE_PROFILE_CIRCLE, U16(0), U16(0), U16(0));
-
- if (!params)
- params = &defaultparams;
-
- U8 tempU8;
- U16 tempU16;
-
- tempU8 = params->getCurveType();
- dp.packU8(tempU8, "Curve");
-
- tempU16 = (U16) ll_round( params->getBegin() / CUT_QUANTA);
- dp.packU16(tempU16, "Begin");
-
- tempU16 = 50000 - (U16) ll_round(params->getEnd() / CUT_QUANTA);
- dp.packU16(tempU16, "End");
-
- tempU16 = (U16) ll_round(params->getHollow() / HOLLOW_QUANTA);
- dp.packU16(tempU16, "Hollow");
- return true;
-}
-
-bool LLVolumeMessage::unpackProfileParams(
- LLProfileParams* params,
- LLMessageSystem* mesgsys,
- char const* block_name,
- S32 block_num)
-{
- bool ok = true;
- U8 temp_u8;
- U16 temp_u16;
- F32 temp_f32;
-
- mesgsys->getU8Fast(block_name, _PREHASH_ProfileCurve, temp_u8, block_num);
- params->setCurveType(temp_u8);
-
- mesgsys->getU16Fast(block_name, _PREHASH_ProfileBegin, temp_u16, block_num);
- temp_f32 = temp_u16 * CUT_QUANTA;
- if (temp_f32 > 1.f)
- {
- LL_WARNS() << "Profile begin out of range: " << temp_f32
- << ". Clamping to 0.0." << LL_ENDL;
- temp_f32 = 0.f;
- ok = false;
- }
- params->setBegin(temp_f32);
-
- mesgsys->getU16Fast(block_name, _PREHASH_ProfileEnd, temp_u16, block_num);
- temp_f32 = temp_u16 * CUT_QUANTA;
- if (temp_f32 > 1.f)
- {
- LL_WARNS() << "Profile end out of range: " << 1.f - temp_f32
- << ". Clamping to 1.0." << LL_ENDL;
- temp_f32 = 1.f;
- ok = false;
- }
- params->setEnd(1.f - temp_f32);
-
- mesgsys->getU16Fast(block_name, _PREHASH_ProfileHollow, temp_u16, block_num);
- temp_f32 = temp_u16 * HOLLOW_QUANTA;
- if (temp_f32 > 1.f)
- {
- LL_WARNS() << "Profile hollow out of range: " << temp_f32
- << ". Clamping to 0.0." << LL_ENDL;
- temp_f32 = 0.f;
- ok = false;
- }
- params->setHollow(temp_f32);
-
- /*
- LL_INFOS() << "Unpacking Profile Block " << block_num << LL_ENDL;
- LL_INFOS() << "Curve: " << (U32)getCurve() << LL_ENDL;
- LL_INFOS() << "Begin: " << getBegin() << LL_ENDL;
- LL_INFOS() << "End: " << getEnd() << LL_ENDL;
- LL_INFOS() << "Hollow: " << getHollow() << LL_ENDL;
- */
- return ok;
-
-}
-
-bool LLVolumeMessage::unpackProfileParams(
- LLProfileParams* params,
- LLDataPacker &dp)
-{
- bool ok = true;
- U8 temp_u8;
- U16 temp_u16;
- F32 temp_f32;
-
- dp.unpackU8(temp_u8, "Curve");
- params->setCurveType(temp_u8);
-
- dp.unpackU16(temp_u16, "Begin");
- temp_f32 = temp_u16 * CUT_QUANTA;
- if (temp_f32 > 1.f)
- {
- LL_WARNS() << "Profile begin out of range: " << temp_f32 << LL_ENDL;
- LL_WARNS() << "Clamping to 0.0" << LL_ENDL;
- temp_f32 = 0.f;
- ok = false;
- }
- params->setBegin(temp_f32);
-
- dp.unpackU16(temp_u16, "End");
- temp_f32 = temp_u16 * CUT_QUANTA;
- if (temp_f32 > 1.f)
- {
- LL_WARNS() << "Profile end out of range: " << 1.f - temp_f32 << LL_ENDL;
- LL_WARNS() << "Clamping to 1.0" << LL_ENDL;
- temp_f32 = 1.f;
- ok = false;
- }
- params->setEnd(1.f - temp_f32);
-
- dp.unpackU16(temp_u16, "Hollow");
- temp_f32 = temp_u16 * HOLLOW_QUANTA;
- if (temp_f32 > 1.f)
- {
- LL_WARNS() << "Profile hollow out of range: " << temp_f32 << LL_ENDL;
- LL_WARNS() << "Clamping to 0.0" << LL_ENDL;
- temp_f32 = 0.f;
- ok = false;
- }
- params->setHollow(temp_f32);
-
- return ok;
-}
-
-//============================================================================
-
-// Quantization:
-// For cut begin, range is 0 to 1, quanta is 0.005, 0 maps to 0
-// For cut end, range is 0 to 1, quanta is 0.005, 1 maps to 0
-// For scale, range is 0 to 1, quanta is 0.01, 0 maps to 0, 1 maps to 100
-// For shear, range is -0.5 to 0.5, quanta is 0.01, 0 maps to 0
-// For taper, range is -1 to 1, quanta is 0.01, 0 maps to 0
-bool LLVolumeMessage::packPathParams(
- const LLPathParams* params,
- LLMessageSystem *mesgsys)
-{
- // Default to cylinder with no cut, top same size as bottom, no shear, no twist
- static LLPathParams defaultparams(LL_PCODE_PATH_LINE, U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), 0);
- if (!params)
- params = &defaultparams;
-
- U8 curve = params->getCurveType();
- mesgsys->addU8Fast(_PREHASH_PathCurve, curve);
-
- U16 begin = (U16) ll_round(params->getBegin() / CUT_QUANTA);
- mesgsys->addU16Fast(_PREHASH_PathBegin, begin);
-
- U16 end = 50000 - (U16) ll_round(params->getEnd() / CUT_QUANTA);
- mesgsys->addU16Fast(_PREHASH_PathEnd, end);
-
- // Avoid truncation problem with direct F32->U8 cast.
- // (e.g., (U8) (0.50 / 0.01) = (U8) 49.9999999 = 49 not 50.
-
- U8 pack_scale_x = 200 - (U8) ll_round(params->getScaleX() / SCALE_QUANTA);
- mesgsys->addU8Fast(_PREHASH_PathScaleX, pack_scale_x );
-
- U8 pack_scale_y = 200 - (U8) ll_round(params->getScaleY() / SCALE_QUANTA);
- mesgsys->addU8Fast(_PREHASH_PathScaleY, pack_scale_y );
-
- U8 pack_shear_x = (U8) ll_round(params->getShearX() / SHEAR_QUANTA);
- mesgsys->addU8Fast(_PREHASH_PathShearX, pack_shear_x );
-
- U8 pack_shear_y = (U8) ll_round(params->getShearY() / SHEAR_QUANTA);
- mesgsys->addU8Fast(_PREHASH_PathShearY, pack_shear_y );
-
- S8 twist = (S8) ll_round(params->getTwist() / SCALE_QUANTA);
- mesgsys->addS8Fast(_PREHASH_PathTwist, twist);
-
- S8 twist_begin = (S8) ll_round(params->getTwistBegin() / SCALE_QUANTA);
- mesgsys->addS8Fast(_PREHASH_PathTwistBegin, twist_begin);
-
- S8 radius_offset = (S8) ll_round(params->getRadiusOffset() / SCALE_QUANTA);
- mesgsys->addS8Fast(_PREHASH_PathRadiusOffset, radius_offset);
-
- S8 taper_x = (S8) ll_round(params->getTaperX() / TAPER_QUANTA);
- mesgsys->addS8Fast(_PREHASH_PathTaperX, taper_x);
-
- S8 taper_y = (S8) ll_round(params->getTaperY() / TAPER_QUANTA);
- mesgsys->addS8Fast(_PREHASH_PathTaperY, taper_y);
-
- U8 revolutions = (U8) ll_round( (params->getRevolutions() - 1.0f) / REV_QUANTA);
- mesgsys->addU8Fast(_PREHASH_PathRevolutions, revolutions);
-
- S8 skew = (S8) ll_round(params->getSkew() / SCALE_QUANTA);
- mesgsys->addS8Fast(_PREHASH_PathSkew, skew);
-
- return true;
-}
-
-bool LLVolumeMessage::packPathParams(
- const LLPathParams* params,
- LLDataPacker &dp)
-{
- // Default to cylinder with no cut, top same size as bottom, no shear, no twist
- static LLPathParams defaultparams(LL_PCODE_PATH_LINE, U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), 0);
- if (!params)
- params = &defaultparams;
-
- U8 curve = params->getCurveType();
- dp.packU8(curve, "Curve");
-
- U16 begin = (U16) ll_round(params->getBegin() / CUT_QUANTA);
- dp.packU16(begin, "Begin");
-
- U16 end = 50000 - (U16) ll_round(params->getEnd() / CUT_QUANTA);
- dp.packU16(end, "End");
-
- // Avoid truncation problem with direct F32->U8 cast.
- // (e.g., (U8) (0.50 / 0.01) = (U8) 49.9999999 = 49 not 50.
-
- U8 pack_scale_x = 200 - (U8) ll_round(params->getScaleX() / SCALE_QUANTA);
- dp.packU8(pack_scale_x, "ScaleX");
-
- U8 pack_scale_y = 200 - (U8) ll_round(params->getScaleY() / SCALE_QUANTA);
- dp.packU8(pack_scale_y, "ScaleY");
-
- S8 pack_shear_x = (S8) ll_round(params->getShearX() / SHEAR_QUANTA);
- dp.packU8(*(U8 *)&pack_shear_x, "ShearX");
-
- S8 pack_shear_y = (S8) ll_round(params->getShearY() / SHEAR_QUANTA);
- dp.packU8(*(U8 *)&pack_shear_y, "ShearY");
-
- S8 twist = (S8) ll_round(params->getTwist() / SCALE_QUANTA);
- dp.packU8(*(U8 *)&twist, "Twist");
-
- S8 twist_begin = (S8) ll_round(params->getTwistBegin() / SCALE_QUANTA);
- dp.packU8(*(U8 *)&twist_begin, "TwistBegin");
-
- S8 radius_offset = (S8) ll_round(params->getRadiusOffset() / SCALE_QUANTA);
- dp.packU8(*(U8 *)&radius_offset, "RadiusOffset");
-
- S8 taper_x = (S8) ll_round(params->getTaperX() / TAPER_QUANTA);
- dp.packU8(*(U8 *)&taper_x, "TaperX");
-
- S8 taper_y = (S8) ll_round(params->getTaperY() / TAPER_QUANTA);
- dp.packU8(*(U8 *)&taper_y, "TaperY");
-
- U8 revolutions = (U8) ll_round( (params->getRevolutions() - 1.0f) / REV_QUANTA);
- dp.packU8(*(U8 *)&revolutions, "Revolutions");
-
- S8 skew = (S8) ll_round(params->getSkew() / SCALE_QUANTA);
- dp.packU8(*(U8 *)&skew, "Skew");
-
- return true;
-}
-
-bool LLVolumeMessage::unpackPathParams(
- LLPathParams* params,
- LLMessageSystem* mesgsys,
- char const* block_name,
- S32 block_num)
-{
- U8 curve;
- mesgsys->getU8Fast(block_name, _PREHASH_PathCurve, curve, block_num);
- params->setCurveType(curve);
-
- U16 begin;
- mesgsys->getU16Fast(block_name, _PREHASH_PathBegin, begin, block_num);
- params->setBegin((F32)(begin * CUT_QUANTA));
-
- U16 end;
- mesgsys->getU16Fast(block_name, _PREHASH_PathEnd, end, block_num);
- params->setEnd((F32)((50000 - end) * CUT_QUANTA));
-
- U8 pack_scale_x, pack_scale_y;
- mesgsys->getU8Fast(block_name, _PREHASH_PathScaleX, pack_scale_x, block_num);
- mesgsys->getU8Fast(block_name, _PREHASH_PathScaleY, pack_scale_y, block_num);
- F32 x = (F32) (200 - pack_scale_x) * SCALE_QUANTA;
- F32 y = (F32) (200 - pack_scale_y) * SCALE_QUANTA;
- params->setScale( x, y );
-
- S8 shear_x_quant, shear_y_quant;
- mesgsys->getS8Fast(block_name, _PREHASH_PathShearX, shear_x_quant, block_num);
- mesgsys->getS8Fast(block_name, _PREHASH_PathShearY, shear_y_quant, block_num);
- F32 shear_x = (F32) shear_x_quant * SHEAR_QUANTA;
- F32 shear_y = (F32) shear_y_quant * SHEAR_QUANTA;
- params->setShear( shear_x, shear_y );
-
- S8 twist;
- mesgsys->getS8Fast(block_name, _PREHASH_PathTwist, twist, block_num );
- params->setTwist((F32)(twist * SCALE_QUANTA));
-
- S8 twist_begin;
- mesgsys->getS8Fast(block_name, _PREHASH_PathTwistBegin, twist_begin, block_num );
- params->setTwistBegin((F32)(twist_begin * SCALE_QUANTA));
-
- S8 radius_offset;
- mesgsys->getS8Fast(block_name, _PREHASH_PathRadiusOffset, radius_offset, block_num );
- params->setRadiusOffset((F32)(radius_offset * SCALE_QUANTA));
-
- S8 taper_x_quant, taper_y_quant;
- mesgsys->getS8Fast(block_name, _PREHASH_PathTaperX, taper_x_quant, block_num );
- mesgsys->getS8Fast(block_name, _PREHASH_PathTaperY, taper_y_quant, block_num );
- F32 taper_x = (F32)(taper_x_quant * TAPER_QUANTA);
- F32 taper_y = (F32)(taper_y_quant * TAPER_QUANTA);
- params->setTaper( taper_x, taper_y );
-
- U8 revolutions;
- mesgsys->getU8Fast(block_name, _PREHASH_PathRevolutions, revolutions, block_num );
- params->setRevolutions((F32)(revolutions * REV_QUANTA + 1.0f));
-
- S8 skew;
- mesgsys->getS8Fast(block_name, _PREHASH_PathSkew, skew, block_num );
- params->setSkew((F32)(skew * SCALE_QUANTA));
-
-/*
- LL_INFOS() << "Unpacking Path Block " << block_num << LL_ENDL;
- LL_INFOS() << "Curve: " << (U32)params->getCurve() << LL_ENDL;
- LL_INFOS() << "Begin: " << params->getBegin() << LL_ENDL;
- LL_INFOS() << "End: " << params->getEnd() << LL_ENDL;
- LL_INFOS() << "Scale: " << params->getScale() << LL_ENDL;
- LL_INFOS() << "Twist: " << params->getTwist() << LL_ENDL;
-*/
-
- return true;
-
-}
-
-bool LLVolumeMessage::unpackPathParams(LLPathParams* params, LLDataPacker &dp)
-{
- U8 value;
- S8 svalue;
- U16 temp_u16;
-
- dp.unpackU8(value, "Curve");
- params->setCurveType( value );
-
- dp.unpackU16(temp_u16, "Begin");
- params->setBegin((F32)(temp_u16 * CUT_QUANTA));
-
- dp.unpackU16(temp_u16, "End");
- params->setEnd((F32)((50000 - temp_u16) * CUT_QUANTA));
-
- dp.unpackU8(value, "ScaleX");
- F32 x = (F32) (200 - value) * SCALE_QUANTA;
- dp.unpackU8(value, "ScaleY");
- F32 y = (F32) (200 - value) * SCALE_QUANTA;
- params->setScale( x, y );
-
- dp.unpackU8(value, "ShearX");
- svalue = *(S8 *)&value;
- F32 shear_x = (F32) svalue * SHEAR_QUANTA;
- dp.unpackU8(value, "ShearY");
- svalue = *(S8 *)&value;
- F32 shear_y = (F32) svalue * SHEAR_QUANTA;
- params->setShear( shear_x, shear_y );
-
- dp.unpackU8(value, "Twist");
- svalue = *(S8 *)&value;
- params->setTwist((F32)(svalue * SCALE_QUANTA));
-
- dp.unpackU8(value, "TwistBegin");
- svalue = *(S8 *)&value;
- params->setTwistBegin((F32)(svalue * SCALE_QUANTA));
-
- dp.unpackU8(value, "RadiusOffset");
- svalue = *(S8 *)&value;
- params->setRadiusOffset((F32)(svalue * SCALE_QUANTA));
-
- dp.unpackU8(value, "TaperX");
- svalue = *(S8 *)&value;
- params->setTaperX((F32)(svalue * TAPER_QUANTA));
-
- dp.unpackU8(value, "TaperY");
- svalue = *(S8 *)&value;
- params->setTaperY((F32)(svalue * TAPER_QUANTA));
-
- dp.unpackU8(value, "Revolutions");
- params->setRevolutions((F32)(value * REV_QUANTA + 1.0f));
-
- dp.unpackU8(value, "Skew");
- svalue = *(S8 *)&value;
- params->setSkew((F32)(svalue * SCALE_QUANTA));
-
- return true;
-}
-
-//============================================================================
-
-// static
-bool LLVolumeMessage::constrainVolumeParams(LLVolumeParams& params)
-{
- U32 bad = 0;
-
- // This is called immediately after an unpack. feed the raw data
- // through the checked setters to constraint it to a valid set of
- // volume params.
- bad |= params.setType(params.getProfileParams().getCurveType(),
- params.getPathParams().getCurveType()) ? 0 : 1;
- bad |= params.setBeginAndEndS(params.getProfileParams().getBegin(),
- params.getProfileParams().getEnd()) ? 0 : 2;
- bad |= params.setBeginAndEndT(params.getPathParams().getBegin(),
- params.getPathParams().getEnd()) ? 0 : 4;
- bad |= params.setHollow(params.getProfileParams().getHollow()) ? 0 : 8;
- bad |= params.setTwistBegin(params.getPathParams().getTwistBegin()) ? 0 : 0x10;
- bad |= params.setTwistEnd(params.getPathParams().getTwistEnd()) ? 0 : 0x20;
- bad |= params.setRatio(params.getPathParams().getScaleX(),
- params.getPathParams().getScaleY()) ? 0 : 0x40;
- bad |= params.setShear(params.getPathParams().getShearX(),
- params.getPathParams().getShearY()) ? 0 : 0x80;
- bad |= params.setTaper(params.getPathParams().getTaperX(),
- params.getPathParams().getTaperY()) ? 0 : 0x100;
- bad |= params.setRevolutions(params.getPathParams().getRevolutions()) ? 0 : 0x200;
- bad |= params.setRadiusOffset(params.getPathParams().getRadiusOffset()) ? 0 : 0x400;
- bad |= params.setSkew(params.getPathParams().getSkew()) ? 0 : 0x800;
-
- if (bad)
- {
- LL_WARNS() << "LLVolumeMessage::constrainVolumeParams() - "
- << "forced to constrain incoming volume params: "
- << llformat("0x%04x", bad) << LL_ENDL;
- }
-
- return bad == 0;
-}
-
-bool LLVolumeMessage::packVolumeParams(const LLVolumeParams* params, LLMessageSystem *mesgsys)
-{
- // LL_INFOS() << "pack volume" << LL_ENDL;
- if (params)
- {
- packPathParams(¶ms->getPathParams(), mesgsys);
- packProfileParams(¶ms->getProfileParams(), mesgsys);
- }
- else
- {
- packPathParams(0, mesgsys);
- packProfileParams(0, mesgsys);
- }
- return true;
-}
-
-bool LLVolumeMessage::packVolumeParams(const LLVolumeParams* params, LLDataPacker &dp)
-{
- // LL_INFOS() << "pack volume" << LL_ENDL;
- if (params)
- {
- packPathParams(¶ms->getPathParams(), dp);
- packProfileParams(¶ms->getProfileParams(), dp);
- }
- else
- {
- packPathParams(0, dp);
- packProfileParams(0, dp);
- }
- return true;
-}
-
-bool LLVolumeMessage::unpackVolumeParams(
- LLVolumeParams* params,
- LLMessageSystem* mesgsys,
- char const* block_name,
- S32 block_num)
-{
- bool ok = true;
- ok &= unpackPathParams(
- ¶ms->getPathParams(),
- mesgsys,
- block_name,
- block_num);
- ok &= unpackProfileParams(
- ¶ms->getProfileParams(),
- mesgsys,
- block_name,
- block_num);
- ok &= constrainVolumeParams(*params);
-
- return ok;
-}
-
-bool LLVolumeMessage::unpackVolumeParams(
- LLVolumeParams* params,
- LLDataPacker &dp)
-{
- bool ok = true;
- ok &= unpackPathParams(¶ms->getPathParams(), dp);
- ok &= unpackProfileParams(¶ms->getProfileParams(), dp);
- ok &= constrainVolumeParams(*params);
- return ok;
-}
-
-//============================================================================
+/** + * @file llvolumemessage.cpp + * @brief LLVolumeMessage base class + * + * $LicenseInfo:firstyear=2001&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 "linden_common.h" + +#include "message.h" +#include "llvolumemessage.h" +#include "lldatapacker.h" + +//============================================================================ + +// LLVolumeMessage is just a wrapper class; all members are static + +//============================================================================ + +bool LLVolumeMessage::packProfileParams( + const LLProfileParams* params, + LLMessageSystem *mesgsys) +{ + // Default to cylinder + static LLProfileParams defaultparams(LL_PCODE_PROFILE_CIRCLE, U16(0), U16(0), U16(0)); + + if (!params) + params = &defaultparams; + + U8 tempU8; + U16 tempU16; + + tempU8 = params->getCurveType(); + mesgsys->addU8Fast(_PREHASH_ProfileCurve, tempU8); + + tempU16 = (U16) ll_round( params->getBegin() / CUT_QUANTA); + mesgsys->addU16Fast(_PREHASH_ProfileBegin, tempU16); + + tempU16 = 50000 - (U16) ll_round(params->getEnd() / CUT_QUANTA); + mesgsys->addU16Fast(_PREHASH_ProfileEnd, tempU16); + + tempU16 = (U16) ll_round(params->getHollow() / HOLLOW_QUANTA); + mesgsys->addU16Fast(_PREHASH_ProfileHollow, tempU16); + + return true; +} + +bool LLVolumeMessage::packProfileParams( + const LLProfileParams* params, + LLDataPacker &dp) +{ + // Default to cylinder + static LLProfileParams defaultparams(LL_PCODE_PROFILE_CIRCLE, U16(0), U16(0), U16(0)); + + if (!params) + params = &defaultparams; + + U8 tempU8; + U16 tempU16; + + tempU8 = params->getCurveType(); + dp.packU8(tempU8, "Curve"); + + tempU16 = (U16) ll_round( params->getBegin() / CUT_QUANTA); + dp.packU16(tempU16, "Begin"); + + tempU16 = 50000 - (U16) ll_round(params->getEnd() / CUT_QUANTA); + dp.packU16(tempU16, "End"); + + tempU16 = (U16) ll_round(params->getHollow() / HOLLOW_QUANTA); + dp.packU16(tempU16, "Hollow"); + return true; +} + +bool LLVolumeMessage::unpackProfileParams( + LLProfileParams* params, + LLMessageSystem* mesgsys, + char const* block_name, + S32 block_num) +{ + bool ok = true; + U8 temp_u8; + U16 temp_u16; + F32 temp_f32; + + mesgsys->getU8Fast(block_name, _PREHASH_ProfileCurve, temp_u8, block_num); + params->setCurveType(temp_u8); + + mesgsys->getU16Fast(block_name, _PREHASH_ProfileBegin, temp_u16, block_num); + temp_f32 = temp_u16 * CUT_QUANTA; + if (temp_f32 > 1.f) + { + LL_WARNS() << "Profile begin out of range: " << temp_f32 + << ". Clamping to 0.0." << LL_ENDL; + temp_f32 = 0.f; + ok = false; + } + params->setBegin(temp_f32); + + mesgsys->getU16Fast(block_name, _PREHASH_ProfileEnd, temp_u16, block_num); + temp_f32 = temp_u16 * CUT_QUANTA; + if (temp_f32 > 1.f) + { + LL_WARNS() << "Profile end out of range: " << 1.f - temp_f32 + << ". Clamping to 1.0." << LL_ENDL; + temp_f32 = 1.f; + ok = false; + } + params->setEnd(1.f - temp_f32); + + mesgsys->getU16Fast(block_name, _PREHASH_ProfileHollow, temp_u16, block_num); + temp_f32 = temp_u16 * HOLLOW_QUANTA; + if (temp_f32 > 1.f) + { + LL_WARNS() << "Profile hollow out of range: " << temp_f32 + << ". Clamping to 0.0." << LL_ENDL; + temp_f32 = 0.f; + ok = false; + } + params->setHollow(temp_f32); + + /* + LL_INFOS() << "Unpacking Profile Block " << block_num << LL_ENDL; + LL_INFOS() << "Curve: " << (U32)getCurve() << LL_ENDL; + LL_INFOS() << "Begin: " << getBegin() << LL_ENDL; + LL_INFOS() << "End: " << getEnd() << LL_ENDL; + LL_INFOS() << "Hollow: " << getHollow() << LL_ENDL; + */ + return ok; + +} + +bool LLVolumeMessage::unpackProfileParams( + LLProfileParams* params, + LLDataPacker &dp) +{ + bool ok = true; + U8 temp_u8; + U16 temp_u16; + F32 temp_f32; + + dp.unpackU8(temp_u8, "Curve"); + params->setCurveType(temp_u8); + + dp.unpackU16(temp_u16, "Begin"); + temp_f32 = temp_u16 * CUT_QUANTA; + if (temp_f32 > 1.f) + { + LL_WARNS() << "Profile begin out of range: " << temp_f32 << LL_ENDL; + LL_WARNS() << "Clamping to 0.0" << LL_ENDL; + temp_f32 = 0.f; + ok = false; + } + params->setBegin(temp_f32); + + dp.unpackU16(temp_u16, "End"); + temp_f32 = temp_u16 * CUT_QUANTA; + if (temp_f32 > 1.f) + { + LL_WARNS() << "Profile end out of range: " << 1.f - temp_f32 << LL_ENDL; + LL_WARNS() << "Clamping to 1.0" << LL_ENDL; + temp_f32 = 1.f; + ok = false; + } + params->setEnd(1.f - temp_f32); + + dp.unpackU16(temp_u16, "Hollow"); + temp_f32 = temp_u16 * HOLLOW_QUANTA; + if (temp_f32 > 1.f) + { + LL_WARNS() << "Profile hollow out of range: " << temp_f32 << LL_ENDL; + LL_WARNS() << "Clamping to 0.0" << LL_ENDL; + temp_f32 = 0.f; + ok = false; + } + params->setHollow(temp_f32); + + return ok; +} + +//============================================================================ + +// Quantization: +// For cut begin, range is 0 to 1, quanta is 0.005, 0 maps to 0 +// For cut end, range is 0 to 1, quanta is 0.005, 1 maps to 0 +// For scale, range is 0 to 1, quanta is 0.01, 0 maps to 0, 1 maps to 100 +// For shear, range is -0.5 to 0.5, quanta is 0.01, 0 maps to 0 +// For taper, range is -1 to 1, quanta is 0.01, 0 maps to 0 +bool LLVolumeMessage::packPathParams( + const LLPathParams* params, + LLMessageSystem *mesgsys) +{ + // Default to cylinder with no cut, top same size as bottom, no shear, no twist + static LLPathParams defaultparams(LL_PCODE_PATH_LINE, U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), 0); + if (!params) + params = &defaultparams; + + U8 curve = params->getCurveType(); + mesgsys->addU8Fast(_PREHASH_PathCurve, curve); + + U16 begin = (U16) ll_round(params->getBegin() / CUT_QUANTA); + mesgsys->addU16Fast(_PREHASH_PathBegin, begin); + + U16 end = 50000 - (U16) ll_round(params->getEnd() / CUT_QUANTA); + mesgsys->addU16Fast(_PREHASH_PathEnd, end); + + // Avoid truncation problem with direct F32->U8 cast. + // (e.g., (U8) (0.50 / 0.01) = (U8) 49.9999999 = 49 not 50. + + U8 pack_scale_x = 200 - (U8) ll_round(params->getScaleX() / SCALE_QUANTA); + mesgsys->addU8Fast(_PREHASH_PathScaleX, pack_scale_x ); + + U8 pack_scale_y = 200 - (U8) ll_round(params->getScaleY() / SCALE_QUANTA); + mesgsys->addU8Fast(_PREHASH_PathScaleY, pack_scale_y ); + + U8 pack_shear_x = (U8) ll_round(params->getShearX() / SHEAR_QUANTA); + mesgsys->addU8Fast(_PREHASH_PathShearX, pack_shear_x ); + + U8 pack_shear_y = (U8) ll_round(params->getShearY() / SHEAR_QUANTA); + mesgsys->addU8Fast(_PREHASH_PathShearY, pack_shear_y ); + + S8 twist = (S8) ll_round(params->getTwist() / SCALE_QUANTA); + mesgsys->addS8Fast(_PREHASH_PathTwist, twist); + + S8 twist_begin = (S8) ll_round(params->getTwistBegin() / SCALE_QUANTA); + mesgsys->addS8Fast(_PREHASH_PathTwistBegin, twist_begin); + + S8 radius_offset = (S8) ll_round(params->getRadiusOffset() / SCALE_QUANTA); + mesgsys->addS8Fast(_PREHASH_PathRadiusOffset, radius_offset); + + S8 taper_x = (S8) ll_round(params->getTaperX() / TAPER_QUANTA); + mesgsys->addS8Fast(_PREHASH_PathTaperX, taper_x); + + S8 taper_y = (S8) ll_round(params->getTaperY() / TAPER_QUANTA); + mesgsys->addS8Fast(_PREHASH_PathTaperY, taper_y); + + U8 revolutions = (U8) ll_round( (params->getRevolutions() - 1.0f) / REV_QUANTA); + mesgsys->addU8Fast(_PREHASH_PathRevolutions, revolutions); + + S8 skew = (S8) ll_round(params->getSkew() / SCALE_QUANTA); + mesgsys->addS8Fast(_PREHASH_PathSkew, skew); + + return true; +} + +bool LLVolumeMessage::packPathParams( + const LLPathParams* params, + LLDataPacker &dp) +{ + // Default to cylinder with no cut, top same size as bottom, no shear, no twist + static LLPathParams defaultparams(LL_PCODE_PATH_LINE, U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), 0); + if (!params) + params = &defaultparams; + + U8 curve = params->getCurveType(); + dp.packU8(curve, "Curve"); + + U16 begin = (U16) ll_round(params->getBegin() / CUT_QUANTA); + dp.packU16(begin, "Begin"); + + U16 end = 50000 - (U16) ll_round(params->getEnd() / CUT_QUANTA); + dp.packU16(end, "End"); + + // Avoid truncation problem with direct F32->U8 cast. + // (e.g., (U8) (0.50 / 0.01) = (U8) 49.9999999 = 49 not 50. + + U8 pack_scale_x = 200 - (U8) ll_round(params->getScaleX() / SCALE_QUANTA); + dp.packU8(pack_scale_x, "ScaleX"); + + U8 pack_scale_y = 200 - (U8) ll_round(params->getScaleY() / SCALE_QUANTA); + dp.packU8(pack_scale_y, "ScaleY"); + + S8 pack_shear_x = (S8) ll_round(params->getShearX() / SHEAR_QUANTA); + dp.packU8(*(U8 *)&pack_shear_x, "ShearX"); + + S8 pack_shear_y = (S8) ll_round(params->getShearY() / SHEAR_QUANTA); + dp.packU8(*(U8 *)&pack_shear_y, "ShearY"); + + S8 twist = (S8) ll_round(params->getTwist() / SCALE_QUANTA); + dp.packU8(*(U8 *)&twist, "Twist"); + + S8 twist_begin = (S8) ll_round(params->getTwistBegin() / SCALE_QUANTA); + dp.packU8(*(U8 *)&twist_begin, "TwistBegin"); + + S8 radius_offset = (S8) ll_round(params->getRadiusOffset() / SCALE_QUANTA); + dp.packU8(*(U8 *)&radius_offset, "RadiusOffset"); + + S8 taper_x = (S8) ll_round(params->getTaperX() / TAPER_QUANTA); + dp.packU8(*(U8 *)&taper_x, "TaperX"); + + S8 taper_y = (S8) ll_round(params->getTaperY() / TAPER_QUANTA); + dp.packU8(*(U8 *)&taper_y, "TaperY"); + + U8 revolutions = (U8) ll_round( (params->getRevolutions() - 1.0f) / REV_QUANTA); + dp.packU8(*(U8 *)&revolutions, "Revolutions"); + + S8 skew = (S8) ll_round(params->getSkew() / SCALE_QUANTA); + dp.packU8(*(U8 *)&skew, "Skew"); + + return true; +} + +bool LLVolumeMessage::unpackPathParams( + LLPathParams* params, + LLMessageSystem* mesgsys, + char const* block_name, + S32 block_num) +{ + U8 curve; + mesgsys->getU8Fast(block_name, _PREHASH_PathCurve, curve, block_num); + params->setCurveType(curve); + + U16 begin; + mesgsys->getU16Fast(block_name, _PREHASH_PathBegin, begin, block_num); + params->setBegin((F32)(begin * CUT_QUANTA)); + + U16 end; + mesgsys->getU16Fast(block_name, _PREHASH_PathEnd, end, block_num); + params->setEnd((F32)((50000 - end) * CUT_QUANTA)); + + U8 pack_scale_x, pack_scale_y; + mesgsys->getU8Fast(block_name, _PREHASH_PathScaleX, pack_scale_x, block_num); + mesgsys->getU8Fast(block_name, _PREHASH_PathScaleY, pack_scale_y, block_num); + F32 x = (F32) (200 - pack_scale_x) * SCALE_QUANTA; + F32 y = (F32) (200 - pack_scale_y) * SCALE_QUANTA; + params->setScale( x, y ); + + S8 shear_x_quant, shear_y_quant; + mesgsys->getS8Fast(block_name, _PREHASH_PathShearX, shear_x_quant, block_num); + mesgsys->getS8Fast(block_name, _PREHASH_PathShearY, shear_y_quant, block_num); + F32 shear_x = (F32) shear_x_quant * SHEAR_QUANTA; + F32 shear_y = (F32) shear_y_quant * SHEAR_QUANTA; + params->setShear( shear_x, shear_y ); + + S8 twist; + mesgsys->getS8Fast(block_name, _PREHASH_PathTwist, twist, block_num ); + params->setTwist((F32)(twist * SCALE_QUANTA)); + + S8 twist_begin; + mesgsys->getS8Fast(block_name, _PREHASH_PathTwistBegin, twist_begin, block_num ); + params->setTwistBegin((F32)(twist_begin * SCALE_QUANTA)); + + S8 radius_offset; + mesgsys->getS8Fast(block_name, _PREHASH_PathRadiusOffset, radius_offset, block_num ); + params->setRadiusOffset((F32)(radius_offset * SCALE_QUANTA)); + + S8 taper_x_quant, taper_y_quant; + mesgsys->getS8Fast(block_name, _PREHASH_PathTaperX, taper_x_quant, block_num ); + mesgsys->getS8Fast(block_name, _PREHASH_PathTaperY, taper_y_quant, block_num ); + F32 taper_x = (F32)(taper_x_quant * TAPER_QUANTA); + F32 taper_y = (F32)(taper_y_quant * TAPER_QUANTA); + params->setTaper( taper_x, taper_y ); + + U8 revolutions; + mesgsys->getU8Fast(block_name, _PREHASH_PathRevolutions, revolutions, block_num ); + params->setRevolutions((F32)(revolutions * REV_QUANTA + 1.0f)); + + S8 skew; + mesgsys->getS8Fast(block_name, _PREHASH_PathSkew, skew, block_num ); + params->setSkew((F32)(skew * SCALE_QUANTA)); + +/* + LL_INFOS() << "Unpacking Path Block " << block_num << LL_ENDL; + LL_INFOS() << "Curve: " << (U32)params->getCurve() << LL_ENDL; + LL_INFOS() << "Begin: " << params->getBegin() << LL_ENDL; + LL_INFOS() << "End: " << params->getEnd() << LL_ENDL; + LL_INFOS() << "Scale: " << params->getScale() << LL_ENDL; + LL_INFOS() << "Twist: " << params->getTwist() << LL_ENDL; +*/ + + return true; + +} + +bool LLVolumeMessage::unpackPathParams(LLPathParams* params, LLDataPacker &dp) +{ + U8 value; + S8 svalue; + U16 temp_u16; + + dp.unpackU8(value, "Curve"); + params->setCurveType( value ); + + dp.unpackU16(temp_u16, "Begin"); + params->setBegin((F32)(temp_u16 * CUT_QUANTA)); + + dp.unpackU16(temp_u16, "End"); + params->setEnd((F32)((50000 - temp_u16) * CUT_QUANTA)); + + dp.unpackU8(value, "ScaleX"); + F32 x = (F32) (200 - value) * SCALE_QUANTA; + dp.unpackU8(value, "ScaleY"); + F32 y = (F32) (200 - value) * SCALE_QUANTA; + params->setScale( x, y ); + + dp.unpackU8(value, "ShearX"); + svalue = *(S8 *)&value; + F32 shear_x = (F32) svalue * SHEAR_QUANTA; + dp.unpackU8(value, "ShearY"); + svalue = *(S8 *)&value; + F32 shear_y = (F32) svalue * SHEAR_QUANTA; + params->setShear( shear_x, shear_y ); + + dp.unpackU8(value, "Twist"); + svalue = *(S8 *)&value; + params->setTwist((F32)(svalue * SCALE_QUANTA)); + + dp.unpackU8(value, "TwistBegin"); + svalue = *(S8 *)&value; + params->setTwistBegin((F32)(svalue * SCALE_QUANTA)); + + dp.unpackU8(value, "RadiusOffset"); + svalue = *(S8 *)&value; + params->setRadiusOffset((F32)(svalue * SCALE_QUANTA)); + + dp.unpackU8(value, "TaperX"); + svalue = *(S8 *)&value; + params->setTaperX((F32)(svalue * TAPER_QUANTA)); + + dp.unpackU8(value, "TaperY"); + svalue = *(S8 *)&value; + params->setTaperY((F32)(svalue * TAPER_QUANTA)); + + dp.unpackU8(value, "Revolutions"); + params->setRevolutions((F32)(value * REV_QUANTA + 1.0f)); + + dp.unpackU8(value, "Skew"); + svalue = *(S8 *)&value; + params->setSkew((F32)(svalue * SCALE_QUANTA)); + + return true; +} + +//============================================================================ + +// static +bool LLVolumeMessage::constrainVolumeParams(LLVolumeParams& params) +{ + U32 bad = 0; + + // This is called immediately after an unpack. feed the raw data + // through the checked setters to constraint it to a valid set of + // volume params. + bad |= params.setType(params.getProfileParams().getCurveType(), + params.getPathParams().getCurveType()) ? 0 : 1; + bad |= params.setBeginAndEndS(params.getProfileParams().getBegin(), + params.getProfileParams().getEnd()) ? 0 : 2; + bad |= params.setBeginAndEndT(params.getPathParams().getBegin(), + params.getPathParams().getEnd()) ? 0 : 4; + bad |= params.setHollow(params.getProfileParams().getHollow()) ? 0 : 8; + bad |= params.setTwistBegin(params.getPathParams().getTwistBegin()) ? 0 : 0x10; + bad |= params.setTwistEnd(params.getPathParams().getTwistEnd()) ? 0 : 0x20; + bad |= params.setRatio(params.getPathParams().getScaleX(), + params.getPathParams().getScaleY()) ? 0 : 0x40; + bad |= params.setShear(params.getPathParams().getShearX(), + params.getPathParams().getShearY()) ? 0 : 0x80; + bad |= params.setTaper(params.getPathParams().getTaperX(), + params.getPathParams().getTaperY()) ? 0 : 0x100; + bad |= params.setRevolutions(params.getPathParams().getRevolutions()) ? 0 : 0x200; + bad |= params.setRadiusOffset(params.getPathParams().getRadiusOffset()) ? 0 : 0x400; + bad |= params.setSkew(params.getPathParams().getSkew()) ? 0 : 0x800; + + if (bad) + { + LL_WARNS() << "LLVolumeMessage::constrainVolumeParams() - " + << "forced to constrain incoming volume params: " + << llformat("0x%04x", bad) << LL_ENDL; + } + + return bad == 0; +} + +bool LLVolumeMessage::packVolumeParams(const LLVolumeParams* params, LLMessageSystem *mesgsys) +{ + // LL_INFOS() << "pack volume" << LL_ENDL; + if (params) + { + packPathParams(¶ms->getPathParams(), mesgsys); + packProfileParams(¶ms->getProfileParams(), mesgsys); + } + else + { + packPathParams(0, mesgsys); + packProfileParams(0, mesgsys); + } + return true; +} + +bool LLVolumeMessage::packVolumeParams(const LLVolumeParams* params, LLDataPacker &dp) +{ + // LL_INFOS() << "pack volume" << LL_ENDL; + if (params) + { + packPathParams(¶ms->getPathParams(), dp); + packProfileParams(¶ms->getProfileParams(), dp); + } + else + { + packPathParams(0, dp); + packProfileParams(0, dp); + } + return true; +} + +bool LLVolumeMessage::unpackVolumeParams( + LLVolumeParams* params, + LLMessageSystem* mesgsys, + char const* block_name, + S32 block_num) +{ + bool ok = true; + ok &= unpackPathParams( + ¶ms->getPathParams(), + mesgsys, + block_name, + block_num); + ok &= unpackProfileParams( + ¶ms->getProfileParams(), + mesgsys, + block_name, + block_num); + ok &= constrainVolumeParams(*params); + + return ok; +} + +bool LLVolumeMessage::unpackVolumeParams( + LLVolumeParams* params, + LLDataPacker &dp) +{ + bool ok = true; + ok &= unpackPathParams(¶ms->getPathParams(), dp); + ok &= unpackProfileParams(¶ms->getProfileParams(), dp); + ok &= constrainVolumeParams(*params); + return ok; +} + +//============================================================================ diff --git a/indra/llprimitive/tests/llprimitive_test.cpp b/indra/llprimitive/tests/llprimitive_test.cpp index cee30cff14..60960067d0 100644 --- a/indra/llprimitive/tests/llprimitive_test.cpp +++ b/indra/llprimitive/tests/llprimitive_test.cpp @@ -1,271 +1,271 @@ -/**
- * @file llprimitive_test.cpp
- * @brief llprimitive tests
- *
- * $LicenseInfo:firstyear=2001&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 "linden_common.h"
-
-#include "../test/lltut.h"
-
-#include "../llprimitive.h"
-
-#include "../../llmath/llvolumemgr.h"
-
-class DummyVolumeMgr : public LLVolumeMgr
-{
-public:
- DummyVolumeMgr() : LLVolumeMgr(), mVolumeTest(NULL), mCurrDetailTest(0) {}
- ~DummyVolumeMgr()
- {
- }
-
-
- virtual LLVolume *refVolume(const LLVolumeParams &volume_params, const S32 detail)
- {
- if (mVolumeTest.isNull() || volume_params != mCurrParamsTest || detail != mCurrDetailTest)
- {
- F32 volume_detail = LLVolumeLODGroup::getVolumeScaleFromDetail(detail);
- mVolumeTest = new LLVolume(volume_params, volume_detail, false, false);
- mCurrParamsTest = volume_params;
- mCurrDetailTest = detail;
- return mVolumeTest;
- }
- else
- {
- return mVolumeTest;
- }
- }
-
- virtual void unrefVolume(LLVolume *volumep)
- {
- if (mVolumeTest == volumep)
- {
- mVolumeTest = NULL;
- }
- }
-
-private:
- LLPointer<LLVolume> mVolumeTest;
- LLVolumeParams mCurrParamsTest;
- S32 mCurrDetailTest;
-};
-
-LLMaterialID::LLMaterialID() {}
-LLMaterialID::LLMaterialID(LLMaterialID const &m) = default;
-LLMaterialID::~LLMaterialID() {}
-void LLMaterialID::set(void const*) { }
-U8 const * LLMaterialID::get() const { return mID; }
-
-LLPrimTextureList::LLPrimTextureList() { }
-LLPrimTextureList::~LLPrimTextureList() { }
-S32 LLPrimTextureList::setBumpMap(const U8 index, const U8 bump) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setOffsetS(const U8 index, const F32 s) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setOffsetT(const U8 index, const F32 t) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry &te) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setRotation(const U8 index, const F32 r) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setBumpShiny(const U8 index, const U8 bump_shiny) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setFullbright(const U8 index, const U8 t) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setMaterialID(const U8 index, const LLMaterialID& pMaterialID) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setMediaFlags(const U8 index, const U8 media_flags) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setMediaTexGen(const U8 index, const U8 media) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setBumpShinyFullbright(const U8 index, const U8 bump) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setID(const U8 index, const LLUUID& id) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setGlow(const U8 index, const F32 glow) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setAlpha(const U8 index, const F32 alpha) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setColor(const U8 index, const LLColor3& color) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setColor(const U8 index, const LLColor4& color) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setScale(const U8 index, const F32 s, const F32 t) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setScaleS(const U8 index, const F32 s) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setScaleT(const U8 index, const F32 t) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setShiny(const U8 index, const U8 shiny) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setOffset(const U8 index, const F32 s, const F32 t) { return TEM_CHANGE_NONE; }
-S32 LLPrimTextureList::setTexGen(const U8 index, const U8 texgen) { return TEM_CHANGE_NONE; }
-
-LLMaterialPtr LLPrimTextureList::getMaterialParams(const U8 index) { return LLMaterialPtr(); }
-void LLPrimTextureList::copy(LLPrimTextureList const & ptl) { mEntryList = ptl.mEntryList; } // do we need to call getTexture()->newCopy()?
-void LLPrimTextureList::take(LLPrimTextureList &other_list) { }
-void LLPrimTextureList::setSize(S32 new_size) { mEntryList.resize(new_size); }
-void LLPrimTextureList::setAllIDs(const LLUUID &id) { }
-LLTextureEntry * LLPrimTextureList::getTexture(const U8 index) const { return nullptr; }
-S32 LLPrimTextureList::size() const { return mEntryList.size(); }
-
-class PRIMITIVE_TEST_SETUP
-{
-public:
- PRIMITIVE_TEST_SETUP()
- {
- volume_manager_test = new DummyVolumeMgr();
- LLPrimitive::setVolumeManager(volume_manager_test);
- }
-
- ~PRIMITIVE_TEST_SETUP()
- {
- LLPrimitive::cleanupVolumeManager();
- }
- DummyVolumeMgr * volume_manager_test;
-};
-
-namespace tut
-{
- struct llprimitive
- {
- PRIMITIVE_TEST_SETUP setup_class;
- };
-
- typedef test_group<llprimitive> llprimitive_t;
- typedef llprimitive_t::object llprimitive_object_t;
- tut::llprimitive_t tut_llprimitive("LLPrimitive");
-
- template<> template<>
- void llprimitive_object_t::test<1>()
- {
- set_test_name("Test LLPrimitive Instantiation");
- LLPrimitive test;
- }
-
- template<> template<>
- void llprimitive_object_t::test<2>()
- {
- set_test_name("Test LLPrimitive PCode setter and getter.");
- LLPrimitive test;
- ensure_equals(test.getPCode(), 0);
- LLPCode code = 1;
- test.setPCode(code);
- ensure_equals(test.getPCode(), code);
- }
-
- template<> template<>
- void llprimitive_object_t::test<3>()
- {
- set_test_name("Test llprimitive constructor and initer.");
- LLPCode code = 1;
- LLPrimitive primitive;
- primitive.init_primitive(code);
- ensure_equals(primitive.getPCode(), code);
- }
-
- template<> template<>
- void llprimitive_object_t::test<4>()
- {
- set_test_name("Test Static llprimitive constructor and initer.");
- LLPCode code = 1;
- LLPrimitive * primitive = LLPrimitive::createPrimitive(code);
- ensure(primitive != NULL);
- ensure_equals(primitive->getPCode(), code);
- }
-
- template<> template<>
- void llprimitive_object_t::test<5>()
- {
- set_test_name("Test setVolume creation of new unique volume.");
- LLPrimitive primitive;
- LLVolumeParams params;
-
- // Make sure volume starts off null
- ensure(primitive.getVolume() == NULL);
-
- // Make sure we have no texture entries before setting the volume
- ensure_equals(primitive.getNumTEs(), 0);
-
- // Test that GEOMETRY has not been flagged as changed.
- ensure(!primitive.isChanged(LLXform::GEOMETRY));
-
- // Make sure setVolume returns true
- ensure(primitive.setVolume(params, 0, true) == true);
- LLVolume* new_volume = primitive.getVolume();
-
- // make sure new volume was actually created
- ensure(new_volume != NULL);
-
- // Make sure that now that we've set the volume we have texture entries
- ensure_not_equals(primitive.getNumTEs(), 0);
-
- // Make sure that the number of texture entries equals the number of faces in the volume (should be 6)
- ensure_equals(new_volume->getNumFaces(), 6);
- ensure_equals(primitive.getNumTEs(), new_volume->getNumFaces());
-
- // Test that GEOMETRY has been flagged as changed.
- ensure(primitive.isChanged(LLXform::GEOMETRY));
-
- // Run it twice to make sure it doesn't create a different one if params are the same
- ensure(primitive.setVolume(params, 0, true) == false);
- ensure(new_volume == primitive.getVolume());
-
- // Change the param definition and try setting it again.
- params.setRevolutions(4);
- ensure(primitive.setVolume(params, 0, true) == true);
-
- // Ensure that we now have a different volume
- ensure(new_volume != primitive.getVolume());
- }
-
- template<> template<>
- void llprimitive_object_t::test<6>()
- {
- set_test_name("Test setVolume creation of new NOT-unique volume.");
- LLPrimitive primitive;
- LLVolumeParams params;
-
- // Make sure volume starts off null
- ensure(primitive.getVolume() == NULL);
-
- // Make sure we have no texture entries before setting the volume
- ensure_equals(primitive.getNumTEs(), 0);
-
- // Test that GEOMETRY has not been flagged as changed.
- ensure(!primitive.isChanged(LLXform::GEOMETRY));
-
- // Make sure setVolume returns true
- ensure(primitive.setVolume(params, 0, false) == true);
-
- LLVolume* new_volume = primitive.getVolume();
-
- // make sure new volume was actually created
- ensure(new_volume != NULL);
-
- // Make sure that now that we've set the volume we have texture entries
- ensure_not_equals(primitive.getNumTEs(), 0);
-
- // Make sure that the number of texture entries equals the number of faces in the volume (should be 6)
- ensure_equals(new_volume->getNumFaces(), 6);
- ensure_equals(primitive.getNumTEs(), new_volume->getNumFaces());
-
- // Test that GEOMETRY has been flagged as changed.
- ensure(primitive.isChanged(LLXform::GEOMETRY));
-
- // Run it twice to make sure it doesn't create a different one if params are the same
- ensure(primitive.setVolume(params, 0, false) == false);
- ensure(new_volume == primitive.getVolume());
-
- // Change the param definition and try setting it again.
- params.setRevolutions(4);
- ensure(primitive.setVolume(params, 0, false) == true);
-
- // Ensure that we now have a different volume
- ensure(new_volume != primitive.getVolume());
- }
-}
-
-#include "llmessagesystem_stub.cpp"
+/** + * @file llprimitive_test.cpp + * @brief llprimitive tests + * + * $LicenseInfo:firstyear=2001&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 "linden_common.h" + +#include "../test/lltut.h" + +#include "../llprimitive.h" + +#include "../../llmath/llvolumemgr.h" + +class DummyVolumeMgr : public LLVolumeMgr +{ +public: + DummyVolumeMgr() : LLVolumeMgr(), mVolumeTest(NULL), mCurrDetailTest(0) {} + ~DummyVolumeMgr() + { + } + + + virtual LLVolume *refVolume(const LLVolumeParams &volume_params, const S32 detail) + { + if (mVolumeTest.isNull() || volume_params != mCurrParamsTest || detail != mCurrDetailTest) + { + F32 volume_detail = LLVolumeLODGroup::getVolumeScaleFromDetail(detail); + mVolumeTest = new LLVolume(volume_params, volume_detail, false, false); + mCurrParamsTest = volume_params; + mCurrDetailTest = detail; + return mVolumeTest; + } + else + { + return mVolumeTest; + } + } + + virtual void unrefVolume(LLVolume *volumep) + { + if (mVolumeTest == volumep) + { + mVolumeTest = NULL; + } + } + +private: + LLPointer<LLVolume> mVolumeTest; + LLVolumeParams mCurrParamsTest; + S32 mCurrDetailTest; +}; + +LLMaterialID::LLMaterialID() {} +LLMaterialID::LLMaterialID(LLMaterialID const &m) = default; +LLMaterialID::~LLMaterialID() {} +void LLMaterialID::set(void const*) { } +U8 const * LLMaterialID::get() const { return mID; } + +LLPrimTextureList::LLPrimTextureList() { } +LLPrimTextureList::~LLPrimTextureList() { } +S32 LLPrimTextureList::setBumpMap(const U8 index, const U8 bump) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setOffsetS(const U8 index, const F32 s) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setOffsetT(const U8 index, const F32 t) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry &te) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setRotation(const U8 index, const F32 r) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setBumpShiny(const U8 index, const U8 bump_shiny) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setFullbright(const U8 index, const U8 t) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setMaterialID(const U8 index, const LLMaterialID& pMaterialID) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setMediaFlags(const U8 index, const U8 media_flags) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setMediaTexGen(const U8 index, const U8 media) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setMaterialParams(const U8 index, const LLMaterialPtr pMaterialParams) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setBumpShinyFullbright(const U8 index, const U8 bump) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setID(const U8 index, const LLUUID& id) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setGlow(const U8 index, const F32 glow) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setAlpha(const U8 index, const F32 alpha) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setColor(const U8 index, const LLColor3& color) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setColor(const U8 index, const LLColor4& color) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setScale(const U8 index, const F32 s, const F32 t) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setScaleS(const U8 index, const F32 s) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setScaleT(const U8 index, const F32 t) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setShiny(const U8 index, const U8 shiny) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setOffset(const U8 index, const F32 s, const F32 t) { return TEM_CHANGE_NONE; } +S32 LLPrimTextureList::setTexGen(const U8 index, const U8 texgen) { return TEM_CHANGE_NONE; } + +LLMaterialPtr LLPrimTextureList::getMaterialParams(const U8 index) { return LLMaterialPtr(); } +void LLPrimTextureList::copy(LLPrimTextureList const & ptl) { mEntryList = ptl.mEntryList; } // do we need to call getTexture()->newCopy()? +void LLPrimTextureList::take(LLPrimTextureList &other_list) { } +void LLPrimTextureList::setSize(S32 new_size) { mEntryList.resize(new_size); } +void LLPrimTextureList::setAllIDs(const LLUUID &id) { } +LLTextureEntry * LLPrimTextureList::getTexture(const U8 index) const { return nullptr; } +S32 LLPrimTextureList::size() const { return mEntryList.size(); } + +class PRIMITIVE_TEST_SETUP +{ +public: + PRIMITIVE_TEST_SETUP() + { + volume_manager_test = new DummyVolumeMgr(); + LLPrimitive::setVolumeManager(volume_manager_test); + } + + ~PRIMITIVE_TEST_SETUP() + { + LLPrimitive::cleanupVolumeManager(); + } + DummyVolumeMgr * volume_manager_test; +}; + +namespace tut +{ + struct llprimitive + { + PRIMITIVE_TEST_SETUP setup_class; + }; + + typedef test_group<llprimitive> llprimitive_t; + typedef llprimitive_t::object llprimitive_object_t; + tut::llprimitive_t tut_llprimitive("LLPrimitive"); + + template<> template<> + void llprimitive_object_t::test<1>() + { + set_test_name("Test LLPrimitive Instantiation"); + LLPrimitive test; + } + + template<> template<> + void llprimitive_object_t::test<2>() + { + set_test_name("Test LLPrimitive PCode setter and getter."); + LLPrimitive test; + ensure_equals(test.getPCode(), 0); + LLPCode code = 1; + test.setPCode(code); + ensure_equals(test.getPCode(), code); + } + + template<> template<> + void llprimitive_object_t::test<3>() + { + set_test_name("Test llprimitive constructor and initer."); + LLPCode code = 1; + LLPrimitive primitive; + primitive.init_primitive(code); + ensure_equals(primitive.getPCode(), code); + } + + template<> template<> + void llprimitive_object_t::test<4>() + { + set_test_name("Test Static llprimitive constructor and initer."); + LLPCode code = 1; + LLPrimitive * primitive = LLPrimitive::createPrimitive(code); + ensure(primitive != NULL); + ensure_equals(primitive->getPCode(), code); + } + + template<> template<> + void llprimitive_object_t::test<5>() + { + set_test_name("Test setVolume creation of new unique volume."); + LLPrimitive primitive; + LLVolumeParams params; + + // Make sure volume starts off null + ensure(primitive.getVolume() == NULL); + + // Make sure we have no texture entries before setting the volume + ensure_equals(primitive.getNumTEs(), 0); + + // Test that GEOMETRY has not been flagged as changed. + ensure(!primitive.isChanged(LLXform::GEOMETRY)); + + // Make sure setVolume returns true + ensure(primitive.setVolume(params, 0, true) == true); + LLVolume* new_volume = primitive.getVolume(); + + // make sure new volume was actually created + ensure(new_volume != NULL); + + // Make sure that now that we've set the volume we have texture entries + ensure_not_equals(primitive.getNumTEs(), 0); + + // Make sure that the number of texture entries equals the number of faces in the volume (should be 6) + ensure_equals(new_volume->getNumFaces(), 6); + ensure_equals(primitive.getNumTEs(), new_volume->getNumFaces()); + + // Test that GEOMETRY has been flagged as changed. + ensure(primitive.isChanged(LLXform::GEOMETRY)); + + // Run it twice to make sure it doesn't create a different one if params are the same + ensure(primitive.setVolume(params, 0, true) == false); + ensure(new_volume == primitive.getVolume()); + + // Change the param definition and try setting it again. + params.setRevolutions(4); + ensure(primitive.setVolume(params, 0, true) == true); + + // Ensure that we now have a different volume + ensure(new_volume != primitive.getVolume()); + } + + template<> template<> + void llprimitive_object_t::test<6>() + { + set_test_name("Test setVolume creation of new NOT-unique volume."); + LLPrimitive primitive; + LLVolumeParams params; + + // Make sure volume starts off null + ensure(primitive.getVolume() == NULL); + + // Make sure we have no texture entries before setting the volume + ensure_equals(primitive.getNumTEs(), 0); + + // Test that GEOMETRY has not been flagged as changed. + ensure(!primitive.isChanged(LLXform::GEOMETRY)); + + // Make sure setVolume returns true + ensure(primitive.setVolume(params, 0, false) == true); + + LLVolume* new_volume = primitive.getVolume(); + + // make sure new volume was actually created + ensure(new_volume != NULL); + + // Make sure that now that we've set the volume we have texture entries + ensure_not_equals(primitive.getNumTEs(), 0); + + // Make sure that the number of texture entries equals the number of faces in the volume (should be 6) + ensure_equals(new_volume->getNumFaces(), 6); + ensure_equals(primitive.getNumTEs(), new_volume->getNumFaces()); + + // Test that GEOMETRY has been flagged as changed. + ensure(primitive.isChanged(LLXform::GEOMETRY)); + + // Run it twice to make sure it doesn't create a different one if params are the same + ensure(primitive.setVolume(params, 0, false) == false); + ensure(new_volume == primitive.getVolume()); + + // Change the param definition and try setting it again. + params.setRevolutions(4); + ensure(primitive.setVolume(params, 0, false) == true); + + // Ensure that we now have a different volume + ensure(new_volume != primitive.getVolume()); + } +} + +#include "llmessagesystem_stub.cpp" |