Merge branch 'DRTVWR-561-maint-O' of https://bitbucket.org/lindenlab/viewer
commit
d5501d459b
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2721,7 +2721,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& mo
|
|||
|
||||
if (!mNoOptimize)
|
||||
{
|
||||
ret->optimizeVolumeFaces();
|
||||
ret->remapVolumeFaces();
|
||||
}
|
||||
|
||||
volume_faces = remainder.size();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
Loading…
Reference in New Issue