summaryrefslogtreecommitdiff
path: root/indra/llmath
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llmath')
-rw-r--r--indra/llmath/CMakeLists.txt3
-rw-r--r--indra/llmath/lloctree.h153
-rw-r--r--indra/llmath/llvolume.cpp624
-rw-r--r--indra/llmath/llvolume.h33
-rw-r--r--indra/llmath/llvolumeoctree.cpp18
-rw-r--r--indra/llmath/llvolumeoctree.h20
-rw-r--r--indra/llmath/v3color.h51
-rw-r--r--indra/llmath/v4color.h41
8 files changed, 553 insertions, 390 deletions
diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt
index 552e820127..4617309606 100644
--- a/indra/llmath/CMakeLists.txt
+++ b/indra/llmath/CMakeLists.txt
@@ -4,12 +4,14 @@ project(llmath)
include(00-Common)
include(LLCommon)
+include(LLMeshOptimizer)
include(bugsplat)
include(Boost)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLCOMMON_SYSTEM_INCLUDE_DIRS}
+ ${LLMESHOPTIMIZER_INCLUDE_DIRS}
)
set(llmath_SOURCE_FILES
@@ -109,6 +111,7 @@ add_library (llmath ${llmath_SOURCE_FILES})
target_link_libraries(llmath
${LLCOMMON_LIBRARIES}
+ ${LLMESHOPTIMIZER_LIBRARIES}
)
# Add tests
diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h
index a9a54a8113..318ee65cc0 100644
--- a/indra/llmath/lloctree.h
+++ b/indra/llmath/lloctree.h
@@ -48,52 +48,59 @@ extern float gOctreeMinSize;
#define LL_OCTREE_MAX_CAPACITY 128
#endif*/
-template <class T> class LLOctreeNode;
-
-template <class T>
+// T is the type of the element referenced by the octree node.
+// T_PTR determines how pointers to elements are stored internally.
+// LLOctreeNode<T, LLPointer<T>> assumes ownership of inserted elements and
+// deletes elements removed from the tree.
+// LLOctreeNode<T, T*> doesn't take ownership of inserted elements, so the API
+// user is responsible for managing the storage lifecycle of elements added to
+// the tree.
+template <class T, typename T_PTR> class LLOctreeNode;
+
+template <class T, typename T_PTR>
class LLOctreeListener: public LLTreeListener<T>
{
public:
typedef LLTreeListener<T> BaseType;
- typedef LLOctreeNode<T> oct_node;
+ typedef LLOctreeNode<T, T_PTR> oct_node;
virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0;
virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0;
};
-template <class T>
+template <class T, typename T_PTR>
class LLOctreeTraveler
{
public:
- virtual void traverse(const LLOctreeNode<T>* node);
- virtual void visit(const LLOctreeNode<T>* branch) = 0;
+ virtual void traverse(const LLOctreeNode<T, T_PTR>* node);
+ virtual void visit(const LLOctreeNode<T, T_PTR>* branch) = 0;
};
-template <class T>
-class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T>
+template <class T, typename T_PTR>
+class LLOctreeTravelerDepthFirst : public LLOctreeTraveler<T, T_PTR>
{
public:
- virtual void traverse(const LLOctreeNode<T>* node) override;
+ virtual void traverse(const LLOctreeNode<T, T_PTR>* node) override;
};
-template <class T>
+template <class T, typename T_PTR>
class alignas(16) LLOctreeNode : public LLTreeNode<T>
{
LL_ALIGN_NEW
public:
- typedef LLOctreeTraveler<T> oct_traveler;
- typedef LLTreeTraveler<T> tree_traveler;
- typedef std::vector< LLPointer<T> > element_list; // note: don't remove the whitespace between "> >"
- typedef LLPointer<T>* element_iter;
- typedef const LLPointer<T>* const_element_iter;
+ typedef LLOctreeTraveler<T, T_PTR> oct_traveler;
+ typedef LLTreeTraveler<T> tree_traveler;
+ typedef std::vector<T_PTR> element_list;
+ typedef typename element_list::iterator element_iter;
+ typedef typename element_list::const_iterator const_element_iter;
typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter;
- typedef LLOctreeNode<T>** child_list;
- typedef LLOctreeNode<T>** child_iter;
+ typedef LLOctreeNode<T, T_PTR>** child_list;
+ typedef LLOctreeNode<T, T_PTR>** child_iter;
- typedef LLTreeNode<T> BaseType;
- typedef LLOctreeNode<T> oct_node;
- typedef LLOctreeListener<T> oct_listener;
+ typedef LLTreeNode<T> BaseType;
+ typedef LLOctreeNode<T, T_PTR> oct_node;
+ typedef LLOctreeListener<T, T_PTR> oct_listener;
enum
{
@@ -108,9 +115,6 @@ public:
mOctant(octant)
{
llassert(size[0] >= gOctreeMinSize*0.5f);
- //always keep a NULL terminated list to avoid out of bounds exceptions in debug builds
- mData.push_back(NULL);
- mDataEnd = &mData[0];
mCenter = center;
mSize = size;
@@ -121,8 +125,6 @@ public:
mOctant = ((oct_node*) mParent)->getOctant(mCenter);
}
- mElementCount = 0;
-
clearChildren();
}
@@ -130,15 +132,14 @@ public:
{
BaseType::destroyListeners();
- for (U32 i = 0; i < mElementCount; ++i)
+ const U32 element_count = getElementCount();
+ for (U32 i = 0; i < element_count; ++i)
{
mData[i]->setBinIndex(-1);
mData[i] = NULL;
}
mData.clear();
- mData.push_back(NULL);
- mDataEnd = &mData[0];
for (U32 i = 0; i < getChildCount(); i++)
{
@@ -238,14 +239,12 @@ public:
void accept(oct_traveler* visitor) { visitor->visit(this); }
virtual bool isLeaf() const { return mChildCount == 0; }
- U32 getElementCount() const { return mElementCount; }
- bool isEmpty() const { return mElementCount == 0; }
- element_list& getData() { return mData; }
- const element_list& getData() const { return mData; }
- element_iter getDataBegin() { return &mData[0]; }
- element_iter getDataEnd() { return mDataEnd; }
- const_element_iter getDataBegin() const { return &mData[0]; }
- const_element_iter getDataEnd() const { return mDataEnd; }
+ U32 getElementCount() const { return (U32)mData.size(); }
+ bool isEmpty() const { return mData.empty(); }
+ element_iter getDataBegin() { return mData.begin(); }
+ element_iter getDataEnd() { return mData.end(); }
+ const_element_iter getDataBegin() const { return mData.cbegin(); }
+ const_element_iter getDataEnd() const { return mData.cend(); }
U32 getChildCount() const { return mChildCount; }
oct_node* getChild(U32 index) { return mChild[index]; }
@@ -263,7 +262,7 @@ public:
U8 idx = mChildMap[i];
if (idx != NO_CHILD_NODES)
{
- LLOctreeNode<T>* child = mChild[idx];
+ oct_node* child = mChild[idx];
if (child->getOctant() != i)
{
@@ -281,7 +280,7 @@ public:
oct_node* getNodeAt(const LLVector4a& pos, const F32& rad)
{
- LLOctreeNode<T>* node = this;
+ oct_node* node = this;
if (node->isInside(pos, rad))
{
@@ -303,7 +302,7 @@ public:
}
else if (!node->contains(rad) && node->getParent())
{ //if we got here, data does not exist in this node
- return ((LLOctreeNode<T>*) node->getParent())->getNodeAt(pos, rad);
+ return ((oct_node*) node->getParent())->getNodeAt(pos, rad);
}
return node;
@@ -318,7 +317,7 @@ public:
OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << LL_ENDL;
return false;
}
- LLOctreeNode<T>* parent = getOctParent();
+ oct_node* parent = getOctParent();
//is it here?
if (isInside(data->getPositionGroup()))
@@ -326,11 +325,8 @@ public:
if ((((getElementCount() < gOctreeMaxCapacity || getSize()[0] <= gOctreeMinSize) && contains(data->getBinRadius())) ||
(data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity)))
{ //it belongs here
- mData.push_back(NULL);
- mData[mElementCount] = data;
- mElementCount++;
- mDataEnd = &mData[mElementCount];
- data->setBinIndex(mElementCount-1);
+ mData.push_back(data);
+ data->setBinIndex(getElementCount() - 1);
BaseType::insert(data);
return true;
}
@@ -354,7 +350,7 @@ public:
size.mul(0.5f);
//push center in direction of data
- LLOctreeNode<T>::pushCenter(center, size, data);
+ oct_node::pushCenter(center, size, data);
// handle case where floating point number gets too small
LLVector4a val;
@@ -366,11 +362,8 @@ public:
if( lt == 0x7 )
{
- mData.push_back(NULL);
- mData[mElementCount] = data;
- mElementCount++;
- mDataEnd = &mData[mElementCount];
- data->setBinIndex(mElementCount-1);
+ mData.push_back(data);
+ data->setBinIndex(getElementCount() - 1);
BaseType::insert(data);
return true;
}
@@ -396,7 +389,7 @@ public:
llassert(size[0] >= gOctreeMinSize*0.5f);
//make the new kid
- child = new LLOctreeNode<T>(center, size, this);
+ child = new oct_node(center, size, this);
addChild(child);
child->insert(data);
@@ -429,28 +422,25 @@ public:
}
void _remove(T* data, S32 i)
- { //precondition -- mElementCount > 0, idx is in range [0, mElementCount)
+ { //precondition -- getElementCount() > 0, idx is in range [0, getElementCount())
- mElementCount--;
data->setBinIndex(-1);
- if (mElementCount > 0)
+ const U32 new_element_count = getElementCount() - 1;
+ if (new_element_count > 0)
{
- if (mElementCount != i)
+ if (new_element_count != i)
{
- mData[i] = mData[mElementCount]; //might unref data, do not access data after this point
+ mData[i] = mData[new_element_count]; //might unref data, do not access data after this point
mData[i]->setBinIndex(i);
}
- mData[mElementCount] = NULL;
+ mData[new_element_count] = NULL;
mData.pop_back();
- mDataEnd = &mData[mElementCount];
}
else
{
mData.clear();
- mData.push_back(NULL);
- mDataEnd = &mData[0];
}
this->notifyRemoval(data);
@@ -463,7 +453,7 @@ public:
S32 i = data->getBinIndex();
- if (i >= 0 && i < mElementCount)
+ if (i >= 0 && i < getElementCount())
{
if (mData[i] == data)
{ //found it
@@ -506,7 +496,8 @@ public:
void removeByAddress(T* data)
{
- for (U32 i = 0; i < mElementCount; ++i)
+ const U32 element_count = getElementCount();
+ for (U32 i = 0; i < element_count; ++i)
{
if (mData[i] == data)
{ //we have data
@@ -518,7 +509,7 @@ public:
for (U32 i = 0; i < getChildCount(); i++)
{ //we don't contain data, so pass this guy down
- LLOctreeNode<T>* child = (LLOctreeNode<T>*) getChild(i);
+ oct_node* child = (oct_node*) getChild(i);
child->removeByAddress(data);
}
}
@@ -672,22 +663,20 @@ protected:
oct_node* mParent;
U8 mOctant;
- LLOctreeNode<T>* mChild[8];
+ oct_node* mChild[8];
U8 mChildMap[8];
U32 mChildCount;
element_list mData;
- element_iter mDataEnd;
- U32 mElementCount;
};
//just like a regular node, except it might expand on insert and compress on balance
-template <class T>
-class LLOctreeRoot : public LLOctreeNode<T>
+template <class T, typename T_PTR>
+class LLOctreeRoot : public LLOctreeNode<T, T_PTR>
{
public:
- typedef LLOctreeNode<T> BaseType;
- typedef LLOctreeNode<T> oct_node;
+ typedef LLOctreeNode<T, T_PTR> BaseType;
+ typedef LLOctreeNode<T, T_PTR> oct_node;
LLOctreeRoot(const LLVector4a& center,
const LLVector4a& size,
@@ -768,7 +757,7 @@ public:
oct_node* node = this->getNodeAt(data);
if (node == this)
{
- LLOctreeNode<T>::insert(data);
+ oct_node::insert(data);
}
else if (node->isInside(data->getPositionGroup()))
{
@@ -788,13 +777,13 @@ public:
LLVector4a center, size;
center = this->getCenter();
size = this->getSize();
- LLOctreeNode<T>::pushCenter(center, size, data);
+ oct_node::pushCenter(center, size, data);
this->setCenter(center);
size.mul(2.f);
this->setSize(size);
this->updateMinMax();
}
- LLOctreeNode<T>::insert(data);
+ oct_node::insert(data);
}
else
{
@@ -806,7 +795,7 @@ public:
//expand this node
LLVector4a newcenter(center);
- LLOctreeNode<T>::pushCenter(newcenter, size, data);
+ oct_node::pushCenter(newcenter, size, data);
this->setCenter(newcenter);
LLVector4a size2 = size;
size2.mul(2.f);
@@ -816,11 +805,11 @@ public:
llassert(size[0] >= gOctreeMinSize);
//copy our children to a new branch
- LLOctreeNode<T>* newnode = new LLOctreeNode<T>(center, size, this);
+ oct_node* newnode = new oct_node(center, size, this);
for (U32 i = 0; i < this->getChildCount(); i++)
{
- LLOctreeNode<T>* child = this->getChild(i);
+ oct_node* child = this->getChild(i);
newnode->addChild(child);
}
@@ -846,8 +835,8 @@ public:
//========================
// LLOctreeTraveler
//========================
-template <class T>
-void LLOctreeTraveler<T>::traverse(const LLOctreeNode<T>* node)
+template <class T, typename T_PTR>
+void LLOctreeTraveler<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
{
node->accept(this);
for (U32 i = 0; i < node->getChildCount(); i++)
@@ -856,8 +845,8 @@ void LLOctreeTraveler<T>::traverse(const LLOctreeNode<T>* node)
}
}
-template <class T>
-void LLOctreeTravelerDepthFirst<T>::traverse(const LLOctreeNode<T>* node)
+template <class T, typename T_PTR>
+void LLOctreeTravelerDepthFirst<T, T_PTR>::traverse(const LLOctreeNode<T, T_PTR>* node)
{
for (U32 i = 0; i < node->getChildCount(); i++)
{
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 5099920f32..a0f3b1463b 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -32,6 +32,7 @@
#include <stdint.h>
#endif
#include <cmath>
+#include <unordered_map>
#include "llerror.h"
@@ -49,8 +50,14 @@
#include "llsdserialize.h"
#include "llvector4a.h"
#include "llmatrix4a.h"
+#include "llmeshoptimizer.h"
#include "lltimer.h"
+#include "mikktspace/mikktspace.h"
+#include "mikktspace/mikktspace.c" // insert mikktspace implementation into llvolume object file
+
+#include "meshoptimizer/meshoptimizer.h"
+
#define DEBUG_SILHOUETTE_BINORMALS 0
#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette
@@ -370,7 +377,7 @@ BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, cons
}
}
-class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle>
+class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst<LLVolumeTriangle, LLVolumeTriangle*>
{
public:
const LLVolumeFace* mFace;
@@ -380,7 +387,7 @@ public:
mFace = face;
}
- virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
+ virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
{ //this is a depth first traversal, so it's safe to assum all children have complete
//bounding data
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
@@ -398,8 +405,7 @@ public:
min = *(tri->mV[0]);
max = *(tri->mV[0]);
- for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter =
- branch->getDataBegin(); iter != branch->getDataEnd(); ++iter)
+ for (LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter)
{ //for each triangle in node
//stretch by triangles in node
@@ -683,7 +689,7 @@ LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, BOOL flat, F3
Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat);
- static LLAlignedArray<LLVector4a,64> pt;
+ static thread_local LLAlignedArray<LLVector4a,64> pt;
pt.resize(mTotal) ;
for (S32 i=mTotalOut;i<mTotal;i++)
@@ -1624,9 +1630,6 @@ BOOL LLPath::generate(const LLPathParams& params, F32 detail, S32 split,
//genNGon(params, llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
genNGon(params, llfloor(MIN_DETAIL_FACES * detail));
- F32 t = 0.f;
- F32 tStep = 1.0f / mPath.size();
-
F32 toggle = 0.5f;
for (S32 i=0;i<(S32)mPath.size();i++)
{
@@ -1635,7 +1638,6 @@ BOOL LLPath::generate(const LLPathParams& params, F32 detail, S32 split,
toggle = -0.5f;
else
toggle = 0.5f;
- t += tStep;
}
}
@@ -2097,7 +2099,9 @@ void LLVolume::regen()
void LLVolume::genTangents(S32 face)
{
- mVolumeFaces[face].createTangents();
+ // generate legacy tangents for the specified face
+ llassert(!isMeshAssetLoaded() || mVolumeFaces[face].mTangents != nullptr); // if this is a complete mesh asset, we should already have tangents
+ mVolumeFaces[face].createTangents();
}
LLVolume::~LLVolume()
@@ -2419,13 +2423,19 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
LLSD::Binary pos = mdl[i]["Position"];
LLSD::Binary norm = mdl[i]["Normal"];
+ LLSD::Binary tangent = mdl[i]["Tangent"];
LLSD::Binary tc = mdl[i]["TexCoord0"];
LLSD::Binary idx = mdl[i]["TriangleList"];
-
-
//copy out indices
S32 num_indices = idx.size() / 2;
+ const S32 indices_to_discard = num_indices % 3;
+ if (indices_to_discard > 0)
+ {
+ // Invalid number of triangle indices
+ LL_WARNS() << "Incomplete triangle discarded from face! Indices count " << num_indices << " was not divisible by 3. face index: " << i << " Total: " << face_count << LL_ENDL;
+ num_indices -= indices_to_discard;
+ }
face.resizeIndices(num_indices);
if (num_indices > 2 && !face.mIndices)
@@ -2441,8 +2451,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
}
U16* indices = (U16*) &(idx[0]);
- U32 count = idx.size()/2;
- for (U32 j = 0; j < count; ++j)
+ for (U32 j = 0; j < num_indices; ++j)
{
face.mIndices[j] = indices[j];
}
@@ -2472,6 +2481,16 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
min_tc.setValue(mdl[i]["TexCoord0Domain"]["Min"]);
max_tc.setValue(mdl[i]["TexCoord0Domain"]["Max"]);
+ //unpack normalized scale/translation
+ if (mdl[i].has("NormalizedScale"))
+ {
+ face.mNormalizedScale.setValue(mdl[i]["NormalizedScale"]);
+ }
+ else
+ {
+ face.mNormalizedScale.set(1, 1, 1);
+ }
+
LLVector4a pos_range;
pos_range.setSub(max_pos, min_pos);
LLVector2 tc_range2 = max_tc - min_tc;
@@ -2522,6 +2541,34 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
}
}
+#if 0 // keep this code for now in case we decide to add support for on-the-wire tangents
+ {
+ if (!tangent.empty())
+ {
+ face.allocateTangents(face.mNumVertices);
+ U16* t = (U16*)&(tangent[0]);
+
+ // NOTE: tangents coming from the asset may not be mikkt space, but they should always be used by the GLTF shaders to
+ // maintain compliance with the GLTF spec
+ LLVector4a* t_out = face.mTangents;
+
+ for (U32 j = 0; j < num_verts; ++j)
+ {
+ t_out->set((F32)t[0], (F32)t[1], (F32)t[2], (F32) t[3]);
+ t_out->div(65535.f);
+ t_out->mul(2.f);
+ t_out->sub(1.f);
+
+ F32* tp = t_out->getF32ptr();
+ tp[3] = tp[3] < 0.f ? -1.f : 1.f;
+
+ t_out++;
+ t += 4;
+ }
+ }
+ }
+#endif
+
{
if (!tc.empty())
{
@@ -2725,7 +2772,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
}
}
- if (!cacheOptimize())
+ if (!cacheOptimize(true))
{
// Out of memory?
LL_WARNS() << "Failed to optimize!" << LL_ENDL;
@@ -2766,11 +2813,11 @@ void LLVolume::copyVolumeFaces(const LLVolume* volume)
mSculptLevel = 0;
}
-bool LLVolume::cacheOptimize()
+bool LLVolume::cacheOptimize(bool gen_tangents)
{
for (S32 i = 0; i < mVolumeFaces.size(); ++i)
{
- if (!mVolumeFaces[i].cacheOptimize())
+ if (!mVolumeFaces[i].cacheOptimize(gen_tangents))
{
return false;
}
@@ -3834,8 +3881,8 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
#if DEBUG_SILHOUETTE_EDGE_MAP
//for each triangle
- U32 count = face.mNumIndices;
- for (U32 j = 0; j < count/3; j++) {
+ U32 tri_count = face.mNumIndices / 3;
+ for (U32 j = 0; j < tri_count; j++) {
//get vertices
S32 v1 = face.mIndices[j*3+0];
S32 v2 = face.mIndices[j*3+1];
@@ -3853,7 +3900,7 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
continue;
}
- if (nIndex >= (S32) count/3) {
+ if (nIndex >= (S32)tri_count) {
continue;
}
//get neighbor vertices
@@ -4053,7 +4100,7 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en
{
if (tangent_out != NULL) // if the caller wants tangents, we may need to generate them
{
- genTangents(i);
+ genTangents(i);
}
if (isUnique())
@@ -4145,13 +4192,13 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en
}
else
{
- if (!face.mOctree)
+ if (!face.getOctree())
{
face.createOctree();
}
LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
- intersect.traverse(face.mOctree);
+ intersect.traverse(face.getOctree());
if (intersect.mHitFace)
{
hit_face = i;
@@ -4706,6 +4753,7 @@ LLVolumeFace::LLVolumeFace() :
#endif
mWeightsScrubbed(FALSE),
mOctree(NULL),
+ mOctreeTriangles(NULL),
mOptimized(FALSE)
{
mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
@@ -4735,8 +4783,9 @@ LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
mJointIndices(NULL),
#endif
mWeightsScrubbed(FALSE),
- mOctree(NULL)
-{
+ mOctree(NULL),
+ mOctreeTriangles(NULL)
+{
mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3);
mCenter = mExtents+2;
*this = src;
@@ -4796,6 +4845,17 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
mTangents = NULL;
}
+ if (src.mTangents)
+ {
+ allocateTangents(src.mNumVertices);
+ LLVector4a::memcpyNonAliased16((F32*)mTangents, (F32*)src.mTangents, vert_size);
+ }
+ else
+ {
+ ll_aligned_free_16(mTangents);
+ mTangents = nullptr;
+ }
+
if (src.mWeights)
{
llassert(!mWeights); // don't orphan an old alloc here accidentally
@@ -4839,6 +4899,7 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
}
mOptimized = src.mOptimized;
+ mNormalizedScale = src.mNormalizedScale;
//delete
return *this;
@@ -4876,8 +4937,7 @@ void LLVolumeFace::freeData()
mJustWeights = NULL;
#endif
- delete mOctree;
- mOctree = NULL;
+ destroyOctree();
}
BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
@@ -4885,8 +4945,7 @@ BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build)
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
//tree for this face is no longer valid
- delete mOctree;
- mOctree = NULL;
+ destroyOctree();
LL_CHECK_MEMORY
BOOL ret = FALSE ;
@@ -4952,6 +5011,50 @@ bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a
return a.mV[2] < b.mV[2];
}
+void LLVolumeFace::remap()
+{
+ // Generate a remap buffer
+ std::vector<unsigned int> remap(mNumVertices);
+ S32 remap_vertices_count = LLMeshOptimizer::generateRemapMultiU16(&remap[0],
+ mIndices,
+ mNumIndices,
+ mPositions,
+ mNormals,
+ mTexCoords,
+ mNumVertices);
+
+ // Allocate new buffers
+ S32 size = ((mNumIndices * sizeof(U16)) + 0xF) & ~0xF;
+ U16* remap_indices = (U16*)ll_aligned_malloc_16(size);
+
+ S32 tc_bytes_size = ((remap_vertices_count * sizeof(LLVector2)) + 0xF) & ~0xF;
+ LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * remap_vertices_count + tc_bytes_size);
+ LLVector4a* remap_normals = remap_positions + remap_vertices_count;
+ LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + remap_vertices_count);
+
+ // Fill the buffers
+ LLMeshOptimizer::remapIndexBufferU16(remap_indices, mIndices, mNumIndices, &remap[0]);
+ LLMeshOptimizer::remapPositionsBuffer(remap_positions, mPositions, mNumVertices, &remap[0]);
+ LLMeshOptimizer::remapNormalsBuffer(remap_normals, mNormals, mNumVertices, &remap[0]);
+ LLMeshOptimizer::remapUVBuffer(remap_tex_coords, mTexCoords, mNumVertices, &remap[0]);
+
+ // Free unused buffers
+ ll_aligned_free_16(mIndices);
+ ll_aligned_free<64>(mPositions);
+
+ // Tangets are now invalid
+ ll_aligned_free_16(mTangents);
+ mTangents = NULL;
+
+ // Assign new values
+ mIndices = remap_indices;
+ mPositions = remap_positions;
+ mNormals = remap_normals;
+ mTexCoords = remap_tex_coords;
+ mNumVertices = remap_vertices_count;
+ mNumAllocatedVertices = remap_vertices_count;
+}
+
void LLVolumeFace::optimize(F32 angle_cutoff)
{
LLVolumeFace new_face;
@@ -5308,245 +5411,200 @@ public:
}
};
+// data structures for tangent generation
-bool LLVolumeFace::cacheOptimize()
-{ //optimize for vertex cache according to Forsyth method:
- // http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
-
- llassert(!mOptimized);
- mOptimized = TRUE;
+struct MikktData
+{
+ LLVolumeFace* face;
+ std::vector<LLVector3> p;
+ std::vector<LLVector3> n;
+ std::vector<LLVector2> tc;
+ std::vector<LLVector4> w;
+ std::vector<LLVector4> t;
- LLVCacheLRU cache;
-
- if (mNumVertices < 3 || mNumIndices < 3)
- { //nothing to do
- return true;
- }
+ MikktData(LLVolumeFace* f)
+ : face(f)
+ {
+ U32 count = face->mNumIndices;
- //mapping of vertices to triangles and indices
- std::vector<LLVCacheVertexData> vertex_data;
+ p.resize(count);
+ n.resize(count);
+ tc.resize(count);
+ t.resize(count);
- //mapping of triangles do vertices
- std::vector<LLVCacheTriangleData> triangle_data;
+ if (face->mWeights)
+ {
+ w.resize(count);
+ }
- try
- {
- triangle_data.resize(mNumIndices / 3);
- vertex_data.resize(mNumVertices);
- for (U32 i = 0; i < mNumIndices; i++)
- { //populate vertex data and triangle data arrays
- U16 idx = mIndices[i];
- U32 tri_idx = i / 3;
+ LLVector3 inv_scale(1.f / face->mNormalizedScale.mV[0], 1.f / face->mNormalizedScale.mV[1], 1.f / face->mNormalizedScale.mV[2]);
+
+
+ for (int i = 0; i < face->mNumIndices; ++i)
+ {
+ U32 idx = face->mIndices[i];
- vertex_data[idx].mTriangles.push_back(&(triangle_data[tri_idx]));
- vertex_data[idx].mIdx = idx;
- triangle_data[tri_idx].mVertex[i % 3] = &(vertex_data[idx]);
+ p[i].set(face->mPositions[idx].getF32ptr());
+ p[i].scaleVec(face->mNormalizedScale); //put mesh in original coordinate frame when reconstructing tangents
+ n[i].set(face->mNormals[idx].getF32ptr());
+ n[i].scaleVec(inv_scale);
+ n[i].normalize();
+ tc[i].set(face->mTexCoords[idx]);
+
+ if (face->mWeights)
+ {
+ w[i].set(face->mWeights[idx].getF32ptr());
+ }
}
}
- catch (std::bad_alloc&)
- {
- // resize or push_back failed
- LL_WARNS("LLVOLUME") << "Resize for " << mNumVertices << " vertices failed" << LL_ENDL;
- return false;
- }
+};
- /*F32 pre_acmr = 1.f;
- //measure cache misses from before rebuild
- {
- LLVCacheFIFO test_cache;
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- test_cache.addVertex(&vertex_data[mIndices[i]]);
- }
- for (U32 i = 0; i < mNumVertices; i++)
- {
- vertex_data[i].mCacheTag = -1;
- }
+bool LLVolumeFace::cacheOptimize(bool gen_tangents)
+{ //optimize for vertex cache according to Forsyth method:
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
+ llassert(!mOptimized);
+ mOptimized = TRUE;
- pre_acmr = (F32) test_cache.mMisses/(mNumIndices/3);
- }*/
+ if (gen_tangents && mNormals && mTexCoords)
+ { // generate mikkt space tangents before cache optimizing since the index buffer may change
+ // a bit of a hack to do this here, but this function gets called exactly once for the lifetime of a mesh
+ // and is executed on a background thread
+ SMikkTSpaceInterface ms;
- for (U32 i = 0; i < mNumVertices; i++)
- { //initialize score values (no cache -- might try a fifo cache here)
- LLVCacheVertexData& data = vertex_data[i];
+ ms.m_getNumFaces = [](const SMikkTSpaceContext* pContext)
+ {
+ MikktData* data = (MikktData*)pContext->m_pUserData;
+ LLVolumeFace* face = data->face;
+ return face->mNumIndices / 3;
+ };
- data.mScore = find_vertex_score(data);
- data.mActiveTriangles = data.mTriangles.size();
+ ms.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace)
+ {
+ return 3;
+ };
- for (U32 j = 0; j < data.mActiveTriangles; ++j)
- {
- data.mTriangles[j]->mScore += data.mScore;
- }
- }
+ ms.m_getPosition = [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert)
+ {
+ MikktData* data = (MikktData*)pContext->m_pUserData;
+ F32* v = data->p[iFace * 3 + iVert].mV;
+ fvPosOut[0] = v[0];
+ fvPosOut[1] = v[1];
+ fvPosOut[2] = v[2];
+ };
+
+ ms.m_getNormal = [](const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert)
+ {
+ MikktData* data = (MikktData*)pContext->m_pUserData;
+ F32* n = data->n[iFace * 3 + iVert].mV;
+ fvNormOut[0] = n[0];
+ fvNormOut[1] = n[1];
+ fvNormOut[2] = n[2];
+ };
+
+ ms.m_getTexCoord = [](const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert)
+ {
+ MikktData* data = (MikktData*)pContext->m_pUserData;
+ F32* tc = data->tc[iFace * 3 + iVert].mV;
+ fvTexcOut[0] = tc[0];
+ fvTexcOut[1] = tc[1];
+ };
- //sort triangle data by score
- std::sort(triangle_data.begin(), triangle_data.end());
+ ms.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert)
+ {
+ MikktData* data = (MikktData*)pContext->m_pUserData;
+ S32 i = iFace * 3 + iVert;
+
+ data->t[i].set(fvTangent);
+ data->t[i].mV[3] = fSign;
+ };
- std::vector<U16> new_indices;
+ ms.m_setTSpace = nullptr;
- LLVCacheTriangleData* tri;
+ MikktData data(this);
- //prime pump by adding first triangle to cache;
- tri = &(triangle_data[0]);
- cache.addTriangle(tri);
- new_indices.push_back(tri->mVertex[0]->mIdx);
- new_indices.push_back(tri->mVertex[1]->mIdx);
- new_indices.push_back(tri->mVertex[2]->mIdx);
- tri->complete();
+ SMikkTSpaceContext ctx = { &ms, &data };
- U32 breaks = 0;
- for (U32 i = 1; i < mNumIndices/3; ++i)
- {
- cache.updateScores();
- tri = cache.mBestTriangle;
- if (!tri)
- {
- breaks++;
- for (U32 j = 0; j < triangle_data.size(); ++j)
- {
- if (triangle_data[j].mActive)
- {
- tri = &(triangle_data[j]);
- break;
- }
- }
- }
-
- cache.addTriangle(tri);
- new_indices.push_back(tri->mVertex[0]->mIdx);
- new_indices.push_back(tri->mVertex[1]->mIdx);
- new_indices.push_back(tri->mVertex[2]->mIdx);
- tri->complete();
- }
+ genTangSpaceDefault(&ctx);
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- mIndices[i] = new_indices[i];
- }
+ //re-weld
+ meshopt_Stream mos[] =
+ {
+ { &data.p[0], sizeof(LLVector3), sizeof(LLVector3) },
+ { &data.n[0], sizeof(LLVector3), sizeof(LLVector3) },
+ { &data.t[0], sizeof(LLVector4), sizeof(LLVector4) },
+ { &data.tc[0], sizeof(LLVector2), sizeof(LLVector2) },
+ { data.w.empty() ? nullptr : &data.w[0], sizeof(LLVector4), sizeof(LLVector4) }
+ };
- /*F32 post_acmr = 1.f;
- //measure cache misses from after rebuild
- {
- LLVCacheFIFO test_cache;
- for (U32 i = 0; i < mNumVertices; i++)
- {
- vertex_data[i].mCacheTag = -1;
- }
+ std::vector<U32> remap;
+ remap.resize(data.p.size());
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- test_cache.addVertex(&vertex_data[mIndices[i]]);
- }
-
- post_acmr = (F32) test_cache.mMisses/(mNumIndices/3);
- }*/
+ U32 stream_count = data.w.empty() ? 4 : 5;
- //optimize for pre-TnL cache
-
- //allocate space for new buffer
- S32 num_verts = mNumVertices;
- S32 size = ((num_verts*sizeof(LLVector2)) + 0xF) & ~0xF;
- LLVector4a* pos = (LLVector4a*) ll_aligned_malloc<64>(sizeof(LLVector4a)*2*num_verts+size);
- if (pos == NULL)
- {
- LL_WARNS("LLVOLUME") << "Allocation of positions vector[" << sizeof(LLVector4a) * 2 * num_verts + size << "] failed. " << LL_ENDL;
- return false;
- }
- LLVector4a* norm = pos + num_verts;
- LLVector2* tc = (LLVector2*) (norm + num_verts);
+ U32 vert_count = meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count);
- LLVector4a* wght = NULL;
- if (mWeights)
- {
- wght = (LLVector4a*)ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
- if (wght == NULL)
- {
- ll_aligned_free<64>(pos);
- LL_WARNS("LLVOLUME") << "Allocation of weights[" << sizeof(LLVector4a) * num_verts << "] failed" << LL_ENDL;
- return false;
- }
- }
+ std::vector<U32> indices;
+ indices.resize(mNumIndices);
- LLVector4a* binorm = NULL;
- if (mTangents)
- {
- binorm = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
- if (binorm == NULL)
- {
- ll_aligned_free<64>(pos);
- ll_aligned_free_16(wght);
- LL_WARNS("LLVOLUME") << "Allocation of binormals[" << sizeof(LLVector4a)*num_verts << "] failed" << LL_ENDL;
- return false;
- }
- }
+ //copy results back into volume
+ resizeVertices(vert_count);
- //allocate mapping of old indices to new indices
- std::vector<S32> new_idx;
+ if (!data.w.empty())
+ {
+ allocateWeights(vert_count);
+ }
- try
- {
- new_idx.resize(mNumVertices, -1);
- }
- catch (std::bad_alloc&)
- {
- ll_aligned_free<64>(pos);
- ll_aligned_free_16(wght);
- ll_aligned_free_16(binorm);
- LL_WARNS("LLVOLUME") << "Resize failed: " << mNumVertices << LL_ENDL;
- return false;
- }
+ allocateTangents(mNumVertices);
- S32 cur_idx = 0;
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- U16 idx = mIndices[i];
- if (new_idx[idx] == -1)
- { //this vertex hasn't been added yet
- new_idx[idx] = cur_idx;
+ for (int i = 0; i < mNumIndices; ++i)
+ {
+ U32 src_idx = i;
+ U32 dst_idx = remap[i];
+ mIndices[i] = dst_idx;
- //copy vertex data
- pos[cur_idx] = mPositions[idx];
- norm[cur_idx] = mNormals[idx];
- tc[cur_idx] = mTexCoords[idx];
- if (mWeights)
- {
- wght[cur_idx] = mWeights[idx];
- }
- if (mTangents)
- {
- binorm[cur_idx] = mTangents[idx];
- }
+ mPositions[dst_idx].load3(data.p[src_idx].mV);
+ mNormals[dst_idx].load3(data.n[src_idx].mV);
+ mTexCoords[dst_idx] = data.tc[src_idx];
- cur_idx++;
- }
- }
+ mTangents[dst_idx].loadua(data.t[src_idx].mV);
- for (U32 i = 0; i < mNumIndices; ++i)
- {
- mIndices[i] = new_idx[mIndices[i]];
- }
-
- ll_aligned_free<64>(mPositions);
- // DO NOT free mNormals and mTexCoords as they are part of mPositions buffer
- ll_aligned_free_16(mWeights);
- ll_aligned_free_16(mTangents);
-#if USE_SEPARATE_JOINT_INDICES_AND_WEIGHTS
- ll_aligned_free_16(mJointIndices);
- ll_aligned_free_16(mJustWeights);
- mJustWeights = NULL;
- mJointIndices = NULL; // filled in later as necessary by skinning code for acceleration
-#endif
+ if (mWeights)
+ {
+ mWeights[dst_idx].loadua(data.w[src_idx].mV);
+ }
+ }
- mPositions = pos;
- mNormals = norm;
- mTexCoords = tc;
- mWeights = wght;
- mTangents = binorm;
- //std::string result = llformat("ACMR pre/post: %.3f/%.3f -- %d triangles %d breaks", pre_acmr, post_acmr, mNumIndices/3, breaks);
- //LL_INFOS() << result << LL_ENDL;
+ // put back in normalized coordinate frame
+ LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]);
+ LLVector4a scale;
+ scale.load3(mNormalizedScale.mV);
+ scale.getF32ptr()[3] = 1.f;
+
+ for (int i = 0; i < mNumVertices; ++i)
+ {
+ mPositions[i].mul(inv_scale);
+ mNormals[i].mul(scale);
+ mNormals[i].normalize3();
+ F32 w = mTangents[i].getF32ptr()[3];
+ mTangents[i].mul(scale);
+ mTangents[i].normalize3();
+ mTangents[i].getF32ptr()[3] = w;
+ }
+ }
+
+ // cache optimize index buffer
+
+ // meshopt needs scratch space, do some pointer shuffling to avoid an extra index buffer copy
+ U16* src_indices = mIndices;
+ mIndices = nullptr;
+ resizeIndices(mNumIndices);
+
+ meshopt_optimizeVertexCache<U16>(mIndices, src_indices, mNumIndices, mNumVertices);
+
+ ll_aligned_free_16(src_indices);
return true;
}
@@ -5555,21 +5613,27 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
- if (mOctree)
+ if (getOctree())
{
return;
}
- mOctree = new LLOctreeRoot<LLVolumeTriangle>(center, size, NULL);
+ llassert(mNumIndices % 3 == 0);
+
+ mOctree = new LLOctreeRoot<LLVolumeTriangle, LLVolumeTriangle*>(center, size, NULL);
new LLVolumeOctreeListener(mOctree);
+ const U32 num_triangles = mNumIndices / 3;
+ // Initialize all the triangles we need
+ mOctreeTriangles = new LLVolumeTriangle[num_triangles];
- for (U32 i = 0; i < mNumIndices; i+= 3)
+ for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index)
{ //for each triangle
- LLPointer<LLVolumeTriangle> tri = new LLVolumeTriangle();
+ const U32 index = triangle_index * 3;
+ LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index];
- const LLVector4a& v0 = mPositions[mIndices[i]];
- const LLVector4a& v1 = mPositions[mIndices[i+1]];
- const LLVector4a& v2 = mPositions[mIndices[i+2]];
+ const LLVector4a& v0 = mPositions[mIndices[index]];
+ const LLVector4a& v1 = mPositions[mIndices[index + 1]];
+ const LLVector4a& v2 = mPositions[mIndices[index + 2]];
//store pointers to vertex data
tri->mV[0] = &v0;
@@ -5577,9 +5641,9 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
tri->mV[2] = &v2;
//store indices
- tri->mIndex[0] = mIndices[i];
- tri->mIndex[1] = mIndices[i+1];
- tri->mIndex[2] = mIndices[i+2];
+ tri->mIndex[0] = mIndices[index];
+ tri->mIndex[1] = mIndices[index + 1];
+ tri->mIndex[2] = mIndices[index + 2];
//get minimum point
LLVector4a min = v0;
@@ -5622,6 +5686,19 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe
}
}
+void LLVolumeFace::destroyOctree()
+{
+ delete mOctree;
+ mOctree = NULL;
+ delete[] mOctreeTriangles;
+ mOctreeTriangles = NULL;
+}
+
+const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* LLVolumeFace::getOctree() const
+{
+ return mOctree;
+}
+
void LLVolumeFace::swapData(LLVolumeFace& rhs)
{
@@ -6337,35 +6414,31 @@ void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVe
void LLVolumeFace::createTangents()
{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
- if (!mTangents)
- {
- allocateTangents(mNumVertices);
-
- //generate tangents
- //LLVector4a* pos = mPositions;
- //LLVector2* tc = (LLVector2*) mTexCoords;
- LLVector4a* binorm = (LLVector4a*) mTangents;
+ if (!mTangents)
+ {
+ allocateTangents(mNumVertices);
+
+ //generate tangents
+ LLVector4a* ptr = (LLVector4a*)mTangents;
- LLVector4a* end = mTangents+mNumVertices;
- while (binorm < end)
- {
- (*binorm++).clear();
- }
+ LLVector4a* end = mTangents + mNumVertices;
+ while (ptr < end)
+ {
+ (*ptr++).clear();
+ }
- binorm = mTangents;
+ CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices / 3, mIndices, mTangents);
- CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices/3, mIndices, mTangents);
+ //normalize normals
+ for (U32 i = 0; i < mNumVertices; i++)
+ {
+ //bump map/planar projection code requires normals to be normalized
+ mNormals[i].normalize3fast();
+ }
+ }
- //normalize tangents
- for (U32 i = 0; i < mNumVertices; i++)
- {
- //binorm[i].normalize3fast();
- //bump map/planar projection code requires normals to be normalized
- mNormals[i].normalize3fast();
- }
- }
}
void LLVolumeFace::resizeVertices(S32 num_verts)
@@ -6493,6 +6566,7 @@ void LLVolumeFace::allocateJointIndices(S32 num_verts)
void LLVolumeFace::resizeIndices(S32 num_indices)
{
ll_aligned_free_16(mIndices);
+ llassert(num_indices % 3 == 0);
if (num_indices)
{
@@ -6629,13 +6703,19 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
else
{
// Get s value for tex-coord.
- if (!flat)
+ S32 index = mBeginS + s;
+ if (index >= profile.size())
+ {
+ // edge?
+ ss = flat ? 1.f - begin_stex : 1.f;
+ }
+ else if (!flat)
{
- ss = profile[mBeginS + s][2];
+ ss = profile[index][2];
}
else
{
- ss = profile[mBeginS + s][2] - begin_stex;
+ ss = profile[index][2] - begin_stex;
}
}
@@ -6821,7 +6901,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
LLVector4a* norm = mNormals;
- static LLAlignedArray<LLVector4a, 64> triangle_normals;
+ static thread_local LLAlignedArray<LLVector4a, 64> triangle_normals;
try
{
triangle_normals.resize(count);
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index c0b224b1ff..e8faf549f4 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -35,7 +35,8 @@ class LLVolumeParams;
class LLProfile;
class LLPath;
-template <class T> class LLOctreeNode;
+template<class T> class LLPointer;
+template <class T, typename T_PTR> class LLOctreeNode;
class LLVolumeFace;
class LLVolume;
@@ -902,10 +903,17 @@ public:
typedef std::map<LLVector3, std::vector<VertexMapData>, VertexMapData::ComparePosition > PointMap;
};
+ // Eliminates non unique triangles, takes positions,
+ // normals and texture coordinates into account.
+ void remap();
+
void optimize(F32 angle_cutoff = 2.f);
- bool cacheOptimize();
+ bool cacheOptimize(bool gen_tangents = false);
void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f));
+ void destroyOctree();
+ // Get a reference to the octree, which may be null
+ const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* getOctree() const;
enum
{
@@ -952,10 +960,6 @@ public:
// indexes for mPositions/mNormals/mTexCoords
U16* mIndices;
- // vertex buffer filled in by LLFace to cache this volume face geometry in vram
- // (declared as a LLPointer to LLRefCount to avoid dependency on LLVertexBuffer)
- mutable LLPointer<LLRefCount> mVertexBuffer;
-
std::vector<S32> mEdge;
//list of skin weights for rigged volumes
@@ -973,13 +977,19 @@ public:
// Which joints are rigged to, and the bounding box of any rigged
// vertices per joint.
LLJointRiggingInfoTab mJointRiggingInfoTab;
-
- LLOctreeNode<LLVolumeTriangle>* mOctree;
//whether or not face has been cache optimized
BOOL mOptimized;
+ // if this is a mesh asset, scale and translation that were applied
+ // when encoding the source mesh into a unit cube
+ // used for regenerating tangents
+ LLVector3 mNormalizedScale = LLVector3(1,1,1);
+
private:
+ LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* mOctree;
+ LLVolumeTriangle* mOctreeTriangles;
+
BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE);
BOOL createCap(LLVolume* volume, BOOL partial_build = FALSE);
BOOL createSide(LLVolume* volume, BOOL partial_build = FALSE);
@@ -1024,7 +1034,7 @@ public:
void setDirty() { mPathp->setDirty(); mProfilep->setDirty(); }
void regen();
- void genTangents(S32 face);
+ void genTangents(S32 face);
BOOL isConvex() const;
BOOL isCap(S32 face);
@@ -1078,7 +1088,10 @@ public:
void copyVolumeFaces(const LLVolume* volume);
void copyFacesTo(std::vector<LLVolumeFace> &faces) const;
void copyFacesFrom(const std::vector<LLVolumeFace> &faces);
- bool cacheOptimize();
+
+ // use meshoptimizer to optimize index buffer for vertex shader cache
+ // gen_tangents - if true, generate MikkTSpace tangents if needed before optimizing index buffer
+ bool cacheOptimize(bool gen_tangents = false);
private:
void sculptGenerateMapVertices(U16 sculpt_width, U16 sculpt_height, S8 sculpt_components, const U8* sculpt_data, U8 sculpt_type);
diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp
index fb232d5f6c..6894d04d3c 100644
--- a/indra/llmath/llvolumeoctree.cpp
+++ b/indra/llmath/llvolumeoctree.cpp
@@ -75,7 +75,7 @@ BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, c
}
-LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle>* node)
+LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
{
node->addListener(this);
}
@@ -85,13 +85,12 @@ LLVolumeOctreeListener::~LLVolumeOctreeListener()
}
-void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTriangle>* parent,
- LLOctreeNode<LLVolumeTriangle>* child)
+void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent,
+ LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child)
{
new LLVolumeOctreeListener(child);
}
-
LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir,
const LLVolumeFace* face, F32* closest_t,
LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
@@ -108,7 +107,7 @@ LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& sta
mEnd.setAdd(mStart, mDir);
}
-void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle>* node)
+void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
{
LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0);
@@ -122,9 +121,9 @@ void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle>
}
}
-void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle>* node)
+void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node)
{
- for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter =
+ for (typename LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter =
node->getDataBegin(); iter != node->getDataEnd(); ++iter)
{
const LLVolumeTriangle* tri = *iter;
@@ -219,7 +218,7 @@ const F32& LLVolumeTriangle::getBinRadius() const
//TEST CODE
-void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle>* branch)
+void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch)
{
LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0);
@@ -256,7 +255,7 @@ void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle>* branch)
}
//children fit, check data
- for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getDataBegin();
+ for (typename LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>::const_element_iter iter = branch->getDataBegin();
iter != branch->getDataEnd(); ++iter)
{
const LLVolumeTriangle* tri = *iter;
@@ -273,4 +272,3 @@ void LLVolumeOctreeValidate::visit(const LLOctreeNode<LLVolumeTriangle>* branch)
}
}
-
diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h
index b2bc440368..d65bca5e52 100644
--- a/indra/llmath/llvolumeoctree.h
+++ b/indra/llmath/llvolumeoctree.h
@@ -77,11 +77,11 @@ public:
};
-class alignas(16) LLVolumeOctreeListener : public LLOctreeListener<LLVolumeTriangle>
+class alignas(16) LLVolumeOctreeListener : public LLOctreeListener<LLVolumeTriangle, LLVolumeTriangle*>
{
LL_ALIGN_NEW
public:
- LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle>* node);
+ LLVolumeOctreeListener(LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
~LLVolumeOctreeListener();
LLVolumeOctreeListener(const LLVolumeOctreeListener& rhs)
@@ -96,11 +96,9 @@ public:
}
//LISTENER FUNCTIONS
- virtual void handleChildAddition(const LLOctreeNode<LLVolumeTriangle>* parent,
- LLOctreeNode<LLVolumeTriangle>* child);
+ virtual void handleChildAddition(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent, LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child);
virtual void handleStateChange(const LLTreeNode<LLVolumeTriangle>* node) { }
- virtual void handleChildRemoval(const LLOctreeNode<LLVolumeTriangle>* parent,
- const LLOctreeNode<LLVolumeTriangle>* child) { }
+ virtual void handleChildRemoval(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* parent, const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* child) { }
virtual void handleInsertion(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { }
virtual void handleRemoval(const LLTreeNode<LLVolumeTriangle>* node, LLVolumeTriangle* tri) { }
virtual void handleDestruction(const LLTreeNode<LLVolumeTriangle>* node) { }
@@ -111,7 +109,7 @@ public:
LL_ALIGN_16(LLVector4a mExtents[2]); // extents (min, max) of this node and all its children
};
-class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle>
+class LLOctreeTriangleRayIntersect : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*>
{
public:
const LLVolumeFace* mFace;
@@ -129,14 +127,14 @@ public:
const LLVolumeFace* face, F32* closest_t,
LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent);
- void traverse(const LLOctreeNode<LLVolumeTriangle>* node);
+ void traverse(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
- virtual void visit(const LLOctreeNode<LLVolumeTriangle>* node);
+ virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* node);
};
-class LLVolumeOctreeValidate : public LLOctreeTraveler<LLVolumeTriangle>
+class LLVolumeOctreeValidate : public LLOctreeTraveler<LLVolumeTriangle, LLVolumeTriangle*>
{
- virtual void visit(const LLOctreeNode<LLVolumeTriangle>* branch);
+ virtual void visit(const LLOctreeNode<LLVolumeTriangle, LLVolumeTriangle*>* branch);
};
#endif
diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h
index 43a632408c..d925f56e97 100644
--- a/indra/llmath/v3color.h
+++ b/indra/llmath/v3color.h
@@ -33,6 +33,7 @@ class LLVector4;
#include "llerror.h"
#include "llmath.h"
#include "llsd.h"
+#include "v3math.h" // needed for linearColor3v implemtation below
#include <string.h>
// LLColor3 = |r g b|
@@ -87,6 +88,16 @@ public:
const LLColor3& set(F32 x, F32 y, F32 z); // Sets LLColor3 to (x, y, z)
const LLColor3& set(const LLColor3 &vec); // Sets LLColor3 to vec
const LLColor3& set(const F32 *vec); // Sets LLColor3 to vec
+
+ // set from a vector of unknown type and size
+ // may leave some data unmodified
+ template<typename T>
+ const LLColor3& set(const std::vector<T>& v);
+
+ // write to a vector of unknown type and size
+ // maye leave some data unmodified
+ template<typename T>
+ void write(std::vector<T>& v) const;
F32 magVec() const; // deprecated
F32 magVecSquared() const; // deprecated
@@ -484,13 +495,45 @@ inline const LLColor3 srgbColor3(const LLColor3 &a) {
return srgbColor;
}
-inline const LLColor3 linearColor3(const LLColor3 &a) {
+inline const LLColor3 linearColor3p(const F32* v) {
LLColor3 linearColor;
- linearColor.mV[0] = sRGBtoLinear(a.mV[0]);
- linearColor.mV[1] = sRGBtoLinear(a.mV[1]);
- linearColor.mV[2] = sRGBtoLinear(a.mV[2]);
+ linearColor.mV[0] = sRGBtoLinear(v[0]);
+ linearColor.mV[1] = sRGBtoLinear(v[1]);
+ linearColor.mV[2] = sRGBtoLinear(v[2]);
return linearColor;
}
+template<class T>
+inline const LLColor3 linearColor3(const T& a) {
+ return linearColor3p(a.mV);
+}
+
+template<class T>
+inline const LLVector3 linearColor3v(const T& a) {
+ return LLVector3(linearColor3p(a.mV).mV);
+}
+
+template<typename T>
+const LLColor3& LLColor3::set(const std::vector<T>& v)
+{
+ for (S32 i = 0; i < llmin((S32)v.size(), 3); ++i)
+ {
+ mV[i] = v[i];
+ }
+
+ return *this;
+}
+
+// write to a vector of unknown type and size
+// maye leave some data unmodified
+template<typename T>
+void LLColor3::write(std::vector<T>& v) const
+{
+ for (int i = 0; i < llmin((S32)v.size(), 3); ++i)
+ {
+ v[i] = mV[i];
+ }
+}
+
#endif
diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h
index 175edf1471..daa61594fb 100644
--- a/indra/llmath/v4color.h
+++ b/indra/llmath/v4color.h
@@ -88,8 +88,18 @@ class LLColor4
const LLColor4& set(const LLColor3 &vec); // Sets LLColor4 to LLColor3 vec (no change in alpha)
const LLColor4& set(const LLColor3 &vec, F32 a); // Sets LLColor4 to LLColor3 vec, with alpha specified
const LLColor4& set(const F32 *vec); // Sets LLColor4 to vec
- const LLColor4& set(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled.
+ const LLColor4& set(const F64 *vec); // Sets LLColor4 to (double)vec
+ const LLColor4& set(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled.
+ // set from a vector of unknown type and size
+ // may leave some data unmodified
+ template<typename T>
+ const LLColor4& set(const std::vector<T>& v);
+
+ // write to a vector of unknown type and size
+ // maye leave some data unmodified
+ template<typename T>
+ void write(std::vector<T>& v) const;
const LLColor4& setAlpha(F32 a);
@@ -334,6 +344,15 @@ inline const LLColor4& LLColor4::set(const F32 *vec)
return (*this);
}
+inline const LLColor4& LLColor4::set(const F64 *vec)
+{
+ mV[VX] = static_cast<F32>(vec[VX]);
+ mV[VY] = static_cast<F32>(vec[VY]);
+ mV[VZ] = static_cast<F32>(vec[VZ]);
+ mV[VW] = static_cast<F32>(vec[VW]);
+ return (*this);
+}
+
// deprecated
inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z)
{
@@ -680,5 +699,25 @@ inline const LLColor4 linearColor4(const LLColor4 &a)
return linearColor;
}
+template<typename T>
+const LLColor4& LLColor4::set(const std::vector<T>& v)
+{
+ for (S32 i = 0; i < llmin((S32)v.size(), 4); ++i)
+ {
+ mV[i] = v[i];
+ }
+
+ return *this;
+}
+
+template<typename T>
+void LLColor4::write(std::vector<T>& v) const
+{
+ for (int i = 0; i < llmin((S32)v.size(), 4); ++i)
+ {
+ v[i] = mV[i];
+ }
+}
+
#endif