Merge branch 'DRTVWR-561-maint-O' of https://bitbucket.org/lindenlab/viewer

meow-7.2.2
Ansariel 2022-07-01 11:36:10 +02:00
commit d5501d459b
21 changed files with 575 additions and 108 deletions

View File

@ -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
@ -114,6 +116,7 @@ add_library (llmath ${llmath_SOURCE_FILES})
target_link_libraries(llmath
${LLCOMMON_LIBRARIES}
${LLMESHOPTIMIZER_LIBRARIES}
)
# Add tests

View File

@ -49,6 +49,7 @@
#include "llsdserialize.h"
#include "llvector4a.h"
#include "llmatrix4a.h"
#include "llmeshoptimizer.h"
#include "lltimer.h"
#define DEBUG_SILHOUETTE_BINORMALS 0
@ -5021,6 +5022,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;

View File

@ -905,6 +905,10 @@ 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();

View File

@ -28,6 +28,9 @@
#include "meshoptimizer.h"
#include "llmath.h"
#include "v2math.h"
LLMeshOptimizer::LLMeshOptimizer()
{
// Todo: Looks like for memory management, we can add allocator and deallocator callbacks
@ -40,24 +43,218 @@ LLMeshOptimizer::~LLMeshOptimizer()
}
//static
void LLMeshOptimizer::generateShadowIndexBuffer(U16 *destination,
const U16 *indices,
void LLMeshOptimizer::generateShadowIndexBufferU32(U32 *destination,
const U32 *indices,
U64 index_count,
const LLVector4a *vertex_positions,
U64 vertex_count,
U64 vertex_positions_stride
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count
)
{
meshopt_generateShadowIndexBuffer<unsigned short>(destination,
meshopt_Stream streams[3];
S32 index = 0;
if (vertex_positions)
{
streams[index].data = (const float*)vertex_positions;
// Despite being LLVector4a, only x, y and z are in use
streams[index].size = sizeof(F32) * 3;
streams[index].stride = sizeof(F32) * 4;
index++;
}
if (normals)
{
streams[index].data = (const float*)normals;
streams[index].size = sizeof(F32) * 3;
streams[index].stride = sizeof(F32) * 4;
index++;
}
if (text_coords)
{
streams[index].data = (const float*)text_coords;
streams[index].size = sizeof(F32) * 2;
streams[index].stride = sizeof(F32) * 2;
index++;
}
if (index == 0)
{
// invalid
return;
}
meshopt_generateShadowIndexBufferMulti<unsigned int>(destination,
indices,
index_count,
(const float*)vertex_positions, // verify that it is correct to convert to float
vertex_count,
sizeof(LLVector4a),
vertex_positions_stride
streams,
index
);
}
//static
void LLMeshOptimizer::generateShadowIndexBufferU16(U16 *destination,
const U16 *indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count
)
{
meshopt_Stream streams[3];
S32 index = 0;
if (vertex_positions)
{
streams[index].data = (const float*)vertex_positions;
streams[index].size = sizeof(F32) * 3;
streams[index].stride = sizeof(F32) * 4;
index++;
}
if (normals)
{
streams[index].data = (const float*)normals;
streams[index].size = sizeof(F32) * 3;
streams[index].stride = sizeof(F32) * 4;
index++;
}
if (text_coords)
{
streams[index].data = (const float*)text_coords;
streams[index].size = sizeof(F32) * 2;
streams[index].stride = sizeof(F32) * 2;
index++;
}
if (index == 0)
{
// invalid
return;
}
meshopt_generateShadowIndexBufferMulti<unsigned short>(destination,
indices,
index_count,
vertex_count,
streams,
index);
}
void LLMeshOptimizer::optimizeVertexCacheU32(U32 * destination, const U32 * indices, U64 index_count, U64 vertex_count)
{
meshopt_optimizeVertexCache<unsigned int>(destination, indices, index_count, vertex_count);
}
void LLMeshOptimizer::optimizeVertexCacheU16(U16 * destination, const U16 * indices, U64 index_count, U64 vertex_count)
{
meshopt_optimizeVertexCache<unsigned short>(destination, indices, index_count, vertex_count);
}
size_t LLMeshOptimizer::generateRemapMultiU32(
unsigned int* remap,
const U32 * indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count)
{
meshopt_Stream streams[] = {
{(const float*)vertex_positions, sizeof(F32) * 3, sizeof(F32) * 4},
{(const float*)normals, sizeof(F32) * 3, sizeof(F32) * 4},
{(const float*)text_coords, sizeof(F32) * 2, sizeof(F32) * 2},
};
// Remap can function without indices,
// but providing indices helps with removing unused vertices
U64 indeces_cmp = indices ? index_count : vertex_count;
// meshopt_generateVertexRemapMulti will throw an assert if (indices[i] >= vertex_count)
return meshopt_generateVertexRemapMulti(&remap[0], indices, indeces_cmp, vertex_count, streams, sizeof(streams) / sizeof(streams[0]));
}
size_t LLMeshOptimizer::generateRemapMultiU16(
unsigned int* remap,
const U16 * indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count)
{
S32 out_of_range_count = 0;
U32* indices_u32 = NULL;
if (indices)
{
indices_u32 = (U32*)ll_aligned_malloc_32(index_count * sizeof(U32));
for (U64 i = 0; i < index_count; i++)
{
if (indices[i] < vertex_count)
{
indices_u32[i] = (U32)indices[i];
}
else
{
out_of_range_count++;
indices_u32[i] = 0;
}
}
}
if (out_of_range_count)
{
LL_WARNS() << out_of_range_count << " indices are out of range." << LL_ENDL;
}
size_t unique = generateRemapMultiU32(remap, indices_u32, index_count, vertex_positions, normals, text_coords, vertex_count);
ll_aligned_free_32(indices_u32);
return unique;
}
void LLMeshOptimizer::remapIndexBufferU32(U32 * destination_indices,
const U32 * indices,
U64 index_count,
const unsigned int* remap)
{
meshopt_remapIndexBuffer<unsigned int>(destination_indices, indices, index_count, remap);
}
void LLMeshOptimizer::remapIndexBufferU16(U16 * destination_indices,
const U16 * indices,
U64 index_count,
const unsigned int* remap)
{
meshopt_remapIndexBuffer<unsigned short>(destination_indices, indices, index_count, remap);
}
void LLMeshOptimizer::remapPositionsBuffer(LLVector4a * destination_vertices,
const LLVector4a * vertex_positions,
U64 vertex_count,
const unsigned int* remap)
{
meshopt_remapVertexBuffer((float*)destination_vertices, (const float*)vertex_positions, vertex_count, sizeof(LLVector4a), remap);
}
void LLMeshOptimizer::remapNormalsBuffer(LLVector4a * destination_normalss,
const LLVector4a * normals,
U64 mormals_count,
const unsigned int* remap)
{
meshopt_remapVertexBuffer((float*)destination_normalss, (const float*)normals, mormals_count, sizeof(LLVector4a), remap);
}
void LLMeshOptimizer::remapUVBuffer(LLVector2 * destination_uvs,
const LLVector2 * uv_positions,
U64 uv_count,
const unsigned int* remap)
{
meshopt_remapVertexBuffer((float*)destination_uvs, (const float*)uv_positions, uv_count, sizeof(LLVector2), remap);
}
//static
U64 LLMeshOptimizer::simplifyU32(U32 *destination,
const U32 *indices,

View File

@ -28,7 +28,8 @@
#include "linden_common.h"
#include "llmath.h"
class LLVector4a;
class LLVector2;
class LLMeshOptimizer
{
@ -36,13 +37,85 @@ public:
LLMeshOptimizer();
~LLMeshOptimizer();
static void generateShadowIndexBuffer(
static void generateShadowIndexBufferU32(
U32 *destination,
const U32 *indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count);
static void generateShadowIndexBufferU16(
U16 *destination,
const U16 *indices,
U64 index_count,
const LLVector4a *vertex_positions,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count);
static void optimizeVertexCacheU32(
U32 *destination,
const U32 *indices,
U64 index_count,
U64 vertex_count);
static void optimizeVertexCacheU16(
U16 *destination,
const U16 *indices,
U64 index_count,
U64 vertex_count);
// Remap functions
// Welds indentical vertexes together.
// Removes unused vertices if indices were provided.
static size_t generateRemapMultiU32(
unsigned int* remap,
const U32 * indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count);
static size_t generateRemapMultiU16(
unsigned int* remap,
const U16 * indices,
U64 index_count,
const LLVector4a * vertex_positions,
const LLVector4a * normals,
const LLVector2 * text_coords,
U64 vertex_count);
static void remapIndexBufferU32(U32 * destination_indices,
const U32 * indices,
U64 index_count,
const unsigned int* remap);
static void remapIndexBufferU16(U16 * destination_indices,
const U16 * indices,
U64 index_count,
const unsigned int* remap);
static void remapPositionsBuffer(LLVector4a * destination_vertices,
const LLVector4a * vertex_positions,
U64 vertex_count,
U64 vertex_positions_stride);
const unsigned int* remap);
static void remapNormalsBuffer(LLVector4a * destination_normalss,
const LLVector4a * normals,
U64 mormals_count,
const unsigned int* remap);
static void remapUVBuffer(LLVector2 * destination_uvs,
const LLVector2 * uv_positions,
U64 uv_count,
const unsigned int* remap);
// Simplification
// returns amount of indices in destiantion
// sloppy engages a variant of a mechanizm that does not respect topology as much

View File

@ -2721,7 +2721,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
if (!mNoOptimize)
{
ret->optimizeVolumeFaces();
ret->remapVolumeFaces();
}
volume_faces = remainder.size();

View File

@ -110,6 +110,14 @@ void LLModel::offsetMesh( const LLVector3& pivotPoint )
}
}
void LLModel::remapVolumeFaces()
{
for (U32 i = 0; i < getNumVolumeFaces(); ++i)
{
mVolumeFaces[i].remap();
}
}
void LLModel::optimizeVolumeFaces()
{
for (U32 i = 0; i < getNumVolumeFaces(); ++i)

View File

@ -189,6 +189,7 @@ public:
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);

View File

@ -105,6 +105,7 @@ LLButton::Params::Params()
scale_image("scale_image", true),
hover_glow_amount("hover_glow_amount"),
commit_on_return("commit_on_return", true),
commit_on_capture_lost("commit_on_capture_lost", false),
display_pressed_state("display_pressed_state", true),
use_draw_context_alpha("use_draw_context_alpha", true),
badge("badge"),
@ -172,6 +173,7 @@ LLButton::LLButton(const LLButton::Params& p)
mBottomVPad(p.pad_bottom),
mHoverGlowStrength(p.hover_glow_amount),
mCommitOnReturn(p.commit_on_return),
mCommitOnCaptureLost(p.commit_on_capture_lost),
mFadeWhenDisabled(FALSE),
mForcePressedState(false),
mDisplayPressedState(p.display_pressed_state),
@ -532,6 +534,10 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
// We only handle the click if the click both started and ended within us
if( hasMouseCapture() )
{
// reset timers before focus change, to not cause
// additional commits if mCommitOnCaptureLost.
resetMouseDownTimer();
// Always release the mouse
gFocusMgr.setMouseCapture( NULL );
@ -546,8 +552,6 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
// Regardless of where mouseup occurs, handle callback
if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
resetMouseDownTimer();
// DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
// If mouseup in the widget, it's been clicked
if (pointInView(x, y))
@ -1270,6 +1274,18 @@ void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignmen
void LLButton::onMouseCaptureLost()
{
if (mCommitOnCaptureLost
&& mMouseDownTimer.getStarted())
{
if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
if (mIsToggle)
{
toggleState();
}
LLUICtrl::onCommit();
}
resetMouseDownTimer();
}

View File

@ -124,6 +124,7 @@ public:
Optional<bool> is_toggle,
scale_image,
commit_on_return,
commit_on_capture_lost,
display_pressed_state;
Optional<F32> hover_glow_amount;
@ -391,6 +392,7 @@ protected:
F32 mCurGlowStrength;
bool mCommitOnReturn;
bool mCommitOnCaptureLost;
bool mFadeWhenDisabled;
bool mForcePressedState;
bool mDisplayPressedState;

View File

@ -108,6 +108,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
up_button_params.rect = LLRect(btn_left, getRect().getHeight(), btn_right, getRect().getHeight() - spinctrl_btn_height);
up_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2));
up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2));
up_button_params.commit_on_capture_lost = true;
mUpBtn = LLUICtrlFactory::create<LLButton>(up_button_params);
addChild(mUpBtn);
@ -116,6 +117,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
down_button_params.rect = LLRect(btn_left, getRect().getHeight() - spinctrl_btn_height, btn_right, getRect().getHeight() - 2 * spinctrl_btn_height);
down_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2));
down_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2));
down_button_params.commit_on_capture_lost = true;
mDownBtn = LLUICtrlFactory::create<LLButton>(down_button_params);
addChild(mDownBtn);

View File

@ -825,7 +825,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
{
case LLModelPreview::MESH_OPTIMIZER_AUTO:
case LLModelPreview::MESH_OPTIMIZER_SLOPPY:
case LLModelPreview::MESH_OPTIMIZER_COMBINE:
case LLModelPreview::MESH_OPTIMIZER_PRECISE:
mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, mode);
break;
default:
@ -1958,7 +1958,7 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod)
S32 index = lod_source_combo->getCurrentIndex();
if (index == LLModelPreview::MESH_OPTIMIZER_AUTO
|| index == LLModelPreview::MESH_OPTIMIZER_SLOPPY
|| index == LLModelPreview::MESH_OPTIMIZER_COMBINE)
|| index == LLModelPreview::MESH_OPTIMIZER_PRECISE)
{ //rebuild LoD to update triangle counts
onLODParamCommit(lod, true);
}

View File

@ -1396,7 +1396,7 @@ void LLFloaterTools::onClickGridOptions()
{
LLFloater* floaterp = LLFloaterReg::showInstance("build_options");
// position floater next to build tools, not over
floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp));
floaterp->setShape(gFloaterView->findNeighboringPosition(this, floaterp), true);
}
// static

View File

@ -233,6 +233,7 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
}
mViewOption["show_textures"] = false;
mFMP = fmp;
mHasPivot = false;
@ -571,7 +572,7 @@ void LLModelPreview::rebuildUploadData()
std::ostringstream out;
out << "Attempting to use model index " << idx;
// <FS:Beq> better debug (watch for dangling single line else)
if(i==4)
if (i==4)
{
out << " for PHYS";
}
@ -922,7 +923,7 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
std::map<std::string, std::string> joint_alias_map;
getJointAliases(joint_alias_map);
std::array<std::string,LLModel::NUM_LODS> lod_suffix;
std::array<std::string, LLModel::NUM_LODS> lod_suffix;
for(int i=0; i < LLModel::NUM_LODS; i++)
{
lod_suffix[i] = gSavedSettings.getString(sSuffixVarNames[i]);
@ -1453,8 +1454,9 @@ void LLModelPreview::restoreNormals()
// Runs per object, but likely it is a better way to run per model+submodels
// returns a ratio of base model indices to resulting indices
// returns -1 in case of failure
F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_decimator, F32 error_threshold, bool sloppy)
F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_decimator, F32 error_threshold, eSimplificationMode simplification_mode)
{
// I. Weld faces together
// Figure out buffer size
S32 size_indices = 0;
S32 size_vertices = 0;
@ -1489,20 +1491,21 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
{
const LLVolumeFace &face = base_model->getVolumeFace(face_idx);
// vertices
// Vertices
S32 copy_bytes = face.mNumVertices * sizeof(LLVector4a);
LLVector4a::memcpyNonAliased16((F32*)(combined_positions + combined_positions_shift), (F32*)face.mPositions, copy_bytes);
// normals
// Normals
LLVector4a::memcpyNonAliased16((F32*)(combined_normals + combined_positions_shift), (F32*)face.mNormals, copy_bytes);
// tex coords
// Tex coords
copy_bytes = face.mNumVertices * sizeof(LLVector2);
memcpy((void*)(combined_tex_coords + combined_positions_shift), (void*)face.mTexCoords, copy_bytes);
combined_positions_shift += face.mNumVertices;
// indices, sadly can't do dumb memcpy for indices, need to adjust each value
// Indices
// Sadly can't do dumb memcpy for indices, need to adjust each value
for (S32 i = 0; i < face.mNumIndices; ++i)
{
U16 idx = face.mIndices[i];
@ -1513,10 +1516,42 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
indices_idx_shift += face.mNumVertices;
}
// Now that we have buffers, optimize
// II. Generate a shadow buffer if nessesary.
// Welds together vertices if possible
U32* shadow_indices = NULL;
// if MESH_OPTIMIZER_FULL, just leave as is, since generateShadowIndexBufferU32
// won't do anything new, model was remaped on a per face basis.
// Similar for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless
// since 'simplifySloppy' ignores all topology, including normals and uvs.
// Note: simplifySloppy can affect UVs significantly.
if (simplification_mode == MESH_OPTIMIZER_NO_NORMALS)
{
// strip normals, reflections should restore relatively correctly
shadow_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32));
LLMeshOptimizer::generateShadowIndexBufferU32(shadow_indices, combined_indices, size_indices, combined_positions, NULL, combined_tex_coords, size_vertices);
}
if (simplification_mode == MESH_OPTIMIZER_NO_UVS)
{
// strip uvs, can heavily affect textures
shadow_indices = (U32*)ll_aligned_malloc_32(size_indices * sizeof(U32));
LLMeshOptimizer::generateShadowIndexBufferU32(shadow_indices, combined_indices, size_indices, combined_positions, NULL, NULL, size_vertices);
}
U32* source_indices = NULL;
if (shadow_indices)
{
source_indices = shadow_indices;
}
else
{
source_indices = combined_indices;
}
// III. Simplify
S32 target_indices = 0;
F32 result_error = 0; // how far from original the model is, 1 == 100%
S32 new_indices = 0;
S32 size_new_indices = 0;
if (indices_decimator > 0)
{
@ -1526,30 +1561,30 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
{
target_indices = 3;
}
new_indices = LLMeshOptimizer::simplifyU32(
size_new_indices = LLMeshOptimizer::simplifyU32(
output_indices,
combined_indices,
source_indices,
size_indices,
combined_positions,
size_vertices,
LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX],
target_indices,
error_threshold,
sloppy,
simplification_mode == MESH_OPTIMIZER_NO_TOPOLOGY,
&result_error);
if (result_error < 0)
{
// <FS:Beq> Log these properly
// LL_WARNS() << "Negative result error from meshoptimizer for model " << target_model->mLabel
// << " target Indices: " << target_indices
// << " new Indices: " << new_indices
// << " new Indices: " << size_new_indices
// << " original count: " << size_indices << LL_ENDL;
std::ostringstream out;
out << "Negative result error from meshoptimizer for model " << target_model->mLabel
<< " target Indices: " << target_indices
<< " new Indices: " << new_indices
<< " new Indices: " << size_new_indices
<< " original count: " << size_indices ;
LL_WARNS() << out.str() << LL_ENDL;
LLFloaterModelPreview::addStringToLog(out, true);
@ -1561,7 +1596,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
std::ostringstream out;
out << "Good result error from meshoptimizer for model " << target_model->mLabel
<< " target Indices: " << target_indices
<< " new Indices: " << new_indices
<< " new Indices: " << size_new_indices
<< " original count: " << size_indices << " (result error:" << result_error << ")";
LL_DEBUGS() << out.str() << LL_ENDL;
LLFloaterModelPreview::addStringToLog(out, true);
@ -1569,17 +1604,22 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
// </FS:Beq>
}
if (new_indices < 3)
// free unused buffers
ll_aligned_free_32(combined_indices);
ll_aligned_free_32(shadow_indices);
combined_indices = NULL;
shadow_indices = NULL;
if (size_new_indices < 3)
{
// Model should have at least one visible triangle
ll_aligned_free<64>(combined_positions);
ll_aligned_free_32(output_indices);
ll_aligned_free_32(combined_indices);
return -1;
}
// repack back into individual faces
// IV. Repack back into individual faces
LLVector4a* buffer_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * size_vertices + tc_bytes_size);
LLVector4a* buffer_normals = buffer_positions + size_vertices;
@ -1610,7 +1650,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
}
// Copy relevant indices and vertices
for (S32 i = 0; i < new_indices; ++i)
for (S32 i = 0; i < size_new_indices; ++i)
{
U32 idx = output_indices[i];
@ -1634,7 +1674,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
// LL_WARNS() << "Over triangle limit. Failed to optimize in 'per object' mode, falling back to per face variant for"
// << " model " << target_model->mLabel
// << " target Indices: " << target_indices
// << " new Indices: " << new_indices
// << " new Indices: " << size_new_indices
// << " original count: " << size_indices
// << " error treshold: " << error_threshold
// << LL_ENDL;
@ -1644,20 +1684,20 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
out << "Over triangle limit. Failed to optimize in 'per object' mode, falling back to per face variant for"
<< " model " << target_model->mLabel
<< " target Indices: " << target_indices
<< " new Indices: " << new_indices
<< " new Indices: " << size_new_indices
<< " original count: " << size_indices
<< " error treshold: " << error_threshold;
LL_DEBUGS() << out.str() << LL_ENDL;
LLFloaterModelPreview::addStringToLog(out, true);
}
// U16 vertices overflow shouldn't happen, but just in case
new_indices = 0;
size_new_indices = 0;
valid_faces = 0;
for (U32 face_idx = 0; face_idx < base_model->getNumVolumeFaces(); ++face_idx)
{
genMeshOptimizerPerFace(base_model, target_model, face_idx, indices_decimator, error_threshold, false);
genMeshOptimizerPerFace(base_model, target_model, face_idx, indices_decimator, error_threshold, simplification_mode);
const LLVolumeFace &face = target_model->getVolumeFace(face_idx);
new_indices += face.mNumIndices;
size_new_indices += face.mNumIndices;
if (face.mNumIndices >= 3)
{
valid_faces++;
@ -1665,7 +1705,7 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
}
if (valid_faces)
{
return (F32)size_indices / (F32)new_indices;
return (F32)size_indices / (F32)size_new_indices;
}
else
{
@ -1735,18 +1775,17 @@ F32 LLModelPreview::genMeshOptimizerPerModel(LLModel *base_model, LLModel *targe
ll_aligned_free<64>(buffer_positions);
ll_aligned_free_32(output_indices);
ll_aligned_free_16(buffer_indices);
ll_aligned_free_32(combined_indices);
if (new_indices < 3 || valid_faces == 0)
if (size_new_indices < 3 || valid_faces == 0)
{
// Model should have at least one visible triangle
return -1;
}
return (F32)size_indices / (F32)new_indices;
return (F32)size_indices / (F32)size_new_indices;
}
F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_decimator, F32 error_threshold, bool sloppy)
F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_decimator, F32 error_threshold, eSimplificationMode simplification_mode)
{
const LLVolumeFace &face = base_model->getVolumeFace(face_idx);
S32 size_indices = face.mNumIndices;
@ -1754,14 +1793,40 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
{
return -1;
}
// todo: do not allocate per each face, add one large buffer somewhere
// faces have limited amount of indices
S32 size = (size_indices * sizeof(U16) + 0xF) & ~0xF;
U16* output = (U16*)ll_aligned_malloc_16(size);
U16* output_indices = (U16*)ll_aligned_malloc_16(size);
U16* shadow_indices = NULL;
// if MESH_OPTIMIZER_FULL, just leave as is, since generateShadowIndexBufferU32
// won't do anything new, model was remaped on a per face basis.
// Similar for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless
// since 'simplifySloppy' ignores all topology, including normals and uvs.
if (simplification_mode == MESH_OPTIMIZER_NO_NORMALS)
{
U16* shadow_indices = (U16*)ll_aligned_malloc_16(size);
LLMeshOptimizer::generateShadowIndexBufferU16(shadow_indices, face.mIndices, size_indices, face.mPositions, NULL, face.mTexCoords, face.mNumVertices);
}
if (simplification_mode == MESH_OPTIMIZER_NO_UVS)
{
U16* shadow_indices = (U16*)ll_aligned_malloc_16(size);
LLMeshOptimizer::generateShadowIndexBufferU16(shadow_indices, face.mIndices, size_indices, face.mPositions, NULL, NULL, face.mNumVertices);
}
// Don't run ShadowIndexBuffer for MESH_OPTIMIZER_NO_TOPOLOGY, it's pointless
U16* source_indices = NULL;
if (shadow_indices)
{
source_indices = shadow_indices;
}
else
{
source_indices = face.mIndices;
}
S32 target_indices = 0;
F32 result_error = 0; // how far from original the model is, 1 == 100%
S32 new_indices = 0;
S32 size_new_indices = 0;
if (indices_decimator > 0)
{
@ -1771,26 +1836,26 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
{
target_indices = 3;
}
new_indices = LLMeshOptimizer::simplify(
output,
face.mIndices,
size_new_indices = LLMeshOptimizer::simplify(
output_indices,
source_indices,
size_indices,
face.mPositions,
face.mNumVertices,
LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_VERTEX],
target_indices,
error_threshold,
sloppy,
simplification_mode == MESH_OPTIMIZER_NO_TOPOLOGY,
&result_error);
if (result_error < 0)
{
// <FS:Beq> Log these properly
// LL_WARNS() << "Negative result error from meshoptimizer for face " << face_idx
// << " of model " << target_model->mLabel
// << " target Indices: " << target_indices
// << " new Indices: " << new_indices
// << " new Indices: " << size_new_indices
// << " original count: " << size_indices
// << " error treshold: " << error_threshold
// << LL_ENDL;
@ -1798,7 +1863,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
out << "Negative result error from meshoptimizer for face " << face_idx
<< " of model " << target_model->mLabel
<< " target Indices: " << target_indices
<< " new Indices: " << new_indices
<< " new Indices: " << size_new_indices
<< " original count: " << size_indices
<< " error treshold: " << error_threshold;
LL_WARNS() << out.str() << LL_ENDL;
@ -1812,7 +1877,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
out << "Good result error from meshoptimizer for face " << face_idx
<< " of model " << target_model->mLabel
<< " target Indices: " << target_indices
<< " new Indices: " << new_indices
<< " new Indices: " << size_new_indices
<< " original count: " << size_indices
<< " error treshold: " << error_threshold << " (result error:" << result_error << ")";
LL_DEBUGS("MeshUpload") << out.str() << LL_ENDL;
@ -1826,10 +1891,9 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
// Copy old values
new_face = face;
if (new_indices < 3)
if (size_new_indices < 3)
{
if (!sloppy)
if (simplification_mode != MESH_OPTIMIZER_NO_TOPOLOGY)
{
// meshopt_optimizeSloppy() can optimize triangles away even if target_indices is > 2,
// but optimize() isn't supposed to
@ -1861,23 +1925,24 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
else
{
// Assign new values
new_face.resizeIndices(new_indices); // will wipe out mIndices, so new_face can't substitute output
S32 idx_size = (new_indices * sizeof(U16) + 0xF) & ~0xF;
LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output, idx_size);
new_face.resizeIndices(size_new_indices); // will wipe out mIndices, so new_face can't substitute output
S32 idx_size = (size_new_indices * sizeof(U16) + 0xF) & ~0xF;
LLVector4a::memcpyNonAliased16((F32*)new_face.mIndices, (F32*)output_indices, idx_size);
// clear unused values
// Clear unused values
new_face.optimize();
}
ll_aligned_free_16(output);
ll_aligned_free_16(output_indices);
ll_aligned_free_16(shadow_indices);
if (new_indices < 3)
if (size_new_indices < 3)
{
// At least one triangle is needed
return -1;
}
return (F32)size_indices / (F32)new_indices;
return (F32)size_indices / (F32)size_new_indices;
}
void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 decimation, bool enforce_tri_limit)
@ -2008,7 +2073,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
// std::string name = base->mLabel + getLodSuffix(lod);
std::string name = stripSuffix(base->mLabel);
std::string suffix = getLodSuffix(lod);
if ( suffix.size() > 0 )
if (suffix.size() > 0)
{
name += suffix;
}
@ -2024,16 +2089,19 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
// Ideally this should run not per model,
// but combine all submodels with origin model as well
if (model_meshopt_mode == MESH_OPTIMIZER_COMBINE)
if (model_meshopt_mode == MESH_OPTIMIZER_PRECISE)
{
// Run meshoptimizer for each model/object, up to 8 faces in one model.
// Ideally this should run not per model,
// but combine all submodels with origin model as well
F32 res = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false);
if (res < 0)
// Run meshoptimizer for each face
for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx)
{
target_model->copyVolumeFaces(base);
F32 res = genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
if (res < 0)
{
// Mesh optimizer failed and returned an invalid model
const LLVolumeFace &face = base->getVolumeFace(face_idx);
LLVolumeFace &new_face = target_model->getVolumeFace(face_idx);
new_face = face;
}
}
}
@ -2042,19 +2110,29 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
// Run meshoptimizer for each face
for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx)
{
if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, true) < 0)
if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY) < 0)
{
// Sloppy failed and returned an invalid model
genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, false);
genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
}
}
}
if (model_meshopt_mode == MESH_OPTIMIZER_AUTO)
{
// Switches between 'combine' method and 'sloppy' based on combine's result.
F32 allowed_ratio_drift = 2.f;
F32 precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false);
// Remove progressively more data if we can't reach the target.
F32 allowed_ratio_drift = 1.8f;
F32 precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator))
{
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_NORMALS);
}
if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator))
{
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_UVS);
}
if (precise_ratio < 0 || (precise_ratio * allowed_ratio_drift < indices_decimator))
{
@ -2062,10 +2140,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
// Sloppy variant can fail entirely and has issues with precision,
// so code needs to do multiple attempts with different decimators.
// Todo: this is a bit of a mess, needs to be refined and improved
F32 last_working_decimator = 0.f;
F32 last_working_ratio = F32_MAX;
F32 sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true);
F32 sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY);
if (sloppy_ratio > 0)
{
@ -2088,13 +2167,13 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
// side due to overal lack of precision, and we don't need an ideal result, which
// likely does not exist, just a better one, so a partial correction is enough.
F32 sloppy_decimator = indices_decimator * (indices_decimator / sloppy_ratio + 1) / 2;
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, true);
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY);
}
if (last_working_decimator > 0 && sloppy_ratio < last_working_ratio)
{
// Compensation didn't work, return back to previous decimator
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, true);
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY);
}
if (sloppy_ratio < 0)
@ -2132,7 +2211,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
&& sloppy_decimator > precise_ratio
&& sloppy_decimator > 1)// precise_ratio isn't supposed to be below 1, but check just in case
{
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, true);
sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY);
sloppy_decimator = sloppy_decimator / sloppy_decimation_step;
}
}
@ -2152,7 +2231,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
else
{
// Fallback to normal method
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, false);
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
}
// <FS:Beq> Log stuff properly
// LL_INFOS() << "Model " << target_model->getName()
@ -2376,7 +2455,6 @@ void LLModelPreview::updateStatusMessages()
{//check for degenerate triangles in physics mesh
U32 lod = LLModel::LOD_PHYSICS;
const LLVector4a scale(0.5f);
for (U32 i = 0; i < mModel[lod].size() && !mHasDegenerate; ++i)
{ //for each model in the lod
if (mModel[lod][i] && mModel[lod][i]->mPhysics.mHull.empty())
@ -2862,7 +2940,7 @@ void LLModelPreview::updateStatusMessages()
if (phys_tris || phys_hulls > 0)
{
fmp->childEnable("Decompose");
fmp->childEnable("Decompose");
}
}
else

View File

@ -126,7 +126,7 @@ public:
{
LOD_FROM_FILE = 0,
MESH_OPTIMIZER_AUTO, // automatically selects method based on model or face
MESH_OPTIMIZER_COMBINE, // combines faces into a single model, simplifies, then splits back into faces
MESH_OPTIMIZER_PRECISE, // combines faces into a single model, simplifies, then splits back into faces
MESH_OPTIMIZER_SLOPPY, // uses sloppy method, works per face
USE_LOD_ABOVE,
} eLoDMode;
@ -227,13 +227,21 @@ private:
// Count amount of original models, excluding sub-models
static U32 countRootModels(LLModelLoader::model_list models);
typedef enum
{
MESH_OPTIMIZER_FULL,
MESH_OPTIMIZER_NO_NORMALS,
MESH_OPTIMIZER_NO_UVS,
MESH_OPTIMIZER_NO_TOPOLOGY,
} eSimplificationMode;
// Merges faces into single mesh, simplifies using mesh optimizer,
// then splits back into faces.
// Returns reached simplification ratio. -1 in case of a failure.
F32 genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_ratio, F32 error_threshold, bool sloppy);
F32 genMeshOptimizerPerModel(LLModel *base_model, LLModel *target_model, F32 indices_ratio, F32 error_threshold, eSimplificationMode simplification_mode);
// Simplifies specified face using mesh optimizer.
// Returns reached simplification ratio. -1 in case of a failure.
F32 genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_ratio, F32 error_threshold, bool sloppy);
F32 genMeshOptimizerPerFace(LLModel *base_model, LLModel *target_model, U32 face_idx, F32 indices_ratio, F32 error_threshold, eSimplificationMode simplification_mode);
protected:
friend class LLModelLoader;

View File

@ -1439,7 +1439,8 @@ void LLPanelEditWearable::changeCamera(U8 subpart)
gMorphView->setCameraOffset( subpart_entry->mCameraOffset );
if (gSavedSettings.getBOOL("AppearanceCameraMovement"))
{
gMorphView->updateCamera();
gAgentCamera.setFocusOnAvatar(FALSE, FALSE);
gMorphView->updateCamera();
}
}

View File

@ -3374,6 +3374,33 @@ void handle_object_touch()
send_ObjectDeGrab_message(object, pick);
}
void handle_object_show_original()
{
LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
if (!object)
{
return;
}
LLViewerObject *parent = (LLViewerObject*)object->getParent();
while (parent)
{
if(parent->isAvatar())
{
break;
}
object = parent;
parent = (LLViewerObject*)parent->getParent();
}
if (!object || object->isAvatar())
{
return;
}
show_item_original(object->getAttachmentItemID());
}
static void init_default_item_label(const std::string& item_name)
{
@ -12268,6 +12295,7 @@ void initialize_menus()
// Object pie menu
view_listener_t::addMenu(new LLObjectBuild(), "Object.Build");
commit.add("Object.Touch", boost::bind(&handle_object_touch));
commit.add("Object.ShowOriginal", boost::bind(&handle_object_show_original));
commit.add("Object.SitOrStand", boost::bind(&handle_object_sit_or_stand));
commit.add("Object.Delete", boost::bind(&handle_object_delete));
view_listener_t::addMenu(new LLObjectAttachToAvatar(true), "Object.AttachToAvatar");

View File

@ -109,7 +109,6 @@
#include "llfilepicker.h"
#include "llfirstuse.h"
#include "llfloater.h"
#include "llfloaterbuildoptions.h"
#include "llfloaterbuyland.h"
#include "llfloatercamera.h"
#include "llfloaterland.h"

View File

@ -4147,7 +4147,7 @@ void LLPipeline::postSort(LLCamera& camera)
}
LL_PUSH_CALLSTACKS();
// If managing your telehub, draw beacons at telehub and currently selected spawnpoint.
if (LLFloaterTelehub::renderBeacons())
if (LLFloaterTelehub::renderBeacons() && !sShadowRender)
{
LLFloaterTelehub::addBeacons();
}

View File

@ -13,16 +13,13 @@
function="Object.EnableTouch"
name="EnableTouch"/>
</menu_item_call>
<!--menu_item_call
label="Stand Up"
<menu_item_call
label="Show in inventory"
layout="topleft"
name="Stand Up">
<menu_item_call.on_click
function="Self.StandUp"
parameter="" />
<menu_item_call.on_enable
function="Self.EnableStandUp" />
</menu_item_call-->
name="Show original">
<menu_item_call.on_click
function="Object.ShowOriginal" />
</menu_item_call>
<menu_item_call
enabled="false"
label="Edit"

View File

@ -95,7 +95,12 @@
function="Object.EnableExport" />
</pie_slice>
</pie_menu>
<pie_separator/>
<pie_slice
label="Show in Inv."
name="Show original">
<pie_slice.on_click
function="Object.ShowOriginal" />
</pie_slice>
<pie_slice
label="Inspect"
name="Avatar Inspect">