From 66235797ab3dfdb77ffa796b7c1a8b6a30fe3a25 Mon Sep 17 00:00:00 2001 From: leviathan Date: Thu, 12 Feb 2026 00:12:47 +0000 Subject: [PATCH 1/7] resolve mesh face_count discrepancy with server (#853 #1901) --- indra/llmath/llvolume.cpp | 27 +- indra/llmath/llvolume.h | 10 +- indra/llprimitive/llprimitive.cpp | 703 +++++++++++------------- indra/llprimitive/llprimitive.h | 15 +- indra/llprimitive/llprimtexturelist.cpp | 44 +- indra/llprimitive/llprimtexturelist.h | 9 +- indra/newview/llface.cpp | 2 +- indra/newview/llvovolume.cpp | 39 +- 8 files changed, 397 insertions(+), 452 deletions(-) diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index c74ea3e42b4..f137ab4ebff 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -609,7 +609,7 @@ LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, bool flat, F3 mProfile[i] = pt[j--]; } - for (S32 i=0;i<(S32)mFaces.size();i++) + for (U8 i=0;imFaces.size(); + return mIsMeshAssetLoaded ? getNumVolumeFaces() : static_cast(mProfilep->mFaces.size()); } @@ -2785,7 +2784,7 @@ void LLVolume::createVolumeFaces() } else { - S32 num_faces = getNumFaces(); + U8 num_faces = getNumFaces(); bool partial_build = true; if (num_faces != mVolumeFaces.size()) { @@ -2793,7 +2792,7 @@ void LLVolume::createVolumeFaces() mVolumeFaces.resize(num_faces); } // Initialize volume faces with parameter data - for (S32 i = 0; i < (S32)mVolumeFaces.size(); i++) + for (U8 i = 0; i < mVolumeFaces.size(); i++) { LLVolumeFace& vf = mVolumeFaces[i]; LLProfile::Face& face = mProfilep->mFaces[i]; @@ -3714,7 +3713,7 @@ S32 LLVolume::getNumTriangles(S32* vcount) const U32 triangle_count = 0; U32 vertex_count = 0; - for (S32 i = 0; i < getNumVolumeFaces(); ++i) + for (U8 i = 0; i < getNumVolumeFaces(); ++i) { const LLVolumeFace& face = getVolumeFace(i); triangle_count += face.mNumIndices/3; @@ -4227,7 +4226,7 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en if (face == -1) // ALL_SIDES { start_face = 0; - end_face = getNumVolumeFaces() - 1; + end_face = static_cast(getNumVolumeFaces()) - 1; } else { @@ -4240,7 +4239,7 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en F32 closest_t = 2.f; // must be larger than 1 - end_face = llmin(end_face, getNumVolumeFaces()-1); + end_face = llmin(end_face, static_cast(getNumVolumeFaces())-1); for (S32 i = start_face; i <= end_face; i++) { @@ -4792,7 +4791,7 @@ LLFaceID LLVolume::generateFaceMask() bool LLVolume::isFaceMaskValid(LLFaceID face_mask) { LLFaceID test_mask = 0; - for(S32 i = 0; i < getNumFaces(); i++) + for(U8 i = 0; i < getNumFaces(); i++) { test_mask |= mProfilep->mFaces[i].mFaceID; } diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 3496928f7bb..eaf66266206 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -62,7 +62,7 @@ class LLVolumeOctree; //============================================================================ -constexpr S32 MIN_DETAIL_FACES = 6; +constexpr U8 MIN_DETAIL_FACES = 6; constexpr S32 MIN_LOD = 0; constexpr S32 MAX_LOD = 3; @@ -201,7 +201,7 @@ constexpr U8 LL_SCULPT_FLAG_INVERT = 64; constexpr U8 LL_SCULPT_FLAG_MIRROR = 128; constexpr U8 LL_SCULPT_FLAG_MASK = LL_SCULPT_FLAG_INVERT | LL_SCULPT_FLAG_MIRROR; -constexpr S32 LL_SCULPT_MESH_MAX_FACES = 8; +constexpr U8 LL_SCULPT_MESH_MAX_FACES = 8; extern bool gDebugGL; @@ -699,7 +699,7 @@ class LLProfile S32 getTotal() const { return mTotal; } S32 getTotalOut() const { return mTotalOut; } // Total number of outside points - bool isFlat(S32 face) const { return (mFaces[face].mCount == 2); } + bool isFlat(U8 face) const { return (mFaces[face].mCount == 2); } bool isOpen() const { return mOpen; } void setDirty() { mDirty = true; } @@ -1027,8 +1027,8 @@ class LLVolume : public LLRefCount U8 getProfileType() const { return mParams.getProfileParams().getCurveType(); } U8 getPathType() const { return mParams.getPathParams().getCurveType(); } - S32 getNumFaces() const; - S32 getNumVolumeFaces() const { return static_cast(mVolumeFaces.size()); } + U8 getNumFaces() const; + U8 getNumVolumeFaces() const { return static_cast(mVolumeFaces.size()); } F32 getDetail() const { return mDetail; } F32 getSurfaceArea() const { return mSurfaceArea; } const LLVolumeParams& getParams() const { return mParams; } diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index c5d6076b984..7752a036581 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -186,7 +186,6 @@ bool LLPrimitive::cleanupVolumeManager() //=============================================================== LLPrimitive::LLPrimitive() : mTextureList(), - mNumTEs(0), mMiscFlags(0), mNumBumpmapTEs(0) { @@ -741,17 +740,8 @@ std::string LLPrimitive::pCodeToString(const LLPCode pcode) void LLPrimitive::copyTEs(const LLPrimitive *primitivep) { - U32 i; - if (primitivep->getExpectedNumTEs() != getExpectedNumTEs()) - { - LL_WARNS() << "Primitives don't have same expected number of TE's" << LL_ENDL; - } - U32 num_tes = llmin(primitivep->getExpectedNumTEs(), getExpectedNumTEs()); - if (mTextureList.size() < getExpectedNumTEs()) - { - mTextureList.setSize(getExpectedNumTEs()); - } - for (i = 0; i < num_tes; i++) + U8 num_tes = llmin(getNumTEs(), primitivep->getNumTEs()); + for (U8 i = 0; i < num_tes; i++) { mTextureList.copyTexture(i, *(primitivep->getTE(i))); } @@ -813,7 +803,6 @@ bool LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detai setChanged(GEOMETRY); - if (!mVolumep) { mVolumep = volumep; @@ -1028,7 +1017,12 @@ bool LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detai sVolumeManager->unrefVolume(mVolumep); mVolumep = volumep; - setNumTEs(mVolumep->getNumFaces()); + if (getNumTEs() < mVolumep->getNumFaces()) + { + // Decreases in the LLVolume's notion of "num_faces" happens for LOD reasons + // which do not reflect actual change of faces of the object, so we ignore them. + setNumTEs(mVolumep->getNumFaces()); + } #endif return true; } @@ -1046,83 +1040,126 @@ bool LLPrimitive::setMaterial(U8 material) } } -S32 LLPrimitive::packTEField(U8 *cur_ptr, U8 *data_ptr, U8 data_size, U8 last_face_index, EMsgVariableType type) const +namespace { - S32 face_index; - S32 i; - U64 exception_faces; - U8 *start_loc = cur_ptr; - - htolememcpy(cur_ptr,data_ptr + (last_face_index * data_size), type, data_size); - cur_ptr += data_size; - - for (face_index = last_face_index-1; face_index >= 0; face_index--) + // packs up to 64 bits as a variable bitfield into buffer + // returns pointer to byte that follows the packed bitfield + // + U8* pack_TE_variable_bitfield(U8* buffer, U64 bits) { - bool already_sent = false; - for (i = face_index+1; i <= last_face_index; i++) - { - if (!memcmp(data_ptr+(data_size *face_index), data_ptr+(data_size *i), data_size)) - { - already_sent = true; - break; - } - } + // The bits are chunked into 7 per packed byte. The 8th bit of each byte is used + // as a continuation flag: all bytes will have the 8th bit set (1) except for the + // last one which will be unset (0). - if (!already_sent) + if (bits >= ((U64)0x1 << 7)) { - exception_faces = 0; - for (i = face_index; i >= 0; i--) + if (bits >= ((U64)0x1 << 14)) { - if (!memcmp(data_ptr+(data_size *face_index), data_ptr+(data_size *i), data_size)) + if (bits >= ((U64)0x1 << 21)) { - exception_faces |= ((U64)1 << i); - } - } - - //assign exception faces to cur_ptr - if (exception_faces >= ((U64)0x1 << 7)) - { - if (exception_faces >= ((U64)0x1 << 14)) - { - if (exception_faces >= ((U64)0x1 << 21)) + if (bits >= ((U64)0x1 << 28)) { - if (exception_faces >= ((U64)0x1 << 28)) + if (bits >= ((U64)0x1 << 35)) { - if (exception_faces >= ((U64)0x1 << 35)) + if (bits >= ((U64)0x1 << 42)) { - if (exception_faces >= ((U64)0x1 << 42)) + if (bits >= ((U64)0x1 << 49)) { - if (exception_faces >= ((U64)0x1 << 49)) - { - *cur_ptr++ = (U8)(((exception_faces >> 49) & 0x7F) | 0x80); - } - *cur_ptr++ = (U8)(((exception_faces >> 42) & 0x7F) | 0x80); + *buffer++ = (U8)(((bits >> 49) & 0x7F) | 0x80); } - *cur_ptr++ = (U8)(((exception_faces >> 35) & 0x7F) | 0x80); + *buffer++ = (U8)(((bits >> 42) & 0x7F) | 0x80); } - *cur_ptr++ = (U8)(((exception_faces >> 28) & 0x7F) | 0x80); + *buffer++ = (U8)(((bits >> 35) & 0x7F) | 0x80); } - *cur_ptr++ = (U8)(((exception_faces >> 21) & 0x7F) | 0x80); + *buffer++ = (U8)(((bits >> 28) & 0x7F) | 0x80); } - *cur_ptr++ = (U8)(((exception_faces >> 14) & 0x7F) | 0x80); + *buffer++ = (U8)(((bits >> 21) & 0x7F) | 0x80); } - *cur_ptr++ = (U8)(((exception_faces >> 7) & 0x7F) | 0x80); + *buffer++ = (U8)(((bits >> 14) & 0x7F) | 0x80); } + *buffer++ = (U8)(((bits >> 7) & 0x7F) | 0x80); + } + *buffer++ = (U8)(bits & 0x7F); // don't set the continuation bit + return buffer; + } + // packs TextureEntry data encoded for wire transport + S32 pack_TE_field(U8 *cur_ptr, U8 *data_ptr, U8 data_size, U8 last_face_index, EMsgVariableType type) + { + U8 *start_loc = cur_ptr; - *cur_ptr++ = (U8)(exception_faces & 0x7F); + // pack the value at last_face_index as the "default value" which will be assigned to all faces + // that are not flagged as exceptions + htolememcpy(cur_ptr, data_ptr + (last_face_index * data_size), type, data_size); + cur_ptr += data_size; - htolememcpy(cur_ptr,data_ptr + (face_index * data_size), type, data_size); - cur_ptr += data_size; + // decrementing toward face_index=0 find all unique non-default values and bundle them together + // (variable_bitfield + value) + U64 packed_mask = ((U64)1 << last_face_index); + S32 max_index = static_cast(last_face_index); + for (S32 face_index = max_index-1; face_index >= 0; face_index--) + { + bool already_sent = packed_mask & ((U64)1 << face_index); + if (!already_sent) + { + // exception_faces is a bitmask of indices whose TE field is set to 'value' + U64 exception_faces = 0; + for (S32 i = face_index; i >= 0; i--) + { + if (!memcmp(data_ptr+(data_size *face_index), data_ptr+(data_size *i), data_size)) + { + exception_faces |= ((U64)1 << i); + } + } + + // pack the bitfield + cur_ptr = pack_TE_variable_bitfield(cur_ptr, exception_faces); + + // pack the 'value' + htolememcpy(cur_ptr, data_ptr + (face_index * data_size), type, data_size); + cur_ptr += data_size; + + packed_mask |= exception_faces; + } } + return (S32)(cur_ptr - start_loc); } - return (S32)(cur_ptr - start_loc); -} -namespace -{ + // packs the default value of a 1-byte TextureEntry field in a way that allows the + // receiving side to unambiguously determine the "number of faces" for the TextureEntry + S32 pack_TE_facecount(U8 *cur_ptr, U8 *data_ptr, U8 last_face_index, EMsgVariableType type) + { + // BUG: There can be a discrepancy in last_face_index as known to client and server + // (for mesh objects). + // + // WORKAROUND: We can inform the other side of our notion of last_face_index by + // re-packing the TE value of the last_face_index as an exception to the default. + // This is effectively a no-op since the value at last_face_index is, by protocol + // definition, the default (see implementation of the packing/unpacking algorithm + // in pack_TE_field() or unpack_TE_field() for details), however the receiving + // side will notice and interpret this as a declaration the number of faces. + // + // The workaround need only be packed once for any particular update, so it is + // always done for a TE field whose data_size is 1 byte. + // + U8 data_size = 1; + + U8 *start_loc = cur_ptr; + + // store a variable bitfield whose only bit corresponds to last_face_index + U64 bits = (U64)1 << last_face_index; + cur_ptr = pack_TE_variable_bitfield(cur_ptr, bits); + + // pack the value + htolememcpy(cur_ptr, data_ptr + (last_face_index * data_size), type, data_size); + cur_ptr += data_size; + + return (S32)(cur_ptr - start_loc); + } + + // unpacks TextureEntry data encoded for wire transport template< typename T > - bool unpack_TEField(T dest[], U8 dest_count, U8 * &source, U8 *source_end, EMsgVariableType type) + bool unpack_TE_field(T dest[], U8& min_num_faces, U8 * &source, U8 *source_end, EMsgVariableType type) { const size_t size(sizeof(T)); @@ -1134,21 +1171,25 @@ namespace return false; } - // Extract the default value and fill the array. + // Extract the first value and fill the array with this "default value" + // Note: this corresponds to the property of the last face (client packs it first) htolememcpy(dest, source, type, size); source += size; - for (S32 idx = 1; idx < dest_count; ++idx) + for (U8 idx = 1; idx < LLTEContents::MAX_TES; ++idx) { dest[idx] = dest[0]; } while (source < source_end) { + // Unpack the variable length bitfield: + // The flags are stored in the lower 7 bits of each byte while the 8th bit + // signals continuation: the final byte of the flags will have a zero 8th bit. + // Meanwhile each bit in the bitfield represents whether the subsequent value + // will be placed at the corresponding index of the 'dest' array. U64 index_flags(0); U8 sbit(0); - - // Unpack the variable length bitfield. Each bit represents whether the following - // value will be placed at the corresponding array index. + U8 highest_flagged_index = 0; do { if (source >= source_end) @@ -1158,9 +1199,27 @@ namespace return false; } + // get ready to load the next 7 bits + index_flags <<= 7; + if (highest_flagged_index) + { + highest_flagged_index += 7; + } + + // copy 7 bits sbit = *source++; - index_flags <<= 7; // original code had this after? - index_flags |= (sbit & 0x7F); + U8 flags = (sbit & 0x7F); + index_flags |= flags; + + if (flags && ! highest_flagged_index) + { + // initialize highest_flagged_index + while (flags > 1) + { + highest_flagged_index++; + flags >>= 1; + } + } } while (sbit & 0x80); if (!index_flags) @@ -1176,12 +1235,13 @@ namespace return false; } - // get the value for the indexs. + // After unpacking the bitfield get the value. T value; htolememcpy(&value, source, type, size); source += size; - for (S32 idx = 0; idx < dest_count; idx++) + // Store the value in the array at the flagged indices + for (U8 idx = 0; idx <= highest_flagged_index; idx++) { if (index_flags & 1ULL << idx) { @@ -1189,6 +1249,11 @@ namespace } } + // remember final_max index so we can add TEs if necessary + if (highest_flagged_index >= min_num_faces) + { + min_num_faces = highest_flagged_index + 1; + } } return true; } @@ -1201,180 +1266,29 @@ namespace // Includes information about image ID, color, scale S,T, offset S,T and rotation bool LLPrimitive::packTEMessage(LLMessageSystem *mesgsys) const { - const U32 MAX_TES = 45; - - U8 image_ids[MAX_TES*16]; - U8 colors[MAX_TES*4]; - F32 scale_s[MAX_TES]; - F32 scale_t[MAX_TES]; - S16 offset_s[MAX_TES]; - S16 offset_t[MAX_TES]; - S16 image_rot[MAX_TES]; - U8 bump[MAX_TES]; - U8 media_flags[MAX_TES]; - U8 glow[MAX_TES]; - U8 material_data[MAX_TES*16]; - const U32 MAX_TE_BUFFER = 4096; U8 packed_buffer[MAX_TE_BUFFER]; U8 *cur_ptr = packed_buffer; - S32 last_face_index = llmin((U32) getNumTEs(), MAX_TES) - 1; - - if (last_face_index > -1) - { - // ...if we hit the front, send one image id - S8 face_index; - LLColor4U coloru; - for (face_index = 0; face_index <= last_face_index; face_index++) - { - // Directly sending image_ids is not safe! - memcpy(&image_ids[face_index*16],getTE(face_index)->getID().mData,16); /* Flawfinder: ignore */ - - // Cast LLColor4 to LLColor4U - coloru.setVec( getTE(face_index)->getColor() ); - - // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f) - // as all zeros. However, the subtraction and addition must be done in unsigned - // byte space, not in float space, otherwise off-by-one errors occur. JC - colors[4*face_index] = 255 - coloru.mV[0]; - colors[4*face_index + 1] = 255 - coloru.mV[1]; - colors[4*face_index + 2] = 255 - coloru.mV[2]; - colors[4*face_index + 3] = 255 - coloru.mV[3]; - - const LLTextureEntry* te = getTE(face_index); - scale_s[face_index] = (F32) te->mScaleS; - scale_t[face_index] = (F32) te->mScaleT; - offset_s[face_index] = (S16) ll_round((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; - offset_t[face_index] = (S16) ll_round((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; - image_rot[face_index] = (S16) ll_round(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); - bump[face_index] = te->getBumpShinyFullbright(); - media_flags[face_index] = te->getMediaTexGen(); - glow[face_index] = (U8) ll_round((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); - - // Directly sending material_ids is not safe! - memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */ - } - - cur_ptr += packTEField(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)scale_s, 4 ,last_face_index, MVT_F32); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)scale_t, 4 ,last_face_index, MVT_F32); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)glow, 1 ,last_face_index, MVT_U8); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)material_data, 16, last_face_index, MVT_LLUUID); - } + cur_ptr = packTEMessageInternal(cur_ptr); mesgsys->addBinaryDataFast(_PREHASH_TextureEntry, packed_buffer, (S32)(cur_ptr - packed_buffer)); - return true; } bool LLPrimitive::packTEMessage(LLDataPacker &dp) const { - const U32 MAX_TES = 45; - - U8 image_ids[MAX_TES*16]; - U8 colors[MAX_TES*4]; - F32 scale_s[MAX_TES]; - F32 scale_t[MAX_TES]; - S16 offset_s[MAX_TES]; - S16 offset_t[MAX_TES]; - S16 image_rot[MAX_TES]; - U8 bump[MAX_TES]; - U8 media_flags[MAX_TES]; - U8 glow[MAX_TES]; - U8 material_data[MAX_TES*16]; - const U32 MAX_TE_BUFFER = 4096; U8 packed_buffer[MAX_TE_BUFFER]; U8 *cur_ptr = packed_buffer; - S32 last_face_index = getNumTEs() - 1; - - if (last_face_index > -1) - { - // ...if we hit the front, send one image id - S8 face_index; - LLColor4U coloru; - for (face_index = 0; face_index <= last_face_index; face_index++) - { - // Directly sending image_ids is not safe! - memcpy(&image_ids[face_index*16],getTE(face_index)->getID().mData,16); /* Flawfinder: ignore */ - - // Cast LLColor4 to LLColor4U - coloru.setVec( getTE(face_index)->getColor() ); - - // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f) - // as all zeros. However, the subtraction and addition must be done in unsigned - // byte space, not in float space, otherwise off-by-one errors occur. JC - colors[4*face_index] = 255 - coloru.mV[0]; - colors[4*face_index + 1] = 255 - coloru.mV[1]; - colors[4*face_index + 2] = 255 - coloru.mV[2]; - colors[4*face_index + 3] = 255 - coloru.mV[3]; - - const LLTextureEntry* te = getTE(face_index); - scale_s[face_index] = (F32) te->mScaleS; - scale_t[face_index] = (F32) te->mScaleT; - offset_s[face_index] = (S16) ll_round((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; - offset_t[face_index] = (S16) ll_round((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; - image_rot[face_index] = (S16) ll_round(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); - bump[face_index] = te->getBumpShinyFullbright(); - media_flags[face_index] = te->getMediaTexGen(); - glow[face_index] = (U8) ll_round((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); - - // Directly sending material_ids is not safe! - memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */ - } - - cur_ptr += packTEField(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)scale_s, 4 ,last_face_index, MVT_F32); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)scale_t, 4 ,last_face_index, MVT_F32); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)glow, 1 ,last_face_index, MVT_U8); - *cur_ptr++ = 0; - cur_ptr += packTEField(cur_ptr, (U8 *)material_data, 16, last_face_index, MVT_LLUUID); - } - + cur_ptr = packTEMessageInternal(cur_ptr); dp.packBinaryData(packed_buffer, (S32)(cur_ptr - packed_buffer), "TextureEntry"); return true; } S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec) { - S32 retval = 0; - // temp buffer for material ID processing - // data will end up in tec.material_id[] - material_id_type material_data[LLTEContents::MAX_TES]; - if (block_num < 0) { tec.size = mesgsys->getSizeFast(block_name, _PREHASH_TextureEntry); @@ -1387,7 +1301,7 @@ S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name if (tec.size == 0) { tec.face_count = 0; - return retval; + return 0; } else if (tec.size >= LLTEContents::MAX_TE_BUFFER) { @@ -1398,52 +1312,16 @@ S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name // if block_num < 0 ask for block 0 mesgsys->getBinaryDataFast(block_name, _PREHASH_TextureEntry, tec.packed_buffer, 0, std::max(block_num, 0), LLTEContents::MAX_TE_BUFFER - 1); - // The last field is not zero terminated. - // Rather than special case the upack functions. Just make it 0x00 terminated. - tec.packed_buffer[tec.size] = 0x00; - ++tec.size; - - tec.face_count = llmin((U32)getNumTEs(),(U32)LLTEContents::MAX_TES); - - U8 *cur_ptr = tec.packed_buffer; - LL_DEBUGS("TEXTUREENTRY") << "Texture Entry with buffere sized: " << tec.size << LL_ENDL; - U8 *buffer_end = tec.packed_buffer + tec.size; - - if (!( unpack_TEField(tec.image_data, tec.face_count, cur_ptr, buffer_end, MVT_LLUUID) && - unpack_TEField(tec.colors, tec.face_count, cur_ptr, buffer_end, MVT_U8) && - unpack_TEField(tec.scale_s, tec.face_count, cur_ptr, buffer_end, MVT_F32) && - unpack_TEField(tec.scale_t, tec.face_count, cur_ptr, buffer_end, MVT_F32) && - unpack_TEField(tec.offset_s, tec.face_count, cur_ptr, buffer_end, MVT_S16) && - unpack_TEField(tec.offset_t, tec.face_count, cur_ptr, buffer_end, MVT_S16) && - unpack_TEField(tec.image_rot, tec.face_count, cur_ptr, buffer_end, MVT_S16) && - unpack_TEField(tec.bump, tec.face_count, cur_ptr, buffer_end, MVT_U8) && - unpack_TEField(tec.media_flags, tec.face_count, cur_ptr, buffer_end, MVT_U8) && - unpack_TEField(tec.glow, tec.face_count, cur_ptr, buffer_end, MVT_U8))) - { - LL_WARNS("TEXTUREENTRY") << "Failure parsing Texture Entry Message due to malformed TE Field! Dropping changes on the floor. " << LL_ENDL; - return 0; - } - - if (cur_ptr >= buffer_end || !unpack_TEField(material_data, tec.face_count, cur_ptr, buffer_end, MVT_LLUUID)) - { - memset((void*)material_data, 0, sizeof(material_data)); - } - - for (U32 i = 0; i < tec.face_count; i++) - { - tec.material_ids[i].set(&(material_data[i])); - } - - retval = 1; - return retval; - } + return unpackTEMessageInternal(tec); +} S32 LLPrimitive::applyParsedTEMessage(LLTEContents& tec) { S32 retval = 0; LLColor4 color; - for (U32 i = 0; i < tec.face_count; i++) + U8 num_faces = llmin(mTextureList.size(), tec.face_count); + for (U8 i = 0; i < num_faces; i++) { LLUUID& req_id = ((LLUUID*)tec.image_data)[i]; retval |= setTETexture(i, req_id); @@ -1469,139 +1347,118 @@ S32 LLPrimitive::applyParsedTEMessage(LLTEContents& tec) return retval; } -S32 LLPrimitive::unpackTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num) +S32 LLPrimitive::unpackTEMessageInternal(LLTEContents& tec) { - LLTEContents tec; - S32 retval = parseTEMessage(mesgsys, block_name, block_num, tec); - if (!retval) - return retval; - return applyParsedTEMessage(tec); -} - -S32 LLPrimitive::unpackTEMessage(LLDataPacker &dp) -{ - // use a negative block_num to indicate a single-block read (a non-variable block) - S32 retval = 0; - constexpr U32 MAX_TES = 45; + // temp buffer for material ID processing + // data will end up in tec.material_ids[] + material_id_type material_data[LLTEContents::MAX_TES]; - // Avoid construction of 32 UUIDs per call - static LLMaterialID material_ids[MAX_TES]; + // The last field is not zero terminated. + // Rather than special case the unpack functions, just make it 0x00 terminated. + tec.packed_buffer[tec.size] = 0x00; + ++tec.size; - constexpr U32 MAX_TE_BUFFER = 4096; - U8 packed_buffer[MAX_TE_BUFFER]; - memset((void*)packed_buffer, 0, MAX_TE_BUFFER); - - LLUUID image_data[MAX_TES]; - LLColor4U colors[MAX_TES]; - F32 scale_s[MAX_TES]; - F32 scale_t[MAX_TES]; - S16 offset_s[MAX_TES]; - S16 offset_t[MAX_TES]; - S16 image_rot[MAX_TES]; - U8 bump[MAX_TES]; - U8 media_flags[MAX_TES]; - U8 glow[MAX_TES]; - material_id_type material_data[MAX_TES]; - - memset((void*)scale_s, 0, sizeof(scale_s)); - memset((void*)scale_t, 0, sizeof(scale_t)); - memset((void*)offset_s, 0, sizeof(offset_s)); - memset((void*)offset_t, 0, sizeof(offset_t)); - memset((void*)image_rot, 0, sizeof(image_rot)); - memset((void*)bump, 0, sizeof(bump)); - memset((void*)media_flags, 0, sizeof(media_flags)); - memset((void*)glow, 0, sizeof(glow)); - - S32 size; - U32 face_count = 0; - - if (!dp.unpackBinaryData(packed_buffer, size, "TextureEntry")) - { - retval = TEM_INVALID; - LL_WARNS() << "Bad texture entry block! Abort!" << LL_ENDL; - return retval; - } + tec.face_count = llmin(getNumTEs(), LLTEContents::MAX_TES); - if (size == 0) - { - return retval; - } - else if (size >= MAX_TE_BUFFER) - { - LL_WARNS("TEXTUREENTRY") << "Excessive buffer size detected in Texture Entry! Truncating." << LL_ENDL; - size = MAX_TE_BUFFER - 1; - } - - // The last field is not zero terminated. - // Rather than special case the upack functions. Just make it 0x00 terminated. - packed_buffer[size] = 0x00; - ++size; - face_count = llmin((U32) getNumTEs(), MAX_TES); - U32 i; + U8 *cur_ptr = tec.packed_buffer; + LL_DEBUGS("TEXTUREENTRY") << "Texture Entry with buffer sized: " << tec.size << LL_ENDL; + U8 *buffer_end = tec.packed_buffer + tec.size; - U8 *cur_ptr = packed_buffer; - LL_DEBUGS("TEXTUREENTRY") << "Texture Entry with buffer sized: " << size << LL_ENDL; - U8 *buffer_end = packed_buffer + size; - - if (!( unpack_TEField(image_data, face_count, cur_ptr, buffer_end, MVT_LLUUID) && - unpack_TEField(colors, face_count, cur_ptr, buffer_end, MVT_U8) && - unpack_TEField(scale_s, face_count, cur_ptr, buffer_end, MVT_F32) && - unpack_TEField(scale_t, face_count, cur_ptr, buffer_end, MVT_F32) && - unpack_TEField(offset_s, face_count, cur_ptr, buffer_end, MVT_S16) && - unpack_TEField(offset_t, face_count, cur_ptr, buffer_end, MVT_S16) && - unpack_TEField(image_rot, face_count, cur_ptr, buffer_end, MVT_S16) && - unpack_TEField(bump, face_count, cur_ptr, buffer_end, MVT_U8) && - unpack_TEField(media_flags, face_count, cur_ptr, buffer_end, MVT_U8) && - unpack_TEField(glow, face_count, cur_ptr, buffer_end, MVT_U8))) + // unpack_TE_field() will increase new_face_count according to the index flags + // it finds in the packed data. If this Primitive is a mesh and new_face_count ends + // up larger than tec.face_count we'll increase the number of TextureEntries. + U8 new_face_count = 1; + + if (!( unpack_TE_field(tec.image_data, new_face_count, cur_ptr, buffer_end, MVT_LLUUID) && + unpack_TE_field(tec.colors, new_face_count, cur_ptr, buffer_end, MVT_U8) && + unpack_TE_field(tec.scale_s, new_face_count, cur_ptr, buffer_end, MVT_F32) && + unpack_TE_field(tec.scale_t, new_face_count, cur_ptr, buffer_end, MVT_F32) && + unpack_TE_field(tec.offset_s, new_face_count, cur_ptr, buffer_end, MVT_S16) && + unpack_TE_field(tec.offset_t, new_face_count, cur_ptr, buffer_end, MVT_S16) && + unpack_TE_field(tec.image_rot, new_face_count, cur_ptr, buffer_end, MVT_S16) && + unpack_TE_field(tec.bump, new_face_count, cur_ptr, buffer_end, MVT_U8) && + unpack_TE_field(tec.media_flags, new_face_count, cur_ptr, buffer_end, MVT_U8) && + unpack_TE_field(tec.glow, new_face_count, cur_ptr, buffer_end, MVT_U8))) { LL_WARNS("TEXTUREENTRY") << "Failure parsing Texture Entry Message due to malformed TE Field! Dropping changes on the floor. " << LL_ENDL; return 0; } - if (cur_ptr >= buffer_end || !unpack_TEField(material_data, face_count, cur_ptr, buffer_end, MVT_LLUUID)) + if (cur_ptr >= buffer_end || !unpack_TE_field(material_data, new_face_count, cur_ptr, buffer_end, MVT_LLUUID)) { memset((void*)material_data, 0, sizeof(material_data)); } - for (i = 0; i < face_count; i++) + // BUG: server and client may disagree as to the face_count on a mesh Primitive. + // + // HISTORY: In the beginning face_count of a Primitive was implicit according to + // its VolumeParams hence the face_count was not encoded in TE field data. Later + // "mesh" Primitives (aka "Scuplties") were added but had only one face. Then + // non-Sculpty mesh Primitives were added with variable face_count (up to 8) and + // multiple LOD, each with a potentially different face_count, and for several + // years the mesh asset upload pipeline suffered a bug where the server-side mesh + // asset would cache the face_count of lower-LOD collision shape rather than of + // the mesh at full glory. The result was: texture adjustments to unknown mesh + // faces would silently fail at the server. There is a similar client side-problem + // when mesh objects are loaded with low LOD shapes and server updates for missing + // faces are silently dropped. + // + // WORKAROUND: face_count as known to each side can be encoded in the TE field + // data without adjusting protocol using a trick (see pack_TE_facecount() for + // details). + // + // TLDR: This is where we adjust our notion of mesh face_count when it is smaller + // than that of the other side. + if (mVolumep && mVolumep->getParams().isMeshSculpt()) { - material_ids[i].set(&(material_data[i])); + U8 num_tes = getNumTEs(); + if (num_tes < new_face_count && new_face_count <= LL_SCULPT_MESH_MAX_FACES) + { + tec.face_count = new_face_count; + setNumTEs(new_face_count); + } } - LLColor4 color; - for (i = 0; i < face_count; i++) + for (U8 i = 0; i < tec.face_count; i++) { - retval |= setTETexture(i, ((LLUUID*)image_data)[i]); - retval |= setTEScale(i, scale_s[i], scale_t[i]); - retval |= setTEOffset(i, (F32)offset_s[i] / (F32)0x7FFF, (F32) offset_t[i] / (F32) 0x7FFF); - retval |= setTERotation(i, ((F32)image_rot[i] / TEXTURE_ROTATION_PACK_FACTOR) * F_TWO_PI); - retval |= setTEBumpShinyFullbright(i, bump[i]); - retval |= setTEMediaTexGen(i, media_flags[i]); - retval |= setTEGlow(i, (F32)glow[i] / (F32)0xFF); - retval |= setTEMaterialID(i, material_ids[i]); - - // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f) - // as all zeros. However, the subtraction and addition must be done in unsigned - // byte space, not in float space, otherwise off-by-one errors occur. JC - color.mV[VRED] = F32(255 - colors[i].mV[VRED]) / 255.f; - color.mV[VGREEN] = F32(255 - colors[i].mV[VGREEN]) / 255.f; - color.mV[VBLUE] = F32(255 - colors[i].mV[VBLUE]) / 255.f; - color.mV[VALPHA] = F32(255 - colors[i].mV[VALPHA]) / 255.f; - - retval |= setTEColor(i, color); + tec.material_ids[i].set(&(material_data[i])); } - return retval; + return 1; +} + +S32 LLPrimitive::unpackTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num) +{ + LLTEContents tec; + S32 retval = parseTEMessage(mesgsys, block_name, block_num, tec); + if (!retval) + return retval; + return applyParsedTEMessage(tec); } -U8 LLPrimitive::getExpectedNumTEs() const +S32 LLPrimitive::unpackTEMessage(LLDataPacker &dp) { - U8 expected_face_count = 0; - if (mVolumep) + LLTEContents tec; + if (!dp.unpackBinaryData(tec.packed_buffer, tec.size, "TextureEntry")) + { + LL_WARNS() << "Bad texture entry block! Abort!" << LL_ENDL; + return TEM_INVALID; + } + + if (tec.size == 0) + { + return 0; + } + else if (tec.size >= LLTEContents::MAX_TE_BUFFER) { - expected_face_count = mVolumep->getNumFaces(); + LL_WARNS("TEXTUREENTRY") << "Excessive buffer size detected in Texture Entry! Truncating." << LL_ENDL; + tec.size = LLTEContents::MAX_TE_BUFFER - 1; } - return expected_face_count; + + S32 retval = unpackTEMessageInternal(tec); + if (!retval) + return retval; + return applyParsedTEMessage(tec); } void LLPrimitive::copyTextureList(const LLPrimTextureList& other_list) @@ -1634,6 +1491,94 @@ void LLPrimitive::updateNumBumpmap(const U8 index, const U8 bump) return; } + +U8* LLPrimitive::packTEMessageInternal(U8* cur_ptr) const +{ + U8 image_ids[LLTEContents::MAX_TES*16]; + U8 colors[LLTEContents::MAX_TES*4]; + F32 scale_s[LLTEContents::MAX_TES]; + F32 scale_t[LLTEContents::MAX_TES]; + S16 offset_s[LLTEContents::MAX_TES]; + S16 offset_t[LLTEContents::MAX_TES]; + S16 image_rot[LLTEContents::MAX_TES]; + U8 bump[LLTEContents::MAX_TES]; + U8 media_flags[LLTEContents::MAX_TES]; + U8 glow[LLTEContents::MAX_TES]; + U8 material_data[LLTEContents::MAX_TES*16]; + + U8 num_faces = getNumTEs(); + U8 last_face_index = llmin(getNumTEs(), LLTEContents::MAX_TES) - 1; + + if (last_face_index > -1) + { + // ...if we hit the front, send one image id + LLColor4U coloru; + for (U8 face_index = 0; face_index <= last_face_index; face_index++) + { + // Directly sending image_ids is not safe! + memcpy(&image_ids[face_index*16], getTE(face_index)->getID().mData,16); /* Flawfinder: ignore */ + + // Cast LLColor4 to LLColor4U + coloru.setVec( getTE(face_index)->getColor() ); + + // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f) + // as all zeros. However, the subtraction and addition must be done in unsigned + // byte space, not in float space, otherwise off-by-one errors occur. JC + colors[4*face_index] = 255 - coloru.mV[0]; + colors[4*face_index + 1] = 255 - coloru.mV[1]; + colors[4*face_index + 2] = 255 - coloru.mV[2]; + colors[4*face_index + 3] = 255 - coloru.mV[3]; + + const LLTextureEntry* te = getTE(face_index); + scale_s[face_index] = (F32) te->mScaleS; + scale_t[face_index] = (F32) te->mScaleT; + offset_s[face_index] = (S16) ll_round((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; + offset_t[face_index] = (S16) ll_round((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; + image_rot[face_index] = (S16) ll_round(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); + bump[face_index] = te->getBumpShinyFullbright(); + media_flags[face_index] = te->getMediaTexGen(); + glow[face_index] = (U8) ll_round((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); + + // Directly sending material_ids is not safe! + memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */ + } + + cur_ptr += pack_TE_field(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID); + *cur_ptr++ = 0; + if (mVolumep && mVolumep->getParams().isMeshSculpt()) + { + cur_ptr += pack_TE_field(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); + } + else + { + cur_ptr += pack_TE_field(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); + } + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)scale_s, 4 ,last_face_index, MVT_F32); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)scale_t, 4 ,last_face_index, MVT_F32); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8); + if (mVolumep && mVolumep->getParams().isMeshSculpt()) + { + // workaround for historical num_faces discrepancy between client and server + cur_ptr += pack_TE_facecount(cur_ptr, (U8 *)bump, last_face_index, MVT_U8); + } + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)glow, 1 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)material_data, 16, last_face_index, MVT_LLUUID); + } + return cur_ptr; +} //============================================================================ // Moved from llselectmgr.cpp diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index c3e3e19ee94..01dd4fd2829 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -402,7 +402,7 @@ class LLRenderMaterialParams : public LLNetworkData // - Vir struct LLTEContents { - static const U32 MAX_TES = 45; + static const U8 MAX_TES = 45; LLUUID image_data[MAX_TES]; LLColor4U colors[MAX_TES]; @@ -419,8 +419,8 @@ struct LLTEContents static const U32 MAX_TE_BUFFER = 4096; U8 packed_buffer[MAX_TE_BUFFER]; - U32 size; - U32 face_count; + S32 size; + U8 face_count; }; class LLPrimitive : public LLXform @@ -496,13 +496,13 @@ class LLPrimitive : public LLXform LLMaterialPtr getTEMaterialParams(const U8 index); void copyTEs(const LLPrimitive *primitive); - S32 packTEField(U8 *cur_ptr, U8 *data_ptr, U8 data_size, U8 last_face_index, EMsgVariableType type) const; bool packTEMessage(LLMessageSystem *mesgsys) const; bool packTEMessage(LLDataPacker &dp) const; S32 unpackTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num); // Variable num of blocks S32 unpackTEMessage(LLDataPacker &dp); S32 parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec); S32 applyParsedTEMessage(LLTEContents& tec); + S32 unpackTEMessageInternal(LLTEContents& tec); #ifdef CHECK_FOR_FINITE inline void setPosition(const LLVector3& pos); @@ -581,6 +581,7 @@ class LLPrimitive : public LLXform private: void updateNumBumpmap(const U8 index, const U8 bump); + U8* packTEMessageInternal(U8* cur_ptr) const; protected: LLPCode mPrimitiveCode; // Primitive code @@ -590,7 +591,6 @@ class LLPrimitive : public LLXform LLPointer mVolumep; LLPrimTextureList mTextureList; // list of texture GUIDs, scales, offsets U8 mMaterial; // Material code - U8 mNumTEs; // # of faces on the primitve U8 mNumBumpmapTEs; // number of bumpmap TEs. U32 mMiscFlags; // home for misc bools @@ -789,9 +789,10 @@ void LLPrimitive::setAcceleration(const F32 x, const F32 y, const F32 z) } #endif // CHECK_FOR_FINITE -inline bool LLPrimitive::validTE(const U8 te_num) const +inline bool LLPrimitive::validTE(const U8 te_index) const { - return (mNumTEs && te_num < mNumTEs); + U8 num_tes = mTextureList.size(); + return (num_tes && te_index < num_tes); } #endif diff --git a/indra/llprimitive/llprimtexturelist.cpp b/indra/llprimitive/llprimtexturelist.cpp index 68f3f5ffacb..840a9a2d27f 100644 --- a/indra/llprimitive/llprimtexturelist.cpp +++ b/indra/llprimitive/llprimtexturelist.cpp @@ -83,13 +83,13 @@ void LLPrimTextureList::clear() void LLPrimTextureList::copy(const LLPrimTextureList& other_list) { // compare the sizes - auto this_size = mEntryList.size(); - auto other_size = other_list.mEntryList.size(); + U8 this_size = static_cast(mEntryList.size()); + U8 other_size = static_cast(other_list.mEntryList.size()); if (this_size > other_size) { // remove the extra entries - for (size_t index = this_size; index > other_size; --index) + for (U8 index = this_size; index > other_size; --index) { delete mEntryList[index-1]; } @@ -97,18 +97,18 @@ void LLPrimTextureList::copy(const LLPrimTextureList& other_list) this_size = other_size; } - size_t index = 0; + U8 index = 0; // copy for the entries that already exist for ( ; index < this_size; ++index) { delete mEntryList[index]; - mEntryList[index] = other_list.getTexture(static_cast(index))->newCopy(); + mEntryList[index] = other_list.getTexture(index)->newCopy(); } // add new entires if needed for ( ; index < other_size; ++index) { - mEntryList.push_back( other_list.getTexture(static_cast(index))->newCopy()); + mEntryList.push_back( other_list.getTexture(index)->newCopy()); } } @@ -122,10 +122,15 @@ void LLPrimTextureList::take(LLPrimTextureList& other_list) other_list.mEntryList.clear(); } +S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry& te) +{ + return copyTexture(index, &te); +} + // virtual // copies LLTextureEntry 'te' // returns TEM_CHANGE_TEXTURE if successful, otherwise TEM_CHANGE_NONE -S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry& te) +S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry* te) { if (size_t(index) >= mEntryList.size()) { @@ -137,9 +142,9 @@ S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry& te) // we're changing an existing entry llassert(mEntryList[index]); delete (mEntryList[index]); - if (&te) + if (te) { - mEntryList[index] = te.newCopy(); + mEntryList[index] = te->newCopy(); } else { @@ -154,7 +159,7 @@ S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry& te) // IMPORTANT! -- if you use this function you must check the return value S32 LLPrimTextureList::takeTexture(const U8 index, LLTextureEntry* te) { - if (S32(index) >= mEntryList.size()) + if (index >= static_cast(mEntryList.size())) { return TEM_CHANGE_NONE; } @@ -387,25 +392,20 @@ LLMaterialPtr LLPrimTextureList::getMaterialParams(const U8 index) return LLMaterialPtr(); } -S32 LLPrimTextureList::size() const +U8 LLPrimTextureList::size() const { - return static_cast(mEntryList.size()); + return static_cast(mEntryList.size()); } // sets the size of the mEntryList container -void LLPrimTextureList::setSize(S32 new_size) +void LLPrimTextureList::setSize(U8 new_size) { - if (new_size < 0) - { - new_size = 0; - } - - auto current_size = mEntryList.size(); + U8 current_size = static_cast(mEntryList.size()); if (new_size > current_size) { mEntryList.resize(new_size); - for (size_t index = current_size; index < new_size; ++index) + for (U8 index = current_size; index < new_size; ++index) { if (current_size > 0 && mEntryList[current_size - 1]) @@ -415,7 +415,7 @@ void LLPrimTextureList::setSize(S32 new_size) } else { - // no valid enries to copy, so we new one up + // no valid entries to copy, so we new one up LLTextureEntry* new_entry = LLPrimTextureList::newTextureEntry(); mEntryList[index] = new_entry; } @@ -423,7 +423,7 @@ void LLPrimTextureList::setSize(S32 new_size) } else if (new_size < current_size) { - for (size_t index = current_size-1; index >= new_size; --index) + for (U8 index = current_size-1; index >= new_size; --index) { delete mEntryList[index]; } diff --git a/indra/llprimitive/llprimtexturelist.h b/indra/llprimitive/llprimtexturelist.h index 79744e9f08e..51a09b20055 100644 --- a/indra/llprimitive/llprimtexturelist.h +++ b/indra/llprimitive/llprimtexturelist.h @@ -78,8 +78,8 @@ class LLPrimTextureList // IMPORTANT! -- if you use this function you must check the return value S32 takeTexture(const U8 index, LLTextureEntry* te); -// // copies contents of 'entry' and stores it in 'index' slot -// void copyTexture(const U8 index, const LLTextureEntry* entry); + // copies contents of 'entry' and stores it in 'index' slot + S32 copyTexture(const U8 index, const LLTextureEntry* entry); // returns pointer to texture at 'index' slot LLTextureEntry* getTexture(const U8 index) const; @@ -109,10 +109,9 @@ class LLPrimTextureList LLMaterialPtr getMaterialParams(const U8 index); - S32 size() const; + U8 size() const; -// void forceResize(S32 new_size); - void setSize(S32 new_size); + void setSize(U8 new_size); void setAllIDs(const LLUUID& id); protected: diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 018d4c4bba5..dc789e63037 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -607,7 +607,7 @@ void renderFace(LLDrawable* drawable, LLFace *face) volume = vobj->getVolume(); } - if (volume) + if (volume && volume->getNumVolumeFaces() > face->getTEOffset()) { const LLVolumeFace& vol_face = volume->getVolumeFace(face->getTEOffset()); LLVertexBuffer::drawElements(LLRender::TRIANGLES, vol_face.mPositions, NULL, vol_face.mNumIndices, vol_face.mIndices); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index b328d3a414c..48a37d7e704 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -2229,32 +2229,33 @@ void LLVOVolume::setNumTEs(const U8 num_tes) { const U8 old_num_tes = getNumTEs() ; - if(old_num_tes && old_num_tes < num_tes) //new faces added + if(old_num_tes) { - LLViewerObject::setNumTEs(num_tes) ; - - if(mMediaImplList.size() >= old_num_tes && mMediaImplList[old_num_tes -1].notNull())//duplicate the last media textures if exists. + if (old_num_tes < num_tes) //new faces added { - mMediaImplList.resize(num_tes) ; - const LLTextureEntry* te = getTE(old_num_tes - 1) ; - for(U8 i = old_num_tes; i < num_tes ; i++) + LLViewerObject::setNumTEs(num_tes) ; + + if(mMediaImplList.size() >= old_num_tes && mMediaImplList[old_num_tes -1].notNull())//duplicate the last media textures if exists. { - setTE(i, *te) ; - mMediaImplList[i] = mMediaImplList[old_num_tes -1] ; + mMediaImplList.resize(num_tes) ; + const LLTextureEntry* te = getTE(old_num_tes - 1) ; + for(U8 i = old_num_tes; i < num_tes ; i++) + { + setTE(i, *te) ; + mMediaImplList[i] = mMediaImplList[old_num_tes -1] ; + } + mMediaImplList[old_num_tes -1]->setUpdated(true) ; } - mMediaImplList[old_num_tes -1]->setUpdated(true) ; } - } - else if(old_num_tes > num_tes && mMediaImplList.size() > num_tes) //old faces removed - { - U8 end = (U8)(mMediaImplList.size()) ; - for(U8 i = num_tes; i < end ; i++) + else if(old_num_tes > num_tes && mMediaImplList.size() > num_tes) //old faces removed { - removeMediaImpl(i) ; + U8 end = (U8)(mMediaImplList.size()) ; + for(U8 i = num_tes; i < end ; i++) + { + removeMediaImpl(i) ; + } + mMediaImplList.resize(num_tes) ; } - mMediaImplList.resize(num_tes) ; - - LLViewerObject::setNumTEs(num_tes) ; } else { From 83dbf0651b85377400b99f0a9cc884a8d7acd76f Mon Sep 17 00:00:00 2001 From: leviathan Date: Thu, 12 Feb 2026 16:17:18 +0000 Subject: [PATCH 2/7] fix LLPrimTextureList stubbery for tests --- indra/llprimitive/tests/llprimitive_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llprimitive/tests/llprimitive_test.cpp b/indra/llprimitive/tests/llprimitive_test.cpp index 0213a3e8b67..0f466375d86 100644 --- a/indra/llprimitive/tests/llprimitive_test.cpp +++ b/indra/llprimitive/tests/llprimitive_test.cpp @@ -106,10 +106,10 @@ S32 LLPrimTextureList::setTexGen(const U8 index, const U8 texgen) { return TEM_C LLMaterialPtr LLPrimTextureList::getMaterialParams(const U8 index) { return LLMaterialPtr(); } void LLPrimTextureList::copy(LLPrimTextureList const & ptl) { mEntryList = ptl.mEntryList; } // do we need to call getTexture()->newCopy()? void LLPrimTextureList::take(LLPrimTextureList &other_list) { } -void LLPrimTextureList::setSize(S32 new_size) { mEntryList.resize(new_size); } +void LLPrimTextureList::setSize(U32 new_size) { mEntryList.resize(new_size); } void LLPrimTextureList::setAllIDs(const LLUUID &id) { } LLTextureEntry * LLPrimTextureList::getTexture(const U8 index) const { return nullptr; } -S32 LLPrimTextureList::size() const { return static_cast(mEntryList.size()); } +U8 LLPrimTextureList::size() const { return static_cast(mEntryList.size()); } class PRIMITIVE_TEST_SETUP { From ef0005c7659715d891f7faa2359bdc1136360a29 Mon Sep 17 00:00:00 2001 From: leviathan Date: Thu, 12 Feb 2026 16:17:45 +0000 Subject: [PATCH 3/7] fix bad comparison of U8 to negative number --- indra/llprimitive/llprimitive.cpp | 135 +++++++++++++++--------------- 1 file changed, 69 insertions(+), 66 deletions(-) diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index 7752a036581..bb99d79ea28 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1494,6 +1494,12 @@ void LLPrimitive::updateNumBumpmap(const U8 index, const U8 bump) U8* LLPrimitive::packTEMessageInternal(U8* cur_ptr) const { + U8 num_faces = getNumTEs(); + if (num_faces == 0) + { + return cur_ptr; + } + U8 image_ids[LLTEContents::MAX_TES*16]; U8 colors[LLTEContents::MAX_TES*4]; F32 scale_s[LLTEContents::MAX_TES]; @@ -1506,77 +1512,74 @@ U8* LLPrimitive::packTEMessageInternal(U8* cur_ptr) const U8 glow[LLTEContents::MAX_TES]; U8 material_data[LLTEContents::MAX_TES*16]; - U8 num_faces = getNumTEs(); U8 last_face_index = llmin(getNumTEs(), LLTEContents::MAX_TES) - 1; - if (last_face_index > -1) + // ...if we hit the front, send one image id + LLColor4U coloru; + for (U8 face_index = 0; face_index <= last_face_index; face_index++) { - // ...if we hit the front, send one image id - LLColor4U coloru; - for (U8 face_index = 0; face_index <= last_face_index; face_index++) - { - // Directly sending image_ids is not safe! - memcpy(&image_ids[face_index*16], getTE(face_index)->getID().mData,16); /* Flawfinder: ignore */ - - // Cast LLColor4 to LLColor4U - coloru.setVec( getTE(face_index)->getColor() ); - - // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f) - // as all zeros. However, the subtraction and addition must be done in unsigned - // byte space, not in float space, otherwise off-by-one errors occur. JC - colors[4*face_index] = 255 - coloru.mV[0]; - colors[4*face_index + 1] = 255 - coloru.mV[1]; - colors[4*face_index + 2] = 255 - coloru.mV[2]; - colors[4*face_index + 3] = 255 - coloru.mV[3]; - - const LLTextureEntry* te = getTE(face_index); - scale_s[face_index] = (F32) te->mScaleS; - scale_t[face_index] = (F32) te->mScaleT; - offset_s[face_index] = (S16) ll_round((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; - offset_t[face_index] = (S16) ll_round((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; - image_rot[face_index] = (S16) ll_round(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); - bump[face_index] = te->getBumpShinyFullbright(); - media_flags[face_index] = te->getMediaTexGen(); - glow[face_index] = (U8) ll_round((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); - - // Directly sending material_ids is not safe! - memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */ - } + // Directly sending image_ids is not safe! + memcpy(&image_ids[face_index*16], getTE(face_index)->getID().mData,16); /* Flawfinder: ignore */ - cur_ptr += pack_TE_field(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID); - *cur_ptr++ = 0; - if (mVolumep && mVolumep->getParams().isMeshSculpt()) - { - cur_ptr += pack_TE_field(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); - } - else - { - cur_ptr += pack_TE_field(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); - } - *cur_ptr++ = 0; - cur_ptr += pack_TE_field(cur_ptr, (U8 *)scale_s, 4 ,last_face_index, MVT_F32); - *cur_ptr++ = 0; - cur_ptr += pack_TE_field(cur_ptr, (U8 *)scale_t, 4 ,last_face_index, MVT_F32); - *cur_ptr++ = 0; - cur_ptr += pack_TE_field(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array); - *cur_ptr++ = 0; - cur_ptr += pack_TE_field(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array); - *cur_ptr++ = 0; - cur_ptr += pack_TE_field(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array); - *cur_ptr++ = 0; - cur_ptr += pack_TE_field(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8); - if (mVolumep && mVolumep->getParams().isMeshSculpt()) - { - // workaround for historical num_faces discrepancy between client and server - cur_ptr += pack_TE_facecount(cur_ptr, (U8 *)bump, last_face_index, MVT_U8); - } - *cur_ptr++ = 0; - cur_ptr += pack_TE_field(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8); - *cur_ptr++ = 0; - cur_ptr += pack_TE_field(cur_ptr, (U8 *)glow, 1 ,last_face_index, MVT_U8); - *cur_ptr++ = 0; - cur_ptr += pack_TE_field(cur_ptr, (U8 *)material_data, 16, last_face_index, MVT_LLUUID); + // Cast LLColor4 to LLColor4U + coloru.setVec( getTE(face_index)->getColor() ); + + // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f) + // as all zeros. However, the subtraction and addition must be done in unsigned + // byte space, not in float space, otherwise off-by-one errors occur. JC + colors[4*face_index] = 255 - coloru.mV[0]; + colors[4*face_index + 1] = 255 - coloru.mV[1]; + colors[4*face_index + 2] = 255 - coloru.mV[2]; + colors[4*face_index + 3] = 255 - coloru.mV[3]; + + const LLTextureEntry* te = getTE(face_index); + scale_s[face_index] = (F32) te->mScaleS; + scale_t[face_index] = (F32) te->mScaleT; + offset_s[face_index] = (S16) ll_round((llclamp(te->mOffsetS,-1.0f,1.0f) * (F32)0x7FFF)) ; + offset_t[face_index] = (S16) ll_round((llclamp(te->mOffsetT,-1.0f,1.0f) * (F32)0x7FFF)) ; + image_rot[face_index] = (S16) ll_round(((fmod(te->mRotation, F_TWO_PI)/F_TWO_PI) * TEXTURE_ROTATION_PACK_FACTOR)); + bump[face_index] = te->getBumpShinyFullbright(); + media_flags[face_index] = te->getMediaTexGen(); + glow[face_index] = (U8) ll_round((llclamp(te->getGlow(), 0.0f, 1.0f) * (F32)0xFF)); + + // Directly sending material_ids is not safe! + memcpy(&material_data[face_index*16],getTE(face_index)->getMaterialID().get(),16); /* Flawfinder: ignore */ + } + + cur_ptr += pack_TE_field(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID); + *cur_ptr++ = 0; + if (mVolumep && mVolumep->getParams().isMeshSculpt()) + { + cur_ptr += pack_TE_field(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); } + else + { + cur_ptr += pack_TE_field(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); + } + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)scale_s, 4 ,last_face_index, MVT_F32); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)scale_t, 4 ,last_face_index, MVT_F32); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8); + if (mVolumep && mVolumep->getParams().isMeshSculpt()) + { + // workaround for historical num_faces discrepancy between client and server + cur_ptr += pack_TE_facecount(cur_ptr, (U8 *)bump, last_face_index, MVT_U8); + } + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)glow, 1 ,last_face_index, MVT_U8); + *cur_ptr++ = 0; + cur_ptr += pack_TE_field(cur_ptr, (U8 *)material_data, 16, last_face_index, MVT_LLUUID); + return cur_ptr; } //============================================================================ From bdb468cd02a4ca6358bfc6816f946c9d0586ba5a Mon Sep 17 00:00:00 2001 From: leviathan Date: Thu, 12 Feb 2026 17:17:11 +0000 Subject: [PATCH 4/7] restore getExpectedNumTEs() --- indra/llprimitive/llprimitive.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index bb99d79ea28..210d70c02d0 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -740,7 +740,11 @@ std::string LLPrimitive::pCodeToString(const LLPCode pcode) void LLPrimitive::copyTEs(const LLPrimitive *primitivep) { - U8 num_tes = llmin(getNumTEs(), primitivep->getNumTEs()); + U8 num_tes = llmin(getExpectedNumTEs(), primitivep->getNumTEs()); + if (mTextureList.size() < num_tes) + { + mTextureList.setSize(num_tes); + } for (U8 i = 0; i < num_tes; i++) { mTextureList.copyTexture(i, *(primitivep->getTE(i))); @@ -1461,6 +1465,18 @@ S32 LLPrimitive::unpackTEMessage(LLDataPacker &dp) return applyParsedTEMessage(tec); } +U8 LLPrimitive::getExpectedNumTEs() const +{ + U8 expected_face_count = mTextureList.size(); + if (mVolumep) + { + // for legacy prims the face count is implicit to the Volume params + // and may be larger than mTextureList.size() during initialization. + expected_face_count = llmax(mVolumep->getNumFaces(), expected_face_count); + } + return expected_face_count; +} + void LLPrimitive::copyTextureList(const LLPrimTextureList& other_list) { mTextureList.copy(other_list); From ed147f2f5cbeca7917f7cbfca8d16b3ce2bd6a0c Mon Sep 17 00:00:00 2001 From: leviathan Date: Thu, 12 Feb 2026 18:17:28 +0000 Subject: [PATCH 5/7] fix comment --- indra/llprimitive/llprimitive.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index 210d70c02d0..f0910a30467 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1403,7 +1403,7 @@ S32 LLPrimitive::unpackTEMessageInternal(LLTEContents& tec) // years the mesh asset upload pipeline suffered a bug where the server-side mesh // asset would cache the face_count of lower-LOD collision shape rather than of // the mesh at full glory. The result was: texture adjustments to unknown mesh - // faces would silently fail at the server. There is a similar client side-problem + // faces would silently fail at the server. There was a similar client side-problem // when mesh objects are loaded with low LOD shapes and server updates for missing // faces are silently dropped. // From 253d5a139adad188158719d141b8a23d3de0e38b Mon Sep 17 00:00:00 2001 From: AndrewMeadows Date: Thu, 12 Feb 2026 21:35:05 -0800 Subject: [PATCH 6/7] fix unit test --- indra/llprimitive/tests/llprimitive_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llprimitive/tests/llprimitive_test.cpp b/indra/llprimitive/tests/llprimitive_test.cpp index 0f466375d86..995e6ec4a92 100644 --- a/indra/llprimitive/tests/llprimitive_test.cpp +++ b/indra/llprimitive/tests/llprimitive_test.cpp @@ -106,7 +106,7 @@ S32 LLPrimTextureList::setTexGen(const U8 index, const U8 texgen) { return TEM_C LLMaterialPtr LLPrimTextureList::getMaterialParams(const U8 index) { return LLMaterialPtr(); } void LLPrimTextureList::copy(LLPrimTextureList const & ptl) { mEntryList = ptl.mEntryList; } // do we need to call getTexture()->newCopy()? void LLPrimTextureList::take(LLPrimTextureList &other_list) { } -void LLPrimTextureList::setSize(U32 new_size) { mEntryList.resize(new_size); } +void LLPrimTextureList::setSize(U8 new_size) { mEntryList.resize(new_size); } void LLPrimTextureList::setAllIDs(const LLUUID &id) { } LLTextureEntry * LLPrimTextureList::getTexture(const U8 index) const { return nullptr; } U8 LLPrimTextureList::size() const { return static_cast(mEntryList.size()); } From 9b30663960d4b3e7513b9085e7338c8794d3b6ce Mon Sep 17 00:00:00 2001 From: leviathan Date: Fri, 13 Feb 2026 16:46:17 +0000 Subject: [PATCH 7/7] remove cruft, add comments and assserts --- indra/llprimitive/llprimitive.cpp | 13 +++---------- indra/llprimitive/llprimtexturelist.cpp | 9 +++++++-- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index f0910a30467..990978812de 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1023,10 +1023,10 @@ bool LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detai if (getNumTEs() < mVolumep->getNumFaces()) { - // Decreases in the LLVolume's notion of "num_faces" happens for LOD reasons - // which do not reflect actual change of faces of the object, so we ignore them. setNumTEs(mVolumep->getNumFaces()); } + // else: Decreases in the LLVolume's notion of "num_faces" happens for LOD reasons + // which do not reflect actual change of faces of the object, so we ignore them. #endif return true; } @@ -1564,14 +1564,7 @@ U8* LLPrimitive::packTEMessageInternal(U8* cur_ptr) const cur_ptr += pack_TE_field(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID); *cur_ptr++ = 0; - if (mVolumep && mVolumep->getParams().isMeshSculpt()) - { - cur_ptr += pack_TE_field(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); - } - else - { - cur_ptr += pack_TE_field(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); - } + cur_ptr += pack_TE_field(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8); *cur_ptr++ = 0; cur_ptr += pack_TE_field(cur_ptr, (U8 *)scale_s, 4 ,last_face_index, MVT_F32); *cur_ptr++ = 0; diff --git a/indra/llprimitive/llprimtexturelist.cpp b/indra/llprimitive/llprimtexturelist.cpp index 840a9a2d27f..30d6af45731 100644 --- a/indra/llprimitive/llprimtexturelist.cpp +++ b/indra/llprimitive/llprimtexturelist.cpp @@ -86,6 +86,10 @@ void LLPrimTextureList::copy(const LLPrimTextureList& other_list) U8 this_size = static_cast(mEntryList.size()); U8 other_size = static_cast(other_list.mEntryList.size()); + llassert(this_size <= MAX_TES); + llassert(other_size <= MAX_TES); + llassert(other_size > 0); + if (this_size > other_size) { // remove the extra entries @@ -105,7 +109,7 @@ void LLPrimTextureList::copy(const LLPrimTextureList& other_list) mEntryList[index] = other_list.getTexture(index)->newCopy(); } - // add new entires if needed + // add new entries if needed for ( ; index < other_size; ++index) { mEntryList.push_back( other_list.getTexture(index)->newCopy()); @@ -139,7 +143,7 @@ S32 LLPrimTextureList::copyTexture(const U8 index, const LLTextureEntry* te) return TEM_CHANGE_NONE; } - // we're changing an existing entry + // we're changing an existing entry llassert(mEntryList[index]); delete (mEntryList[index]); if (te) @@ -400,6 +404,7 @@ U8 LLPrimTextureList::size() const // sets the size of the mEntryList container void LLPrimTextureList::setSize(U8 new_size) { + llassert(new_size > 0); U8 current_size = static_cast(mEntryList.size()); if (new_size > current_size)