Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 57 additions & 22 deletions indra/llprimitive/llprimitive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,14 @@ LLTextureEntry* LLPrimitive::getTE(const U8 index) const
//===============================================================
void LLPrimitive::setNumTEs(const U8 num_tes)
{
const U8 old_num_tes = getNumTEs();
// A face gained starts as a copy of the last, and takes the values of the
// entry last unpacked where there is one.
mTextureList.setSize(num_tes);
if (num_tes > old_num_tes)
{
applyTEEntry(old_num_tes);
}
}

//===============================================================
Expand Down Expand Up @@ -1307,6 +1314,28 @@ S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name

namespace
{
// Reads a message's TextureEntry field whole into packed; block_num < 0
// reads block 0.
void read_te_field(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, boost::container::small_vector_base<U8>& packed)
{
S32 size;
if (block_num < 0)
{
size = mesgsys->getSizeFast(block_name, _PREHASH_TextureEntry);
}
else
{
size = mesgsys->getSizeFast(block_name, block_num, _PREHASH_TextureEntry);
}

packed.clear();
if (size > 0)
{
packed.resize(size);
mesgsys->getBinaryDataFast(block_name, _PREHASH_TextureEntry, packed.data(), 0, std::max(block_num, 0), size);
}
}

// Parses a packed TextureEntry field of any length into tec, for
// face_count faces.
S32 parse_te_entry(boost::container::small_vector_base<U8>& packed, LLTEContents& tec, U8 face_count)
Expand Down Expand Up @@ -1367,23 +1396,8 @@ namespace
// static
S32 LLPrimitive::parseTEMessage(LLMessageSystem* mesgsys, char const* block_name, const S32 block_num, LLTEContents& tec, U8 face_count)
{
S32 size;
if (block_num < 0)
{
size = mesgsys->getSizeFast(block_name, _PREHASH_TextureEntry);
}
else
{
size = mesgsys->getSizeFast(block_name, block_num, _PREHASH_TextureEntry);
}

te_bytes_t packed;
if (size > 0)
{
packed.resize(size);
// if block_num < 0 ask for block 0
mesgsys->getBinaryDataFast(block_name, _PREHASH_TextureEntry, packed.data(), 0, std::max(block_num, 0), size);
}
read_te_field(mesgsys, block_name, block_num, packed);
return parse_te_entry(packed, tec, face_count);
}

Expand All @@ -1408,11 +1422,16 @@ void LLPrimitive::unpackCompressedData(LLDataPacker& dp, U32 flags, boost::conta
}

S32 LLPrimitive::applyParsedTEMessage(LLTEContents& tec)
{
return applyParsedTEFaces(tec, 0);
}

S32 LLPrimitive::applyParsedTEFaces(LLTEContents& tec, U32 first_face)
{
S32 retval = 0;

LLColor4 color;
for (U32 i = 0; i < tec.face_count; i++)
for (U32 i = first_face; i < tec.face_count; i++)
{
LLUUID& req_id = ((LLUUID*)tec.image_data)[i];
retval |= setTETexture(i, req_id);
Expand Down Expand Up @@ -1440,11 +1459,9 @@ S32 LLPrimitive::applyParsedTEMessage(LLTEContents& tec)

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);
te_bytes_t packed;
read_te_field(mesgsys, block_name, block_num, packed);
return unpackTEEntry(packed);
}

S32 LLPrimitive::unpackTEMessage(LLDataPacker &dp)
Expand All @@ -1463,11 +1480,29 @@ S32 LLPrimitive::unpackTEEntry(boost::container::small_vector_base<U8>& packed)
LLTEContents tec;
if (!parse_te_entry(packed, tec, getNumTEs()))
{
// An entry refused, or none, leaves the one kept as it was.
return 0;
}
// Kept as it arrived, without the zero the parse ends it with.
mTEEntry.assign(packed.begin(), packed.end() - 1);
return applyParsedTEMessage(tec);
}

void LLPrimitive::applyTEEntry(U8 first_face)
{
if (mTEEntry.empty())
{
return;
}

te_bytes_t packed(mTEEntry.begin(), mTEEntry.end());
LLTEContents tec;
if (parse_te_entry(packed, tec, getNumTEs()))
{
applyParsedTEFaces(tec, first_face);
}
}

U8 LLPrimitive::getExpectedNumTEs() const
{
U8 expected_face_count = 0;
Expand Down
14 changes: 14 additions & 0 deletions indra/llprimitive/llprimitive.h
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,10 @@ class LLPrimitive : public LLXform

S32 applyParsedTEMessage(LLTEContents& tec);

// Whether the primitive keeps a texture entry it unpacked, whose values
// the faces it gains take (setNumTEs).
bool hasTEEntry() const { return !mTEEntry.empty(); }

#ifdef CHECK_FOR_FINITE
inline void setPosition(const LLVector3& pos);
inline void setPosition(const F32 x, const F32 y, const F32 z);
Expand Down Expand Up @@ -634,6 +638,11 @@ class LLPrimitive : public LLXform
private:
void updateNumBumpmap(const U8 index, const U8 bump);

// Applies the faces of tec from first_face on.
S32 applyParsedTEFaces(LLTEContents& tec, U32 first_face);
// The faces from first_face on take the values mTEEntry gives them.
void applyTEEntry(U8 first_face);

protected:
LLPCode mPrimitiveCode; // Primitive code
LLVector3 mVelocity; // how fast are we moving?
Expand All @@ -646,6 +655,11 @@ class LLPrimitive : public LLXform
U8 mNumBumpmapTEs; // number of bumpmap TEs.
U32 mMiscFlags; // home for misc bools

// The texture entry last unpacked, as it arrived. It gives a value for
// every face, so a face the primitive gains later, as a mesh's asset
// loads, takes its own value from it rather than the last face's.
std::vector<U8> mTEEntry;

public:
static LLVolumeMgr* sVolumeManager;

Expand Down
126 changes: 126 additions & 0 deletions indra/llprimitive/tests/llprimitivetemessage_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,130 @@ namespace
CHECK(copy.unpackTEMessage(reader) == TEM_INVALID);
CHECK(packed_entry(copy) == before);
}

// Unpacks entry into prim through a data packer, as an update does.
S32 unpack_entry(LLPrimitive& prim, const std::vector<U8>& entry)
{
std::vector<U8> buffer = sized(entry);
LLDataPackerBinaryBuffer reader(buffer.data(), (S32)buffer.size());
return prim.unpackTEMessage(reader);
}

// A mesh has the faces of its placeholder shape until its asset loads,
// fewer than the entry it is sent names.
TEST_CASE("LLPrimitiveTEMessage: the faces a primitive gains take the values its entry gives them", "[LLPrimitiveTEMessage]")
{
LLPrimitive sent;
give_faces_their_own_entries(sent, 6);
const std::vector<U8> entry = packed_entry(sent);

LLPrimitive prim;
prim.setNumTEs(4);
CHECK(unpack_entry(prim, entry) != TEM_INVALID);
REQUIRE(prim.hasTEEntry());

prim.setNumTEs(6);
REQUIRE(prim.getNumTEs() == 6);
CHECK(prim.getTE(4)->getID() == face_uuid(4, 1));
CHECK(prim.getTE(5)->getID() == face_uuid(5, 1));
CHECK(prim.getTE(5)->getMaterialID() == LLMaterialID(face_uuid(5, 100)));
CHECK(packed_entry(prim) == entry);
}

TEST_CASE("LLPrimitiveTEMessage: faces removed and gained again take the values the entry gives them", "[LLPrimitiveTEMessage]")
{
LLPrimitive sent;
give_faces_their_own_entries(sent, 6);
const std::vector<U8> entry = packed_entry(sent);

LLPrimitive prim;
prim.setNumTEs(6);
CHECK(unpack_entry(prim, entry) != TEM_INVALID);

prim.setNumTEs(2);
REQUIRE(prim.getNumTEs() == 2);
CHECK(prim.getTE(1)->getID() == face_uuid(1, 1));

prim.setNumTEs(6);
CHECK(packed_entry(prim) == entry);
}

TEST_CASE("LLPrimitiveTEMessage: the faces a primitive gains take the values of the newest entry", "[LLPrimitiveTEMessage]")
{
LLPrimitive sent;
give_faces_their_own_entries(sent, 6);
const std::vector<U8> first = packed_entry(sent);
sent.setTETexture(4, face_uuid(4, 50));
sent.setTEColor(5, LLColor4(0.5f, 0.25f, 0.75f, 1.f));
const std::vector<U8> second = packed_entry(sent);
REQUIRE(second != first);

LLPrimitive prim;
prim.setNumTEs(4);
CHECK(unpack_entry(prim, first) != TEM_INVALID);
CHECK(unpack_entry(prim, second) != TEM_INVALID);

prim.setNumTEs(6);
CHECK(prim.getTE(4)->getID() == face_uuid(4, 50));
CHECK(packed_entry(prim) == second);
}

// A refused entry changes no face, so the faces gained later take the
// values of the entry before it.
TEST_CASE("LLPrimitiveTEMessage: the faces a primitive gains after a refused entry take the entry before it", "[LLPrimitiveTEMessage]")
{
LLPrimitive sent;
give_faces_their_own_entries(sent, 6);
const std::vector<U8> entry = packed_entry(sent);

LLPrimitive prim;
prim.setNumTEs(4);
CHECK(unpack_entry(prim, entry) != TEM_INVALID);

// Cut inside the first field's default value, and empty: each
// changes nothing.
const std::vector<U8> malformed(entry.begin(), entry.begin() + 8);
CHECK(unpack_entry(prim, malformed) == TEM_CHANGE_NONE);
CHECK(unpack_entry(prim, {}) == TEM_CHANGE_NONE);

prim.setNumTEs(6);
CHECK(packed_entry(prim) == entry);
}

// An entry gives every face past the last it packs that face's value.
TEST_CASE("LLPrimitiveTEMessage: faces gained past those an entry packs take the value it gives every face", "[LLPrimitiveTEMessage]")
{
LLPrimitive sent;
give_faces_their_own_entries(sent, 4);
const std::vector<U8> entry = packed_entry(sent);

LLPrimitive prim;
prim.setNumTEs(4);
CHECK(unpack_entry(prim, entry) != TEM_INVALID);

prim.setNumTEs(6);
for (U8 face = 4; face < 6; ++face)
{
CAPTURE((int)face);
CHECK(*prim.getTE(face) == *prim.getTE(3));
}
CHECK(prim.getTE(3)->getID() == face_uuid(3, 1));
}

// With no entry to take values from, a face gained is a copy of the
// last.
TEST_CASE("LLPrimitiveTEMessage: a primitive that unpacked no entry copies its last face into the faces it gains", "[LLPrimitiveTEMessage]")
{
LLPrimitive prim;
give_faces_their_own_entries(prim, 2);
REQUIRE_FALSE(prim.hasTEEntry());

prim.setNumTEs(4);
for (U8 face = 2; face < 4; ++face)
{
CAPTURE((int)face);
CHECK(*prim.getTE(face) == *prim.getTE(1));
}
CHECK(prim.getTE(1)->getID() == face_uuid(1, 1));
}
}
30 changes: 28 additions & 2 deletions indra/newview/llviewerobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5229,8 +5229,29 @@ void LLViewerObject::setNumTEs(const U8 num_tes)
LLPrimitive::setNumTEs(num_tes);
setChanged(TEXTURE);

if (hasTEEntry())
{
// The faces gained took their values from the texture entry last
// unpacked. Each takes the render material the parameter block
// names for it, and the override the region holds for it, rather
// than the last face's.
if (original_tes < getNumTEs())
{
for (int i = original_tes; i < getNumTEs(); ++i)
{
LLTextureEntry* tep = getTE(i);
const LLUUID id = getRenderMaterialID(i);
tep->setGLTFMaterialOverride(nullptr);
if (id.notNull() || tep->getGLTFMaterial())
{
setRenderMaterialID(i, id, false, false);
}
}
gGLTFMaterialList.applyQueuedOverrides(this);
}
}
// touch up GLTF materials
if (original_tes > 0)
else if (original_tes > 0)
{
for (int i = original_tes; i < getNumTEs(); ++i)
{
Expand Down Expand Up @@ -7908,7 +7929,12 @@ void LLViewerObject::setRenderMaterialID(S32 te_in, const LLUUID& id, bool updat
{ // update existing parameter block
for (S32 te = start_idx; te < end_idx; ++te)
{
param_block->setMaterial(te, id);
// Only where the block names another material: a face given no
// material where it names none takes no entry for it.
if (param_block->getMaterial(te) != id)
{
param_block->setMaterial(te, id);
}
}
}

Expand Down
Loading