From ce75d0e63b5b0efa1f5e880ee029f95aed56f66d Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 6 Dec 2023 19:30:48 +0000 Subject: SL-20635 - adde::qd new data to ObjectUpdate message containing num attachments or child prims --- indra/newview/llviewerobject.cpp | 331 +++++++++++++-------------------------- 1 file changed, 107 insertions(+), 224 deletions(-) (limited to 'indra/newview/llviewerobject.cpp') diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 2bcd0858d3..735b3c03b7 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -1159,6 +1159,20 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, LL_DEBUGS("ObjectUpdate") << " mesgsys " << mesgsys << " dp " << dp << " id " << getID() << " update_type " << (S32) update_type << LL_ENDL; dumpStack("ObjectUpdateStack"); + // The new OBJECTDATA_FIELD_SIZE_124, OBJECTDATA_FIELD_SIZE_140, OBJECTDATA_FIELD_SIZE_80 + // and OBJECTDATA_FIELD_SIZE_64 lengths should be supported in the existing cases below. + // Each case should start at the beginning of the buffer and extract all known + // values, and ignore any unknown data at the end of the buffer. + // This allows new data in the future without breaking current viewers. + const S32 OBJECTDATA_FIELD_SIZE_140 = 140; // Full precision avatar update for future extended data + const S32 OBJECTDATA_FIELD_SIZE_124 = 124; // Full precision object update for future extended data + const S32 OBJECTDATA_FIELD_SIZE_76 = 76; // Full precision avatar update + const S32 OBJECTDATA_FIELD_SIZE_60 = 60; // Full precision object update + const S32 OBJECTDATA_FIELD_SIZE_80 = 80; // Terse avatar update, 16 bit precision for future extended data + const S32 OBJECTDATA_FIELD_SIZE_64 = 64; // Terse object update, 16 bit precision for future extended data + const S32 OBJECTDATA_FIELD_SIZE_48 = 48; // Terse avatar update, 16 bit precision + const S32 OBJECTDATA_FIELD_SIZE_32 = 32; // Terse object update, 16 bit precision + U32 retval = 0x0; // If region is removed from the list it is also deleted. @@ -1206,7 +1220,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, U32 x, y; from_region_handle(region_handle, &x, &y); - LL_ERRS() << "Object has invalid region " << x << ":" << y << "!" << LL_ENDL; + LL_WARNS("UpdateFail") << "Object has invalid region " << x << ":" << y << "!" << LL_ENDL; return retval; } @@ -1233,8 +1247,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, const F32 size = LLWorld::getInstance()->getRegionWidthInMeters(); const F32 MAX_HEIGHT = LLWorld::getInstance()->getRegionMaxHeight(); const F32 MIN_HEIGHT = LLWorld::getInstance()->getRegionMinHeight(); - S32 length; - S32 count; + S32 length = 0; + S32 count = 0; S32 this_update_precision = 32; // in bits // Temporaries, because we need to compare w/ previous to set dirty flags... @@ -1295,6 +1309,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_Scale, new_scale, block_num ); length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData); mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num, MAX_OBJECT_BINARY_DATA_SIZE); + length = llmin(length, MAX_OBJECT_BINARY_DATA_SIZE); // getBinaryDataFast() safely fills the buffer to max_size mTotalCRC = crc; // Might need to update mSourceMuted here to properly pick up new radius @@ -1314,20 +1329,23 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, } setClickAction(click_action); - count = 0; - LLVector4 collision_plane; - + count = 0; + LLVector4 collision_plane; + switch(length) { - case (60 + 16): + case OBJECTDATA_FIELD_SIZE_140: + case OBJECTDATA_FIELD_SIZE_76: // pull out collision normal for avatar htolememcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); ((LLVOAvatar*)this)->setFootPlane(collision_plane); count += sizeof(LLVector4); - // fall through - case 60: + [[fallthrough]]; + + case OBJECTDATA_FIELD_SIZE_124: + case OBJECTDATA_FIELD_SIZE_60: this_update_precision = 32; - // this is a terse update + // this is a full precision update // pos htolememcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); count += sizeof(LLVector3); @@ -1352,117 +1370,21 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, resetRot(); } setAngularVelocity(new_angv); + count += sizeof(LLVector3); #if LL_DARWIN - if (length == 76) + if (length == OBJECTDATA_FIELD_SIZE_76 || + length == OBJECTDATA_FIELD_SIZE_140) { setAngularVelocity(LLVector3::zero); } #endif break; - case(32 + 16): - // pull out collision normal for avatar - htolememcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); - ((LLVOAvatar*)this)->setFootPlane(collision_plane); - count += sizeof(LLVector4); - // fall through - case 32: - this_update_precision = 16; - test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); - - // This is a terse 16 update, so treat data as an array of U16's. -#ifdef LL_BIG_ENDIAN - htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - new_pos_parent.mV[VX] = U16_to_F32(val[VX], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VY] = U16_to_F32(val[VY], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VZ] = U16_to_F32(val[VZ], MIN_HEIGHT, MAX_HEIGHT); - -#ifdef LL_BIG_ENDIAN - htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - setVelocity(LLVector3(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size))); - -#ifdef LL_BIG_ENDIAN - htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - setAcceleration(LLVector3(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size))); - -#ifdef LL_BIG_ENDIAN - htolememcpy(valswizzle, &data[count], MVT_U16Quat, 4); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*4; - new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f); - new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f); - new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f); - new_rot.mQ[VW] = U16_to_F32(val[VW], -1.f, 1.f); - -#ifdef LL_BIG_ENDIAN - htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - new_angv.setVec(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size)); - if (new_angv.isExactlyZero()) - { - // reset rotation time - resetRot(); - } - setAngularVelocity(new_angv); - break; - case 16: - this_update_precision = 8; - test_pos_parent.quantize8(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); - // this is a terse 8 update - new_pos_parent.mV[VX] = U8_to_F32(data[0], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VY] = U8_to_F32(data[1], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VZ] = U8_to_F32(data[2], MIN_HEIGHT, MAX_HEIGHT); - - setVelocity(U8_to_F32(data[3], -size, size), - U8_to_F32(data[4], -size, size), - U8_to_F32(data[5], -size, size) ); - - setAcceleration(U8_to_F32(data[6], -size, size), - U8_to_F32(data[7], -size, size), - U8_to_F32(data[8], -size, size) ); - - new_rot.mQ[VX] = U8_to_F32(data[9], -1.f, 1.f); - new_rot.mQ[VY] = U8_to_F32(data[10], -1.f, 1.f); - new_rot.mQ[VZ] = U8_to_F32(data[11], -1.f, 1.f); - new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f); - - new_angv.setVec(U8_to_F32(data[13], -size, size), - U8_to_F32(data[14], -size, size), - U8_to_F32(data[15], -size, size) ); - if (new_angv.isExactlyZero()) - { - // reset rotation time - resetRot(); - } - setAngularVelocity(new_angv); - break; + // length values 48, 32 and 16 were once in viewer code but + // are never sent by the SL simulator + default: + LL_WARNS("UpdateFail") << "Unexpected ObjectData buffer size " << length + << " for " << getID() << " with OUT_FULL message" << LL_ENDL; } //////////////////////////////////////////////////// @@ -1496,20 +1418,50 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, if (mData) { delete [] mData; - } + mData = NULL; + } - // Check for appended generic data - S32 data_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Data); - if (data_size <= 0) - { - mData = NULL; - } - else - { - // ...has generic data - mData = new U8[data_size]; - mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, mData, data_size, block_num); - } + // Dec 2023 new generic data: + // Trees work as before, this field contains genome data + // Not a tree: + // avatars send 1 byte with the number of attachments + // root objects send 1 byte with the number of prims in the linkset + // If the generic data size is zero, then number of attachments or prims is zero + // + // Viewers should not match the data size exactly, but if the field has data, + // read the 1st byte as described above, and ignore the rest. + + // Check for appended generic data + const S32 GENERIC_DATA_BUFFER_SIZE = 16; + S32 data_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Data); + if (data_size > 0) + { // has generic data + if (getPCode() == LL_PCODE_LEGACY_TREE || getPCode() == LL_PCODE_TREE_NEW) + { + mData = new U8[data_size]; + mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, mData, data_size, block_num); + LL_DEBUGS("NewObjectData") << "Read " << data_size << " bytes tree genome data for " << getID() << ", pcode " + << getPCodeString() << ", value " << (S32) mData[0] << LL_ENDL; + } + else + { // Extract number of prims or attachments + U8 generic_data[GENERIC_DATA_BUFFER_SIZE]; + mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, + &generic_data[0], llmin(data_size, GENERIC_DATA_BUFFER_SIZE), block_num); + // This is sample code to extract the number of attachments or prims + // Future viewers should use it for their own purposes + if (isAvatar()) + { + LL_DEBUGS("NewObjectData") << "Avatar " << getID() << " has " + << (S32) generic_data[0] << " attachments" << LL_ENDL; + } + else + { + LL_DEBUGS("NewObjectData") << "Root prim " << getID() << " has " + << (S32) generic_data[0] << " prims in linkset" << LL_ENDL; + } + } + } S32 text_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Text); if (text_size > 1) @@ -1605,60 +1557,24 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, #endif length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData); mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num, MAX_OBJECT_BINARY_DATA_SIZE); - count = 0; + length = llmin(length, MAX_OBJECT_BINARY_DATA_SIZE); // getBinaryDataFast() safely fills the buffer to max_size + count = 0; LLVector4 collision_plane; - + switch(length) { - case(60 + 16): + case OBJECTDATA_FIELD_SIZE_80: + case OBJECTDATA_FIELD_SIZE_48: // pull out collision normal for avatar htolememcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); ((LLVOAvatar*)this)->setFootPlane(collision_plane); count += sizeof(LLVector4); - // fall through - case 60: - // this is a terse 32 update - // pos - this_update_precision = 32; - htolememcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // vel - htolememcpy((void*)getVelocity().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // acc - htolememcpy((void*)getAcceleration().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // theta - { - LLVector3 vec; - htolememcpy(vec.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - new_rot.unpackFromVector3(vec); - } - count += sizeof(LLVector3); - // omega - htolememcpy((void*)new_angv.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - if (new_angv.isExactlyZero()) - { - // reset rotation time - resetRot(); - } - setAngularVelocity(new_angv); -#if LL_DARWIN - if (length == 76) - { - setAngularVelocity(LLVector3::zero); - } -#endif - break; - case(32 + 16): - // pull out collision normal for avatar - htolememcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); - ((LLVOAvatar*)this)->setFootPlane(collision_plane); - count += sizeof(LLVector4); - // fall through - case 32: - // this is a terse 16 update - this_update_precision = 16; + [[fallthrough]]; + + case OBJECTDATA_FIELD_SIZE_64: + case OBJECTDATA_FIELD_SIZE_32: + // this is a terse 16 bit quantized update + this_update_precision = 16; test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); #ifdef LL_BIG_ENDIAN @@ -1718,33 +1634,14 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, setAngularVelocity(new_angv); break; - case 16: - // this is a terse 8 update - this_update_precision = 8; - test_pos_parent.quantize8(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); - new_pos_parent.mV[VX] = U8_to_F32(data[0], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VY] = U8_to_F32(data[1], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VZ] = U8_to_F32(data[2], MIN_HEIGHT, MAX_HEIGHT); - - setVelocity(U8_to_F32(data[3], -size, size), - U8_to_F32(data[4], -size, size), - U8_to_F32(data[5], -size, size) ); - - setAcceleration(U8_to_F32(data[6], -size, size), - U8_to_F32(data[7], -size, size), - U8_to_F32(data[8], -size, size) ); - - new_rot.mQ[VX] = U8_to_F32(data[9], -1.f, 1.f); - new_rot.mQ[VY] = U8_to_F32(data[10], -1.f, 1.f); - new_rot.mQ[VZ] = U8_to_F32(data[11], -1.f, 1.f); - new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f); - - new_angv.set(U8_to_F32(data[13], -size, size), - U8_to_F32(data[14], -size, size), - U8_to_F32(data[15], -size, size) ); - setAngularVelocity(new_angv); - break; - } + // Previous viewers had code for length 76, 60 or 16 byte length + // with full precision or 8 bit quanitzation, but the + // SL servers will never send those data formats. If you ever see this + // warning on a SL server, please file a bug report + default: + LL_WARNS("UpdateFail") << "Unexpected ObjectData buffer size " << length << " for " << getID() + << " with OUT_FULL message" << LL_ENDL; + } U8 state; mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); @@ -1753,13 +1650,13 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, } default: + LL_WARNS("UpdateFail") << "Unknown uncompressed update type " << update_type << " for " << getID() << LL_ENDL; break; - } } else { - // handle the compressed case + // handle the compressed case - have dp datapacker LLUUID sound_uuid; LLUUID owner_id; F32 gain = 0; @@ -2013,6 +1910,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, break; default: + LL_WARNS("UpdateFail") << "Unknown compressed update type " << update_type << " for " << getID() << LL_ENDL; break; } } @@ -2060,7 +1958,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, if (sent_parentp && sent_parentp->getParent() == this) { // Try to recover if we attempt to attach a parent to its child - LL_WARNS() << "Attempt to attach a parent to it's child: " << this->getID() << " to " << sent_parentp->getID() << LL_ENDL; + LL_WARNS("UpdateFail") << "Attempt to attach a parent to it's child: " << this->getID() << " to " + << sent_parentp->getID() << LL_ENDL; this->removeChild(sent_parentp); sent_parentp->setDrawableParent(NULL); } @@ -2084,7 +1983,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, { if (mDrawable->isDead() || !mDrawable->getVObj()) { - LL_WARNS() << "Drawable is dead or no VObj!" << LL_ENDL; + LL_WARNS("UpdateFail") << "Drawable is dead or no VObj!" << LL_ENDL; sent_parentp->addChild(this); } else @@ -2094,9 +1993,9 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // Bad, we got a cycle somehow. // Kill both the parent and the child, and // set cache misses for both of them. - LL_WARNS() << "Attempting to recover from parenting cycle!" << LL_ENDL; - LL_WARNS() << "Killing " << sent_parentp->getID() << " and " << getID() << LL_ENDL; - LL_WARNS() << "Adding to cache miss list" << LL_ENDL; + LL_WARNS("UpdateFail") << "Attempting to recover from parenting cycle! " + << "Killing " << sent_parentp->getID() << " and " << getID() + << ", Adding to cache miss list" << LL_ENDL; setParent(NULL); sent_parentp->setParent(NULL); getRegion()->addCacheMissFull(getLocalID()); @@ -2751,22 +2650,6 @@ void LLViewerObject::interpolateLinearMotion(const F64SecondsImplicit& frame_tim -BOOL LLViewerObject::setData(const U8 *datap, const U32 data_size) -{ - delete [] mData; - - if (datap) - { - mData = new U8[data_size]; - if (!mData) - { - return FALSE; - } - memcpy(mData, datap, data_size); /* Flawfinder: ignore */ - } - return TRUE; -} - // delete an item in the inventory, but don't tell the server. This is // used internally by remove, update, and savescript. // This will only delete the first item with an item_id in the list -- cgit v1.2.3 From d4c3300f9b81296f765c83c1e822251e86d91925 Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 11 Dec 2023 17:30:49 +0000 Subject: sl-20635 - cleaned up code after reading it yet again --- indra/newview/llviewerobject.cpp | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'indra/newview/llviewerobject.cpp') diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 735b3c03b7..c5d4c6dc09 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -1423,13 +1423,12 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // Dec 2023 new generic data: // Trees work as before, this field contains genome data - // Not a tree: - // avatars send 1 byte with the number of attachments - // root objects send 1 byte with the number of prims in the linkset - // If the generic data size is zero, then number of attachments or prims is zero + // Not a tree: root objects send 1 byte with the number of + // total prims in the linkset + // If the generic data size is zero, then number of prims is 1 // - // Viewers should not match the data size exactly, but if the field has data, - // read the 1st byte as described above, and ignore the rest. + // Viewers should not check for specific data sizes exactly, but if + // the field has data, process it from the start and ignore the remainder. // Check for appended generic data const S32 GENERIC_DATA_BUFFER_SIZE = 16; @@ -1444,21 +1443,17 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, << getPCodeString() << ", value " << (S32) mData[0] << LL_ENDL; } else - { // Extract number of prims or attachments + { // Extract number of prims U8 generic_data[GENERIC_DATA_BUFFER_SIZE]; mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, &generic_data[0], llmin(data_size, GENERIC_DATA_BUFFER_SIZE), block_num); - // This is sample code to extract the number of attachments or prims + // This is sample code to extract the number of prims // Future viewers should use it for their own purposes - if (isAvatar()) - { - LL_DEBUGS("NewObjectData") << "Avatar " << getID() << " has " - << (S32) generic_data[0] << " attachments" << LL_ENDL; - } - else + if (!isAvatar()) { + S32 num_prims = (S32) generic_data[0]; LL_DEBUGS("NewObjectData") << "Root prim " << getID() << " has " - << (S32) generic_data[0] << " prims in linkset" << LL_ENDL; + << num_prims << " prims in linkset" << LL_ENDL; } } } @@ -1637,7 +1632,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // Previous viewers had code for length 76, 60 or 16 byte length // with full precision or 8 bit quanitzation, but the // SL servers will never send those data formats. If you ever see this - // warning on a SL server, please file a bug report + // warning in Second Life, please file a bug report default: LL_WARNS("UpdateFail") << "Unexpected ObjectData buffer size " << length << " for " << getID() << " with OUT_FULL message" << LL_ENDL; -- cgit v1.2.3 From 82d8b2a7209a96cdc38dd8e77f458cac3ffeedbd Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 5 Jan 2024 19:01:40 +0000 Subject: sl-20635 attempts at build fixes, added a few stray log messages about log file changes --- indra/newview/llviewerobject.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'indra/newview/llviewerobject.cpp') diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 2669c956e1..c4e5fcc7b6 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -1340,7 +1340,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, htolememcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); ((LLVOAvatar*)this)->setFootPlane(collision_plane); count += sizeof(LLVector4); - [[fallthrough]]; case OBJECTDATA_FIELD_SIZE_124: case OBJECTDATA_FIELD_SIZE_60: @@ -1564,7 +1563,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, htolememcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); ((LLVOAvatar*)this)->setFootPlane(collision_plane); count += sizeof(LLVector4); - [[fallthrough]]; case OBJECTDATA_FIELD_SIZE_64: case OBJECTDATA_FIELD_SIZE_32: -- cgit v1.2.3 From 4419bb870986c6900fc096338622d27b999cd771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sun, 11 Feb 2024 01:23:28 +0100 Subject: more misc: BOOL (int) to real bool --- indra/newview/llviewerobject.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/llviewerobject.cpp') diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index c4e5fcc7b6..a0a9da5bec 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -3656,14 +3656,14 @@ void LLViewerObject::setPixelAreaAndAngle(LLAgent &agent) } } -BOOL LLViewerObject::updateLOD() +bool LLViewerObject::updateLOD() { - return FALSE; + return false; } BOOL LLViewerObject::updateGeometry(LLDrawable *drawable) { - return TRUE; + return false; } void LLViewerObject::updateGL() -- cgit v1.2.3 From f1c97f4057833220a2e9ac045d701208e30457d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sun, 18 Feb 2024 16:41:22 +0100 Subject: misc: BOOL to bool --- indra/newview/llviewerobject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llviewerobject.cpp') diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index a0a9da5bec..1b7c668b7d 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -3186,7 +3186,7 @@ void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data) U64 new_id = gXferManager->requestFile(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ft->mFilename), ft->mFilename, LL_PATH_CACHE, object->mRegionp->getHost(), - TRUE, + true, &LLViewerObject::processTaskInvFile, (void**)ft, // This takes ownership of ft LLXferManager::HIGH_PRIORITY); -- cgit v1.2.3 From c3e6f7b1643076df35a6960f735024078ddbb353 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Mon, 19 Feb 2024 21:33:38 +0100 Subject: Convert remaining cases of BOOL to bool in llmath and llprimitive Changed the return values for LLPrimitive::packTEMessage methods from FALSE to true - these seemed to be strange and wrong, especially considering the following statement in LLVOAvatarSelf: bool success = packTEMessage(mesgsys); --- indra/newview/llviewerobject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview/llviewerobject.cpp') diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 1b7c668b7d..c56da0722b 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4663,9 +4663,9 @@ void LLViewerObject::setMediaPassedWhitelist(BOOL passed) } } -BOOL LLViewerObject::setMaterial(const U8 material) +bool LLViewerObject::setMaterial(const U8 material) { - BOOL res = LLPrimitive::setMaterial(material); + bool res = LLPrimitive::setMaterial(material); if (res) { setChanged(TEXTURE); -- cgit v1.2.3 From 60d3dd98a44230c21803c1606552ee098ed9fa7c Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 21 Feb 2024 21:05:14 +0100 Subject: Convert remaining BOOL to bool --- indra/newview/llviewerobject.cpp | 352 +++++++++++++++++++-------------------- 1 file changed, 176 insertions(+), 176 deletions(-) (limited to 'indra/newview/llviewerobject.cpp') diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index c56da0722b..b4f5142843 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -110,19 +110,19 @@ //#define DEBUG_UPDATE_TYPE -BOOL LLViewerObject::sVelocityInterpolate = TRUE; -BOOL LLViewerObject::sPingInterpolate = TRUE; +bool LLViewerObject::sVelocityInterpolate = true; +bool LLViewerObject::sPingInterpolate = true; U32 LLViewerObject::sNumZombieObjects = 0; S32 LLViewerObject::sNumObjects = 0; -BOOL LLViewerObject::sMapDebug = TRUE; +bool LLViewerObject::sMapDebug = true; LLColor4 LLViewerObject::sEditSelectColor( 1.0f, 1.f, 0.f, 0.3f); // Edit OK LLColor4 LLViewerObject::sNoEditSelectColor( 1.0f, 0.f, 0.f, 0.3f); // Can't edit S32 LLViewerObject::sAxisArrowLength(50); -BOOL LLViewerObject::sPulseEnabled(FALSE); -BOOL LLViewerObject::sUseSharedDrawables(FALSE); // TRUE +bool LLViewerObject::sPulseEnabled(false); +bool LLViewerObject::sUseSharedDrawables(false); // true // sMaxUpdateInterpolationTime must be greater than sPhaseOutUpdateInterpolationTime F64Seconds LLViewerObject::sMaxUpdateInterpolationTime(3.0); // For motion interpolation: after X seconds with no updates, don't predict object motion @@ -246,7 +246,7 @@ LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pco return res; } -LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp, BOOL is_global) +LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp, bool is_global) : LLPrimitive(), mChildList(), mID(id), @@ -256,7 +256,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mTEImages(NULL), mTENormalMaps(NULL), mTESpecularMaps(NULL), - mbCanSelect(TRUE), + mbCanSelect(true), mFlags(0), mPhysicsShapeType(0), mPhysicsGravity(0), @@ -264,8 +264,8 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mPhysicsDensity(0), mPhysicsRestitution(0), mDrawable(), - mCreateSelected(FALSE), - mRenderMedia(FALSE), + mCreateSelected(false), + mRenderMedia(false), mBestUpdatePrecision(0), mText(), mHudText(""), @@ -286,14 +286,14 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mExpectedInventorySerialNum(0), mInvRequestState(INVENTORY_REQUEST_STOPPED), mInvRequestXFerId(0), - mInventoryDirty(FALSE), + mInventoryDirty(false), mRegionp(regionp), - mDead(FALSE), - mOrphaned(FALSE), - mUserSelected(FALSE), - mOnActiveList(FALSE), - mOnMap(FALSE), - mStatic(FALSE), + mDead(false), + mOrphaned(false), + mUserSelected(false), + mOnActiveList(false), + mOnMap(false), + mStatic(false), mSeatCount(0), mNumFaces(0), mRotTime(0.f), @@ -310,7 +310,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mPhysicsShapeUnknown(true), mAttachmentItemID(LLUUID::null), mLastUpdateType(OUT_UNKNOWN), - mLastUpdateCached(FALSE), + mLastUpdateCached(false), mCachedMuteListUpdateTime(0), mCachedOwnerInMuteList(false), mRiggedAttachedWarned(false) @@ -440,7 +440,7 @@ void LLViewerObject::markDead() } // Mark itself as dead - mDead = TRUE; + mDead = true; if(mRegionp) { mRegionp->removeFromCreatedList(getLocalID()); @@ -744,7 +744,7 @@ void LLViewerObject::setNameValueList(const std::string& name_value_list) } } -BOOL LLViewerObject::isAnySelected() const +bool LLViewerObject::isAnySelected() const { bool any_selected = isSelected(); for (child_list_t::const_iterator iter = mChildList.begin(); @@ -756,7 +756,7 @@ BOOL LLViewerObject::isAnySelected() const return any_selected; } -void LLViewerObject::setSelected(BOOL sel) +void LLViewerObject::setSelected(bool sel) { mUserSelected = sel; resetRot(); @@ -893,12 +893,12 @@ bool LLViewerObject::crossesParcelBounds() return mRegionp && mRegionp->objectsCrossParcel(boxes); } -BOOL LLViewerObject::setParent(LLViewerObject* parent) +bool LLViewerObject::setParent(LLViewerObject* parent) { if(mParent != parent) { LLViewerObject* old_parent = (LLViewerObject*)mParent ; - BOOL ret = LLPrimitive::setParent(parent); + bool ret = LLPrimitive::setParent(parent); if(ret && old_parent && parent) { old_parent->removeChild(this) ; @@ -906,7 +906,7 @@ BOOL LLViewerObject::setParent(LLViewerObject* parent) return ret ; } - return FALSE ; + return false ; } void LLViewerObject::addChild(LLViewerObject *childp) @@ -974,7 +974,7 @@ void LLViewerObject::removeChild(LLViewerObject *childp) if (childp->isSelected()) { LLSelectMgr::getInstance()->deselectObjectAndFamily(childp); - BOOL add_to_end = TRUE; + bool add_to_end = true; LLSelectMgr::getInstance()->selectObjectAndFamily(childp, add_to_end); } } @@ -1012,35 +1012,35 @@ void LLViewerObject::addThisAndNonJointChildren(std::vector& ob } } -BOOL LLViewerObject::isChild(LLViewerObject *childp) const +bool LLViewerObject::isChild(LLViewerObject *childp) const { for (child_list_t::const_iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { LLViewerObject* testchild = *iter; if (testchild == childp) - return TRUE; + return true; } - return FALSE; + return false; } -// returns TRUE if at least one avatar is sitting on this object -BOOL LLViewerObject::isSeat() const +// returns true if at least one avatar is sitting on this object +bool LLViewerObject::isSeat() const { return mSeatCount > 0; } -BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp) +bool LLViewerObject::setDrawableParent(LLDrawable* parentp) { if (mDrawable.isNull()) { - return FALSE; + return false; } - BOOL ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL); + bool ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL); if(!ret) { - return FALSE ; + return false ; } LLDrawable* old_parent = mDrawable->mParent; mDrawable->mParent = parentp; @@ -1056,11 +1056,11 @@ BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp) || (parentp && parentp->isActive())) { // *TODO we should not be relying on setDrawable parent to call markMoved - gPipeline.markMoved(mDrawable, FALSE); + gPipeline.markMoved(mDrawable, false); } else if (!mDrawable->isAvatar()) { - mDrawable->updateXform(TRUE); + mDrawable->updateXform(true); /*if (!mDrawable->getSpatialGroup()) { mDrawable->movePartition(); @@ -1071,7 +1071,7 @@ BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp) } // Show or hide particles, icon and HUD -void LLViewerObject::hideExtraDisplayItems( BOOL hidden ) +void LLViewerObject::hideExtraDisplayItems( bool hidden ) { if( mPartSourcep.notNull() ) { @@ -1101,7 +1101,7 @@ U32 LLViewerObject::checkMediaURL(const std::string &media_url) mMedia = new LLViewerObjectMedia; mMedia->mMediaURL = media_url; mMedia->mMediaType = LLViewerObject::MEDIA_SET; - mMedia->mPassedWhitelist = FALSE; + mMedia->mPassedWhitelist = false; } else if (mMedia) { @@ -1123,7 +1123,7 @@ U32 LLViewerObject::checkMediaURL(const std::string &media_url) retval |= MEDIA_URL_UPDATED; } mMedia->mMediaURL = media_url; - mMedia->mPassedWhitelist = FALSE; + mMedia->mPassedWhitelist = false; } } return retval; @@ -1324,7 +1324,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, setMaterial(material); if (mDrawable.notNull()) { - gPipeline.markMoved(mDrawable, FALSE); // undamped + gPipeline.markMoved(mDrawable, false); // undamped } } setClickAction(click_action); @@ -1505,7 +1505,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, std::unordered_map::iterator iter; for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) { - iter->second->in_use = FALSE; + iter->second->in_use = false; } // Unpack extra parameters @@ -1537,7 +1537,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, if (!iter->second->in_use) { // Send an update message in case it was formerly in use - parameterChanged(iter->first, iter->second->data, FALSE, false); + parameterChanged(iter->first, iter->second->data, false, false); } } @@ -1732,7 +1732,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, setMaterial(material); if (mDrawable.notNull()) { - gPipeline.markMoved(mDrawable, FALSE); // undamped + gPipeline.markMoved(mDrawable, false); // undamped } } dp->unpackU8(click_action, "ClickAction"); @@ -1841,7 +1841,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, std::unordered_map::iterator iter; for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) { - iter->second->in_use = FALSE; + iter->second->in_use = false; } // Unpack extra params @@ -1864,7 +1864,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, if (!iter->second->in_use) { // Send an update message in case it was formerly in use - parameterChanged(iter->first, iter->second->data, FALSE, false); + parameterChanged(iter->first, iter->second->data, false, false); } } @@ -1911,7 +1911,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // // Fix object parenting. // - BOOL b_changed_status = FALSE; + bool b_changed_status = false; if (OUT_TERSE_IMPROVED != update_type) { @@ -1970,7 +1970,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // // new parent is valid - b_changed_status = TRUE; + b_changed_status = true; // ...no current parent, so don't try to remove child if (mDrawable.notNull()) { @@ -2001,7 +2001,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // make sure this object gets a non-damped update if (sent_parentp->mDrawable.notNull()) { - gPipeline.markMoved(sent_parentp->mDrawable, FALSE); // undamped + gPipeline.markMoved(sent_parentp->mDrawable, false); // undamped } } } @@ -2011,7 +2011,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, } // Show particles, icon and HUD - hideExtraDisplayItems( FALSE ); + hideExtraDisplayItems( false ); setChanged(MOVED | SILHOUETTE); } @@ -2038,7 +2038,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, gObjectList.orphanize(this, parent_id, ip, port); // Hide particles, icon and HUD - hideExtraDisplayItems( TRUE ); + hideExtraDisplayItems( true ); } } } @@ -2129,7 +2129,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, if (sent_parentp && sent_parentp != cur_parentp && sent_parentp != this) { // New parent is valid, detach and reattach - b_changed_status = TRUE; + b_changed_status = true; if (mDrawable.notNull()) { if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 2 @@ -2156,7 +2156,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, sent_parentp->setChanged(MOVED | SILHOUETTE); if (sent_parentp->mDrawable.notNull()) { - gPipeline.markMoved(sent_parentp->mDrawable, FALSE); // undamped + gPipeline.markMoved(sent_parentp->mDrawable, false); // undamped } } else if (!sent_parentp) @@ -2178,7 +2178,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, if (remove_parent) { - b_changed_status = TRUE; + b_changed_status = true; if (mDrawable.notNull()) { // clear parent to removeChild can put the drawable on the damped list @@ -2192,7 +2192,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, if (mDrawable.notNull()) { // make sure this object gets a non-damped update - gPipeline.markMoved(mDrawable, FALSE); // undamped + gPipeline.markMoved(mDrawable, false); // undamped } } } @@ -2341,11 +2341,11 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, (MAG_CUTOFF >= accel_mag_sq) && (MAG_CUTOFF >= getAngularVelocity().magVecSquared())) { - mStatic = TRUE; // This object doesn't move! + mStatic = true; // This object doesn't move! } else { - mStatic = FALSE; + mStatic = false; } // BUG: This code leads to problems during group rotate and any scale operation. @@ -2360,7 +2360,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, // // Additionally, if any child is selected, need to update the dialogs and selection // center. - BOOL needs_refresh = mUserSelected; + bool needs_refresh = mUserSelected; for (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) { @@ -2368,7 +2368,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, needs_refresh = needs_refresh || child->mUserSelected; } - static LLCachedControl allow_select_avatar(gSavedSettings, "AllowSelectAvatar", FALSE); + static LLCachedControl allow_select_avatar(gSavedSettings, "AllowSelectAvatar", false); if (needs_refresh) { LLSelectMgr::getInstance()->updateSelectionCenter(); @@ -2412,9 +2412,9 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, return retval; } -BOOL LLViewerObject::isActive() const +bool LLViewerObject::isActive() const { - return TRUE; + return true; } //load flags from cache or from message @@ -2457,7 +2457,7 @@ void LLViewerObject::idleUpdate(LLAgent &agent, const F64 &frame_time) } } - updateDrawable(FALSE); + updateDrawable(false); } } @@ -2682,7 +2682,7 @@ void LLViewerObject::doUpdateInventory( LLUUID item_id; LLUUID new_owner; LLUUID new_group; - BOOL group_owned = FALSE; + bool group_owned = false; if(old_item) { item_id = old_item->getUUID(); @@ -2754,7 +2754,7 @@ void LLViewerObject::doUpdateInventory( // of the new and old script AFTER the bytecode has been saved. void LLViewerObject::saveScript( const LLViewerInventoryItem* item, - BOOL active, + bool active, bool is_new) { /* @@ -2828,7 +2828,7 @@ void LLViewerObject::dirtyInventory() delete mInventory; mInventory = NULL; } - mInventoryDirty = TRUE; + mInventoryDirty = true; } void LLViewerObject::registerInventoryListener(LLVOInventoryListener* listener, void* user_data) @@ -2857,7 +2857,7 @@ void LLViewerObject::removeInventoryListener(LLVOInventoryListener* listener) } } -BOOL LLViewerObject::isInventoryPending() +bool LLViewerObject::isInventoryPending() { return mInvRequestState != INVENTORY_REQUEST_STOPPED; } @@ -2891,7 +2891,7 @@ void LLViewerObject::requestInventory() else { // since we are going to request it now - mInventoryDirty = FALSE; + mInventoryDirty = false; // Note: throws away duplicate requests fetchInventoryFromServer(); @@ -3263,7 +3263,7 @@ void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code, LLExtS delete ft; } -BOOL LLViewerObject::loadTaskInvFile(const std::string& filename) +bool LLViewerObject::loadTaskInvFile(const std::string& filename) { std::string filename_and_local_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, filename); llifstream ifs(filename_and_local_path.c_str()); @@ -3325,11 +3325,11 @@ BOOL LLViewerObject::loadTaskInvFile(const std::string& filename) { LL_WARNS() << "unable to load task inventory: " << filename_and_local_path << LL_ENDL; - return FALSE; + return false; } doInventoryCallback(); - return TRUE; + return true; } void LLViewerObject::doInventoryCallback() @@ -3661,7 +3661,7 @@ bool LLViewerObject::updateLOD() return false; } -BOOL LLViewerObject::updateGeometry(LLDrawable *drawable) +bool LLViewerObject::updateGeometry(LLDrawable *drawable) { return false; } @@ -3681,7 +3681,7 @@ LLDrawable* LLViewerObject::createDrawable(LLPipeline *pipeline) return NULL; } -void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped) +void LLViewerObject::setScale(const LLVector3 &scale, bool damped) { LLPrimitive::setScale(scale); if (mDrawable.notNull()) @@ -3701,7 +3701,7 @@ void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped) llassert_always(LLWorld::getInstance()->getRegionFromHandle(getRegion()->getHandle())); gObjectList.addToMap(this); - mOnMap = TRUE; + mOnMap = true; } } else @@ -3709,7 +3709,7 @@ void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped) if (mOnMap) { gObjectList.removeFromMap(this); - mOnMap = FALSE; + mOnMap = false; } } } @@ -3741,7 +3741,7 @@ void LLViewerObject::setLinksetCost(F32 cost) mLinksetCost = cost; mCostStale = false; - BOOL needs_refresh = isSelected(); + bool needs_refresh = isSelected(); child_list_t::iterator iter = mChildList.begin(); while(iter != mChildList.end() && !needs_refresh) { @@ -4013,7 +4013,7 @@ void LLViewerObject::updateTextures() { } -void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */) +void LLViewerObject::boostTexturePriority(bool boost_children /* = true */) { if (isDead() || !getVolume()) { @@ -4031,7 +4031,7 @@ void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */) { LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); LLUUID sculpt_id = sculpt_params->getSculptTexture(); - LLViewerTextureManager::getFetchedTexture(sculpt_id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)->setBoostLevel(LLGLTexture::BOOST_SELECTED); + LLViewerTextureManager::getFetchedTexture(sculpt_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)->setBoostLevel(LLGLTexture::BOOST_SELECTED); } if (boost_children) @@ -4124,7 +4124,7 @@ void LLViewerObject::addNVPair(const std::string& data) mNameValuePairs[nv->mName] = nv; } -BOOL LLViewerObject::removeNVPair(const std::string& name) +bool LLViewerObject::removeNVPair(const std::string& name) { char* canonical_name = gNVNameTable.addString(name); @@ -4150,14 +4150,14 @@ BOOL LLViewerObject::removeNVPair(const std::string& name) // Remove the NV pair from the local list. delete nv; mNameValuePairs.erase(iter); - return TRUE; + return true; } else { LL_DEBUGS() << "removeNVPair - No region for object" << LL_ENDL; } } - return FALSE; + return false; } @@ -4357,7 +4357,7 @@ const LLQuaternion LLViewerObject::getRotationEdit() const return global_rotation; } -void LLViewerObject::setPositionAbsoluteGlobal( const LLVector3d &pos_global, BOOL damped ) +void LLViewerObject::setPositionAbsoluteGlobal( const LLVector3d &pos_global, bool damped ) { if (isAttachment()) { @@ -4406,7 +4406,7 @@ void LLViewerObject::setPositionAbsoluteGlobal( const LLVector3d &pos_global, BO gPipeline.updateMoveNormalAsync(mDrawable); } -void LLViewerObject::setPosition(const LLVector3 &pos, BOOL damped) +void LLViewerObject::setPosition(const LLVector3 &pos, bool damped) { if (getPosition() != pos) { @@ -4422,7 +4422,7 @@ void LLViewerObject::setPosition(const LLVector3 &pos, BOOL damped) } } -void LLViewerObject::setPositionGlobal(const LLVector3d &pos_global, BOOL damped) +void LLViewerObject::setPositionGlobal(const LLVector3d &pos_global, bool damped) { if (isAttachment()) { @@ -4482,7 +4482,7 @@ void LLViewerObject::setPositionGlobal(const LLVector3d &pos_global, BOOL damped } -void LLViewerObject::setPositionParent(const LLVector3 &pos_parent, BOOL damped) +void LLViewerObject::setPositionParent(const LLVector3 &pos_parent, bool damped) { // Set position relative to parent, if no parent, relative to region if (!isRoot()) @@ -4496,7 +4496,7 @@ void LLViewerObject::setPositionParent(const LLVector3 &pos_parent, BOOL damped) } } -void LLViewerObject::setPositionRegion(const LLVector3 &pos_region, BOOL damped) +void LLViewerObject::setPositionRegion(const LLVector3 &pos_region, bool damped) { if (!isRootEdit()) { @@ -4511,7 +4511,7 @@ void LLViewerObject::setPositionRegion(const LLVector3 &pos_region, BOOL damped) } } -void LLViewerObject::setPositionAgent(const LLVector3 &pos_agent, BOOL damped) +void LLViewerObject::setPositionAgent(const LLVector3 &pos_agent, bool damped) { LLVector3 pos_region = getRegion()->getPosRegionFromAgent(pos_agent); setPositionRegion(pos_region, damped); @@ -4521,7 +4521,7 @@ void LLViewerObject::setPositionAgent(const LLVector3 &pos_agent, BOOL damped) // and doesn't also move the joint-parent // TODO -- implement similar intelligence for joint-parents toward // their joint-children -void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, BOOL damped) +void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, bool damped) { if (!isRootEdit()) { @@ -4552,11 +4552,11 @@ LLViewerObject* LLViewerObject::getRootEdit() const } -BOOL LLViewerObject::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, +bool LLViewerObject::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, - BOOL pick_transparent, - BOOL pick_rigged, - BOOL pick_unselectable, + bool pick_transparent, + bool pick_rigged, + bool pick_unselectable, S32* face_hit, LLVector4a* intersection, LLVector2* tex_coord, @@ -4566,11 +4566,11 @@ BOOL LLViewerObject::lineSegmentIntersect(const LLVector4a& start, const LLVecto return false; } -BOOL LLViewerObject::lineSegmentBoundingBox(const LLVector4a& start, const LLVector4a& end) +bool LLViewerObject::lineSegmentBoundingBox(const LLVector4a& start, const LLVector4a& end) { if (mDrawable.isNull() || mDrawable->isDead()) { - return FALSE; + return false; } const LLVector4a* ext = mDrawable->getSpatialExtents(); @@ -4630,20 +4630,20 @@ void LLViewerObject::setMediaURL(const std::string& media_url) { mMedia = new LLViewerObjectMedia; mMedia->mMediaURL = media_url; - mMedia->mPassedWhitelist = FALSE; + mMedia->mPassedWhitelist = false; // TODO: update materials with new image } else if (mMedia->mMediaURL != media_url) { mMedia->mMediaURL = media_url; - mMedia->mPassedWhitelist = FALSE; + mMedia->mPassedWhitelist = false; // TODO: update materials with new image } } -BOOL LLViewerObject::getMediaPassedWhitelist() const +bool LLViewerObject::getMediaPassedWhitelist() const { if (mMedia) { @@ -4651,11 +4651,11 @@ BOOL LLViewerObject::getMediaPassedWhitelist() const } else { - return FALSE; + return false; } } -void LLViewerObject::setMediaPassedWhitelist(BOOL passed) +void LLViewerObject::setMediaPassedWhitelist(bool passed) { if (mMedia) { @@ -4829,7 +4829,7 @@ LLViewerTexture* LLViewerObject::getBakedTextureForMagicId(const LLUUID& id) LLViewerObject *root = getRootEdit(); if (root && root->isAnimatedObject()) { - return LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + return LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); } LLVOAvatar* avatar = getAvatar(); @@ -4839,7 +4839,7 @@ LLViewerTexture* LLViewerObject::getBakedTextureForMagicId(const LLUUID& id) LLViewerTexture* bakedTexture = avatar->getBakedTexture(texIndex); if (bakedTexture == NULL || bakedTexture->isMissingAsset()) { - return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); } else { @@ -4848,7 +4848,7 @@ LLViewerTexture* LLViewerObject::getBakedTextureForMagicId(const LLUUID& id) } else { - return LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + return LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); } } @@ -4885,7 +4885,7 @@ void LLViewerObject::setTE(const U8 te, const LLTextureEntry& texture_entry) const LLUUID& image_id = getTE(te)->getID(); LLViewerTexture* bakedTexture = getBakedTextureForMagicId(image_id); - mTEImages[te] = bakedTexture ? bakedTexture : LLViewerTextureManager::getFetchedTexture(image_id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + mTEImages[te] = bakedTexture ? bakedTexture : LLViewerTextureManager::getFetchedTexture(image_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); updateAvatarMeshVisibility(image_id, old_image_id); @@ -4897,10 +4897,10 @@ void LLViewerObject::updateTEMaterialTextures(U8 te) if (getTE(te)->getMaterialParams().notNull()) { const LLUUID& norm_id = getTE(te)->getMaterialParams()->getNormalID(); - mTENormalMaps[te] = LLViewerTextureManager::getFetchedTexture(norm_id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + mTENormalMaps[te] = LLViewerTextureManager::getFetchedTexture(norm_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); const LLUUID& spec_id = getTE(te)->getMaterialParams()->getSpecularID(); - mTESpecularMaps[te] = LLViewerTextureManager::getFetchedTexture(spec_id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + mTESpecularMaps[te] = LLViewerTextureManager::getFetchedTexture(spec_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); } LLFetchedGLTFMaterial* mat = (LLFetchedGLTFMaterial*) getTE(te)->getGLTFRenderMaterial(); @@ -4947,8 +4947,8 @@ void LLViewerObject::updateTEMaterialTextures(U8 te) } else { - img = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - img->addTextureStats(64.f * 64.f, TRUE); + img = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + img->addTextureStats(64.f * 64.f, true); } } @@ -5098,21 +5098,21 @@ S32 LLViewerObject::setTETexture(const U8 te, const LLUUID& uuid) { // Invalid host == get from the agent's sim LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture( - uuid, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); + uuid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); return setTETextureCore(te, image); } S32 LLViewerObject::setTENormalMap(const U8 te, const LLUUID& uuid) { LLViewerFetchedTexture *image = (uuid == LLUUID::null) ? NULL : LLViewerTextureManager::getFetchedTexture( - uuid, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); + uuid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); return setTENormalMapCore(te, image); } S32 LLViewerObject::setTESpecularMap(const U8 te, const LLUUID& uuid) { LLViewerFetchedTexture *image = (uuid == LLUUID::null) ? NULL : LLViewerTextureManager::getFetchedTexture( - uuid, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); + uuid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); return setTESpecularMapCore(te, image); } @@ -5652,8 +5652,8 @@ void LLViewerObject::setDebugText(const std::string &utf8text, const LLColor4& c } mText->setColor(color); mText->setString(utf8text); - mText->setZCompare(FALSE); - mText->setDoFade(FALSE); + mText->setZCompare(false); + mText->setDoFade(false); updateText(); } @@ -5669,8 +5669,8 @@ void LLViewerObject::appendDebugText(const std::string &utf8text) initHudText(); } mText->addLine(utf8text, LLColor4::white); - mText->setZCompare(FALSE); - mText->setDoFade(FALSE); + mText->setZCompare(false); + mText->setDoFade(false); updateText(); } @@ -5703,8 +5703,8 @@ void LLViewerObject::restoreHudText() else { // Restore default values - mText->setZCompare(TRUE); - mText->setDoFade(TRUE); + mText->setZCompare(true); + mText->setDoFade(true); } mText->setColor(mHudTextColor); mText->setString(mHudText); @@ -5745,7 +5745,7 @@ const LLViewerObject* LLViewerObject::getSubParent() const return (const LLViewerObject*) getParent(); } -BOOL LLViewerObject::isOnMap() +bool LLViewerObject::isOnMap() { return mOnMap; } @@ -5829,7 +5829,7 @@ LLVOAvatar* LLViewerObject::getAvatarAncestor() return NULL; } -BOOL LLViewerObject::isParticleSource() const +bool LLViewerObject::isParticleSource() const { return !mPartSourcep.isNull() && !mPartSourcep->isDead(); } @@ -5970,7 +5970,7 @@ void LLViewerObject::deleteParticleSource() } // virtual -void LLViewerObject::updateDrawable(BOOL force_damped) +void LLViewerObject::updateDrawable(bool force_damped) { if (!isChanged(MOVED)) { //most common case, having an empty if case here makes for better branch prediction @@ -5978,7 +5978,7 @@ void LLViewerObject::updateDrawable(BOOL force_damped) else if (mDrawable.notNull() && !mDrawable->isState(LLDrawable::ON_MOVE_LIST)) { - BOOL damped_motion = + bool damped_motion = !isChanged(SHIFTED) && // not shifted between regions this frame and... ( force_damped || // ...forced into damped motion by application logic or... ( !isSelected() && // ...not selected and... @@ -6057,7 +6057,7 @@ void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& ow if (mAudioSourcep) { - BOOL queue = flags & LL_SOUND_FLAG_QUEUE; + bool queue = flags & LL_SOUND_FLAG_QUEUE; mAudioGain = gain; mAudioSourcep->setGain(gain); mAudioSourcep->setLoop(flags & LL_SOUND_FLAG_LOOP); @@ -6119,8 +6119,8 @@ bool LLViewerObject::unpackParameterEntry(U16 param_type, LLDataPacker *dp) if (param) { param->data->unpack(*dp); - param->in_use = TRUE; - parameterChanged(param_type, param->data, TRUE, false); + param->in_use = true; + parameterChanged(param_type, param->data, true, false); return true; } else @@ -6222,7 +6222,7 @@ LLNetworkData* LLViewerObject::getParameterEntry(U16 param_type) const } } -BOOL LLViewerObject::getParameterEntryInUse(U16 param_type) const +bool LLViewerObject::getParameterEntryInUse(U16 param_type) const { ExtraParameter* param = getExtraParameterEntry(param_type); if (param) @@ -6231,7 +6231,7 @@ BOOL LLViewerObject::getParameterEntryInUse(U16 param_type) const } else { - return FALSE; + return false; } } @@ -6246,7 +6246,7 @@ bool LLViewerObject::setParameterEntry(U16 param_type, const LLNetworkData& new_ } param->in_use = true; param->data->copy(new_value); - parameterChanged(param_type, param->data, TRUE, local_origin); + parameterChanged(param_type, param->data, true, local_origin); return true; } else @@ -6256,9 +6256,9 @@ bool LLViewerObject::setParameterEntry(U16 param_type, const LLNetworkData& new_ } // Assumed to be called locally -// If in_use is TRUE, will crate a new extra parameter if none exists. +// If in_use is true, will crate a new extra parameter if none exists. // Should always return true. -bool LLViewerObject::setParameterEntryInUse(U16 param_type, BOOL in_use, bool local_origin) +bool LLViewerObject::setParameterEntryInUse(U16 param_type, bool in_use, bool local_origin) { ExtraParameter* param = getExtraParameterEntryCreate(param_type); if (param && param->in_use != in_use) @@ -6279,7 +6279,7 @@ void LLViewerObject::parameterChanged(U16 param_type, bool local_origin) } } -void LLViewerObject::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin) +void LLViewerObject::parameterChanged(U16 param_type, LLNetworkData* data, bool in_use, bool local_origin) { if (local_origin) { @@ -6329,7 +6329,7 @@ void LLViewerObject::parameterChanged(U16 param_type, LLNetworkData* data, BOOL } } -void LLViewerObject::setDrawableState(U32 state, BOOL recursive) +void LLViewerObject::setDrawableState(U32 state, bool recursive) { if (mDrawable) { @@ -6346,7 +6346,7 @@ void LLViewerObject::setDrawableState(U32 state, BOOL recursive) } } -void LLViewerObject::clearDrawableState(U32 state, BOOL recursive) +void LLViewerObject::clearDrawableState(U32 state, bool recursive) { if (mDrawable) { @@ -6363,9 +6363,9 @@ void LLViewerObject::clearDrawableState(U32 state, BOOL recursive) } } -BOOL LLViewerObject::isDrawableState(U32 state, BOOL recursive) const +bool LLViewerObject::isDrawableState(U32 state, bool recursive) const { - BOOL matches = FALSE; + bool matches = false; if (mDrawable) { matches = mDrawable->isState(state); @@ -6390,7 +6390,7 @@ BOOL LLViewerObject::isDrawableState(U32 state, BOOL recursive) const //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Owned by anyone? -BOOL LLViewerObject::permAnyOwner() const +bool LLViewerObject::permAnyOwner() const { if (isRootEdit()) { @@ -6402,18 +6402,18 @@ BOOL LLViewerObject::permAnyOwner() const } } // Owned by this viewer? -BOOL LLViewerObject::permYouOwner() const +bool LLViewerObject::permYouOwner() const { if (isRootEdit()) { #ifdef HACKED_GODLIKE_VIEWER - return TRUE; + return true; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { - return TRUE; + return true; } # endif return flagObjectYouOwner(); @@ -6426,7 +6426,7 @@ BOOL LLViewerObject::permYouOwner() const } // Owned by a group? -BOOL LLViewerObject::permGroupOwner() const +bool LLViewerObject::permGroupOwner() const { if (isRootEdit()) { @@ -6439,18 +6439,18 @@ BOOL LLViewerObject::permGroupOwner() const } // Can the owner edit -BOOL LLViewerObject::permOwnerModify() const +bool LLViewerObject::permOwnerModify() const { if (isRootEdit()) { #ifdef HACKED_GODLIKE_VIEWER - return TRUE; + return true; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { - return TRUE; + return true; } # endif return flagObjectOwnerModify(); @@ -6463,18 +6463,18 @@ BOOL LLViewerObject::permOwnerModify() const } // Can edit -BOOL LLViewerObject::permModify() const +bool LLViewerObject::permModify() const { if (isRootEdit()) { #ifdef HACKED_GODLIKE_VIEWER - return TRUE; + return true; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { - return TRUE; + return true; } # endif return flagObjectModify(); @@ -6487,18 +6487,18 @@ BOOL LLViewerObject::permModify() const } // Can copy -BOOL LLViewerObject::permCopy() const +bool LLViewerObject::permCopy() const { if (isRootEdit()) { #ifdef HACKED_GODLIKE_VIEWER - return TRUE; + return true; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { - return TRUE; + return true; } # endif return flagObjectCopy(); @@ -6511,18 +6511,18 @@ BOOL LLViewerObject::permCopy() const } // Can move -BOOL LLViewerObject::permMove() const +bool LLViewerObject::permMove() const { if (isRootEdit()) { #ifdef HACKED_GODLIKE_VIEWER - return TRUE; + return true; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { - return TRUE; + return true; } # endif return flagObjectMove(); @@ -6535,18 +6535,18 @@ BOOL LLViewerObject::permMove() const } // Can be transferred -BOOL LLViewerObject::permTransfer() const +bool LLViewerObject::permTransfer() const { if (isRootEdit()) { #ifdef HACKED_GODLIKE_VIEWER - return TRUE; + return true; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER if (!LLGridManager::getInstance()->isInProductionGrid() && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) { - return TRUE; + return true; } # endif return flagObjectTransfer(); @@ -6560,7 +6560,7 @@ BOOL LLViewerObject::permTransfer() const // Can only open objects that you own, or that someone has // given you modify rights to. JC -BOOL LLViewerObject::allowOpen() const +bool LLViewerObject::allowOpen() const { return !flagInventoryEmpty() && (permYouOwner() || permModify()); } @@ -6651,7 +6651,7 @@ void LLViewerObject::setRegion(LLViewerRegion *regionp) } setChanged(MOVED | SILHOUETTE); - updateDrawable(FALSE); + updateDrawable(false); } // virtual @@ -6675,7 +6675,7 @@ bool LLViewerObject::specialHoverCursor() const || (mClickAction != 0); } -void LLViewerObject::updateFlags(BOOL physics_changed) +void LLViewerObject::updateFlags(bool physics_changed) { LLViewerRegion* regionp = getRegion(); if(!regionp) return; @@ -6688,10 +6688,10 @@ void LLViewerObject::updateFlags(BOOL physics_changed) gMessageSystem->addBOOL("IsTemporary", flagTemporaryOnRez() ); gMessageSystem->addBOOL("IsPhantom", flagPhantom() ); - // stinson 02/28/2012 : This CastsShadows BOOL is no longer used in either the viewer or the simulator + // stinson 02/28/2012 : This CastsShadows bool is no longer used in either the viewer or the simulator // The simulator code does not even unpack this value when the message is received. // This could be potentially hijacked in the future for another use should the urgent need arise. - gMessageSystem->addBOOL("CastsShadows", FALSE ); + gMessageSystem->addBOOL("CastsShadows", false ); if (physics_changed) { @@ -6705,9 +6705,9 @@ void LLViewerObject::updateFlags(BOOL physics_changed) gMessageSystem->sendReliable( regionp->getHost() ); } -BOOL LLViewerObject::setFlags(U32 flags, BOOL state) +bool LLViewerObject::setFlags(U32 flags, bool state) { - BOOL setit = setFlagsWithoutUpdate(flags, state); + bool setit = setFlagsWithoutUpdate(flags, state); // BUG: Sometimes viewer physics and simulator physics get // out of sync. To fix this, always send update to simulator. @@ -6718,15 +6718,15 @@ BOOL LLViewerObject::setFlags(U32 flags, BOOL state) return setit; } -BOOL LLViewerObject::setFlagsWithoutUpdate(U32 flags, BOOL state) +bool LLViewerObject::setFlagsWithoutUpdate(U32 flags, bool state) { - BOOL setit = FALSE; + bool setit = false; if (state) { if ((mFlags & flags) != flags) { mFlags |= flags; - setit = TRUE; + setit = true; } } else @@ -6734,7 +6734,7 @@ BOOL LLViewerObject::setFlagsWithoutUpdate(U32 flags, BOOL state) if ((mFlags & flags) != 0) { mFlags &= ~flags; - setit = TRUE; + setit = true; } } return setit; @@ -6857,12 +6857,12 @@ void LLAlphaObject::getBlendFunc(S32 face, LLRender::eBlendFactor& src, LLRender } // virtual -void LLStaticViewerObject::updateDrawable(BOOL force_damped) +void LLStaticViewerObject::updateDrawable(bool force_damped) { // Force an immediate rebuild on any update if (mDrawable.notNull()) { - mDrawable->updateXform(TRUE); + mDrawable->updateXform(true); gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL); } clearChanged(SHIFTED); @@ -6940,8 +6940,8 @@ void LLViewerObject::resetChildrenRotationAndPosition(const std::vectormDrawable->mXform.setPosition(reset_pos); ((LLVOAvatar*)childp)->mDrawable->mXform.setRotation(reset_rot) ; - ((LLVOAvatar*)childp)->mDrawable->getVObj()->setPosition(reset_pos, TRUE); - ((LLVOAvatar*)childp)->mDrawable->getVObj()->setRotation(reset_rot, TRUE) ; + ((LLVOAvatar*)childp)->mDrawable->getVObj()->setPosition(reset_pos, true); + ((LLVOAvatar*)childp)->mDrawable->getVObj()->setRotation(reset_rot, true) ; LLManip::rebuild(childp); } @@ -6953,7 +6953,7 @@ void LLViewerObject::resetChildrenRotationAndPosition(const std::vector Date: Mon, 6 May 2024 16:52:34 +0200 Subject: secondlife/viewer#1333 BOOL to bool conversion leftovers: ternaries --- indra/newview/llviewerobject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llviewerobject.cpp') diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 668596b96c..8b9a59a79b 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -2711,7 +2711,7 @@ void LLViewerObject::doUpdateInventory( deleteInventoryItem(item_id); LLPermissions perm(item->getPermissions()); LLPermissions* obj_perm = LLSelectMgr::getInstance()->findObjectPermissions(this); - bool is_atomic = ((S32)LLAssetType::AT_OBJECT == item->getType()) ? false : true; + bool is_atomic = (S32)LLAssetType::AT_OBJECT != item->getType(); if(obj_perm) { perm.setOwnerAndGroup(LLUUID::null, obj_perm->getOwner(), obj_perm->getGroup(), is_atomic); -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/newview/llviewerobject.cpp | 14762 ++++++++++++++++++------------------- 1 file changed, 7381 insertions(+), 7381 deletions(-) (limited to 'indra/newview/llviewerobject.cpp') diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index ec2078a365..c791a30d1a 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -1,7381 +1,7381 @@ -/** - * @file llviewerobject.cpp - * @brief Base class for viewer objects - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llviewerobject.h" - -#include "llaudioengine.h" -#include "indra_constants.h" -#include "llmath.h" -#include "llflexibleobject.h" -#include "llviewercontrol.h" -#include "lldatapacker.h" -#include "llfasttimer.h" -#include "llfloaterreg.h" -#include "llfontgl.h" -#include "llframetimer.h" -#include "llhudicon.h" -#include "llinventory.h" -#include "llinventorydefines.h" -#include "llmaterialtable.h" -#include "llmutelist.h" -#include "llnamevalue.h" -#include "llprimitive.h" -#include "llquantize.h" -#include "llregionhandle.h" -#include "llsdserialize.h" -#include "lltree_common.h" -#include "llxfermanager.h" -#include "message.h" -#include "object_flags.h" - -#include "llaudiosourcevo.h" -#include "llagent.h" -#include "llagentcamera.h" -#include "llagentwearables.h" -#include "llbbox.h" -#include "llbox.h" -#include "llcylinder.h" -#include "llcontrolavatar.h" -#include "lldrawable.h" -#include "llface.h" -#include "llfloatertools.h" -#include "llfollowcam.h" -#include "llhudtext.h" -#include "llselectmgr.h" -#include "llrendersphere.h" -#include "lltooldraganddrop.h" -#include "lluiavatar.h" -#include "llviewercamera.h" -#include "llviewertexturelist.h" -#include "llviewerinventory.h" -#include "llviewerobjectlist.h" -#include "llviewerparceloverlay.h" -#include "llviewerpartsource.h" -#include "llviewerregion.h" -#include "llviewerstats.h" -#include "llviewertextureanim.h" -#include "llviewerwindow.h" // For getSpinAxis -#include "llvoavatar.h" -#include "llvoavatarself.h" -#include "llvograss.h" -#include "llvosky.h" -#include "llvolume.h" -#include "llvolumemessage.h" -#include "llvopartgroup.h" -#include "llvosurfacepatch.h" -#include "llvotree.h" -#include "llvovolume.h" -#include "llvowater.h" -#include "llworld.h" -#include "llui.h" -#include "pipeline.h" -#include "llviewernetwork.h" -#include "llvowlsky.h" -#include "llmanip.h" -#include "lltrans.h" -#include "llsdutil.h" -#include "llmediaentry.h" -#include "llfloaterperms.h" -#include "llvocache.h" -#include "llcleanup.h" -#include "llcallstack.h" -#include "llmeshrepository.h" -#include "llgltfmateriallist.h" -#include "llgl.h" - -//#define DEBUG_UPDATE_TYPE - -bool LLViewerObject::sVelocityInterpolate = true; -bool LLViewerObject::sPingInterpolate = true; - -U32 LLViewerObject::sNumZombieObjects = 0; -S32 LLViewerObject::sNumObjects = 0; -bool LLViewerObject::sMapDebug = true; -LLColor4 LLViewerObject::sEditSelectColor( 1.0f, 1.f, 0.f, 0.3f); // Edit OK -LLColor4 LLViewerObject::sNoEditSelectColor( 1.0f, 0.f, 0.f, 0.3f); // Can't edit -S32 LLViewerObject::sAxisArrowLength(50); - - -bool LLViewerObject::sPulseEnabled(false); -bool LLViewerObject::sUseSharedDrawables(false); // true - -// sMaxUpdateInterpolationTime must be greater than sPhaseOutUpdateInterpolationTime -F64Seconds LLViewerObject::sMaxUpdateInterpolationTime(3.0); // For motion interpolation: after X seconds with no updates, don't predict object motion -F64Seconds LLViewerObject::sPhaseOutUpdateInterpolationTime(2.0); // For motion interpolation: after Y seconds with no updates, taper off motion prediction -F64Seconds LLViewerObject::sMaxRegionCrossingInterpolationTime(1.0);// For motion interpolation: don't interpolate over this time on region crossing - -std::map LLViewerObject::sObjectDataMap; - -// The maximum size of an object extra parameters binary (packed) block -#define MAX_OBJECT_PARAMS_SIZE 1024 - -// At 45 Hz collisions seem stable and objects seem -// to settle down at a reasonable rate. -// JC 3/18/2003 - -const F32 PHYSICS_TIMESTEP = 1.f / 45.f; -const U32 MAX_INV_FILE_READ_FAILS = 25; -const S32 MAX_OBJECT_BINARY_DATA_SIZE = 60 + 16; - -const F64 INVENTORY_UPDATE_WAIT_TIME_DESYNC = 5; // seconds -const F64 INVENTORY_UPDATE_WAIT_TIME_OUTDATED = 1; - -// static -LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp, S32 flags) -{ - LL_PROFILE_ZONE_SCOPED; - LL_DEBUGS("ObjectUpdate") << "creating " << id << LL_ENDL; - dumpStack("ObjectUpdateStack"); - - LLViewerObject *res = NULL; - - if (gNonInteractive - && pcode != LL_PCODE_LEGACY_AVATAR - && pcode != LL_VO_SURFACE_PATCH - && pcode != LL_VO_WATER - && pcode != LL_VO_VOID_WATER - && pcode != LL_VO_WL_SKY - && pcode != LL_VO_SKY - && pcode != LL_VO_PART_GROUP - ) - { - return res; - } - switch (pcode) - { - case LL_PCODE_VOLUME: - { - res = new LLVOVolume(id, pcode, regionp); break; - break; - } - case LL_PCODE_LEGACY_AVATAR: - { - if (id == gAgentID) - { - if (!gAgentAvatarp) - { - gAgentAvatarp = new LLVOAvatarSelf(id, pcode, regionp); - gAgentAvatarp->initInstance(); - gAgentWearables.setAvatarObject(gAgentAvatarp); - } - else - { - if (isAgentAvatarValid()) - { - gAgentAvatarp->updateRegion(regionp); - } - } - res = gAgentAvatarp; - } - else if (flags & CO_FLAG_CONTROL_AVATAR) - { - LLControlAvatar *control_avatar = new LLControlAvatar(id, pcode, regionp); - control_avatar->initInstance(); - res = control_avatar; - } - else if (flags & CO_FLAG_UI_AVATAR) - { - LLUIAvatar *ui_avatar = new LLUIAvatar(id, pcode, regionp); - ui_avatar->initInstance(); - res = ui_avatar; - } - else - { - LLVOAvatar *avatar = new LLVOAvatar(id, pcode, regionp); - avatar->initInstance(); - res = avatar; - } - break; - } - case LL_PCODE_LEGACY_GRASS: - res = new LLVOGrass(id, pcode, regionp); break; - case LL_PCODE_LEGACY_PART_SYS: -// LL_WARNS() << "Creating old part sys!" << LL_ENDL; -// res = new LLVOPart(id, pcode, regionp); break; - res = NULL; break; - case LL_PCODE_LEGACY_TREE: - res = new LLVOTree(id, pcode, regionp); break; - case LL_PCODE_TREE_NEW: -// LL_WARNS() << "Creating new tree!" << LL_ENDL; -// res = new LLVOTree(id, pcode, regionp); break; - res = NULL; break; - case LL_VO_SURFACE_PATCH: - res = new LLVOSurfacePatch(id, pcode, regionp); break; - case LL_VO_SKY: - res = new LLVOSky(id, pcode, regionp); break; - case LL_VO_VOID_WATER: - res = new LLVOVoidWater(id, pcode, regionp); break; - case LL_VO_WATER: - res = new LLVOWater(id, pcode, regionp); break; - case LL_VO_PART_GROUP: - res = new LLVOPartGroup(id, pcode, regionp); break; - case LL_VO_HUD_PART_GROUP: - res = new LLVOHUDPartGroup(id, pcode, regionp); break; - case LL_VO_WL_SKY: - res = new LLVOWLSky(id, pcode, regionp); break; - default: - LL_WARNS() << "Unknown object pcode " << (S32)pcode << LL_ENDL; - res = NULL; break; - } - - return res; -} - -LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp, bool is_global) -: LLPrimitive(), - mChildList(), - mID(id), - mLocalID(0), - mTotalCRC(0), - mListIndex(-1), - mTEImages(NULL), - mTENormalMaps(NULL), - mTESpecularMaps(NULL), - mbCanSelect(true), - mFlags(0), - mPhysicsShapeType(0), - mPhysicsGravity(0), - mPhysicsFriction(0), - mPhysicsDensity(0), - mPhysicsRestitution(0), - mDrawable(), - mCreateSelected(false), - mRenderMedia(false), - mBestUpdatePrecision(0), - mText(), - mHudText(""), - mHudTextColor(LLColor4::white), - mControlAvatar(NULL), - mLastInterpUpdateSecs(0.f), - mLastMessageUpdateSecs(0.f), - mLatestRecvPacketID(0), - mRegionCrossExpire(0), - mData(NULL), - mAudioSourcep(NULL), - mAudioGain(1.f), - mSoundCutOffRadius(0.f), - mAppAngle(0.f), - mPixelArea(1024.f), - mInventory(NULL), - mInventorySerialNum(0), - mExpectedInventorySerialNum(0), - mInvRequestState(INVENTORY_REQUEST_STOPPED), - mInvRequestXFerId(0), - mInventoryDirty(false), - mRegionp(regionp), - mDead(false), - mOrphaned(false), - mUserSelected(false), - mOnActiveList(false), - mOnMap(false), - mStatic(false), - mSeatCount(0), - mNumFaces(0), - mRotTime(0.f), - mAngularVelocityRot(), - mPreviousRotation(), - mAttachmentState(0), - mMedia(NULL), - mClickAction(0), - mObjectCost(0), - mLinksetCost(0), - mPhysicsCost(0), - mLinksetPhysicsCost(0.f), - mCostStale(true), - mPhysicsShapeUnknown(true), - mAttachmentItemID(LLUUID::null), - mLastUpdateType(OUT_UNKNOWN), - mLastUpdateCached(false), - mCachedMuteListUpdateTime(0), - mCachedOwnerInMuteList(false), - mRiggedAttachedWarned(false) -{ - if (!is_global) - { - llassert(mRegionp); - } - - LLPrimitive::init_primitive(pcode); - - // CP: added 12/2/2005 - this was being initialised to 0, not the current frame time - mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds(); - - mPositionRegion = LLVector3(0.f, 0.f, 0.f); - - if (!is_global && mRegionp) - { - mPositionAgent = mRegionp->getOriginAgent(); - } - resetRot(); - - LLViewerObject::sNumObjects++; -} - -LLViewerObject::~LLViewerObject() -{ - deleteTEImages(); - - // unhook from reflection probe manager - if (mReflectionProbe.notNull()) - { - mReflectionProbe->mViewerObject = nullptr; - mReflectionProbe = nullptr; - } - - if(mInventory) - { - mInventory->clear(); // will deref and delete entries - delete mInventory; - mInventory = NULL; - } - - if (mPartSourcep) - { - mPartSourcep->setDead(); - mPartSourcep = NULL; - } - - if (mText) - { - // something recovered LLHUDText when object was already dead - mText->markDead(); - mText = NULL; - } - - // Delete memory associated with extra parameters. - std::unordered_map::iterator iter; - for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) - { - if(iter->second != NULL) - { - delete iter->second->data; - delete iter->second; - } - } - mExtraParameterList.clear(); - - for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ; - mNameValuePairs.clear(); - - delete[] mData; - mData = NULL; - - delete mMedia; - mMedia = NULL; - - sNumObjects--; - sNumZombieObjects--; - llassert(mChildList.size() == 0); - llassert(mControlAvatar.isNull()); // Should have been cleaned by now - if (mControlAvatar.notNull()) - { - mControlAvatar->markForDeath(); - mControlAvatar = NULL; - LL_WARNS() << "Dead object owned a live control avatar" << LL_ENDL; - } - - clearInventoryListeners(); -} - -void LLViewerObject::deleteTEImages() -{ - delete[] mTEImages; - mTEImages = NULL; - - if (mTENormalMaps != NULL) - { - delete[] mTENormalMaps; - mTENormalMaps = NULL; - } - - if (mTESpecularMaps != NULL) - { - delete[] mTESpecularMaps; - mTESpecularMaps = NULL; - } -} - -void LLViewerObject::markDead() -{ - if (!mDead) - { - LL_PROFILE_ZONE_SCOPED; - //LL_INFOS() << "Marking self " << mLocalID << " as dead." << LL_ENDL; - - // Root object of this hierarchy unlinks itself. - if (getParent()) - { - ((LLViewerObject *)getParent())->removeChild(this); - } - LLUUID mesh_id; - { - LLVOAvatar *av = getAvatar(); - if (av && LLVOAvatar::getRiggedMeshID(this,mesh_id)) - { - // This case is needed for indirectly attached mesh objects. - av->updateAttachmentOverrides(); - } - } - if (getControlAvatar()) - { - unlinkControlAvatar(); - } - - // Mark itself as dead - mDead = true; - if(mRegionp) - { - mRegionp->removeFromCreatedList(getLocalID()); - } - gObjectList.cleanupReferences(this); - - LLViewerObject *childp; - while (mChildList.size() > 0) - { - childp = mChildList.back(); - if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR) - { - //LL_INFOS() << "Marking child " << childp->getLocalID() << " as dead." << LL_ENDL; - childp->setParent(NULL); // LLViewerObject::markDead 1 - childp->markDead(); - } - else - { - // make sure avatar is no longer parented, - // so we can properly set it's position - childp->setDrawableParent(NULL); - ((LLVOAvatar*)childp)->getOffObject(); - childp->setParent(NULL); // LLViewerObject::markDead 2 - } - mChildList.pop_back(); - } - - if (mDrawable.notNull()) - { - // Drawables are reference counted, mark as dead, then nuke the pointer. - mDrawable->markDead(); - mDrawable = NULL; - } - - if (mText) - { - mText->markDead(); - mText = NULL; - } - - if (mIcon) - { - mIcon->markDead(); - mIcon = NULL; - } - - if (mPartSourcep) - { - mPartSourcep->setDead(); - mPartSourcep = NULL; - } - - if (mAudioSourcep) - { - // Do some cleanup - if (gAudiop) - { - gAudiop->cleanupAudioSource(mAudioSourcep); - } - mAudioSourcep = NULL; - } - - if (flagAnimSource()) - { - if (isAgentAvatarValid()) - { - // stop motions associated with this object - gAgentAvatarp->stopMotionFromSource(mID); - } - } - - if (flagCameraSource()) - { - LLFollowCamMgr::getInstance()->removeFollowCamParams(mID); - } - - if (mReflectionProbe.notNull()) - { - mReflectionProbe->mViewerObject = nullptr; - mReflectionProbe = nullptr; - } - - sNumZombieObjects++; - } -} - -void LLViewerObject::dump() const -{ - LL_INFOS() << "Type: " << pCodeToString(mPrimitiveCode) << LL_ENDL; - LL_INFOS() << "Drawable: " << (LLDrawable *)mDrawable << LL_ENDL; - LL_INFOS() << "Update Age: " << LLFrameTimer::getElapsedSeconds() - mLastMessageUpdateSecs << LL_ENDL; - - LL_INFOS() << "Parent: " << getParent() << LL_ENDL; - LL_INFOS() << "ID: " << mID << LL_ENDL; - LL_INFOS() << "LocalID: " << mLocalID << LL_ENDL; - LL_INFOS() << "PositionRegion: " << getPositionRegion() << LL_ENDL; - LL_INFOS() << "PositionAgent: " << getPositionAgent() << LL_ENDL; - LL_INFOS() << "PositionGlobal: " << getPositionGlobal() << LL_ENDL; - LL_INFOS() << "Velocity: " << getVelocity() << LL_ENDL; - if (mDrawable.notNull() && - mDrawable->getNumFaces() && - mDrawable->getFace(0)) - { - LLFacePool *poolp = mDrawable->getFace(0)->getPool(); - if (poolp) - { - LL_INFOS() << "Pool: " << poolp << LL_ENDL; - LL_INFOS() << "Pool reference count: " << poolp->mReferences.size() << LL_ENDL; - } - } - //LL_INFOS() << "BoxTree Min: " << mDrawable->getBox()->getMin() << LL_ENDL; - //LL_INFOS() << "BoxTree Max: " << mDrawable->getBox()->getMin() << LL_ENDL; - /* - LL_INFOS() << "Velocity: " << getVelocity() << LL_ENDL; - LL_INFOS() << "AnyOwner: " << permAnyOwner() << " YouOwner: " << permYouOwner() << " Edit: " << mPermEdit << LL_ENDL; - LL_INFOS() << "UsePhysics: " << flagUsePhysics() << " CanSelect " << mbCanSelect << " UserSelected " << mUserSelected << LL_ENDL; - LL_INFOS() << "AppAngle: " << mAppAngle << LL_ENDL; - LL_INFOS() << "PixelArea: " << mPixelArea << LL_ENDL; - - char buffer[1000]; - char *key; - for (key = mNameValuePairs.getFirstKey(); key; key = mNameValuePairs.getNextKey() ) - { - mNameValuePairs[key]->printNameValue(buffer); - LL_INFOS() << buffer << LL_ENDL; - } - for (child_list_t::iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - LL_INFOS() << " child " << child->getID() << LL_ENDL; - } - */ -} - -void LLViewerObject::printNameValuePairs() const -{ - for (name_value_map_t::const_iterator iter = mNameValuePairs.begin(); - iter != mNameValuePairs.end(); iter++) - { - LLNameValue* nv = iter->second; - LL_INFOS() << nv->printNameValue() << LL_ENDL; - } -} - -void LLViewerObject::initVOClasses() -{ - // Initialized shared class stuff first. - LLVOAvatar::initClass(); - LLVOTree::initClass(); - LL_INFOS() << "Viewer Object size: " << sizeof(LLViewerObject) << LL_ENDL; - LLVOGrass::initClass(); - LLVOWater::initClass(); - LLVOVolume::initClass(); - - initObjectDataMap(); -} - -void LLViewerObject::cleanupVOClasses() -{ - SUBSYSTEM_CLEANUP(LLVOGrass); - SUBSYSTEM_CLEANUP(LLVOWater); - SUBSYSTEM_CLEANUP(LLVOTree); - SUBSYSTEM_CLEANUP(LLVOAvatar); - SUBSYSTEM_CLEANUP(LLVOVolume); - - sObjectDataMap.clear(); -} - -//object data map for compressed && !OUT_TERSE_IMPROVED -//static -void LLViewerObject::initObjectDataMap() -{ - U32 count = 0; - - sObjectDataMap["ID"] = count; //full id //LLUUID - count += sizeof(LLUUID); - - sObjectDataMap["LocalID"] = count; //U32 - count += sizeof(U32); - - sObjectDataMap["PCode"] = count; //U8 - count += sizeof(U8); - - sObjectDataMap["State"] = count; //U8 - count += sizeof(U8); - - sObjectDataMap["CRC"] = count; //U32 - count += sizeof(U32); - - sObjectDataMap["Material"] = count; //U8 - count += sizeof(U8); - - sObjectDataMap["ClickAction"] = count; //U8 - count += sizeof(U8); - - sObjectDataMap["Scale"] = count; //LLVector3 - count += sizeof(LLVector3); - - sObjectDataMap["Pos"] = count; //LLVector3 - count += sizeof(LLVector3); - - sObjectDataMap["Rot"] = count; //LLVector3 - count += sizeof(LLVector3); - - sObjectDataMap["SpecialCode"] = count; //U32 - count += sizeof(U32); - - sObjectDataMap["Owner"] = count; //LLUUID - count += sizeof(LLUUID); - - sObjectDataMap["Omega"] = count; //LLVector3, when SpecialCode & 0x80 is set - count += sizeof(LLVector3); - - //ParentID is after Omega if there is Omega, otherwise is after Owner - sObjectDataMap["ParentID"] = count;//U32, when SpecialCode & 0x20 is set - count += sizeof(U32); - - //------- - //The rest items are not included here - //------- -} - -//static -void LLViewerObject::unpackVector3(LLDataPackerBinaryBuffer* dp, LLVector3& value, std::string name) -{ - dp->shift(sObjectDataMap[name]); - dp->unpackVector3(value, name.c_str()); - dp->reset(); -} - -//static -void LLViewerObject::unpackUUID(LLDataPackerBinaryBuffer* dp, LLUUID& value, std::string name) -{ - dp->shift(sObjectDataMap[name]); - dp->unpackUUID(value, name.c_str()); - dp->reset(); -} - -//static -void LLViewerObject::unpackU32(LLDataPackerBinaryBuffer* dp, U32& value, std::string name) -{ - dp->shift(sObjectDataMap[name]); - dp->unpackU32(value, name.c_str()); - dp->reset(); -} - -//static -void LLViewerObject::unpackU8(LLDataPackerBinaryBuffer* dp, U8& value, std::string name) -{ - dp->shift(sObjectDataMap[name]); - dp->unpackU8(value, name.c_str()); - dp->reset(); -} - -//static -U32 LLViewerObject::unpackParentID(LLDataPackerBinaryBuffer* dp, U32& parent_id) -{ - dp->shift(sObjectDataMap["SpecialCode"]); - U32 value; - dp->unpackU32(value, "SpecialCode"); - - parent_id = 0; - if(value & 0x20) - { - S32 offset = sObjectDataMap["ParentID"]; - if(!(value & 0x80)) - { - offset -= sizeof(LLVector3); - } - - dp->shift(offset); - dp->unpackU32(parent_id, "ParentID"); - } - dp->reset(); - - return parent_id; -} - -// Replaces all name value pairs with data from \n delimited list -// Does not update server -void LLViewerObject::setNameValueList(const std::string& name_value_list) -{ - // Clear out the old - for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ; - mNameValuePairs.clear(); - - // Bring in the new - std::string::size_type length = name_value_list.length(); - std::string::size_type start = 0; - while (start < length) - { - std::string::size_type end = name_value_list.find_first_of("\n", start); - if (end == std::string::npos) end = length; - if (end > start) - { - std::string tok = name_value_list.substr(start, end - start); - addNVPair(tok); - } - start = end+1; - } -} - -bool LLViewerObject::isAnySelected() const -{ - bool any_selected = isSelected(); - for (child_list_t::const_iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - const LLViewerObject* child = *iter; - any_selected = any_selected || child->isSelected(); - } - return any_selected; -} - -void LLViewerObject::setSelected(bool sel) -{ - mUserSelected = sel; - resetRot(); - - if (!sel) - { - setAllTESelected(false); - } -} - -// This method returns true if the object is over land owned by the -// agent. -bool LLViewerObject::isReturnable() -{ - if (isAttachment()) - { - return false; - } - - std::vector boxes; - boxes.push_back(LLBBox(getPositionRegion(), getRotationRegion(), getScale() * -0.5f, getScale() * 0.5f).getAxisAligned()); - for (child_list_t::iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - boxes.push_back( LLBBox(child->getPositionRegion(), child->getRotationRegion(), child->getScale() * -0.5f, child->getScale() * 0.5f).getAxisAligned()); - } - - bool result = (mRegionp && mRegionp->objectIsReturnable(getPositionRegion(), boxes)) ? 1 : 0; - - if ( !result ) - { - //Get list of neighboring regions relative to this vo's region - std::vector uniqueRegions; - mRegionp->getNeighboringRegions( uniqueRegions ); - - //Build aabb's - for root and all children - std::vector returnables; - typedef std::vector::iterator RegionIt; - RegionIt regionStart = uniqueRegions.begin(); - RegionIt regionEnd = uniqueRegions.end(); - - for (; regionStart != regionEnd; ++regionStart ) - { - LLViewerRegion* pTargetRegion = *regionStart; - //Add the root vo as there may be no children and we still want - //to test for any edge overlap - buildReturnablesForChildrenVO( returnables, this, pTargetRegion ); - //Add it's children - for (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) - { - LLViewerObject* pChild = *iter; - buildReturnablesForChildrenVO( returnables, pChild, pTargetRegion ); - } - } - - //TBD#Eventually create a region -> box list map - typedef std::vector::iterator ReturnablesIt; - ReturnablesIt retCurrentIt = returnables.begin(); - ReturnablesIt retEndIt = returnables.end(); - - for ( ; retCurrentIt !=retEndIt; ++retCurrentIt ) - { - boxes.clear(); - LLViewerRegion* pRegion = (*retCurrentIt).pRegion; - boxes.push_back( (*retCurrentIt).box ); - bool retResult = pRegion - && pRegion->childrenObjectReturnable( boxes ) - && pRegion->canManageEstate(); - if ( retResult ) - { - result = true; - break; - } - } - } - return result; -} - -void LLViewerObject::buildReturnablesForChildrenVO( std::vector& returnables, LLViewerObject* pChild, LLViewerRegion* pTargetRegion ) -{ - if ( !pChild ) - { - LL_ERRS()<<"child viewerobject is NULL "<mChildList.begin(); iter != pChild->mChildList.end(); iter++) - { - LLViewerObject* pChildofChild = *iter; - buildReturnablesForChildrenVO( returnables, pChildofChild, pTargetRegion ); - } -} - -void LLViewerObject::constructAndAddReturnable( std::vector& returnables, LLViewerObject* pChild, LLViewerRegion* pTargetRegion ) -{ - - LLVector3 targetRegionPos; - targetRegionPos.setVec( pChild->getPositionGlobal() ); - - LLBBox childBBox = LLBBox( targetRegionPos, pChild->getRotationRegion(), pChild->getScale() * -0.5f, - pChild->getScale() * 0.5f).getAxisAligned(); - - LLVector3 edgeA = targetRegionPos + childBBox.getMinLocal(); - LLVector3 edgeB = targetRegionPos + childBBox.getMaxLocal(); - - LLVector3d edgeAd, edgeBd; - edgeAd.setVec(edgeA); - edgeBd.setVec(edgeB); - - //Only add the box when either of the extents are in a neighboring region - if ( pTargetRegion->pointInRegionGlobal( edgeAd ) || pTargetRegion->pointInRegionGlobal( edgeBd ) ) - { - PotentialReturnableObject returnableObj; - returnableObj.box = childBBox; - returnableObj.pRegion = pTargetRegion; - returnables.push_back( returnableObj ); - } -} - -bool LLViewerObject::crossesParcelBounds() -{ - std::vector boxes; - boxes.push_back(LLBBox(getPositionRegion(), getRotationRegion(), getScale() * -0.5f, getScale() * 0.5f).getAxisAligned()); - for (child_list_t::iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - boxes.push_back(LLBBox(child->getPositionRegion(), child->getRotationRegion(), child->getScale() * -0.5f, child->getScale() * 0.5f).getAxisAligned()); - } - - return mRegionp && mRegionp->objectsCrossParcel(boxes); -} - -bool LLViewerObject::setParent(LLViewerObject* parent) -{ - if(mParent != parent) - { - LLViewerObject* old_parent = (LLViewerObject*)mParent ; - bool ret = LLPrimitive::setParent(parent); - if(ret && old_parent && parent) - { - old_parent->removeChild(this) ; - } - return ret ; - } - - return false ; -} - -void LLViewerObject::addChild(LLViewerObject *childp) -{ - for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i) - { - if (*i == childp) - { //already has child - return; - } - } - - if (!isAvatar()) - { - // propagate selection properties - childp->mbCanSelect = mbCanSelect; - } - - if(childp->setParent(this)) - { - mChildList.push_back(childp); - childp->afterReparent(); - - if (childp->isAvatar()) - { - mSeatCount++; - } - } -} - -void LLViewerObject::onReparent(LLViewerObject *old_parent, LLViewerObject *new_parent) -{ -} - -void LLViewerObject::afterReparent() -{ -} - -void LLViewerObject::removeChild(LLViewerObject *childp) -{ - for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i) - { - if (*i == childp) - { - if (!childp->isAvatar() && mDrawable.notNull() && mDrawable->isActive() && childp->mDrawable.notNull() && !isAvatar()) - { - gPipeline.markRebuild(childp->mDrawable, LLDrawable::REBUILD_VOLUME); - } - - mChildList.erase(i); - - if(childp->getParent() == this) - { - childp->setParent(NULL); - } - - if (childp->isAvatar()) - { - mSeatCount--; - } - break; - } - } - - if (childp->isSelected()) - { - LLSelectMgr::getInstance()->deselectObjectAndFamily(childp); - bool add_to_end = true; - LLSelectMgr::getInstance()->selectObjectAndFamily(childp, add_to_end); - } -} - -void LLViewerObject::addThisAndAllChildren(std::vector& objects) -{ - objects.push_back(this); - for (child_list_t::iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - if (!child->isAvatar()) - { - child->addThisAndAllChildren(objects); - } - } -} - -void LLViewerObject::addThisAndNonJointChildren(std::vector& objects) -{ - objects.push_back(this); - // don't add any attachments when temporarily selecting avatar - if (isAvatar()) - { - return; - } - for (child_list_t::iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - if ( (!child->isAvatar())) - { - child->addThisAndNonJointChildren(objects); - } - } -} - -bool LLViewerObject::isChild(LLViewerObject *childp) const -{ - for (child_list_t::const_iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* testchild = *iter; - if (testchild == childp) - return true; - } - return false; -} - -// returns true if at least one avatar is sitting on this object -bool LLViewerObject::isSeat() const -{ - return mSeatCount > 0; -} - -bool LLViewerObject::setDrawableParent(LLDrawable* parentp) -{ - if (mDrawable.isNull()) - { - return false; - } - - bool ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL); - if(!ret) - { - return false ; - } - LLDrawable* old_parent = mDrawable->mParent; - mDrawable->mParent = parentp; - - if (parentp && mDrawable->isActive()) - { - parentp->makeActive(); - parentp->setState(LLDrawable::ACTIVE_CHILD); - } - - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME); - if( (old_parent != parentp && old_parent) - || (parentp && parentp->isActive())) - { - // *TODO we should not be relying on setDrawable parent to call markMoved - gPipeline.markMoved(mDrawable, false); - } - else if (!mDrawable->isAvatar()) - { - mDrawable->updateXform(true); - /*if (!mDrawable->getSpatialGroup()) - { - mDrawable->movePartition(); - }*/ - } - - return ret; -} - -// Show or hide particles, icon and HUD -void LLViewerObject::hideExtraDisplayItems( bool hidden ) -{ - if( mPartSourcep.notNull() ) - { - LLViewerPartSourceScript *partSourceScript = mPartSourcep.get(); - partSourceScript->setSuspended( hidden ); - } - - if( mText.notNull() ) - { - LLHUDText *hudText = mText.get(); - hudText->setHidden( hidden ); - } - - if( mIcon.notNull() ) - { - LLHUDIcon *hudIcon = mIcon.get(); - hudIcon->setHidden( hidden ); - } -} - -U32 LLViewerObject::checkMediaURL(const std::string &media_url) -{ - U32 retval = (U32)0x0; - if (!mMedia && !media_url.empty()) - { - retval |= MEDIA_URL_ADDED; - mMedia = new LLViewerObjectMedia; - mMedia->mMediaURL = media_url; - mMedia->mMediaType = LLViewerObject::MEDIA_SET; - mMedia->mPassedWhitelist = false; - } - else if (mMedia) - { - if (media_url.empty()) - { - retval |= MEDIA_URL_REMOVED; - delete mMedia; - mMedia = NULL; - } - else if (mMedia->mMediaURL != media_url) // <-- This is an optimization. If they are equal don't bother with below's test. - { - /*if (! (LLTextureEntry::getAgentIDFromMediaVersionString(media_url) == gAgent.getID() && - LLTextureEntry::getVersionFromMediaVersionString(media_url) == - LLTextureEntry::getVersionFromMediaVersionString(mMedia->mMediaURL) + 1)) - */ - { - // If the media URL is different and WE were not the one who - // changed it, mark dirty. - retval |= MEDIA_URL_UPDATED; - } - mMedia->mMediaURL = media_url; - mMedia->mPassedWhitelist = false; - } - } - return retval; -} - -//extract spatial information from object update message -//return parent_id -//static -U32 LLViewerObject::extractSpatialExtents(LLDataPackerBinaryBuffer *dp, LLVector3& pos, LLVector3& scale, LLQuaternion& rot) -{ - U32 parent_id = 0; - LLViewerObject::unpackParentID(dp, parent_id); - - LLViewerObject::unpackVector3(dp, scale, "Scale"); - LLViewerObject::unpackVector3(dp, pos, "Pos"); - - LLVector3 vec; - LLViewerObject::unpackVector3(dp, vec, "Rot"); - rot.unpackFromVector3(vec); - - return parent_id; -} - -U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, - void **user_data, - U32 block_num, - const EObjectUpdateType update_type, - LLDataPacker *dp) -{ - LL_PROFILE_ZONE_SCOPED; - LL_DEBUGS_ONCE("SceneLoadTiming") << "Received viewer object data" << LL_ENDL; - - LL_DEBUGS("ObjectUpdate") << " mesgsys " << mesgsys << " dp " << dp << " id " << getID() << " update_type " << (S32) update_type << LL_ENDL; - dumpStack("ObjectUpdateStack"); - - // The new OBJECTDATA_FIELD_SIZE_124, OBJECTDATA_FIELD_SIZE_140, OBJECTDATA_FIELD_SIZE_80 - // and OBJECTDATA_FIELD_SIZE_64 lengths should be supported in the existing cases below. - // Each case should start at the beginning of the buffer and extract all known - // values, and ignore any unknown data at the end of the buffer. - // This allows new data in the future without breaking current viewers. - const S32 OBJECTDATA_FIELD_SIZE_140 = 140; // Full precision avatar update for future extended data - const S32 OBJECTDATA_FIELD_SIZE_124 = 124; // Full precision object update for future extended data - const S32 OBJECTDATA_FIELD_SIZE_76 = 76; // Full precision avatar update - const S32 OBJECTDATA_FIELD_SIZE_60 = 60; // Full precision object update - const S32 OBJECTDATA_FIELD_SIZE_80 = 80; // Terse avatar update, 16 bit precision for future extended data - const S32 OBJECTDATA_FIELD_SIZE_64 = 64; // Terse object update, 16 bit precision for future extended data - const S32 OBJECTDATA_FIELD_SIZE_48 = 48; // Terse avatar update, 16 bit precision - const S32 OBJECTDATA_FIELD_SIZE_32 = 32; // Terse object update, 16 bit precision - - U32 retval = 0x0; - - // If region is removed from the list it is also deleted. - if (!LLWorld::instance().isRegionListed(mRegionp)) - { - LL_WARNS() << "Updating object in an invalid region" << LL_ENDL; - return retval; - } - - // Coordinates of objects on simulators are region-local. - U64 region_handle = 0; - - if(mesgsys != NULL) - { - mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle); - LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromHandle(region_handle); - if(regionp != mRegionp && regionp && mRegionp)//region cross - { - //this is the redundant position and region update, but it is necessary in case the viewer misses the following - //position and region update messages from sim. - //this redundant update should not cause any problems. - LLVector3 delta_pos = mRegionp->getOriginAgent() - regionp->getOriginAgent(); - setPositionParent(getPosition() + delta_pos); //update to the new region position immediately. - setRegion(regionp) ; //change the region. - } - else - { - if(regionp != mRegionp) - { - if(mRegionp) - { - mRegionp->removeFromCreatedList(getLocalID()); - } - if(regionp) - { - regionp->addToCreatedList(getLocalID()); - } - } - mRegionp = regionp ; - } - } - - if (!mRegionp) - { - U32 x, y; - from_region_handle(region_handle, &x, &y); - - LL_WARNS("UpdateFail") << "Object has invalid region " << x << ":" << y << "!" << LL_ENDL; - return retval; - } - - F32 time_dilation = 1.f; - if(mesgsys != NULL) - { - U16 time_dilation16; - mesgsys->getU16Fast(_PREHASH_RegionData, _PREHASH_TimeDilation, time_dilation16); - time_dilation = ((F32) time_dilation16) / 65535.f; - mRegionp->setTimeDilation(time_dilation); - } - - // this will be used to determine if we've really changed position - // Use getPosition, not getPositionRegion, since this is what we're comparing directly against. - LLVector3 test_pos_parent = getPosition(); - - // This needs to match the largest size below. See switch(length) - U8 data[MAX_OBJECT_BINARY_DATA_SIZE]; - -#ifdef LL_BIG_ENDIAN - U16 valswizzle[4]; -#endif - U16 *val; - const F32 size = LLWorld::getInstance()->getRegionWidthInMeters(); - const F32 MAX_HEIGHT = LLWorld::getInstance()->getRegionMaxHeight(); - const F32 MIN_HEIGHT = LLWorld::getInstance()->getRegionMinHeight(); - S32 length = 0; - S32 count = 0; - S32 this_update_precision = 32; // in bits - - // Temporaries, because we need to compare w/ previous to set dirty flags... - LLVector3 new_pos_parent; - LLVector3 new_vel; - LLVector3 new_acc; - LLVector3 new_angv; - LLVector3 old_angv = getAngularVelocity(); - LLQuaternion new_rot; - LLVector3 new_scale = getScale(); - - U32 parent_id = 0; - U8 material = 0; - U8 click_action = 0; - U32 crc = 0; - - bool old_special_hover_cursor = specialHoverCursor(); - - LLViewerObject *cur_parentp = (LLViewerObject *)getParent(); - - if (cur_parentp) - { - parent_id = cur_parentp->mLocalID; - } - - if (!dp) - { - switch(update_type) - { - case OUT_FULL: - { -#ifdef DEBUG_UPDATE_TYPE - LL_INFOS() << "Full:" << getID() << LL_ENDL; -#endif - //clear cost and linkset cost - setObjectCostStale(); - if (isSelected()) - { - gFloaterTools->dirty(); - } - - LLUUID audio_uuid; - LLUUID owner_id; // only valid if audio_uuid or particle system is not null - F32 gain; - F32 cutoff; - U8 sound_flags; - - mesgsys->getU32Fast( _PREHASH_ObjectData, _PREHASH_CRC, crc, block_num); - mesgsys->getU32Fast( _PREHASH_ObjectData, _PREHASH_ParentID, parent_id, block_num); - mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_Sound, audio_uuid, block_num ); - // HACK: Owner id only valid if non-null sound id or particle system - mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id, block_num ); - mesgsys->getF32Fast( _PREHASH_ObjectData, _PREHASH_Gain, gain, block_num ); - mesgsys->getF32Fast( _PREHASH_ObjectData, _PREHASH_Radius, cutoff, block_num ); - mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_Flags, sound_flags, block_num ); - mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_Material, material, block_num ); - mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_ClickAction, click_action, block_num); - mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_Scale, new_scale, block_num ); - length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData); - mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num, MAX_OBJECT_BINARY_DATA_SIZE); - length = llmin(length, MAX_OBJECT_BINARY_DATA_SIZE); // getBinaryDataFast() safely fills the buffer to max_size - - mTotalCRC = crc; - // Might need to update mSourceMuted here to properly pick up new radius - mSoundCutOffRadius = cutoff; - - // Owner ID used for sound muting or particle system muting - setAttachedSound(audio_uuid, owner_id, gain, sound_flags); - - U8 old_material = getMaterial(); - if (old_material != material) - { - setMaterial(material); - if (mDrawable.notNull()) - { - gPipeline.markMoved(mDrawable, false); // undamped - } - } - setClickAction(click_action); - - count = 0; - LLVector4 collision_plane; - - switch(length) - { - case OBJECTDATA_FIELD_SIZE_140: - case OBJECTDATA_FIELD_SIZE_76: - // pull out collision normal for avatar - htolememcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); - ((LLVOAvatar*)this)->setFootPlane(collision_plane); - count += sizeof(LLVector4); - - case OBJECTDATA_FIELD_SIZE_124: - case OBJECTDATA_FIELD_SIZE_60: - this_update_precision = 32; - // this is a full precision update - // pos - htolememcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // vel - htolememcpy((void*)getVelocity().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // acc - htolememcpy((void*)getAcceleration().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - count += sizeof(LLVector3); - // theta - { - LLVector3 vec; - htolememcpy(vec.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - new_rot.unpackFromVector3(vec); - } - count += sizeof(LLVector3); - // omega - htolememcpy((void*)new_angv.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); - if (new_angv.isExactlyZero()) - { - // reset rotation time - resetRot(); - } - setAngularVelocity(new_angv); - count += sizeof(LLVector3); -#if LL_DARWIN - if (length == OBJECTDATA_FIELD_SIZE_76 || - length == OBJECTDATA_FIELD_SIZE_140) - { - setAngularVelocity(LLVector3::zero); - } -#endif - break; - - // length values 48, 32 and 16 were once in viewer code but - // are never sent by the SL simulator - default: - LL_WARNS("UpdateFail") << "Unexpected ObjectData buffer size " << length - << " for " << getID() << " with OUT_FULL message" << LL_ENDL; - } - - //////////////////////////////////////////////////// - // - // Here we handle data specific to the full message. - // - - U32 flags; - mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, block_num); - // clear all but local flags - mFlags &= FLAGS_LOCAL; - mFlags |= flags; - - U8 state; - mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); - mAttachmentState = state; - - // ...new objects that should come in selected need to be added to the selected list - mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0); - - // Set all name value pairs - S32 nv_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_NameValue); - if (nv_size > 0) - { - std::string name_value_list; - mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_NameValue, name_value_list, block_num); - setNameValueList(name_value_list); - } - - // Clear out any existing generic data - if (mData) - { - delete [] mData; - mData = NULL; - } - - // Dec 2023 new generic data: - // Trees work as before, this field contains genome data - // Not a tree: root objects send 1 byte with the number of - // total prims in the linkset - // If the generic data size is zero, then number of prims is 1 - // - // Viewers should not check for specific data sizes exactly, but if - // the field has data, process it from the start and ignore the remainder. - - // Check for appended generic data - const S32 GENERIC_DATA_BUFFER_SIZE = 16; - S32 data_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Data); - if (data_size > 0) - { // has generic data - if (getPCode() == LL_PCODE_LEGACY_TREE || getPCode() == LL_PCODE_TREE_NEW) - { - mData = new U8[data_size]; - mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, mData, data_size, block_num); - LL_DEBUGS("NewObjectData") << "Read " << data_size << " bytes tree genome data for " << getID() << ", pcode " - << getPCodeString() << ", value " << (S32) mData[0] << LL_ENDL; - } - else - { // Extract number of prims - U8 generic_data[GENERIC_DATA_BUFFER_SIZE]; - mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, - &generic_data[0], llmin(data_size, GENERIC_DATA_BUFFER_SIZE), block_num); - // This is sample code to extract the number of prims - // Future viewers should use it for their own purposes - if (!isAvatar()) - { - S32 num_prims = (S32) generic_data[0]; - LL_DEBUGS("NewObjectData") << "Root prim " << getID() << " has " - << num_prims << " prims in linkset" << LL_ENDL; - } - } - } - - S32 text_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Text); - if (text_size > 1) - { - // Setup object text - if (!mText) - { - initHudText(); - } - - std::string temp_string; - mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_Text, temp_string, block_num ); - - LLColor4U coloru; - mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextColor, coloru.mV, 4, block_num); - - // alpha was flipped so that it zero encoded better - coloru.mV[3] = 255 - coloru.mV[3]; - mText->setColor(LLColor4(coloru)); - mText->setString(temp_string); - - mHudText = temp_string; - mHudTextColor = LLColor4(coloru); - - setChanged(MOVED | SILHOUETTE); - } - else - { - if (mText.notNull()) - { - mText->markDead(); - mText = NULL; - } - mHudText.clear(); - } - - std::string media_url; - mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_MediaURL, media_url, block_num); - retval |= checkMediaURL(media_url); - - // - // Unpack particle system data - // - unpackParticleSource(block_num, owner_id); - - // Mark all extra parameters not used - std::unordered_map::iterator iter; - for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) - { - iter->second->in_use = false; - } - - // Unpack extra parameters - S32 size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ExtraParams); - if (size > 0) - { - U8 *buffer = new U8[size]; - mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ExtraParams, buffer, size, block_num); - LLDataPackerBinaryBuffer dp(buffer, size); - - U8 num_parameters; - dp.unpackU8(num_parameters, "num_params"); - U8 param_block[MAX_OBJECT_PARAMS_SIZE]; - for (U8 param=0; paramsecond->in_use) - { - // Send an update message in case it was formerly in use - parameterChanged(iter->first, iter->second->data, false, false); - } - } - - break; - } - - case OUT_TERSE_IMPROVED: - { -#ifdef DEBUG_UPDATE_TYPE - LL_INFOS() << "TI:" << getID() << LL_ENDL; -#endif - length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData); - mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num, MAX_OBJECT_BINARY_DATA_SIZE); - length = llmin(length, MAX_OBJECT_BINARY_DATA_SIZE); // getBinaryDataFast() safely fills the buffer to max_size - count = 0; - LLVector4 collision_plane; - - switch(length) - { - case OBJECTDATA_FIELD_SIZE_80: - case OBJECTDATA_FIELD_SIZE_48: - // pull out collision normal for avatar - htolememcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); - ((LLVOAvatar*)this)->setFootPlane(collision_plane); - count += sizeof(LLVector4); - - case OBJECTDATA_FIELD_SIZE_64: - case OBJECTDATA_FIELD_SIZE_32: - // this is a terse 16 bit quantized update - this_update_precision = 16; - test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); - -#ifdef LL_BIG_ENDIAN - htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - new_pos_parent.mV[VX] = U16_to_F32(val[VX], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VY] = U16_to_F32(val[VY], -0.5f*size, 1.5f*size); - new_pos_parent.mV[VZ] = U16_to_F32(val[VZ], MIN_HEIGHT, MAX_HEIGHT); - -#ifdef LL_BIG_ENDIAN - htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - setVelocity(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size)); - -#ifdef LL_BIG_ENDIAN - htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*3; - setAcceleration(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size)); - -#ifdef LL_BIG_ENDIAN - htolememcpy(valswizzle, &data[count], MVT_U16Quat, 8); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - count += sizeof(U16)*4; - new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f); - new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f); - new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f); - new_rot.mQ[VW] = U16_to_F32(val[VW], -1.f, 1.f); - -#ifdef LL_BIG_ENDIAN - htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); - val = valswizzle; -#else - val = (U16 *) &data[count]; -#endif - new_angv.set(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size)); - setAngularVelocity(new_angv); - break; - - // Previous viewers had code for length 76, 60 or 16 byte length - // with full precision or 8 bit quanitzation, but the - // SL servers will never send those data formats. If you ever see this - // warning in Second Life, please file a bug report - default: - LL_WARNS("UpdateFail") << "Unexpected ObjectData buffer size " << length << " for " << getID() - << " with OUT_FULL message" << LL_ENDL; - } - - U8 state; - mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); - mAttachmentState = state; - break; - } - - default: - LL_WARNS("UpdateFail") << "Unknown uncompressed update type " << update_type << " for " << getID() << LL_ENDL; - break; - } - } - else - { - // handle the compressed case - have dp datapacker - LLUUID sound_uuid; - LLUUID owner_id; - F32 gain = 0; - U8 sound_flags = 0; - F32 cutoff = 0; - - U16 val[4]; - - U8 state; - - dp->unpackU8(state, "State"); - mAttachmentState = state; - - switch(update_type) - { - case OUT_TERSE_IMPROVED: - { -#ifdef DEBUG_UPDATE_TYPE - LL_INFOS() << "CompTI:" << getID() << LL_ENDL; -#endif - U8 value; - dp->unpackU8(value, "agent"); - if (value) - { - LLVector4 collision_plane; - dp->unpackVector4(collision_plane, "Plane"); - ((LLVOAvatar*)this)->setFootPlane(collision_plane); - } - test_pos_parent = getPosition(); - dp->unpackVector3(new_pos_parent, "Pos"); - dp->unpackU16(val[VX], "VelX"); - dp->unpackU16(val[VY], "VelY"); - dp->unpackU16(val[VZ], "VelZ"); - setVelocity(U16_to_F32(val[VX], -128.f, 128.f), - U16_to_F32(val[VY], -128.f, 128.f), - U16_to_F32(val[VZ], -128.f, 128.f)); - dp->unpackU16(val[VX], "AccX"); - dp->unpackU16(val[VY], "AccY"); - dp->unpackU16(val[VZ], "AccZ"); - setAcceleration(U16_to_F32(val[VX], -64.f, 64.f), - U16_to_F32(val[VY], -64.f, 64.f), - U16_to_F32(val[VZ], -64.f, 64.f)); - - dp->unpackU16(val[VX], "ThetaX"); - dp->unpackU16(val[VY], "ThetaY"); - dp->unpackU16(val[VZ], "ThetaZ"); - dp->unpackU16(val[VS], "ThetaS"); - new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f); - new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f); - new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f); - new_rot.mQ[VS] = U16_to_F32(val[VS], -1.f, 1.f); - dp->unpackU16(val[VX], "AccX"); - dp->unpackU16(val[VY], "AccY"); - dp->unpackU16(val[VZ], "AccZ"); - new_angv.set(U16_to_F32(val[VX], -64.f, 64.f), - U16_to_F32(val[VY], -64.f, 64.f), - U16_to_F32(val[VZ], -64.f, 64.f)); - setAngularVelocity(new_angv); - } - break; - case OUT_FULL_COMPRESSED: - case OUT_FULL_CACHED: - { -#ifdef DEBUG_UPDATE_TYPE - LL_INFOS() << "CompFull:" << getID() << LL_ENDL; -#endif - setObjectCostStale(); - - if (isSelected()) - { - gFloaterTools->dirty(); - } - - dp->unpackU32(crc, "CRC"); - mTotalCRC = crc; - dp->unpackU8(material, "Material"); - U8 old_material = getMaterial(); - if (old_material != material) - { - setMaterial(material); - if (mDrawable.notNull()) - { - gPipeline.markMoved(mDrawable, false); // undamped - } - } - dp->unpackU8(click_action, "ClickAction"); - setClickAction(click_action); - dp->unpackVector3(new_scale, "Scale"); - dp->unpackVector3(new_pos_parent, "Pos"); - LLVector3 vec; - dp->unpackVector3(vec, "Rot"); - new_rot.unpackFromVector3(vec); - setAcceleration(LLVector3::zero); - - U32 value; - dp->unpackU32(value, "SpecialCode"); - dp->setPassFlags(value); - dp->unpackUUID(owner_id, "Owner"); - - mOwnerID = owner_id; - - if (value & 0x80) - { - dp->unpackVector3(new_angv, "Omega"); - setAngularVelocity(new_angv); - } - - if (value & 0x20) - { - dp->unpackU32(parent_id, "ParentID"); - } - else - { - parent_id = 0; - } - - S32 sp_size; - U32 size; - if (value & 0x2) - { - sp_size = 1; - delete [] mData; - mData = new U8[1]; - dp->unpackU8(((U8*)mData)[0], "TreeData"); - } - else if (value & 0x1) - { - dp->unpackU32(size, "ScratchPadSize"); - delete [] mData; - mData = new U8[size]; - dp->unpackBinaryData((U8 *)mData, sp_size, "PartData"); - } - else - { - mData = NULL; - } - - // Setup object text - if (!mText && (value & 0x4)) - { - initHudText(); - } - - if (value & 0x4) - { - std::string temp_string; - dp->unpackString(temp_string, "Text"); - LLColor4U coloru; - dp->unpackBinaryDataFixed(coloru.mV, 4, "Color"); - coloru.mV[3] = 255 - coloru.mV[3]; - mText->setColor(LLColor4(coloru)); - mText->setString(temp_string); - - mHudText = temp_string; - mHudTextColor = LLColor4(coloru); - - setChanged(TEXTURE); - } - else - { - if (mText.notNull()) - { - mText->markDead(); - mText = NULL; - } - mHudText.clear(); - } - - std::string media_url; - if (value & 0x200) - { - dp->unpackString(media_url, "MediaURL"); - } - retval |= checkMediaURL(media_url); - - // - // Unpack particle system data (legacy) - // - if (value & 0x8) - { - unpackParticleSource(*dp, owner_id, true); - } - else if (!(value & 0x400)) - { - deleteParticleSource(); - } - - // Mark all extra parameters not used - std::unordered_map::iterator iter; - for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) - { - iter->second->in_use = false; - } - - // Unpack extra params - U8 num_parameters; - dp->unpackU8(num_parameters, "num_params"); - U8 param_block[MAX_OBJECT_PARAMS_SIZE]; - for (U8 param=0; paramunpackU16(param_type, "param_type"); - dp->unpackBinaryData(param_block, param_size, "param_data"); - //LL_INFOS() << "Param type: " << param_type << ", Size: " << param_size << LL_ENDL; - LLDataPackerBinaryBuffer dp2(param_block, param_size); - unpackParameterEntry(param_type, &dp2); - } - - for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) - { - if (!iter->second->in_use) - { - // Send an update message in case it was formerly in use - parameterChanged(iter->first, iter->second->data, false, false); - } - } - - if (value & 0x10) - { - dp->unpackUUID(sound_uuid, "SoundUUID"); - dp->unpackF32(gain, "SoundGain"); - dp->unpackU8(sound_flags, "SoundFlags"); - dp->unpackF32(cutoff, "SoundRadius"); - } - - if (value & 0x100) - { - std::string name_value_list; - dp->unpackString(name_value_list, "NV"); - - setNameValueList(name_value_list); - } - - mTotalCRC = crc; - mSoundCutOffRadius = cutoff; - - setAttachedSound(sound_uuid, owner_id, gain, sound_flags); - - // only get these flags on updates from sim, not cached ones - // Preload these five flags for every object. - // Finer shades require the object to be selected, and the selection manager - // stores the extended permission info. - if(mesgsys != NULL) - { - U32 flags; - mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, block_num); - loadFlags(flags); - } - } - break; - - default: - LL_WARNS("UpdateFail") << "Unknown compressed update type " << update_type << " for " << getID() << LL_ENDL; - break; - } - } - - // - // Fix object parenting. - // - bool b_changed_status = false; - - if (OUT_TERSE_IMPROVED != update_type) - { - // We only need to update parenting on full updates, terse updates - // don't send parenting information. - if (!cur_parentp) - { - if (parent_id == 0) - { - // No parent now, no parent in message -> do nothing - } - else - { - // No parent now, new parent in message -> attach to that parent if possible - LLUUID parent_uuid; - - if(mesgsys != NULL) - { - LLViewerObjectList::getUUIDFromLocal(parent_uuid, - parent_id, - mesgsys->getSenderIP(), - mesgsys->getSenderPort()); - } - else - { - LLViewerObjectList::getUUIDFromLocal(parent_uuid, - parent_id, - mRegionp->getHost().getAddress(), - mRegionp->getHost().getPort()); - } - - LLViewerObject *sent_parentp = gObjectList.findObject(parent_uuid); - - // - // Check to see if we have the corresponding viewer object for the parent. - // - if (sent_parentp && sent_parentp->getParent() == this) - { - // Try to recover if we attempt to attach a parent to its child - LL_WARNS("UpdateFail") << "Attempt to attach a parent to it's child: " << this->getID() << " to " - << sent_parentp->getID() << LL_ENDL; - this->removeChild(sent_parentp); - sent_parentp->setDrawableParent(NULL); - } - - if (sent_parentp && (sent_parentp != this) && !sent_parentp->isDead()) - { - if (((LLViewerObject*)sent_parentp)->isAvatar()) - { - //LL_DEBUGS("Avatar") << "ATT got object update for attachment " << LL_ENDL; - } - - // - // We have a viewer object for the parent, and it's not dead. - // Do the actual reparenting here. - // - - // new parent is valid - b_changed_status = true; - // ...no current parent, so don't try to remove child - if (mDrawable.notNull()) - { - if (mDrawable->isDead() || !mDrawable->getVObj()) - { - LL_WARNS("UpdateFail") << "Drawable is dead or no VObj!" << LL_ENDL; - sent_parentp->addChild(this); - } - else - { - if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 1 - { - // Bad, we got a cycle somehow. - // Kill both the parent and the child, and - // set cache misses for both of them. - LL_WARNS("UpdateFail") << "Attempting to recover from parenting cycle! " - << "Killing " << sent_parentp->getID() << " and " << getID() - << ", Adding to cache miss list" << LL_ENDL; - setParent(NULL); - sent_parentp->setParent(NULL); - getRegion()->addCacheMissFull(getLocalID()); - getRegion()->addCacheMissFull(sent_parentp->getLocalID()); - gObjectList.killObject(sent_parentp); - gObjectList.killObject(this); - return retval; - } - sent_parentp->addChild(this); - // make sure this object gets a non-damped update - if (sent_parentp->mDrawable.notNull()) - { - gPipeline.markMoved(sent_parentp->mDrawable, false); // undamped - } - } - } - else - { - sent_parentp->addChild(this); - } - - // Show particles, icon and HUD - hideExtraDisplayItems( false ); - - setChanged(MOVED | SILHOUETTE); - } - else - { - // - // No corresponding viewer object for the parent, put the various - // pieces on the orphan list. - // - - //parent_id - U32 ip, port; - - if(mesgsys != NULL) - { - ip = mesgsys->getSenderIP(); - port = mesgsys->getSenderPort(); - } - else - { - ip = mRegionp->getHost().getAddress(); - port = mRegionp->getHost().getPort(); - } - gObjectList.orphanize(this, parent_id, ip, port); - - // Hide particles, icon and HUD - hideExtraDisplayItems( true ); - } - } - } - else - { - // BUG: this is a bad assumption once border crossing is alowed - if ( (parent_id == cur_parentp->mLocalID) - &&(update_type == OUT_TERSE_IMPROVED)) - { - // Parent now, same parent in message -> do nothing - - // Debugging for suspected problems with local ids. - //LLUUID parent_uuid; - //LLViewerObjectList::getUUIDFromLocal(parent_uuid, parent_id, mesgsys->getSenderIP(), mesgsys->getSenderPort() ); - //if (parent_uuid != cur_parentp->getID() ) - //{ - // LL_ERRS() << "Local ID match but UUID mismatch of viewer object" << LL_ENDL; - //} - } - else - { - // Parented now, different parent in message - LLViewerObject *sent_parentp; - if (parent_id == 0) - { - // - // This object is no longer parented, we sent in a zero parent ID. - // - sent_parentp = NULL; - } - else - { - LLUUID parent_uuid; - - if(mesgsys != NULL) - { - LLViewerObjectList::getUUIDFromLocal(parent_uuid, - parent_id, - gMessageSystem->getSenderIP(), - gMessageSystem->getSenderPort()); - } - else - { - LLViewerObjectList::getUUIDFromLocal(parent_uuid, - parent_id, - mRegionp->getHost().getAddress(), - mRegionp->getHost().getPort()); - } - sent_parentp = gObjectList.findObject(parent_uuid); - - if (isAvatar()) - { - // This logic is meant to handle the case where a sitting avatar has reached a new sim - // ahead of the object she was sitting on (which is common as objects are transfered through - // a slower route than agents)... - // In this case, the local id for the object will not be valid, since the viewer has not received - // a full update for the object from that sim yet, so we assume that the agent is still sitting - // where she was originally. --RN - if (!sent_parentp) - { - sent_parentp = cur_parentp; - } - } - else if (!sent_parentp) - { - // - // Switching parents, but we don't know the new parent. - // - U32 ip, port; - - if(mesgsys != NULL) - { - ip = mesgsys->getSenderIP(); - port = mesgsys->getSenderPort(); - } - else - { - ip = mRegionp->getHost().getAddress(); - port = mRegionp->getHost().getPort(); - } - - // We're an orphan, flag things appropriately. - gObjectList.orphanize(this, parent_id, ip, port); - } - } - - // Reattach if possible. - if (sent_parentp && sent_parentp != cur_parentp && sent_parentp != this) - { - // New parent is valid, detach and reattach - b_changed_status = true; - if (mDrawable.notNull()) - { - if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 2 - { - // Bad, we got a cycle somehow. - // Kill both the parent and the child, and - // set cache misses for both of them. - LL_WARNS() << "Attempting to recover from parenting cycle!" << LL_ENDL; - LL_WARNS() << "Killing " << sent_parentp->getID() << " and " << getID() << LL_ENDL; - LL_WARNS() << "Adding to cache miss list" << LL_ENDL; - setParent(NULL); - sent_parentp->setParent(NULL); - getRegion()->addCacheMissFull(getLocalID()); - getRegion()->addCacheMissFull(sent_parentp->getLocalID()); - gObjectList.killObject(sent_parentp); - gObjectList.killObject(this); - return retval; - } - // make sure this object gets a non-damped update - } - cur_parentp->removeChild(this); - sent_parentp->addChild(this); - setChanged(MOVED | SILHOUETTE); - sent_parentp->setChanged(MOVED | SILHOUETTE); - if (sent_parentp->mDrawable.notNull()) - { - gPipeline.markMoved(sent_parentp->mDrawable, false); // undamped - } - } - else if (!sent_parentp) - { - bool remove_parent = true; - // No new parent, or the parent that we sent doesn't exist on the viewer. - LLViewerObject *parentp = (LLViewerObject *)getParent(); - if (parentp) - { - if (parentp->getRegion() != getRegion()) - { - // This is probably an object flying across a region boundary, the - // object probably ISN'T being reparented, but just got an object - // update out of order (child update before parent). - //LL_INFOS() << "Don't reparent object handoffs!" << LL_ENDL; - remove_parent = false; - } - } - - if (remove_parent) - { - b_changed_status = true; - if (mDrawable.notNull()) - { - // clear parent to removeChild can put the drawable on the damped list - setDrawableParent(NULL); // LLViewerObject::processUpdateMessage 3 - } - - cur_parentp->removeChild(this); - - setChanged(MOVED | SILHOUETTE); - - if (mDrawable.notNull()) - { - // make sure this object gets a non-damped update - gPipeline.markMoved(mDrawable, false); // undamped - } - } - } - } - } - } - - new_rot.normQuat(); - - if (sPingInterpolate && mesgsys != NULL) - { - LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mesgsys->getSender()); - if (cdp) - { - // Note: delay is U32 and usually less then second, - // converting it into seconds with valueInUnits will result in 0 - F32 ping_delay = 0.5f * time_dilation * ( ((F32)cdp->getPingDelay().value()) * 0.001f + gFrameDTClamped); - LLVector3 diff = getVelocity() * ping_delay; - new_pos_parent += diff; - } - else - { - LL_WARNS() << "findCircuit() returned NULL; skipping interpolation" << LL_ENDL; - } - } - - ////////////////////////// - // - // Set the generic change flags... - // - // - - // If we're going to skip this message, why are we - // doing all the parenting, etc above? - if(mesgsys != NULL) - { - U32 packet_id = mesgsys->getCurrentRecvPacketID(); - if (packet_id < mLatestRecvPacketID && - mLatestRecvPacketID - packet_id < 65536) - { - //skip application of this message, it's old - return retval; - } - mLatestRecvPacketID = packet_id; - } - - // Set the change flags for scale - if (new_scale != getScale()) - { - setChanged(SCALED | SILHOUETTE); - setScale(new_scale); // Must follow setting permYouOwner() - } - - // first, let's see if the new position is actually a change - - //static S32 counter = 0; - - F32 vel_mag_sq = getVelocity().magVecSquared(); - F32 accel_mag_sq = getAcceleration().magVecSquared(); - - if ( ((b_changed_status)||(test_pos_parent != new_pos_parent)) - ||( (!isSelected()) - &&( (vel_mag_sq != 0.f) - ||(accel_mag_sq != 0.f) - ||(this_update_precision > mBestUpdatePrecision)))) - { - mBestUpdatePrecision = this_update_precision; - - LLVector3 diff = new_pos_parent - test_pos_parent ; - F32 mag_sqr = diff.magVecSquared() ; - if(llfinite(mag_sqr)) - { - setPositionParent(new_pos_parent); - } - else - { - LL_WARNS() << "Can not move the object/avatar to an infinite location!" << LL_ENDL ; - - retval |= INVALID_UPDATE ; - } - - if (mParent && ((LLViewerObject*)mParent)->isAvatar()) - { - // we have changed the position of an attachment, so we need to clamp it - LLVOAvatar *avatar = (LLVOAvatar*)mParent; - - avatar->clampAttachmentPositions(); - } - - // If we're snapping the position by more than 0.5m, update LLViewerStats::mAgentPositionSnaps - if ( asAvatar() && asAvatar()->isSelf() && (mag_sqr > 0.25f) ) - { - record(LLStatViewer::AGENT_POSITION_SNAP, LLUnit(diff.length())); - } - } - - if ((new_rot.isNotEqualEps(getRotation(), F_ALMOST_ZERO)) - || (new_angv != old_angv)) - { - if (new_rot != mPreviousRotation) - { - resetRot(); - } - else if (new_angv != old_angv) - { - if (flagUsePhysics()) - { - resetRot(); - } - else - { - resetRotTime(); - } - } - - // Remember the last rotation value - mPreviousRotation = new_rot; - - // Set the rotation of the object followed by adjusting for the accumulated angular velocity (llSetTargetOmega) - setRotation(new_rot * mAngularVelocityRot); - setChanged(ROTATED | SILHOUETTE); - } - - if ( gShowObjectUpdates ) - { - LLColor4 color; - if (update_type == OUT_TERSE_IMPROVED) - { - color.setVec(0.f, 0.f, 1.f, 1.f); - } - else - { - color.setVec(1.f, 0.f, 0.f, 1.f); - } - gPipeline.addDebugBlip(getPositionAgent(), color); - LL_DEBUGS("MessageBlip") << "Update type " << (S32)update_type << " blip for local " << mLocalID << " at " << getPositionAgent() << LL_ENDL; - } - - const F32 MAG_CUTOFF = F_APPROXIMATELY_ZERO; - - llassert(vel_mag_sq >= 0.f); - llassert(accel_mag_sq >= 0.f); - llassert(getAngularVelocity().magVecSquared() >= 0.f); - - if ((MAG_CUTOFF >= vel_mag_sq) && - (MAG_CUTOFF >= accel_mag_sq) && - (MAG_CUTOFF >= getAngularVelocity().magVecSquared())) - { - mStatic = true; // This object doesn't move! - } - else - { - mStatic = false; - } - -// BUG: This code leads to problems during group rotate and any scale operation. -// Small discepencies between the simulator and viewer representations cause the -// selection center to creep, leading to objects moving around the wrong center. -// -// Removing this, however, means that if someone else drags an object you have -// selected, your selection center and dialog boxes will be wrong. It also means -// that higher precision information on selected objects will be ignored. -// -// I believe the group rotation problem is fixed. JNC 1.21.2002 -// - // Additionally, if any child is selected, need to update the dialogs and selection - // center. - bool needs_refresh = mUserSelected; - for (child_list_t::iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - needs_refresh = needs_refresh || child->mUserSelected; - } - - static LLCachedControl allow_select_avatar(gSavedSettings, "AllowSelectAvatar", false); - if (needs_refresh) - { - LLSelectMgr::getInstance()->updateSelectionCenter(); - dialog_refresh_all(); - } - else if (allow_select_avatar && asAvatar()) - { - // Override any avatar position updates received - // Works only if avatar was repositioned using build - // tools and build floater is visible - LLSelectMgr::getInstance()->overrideAvatarUpdates(); - } - - - // Mark update time as approx. now, with the ping delay. - // Ping delay is off because it's not set for velocity interpolation, causing - // much jumping and hopping around... - -// U32 ping_delay = mesgsys->mCircuitInfo.getPingDelay(); - mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds(); - mLastMessageUpdateSecs = mLastInterpUpdateSecs; - if (mDrawable.notNull()) - { - // Don't clear invisibility flag on update if still orphaned! - if (mDrawable->isState(LLDrawable::FORCE_INVISIBLE) && !mOrphaned) - { -// LL_DEBUGS() << "Clearing force invisible: " << mID << ":" << getPCodeString() << ":" << getPositionAgent() << LL_ENDL; - mDrawable->clearState(LLDrawable::FORCE_INVISIBLE); - gPipeline.markRebuild( mDrawable, LLDrawable::REBUILD_ALL); - } - } - - // Update special hover cursor status - bool special_hover_cursor = specialHoverCursor(); - if (old_special_hover_cursor != special_hover_cursor - && mDrawable.notNull()) - { - mDrawable->updateSpecialHoverCursor(special_hover_cursor); - } - - return retval; -} - -bool LLViewerObject::isActive() const -{ - return true; -} - -//load flags from cache or from message -void LLViewerObject::loadFlags(U32 flags) -{ - if(flags == (U32)(-1)) - { - return; //invalid - } - - // keep local flags and overwrite remote-controlled flags - mFlags = (mFlags & FLAGS_LOCAL) | flags; - - // ...new objects that should come in selected need to be added to the selected list - mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0); - return; -} - -void LLViewerObject::idleUpdate(LLAgent &agent, const F64 &frame_time) -{ - if (!mDead) - { - if (!mStatic && sVelocityInterpolate && !isSelected()) - { - // calculate dt from last update - F32 time_dilation = mRegionp ? mRegionp->getTimeDilation() : 1.0f; - F32 dt_raw = ((F64Seconds)frame_time - mLastInterpUpdateSecs).value(); - F32 dt = time_dilation * dt_raw; - - applyAngularVelocity(dt); - - if (isAttachment()) - { - mLastInterpUpdateSecs = (F64Seconds)frame_time; - return; - } - else - { // Move object based on it's velocity and rotation - interpolateLinearMotion(frame_time, dt); - } - } - - updateDrawable(false); - } -} - - -// Move an object due to idle-time viewer side updates by interpolating motion -void LLViewerObject::interpolateLinearMotion(const F64SecondsImplicit& frame_time, const F32SecondsImplicit& dt_seconds) -{ - // linear motion - // PHYSICS_TIMESTEP is used below to correct for the fact that the velocity in object - // updates represents the average velocity of the last timestep, rather than the final velocity. - // the time dilation above should guarantee that dt is never less than PHYSICS_TIMESTEP, theoretically - // - // *TODO: should also wrap linear accel/velocity in check - // to see if object is selected, instead of explicitly - // zeroing it out - - F32 dt = dt_seconds; - F64Seconds time_since_last_update = frame_time - mLastMessageUpdateSecs; - if (time_since_last_update <= (F64Seconds)0.0 || dt <= 0.f) - { - return; - } - - LLVector3 accel = getAcceleration(); - LLVector3 vel = getVelocity(); - - if (sMaxUpdateInterpolationTime <= (F64Seconds)0.0) - { // Old code path ... unbounded, simple interpolation - if (!(accel.isExactlyZero() && vel.isExactlyZero())) - { - LLVector3 pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt; - - // region local - setPositionRegion(pos + getPositionRegion()); - setVelocity(vel + accel*dt); - - // for objects that are spinning but not translating, make sure to flag them as having moved - setChanged(MOVED | SILHOUETTE); - } - } - else if (!accel.isExactlyZero() || !vel.isExactlyZero()) // object is moving - { // Object is moving, and hasn't been too long since we got an update from the server - - // Calculate predicted position and velocity - LLVector3 new_pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt; - LLVector3 new_v = accel * dt; - - if (time_since_last_update > sPhaseOutUpdateInterpolationTime && - sPhaseOutUpdateInterpolationTime > (F64Seconds)0.0) - { // Haven't seen a viewer update in a while, check to see if the circuit is still active - if (mRegionp) - { // The simulator will NOT send updates if the object continues normally on the path - // predicted by the velocity and the acceleration (often gravity) sent to the viewer - // So check to see if the circuit is blocked, which means the sim is likely in a long lag - LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit( mRegionp->getHost() ); - if (cdp) - { - // Find out how many seconds since last packet arrived on the circuit - F64Seconds time_since_last_packet = LLMessageSystem::getMessageTimeSeconds() - cdp->getLastPacketInTime(); - - if (!cdp->isAlive() || // Circuit is dead or blocked - cdp->isBlocked() || // or doesn't seem to be getting any packets - (time_since_last_packet > sPhaseOutUpdateInterpolationTime)) - { - // Start to reduce motion interpolation since we haven't seen a server update in a while - F64Seconds time_since_last_interpolation = frame_time - mLastInterpUpdateSecs; - F64 phase_out = 1.0; - if (time_since_last_update > sMaxUpdateInterpolationTime) - { // Past the time limit, so stop the object - phase_out = 0.0; - //LL_INFOS() << "Motion phase out to zero" << LL_ENDL; - - // Kill angular motion as well. Note - not adding this due to paranoia - // about stopping rotation for llTargetOmega objects and not having it restart - // setAngularVelocity(LLVector3::zero); - } - else if (mLastInterpUpdateSecs - mLastMessageUpdateSecs > sPhaseOutUpdateInterpolationTime) - { // Last update was already phased out a bit - phase_out = (sMaxUpdateInterpolationTime - time_since_last_update) / - (sMaxUpdateInterpolationTime - time_since_last_interpolation); - //LL_INFOS() << "Continuing motion phase out of " << (F32) phase_out << LL_ENDL; - } - else - { // Phase out from full value - phase_out = (sMaxUpdateInterpolationTime - time_since_last_update) / - (sMaxUpdateInterpolationTime - sPhaseOutUpdateInterpolationTime); - //LL_INFOS() << "Starting motion phase out of " << (F32) phase_out << LL_ENDL; - } - phase_out = llclamp(phase_out, 0.0, 1.0); - - new_pos = new_pos * ((F32) phase_out); - new_v = new_v * ((F32) phase_out); - } - } - } - } - - new_pos = new_pos + getPositionRegion(); - new_v = new_v + vel; - - - // Clamp interpolated position to minimum underground and maximum region height - LLVector3d new_pos_global = mRegionp->getPosGlobalFromRegion(new_pos); - F32 min_height; - if (isAvatar()) - { // Make a better guess about AVs not going underground - min_height = LLWorld::getInstance()->resolveLandHeightGlobal(new_pos_global); - min_height += (0.5f * getScale().mV[VZ]); - } - else - { // This will put the object underground, but we can't tell if it will stop - // at ground level or not - min_height = LLWorld::getInstance()->getMinAllowedZ(this, new_pos_global); - // Cap maximum height - new_pos.mV[VZ] = llmin(LLWorld::getInstance()->getRegionMaxHeight(), new_pos.mV[VZ]); - } - - new_pos.mV[VZ] = llmax(min_height, new_pos.mV[VZ]); - - // Check to see if it's going off the region - LLVector3 temp(new_pos.mV[VX], new_pos.mV[VY], 0.f); - if (temp.clamp(0.f, mRegionp->getWidth())) - { // Going off this region, so see if we might end up on another region - LLVector3d old_pos_global = mRegionp->getPosGlobalFromRegion(getPositionRegion()); - new_pos_global = mRegionp->getPosGlobalFromRegion(new_pos); // Re-fetch in case it got clipped above - - // Clip the positions to known regions - LLVector3d clip_pos_global = LLWorld::getInstance()->clipToVisibleRegions(old_pos_global, new_pos_global); - if (clip_pos_global != new_pos_global) - { - // Was clipped, so this means we hit a edge where there is no region to enter - LLVector3 clip_pos = mRegionp->getPosRegionFromGlobal(clip_pos_global); - LL_DEBUGS("Interpolate") << "Hit empty region edge, clipped predicted position to " - << clip_pos - << " from " << new_pos << LL_ENDL; - new_pos = clip_pos; - - // Stop motion and get server update for bouncing on the edge - new_v.clear(); - setAcceleration(LLVector3::zero); - } - else - { - // Check for how long we are crossing. - // Note: theoretically we can find time from velocity, acceleration and - // distance from border to new position, but it is not going to work - // if 'phase_out' activates - if (mRegionCrossExpire == 0) - { - // Workaround: we can't accurately figure out time when we cross border - // so just write down time 'after the fact', it is far from optimal in - // case of lags, but for lags sMaxUpdateInterpolationTime will kick in first - LL_DEBUGS("Interpolate") << "Predicted region crossing, new position " << new_pos << LL_ENDL; - mRegionCrossExpire = frame_time + sMaxRegionCrossingInterpolationTime; - } - else if (frame_time > mRegionCrossExpire) - { - // Predicting crossing over 1s, stop motion - // Stop motion - LL_DEBUGS("Interpolate") << "Predicting region crossing for too long, stopping at " << new_pos << LL_ENDL; - new_v.clear(); - setAcceleration(LLVector3::zero); - mRegionCrossExpire = 0; - } - } - } - else - { - mRegionCrossExpire = 0; - } - - // Set new position and velocity - setPositionRegion(new_pos); - setVelocity(new_v); - - // for objects that are spinning but not translating, make sure to flag them as having moved - setChanged(MOVED | SILHOUETTE); - } - - // Update the last time we did anything - mLastInterpUpdateSecs = frame_time; -} - - - -// delete an item in the inventory, but don't tell the server. This is -// used internally by remove, update, and savescript. -// This will only delete the first item with an item_id in the list -void LLViewerObject::deleteInventoryItem(const LLUUID& item_id) -{ - if(mInventory) - { - LLInventoryObject::object_list_t::iterator it = mInventory->begin(); - LLInventoryObject::object_list_t::iterator end = mInventory->end(); - for( ; it != end; ++it ) - { - if((*it)->getUUID() == item_id) - { - // This is safe only because we return immediatly. - mInventory->erase(it); // will deref and delete it - return; - } - } - doInventoryCallback(); - } -} - -void LLViewerObject::doUpdateInventory( - LLPointer& item, - U8 key, - bool is_new) -{ - LLViewerInventoryItem* old_item = NULL; - if(TASK_INVENTORY_ITEM_KEY == key) - { - old_item = (LLViewerInventoryItem*)getInventoryObject(item->getUUID()); - } - else if(TASK_INVENTORY_ASSET_KEY == key) - { - old_item = getInventoryItemByAsset(item->getAssetUUID()); - } - LLUUID item_id; - LLUUID new_owner; - LLUUID new_group; - bool group_owned = false; - if(old_item) - { - item_id = old_item->getUUID(); - new_owner = old_item->getPermissions().getOwner(); - new_group = old_item->getPermissions().getGroup(); - group_owned = old_item->getPermissions().isGroupOwned(); - old_item = NULL; - } - else - { - item_id = item->getUUID(); - } - if(!is_new && mInventory) - { - // Attempt to update the local inventory. If we can get the - // object perm, we have perfect visibility, so we want the - // serial number to match. Otherwise, take our best guess and - // make sure that the serial number does not match. - deleteInventoryItem(item_id); - LLPermissions perm(item->getPermissions()); - LLPermissions* obj_perm = LLSelectMgr::getInstance()->findObjectPermissions(this); - bool is_atomic = (S32)LLAssetType::AT_OBJECT != item->getType(); - if(obj_perm) - { - perm.setOwnerAndGroup(LLUUID::null, obj_perm->getOwner(), obj_perm->getGroup(), is_atomic); - } - else - { - if(group_owned) - { - perm.setOwnerAndGroup(LLUUID::null, new_owner, new_group, is_atomic); - } - else if(!new_owner.isNull()) - { - // The object used to be in inventory, so we can - // assume the owner and group will match what they are - // there. - perm.setOwnerAndGroup(LLUUID::null, new_owner, new_group, is_atomic); - } - // *FIX: can make an even better guess by using the mPermGroup flags - else if(permYouOwner()) - { - // best guess. - perm.setOwnerAndGroup(LLUUID::null, gAgent.getID(), item->getPermissions().getGroup(), is_atomic); - --mExpectedInventorySerialNum; - } - else - { - // dummy it up. - perm.setOwnerAndGroup(LLUUID::null, LLUUID::null, LLUUID::null, is_atomic); - --mExpectedInventorySerialNum; - } - } - LLViewerInventoryItem* oldItem = item; - LLViewerInventoryItem* new_item = new LLViewerInventoryItem(oldItem); - new_item->setPermissions(perm); - mInventory->push_front(new_item); - doInventoryCallback(); - ++mExpectedInventorySerialNum; - } - else if (is_new) - { - ++mExpectedInventorySerialNum; - } -} - -// save a script, which involves removing the old one, and rezzing -// in the new one. This method should be called with the asset id -// of the new and old script AFTER the bytecode has been saved. -void LLViewerObject::saveScript( - const LLViewerInventoryItem* item, - bool active, - bool is_new) -{ - /* - * XXXPAM Investigate not making this copy. Seems unecessary, but I'm unsure about the - * interaction with doUpdateInventory() called below. - */ - LL_DEBUGS() << "LLViewerObject::saveScript() " << item->getUUID() << " " << item->getAssetUUID() << LL_ENDL; - - LLPointer task_item = - new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(), - item->getAssetUUID(), item->getType(), - item->getInventoryType(), - item->getName(), item->getDescription(), - item->getSaleInfo(), item->getFlags(), - item->getCreationDate()); - task_item->setTransactionID(item->getTransactionID()); - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RezScript); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); - msg->nextBlockFast(_PREHASH_UpdateBlock); - msg->addU32Fast(_PREHASH_ObjectLocalID, (mLocalID)); - U8 enabled = active; - msg->addBOOLFast(_PREHASH_Enabled, enabled); - msg->nextBlockFast(_PREHASH_InventoryBlock); - task_item->packMessage(msg); - msg->sendReliable(mRegionp->getHost()); - - // do the internal logic - doUpdateInventory(task_item, TASK_INVENTORY_ITEM_KEY, is_new); -} - -void LLViewerObject::moveInventory(const LLUUID& folder_id, - const LLUUID& item_id) -{ - LL_DEBUGS() << "LLViewerObject::moveInventory " << item_id << LL_ENDL; - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_MoveTaskInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_FolderID, folder_id); - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addU32Fast(_PREHASH_LocalID, mLocalID); - msg->addUUIDFast(_PREHASH_ItemID, item_id); - msg->sendReliable(mRegionp->getHost()); - - LLInventoryObject* inv_obj = getInventoryObject(item_id); - if(inv_obj) - { - LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_obj; - if(!item->getPermissions().allowCopyBy(gAgent.getID())) - { - deleteInventoryItem(item_id); - ++mExpectedInventorySerialNum; - } - } -} - -void LLViewerObject::dirtyInventory() -{ - // If there aren't any LLVOInventoryListeners, we won't be - // able to update our mInventory when it comes back from the - // simulator, so we should not clear the inventory either. - if(mInventory && !mInventoryCallbacks.empty()) - { - mInventory->clear(); // will deref and delete entries - delete mInventory; - mInventory = NULL; - } - mInventoryDirty = true; -} - -void LLViewerObject::registerInventoryListener(LLVOInventoryListener* listener, void* user_data) -{ - LLInventoryCallbackInfo* info = new LLInventoryCallbackInfo; - info->mListener = listener; - info->mInventoryData = user_data; - mInventoryCallbacks.push_front(info); -} - -void LLViewerObject::removeInventoryListener(LLVOInventoryListener* listener) -{ - if (listener == NULL) - return; - for (callback_list_t::iterator iter = mInventoryCallbacks.begin(); - iter != mInventoryCallbacks.end(); ) - { - callback_list_t::iterator curiter = iter++; - LLInventoryCallbackInfo* info = *curiter; - if (info->mListener == listener) - { - delete info; - mInventoryCallbacks.erase(curiter); - break; - } - } -} - -bool LLViewerObject::isInventoryPending() -{ - return mInvRequestState != INVENTORY_REQUEST_STOPPED; -} - -void LLViewerObject::clearInventoryListeners() -{ - for_each(mInventoryCallbacks.begin(), mInventoryCallbacks.end(), DeletePointer()); - mInventoryCallbacks.clear(); -} - -bool LLViewerObject::hasInventoryListeners() -{ - return !mInventoryCallbacks.empty(); -} - -void LLViewerObject::requestInventory() -{ - if(mInventoryDirty && mInventory && !mInventoryCallbacks.empty()) - { - mInventory->clear(); // will deref and delete entries - delete mInventory; - mInventory = NULL; - } - - if(mInventory) - { - // inventory is either up to date or doesn't has a listener - // if it is dirty, leave it this way in case we gain a listener - doInventoryCallback(); - } - else - { - // since we are going to request it now - mInventoryDirty = false; - - // Note: throws away duplicate requests - fetchInventoryFromServer(); - } -} - -void LLViewerObject::fetchInventoryFromServer() -{ - if (!isInventoryPending()) - { - delete mInventory; - mInventory = NULL; - - // Results in processTaskInv - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RequestTaskInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addU32Fast(_PREHASH_LocalID, mLocalID); - msg->sendReliable(mRegionp->getHost()); - - // This will get reset by doInventoryCallback or processTaskInv - mInvRequestState = INVENTORY_REQUEST_PENDING; - } -} - -void LLViewerObject::fetchInventoryDelayed(const F64 &time_seconds) -{ - // unless already waiting, drop previous request and shedule an update - if (mInvRequestState != INVENTORY_REQUEST_WAIT) - { - if (mInvRequestXFerId != 0) - { - // abort download. - gXferManager->abortRequestById(mInvRequestXFerId, -1); - mInvRequestXFerId = 0; - } - mInvRequestState = INVENTORY_REQUEST_WAIT; // affects isInventoryPending() - LLCoros::instance().launch("LLViewerObject::fetchInventoryDelayedCoro()", - boost::bind(&LLViewerObject::fetchInventoryDelayedCoro, mID, time_seconds)); - } -} - -//static -void LLViewerObject::fetchInventoryDelayedCoro(const LLUUID task_inv, const F64 time_seconds) -{ - llcoro::suspendUntilTimeout(time_seconds); - LLViewerObject *obj = gObjectList.findObject(task_inv); - if (obj) - { - // Might be good idea to prolong delay here in case expected serial changed. - // As it is, it will get a response with obsolete serial and will delay again. - - // drop waiting state to unlock isInventoryPending() - obj->mInvRequestState = INVENTORY_REQUEST_STOPPED; - obj->fetchInventoryFromServer(); - } -} - -LLControlAvatar *LLViewerObject::getControlAvatar() -{ - return getRootEdit()->mControlAvatar.get(); -} - -LLControlAvatar *LLViewerObject::getControlAvatar() const -{ - return getRootEdit()->mControlAvatar.get(); -} - -// Manage the control avatar state of a given object. -// Any object can be flagged as animated, but for performance reasons -// we don't want to incur the overhead of managing a control avatar -// unless this would have some user-visible consequence. That is, -// there should be at least one rigged mesh in the linkset. Operations -// that change the state of a linkset, such as linking or unlinking -// prims, can also mean that a control avatar needs to be added or -// removed. At the end, if there is a control avatar, we make sure -// that its animation state is current. -void LLViewerObject::updateControlAvatar() -{ - LLViewerObject *root = getRootEdit(); - bool is_animated_object = root->isAnimatedObject(); - bool has_control_avatar = getControlAvatar(); - if (!is_animated_object && !has_control_avatar) - { - return; - } - - // caller isn't supposed to operate on a dead object, - // avatar was already cleaned up - llassert(!isDead()); - - bool should_have_control_avatar = false; - if (is_animated_object) - { - bool any_rigged_mesh = root->isRiggedMesh(); - LLViewerObject::const_child_list_t& child_list = root->getChildren(); - for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); ++iter) - { - const LLViewerObject* child = *iter; - any_rigged_mesh = any_rigged_mesh || child->isRiggedMesh(); - } - should_have_control_avatar = is_animated_object && any_rigged_mesh; - } - - if (should_have_control_avatar && !has_control_avatar) - { - std::string vobj_name = llformat("Vol%p", root); - LL_DEBUGS("AnimatedObjects") << vobj_name << " calling linkControlAvatar()" << LL_ENDL; - root->linkControlAvatar(); - } - if (!should_have_control_avatar && has_control_avatar) - { - std::string vobj_name = llformat("Vol%p", root); - LL_DEBUGS("AnimatedObjects") << vobj_name << " calling unlinkControlAvatar()" << LL_ENDL; - root->unlinkControlAvatar(); - } - if (getControlAvatar()) - { - getControlAvatar()->updateAnimations(); - if (isSelected()) - { - LLSelectMgr::getInstance()->pauseAssociatedAvatars(); - } - } -} - -void LLViewerObject::linkControlAvatar() -{ - if (!getControlAvatar() && isRootEdit()) - { - LLVOVolume *volp = dynamic_cast(this); - if (!volp) - { - LL_WARNS() << "called with null or non-volume object" << LL_ENDL; - return; - } - mControlAvatar = LLControlAvatar::createControlAvatar(volp); - LL_DEBUGS("AnimatedObjects") << volp->getID() - << " created control av for " - << (S32) (1+volp->numChildren()) << " prims" << LL_ENDL; - } - LLControlAvatar *cav = getControlAvatar(); - if (cav) - { - cav->updateAttachmentOverrides(); - if (!cav->mPlaying) - { - cav->mPlaying = true; - //if (!cav->mRootVolp->isAnySelected()) - { - cav->updateVolumeGeom(); - cav->mRootVolp->recursiveMarkForUpdate(); - } - } - } - else - { - LL_WARNS() << "no control avatar found!" << LL_ENDL; - } -} - -void LLViewerObject::unlinkControlAvatar() -{ - if (getControlAvatar()) - { - getControlAvatar()->updateAttachmentOverrides(); - } - if (isRootEdit()) - { - // This will remove the entire linkset from the control avatar - if (mControlAvatar) - { - mControlAvatar->markForDeath(); - mControlAvatar = NULL; - } - } - // For non-root prims, removing from the linkset will - // automatically remove the control avatar connection. -} - -// virtual -bool LLViewerObject::isAnimatedObject() const -{ - return false; -} - -struct LLFilenameAndTask -{ - LLUUID mTaskID; - std::string mFilename; - - // for sequencing in case of multiple updates - S16 mSerial; -#ifdef _DEBUG - static S32 sCount; - LLFilenameAndTask() - { - ++sCount; - LL_DEBUGS() << "Constructing LLFilenameAndTask: " << sCount << LL_ENDL; - } - ~LLFilenameAndTask() - { - --sCount; - LL_DEBUGS() << "Destroying LLFilenameAndTask: " << sCount << LL_ENDL; - } -private: - LLFilenameAndTask(const LLFilenameAndTask& rhs); - const LLFilenameAndTask& operator=(const LLFilenameAndTask& rhs) const; -#endif -}; - -#ifdef _DEBUG -S32 LLFilenameAndTask::sCount = 0; -#endif - -// static -void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data) -{ - LLUUID task_id; - msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_TaskID, task_id); - LLViewerObject* object = gObjectList.findObject(task_id); - if (!object) - { - LL_WARNS() << "LLViewerObject::processTaskInv object " - << task_id << " does not exist." << LL_ENDL; - return; - } - - // we can receive multiple task updates simultaneously, make sure we will not rewrite newer with older update - S16 serial = 0; - msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, serial); - - if (serial == object->mInventorySerialNum - && serial < object->mExpectedInventorySerialNum) - { - // Loop Protection. - // We received same serial twice. - // Viewer did some changes to inventory that couldn't be saved server side - // or something went wrong to cause serial to be out of sync. - // Drop xfer and restart after some time, assign server's value as expected - LL_WARNS() << "Task inventory serial might be out of sync, server serial: " << serial << " client expected serial: " << object->mExpectedInventorySerialNum << LL_ENDL; - object->mExpectedInventorySerialNum = serial; - object->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_DESYNC); - } - else if (serial < object->mExpectedInventorySerialNum) - { - // Out of date message, record to current serial for loop protection, but do not load it - // Drop xfer and restart after some time - if (serial < object->mInventorySerialNum) - { - LL_WARNS() << "Task serial decreased. Potentially out of order packet or desync." << LL_ENDL; - } - object->mInventorySerialNum = serial; - object->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_OUTDATED); - } - else if (serial >= object->mExpectedInventorySerialNum) - { - LLFilenameAndTask* ft = new LLFilenameAndTask; - ft->mTaskID = task_id; - ft->mSerial = serial; - - // We received version we expected or newer. Load it. - object->mInventorySerialNum = ft->mSerial; - object->mExpectedInventorySerialNum = ft->mSerial; - - std::string unclean_filename; - msg->getStringFast(_PREHASH_InventoryData, _PREHASH_Filename, unclean_filename); - ft->mFilename = LLDir::getScrubbedFileName(unclean_filename); - - if (ft->mFilename.empty()) - { - LL_DEBUGS() << "Task has no inventory" << LL_ENDL; - // mock up some inventory to make a drop target. - if (object->mInventory) - { - object->mInventory->clear(); // will deref and delete it - } - else - { - object->mInventory = new LLInventoryObject::object_list_t(); - } - LLPointer obj; - obj = new LLInventoryObject(object->mID, LLUUID::null, - LLAssetType::AT_CATEGORY, - "Contents"); - object->mInventory->push_front(obj); - object->doInventoryCallback(); - delete ft; - return; - } - U64 new_id = gXferManager->requestFile(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ft->mFilename), - ft->mFilename, LL_PATH_CACHE, - object->mRegionp->getHost(), - true, - &LLViewerObject::processTaskInvFile, - (void**)ft, // This takes ownership of ft - LLXferManager::HIGH_PRIORITY); - if (object->mInvRequestState == INVENTORY_XFER) - { - if (new_id > 0 && new_id != object->mInvRequestXFerId) - { - // we started new download. - gXferManager->abortRequestById(object->mInvRequestXFerId, -1); - object->mInvRequestXFerId = new_id; - } - } - else - { - object->mInvRequestState = INVENTORY_XFER; - object->mInvRequestXFerId = new_id; - } - } -} - -void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code, LLExtStat ext_status) -{ - LLFilenameAndTask* ft = (LLFilenameAndTask*)user_data; - LLViewerObject* object = NULL; - - if (ft - && (0 == error_code) - && (object = gObjectList.findObject(ft->mTaskID)) - && ft->mSerial >= object->mInventorySerialNum) - { - object->mInventorySerialNum = ft->mSerial; - LL_DEBUGS() << "Receiving inventory task file for serial " << object->mInventorySerialNum << " taskid: " << ft->mTaskID << LL_ENDL; - if (ft->mSerial < object->mExpectedInventorySerialNum) - { - // User managed to change something while inventory was loading - LL_DEBUGS() << "Processing file that is potentially out of date for task: " << ft->mTaskID << LL_ENDL; - } - - if (object->loadTaskInvFile(ft->mFilename)) - { - - LLInventoryObject::object_list_t::iterator it = object->mInventory->begin(); - LLInventoryObject::object_list_t::iterator end = object->mInventory->end(); - std::list& pending_lst = object->mPendingInventoryItemsIDs; - - for (; it != end && pending_lst.size(); ++it) - { - LLViewerInventoryItem* item = dynamic_cast(it->get()); - if(item && item->getType() != LLAssetType::AT_CATEGORY) - { - std::list::iterator id_it = std::find(pending_lst.begin(), pending_lst.begin(), item->getAssetUUID()); - if (id_it != pending_lst.end()) - { - pending_lst.erase(id_it); - } - } - } - } - else - { - // MAINT-2597 - crash when trying to edit a no-mod object - // Somehow get an contents inventory response, but with an invalid stream (possibly 0 size?) - // Stated repro was specific to no-mod objects so failing without user interaction should be safe. - LL_WARNS() << "Trying to load invalid task inventory file. Ignoring file contents." << LL_ENDL; - } - } - else - { - // This Occurs When two requests were made, and the first one - // has already handled it. - LL_DEBUGS() << "Problem loading task inventory. Return code: " - << error_code << LL_ENDL; - } - delete ft; -} - -bool LLViewerObject::loadTaskInvFile(const std::string& filename) -{ - std::string filename_and_local_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, filename); - llifstream ifs(filename_and_local_path.c_str()); - if(ifs.good()) - { - U32 fail_count = 0; - char buffer[MAX_STRING]; /* Flawfinder: ignore */ - // *NOTE: This buffer size is hard coded into scanf() below. - char keyword[MAX_STRING]; /* Flawfinder: ignore */ - if(mInventory) - { - mInventory->clear(); // will deref and delete it - } - else - { - mInventory = new LLInventoryObject::object_list_t; - } - while(ifs.good()) - { - ifs.getline(buffer, MAX_STRING); - if (sscanf(buffer, " %254s", keyword) == EOF) /* Flawfinder: ignore */ - { - // Blank file? - LL_WARNS() << "Issue reading from file '" - << filename << "'" << LL_ENDL; - break; - } - else if(0 == strcmp("inv_item", keyword)) - { - LLPointer inv = new LLViewerInventoryItem; - inv->importLegacyStream(ifs); - mInventory->push_front(inv); - } - else if(0 == strcmp("inv_object", keyword)) - { - LLPointer inv = new LLInventoryObject; - inv->importLegacyStream(ifs); - inv->rename("Contents"); - mInventory->push_front(inv); - } - else if (fail_count >= MAX_INV_FILE_READ_FAILS) - { - LL_WARNS() << "Encountered too many unknowns while reading from file: '" - << filename << "'" << LL_ENDL; - break; - } - else - { - // Is there really a point to continue processing? We already failing to display full inventory - fail_count++; - LL_WARNS_ONCE() << "Unknown token while reading from inventory file. Token: '" - << keyword << "'" << LL_ENDL; - } - } - ifs.close(); - LLFile::remove(filename_and_local_path); - } - else - { - LL_WARNS() << "unable to load task inventory: " << filename_and_local_path - << LL_ENDL; - return false; - } - doInventoryCallback(); - - return true; -} - -void LLViewerObject::doInventoryCallback() -{ - for (callback_list_t::iterator iter = mInventoryCallbacks.begin(); - iter != mInventoryCallbacks.end(); ) - { - callback_list_t::iterator curiter = iter++; - LLInventoryCallbackInfo* info = *curiter; - if (info->mListener != NULL) - { - info->mListener->inventoryChanged(this, - mInventory, - mInventorySerialNum, - info->mInventoryData); - } - else - { - LL_INFOS() << "LLViewerObject::doInventoryCallback() deleting bad listener entry." << LL_ENDL; - delete info; - mInventoryCallbacks.erase(curiter); - } - } - - // release inventory loading state - mInvRequestXFerId = 0; - mInvRequestState = INVENTORY_REQUEST_STOPPED; -} - -void LLViewerObject::removeInventory(const LLUUID& item_id) -{ - // close associated floater properties - LLSD params; - params["id"] = item_id; - params["object"] = mID; - LLFloaterReg::hideInstance("item_properties", params); - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RemoveTaskInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addU32Fast(_PREHASH_LocalID, mLocalID); - msg->addUUIDFast(_PREHASH_ItemID, item_id); - msg->sendReliable(mRegionp->getHost()); - deleteInventoryItem(item_id); - ++mExpectedInventorySerialNum; -} - -bool LLViewerObject::isAssetInInventory(LLViewerInventoryItem* item, LLAssetType::EType type) -{ - bool result = false; - - if (item) - { - // For now mPendingInventoryItemsIDs only stores textures and materials - // but if it gets to store more types, it will need to verify type as well - // since null can be a shared default id and it is fine to need a null - // script and a null material simultaneously. - std::list::iterator begin = mPendingInventoryItemsIDs.begin(); - std::list::iterator end = mPendingInventoryItemsIDs.end(); - - bool is_fetching = std::find(begin, end, item->getAssetUUID()) != end; - - // null is the default asset for materials and default for scripts - // so need to check type as well - bool is_fetched = getInventoryItemByAsset(item->getAssetUUID(), type) != NULL; - - result = is_fetched || is_fetching; - } - - return result; -} - -void LLViewerObject::updateMaterialInventory(LLViewerInventoryItem* item, U8 key, bool is_new) -{ - if (!item) - { - return; - } - if (LLAssetType::AT_TEXTURE != item->getType() - && LLAssetType::AT_MATERIAL != item->getType()) - { - // Not supported - return; - } - - if (isAssetInInventory(item, item->getType())) - { - // already there - return; - } - - mPendingInventoryItemsIDs.push_back(item->getAssetUUID()); - updateInventory(item, key, is_new); -} - -void LLViewerObject::updateInventory( - LLViewerInventoryItem* item, - U8 key, - bool is_new) -{ - // This slices the object into what we're concerned about on the - // viewer. The simulator will take the permissions and transfer - // ownership. - LLPointer task_item = - new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(), - item->getAssetUUID(), item->getType(), - item->getInventoryType(), - item->getName(), item->getDescription(), - item->getSaleInfo(), - item->getFlags(), - item->getCreationDate()); - task_item->setTransactionID(item->getTransactionID()); - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_UpdateTaskInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_UpdateData); - msg->addU32Fast(_PREHASH_LocalID, mLocalID); - msg->addU8Fast(_PREHASH_Key, key); - msg->nextBlockFast(_PREHASH_InventoryData); - task_item->packMessage(msg); - msg->sendReliable(mRegionp->getHost()); - - // do the internal logic - doUpdateInventory(task_item, key, is_new); -} - -void LLViewerObject::updateInventoryLocal(LLInventoryItem* item, U8 key) -{ - LLPointer task_item = - new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(), - item->getAssetUUID(), item->getType(), - item->getInventoryType(), - item->getName(), item->getDescription(), - item->getSaleInfo(), item->getFlags(), - item->getCreationDate()); - - // do the internal logic - const bool is_new = false; - doUpdateInventory(task_item, key, is_new); -} - -LLInventoryObject* LLViewerObject::getInventoryObject(const LLUUID& item_id) -{ - LLInventoryObject* rv = NULL; - if(mInventory) - { - LLInventoryObject::object_list_t::iterator it = mInventory->begin(); - LLInventoryObject::object_list_t::iterator end = mInventory->end(); - for ( ; it != end; ++it) - { - if((*it)->getUUID() == item_id) - { - rv = *it; - break; - } - } - } - return rv; -} - -LLInventoryItem* LLViewerObject::getInventoryItem(const LLUUID& item_id) -{ - LLInventoryObject* iobj = getInventoryObject(item_id); - if (!iobj || iobj->getType() == LLAssetType::AT_CATEGORY) - { - return NULL; - } - LLInventoryItem* item = dynamic_cast(iobj); - return item; -} - -void LLViewerObject::getInventoryContents(LLInventoryObject::object_list_t& objects) -{ - if(mInventory) - { - LLInventoryObject::object_list_t::iterator it = mInventory->begin(); - LLInventoryObject::object_list_t::iterator end = mInventory->end(); - for( ; it != end; ++it) - { - if ((*it)->getType() != LLAssetType::AT_CATEGORY) - { - objects.push_back(*it); - } - } - } -} - -LLInventoryObject* LLViewerObject::getInventoryRoot() -{ - if (!mInventory || !mInventory->size()) - { - return NULL; - } - return mInventory->back(); -} - -LLViewerInventoryItem* LLViewerObject::getInventoryItemByAsset(const LLUUID& asset_id) -{ - if (mInventoryDirty) - LL_WARNS() << "Peforming inventory lookup for object " << mID << " that has dirty inventory!" << LL_ENDL; - - LLViewerInventoryItem* rv = NULL; - if(mInventory) - { - LLViewerInventoryItem* item = NULL; - - LLInventoryObject::object_list_t::iterator it = mInventory->begin(); - LLInventoryObject::object_list_t::iterator end = mInventory->end(); - for( ; it != end; ++it) - { - LLInventoryObject* obj = *it; - if(obj->getType() != LLAssetType::AT_CATEGORY) - { - // *FIX: gank-ass down cast! - item = (LLViewerInventoryItem*)obj; - if(item->getAssetUUID() == asset_id) - { - rv = item; - break; - } - } - } - } - return rv; -} - -LLViewerInventoryItem* LLViewerObject::getInventoryItemByAsset(const LLUUID& asset_id, LLAssetType::EType type) -{ - if (mInventoryDirty) - LL_WARNS() << "Peforming inventory lookup for object " << mID << " that has dirty inventory!" << LL_ENDL; - - LLViewerInventoryItem* rv = NULL; - if (type == LLAssetType::AT_CATEGORY) - { - // Whatever called this shouldn't be trying to get a folder by asset - // categories don't have assets - llassert(0); - return rv; - } - - if (mInventory) - { - LLViewerInventoryItem* item = NULL; - - LLInventoryObject::object_list_t::iterator it = mInventory->begin(); - LLInventoryObject::object_list_t::iterator end = mInventory->end(); - for (; it != end; ++it) - { - LLInventoryObject* obj = *it; - if (obj->getType() == type) - { - // *FIX: gank-ass down cast! - item = (LLViewerInventoryItem*)obj; - if (item->getAssetUUID() == asset_id) - { - rv = item; - break; - } - } - } - } - return rv; -} - -void LLViewerObject::updateViewerInventoryAsset( - const LLViewerInventoryItem* item, - const LLUUID& new_asset) -{ - LLPointer task_item = - new LLViewerInventoryItem(item); - task_item->setAssetUUID(new_asset); - - // do the internal logic - doUpdateInventory(task_item, TASK_INVENTORY_ITEM_KEY, false); -} - -void LLViewerObject::setPixelAreaAndAngle(LLAgent &agent) -{ - if (getVolume()) - { //volumes calculate pixel area and angle per face - return; - } - - LLVector3 viewer_pos_agent = gAgentCamera.getCameraPositionAgent(); - LLVector3 pos_agent = getRenderPosition(); - - F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX]; - F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY]; - F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ]; - - F32 max_scale = getMaxScale(); - F32 mid_scale = getMidScale(); - F32 min_scale = getMinScale(); - - // IW: estimate - when close to large objects, computing range based on distance from center is no good - // to try to get a min distance from face, subtract min_scale/2 from the range. - // This means we'll load too much detail sometimes, but that's better than not enough - // I don't think there's a better way to do this without calculating distance per-poly - F32 range = sqrt(dx*dx + dy*dy + dz*dz) - min_scale/2; - - LLViewerCamera* camera = LLViewerCamera::getInstance(); - if (range < 0.001f || isHUDAttachment()) // range == zero - { - mAppAngle = 180.f; - mPixelArea = (F32)camera->getScreenPixelArea(); - } - else - { - mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG; - - F32 pixels_per_meter = camera->getPixelMeterRatio() / range; - - mPixelArea = (pixels_per_meter * max_scale) * (pixels_per_meter * mid_scale); - if (mPixelArea > camera->getScreenPixelArea()) - { - mAppAngle = 180.f; - mPixelArea = (F32)camera->getScreenPixelArea(); - } - } -} - -bool LLViewerObject::updateLOD() -{ - return false; -} - -bool LLViewerObject::updateGeometry(LLDrawable *drawable) -{ - return false; -} - -void LLViewerObject::updateGL() -{ - -} - -void LLViewerObject::updateFaceSize(S32 idx) -{ - -} - -LLDrawable* LLViewerObject::createDrawable(LLPipeline *pipeline) -{ - return NULL; -} - -void LLViewerObject::setScale(const LLVector3 &scale, bool damped) -{ - LLPrimitive::setScale(scale); - if (mDrawable.notNull()) - { - //encompass completely sheared objects by taking - //the most extreme point possible (<1,1,0.5>) - mDrawable->setRadius(LLVector3(1,1,0.5f).scaleVec(scale).magVec()); - updateDrawable(damped); - } - - if( (LL_PCODE_VOLUME == getPCode()) && !isDead() ) - { - if (permYouOwner() || (scale.magVecSquared() > (7.5f * 7.5f)) ) - { - if (!mOnMap) - { - llassert_always(LLWorld::getInstance()->getRegionFromHandle(getRegion()->getHandle())); - - gObjectList.addToMap(this); - mOnMap = true; - } - } - else - { - if (mOnMap) - { - gObjectList.removeFromMap(this); - mOnMap = false; - } - } - } -} - -void LLViewerObject::setObjectCostStale() -{ - mCostStale = true; - // *NOTE: This is harmlessly redundant for Blinn-Phong material updates, as - // the root prim currently gets set stale anyway due to other property - // updates. But it is needed for GLTF material ID updates. - // -Cosmic,2023-06-27 - getRootEdit()->mCostStale = true; -} - -void LLViewerObject::setObjectCost(F32 cost) -{ - mObjectCost = cost; - mCostStale = false; - - if (isSelected()) - { - gFloaterTools->dirty(); - } -} - -void LLViewerObject::setLinksetCost(F32 cost) -{ - mLinksetCost = cost; - mCostStale = false; - - bool needs_refresh = isSelected(); - child_list_t::iterator iter = mChildList.begin(); - while(iter != mChildList.end() && !needs_refresh) - { - LLViewerObject* child = *iter; - needs_refresh = child->isSelected(); - iter++; - } - - if (needs_refresh) - { - gFloaterTools->dirty(); - } -} - -void LLViewerObject::setPhysicsCost(F32 cost) -{ - mPhysicsCost = cost; - mCostStale = false; - - if (isSelected()) - { - gFloaterTools->dirty(); - } -} - -void LLViewerObject::setLinksetPhysicsCost(F32 cost) -{ - mLinksetPhysicsCost = cost; - mCostStale = false; - - if (isSelected()) - { - gFloaterTools->dirty(); - } -} - - -F32 LLViewerObject::getObjectCost() -{ - if (mCostStale) - { - gObjectList.updateObjectCost(this); - } - - return mObjectCost; -} - -F32 LLViewerObject::getLinksetCost() -{ - if (mCostStale) - { - gObjectList.updateObjectCost(this); - } - - return mLinksetCost; -} - -F32 LLViewerObject::getPhysicsCost() -{ - if (mCostStale) - { - gObjectList.updateObjectCost(this); - } - - return mPhysicsCost; -} - -F32 LLViewerObject::getLinksetPhysicsCost() -{ - if (mCostStale) - { - gObjectList.updateObjectCost(this); - } - - return mLinksetPhysicsCost; -} - -F32 LLViewerObject::recursiveGetEstTrianglesMax() const -{ - F32 est_tris = getEstTrianglesMax(); - for (child_list_t::const_iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - const LLViewerObject* child = *iter; - if (!child->isAvatar()) - { - est_tris += child->recursiveGetEstTrianglesMax(); - } - } - return est_tris; -} - -S32 LLViewerObject::getAnimatedObjectMaxTris() const -{ - S32 max_tris = 0; - if (gAgent.getRegion()) - { - LLSD features; - gAgent.getRegion()->getSimulatorFeatures(features); - if (features.has("AnimatedObjects")) - { - max_tris = features["AnimatedObjects"]["AnimatedObjectMaxTris"].asInteger(); - } - } - return max_tris; -} - -F32 LLViewerObject::getEstTrianglesMax() const -{ - return 0.f; -} - -F32 LLViewerObject::getEstTrianglesStreamingCost() const -{ - return 0.f; -} - -// virtual -F32 LLViewerObject::getStreamingCost() const -{ - return 0.f; -} - -// virtual -bool LLViewerObject::getCostData(LLMeshCostData& costs) const -{ - costs = LLMeshCostData(); - return false; -} - -U32 LLViewerObject::getTriangleCount(S32* vcount) const -{ - return 0; -} - -U32 LLViewerObject::getHighLODTriangleCount() -{ - return 0; -} - -U32 LLViewerObject::recursiveGetTriangleCount(S32* vcount) const -{ - S32 total_tris = getTriangleCount(vcount); - LLViewerObject::const_child_list_t& child_list = getChildren(); - for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); ++iter) - { - LLViewerObject* childp = *iter; - if (childp) - { - total_tris += childp->getTriangleCount(vcount); - } - } - return total_tris; -} - -// This is using the stored surface area for each volume (which -// defaults to 1.0 for the case of everything except a sculpt) and -// then scaling it linearly based on the largest dimension in the -// prim's scale. Should revisit at some point. -F32 LLViewerObject::recursiveGetScaledSurfaceArea() const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; - F32 area = 0.f; - const LLDrawable* drawable = mDrawable; - if (drawable) - { - const LLVOVolume* volume = drawable->getVOVolume(); - if (volume) - { - if (volume->getVolume()) - { - const LLVector3& scale = volume->getScale(); - area += volume->getVolume()->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]); - } - LLViewerObject::const_child_list_t children = volume->getChildren(); - for (LLViewerObject::const_child_list_t::const_iterator child_iter = children.begin(); - child_iter != children.end(); - ++child_iter) - { - LLViewerObject* child_obj = *child_iter; - LLVOVolume *child = dynamic_cast( child_obj ); - if (child && child->getVolume()) - { - const LLVector3& scale = child->getScale(); - area += child->getVolume()->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]); - } - } - } - } - return area; -} - -void LLViewerObject::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax) -{ - LLVector4a center; - center.load3(getRenderPosition().mV); - LLVector4a size; - size.load3(getScale().mV); - newMin.setSub(center, size); - newMax.setAdd(center, size); - - mDrawable->setPositionGroup(center); -} - -F32 LLViewerObject::getBinRadius() -{ - if (mDrawable.notNull()) - { - const LLVector4a* ext = mDrawable->getSpatialExtents(); - LLVector4a diff; - diff.setSub(ext[1], ext[0]); - return diff.getLength3().getF32(); - } - - return getScale().magVec(); -} - -F32 LLViewerObject::getMaxScale() const -{ - return llmax(getScale().mV[VX],getScale().mV[VY], getScale().mV[VZ]); -} - -F32 LLViewerObject::getMinScale() const -{ - return llmin(getScale().mV[0],getScale().mV[1],getScale().mV[2]); -} - -F32 LLViewerObject::getMidScale() const -{ - if (getScale().mV[VX] < getScale().mV[VY]) - { - if (getScale().mV[VY] < getScale().mV[VZ]) - { - return getScale().mV[VY]; - } - else if (getScale().mV[VX] < getScale().mV[VZ]) - { - return getScale().mV[VZ]; - } - else - { - return getScale().mV[VX]; - } - } - else if (getScale().mV[VX] < getScale().mV[VZ]) - { - return getScale().mV[VX]; - } - else if (getScale().mV[VY] < getScale().mV[VZ]) - { - return getScale().mV[VZ]; - } - else - { - return getScale().mV[VY]; - } -} - - -void LLViewerObject::updateTextures() -{ -} - -void LLViewerObject::boostTexturePriority(bool boost_children /* = true */) -{ - if (isDead() || !getVolume()) - { - return; - } - - S32 i; - S32 tex_count = getNumTEs(); - for (i = 0; i < tex_count; i++) - { - getTEImage(i)->setBoostLevel(LLGLTexture::BOOST_SELECTED); - } - - if (isSculpted() && !isMesh()) - { - LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); - LLUUID sculpt_id = sculpt_params->getSculptTexture(); - LLViewerTextureManager::getFetchedTexture(sculpt_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)->setBoostLevel(LLGLTexture::BOOST_SELECTED); - } - - if (boost_children) - { - for (child_list_t::iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - child->boostTexturePriority(); - } - } -} - -void LLViewerObject::setLineWidthForWindowSize(S32 window_width) -{ - if (window_width < 700) - { - LLUI::setLineWidth(2.0f); - } - else if (window_width < 1100) - { - LLUI::setLineWidth(3.0f); - } - else if (window_width < 2000) - { - LLUI::setLineWidth(4.0f); - } - else - { - // _damn_, what a nice monitor! - LLUI::setLineWidth(5.0f); - } -} - -void LLViewerObject::increaseArrowLength() -{ -/* ??? - if (mAxisArrowLength == 50) - { - mAxisArrowLength = 100; - } - else - { - mAxisArrowLength = 150; - } -*/ -} - - -void LLViewerObject::decreaseArrowLength() -{ -/* ??? - if (mAxisArrowLength == 150) - { - mAxisArrowLength = 100; - } - else - { - mAxisArrowLength = 50; - } -*/ -} - -// Culled from newsim LLTask::addNVPair -void LLViewerObject::addNVPair(const std::string& data) -{ - // cout << "LLViewerObject::addNVPair() with ---" << data << "---" << endl; - LLNameValue *nv = new LLNameValue(data.c_str()); - -// char splat[MAX_STRING]; -// temp->printNameValue(splat); -// LL_INFOS() << "addNVPair " << splat << LL_ENDL; - - name_value_map_t::iterator iter = mNameValuePairs.find(nv->mName); - if (iter != mNameValuePairs.end()) - { - LLNameValue* foundnv = iter->second; - if (foundnv->mClass != NVC_READ_ONLY) - { - delete foundnv; - mNameValuePairs.erase(iter); - } - else - { - delete nv; -// LL_INFOS() << "Trying to write to Read Only NVPair " << temp->mName << " in addNVPair()" << LL_ENDL; - return; - } - } - mNameValuePairs[nv->mName] = nv; -} - -bool LLViewerObject::removeNVPair(const std::string& name) -{ - char* canonical_name = gNVNameTable.addString(name); - - LL_DEBUGS() << "LLViewerObject::removeNVPair(): " << name << LL_ENDL; - - name_value_map_t::iterator iter = mNameValuePairs.find(canonical_name); - if (iter != mNameValuePairs.end()) - { - if( mRegionp ) - { - LLNameValue* nv = iter->second; -/* - std::string buffer = nv->printNameValue(); - gMessageSystem->newMessageFast(_PREHASH_RemoveNameValuePair); - gMessageSystem->nextBlockFast(_PREHASH_TaskData); - gMessageSystem->addUUIDFast(_PREHASH_ID, mID); - - gMessageSystem->nextBlockFast(_PREHASH_NameValueData); - gMessageSystem->addStringFast(_PREHASH_NVPair, buffer); - - gMessageSystem->sendReliable( mRegionp->getHost() ); -*/ - // Remove the NV pair from the local list. - delete nv; - mNameValuePairs.erase(iter); - return true; - } - else - { - LL_DEBUGS() << "removeNVPair - No region for object" << LL_ENDL; - } - } - return false; -} - - -LLNameValue *LLViewerObject::getNVPair(const std::string& name) const -{ - char *canonical_name; - - canonical_name = gNVNameTable.addString(name); - - // If you access a map with a name that isn't in it, it will add the name and a null pointer. - // So first check if the data is in the map. - name_value_map_t::const_iterator iter = mNameValuePairs.find(canonical_name); - if (iter != mNameValuePairs.end()) - { - return iter->second; - } - else - { - return NULL; - } -} - -void LLViewerObject::updatePositionCaches() const -{ - // If region is removed from the list it is also deleted. - if(mRegionp && LLWorld::instance().isRegionListed(mRegionp)) - { - if (!isRoot()) - { - mPositionRegion = ((LLViewerObject *)getParent())->getPositionRegion() + getPosition() * getParent()->getRotation(); - mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion); - } - else - { - mPositionRegion = getPosition(); - mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion); - } - } -} - -const LLVector3d LLViewerObject::getPositionGlobal() const -{ - // If region is removed from the list it is also deleted. - if(mRegionp && LLWorld::instance().isRegionListed(mRegionp)) - { - LLVector3d position_global = mRegionp->getPosGlobalFromRegion(getPositionRegion()); - - if (isAttachment()) - { - position_global = gAgent.getPosGlobalFromAgent(getRenderPosition()); - } - return position_global; - } - else - { - LLVector3d position_global(getPosition()); - return position_global; - } -} - -const LLVector3 &LLViewerObject::getPositionAgent() const -{ - // If region is removed from the list it is also deleted. - if(mRegionp && LLWorld::instance().isRegionListed(mRegionp)) - { - if (mDrawable.notNull() && (!mDrawable->isRoot() && getParent())) - { - // Don't return cached position if you have a parent, recalc (until all dirtying is done correctly. - LLVector3 position_region; - position_region = ((LLViewerObject *)getParent())->getPositionRegion() + getPosition() * getParent()->getRotation(); - mPositionAgent = mRegionp->getPosAgentFromRegion(position_region); - } - else - { - mPositionAgent = mRegionp->getPosAgentFromRegion(getPosition()); - } - } - return mPositionAgent; -} - -const LLVector3 &LLViewerObject::getPositionRegion() const -{ - if (!isRoot()) - { - LLViewerObject *parent = (LLViewerObject *)getParent(); - mPositionRegion = parent->getPositionRegion() + (getPosition() * parent->getRotation()); - } - else - { - mPositionRegion = getPosition(); - } - - return mPositionRegion; -} - -const LLVector3 LLViewerObject::getPositionEdit() const -{ - if (isRootEdit()) - { - return getPosition(); - } - else - { - LLViewerObject *parent = (LLViewerObject *)getParent(); - LLVector3 position_edit = parent->getPositionEdit() + getPosition() * parent->getRotationEdit(); - return position_edit; - } -} - -const LLVector3 LLViewerObject::getRenderPosition() const -{ - if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED)) - { - LLControlAvatar *cav = getControlAvatar(); - if (isRoot() && cav) - { - F32 fixup; - if ( cav->hasPelvisFixup( fixup) ) - { - //Apply a pelvis fixup (as defined by the avs skin) - LLVector3 pos = mDrawable->getPositionAgent(); - pos[VZ] += fixup; - return pos; - } - } - LLVOAvatar* avatar = getAvatar(); - if ((avatar) && !getControlAvatar()) - { - return avatar->getPositionAgent(); - } - } - - if (mDrawable.isNull() || mDrawable->getGeneration() < 0) - { - return getPositionAgent(); - } - else - { - return mDrawable->getPositionAgent(); - } -} - -const LLVector3 LLViewerObject::getPivotPositionAgent() const -{ - return getRenderPosition(); -} - -const LLQuaternion LLViewerObject::getRenderRotation() const -{ - LLQuaternion ret; - if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED) && !isAnimatedObject()) - { - return ret; - } - - if (mDrawable.isNull() || mDrawable->isStatic()) - { - ret = getRotationEdit(); - } - else - { - if (!mDrawable->isRoot()) - { - ret = getRotation() * LLQuaternion(mDrawable->getParent()->getWorldMatrix()); - } - else - { - ret = LLQuaternion(mDrawable->getWorldMatrix()); - } - } - - return ret; -} - -const LLMatrix4 LLViewerObject::getRenderMatrix() const -{ - return mDrawable->getWorldMatrix(); -} - -const LLQuaternion LLViewerObject::getRotationRegion() const -{ - LLQuaternion global_rotation = getRotation(); - if (!((LLXform *)this)->isRoot()) - { - global_rotation = global_rotation * getParent()->getRotation(); - } - return global_rotation; -} - -const LLQuaternion LLViewerObject::getRotationEdit() const -{ - LLQuaternion global_rotation = getRotation(); - if (!((LLXform *)this)->isRootEdit()) - { - global_rotation = global_rotation * getParent()->getRotation(); - } - return global_rotation; -} - -void LLViewerObject::setPositionAbsoluteGlobal( const LLVector3d &pos_global, bool damped ) -{ - if (isAttachment()) - { - LLVector3 new_pos = mRegionp->getPosRegionFromGlobal(pos_global); - if (isRootEdit()) - { - new_pos -= mDrawable->mXform.getParent()->getWorldPosition(); - LLQuaternion world_rotation = mDrawable->mXform.getParent()->getWorldRotation(); - new_pos = new_pos * ~world_rotation; - } - else - { - LLViewerObject* parentp = (LLViewerObject*)getParent(); - new_pos -= parentp->getPositionAgent(); - new_pos = new_pos * ~parentp->getRotationRegion(); - } - LLViewerObject::setPosition(new_pos); - - if (mParent && ((LLViewerObject*)mParent)->isAvatar()) - { - // we have changed the position of an attachment, so we need to clamp it - LLVOAvatar *avatar = (LLVOAvatar*)mParent; - - avatar->clampAttachmentPositions(); - } - } - else - { - if( isRoot() ) - { - setPositionRegion(mRegionp->getPosRegionFromGlobal(pos_global)); - } - else - { - // the relative position with the parent is not constant - LLViewerObject* parent = (LLViewerObject *)getParent(); - //RN: this assumes we are only calling this function from the edit tools - gPipeline.updateMoveNormalAsync(parent->mDrawable); - - LLVector3 pos_local = mRegionp->getPosRegionFromGlobal(pos_global) - parent->getPositionRegion(); - pos_local = pos_local * ~parent->getRotationRegion(); - LLViewerObject::setPosition( pos_local ); - } - } - //RN: assumes we always want to snap the object when calling this function - gPipeline.updateMoveNormalAsync(mDrawable); -} - -void LLViewerObject::setPosition(const LLVector3 &pos, bool damped) -{ - if (getPosition() != pos) - { - setChanged(TRANSLATED | SILHOUETTE); - } - - LLXform::setPosition(pos); - updateDrawable(damped); - if (isRoot()) - { - // position caches need to be up to date on root objects - updatePositionCaches(); - } -} - -void LLViewerObject::setPositionGlobal(const LLVector3d &pos_global, bool damped) -{ - if (isAttachment()) - { - if (isRootEdit()) - { - LLVector3 newPos = mRegionp->getPosRegionFromGlobal(pos_global); - newPos = newPos - mDrawable->mXform.getParent()->getWorldPosition(); - - LLQuaternion invWorldRotation = mDrawable->mXform.getParent()->getWorldRotation(); - invWorldRotation.transQuat(); - - newPos = newPos * invWorldRotation; - LLViewerObject::setPosition(newPos); - } - else - { - // assumes parent is root editable (root of attachment) - LLVector3 newPos = mRegionp->getPosRegionFromGlobal(pos_global); - newPos = newPos - mDrawable->mXform.getParent()->getWorldPosition(); - LLVector3 delta_pos = newPos - getPosition(); - - LLQuaternion invRotation = mDrawable->getRotation(); - invRotation.transQuat(); - - delta_pos = delta_pos * invRotation; - - // *FIX: is this right? Shouldn't we be calling the - // LLViewerObject version of setPosition? - LLVector3 old_pos = mDrawable->mXform.getParent()->getPosition(); - mDrawable->mXform.getParent()->setPosition(old_pos + delta_pos); - setChanged(TRANSLATED | SILHOUETTE); - } - if (mParent && ((LLViewerObject*)mParent)->isAvatar()) - { - // we have changed the position of an attachment, so we need to clamp it - LLVOAvatar *avatar = (LLVOAvatar*)mParent; - - avatar->clampAttachmentPositions(); - } - } - else - { - if (isRoot()) - { - setPositionRegion(mRegionp->getPosRegionFromGlobal(pos_global)); - } - else - { - // the relative position with the parent is constant, but the parent's position needs to be changed - LLVector3d position_offset; - position_offset.setVec(getPosition()*getParent()->getRotation()); - LLVector3d new_pos_global = pos_global - position_offset; - ((LLViewerObject *)getParent())->setPositionGlobal(new_pos_global); - } - } - updateDrawable(damped); -} - - -void LLViewerObject::setPositionParent(const LLVector3 &pos_parent, bool damped) -{ - // Set position relative to parent, if no parent, relative to region - if (!isRoot()) - { - LLViewerObject::setPosition(pos_parent, damped); - //updateDrawable(damped); - } - else - { - setPositionRegion(pos_parent, damped); - } -} - -void LLViewerObject::setPositionRegion(const LLVector3 &pos_region, bool damped) -{ - if (!isRootEdit()) - { - LLViewerObject* parent = (LLViewerObject*) getParent(); - LLViewerObject::setPosition((pos_region-parent->getPositionRegion())*~parent->getRotationRegion()); - } - else - { - LLViewerObject::setPosition(pos_region); - mPositionRegion = pos_region; - mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion); - } -} - -void LLViewerObject::setPositionAgent(const LLVector3 &pos_agent, bool damped) -{ - LLVector3 pos_region = getRegion()->getPosRegionFromAgent(pos_agent); - setPositionRegion(pos_region, damped); -} - -// identical to setPositionRegion() except it checks for child-joints -// and doesn't also move the joint-parent -// TODO -- implement similar intelligence for joint-parents toward -// their joint-children -void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, bool damped) -{ - if (!isRootEdit()) - { - // the relative position with the parent is constant, but the parent's position needs to be changed - LLVector3 position_offset = getPosition() * getParent()->getRotation(); - - ((LLViewerObject *)getParent())->setPositionEdit(pos_edit - position_offset); - updateDrawable(damped); - } - else - { - LLViewerObject::setPosition(pos_edit, damped); - mPositionRegion = pos_edit; - mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion); - } -} - - -LLViewerObject* LLViewerObject::getRootEdit() const -{ - const LLViewerObject* root = this; - while (root->mParent - && !((LLViewerObject*)root->mParent)->isAvatar()) - { - root = (LLViewerObject*)root->mParent; - } - return (LLViewerObject*)root; -} - - -bool LLViewerObject::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, - S32 face, - bool pick_transparent, - bool pick_rigged, - bool pick_unselectable, - S32* face_hit, - LLVector4a* intersection, - LLVector2* tex_coord, - LLVector4a* normal, - LLVector4a* tangent) -{ - return false; -} - -bool LLViewerObject::lineSegmentBoundingBox(const LLVector4a& start, const LLVector4a& end) -{ - if (mDrawable.isNull() || mDrawable->isDead()) - { - return false; - } - - const LLVector4a* ext = mDrawable->getSpatialExtents(); - - //VECTORIZE THIS - LLVector4a center; - center.setAdd(ext[1], ext[0]); - center.mul(0.5f); - LLVector4a size; - size.setSub(ext[1], ext[0]); - size.mul(0.5f); - - return LLLineSegmentBoxIntersect(start, end, center, size); -} - -U8 LLViewerObject::getMediaType() const -{ - if (mMedia) - { - return mMedia->mMediaType; - } - else - { - return LLViewerObject::MEDIA_NONE; - } -} - -void LLViewerObject::setMediaType(U8 media_type) -{ - if (!mMedia) - { - // TODO what if we don't have a media pointer? - } - else if (mMedia->mMediaType != media_type) - { - mMedia->mMediaType = media_type; - - // TODO: update materials with new image - } -} - -std::string LLViewerObject::getMediaURL() const -{ - if (mMedia) - { - return mMedia->mMediaURL; - } - else - { - return std::string(); - } -} - -void LLViewerObject::setMediaURL(const std::string& media_url) -{ - if (!mMedia) - { - mMedia = new LLViewerObjectMedia; - mMedia->mMediaURL = media_url; - mMedia->mPassedWhitelist = false; - - // TODO: update materials with new image - } - else if (mMedia->mMediaURL != media_url) - { - mMedia->mMediaURL = media_url; - mMedia->mPassedWhitelist = false; - - // TODO: update materials with new image - } -} - -bool LLViewerObject::getMediaPassedWhitelist() const -{ - if (mMedia) - { - return mMedia->mPassedWhitelist; - } - else - { - return false; - } -} - -void LLViewerObject::setMediaPassedWhitelist(bool passed) -{ - if (mMedia) - { - mMedia->mPassedWhitelist = passed; - } -} - -bool LLViewerObject::setMaterial(const U8 material) -{ - bool res = LLPrimitive::setMaterial(material); - if (res) - { - setChanged(TEXTURE); - } - return res; -} - -void LLViewerObject::setNumTEs(const U8 num_tes) -{ - U32 i; - if (num_tes != getNumTEs()) - { - if (num_tes) - { - LLPointer *new_images; - new_images = new LLPointer[num_tes]; - - LLPointer *new_normmaps; - new_normmaps = new LLPointer[num_tes]; - - LLPointer *new_specmaps; - new_specmaps = new LLPointer[num_tes]; - for (i = 0; i < num_tes; i++) - { - if (i < getNumTEs()) - { - new_images[i] = mTEImages[i]; - new_normmaps[i] = mTENormalMaps[i]; - new_specmaps[i] = mTESpecularMaps[i]; - } - else if (getNumTEs()) - { - new_images[i] = mTEImages[getNumTEs()-1]; - new_normmaps[i] = mTENormalMaps[getNumTEs()-1]; - new_specmaps[i] = mTESpecularMaps[getNumTEs()-1]; - } - else - { - new_images[i] = NULL; - new_normmaps[i] = NULL; - new_specmaps[i] = NULL; - } - } - - deleteTEImages(); - - mTEImages = new_images; - mTENormalMaps = new_normmaps; - mTESpecularMaps = new_specmaps; - } - else - { - deleteTEImages(); - } - - S32 original_tes = getNumTEs(); - - LLPrimitive::setNumTEs(num_tes); - setChanged(TEXTURE); - - // touch up GLTF materials - if (original_tes > 0) - { - for (int i = original_tes; i < getNumTEs(); ++i) - { - LLTextureEntry* src = getTE(original_tes - 1); - LLTextureEntry* tep = getTE(i); - setRenderMaterialID(i, getRenderMaterialID(original_tes - 1), false); - - if (tep) - { - LLGLTFMaterial* base_material = src->getGLTFMaterial(); - LLGLTFMaterial* override_material = src->getGLTFMaterialOverride(); - if (base_material && override_material) - { - tep->setGLTFMaterialOverride(new LLGLTFMaterial(*override_material)); - - LLGLTFMaterial* render_material = new LLFetchedGLTFMaterial(); - *render_material = *base_material; - render_material->applyOverride(*override_material); - tep->setGLTFRenderMaterial(render_material); - } - } - } - } - - if (mDrawable.notNull()) - { - gPipeline.markTextured(mDrawable); - } - } -} - -void LLViewerObject::sendMaterialUpdate() const -{ - LLViewerRegion* regionp = getRegion(); - if(!regionp) return; - gMessageSystem->newMessageFast(_PREHASH_ObjectMaterial); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID ); - gMessageSystem->addU8Fast(_PREHASH_Material, getMaterial() ); - gMessageSystem->sendReliable( regionp->getHost() ); - -} - -//formerly send_object_shape(LLViewerObject *object) -void LLViewerObject::sendShapeUpdate() -{ - gMessageSystem->newMessageFast(_PREHASH_ObjectShape); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID ); - - LLVolumeMessage::packVolumeParams(&getVolume()->getParams(), gMessageSystem); - - LLViewerRegion *regionp = getRegion(); - gMessageSystem->sendReliable( regionp->getHost() ); -} - - -void LLViewerObject::sendTEUpdate() const -{ - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ObjectImage); - - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - - msg->nextBlockFast(_PREHASH_ObjectData); - msg->addU32Fast(_PREHASH_ObjectLocalID, mLocalID ); - if (mMedia) - { - msg->addString("MediaURL", mMedia->mMediaURL); - } - else - { - msg->addString("MediaURL", NULL); - } - - // TODO send media type - - packTEMessage(msg); - - LLViewerRegion *regionp = getRegion(); - msg->sendReliable( regionp->getHost() ); -} - -LLViewerTexture* LLViewerObject::getBakedTextureForMagicId(const LLUUID& id) -{ - if (!LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(id)) - { - return NULL; - } - - LLViewerObject *root = getRootEdit(); - if (root && root->isAnimatedObject()) - { - return LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - } - - LLVOAvatar* avatar = getAvatar(); - if (avatar && !isHUDAttachment()) - { - LLAvatarAppearanceDefines::EBakedTextureIndex texIndex = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::assetIdToBakedTextureIndex(id); - LLViewerTexture* bakedTexture = avatar->getBakedTexture(texIndex); - if (bakedTexture == NULL || bakedTexture->isMissingAsset()) - { - return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - } - else - { - return bakedTexture; - } - } - else - { - return LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - } - -} - -void LLViewerObject::updateAvatarMeshVisibility(const LLUUID& id, const LLUUID& old_id) -{ - if (id == old_id) - { - return; - } - - if (!LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(old_id) && !LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(id)) - { - return; - } - - LLVOAvatar* avatar = getAvatar(); - if (avatar) - { - avatar->updateMeshVisibility(); - } -} - - -void LLViewerObject::setTE(const U8 te, const LLTextureEntry& texture_entry) -{ - LLUUID old_image_id; - if (getTE(te)) - { - old_image_id = getTE(te)->getID(); - } - - LLPrimitive::setTE(te, texture_entry); - - const LLUUID& image_id = getTE(te)->getID(); - LLViewerTexture* bakedTexture = getBakedTextureForMagicId(image_id); - mTEImages[te] = bakedTexture ? bakedTexture : LLViewerTextureManager::getFetchedTexture(image_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - - updateAvatarMeshVisibility(image_id, old_image_id); - - updateTEMaterialTextures(te); -} - -void LLViewerObject::updateTEMaterialTextures(U8 te) -{ - if (getTE(te)->getMaterialParams().notNull()) - { - const LLUUID& norm_id = getTE(te)->getMaterialParams()->getNormalID(); - mTENormalMaps[te] = LLViewerTextureManager::getFetchedTexture(norm_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - - const LLUUID& spec_id = getTE(te)->getMaterialParams()->getSpecularID(); - mTESpecularMaps[te] = LLViewerTextureManager::getFetchedTexture(spec_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - } - - LLFetchedGLTFMaterial* mat = (LLFetchedGLTFMaterial*) getTE(te)->getGLTFRenderMaterial(); - llassert(mat == nullptr || dynamic_cast(getTE(te)->getGLTFRenderMaterial()) != nullptr); - LLUUID mat_id = getRenderMaterialID(te); - if (mat == nullptr && mat_id.notNull()) - { - mat = (LLFetchedGLTFMaterial*) gGLTFMaterialList.getMaterial(mat_id); - llassert(mat == nullptr || dynamic_cast(gGLTFMaterialList.getMaterial(mat_id)) != nullptr); - if (mat->isFetching()) - { // material is not loaded yet, rebuild draw info when the object finishes loading - mat->onMaterialComplete([id=getID()] - { - LLViewerObject* obj = gObjectList.findObject(id); - if (obj) - { - obj->markForUpdate(); - } - }); - } - getTE(te)->setGLTFMaterial(mat); - } - else if (mat_id.isNull() && mat != nullptr) - { - mat = nullptr; - getTE(te)->setGLTFMaterial(nullptr); - } - - auto fetch_texture = [this](const LLUUID& id) - { - LLViewerFetchedTexture* img = nullptr; - if (id.notNull()) - { - if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(id)) - { - // TODO -- fall back to LLTextureEntry::mGLTFRenderMaterial when overriding with baked texture - LLViewerTexture* viewerTexture = getBakedTextureForMagicId(id); - img = viewerTexture ? dynamic_cast(viewerTexture) : nullptr; - } - else - { - img = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - img->addTextureStats(64.f * 64.f, true); - } - } - - return img; - }; - - if (mat != nullptr) - { - mat->mBaseColorTexture = fetch_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]); - mat->mNormalTexture = fetch_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL]); - mat->mMetallicRoughnessTexture = fetch_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS]); - mat->mEmissiveTexture= fetch_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE]); - } -} - -void LLViewerObject::refreshBakeTexture() -{ - for (int face_index = 0; face_index < getNumTEs(); face_index++) - { - LLTextureEntry* tex_entry = getTE(face_index); - if (tex_entry && LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(tex_entry->getID())) - { - const LLUUID& image_id = tex_entry->getID(); - LLViewerTexture* bakedTexture = getBakedTextureForMagicId(image_id); - changeTEImage(face_index, bakedTexture); - } - } -} - -void LLViewerObject::setTEImage(const U8 te, LLViewerTexture *imagep) -{ - if (mTEImages[te] != imagep) - { - LLUUID old_image_id = getTE(te) ? getTE(te)->getID() : LLUUID::null; - - LLPrimitive::setTETexture(te, imagep->getID()); - - LLViewerTexture* baked_texture = getBakedTextureForMagicId(imagep->getID()); - mTEImages[te] = baked_texture ? baked_texture : imagep; - updateAvatarMeshVisibility(imagep->getID(), old_image_id); - setChanged(TEXTURE); - if (mDrawable.notNull()) - { - gPipeline.markTextured(mDrawable); - } - } -} - -S32 LLViewerObject::setTETextureCore(const U8 te, LLViewerTexture *image) -{ - LLUUID old_image_id = getTE(te)->getID(); - const LLUUID& uuid = image ? image->getID() : LLUUID::null; - S32 retval = 0; - if (uuid != getTE(te)->getID() || - uuid == LLUUID::null) - { - retval = LLPrimitive::setTETexture(te, uuid); - LLViewerTexture* baked_texture = getBakedTextureForMagicId(uuid); - mTEImages[te] = baked_texture ? baked_texture : image; - updateAvatarMeshVisibility(uuid,old_image_id); - setChanged(TEXTURE); - if (mDrawable.notNull()) - { - gPipeline.markTextured(mDrawable); - } - } - return retval; -} - -S32 LLViewerObject::setTENormalMapCore(const U8 te, LLViewerTexture *image) -{ - S32 retval = TEM_CHANGE_TEXTURE; - const LLUUID& uuid = image ? image->getID() : LLUUID::null; - if (uuid != getTE(te)->getID() || - uuid == LLUUID::null) - { - LLTextureEntry* tep = getTE(te); - LLMaterial* mat = NULL; - if (tep) - { - mat = tep->getMaterialParams(); - } - - if (mat) - { - mat->setNormalID(uuid); - } - } - changeTENormalMap(te,image); - return retval; -} - -S32 LLViewerObject::setTESpecularMapCore(const U8 te, LLViewerTexture *image) -{ - S32 retval = TEM_CHANGE_TEXTURE; - const LLUUID& uuid = image ? image->getID() : LLUUID::null; - if (uuid != getTE(te)->getID() || - uuid == LLUUID::null) - { - LLTextureEntry* tep = getTE(te); - LLMaterial* mat = NULL; - if (tep) - { - mat = tep->getMaterialParams(); - } - - if (mat) - { - mat->setSpecularID(uuid); - } - } - changeTESpecularMap(te, image); - return retval; -} - -//virtual -void LLViewerObject::changeTEImage(S32 index, LLViewerTexture* new_image) -{ - if(index < 0 || index >= getNumTEs()) - { - return ; - } - mTEImages[index] = new_image ; -} - -void LLViewerObject::changeTENormalMap(S32 index, LLViewerTexture* new_image) -{ - if(index < 0 || index >= getNumTEs()) - { - return ; - } - mTENormalMaps[index] = new_image ; - refreshMaterials(); -} - -void LLViewerObject::changeTESpecularMap(S32 index, LLViewerTexture* new_image) -{ - if(index < 0 || index >= getNumTEs()) - { - return ; - } - mTESpecularMaps[index] = new_image ; - refreshMaterials(); -} - -S32 LLViewerObject::setTETexture(const U8 te, const LLUUID& uuid) -{ - // Invalid host == get from the agent's sim - LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture( - uuid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); - return setTETextureCore(te, image); -} - -S32 LLViewerObject::setTENormalMap(const U8 te, const LLUUID& uuid) -{ - LLViewerFetchedTexture *image = (uuid == LLUUID::null) ? NULL : LLViewerTextureManager::getFetchedTexture( - uuid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); - return setTENormalMapCore(te, image); -} - -S32 LLViewerObject::setTESpecularMap(const U8 te, const LLUUID& uuid) -{ - LLViewerFetchedTexture *image = (uuid == LLUUID::null) ? NULL : LLViewerTextureManager::getFetchedTexture( - uuid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); - return setTESpecularMapCore(te, image); -} - -S32 LLViewerObject::setTEColor(const U8 te, const LLColor3& color) -{ - return setTEColor(te, LLColor4(color)); -} - -S32 LLViewerObject::setTEColor(const U8 te, const LLColor4& color) -{ - S32 retval = 0; - const LLTextureEntry *tep = getTE(te); - if (!tep) - { - LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; - } - else if (color != tep->getColor()) - { - retval = LLPrimitive::setTEColor(te, color); - if (mDrawable.notNull() && retval) - { - // These should only happen on updates which are not the initial update. - dirtyMesh(); - } - } - return retval; -} - -S32 LLViewerObject::setTEBumpmap(const U8 te, const U8 bump) -{ - S32 retval = 0; - const LLTextureEntry *tep = getTE(te); - if (!tep) - { - LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; - } - else if (bump != tep->getBumpmap()) - { - retval = LLPrimitive::setTEBumpmap(te, bump); - setChanged(TEXTURE); - if (mDrawable.notNull() && retval) - { - gPipeline.markTextured(mDrawable); - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY); - } - } - return retval; -} - -S32 LLViewerObject::setTETexGen(const U8 te, const U8 texgen) -{ - S32 retval = 0; - const LLTextureEntry *tep = getTE(te); - if (!tep) - { - LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; - } - else if (texgen != tep->getTexGen()) - { - retval = LLPrimitive::setTETexGen(te, texgen); - setChanged(TEXTURE); - } - return retval; -} - -S32 LLViewerObject::setTEMediaTexGen(const U8 te, const U8 media) -{ - S32 retval = 0; - const LLTextureEntry *tep = getTE(te); - if (!tep) - { - LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; - } - else if (media != tep->getMediaTexGen()) - { - retval = LLPrimitive::setTEMediaTexGen(te, media); - setChanged(TEXTURE); - } - return retval; -} - -S32 LLViewerObject::setTEShiny(const U8 te, const U8 shiny) -{ - S32 retval = 0; - const LLTextureEntry *tep = getTE(te); - if (!tep) - { - LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; - } - else if (shiny != tep->getShiny()) - { - retval = LLPrimitive::setTEShiny(te, shiny); - setChanged(TEXTURE); - } - return retval; -} - -S32 LLViewerObject::setTEFullbright(const U8 te, const U8 fullbright) -{ - S32 retval = 0; - const LLTextureEntry *tep = getTE(te); - if (!tep) - { - LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; - } - else if (fullbright != tep->getFullbright()) - { - retval = LLPrimitive::setTEFullbright(te, fullbright); - setChanged(TEXTURE); - if (mDrawable.notNull() && retval) - { - gPipeline.markTextured(mDrawable); - } - } - return retval; -} - - -S32 LLViewerObject::setTEMediaFlags(const U8 te, const U8 media_flags) -{ - // this might need work for media type - S32 retval = 0; - const LLTextureEntry *tep = getTE(te); - if (!tep) - { - LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; - } - else if (media_flags != tep->getMediaFlags()) - { - retval = LLPrimitive::setTEMediaFlags(te, media_flags); - setChanged(TEXTURE); - if (mDrawable.notNull() && retval) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); - gPipeline.markTextured(mDrawable); - } - } - return retval; -} - -S32 LLViewerObject::setTEGlow(const U8 te, const F32 glow) -{ - S32 retval = 0; - const LLTextureEntry *tep = getTE(te); - if (!tep) - { - LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; - } - else if (glow != tep->getGlow()) - { - retval = LLPrimitive::setTEGlow(te, glow); - setChanged(TEXTURE); - if (mDrawable.notNull() && retval) - { - gPipeline.markTextured(mDrawable); - } - } - return retval; -} - -S32 LLViewerObject::setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID) -{ - S32 retval = 0; - const LLTextureEntry *tep = getTE(te); - if (!tep) - { - LL_WARNS("Material") << "No texture entry for te " << (S32)te - << ", object " << mID - << ", material " << pMaterialID - << LL_ENDL; - } - //else if (pMaterialID != tep->getMaterialID()) - { - LL_DEBUGS("Material") << "Changing texture entry for te " << (S32)te - << ", object " << mID - << ", material " << pMaterialID - << LL_ENDL; - retval = LLPrimitive::setTEMaterialID(te, pMaterialID); - refreshMaterials(); - } - return retval; -} - -S32 LLViewerObject::setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams) -{ - S32 retval = 0; - const LLTextureEntry *tep = getTE(te); - if (!tep) - { - LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; - return 0; - } - - retval = LLPrimitive::setTEMaterialParams(te, pMaterialParams); - LL_DEBUGS("Material") << "Changing material params for te " << (S32)te - << ", object " << mID - << " (" << retval << ")" - << LL_ENDL; - setTENormalMap(te, (pMaterialParams) ? pMaterialParams->getNormalID() : LLUUID::null); - setTESpecularMap(te, (pMaterialParams) ? pMaterialParams->getSpecularID() : LLUUID::null); - - return retval; -} - -S32 LLViewerObject::setTEGLTFMaterialOverride(U8 te, LLGLTFMaterial* override_mat) -{ - LL_PROFILE_ZONE_SCOPED; - S32 retval = TEM_CHANGE_NONE; - - LLTextureEntry* tep = getTE(te); - if (!tep) - { // this could happen if the object is not fully formed yet - // returning TEM_CHANGE_NONE here signals to LLGLTFMaterialList to queue the override for later - return retval; - } - - LLFetchedGLTFMaterial* src_mat = (LLFetchedGLTFMaterial*) tep->getGLTFMaterial(); - llassert(src_mat == nullptr || dynamic_cast(tep->getGLTFMaterial()) != nullptr); - // if override mat exists, we must also have a source mat - if (!src_mat) - { - // we can get into this state if an override has arrived before the viewer has - // received or handled an update, return TEM_CHANGE_NONE to signal to LLGLTFMaterialList that it - // should queue the update for later - return retval; - } - - if(src_mat->isFetching()) - { - // if still fetching, we need to wait until it is done and try again - return retval; - } - - retval = tep->setGLTFMaterialOverride(override_mat); - - if (retval) - { - if (override_mat) - { - LLFetchedGLTFMaterial* render_mat = new LLFetchedGLTFMaterial(*src_mat); - render_mat->applyOverride(*override_mat); - tep->setGLTFRenderMaterial(render_mat); - retval = TEM_CHANGE_TEXTURE; - - for (LLGLTFMaterial::local_tex_map_t::value_type &val : override_mat->mTrackingIdToLocalTexture) - { - LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(val.first, override_mat); - } - - } - else if (tep->setGLTFRenderMaterial(nullptr)) - { - retval = TEM_CHANGE_TEXTURE; - } - } - - return retval; -} - -void LLViewerObject::refreshMaterials() -{ - setChanged(TEXTURE); - if (mDrawable.notNull()) - { - gPipeline.markTextured(mDrawable); - } -} - -S32 LLViewerObject::setTEScale(const U8 te, const F32 s, const F32 t) -{ - S32 retval = 0; - retval = LLPrimitive::setTEScale(te, s, t); - setChanged(TEXTURE); - if (mDrawable.notNull() && retval) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); - } - return retval; -} - -S32 LLViewerObject::setTEScaleS(const U8 te, const F32 s) -{ - S32 retval = LLPrimitive::setTEScaleS(te, s); - if (mDrawable.notNull() && retval) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); - } - - return retval; -} - -S32 LLViewerObject::setTEScaleT(const U8 te, const F32 t) -{ - S32 retval = LLPrimitive::setTEScaleT(te, t); - if (mDrawable.notNull() && retval) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); - } - - return retval; -} - -S32 LLViewerObject::setTEOffset(const U8 te, const F32 s, const F32 t) -{ - S32 retval = LLPrimitive::setTEOffset(te, s, t); - if (mDrawable.notNull() && retval) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); - } - return retval; -} - -S32 LLViewerObject::setTEOffsetS(const U8 te, const F32 s) -{ - S32 retval = LLPrimitive::setTEOffsetS(te, s); - if (mDrawable.notNull() && retval) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); - } - - return retval; -} - -S32 LLViewerObject::setTEOffsetT(const U8 te, const F32 t) -{ - S32 retval = LLPrimitive::setTEOffsetT(te, t); - if (mDrawable.notNull() && retval) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); - } - - return retval; -} - -S32 LLViewerObject::setTERotation(const U8 te, const F32 r) -{ - S32 retval = LLPrimitive::setTERotation(te, r); - if (mDrawable.notNull() && retval) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); - shrinkWrap(); - } - return retval; -} - - -LLViewerTexture *LLViewerObject::getTEImage(const U8 face) const -{ -// llassert(mTEImages); - - if (face < getNumTEs()) - { - LLViewerTexture* image = mTEImages[face]; - if (image) - { - return image; - } - else - { - return (LLViewerTexture*)(LLViewerFetchedTexture::sDefaultImagep); - } - } - - LL_ERRS() << llformat("Requested Image from invalid face: %d/%d",face,getNumTEs()) << LL_ENDL; - - return NULL; -} - - -bool LLViewerObject::isImageAlphaBlended(const U8 te) const -{ - LLViewerTexture* image = getTEImage(te); - LLGLenum format = image ? image->getPrimaryFormat() : GL_RGB; - switch (format) - { - case GL_RGBA: - case GL_ALPHA: - { - return true; - } - break; - - case GL_RGB: break; - default: - { - LL_WARNS() << "Unexpected tex format in LLViewerObject::isImageAlphaBlended...returning no alpha." << LL_ENDL; - } - break; - } - - return false; -} - -LLViewerTexture *LLViewerObject::getTENormalMap(const U8 face) const -{ - // llassert(mTEImages); - - if (face < getNumTEs()) - { - LLViewerTexture* image = mTENormalMaps[face]; - if (image) - { - return image; - } - else - { - return (LLViewerTexture*)(LLViewerFetchedTexture::sDefaultImagep); - } - } - - LL_ERRS() << llformat("Requested Image from invalid face: %d/%d",face,getNumTEs()) << LL_ENDL; - - return NULL; -} - -LLViewerTexture *LLViewerObject::getTESpecularMap(const U8 face) const -{ - // llassert(mTEImages); - - if (face < getNumTEs()) - { - LLViewerTexture* image = mTESpecularMaps[face]; - if (image) - { - return image; - } - else - { - return (LLViewerTexture*)(LLViewerFetchedTexture::sDefaultImagep); - } - } - - LL_ERRS() << llformat("Requested Image from invalid face: %d/%d",face,getNumTEs()) << LL_ENDL; - - return NULL; -} - -void LLViewerObject::fitFaceTexture(const U8 face) -{ - LL_INFOS() << "fitFaceTexture not implemented" << LL_ENDL; -} - -LLBBox LLViewerObject::getBoundingBoxAgent() const -{ - LLVector3 position_agent; - LLQuaternion rot; - LLViewerObject* avatar_parent = NULL; - LLViewerObject* root_edit = (LLViewerObject*)getRootEdit(); - if (root_edit) - { - avatar_parent = (LLViewerObject*)root_edit->getParent(); - } - - if (avatar_parent && avatar_parent->isAvatar() && - root_edit && root_edit->mDrawable.notNull() && root_edit->mDrawable->getXform()->getParent()) - { - LLXform* parent_xform = root_edit->mDrawable->getXform()->getParent(); - position_agent = (getPositionEdit() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition(); - rot = getRotationEdit() * parent_xform->getWorldRotation(); - } - else - { - position_agent = getPositionAgent(); - rot = getRotationRegion(); - } - - return LLBBox( position_agent, rot, getScale() * -0.5f, getScale() * 0.5f ); -} - -U32 LLViewerObject::getNumVertices() const -{ - U32 num_vertices = 0; - if (mDrawable.notNull()) - { - S32 i, num_faces; - num_faces = mDrawable->getNumFaces(); - for (i = 0; i < num_faces; i++) - { - LLFace * facep = mDrawable->getFace(i); - if (facep) - { - num_vertices += facep->getGeomCount(); - } - } - } - return num_vertices; -} - -U32 LLViewerObject::getNumIndices() const -{ - U32 num_indices = 0; - if (mDrawable.notNull()) - { - S32 i, num_faces; - num_faces = mDrawable->getNumFaces(); - for (i = 0; i < num_faces; i++) - { - LLFace * facep = mDrawable->getFace(i); - if (facep) - { - num_indices += facep->getIndicesCount(); - } - } - } - return num_indices; -} - -// Find the number of instances of this object's inventory that are of the given type -S32 LLViewerObject::countInventoryContents(LLAssetType::EType type) -{ - S32 count = 0; - if( mInventory ) - { - LLInventoryObject::object_list_t::const_iterator it = mInventory->begin(); - LLInventoryObject::object_list_t::const_iterator end = mInventory->end(); - for( ; it != end ; ++it ) - { - if( (*it)->getType() == type ) - { - ++count; - } - } - } - return count; -} - -void LLViewerObject::setDebugText(const std::string &utf8text, const LLColor4& color) -{ - if (utf8text.empty() && !mText) - { - return; - } - - if (!mText) - { - initHudText(); - } - mText->setColor(color); - mText->setString(utf8text); - mText->setZCompare(false); - mText->setDoFade(false); - updateText(); -} - -void LLViewerObject::appendDebugText(const std::string &utf8text) -{ - if (utf8text.empty() && !mText) - { - return; - } - - if (!mText) - { - initHudText(); - } - mText->addLine(utf8text, LLColor4::white); - mText->setZCompare(false); - mText->setDoFade(false); - updateText(); -} - -void LLViewerObject::initHudText() -{ - mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT); - mText->setFont(LLFontGL::getFontSansSerif()); - mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP); - mText->setMaxLines(-1); - mText->setSourceObject(this); - mText->setOnHUDAttachment(isHUDAttachment()); -} - -void LLViewerObject::restoreHudText() -{ - if (mHudText.empty()) - { - if (mText) - { - mText->markDead(); - mText = NULL; - } - } - else - { - if (!mText) - { - initHudText(); - } - else - { - // Restore default values - mText->setZCompare(true); - mText->setDoFade(true); - } - mText->setColor(mHudTextColor); - mText->setString(mHudText); - } -} - -void LLViewerObject::setIcon(LLViewerTexture* icon_image) -{ - if (!mIcon) - { - mIcon = (LLHUDIcon *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_ICON); - mIcon->setSourceObject(this); - mIcon->setImage(icon_image); - // *TODO: make this user configurable - mIcon->setScale(0.03f); - } - else - { - mIcon->restartLifeTimer(); - } -} - -void LLViewerObject::clearIcon() -{ - if (mIcon) - { - mIcon = NULL; - } -} - -LLViewerObject* LLViewerObject::getSubParent() -{ - return (LLViewerObject*) getParent(); -} - -const LLViewerObject* LLViewerObject::getSubParent() const -{ - return (const LLViewerObject*) getParent(); -} - -bool LLViewerObject::isOnMap() -{ - return mOnMap; -} - - -void LLViewerObject::updateText() -{ - if (!isDead()) - { - if (mText.notNull()) - { - LLVOAvatar* avatar = getAvatar(); - if (avatar) - { - mText->setHidden(avatar->isInMuteList()); - } - - LLVector3 up_offset(0,0,0); - up_offset.mV[2] = getScale().mV[VZ]*0.6f; - - if (mDrawable.notNull()) - { - mText->setPositionAgent(getRenderPosition() + up_offset); - } - else - { - mText->setPositionAgent(getPositionAgent() + up_offset); - } - } - } -} - -bool LLViewerObject::isOwnerInMuteList(LLUUID id) -{ - LLUUID owner_id = id.isNull() ? mOwnerID : id; - if (isAvatar() || owner_id.isNull()) - { - return false; - } - bool muted = false; - F64 now = LLFrameTimer::getTotalSeconds(); - if (now < mCachedMuteListUpdateTime) - { - muted = mCachedOwnerInMuteList; - } - else - { - muted = LLMuteList::getInstance()->isMuted(owner_id); - - const F64 SECONDS_BETWEEN_MUTE_UPDATES = 1; - mCachedMuteListUpdateTime = now + SECONDS_BETWEEN_MUTE_UPDATES; - mCachedOwnerInMuteList = muted; - } - return muted; -} - -LLVOAvatar* LLViewerObject::asAvatar() -{ - return NULL; -} - -// If this object is directly or indirectly parented by an avatar, -// return it. Normally getAvatar() is the correct function to call; -// it will give the avatar used for skinning. The exception is with -// animated objects that are also attachments; in that case, -// getAvatar() will return the control avatar, used for skinning, and -// getAvatarAncestor will return the avatar to which the object is -// attached. -LLVOAvatar* LLViewerObject::getAvatarAncestor() -{ - LLViewerObject *pobj = (LLViewerObject*) getParent(); - while (pobj) - { - LLVOAvatar *av = pobj->asAvatar(); - if (av) - { - return av; - } - pobj = (LLViewerObject*) pobj->getParent(); - } - return NULL; -} - -bool LLViewerObject::isParticleSource() const -{ - return !mPartSourcep.isNull() && !mPartSourcep->isDead(); -} - -void LLViewerObject::setParticleSource(const LLPartSysData& particle_parameters, const LLUUID& owner_id) -{ - if (mPartSourcep) - { - deleteParticleSource(); - } - - LLPointer pss = LLViewerPartSourceScript::createPSS(this, particle_parameters); - mPartSourcep = pss; - - if (mPartSourcep) - { - mPartSourcep->setOwnerUUID(owner_id); - - if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID) - { - LLViewerTexture* image; - if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null) - { - image = LLViewerTextureManager::getFetchedTextureFromFile("pixiesmall.tga"); - } - else - { - image = LLViewerTextureManager::getFetchedTexture(mPartSourcep->mPartSysData.mPartImageID); - } - mPartSourcep->setImage(image); - } - } - LLViewerPartSim::getInstance()->addPartSource(pss); -} - -void LLViewerObject::unpackParticleSource(const S32 block_num, const LLUUID& owner_id) -{ - if (!mPartSourcep.isNull() && mPartSourcep->isDead()) - { - mPartSourcep = NULL; - } - if (mPartSourcep) - { - // If we've got one already, just update the existing source (or remove it) - if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, block_num)) - { - mPartSourcep->setDead(); - mPartSourcep = NULL; - } - } - else - { - LLPointer pss = LLViewerPartSourceScript::unpackPSS(this, NULL, block_num); - //If the owner is muted, don't create the system - if(LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagParticles)) return; - - // We need to be able to deal with a particle source that hasn't changed, but still got an update! - if (pss) - { -// LL_INFOS() << "Making particle system with owner " << owner_id << LL_ENDL; - pss->setOwnerUUID(owner_id); - mPartSourcep = pss; - LLViewerPartSim::getInstance()->addPartSource(pss); - } - } - if (mPartSourcep) - { - if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID) - { - LLViewerTexture* image; - if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null) - { - image = LLViewerTextureManager::getFetchedTextureFromFile("pixiesmall.j2c"); - } - else - { - image = LLViewerTextureManager::getFetchedTexture(mPartSourcep->mPartSysData.mPartImageID); - } - mPartSourcep->setImage(image); - } - } -} - -void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id, bool legacy) -{ - if (!mPartSourcep.isNull() && mPartSourcep->isDead()) - { - mPartSourcep = NULL; - } - if (mPartSourcep) - { - // If we've got one already, just update the existing source (or remove it) - if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, dp, legacy)) - { - mPartSourcep->setDead(); - mPartSourcep = NULL; - } - } - else - { - LLPointer pss = LLViewerPartSourceScript::unpackPSS(this, NULL, dp, legacy); - //If the owner is muted, don't create the system - if(LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagParticles)) return; - // We need to be able to deal with a particle source that hasn't changed, but still got an update! - if (pss) - { -// LL_INFOS() << "Making particle system with owner " << owner_id << LL_ENDL; - pss->setOwnerUUID(owner_id); - mPartSourcep = pss; - LLViewerPartSim::getInstance()->addPartSource(pss); - } - } - if (mPartSourcep) - { - if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID) - { - LLViewerTexture* image; - if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null) - { - image = LLViewerTextureManager::getFetchedTextureFromFile("pixiesmall.j2c"); - } - else - { - image = LLViewerTextureManager::getFetchedTexture(mPartSourcep->mPartSysData.mPartImageID); - } - mPartSourcep->setImage(image); - } - } -} - -void LLViewerObject::deleteParticleSource() -{ - if (mPartSourcep.notNull()) - { - mPartSourcep->setDead(); - mPartSourcep = NULL; - } -} - -// virtual -void LLViewerObject::updateDrawable(bool force_damped) -{ - if (!isChanged(MOVED)) - { //most common case, having an empty if case here makes for better branch prediction - } - else if (mDrawable.notNull() && - !mDrawable->isState(LLDrawable::ON_MOVE_LIST)) - { - bool damped_motion = - !isChanged(SHIFTED) && // not shifted between regions this frame and... - ( force_damped || // ...forced into damped motion by application logic or... - ( !isSelected() && // ...not selected and... - ( mDrawable->isRoot() || // ... is root or ... - (getParent() && !((LLViewerObject*)getParent())->isSelected())// ... parent is not selected and ... - ) && - getPCode() == LL_PCODE_VOLUME && // ...is a volume object and... - getVelocity().isExactlyZero() && // ...is not moving physically and... - mDrawable->getGeneration() != -1 // ...was not created this frame. - ) - ); - gPipeline.markMoved(mDrawable, damped_motion); - } - clearChanged(SHIFTED); -} - -// virtual, overridden by LLVOVolume -F32 LLViewerObject::getVObjRadius() const -{ - return mDrawable.notNull() ? mDrawable->getRadius() : 0.f; -} - -void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, const U8 flags) -{ - if (!gAudiop) - { - return; - } - - if (audio_uuid.isNull()) - { - if (!mAudioSourcep) - { - return; - } - if (mAudioSourcep->isLoop() && !mAudioSourcep->hasPendingPreloads()) - { - // We don't clear the sound if it's a loop, it'll go away on its own. - // At least, this appears to be how the scripts work. - // The attached sound ID is set to NULL to avoid it playing back when the - // object rezzes in on non-looping sounds. - //LL_INFOS() << "Clearing attached sound " << mAudioSourcep->getCurrentData()->getID() << LL_ENDL; - gAudiop->cleanupAudioSource(mAudioSourcep); - mAudioSourcep = NULL; - } - else if (flags & LL_SOUND_FLAG_STOP) - { - // Just shut off the sound - mAudioSourcep->stop(); - } - return; - } - if (flags & LL_SOUND_FLAG_LOOP - && mAudioSourcep && mAudioSourcep->isLoop() && mAudioSourcep->getCurrentData() - && mAudioSourcep->getCurrentData()->getID() == audio_uuid) - { - //LL_INFOS() << "Already playing this sound on a loop, ignoring" << LL_ENDL; - return; - } - - // don't clean up before previous sound is done. Solves: SL-33486 - if ( mAudioSourcep && mAudioSourcep->isDone() ) - { - gAudiop->cleanupAudioSource(mAudioSourcep); - mAudioSourcep = NULL; - } - - if (mAudioSourcep && mAudioSourcep->isMuted() && - mAudioSourcep->getCurrentData() && mAudioSourcep->getCurrentData()->getID() == audio_uuid) - { - //LL_INFOS() << "Already having this sound as muted sound, ignoring" << LL_ENDL; - return; - } - - getAudioSource(owner_id); - - if (mAudioSourcep) - { - bool queue = flags & LL_SOUND_FLAG_QUEUE; - mAudioGain = gain; - mAudioSourcep->setGain(gain); - mAudioSourcep->setLoop(flags & LL_SOUND_FLAG_LOOP); - mAudioSourcep->setSyncMaster(flags & LL_SOUND_FLAG_SYNC_MASTER); - mAudioSourcep->setSyncSlave(flags & LL_SOUND_FLAG_SYNC_SLAVE); - mAudioSourcep->setQueueSounds(queue); - if(!queue) // stop any current sound first to avoid "farts of doom" (SL-1541) -MG - { - mAudioSourcep->stop(); - } - - // Play this sound if region maturity permits - if( gAgent.canAccessMaturityAtGlobal(this->getPositionGlobal()) ) - { - //LL_INFOS() << "Playing attached sound " << audio_uuid << LL_ENDL; - // recheck cutoff radius in case this update was an object-update with new value - mAudioSourcep->checkCutOffRadius(); - mAudioSourcep->play(audio_uuid); - } - } -} - -LLAudioSource *LLViewerObject::getAudioSource(const LLUUID& owner_id) -{ - if (!mAudioSourcep) - { - // Arbitrary low gain for a sound that's not playing. - // This is used for sound preloads, for example. - LLAudioSourceVO *asvop = new LLAudioSourceVO(mID, owner_id, 0.01f, this); - - mAudioSourcep = asvop; - if(gAudiop) - { - gAudiop->addAudioSource(asvop); - } - } - - return mAudioSourcep; -} - -void LLViewerObject::adjustAudioGain(const F32 gain) -{ - if (mAudioSourcep) - { - mAudioGain = gain; - mAudioSourcep->setGain(mAudioGain); - } -} - -//---------------------------------------------------------------------------- - -bool LLViewerObject::unpackParameterEntry(U16 param_type, LLDataPacker *dp) -{ - if (LLNetworkData::PARAMS_MESH == param_type) - { - param_type = LLNetworkData::PARAMS_SCULPT; - } - ExtraParameter* param = getExtraParameterEntryCreate(param_type); - if (param) - { - param->data->unpack(*dp); - param->in_use = true; - parameterChanged(param_type, param->data, true, false); - return true; - } - else - { - return false; - } -} - -LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 param_type) -{ - LLNetworkData* new_block = NULL; - switch (param_type) - { - case LLNetworkData::PARAMS_FLEXIBLE: - { - new_block = new LLFlexibleObjectData(); - break; - } - case LLNetworkData::PARAMS_LIGHT: - { - new_block = new LLLightParams(); - break; - } - case LLNetworkData::PARAMS_SCULPT: - { - new_block = new LLSculptParams(); - break; - } - case LLNetworkData::PARAMS_LIGHT_IMAGE: - { - new_block = new LLLightImageParams(); - break; - } - case LLNetworkData::PARAMS_EXTENDED_MESH: - { - new_block = new LLExtendedMeshParams(); - break; - } - case LLNetworkData::PARAMS_RENDER_MATERIAL: - { - new_block = new LLRenderMaterialParams(); - break; - } - case LLNetworkData::PARAMS_REFLECTION_PROBE: - { - new_block = new LLReflectionProbeParams(); - break; - } - default: - { - LL_INFOS_ONCE() << "Unknown param type: " << param_type << LL_ENDL; - break; - } - }; - - if (new_block) - { - ExtraParameter* new_entry = new ExtraParameter; - new_entry->data = new_block; - new_entry->in_use = false; // not in use yet - llassert(mExtraParameterList[param_type] == nullptr); // leak -- redundantly allocated parameter entry - mExtraParameterList[param_type] = new_entry; - return new_entry; - } - return NULL; -} - -LLViewerObject::ExtraParameter* LLViewerObject::getExtraParameterEntry(U16 param_type) const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_VIEWER; - std::unordered_map::const_iterator itor = mExtraParameterList.find(param_type); - if (itor != mExtraParameterList.end()) - { - return itor->second; - } - return NULL; -} - -LLViewerObject::ExtraParameter* LLViewerObject::getExtraParameterEntryCreate(U16 param_type) -{ - ExtraParameter* param = getExtraParameterEntry(param_type); - if (!param) - { - param = createNewParameterEntry(param_type); - } - return param; -} - -LLNetworkData* LLViewerObject::getParameterEntry(U16 param_type) const -{ - ExtraParameter* param = getExtraParameterEntry(param_type); - if (param) - { - return param->data; - } - else - { - return NULL; - } -} - -bool LLViewerObject::getParameterEntryInUse(U16 param_type) const -{ - ExtraParameter* param = getExtraParameterEntry(param_type); - if (param) - { - return param->in_use; - } - else - { - return false; - } -} - -bool LLViewerObject::setParameterEntry(U16 param_type, const LLNetworkData& new_value, bool local_origin) -{ - ExtraParameter* param = getExtraParameterEntryCreate(param_type); - if (param) - { - if (param->in_use && new_value == *(param->data)) - { - return false; - } - param->in_use = true; - param->data->copy(new_value); - parameterChanged(param_type, param->data, true, local_origin); - return true; - } - else - { - return false; - } -} - -// Assumed to be called locally -// If in_use is true, will crate a new extra parameter if none exists. -// Should always return true. -bool LLViewerObject::setParameterEntryInUse(U16 param_type, bool in_use, bool local_origin) -{ - ExtraParameter* param = getExtraParameterEntryCreate(param_type); - if (param && param->in_use != in_use) - { - param->in_use = in_use; - parameterChanged(param_type, param->data, in_use, local_origin); - return true; - } - return false; -} - -void LLViewerObject::parameterChanged(U16 param_type, bool local_origin) -{ - ExtraParameter* param = getExtraParameterEntry(param_type); - if (param) - { - parameterChanged(param_type, param->data, param->in_use, local_origin); - } -} - -void LLViewerObject::parameterChanged(U16 param_type, LLNetworkData* data, bool in_use, bool local_origin) -{ - if (local_origin) - { - // *NOTE: Do not send the render material ID in this way as it will get - // out-of-sync with other sent client data. - // See LLViewerObject::setRenderMaterialID and LLGLTFMaterialList - llassert(param_type != LLNetworkData::PARAMS_RENDER_MATERIAL); - - LLViewerRegion* regionp = getRegion(); - if(!regionp) return; - - // Change happened on the viewer. Send the change up - U8 tmp[MAX_OBJECT_PARAMS_SIZE]; - LLDataPackerBinaryBuffer dpb(tmp, MAX_OBJECT_PARAMS_SIZE); - if (data->pack(dpb)) - { - U32 datasize = (U32)dpb.getCurrentSize(); - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ObjectExtraParams); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ObjectData); - msg->addU32Fast(_PREHASH_ObjectLocalID, mLocalID ); - - msg->addU16Fast(_PREHASH_ParamType, param_type); - msg->addBOOLFast(_PREHASH_ParamInUse, in_use); - - msg->addU32Fast(_PREHASH_ParamSize, datasize); - msg->addBinaryDataFast(_PREHASH_ParamData, tmp, datasize); - - msg->sendReliable( regionp->getHost() ); - } - else - { - LL_WARNS() << "Failed to send object extra parameters: " << param_type << LL_ENDL; - } - } - else - { - if (param_type == LLNetworkData::PARAMS_RENDER_MATERIAL) - { - const LLRenderMaterialParams* params = in_use ? (LLRenderMaterialParams*)getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL) : nullptr; - setRenderMaterialIDs(params, local_origin); - } - } -} - -void LLViewerObject::setDrawableState(U32 state, bool recursive) -{ - if (mDrawable) - { - mDrawable->setState(state); - } - if (recursive) - { - for (child_list_t::iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - child->setDrawableState(state, recursive); - } - } -} - -void LLViewerObject::clearDrawableState(U32 state, bool recursive) -{ - if (mDrawable) - { - mDrawable->clearState(state); - } - if (recursive) - { - for (child_list_t::iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - child->clearDrawableState(state, recursive); - } - } -} - -bool LLViewerObject::isDrawableState(U32 state, bool recursive) const -{ - bool matches = false; - if (mDrawable) - { - matches = mDrawable->isState(state); - } - if (recursive) - { - for (child_list_t::const_iterator iter = mChildList.begin(); - (iter != mChildList.end()) && matches; iter++) - { - LLViewerObject* child = *iter; - matches &= child->isDrawableState(state, recursive); - } - } - - return matches; -} - - - -//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// RN: these functions assume a 2-level hierarchy -//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -// Owned by anyone? -bool LLViewerObject::permAnyOwner() const -{ - if (isRootEdit()) - { - return flagObjectAnyOwner(); - } - else - { - return ((LLViewerObject*)getParent())->permAnyOwner(); - } -} -// Owned by this viewer? -bool LLViewerObject::permYouOwner() const -{ - if (isRootEdit()) - { -#ifdef HACKED_GODLIKE_VIEWER - return true; -#else -# ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLGridManager::getInstance()->isInProductionGrid() - && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) - { - return true; - } -# endif - return flagObjectYouOwner(); -#endif - } - else - { - return ((LLViewerObject*)getParent())->permYouOwner(); - } -} - -// Owned by a group? -bool LLViewerObject::permGroupOwner() const -{ - if (isRootEdit()) - { - return flagObjectGroupOwned(); - } - else - { - return ((LLViewerObject*)getParent())->permGroupOwner(); - } -} - -// Can the owner edit -bool LLViewerObject::permOwnerModify() const -{ - if (isRootEdit()) - { -#ifdef HACKED_GODLIKE_VIEWER - return true; -#else -# ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLGridManager::getInstance()->isInProductionGrid() - && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) - { - return true; - } -# endif - return flagObjectOwnerModify(); -#endif - } - else - { - return ((LLViewerObject*)getParent())->permOwnerModify(); - } -} - -// Can edit -bool LLViewerObject::permModify() const -{ - if (isRootEdit()) - { -#ifdef HACKED_GODLIKE_VIEWER - return true; -#else -# ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLGridManager::getInstance()->isInProductionGrid() - && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) - { - return true; - } -# endif - return flagObjectModify(); -#endif - } - else - { - return ((LLViewerObject*)getParent())->permModify(); - } -} - -// Can copy -bool LLViewerObject::permCopy() const -{ - if (isRootEdit()) - { -#ifdef HACKED_GODLIKE_VIEWER - return true; -#else -# ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLGridManager::getInstance()->isInProductionGrid() - && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) - { - return true; - } -# endif - return flagObjectCopy(); -#endif - } - else - { - return ((LLViewerObject*)getParent())->permCopy(); - } -} - -// Can move -bool LLViewerObject::permMove() const -{ - if (isRootEdit()) - { -#ifdef HACKED_GODLIKE_VIEWER - return true; -#else -# ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLGridManager::getInstance()->isInProductionGrid() - && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) - { - return true; - } -# endif - return flagObjectMove(); -#endif - } - else - { - return ((LLViewerObject*)getParent())->permMove(); - } -} - -// Can be transferred -bool LLViewerObject::permTransfer() const -{ - if (isRootEdit()) - { -#ifdef HACKED_GODLIKE_VIEWER - return true; -#else -# ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLGridManager::getInstance()->isInProductionGrid() - && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) - { - return true; - } -# endif - return flagObjectTransfer(); -#endif - } - else - { - return ((LLViewerObject*)getParent())->permTransfer(); - } -} - -// Can only open objects that you own, or that someone has -// given you modify rights to. JC -bool LLViewerObject::allowOpen() const -{ - return !flagInventoryEmpty() && (permYouOwner() || permModify()); -} - -LLViewerObject::LLInventoryCallbackInfo::~LLInventoryCallbackInfo() -{ - if (mListener) - { - mListener->clearVOInventoryListener(); - } -} - -void LLViewerObject::updateVolume(const LLVolumeParams& volume_params) -{ - if (setVolume(volume_params, 1)) // *FIX: magic number, ack! - { - // Transmit the update to the simulator - sendShapeUpdate(); - markForUpdate(); - } -} - -void LLViewerObject::recursiveMarkForUpdate() -{ - for (LLViewerObject::child_list_t::iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* child = *iter; - child->markForUpdate(); - } - markForUpdate(); -} - -void LLViewerObject::markForUpdate() -{ - if (mDrawable.notNull()) - { - gPipeline.markTextured(mDrawable); - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY); - } -} - -bool LLViewerObject::isPermanentEnforced() const -{ - return flagObjectPermanent() && (mRegionp != gAgent.getRegion()) && !gAgent.isGodlike(); -} - -bool LLViewerObject::getIncludeInSearch() const -{ - return flagIncludeInSearch(); -} - -void LLViewerObject::setIncludeInSearch(bool include_in_search) -{ - setFlags(FLAGS_INCLUDE_IN_SEARCH, include_in_search); -} - -void LLViewerObject::setRegion(LLViewerRegion *regionp) -{ - if (!regionp) - { - LL_WARNS() << "viewer object set region to NULL" << LL_ENDL; - } - if(regionp != mRegionp) - { - if(mRegionp) - { - mRegionp->removeFromCreatedList(getLocalID()); - } - if(regionp) - { - regionp->addToCreatedList(getLocalID()); - } - } - - mLatestRecvPacketID = 0; - mRegionp = regionp; - - for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i) - { - LLViewerObject* child = *i; - child->setRegion(regionp); - } - - if (mControlAvatar) - { - mControlAvatar->setRegion(regionp); - } - - setChanged(MOVED | SILHOUETTE); - updateDrawable(false); -} - -// virtual -void LLViewerObject::updateRegion(LLViewerRegion *regionp) -{ -// if (regionp) -// { -// F64 now = LLFrameTimer::getElapsedSeconds(); -// LL_INFOS() << "Updating to region " << regionp->getName() -// << ", ms since last update message: " << (F32)((now - mLastMessageUpdateSecs) * 1000.0) -// << ", ms since last interpolation: " << (F32)((now - mLastInterpUpdateSecs) * 1000.0) -// << LL_ENDL; -// } -} - - -bool LLViewerObject::specialHoverCursor() const -{ - return flagUsePhysics() - || flagHandleTouch() - || (mClickAction != 0); -} - -void LLViewerObject::updateFlags(bool physics_changed) -{ - LLViewerRegion* regionp = getRegion(); - if(!regionp) return; - gMessageSystem->newMessage("ObjectFlagUpdate"); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, getLocalID() ); - gMessageSystem->addBOOLFast(_PREHASH_UsePhysics, flagUsePhysics() ); - gMessageSystem->addBOOL("IsTemporary", flagTemporaryOnRez() ); - gMessageSystem->addBOOL("IsPhantom", flagPhantom() ); - - // stinson 02/28/2012 : This CastsShadows bool is no longer used in either the viewer or the simulator - // The simulator code does not even unpack this value when the message is received. - // This could be potentially hijacked in the future for another use should the urgent need arise. - gMessageSystem->addBOOL("CastsShadows", false ); - - if (physics_changed) - { - gMessageSystem->nextBlock("ExtraPhysics"); - gMessageSystem->addU8("PhysicsShapeType", getPhysicsShapeType() ); - gMessageSystem->addF32("Density", getPhysicsDensity() ); - gMessageSystem->addF32("Friction", getPhysicsFriction() ); - gMessageSystem->addF32("Restitution", getPhysicsRestitution() ); - gMessageSystem->addF32("GravityMultiplier", getPhysicsGravity() ); - } - gMessageSystem->sendReliable( regionp->getHost() ); -} - -bool LLViewerObject::setFlags(U32 flags, bool state) -{ - bool setit = setFlagsWithoutUpdate(flags, state); - - // BUG: Sometimes viewer physics and simulator physics get - // out of sync. To fix this, always send update to simulator. -// if (setit) - { - updateFlags(); - } - return setit; -} - -bool LLViewerObject::setFlagsWithoutUpdate(U32 flags, bool state) -{ - bool setit = false; - if (state) - { - if ((mFlags & flags) != flags) - { - mFlags |= flags; - setit = true; - } - } - else - { - if ((mFlags & flags) != 0) - { - mFlags &= ~flags; - setit = true; - } - } - return setit; -} - -void LLViewerObject::setPhysicsShapeType(U8 type) -{ - mPhysicsShapeUnknown = false; - if (type != mPhysicsShapeType) - { - mPhysicsShapeType = type; - setObjectCostStale(); -} -} - -void LLViewerObject::setPhysicsGravity(F32 gravity) -{ - mPhysicsGravity = gravity; -} - -void LLViewerObject::setPhysicsFriction(F32 friction) -{ - mPhysicsFriction = friction; -} - -void LLViewerObject::setPhysicsDensity(F32 density) -{ - mPhysicsDensity = density; -} - -void LLViewerObject::setPhysicsRestitution(F32 restitution) -{ - mPhysicsRestitution = restitution; -} - -U8 LLViewerObject::getPhysicsShapeType() const -{ - if (mPhysicsShapeUnknown) - { - gObjectList.updatePhysicsFlags(this); - } - - return mPhysicsShapeType; -} - -void LLViewerObject::applyAngularVelocity(F32 dt) -{ - //do target omega here - mRotTime += dt; - LLVector3 ang_vel = getAngularVelocity(); - F32 omega = ang_vel.magVecSquared(); - F32 angle = 0.0f; - LLQuaternion dQ; - if (omega > 0.00001f) - { - omega = sqrt(omega); - angle = omega * dt; - - ang_vel *= 1.f/omega; - - // calculate the delta increment based on the object's angular velocity - dQ.setQuat(angle, ang_vel); - - // accumulate the angular velocity rotations to re-apply in the case of an object update - mAngularVelocityRot *= dQ; - - // Just apply the delta increment to the current rotation - setRotation(getRotation()*dQ); - setChanged(MOVED | SILHOUETTE); - } -} - -void LLViewerObject::resetRotTime() -{ - mRotTime = 0.0f; -} - -void LLViewerObject::resetRot() -{ - resetRotTime(); - - // Reset the accumulated angular velocity rotation - mAngularVelocityRot.loadIdentity(); -} - -U32 LLViewerObject::getPartitionType() const -{ - return LLViewerRegion::PARTITION_NONE; -} - -void LLViewerObject::dirtySpatialGroup() const -{ - if (mDrawable) - { - LLSpatialGroup* group = mDrawable->getSpatialGroup(); - if (group) - { - group->dirtyGeom(); - gPipeline.markRebuild(group); - } - } -} - -void LLViewerObject::dirtyMesh() -{ - if (mDrawable) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL); - } -} - -F32 LLAlphaObject::getPartSize(S32 idx) -{ - return 0.f; -} - -void LLAlphaObject::getBlendFunc(S32 face, LLRender::eBlendFactor& src, LLRender::eBlendFactor& dst) -{ - -} - -// virtual -void LLStaticViewerObject::updateDrawable(bool force_damped) -{ - // Force an immediate rebuild on any update - if (mDrawable.notNull()) - { - mDrawable->updateXform(true); - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL); - } - clearChanged(SHIFTED); -} - -void LLViewerObject::saveUnselectedChildrenPosition(std::vector& positions) -{ - if(mChildList.empty() || !positions.empty()) - { - return ; - } - - for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* childp = *iter; - if (!childp->isSelected() && childp->mDrawable.notNull()) - { - positions.push_back(childp->getPositionEdit()); - } - } - - return ; -} - -void LLViewerObject::saveUnselectedChildrenRotation(std::vector& rotations) -{ - if(mChildList.empty()) - { - return ; - } - - for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* childp = *iter; - if (!childp->isSelected() && childp->mDrawable.notNull()) - { - rotations.push_back(childp->getRotationEdit()); - } - } - - return ; -} - -//counter-rotation -void LLViewerObject::resetChildrenRotationAndPosition(const std::vector& rotations, - const std::vector& positions) -{ - if(mChildList.empty()) - { - return ; - } - - S32 index = 0 ; - LLQuaternion inv_rotation = ~getRotationEdit() ; - LLVector3 offset = getPositionEdit() ; - for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* childp = *iter; - if (!childp->isSelected() && childp->mDrawable.notNull()) - { - if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR) - { - childp->setRotation(rotations[index] * inv_rotation); - childp->setPosition((positions[index] - offset) * inv_rotation); - LLManip::rebuild(childp); - } - else //avatar - { - LLVector3 reset_pos = (positions[index] - offset) * inv_rotation ; - LLQuaternion reset_rot = rotations[index] * inv_rotation ; - - ((LLVOAvatar*)childp)->mDrawable->mXform.setPosition(reset_pos); - ((LLVOAvatar*)childp)->mDrawable->mXform.setRotation(reset_rot) ; - - ((LLVOAvatar*)childp)->mDrawable->getVObj()->setPosition(reset_pos, true); - ((LLVOAvatar*)childp)->mDrawable->getVObj()->setRotation(reset_rot, true) ; - - LLManip::rebuild(childp); - } - index++; - } - } - - return ; -} - -//counter-translation -void LLViewerObject::resetChildrenPosition(const LLVector3& offset, bool simplified, bool skip_avatar_child) -{ - if(mChildList.empty()) - { - return ; - } - - LLVector3 child_offset; - if(simplified) //translation only, rotation matrix does not change - { - child_offset = offset * ~getRotation(); - } - else //rotation matrix might change too. - { - if (isAttachment() && mDrawable.notNull()) - { - LLXform* attachment_point_xform = mDrawable->getXform()->getParent(); - LLQuaternion parent_rotation = getRotation() * attachment_point_xform->getWorldRotation(); - child_offset = offset * ~parent_rotation; - } - else - { - child_offset = offset * ~getRenderRotation(); - } - } - - for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); - iter != mChildList.end(); iter++) - { - LLViewerObject* childp = *iter; - - if (!childp->isSelected() && childp->mDrawable.notNull()) - { - if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR) - { - childp->setPosition(childp->getPosition() + child_offset); - LLManip::rebuild(childp); - } - else //avatar - { - if(!skip_avatar_child) - { - LLVector3 reset_pos = ((LLVOAvatar*)childp)->mDrawable->mXform.getPosition() + child_offset ; - - ((LLVOAvatar*)childp)->mDrawable->mXform.setPosition(reset_pos); - ((LLVOAvatar*)childp)->mDrawable->getVObj()->setPosition(reset_pos); - LLManip::rebuild(childp); - } - } - } - } - - return ; -} - -// virtual -bool LLViewerObject::isTempAttachment() const -{ - return (mID.notNull() && (mID == mAttachmentItemID)); -} - -bool LLViewerObject::isHiglightedOrBeacon() const -{ - if (LLFloaterReg::instanceVisible("beacons") && (gPipeline.getRenderBeacons() || gPipeline.getRenderHighlights())) - { - bool has_media = (getMediaType() == LLViewerObject::MEDIA_SET); - bool is_scripted = !isAvatar() && !getParent() && flagScripted(); - bool is_physical = !isAvatar() && flagUsePhysics(); - - return (isParticleSource() && gPipeline.getRenderParticleBeacons()) - || (isAudioSource() && gPipeline.getRenderSoundBeacons()) - || (has_media && gPipeline.getRenderMOAPBeacons()) - || (is_scripted && gPipeline.getRenderScriptedBeacons()) - || (is_scripted && flagHandleTouch() && gPipeline.getRenderScriptedTouchBeacons()) - || (is_physical && gPipeline.getRenderPhysicalBeacons()); - } - return false; -} - - -const LLUUID &LLViewerObject::getAttachmentItemID() const -{ - return mAttachmentItemID; -} - -void LLViewerObject::setAttachmentItemID(const LLUUID &id) -{ - mAttachmentItemID = id; -} - -EObjectUpdateType LLViewerObject::getLastUpdateType() const -{ - return mLastUpdateType; -} - -void LLViewerObject::setLastUpdateType(EObjectUpdateType last_update_type) -{ - mLastUpdateType = last_update_type; -} - -bool LLViewerObject::getLastUpdateCached() const -{ - return mLastUpdateCached; -} - -void LLViewerObject::setLastUpdateCached(bool last_update_cached) -{ - mLastUpdateCached = last_update_cached; -} - -const LLUUID &LLViewerObject::extractAttachmentItemID() -{ - LLUUID item_id = LLUUID::null; - LLNameValue* item_id_nv = getNVPair("AttachItemID"); - if( item_id_nv ) - { - const char* s = item_id_nv->getString(); - if( s ) - { - item_id.set(s); - } - } - setAttachmentItemID(item_id); - return getAttachmentItemID(); -} - -const std::string& LLViewerObject::getAttachmentItemName() const -{ - static std::string empty; - LLInventoryItem *item = gInventory.getItem(getAttachmentItemID()); - if (isAttachment() && item) - { - return item->getName(); - } - return empty; -} - -//virtual -LLVOAvatar* LLViewerObject::getAvatar() const -{ - if (getControlAvatar()) - { - return getControlAvatar(); - } - if (isAttachment()) - { - LLViewerObject* vobj = (LLViewerObject*) getParent(); - - while (vobj && !vobj->asAvatar()) - { - vobj = (LLViewerObject*) vobj->getParent(); - } - - return (LLVOAvatar*) vobj; - } - - return NULL; -} - -bool LLViewerObject::hasRenderMaterialParams() const -{ - return getParameterEntryInUse(LLNetworkData::PARAMS_RENDER_MATERIAL); -} - -void LLViewerObject::setHasRenderMaterialParams(bool has_materials) -{ - bool had_materials = hasRenderMaterialParams(); - - if (had_materials != has_materials) - { - if (has_materials) - { - setParameterEntryInUse(LLNetworkData::PARAMS_RENDER_MATERIAL, true, true); - } - else - { - setParameterEntryInUse(LLNetworkData::PARAMS_RENDER_MATERIAL, false, true); - } - } -} - -const LLUUID& LLViewerObject::getRenderMaterialID(U8 te) const -{ - LLRenderMaterialParams* param_block = (LLRenderMaterialParams*)getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL); - if (param_block) - { - return param_block->getMaterial(te); - } - - return LLUUID::null; -} - -void LLViewerObject::rebuildMaterial() -{ - llassert(!isDead()); - - faceMappingChanged(); - gPipeline.markTextured(mDrawable); -} - -void LLViewerObject::setRenderMaterialID(S32 te_in, const LLUUID& id, bool update_server, bool local_origin) -{ - // implementation is delicate - - // if update is bound for server, should always null out GLTFRenderMaterial and clear GLTFMaterialOverride even if ids haven't changed - // (the case where ids haven't changed indicates the user has reapplied the original material, in which case overrides should be dropped) - // otherwise, should only null out the render material where ids or overrides have changed - // (the case where ids have changed but overrides are still present is from unsynchronized updates from the simulator, or synchronized - // updates with solely transform overrides) - - llassert(!update_server || local_origin); - - S32 start_idx = 0; - S32 end_idx = getNumTEs(); - - if (te_in != -1) - { - start_idx = te_in; - end_idx = start_idx + 1; - } - - start_idx = llmax(start_idx, 0); - end_idx = llmin(end_idx, (S32) getNumTEs()); - - LLRenderMaterialParams* param_block = (LLRenderMaterialParams*)getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL); - if (!param_block && id.notNull()) - { // block doesn't exist, but it will need to - param_block = (LLRenderMaterialParams*)createNewParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL)->data; - } - - - LLFetchedGLTFMaterial* new_material = nullptr; - if (id.notNull()) - { - new_material = gGLTFMaterialList.getMaterial(id); - } - - // update local state - for (S32 te = start_idx; te < end_idx; ++te) - { - LLTextureEntry* tep = getTE(te); - - // If local_origin=false (i.e. it's from the server), we know the - // material has updated or been created, because extra params are - // checked for equality on unpacking. In that case, checking the - // material ID for inequality won't work, because the material ID has - // already been set. - bool material_changed = !local_origin || !param_block || id != param_block->getMaterial(te); - - if (update_server) - { - // Clear most overrides so the render material better matches the material - // ID (preserve transforms). If overrides become passthrough, set the overrides - // to nullptr. - if (tep->setBaseMaterial()) - { - material_changed = true; - } - } - - if (update_server || material_changed) - { - tep->setGLTFRenderMaterial(nullptr); - } - - if (new_material != tep->getGLTFMaterial()) - { - tep->setGLTFMaterial(new_material, !update_server); - } - - if (material_changed && new_material) - { - // Sometimes, the material may change out from underneath the overrides. - // This is usually due to the server sending a new material ID, but - // the overrides have not changed due to being only texture - // transforms. Re-apply the overrides to the render material here, - // if present. - const LLGLTFMaterial* override_material = tep->getGLTFMaterialOverride(); - if (override_material) - { - new_material->onMaterialComplete([obj_id = getID(), te]() - { - LLViewerObject* obj = gObjectList.findObject(obj_id); - if (!obj) { return; } - LLTextureEntry* tep = obj->getTE(te); - if (!tep) { return; } - const LLGLTFMaterial* new_material = tep->getGLTFMaterial(); - if (!new_material) { return; } - const LLGLTFMaterial* override_material = tep->getGLTFMaterialOverride(); - if (!override_material) { return; } - LLGLTFMaterial* render_material = new LLFetchedGLTFMaterial(); - *render_material = *new_material; - render_material->applyOverride(*override_material); - tep->setGLTFRenderMaterial(render_material); - }); - } - } - } - - // signal to render pipe that render batches must be rebuilt for this object - if (!new_material) - { - rebuildMaterial(); - } - else - { - new_material->onMaterialComplete([obj_id = getID()]() - { - LLViewerObject* obj = gObjectList.findObject(obj_id); - if (obj) - { - obj->rebuildMaterial(); - } - }); - } - - // predictively update LLRenderMaterialParams (don't wait for server) - if (param_block) - { // update existing parameter block - for (S32 te = start_idx; te < end_idx; ++te) - { - param_block->setMaterial(te, id); - } - } - - if (update_server) - { - // update via ModifyMaterialParams cap (server will echo back changes) - for (S32 te = start_idx; te < end_idx; ++te) - { - // This sends a cleared version of this object's current material - // override, but the override should already be cleared due to - // calling setBaseMaterial above. - LLGLTFMaterialList::queueApply(this, te, id); - } - } - - if (!update_server) - { - // Land impact may have changed - setObjectCostStale(); - } -} - -void LLViewerObject::setRenderMaterialIDs(const LLUUID& id) -{ - setRenderMaterialID(-1, id); -} - -void LLViewerObject::setRenderMaterialIDs(const LLRenderMaterialParams* material_params, bool local_origin) -{ - if (!local_origin) - { - for (S32 te = 0; te < getNumTEs(); ++te) - { - const LLUUID& id = material_params ? material_params->getMaterial(te) : LLUUID::null; - // We know material_params has updated or been created, because - // extra params are checked for equality on unpacking. - setRenderMaterialID(te, id, false, false); - } - } -} - -void LLViewerObject::shrinkWrap() -{ - if (!mShouldShrinkWrap) - { - mShouldShrinkWrap = true; - if (mDrawable) - { // we weren't shrink wrapped before but we are now, update the spatial partition - gPipeline.markPartitionMove(mDrawable); - } - } -} - -class ObjectPhysicsProperties : public LLHTTPNode -{ -public: - virtual void post( - ResponsePtr responder, - const LLSD& context, - const LLSD& input) const - { - LLSD object_data = input["body"]["ObjectData"]; - S32 num_entries = object_data.size(); - - for ( S32 i = 0; i < num_entries; i++ ) - { - LLSD& curr_object_data = object_data[i]; - U32 local_id = curr_object_data["LocalID"].asInteger(); - - // Iterate through nodes at end, since it can be on both the regular AND hover list - struct f : public LLSelectedNodeFunctor - { - U32 mID; - f(const U32& id) : mID(id) {} - virtual bool apply(LLSelectNode* node) - { - return (node->getObject() && node->getObject()->mLocalID == mID ); - } - } func(local_id); - - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(&func); - - if (node) - { - // The LLSD message builder doesn't know how to handle U8, so we need to send as S8 and cast - U8 type = (U8)curr_object_data["PhysicsShapeType"].asInteger(); - F32 density = (F32)curr_object_data["Density"].asReal(); - F32 friction = (F32)curr_object_data["Friction"].asReal(); - F32 restitution = (F32)curr_object_data["Restitution"].asReal(); - F32 gravity = (F32)curr_object_data["GravityMultiplier"].asReal(); - - node->getObject()->setPhysicsShapeType(type); - node->getObject()->setPhysicsGravity(gravity); - node->getObject()->setPhysicsFriction(friction); - node->getObject()->setPhysicsDensity(density); - node->getObject()->setPhysicsRestitution(restitution); - } - } - - dialog_refresh_all(); - }; -}; - -LLHTTPRegistration - gHTTPRegistrationObjectPhysicsProperties("/message/ObjectPhysicsProperties"); - +/** + * @file llviewerobject.cpp + * @brief Base class for viewer objects + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewerobject.h" + +#include "llaudioengine.h" +#include "indra_constants.h" +#include "llmath.h" +#include "llflexibleobject.h" +#include "llviewercontrol.h" +#include "lldatapacker.h" +#include "llfasttimer.h" +#include "llfloaterreg.h" +#include "llfontgl.h" +#include "llframetimer.h" +#include "llhudicon.h" +#include "llinventory.h" +#include "llinventorydefines.h" +#include "llmaterialtable.h" +#include "llmutelist.h" +#include "llnamevalue.h" +#include "llprimitive.h" +#include "llquantize.h" +#include "llregionhandle.h" +#include "llsdserialize.h" +#include "lltree_common.h" +#include "llxfermanager.h" +#include "message.h" +#include "object_flags.h" + +#include "llaudiosourcevo.h" +#include "llagent.h" +#include "llagentcamera.h" +#include "llagentwearables.h" +#include "llbbox.h" +#include "llbox.h" +#include "llcylinder.h" +#include "llcontrolavatar.h" +#include "lldrawable.h" +#include "llface.h" +#include "llfloatertools.h" +#include "llfollowcam.h" +#include "llhudtext.h" +#include "llselectmgr.h" +#include "llrendersphere.h" +#include "lltooldraganddrop.h" +#include "lluiavatar.h" +#include "llviewercamera.h" +#include "llviewertexturelist.h" +#include "llviewerinventory.h" +#include "llviewerobjectlist.h" +#include "llviewerparceloverlay.h" +#include "llviewerpartsource.h" +#include "llviewerregion.h" +#include "llviewerstats.h" +#include "llviewertextureanim.h" +#include "llviewerwindow.h" // For getSpinAxis +#include "llvoavatar.h" +#include "llvoavatarself.h" +#include "llvograss.h" +#include "llvosky.h" +#include "llvolume.h" +#include "llvolumemessage.h" +#include "llvopartgroup.h" +#include "llvosurfacepatch.h" +#include "llvotree.h" +#include "llvovolume.h" +#include "llvowater.h" +#include "llworld.h" +#include "llui.h" +#include "pipeline.h" +#include "llviewernetwork.h" +#include "llvowlsky.h" +#include "llmanip.h" +#include "lltrans.h" +#include "llsdutil.h" +#include "llmediaentry.h" +#include "llfloaterperms.h" +#include "llvocache.h" +#include "llcleanup.h" +#include "llcallstack.h" +#include "llmeshrepository.h" +#include "llgltfmateriallist.h" +#include "llgl.h" + +//#define DEBUG_UPDATE_TYPE + +bool LLViewerObject::sVelocityInterpolate = true; +bool LLViewerObject::sPingInterpolate = true; + +U32 LLViewerObject::sNumZombieObjects = 0; +S32 LLViewerObject::sNumObjects = 0; +bool LLViewerObject::sMapDebug = true; +LLColor4 LLViewerObject::sEditSelectColor( 1.0f, 1.f, 0.f, 0.3f); // Edit OK +LLColor4 LLViewerObject::sNoEditSelectColor( 1.0f, 0.f, 0.f, 0.3f); // Can't edit +S32 LLViewerObject::sAxisArrowLength(50); + + +bool LLViewerObject::sPulseEnabled(false); +bool LLViewerObject::sUseSharedDrawables(false); // true + +// sMaxUpdateInterpolationTime must be greater than sPhaseOutUpdateInterpolationTime +F64Seconds LLViewerObject::sMaxUpdateInterpolationTime(3.0); // For motion interpolation: after X seconds with no updates, don't predict object motion +F64Seconds LLViewerObject::sPhaseOutUpdateInterpolationTime(2.0); // For motion interpolation: after Y seconds with no updates, taper off motion prediction +F64Seconds LLViewerObject::sMaxRegionCrossingInterpolationTime(1.0);// For motion interpolation: don't interpolate over this time on region crossing + +std::map LLViewerObject::sObjectDataMap; + +// The maximum size of an object extra parameters binary (packed) block +#define MAX_OBJECT_PARAMS_SIZE 1024 + +// At 45 Hz collisions seem stable and objects seem +// to settle down at a reasonable rate. +// JC 3/18/2003 + +const F32 PHYSICS_TIMESTEP = 1.f / 45.f; +const U32 MAX_INV_FILE_READ_FAILS = 25; +const S32 MAX_OBJECT_BINARY_DATA_SIZE = 60 + 16; + +const F64 INVENTORY_UPDATE_WAIT_TIME_DESYNC = 5; // seconds +const F64 INVENTORY_UPDATE_WAIT_TIME_OUTDATED = 1; + +// static +LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp, S32 flags) +{ + LL_PROFILE_ZONE_SCOPED; + LL_DEBUGS("ObjectUpdate") << "creating " << id << LL_ENDL; + dumpStack("ObjectUpdateStack"); + + LLViewerObject *res = NULL; + + if (gNonInteractive + && pcode != LL_PCODE_LEGACY_AVATAR + && pcode != LL_VO_SURFACE_PATCH + && pcode != LL_VO_WATER + && pcode != LL_VO_VOID_WATER + && pcode != LL_VO_WL_SKY + && pcode != LL_VO_SKY + && pcode != LL_VO_PART_GROUP + ) + { + return res; + } + switch (pcode) + { + case LL_PCODE_VOLUME: + { + res = new LLVOVolume(id, pcode, regionp); break; + break; + } + case LL_PCODE_LEGACY_AVATAR: + { + if (id == gAgentID) + { + if (!gAgentAvatarp) + { + gAgentAvatarp = new LLVOAvatarSelf(id, pcode, regionp); + gAgentAvatarp->initInstance(); + gAgentWearables.setAvatarObject(gAgentAvatarp); + } + else + { + if (isAgentAvatarValid()) + { + gAgentAvatarp->updateRegion(regionp); + } + } + res = gAgentAvatarp; + } + else if (flags & CO_FLAG_CONTROL_AVATAR) + { + LLControlAvatar *control_avatar = new LLControlAvatar(id, pcode, regionp); + control_avatar->initInstance(); + res = control_avatar; + } + else if (flags & CO_FLAG_UI_AVATAR) + { + LLUIAvatar *ui_avatar = new LLUIAvatar(id, pcode, regionp); + ui_avatar->initInstance(); + res = ui_avatar; + } + else + { + LLVOAvatar *avatar = new LLVOAvatar(id, pcode, regionp); + avatar->initInstance(); + res = avatar; + } + break; + } + case LL_PCODE_LEGACY_GRASS: + res = new LLVOGrass(id, pcode, regionp); break; + case LL_PCODE_LEGACY_PART_SYS: +// LL_WARNS() << "Creating old part sys!" << LL_ENDL; +// res = new LLVOPart(id, pcode, regionp); break; + res = NULL; break; + case LL_PCODE_LEGACY_TREE: + res = new LLVOTree(id, pcode, regionp); break; + case LL_PCODE_TREE_NEW: +// LL_WARNS() << "Creating new tree!" << LL_ENDL; +// res = new LLVOTree(id, pcode, regionp); break; + res = NULL; break; + case LL_VO_SURFACE_PATCH: + res = new LLVOSurfacePatch(id, pcode, regionp); break; + case LL_VO_SKY: + res = new LLVOSky(id, pcode, regionp); break; + case LL_VO_VOID_WATER: + res = new LLVOVoidWater(id, pcode, regionp); break; + case LL_VO_WATER: + res = new LLVOWater(id, pcode, regionp); break; + case LL_VO_PART_GROUP: + res = new LLVOPartGroup(id, pcode, regionp); break; + case LL_VO_HUD_PART_GROUP: + res = new LLVOHUDPartGroup(id, pcode, regionp); break; + case LL_VO_WL_SKY: + res = new LLVOWLSky(id, pcode, regionp); break; + default: + LL_WARNS() << "Unknown object pcode " << (S32)pcode << LL_ENDL; + res = NULL; break; + } + + return res; +} + +LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp, bool is_global) +: LLPrimitive(), + mChildList(), + mID(id), + mLocalID(0), + mTotalCRC(0), + mListIndex(-1), + mTEImages(NULL), + mTENormalMaps(NULL), + mTESpecularMaps(NULL), + mbCanSelect(true), + mFlags(0), + mPhysicsShapeType(0), + mPhysicsGravity(0), + mPhysicsFriction(0), + mPhysicsDensity(0), + mPhysicsRestitution(0), + mDrawable(), + mCreateSelected(false), + mRenderMedia(false), + mBestUpdatePrecision(0), + mText(), + mHudText(""), + mHudTextColor(LLColor4::white), + mControlAvatar(NULL), + mLastInterpUpdateSecs(0.f), + mLastMessageUpdateSecs(0.f), + mLatestRecvPacketID(0), + mRegionCrossExpire(0), + mData(NULL), + mAudioSourcep(NULL), + mAudioGain(1.f), + mSoundCutOffRadius(0.f), + mAppAngle(0.f), + mPixelArea(1024.f), + mInventory(NULL), + mInventorySerialNum(0), + mExpectedInventorySerialNum(0), + mInvRequestState(INVENTORY_REQUEST_STOPPED), + mInvRequestXFerId(0), + mInventoryDirty(false), + mRegionp(regionp), + mDead(false), + mOrphaned(false), + mUserSelected(false), + mOnActiveList(false), + mOnMap(false), + mStatic(false), + mSeatCount(0), + mNumFaces(0), + mRotTime(0.f), + mAngularVelocityRot(), + mPreviousRotation(), + mAttachmentState(0), + mMedia(NULL), + mClickAction(0), + mObjectCost(0), + mLinksetCost(0), + mPhysicsCost(0), + mLinksetPhysicsCost(0.f), + mCostStale(true), + mPhysicsShapeUnknown(true), + mAttachmentItemID(LLUUID::null), + mLastUpdateType(OUT_UNKNOWN), + mLastUpdateCached(false), + mCachedMuteListUpdateTime(0), + mCachedOwnerInMuteList(false), + mRiggedAttachedWarned(false) +{ + if (!is_global) + { + llassert(mRegionp); + } + + LLPrimitive::init_primitive(pcode); + + // CP: added 12/2/2005 - this was being initialised to 0, not the current frame time + mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds(); + + mPositionRegion = LLVector3(0.f, 0.f, 0.f); + + if (!is_global && mRegionp) + { + mPositionAgent = mRegionp->getOriginAgent(); + } + resetRot(); + + LLViewerObject::sNumObjects++; +} + +LLViewerObject::~LLViewerObject() +{ + deleteTEImages(); + + // unhook from reflection probe manager + if (mReflectionProbe.notNull()) + { + mReflectionProbe->mViewerObject = nullptr; + mReflectionProbe = nullptr; + } + + if(mInventory) + { + mInventory->clear(); // will deref and delete entries + delete mInventory; + mInventory = NULL; + } + + if (mPartSourcep) + { + mPartSourcep->setDead(); + mPartSourcep = NULL; + } + + if (mText) + { + // something recovered LLHUDText when object was already dead + mText->markDead(); + mText = NULL; + } + + // Delete memory associated with extra parameters. + std::unordered_map::iterator iter; + for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) + { + if(iter->second != NULL) + { + delete iter->second->data; + delete iter->second; + } + } + mExtraParameterList.clear(); + + for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ; + mNameValuePairs.clear(); + + delete[] mData; + mData = NULL; + + delete mMedia; + mMedia = NULL; + + sNumObjects--; + sNumZombieObjects--; + llassert(mChildList.size() == 0); + llassert(mControlAvatar.isNull()); // Should have been cleaned by now + if (mControlAvatar.notNull()) + { + mControlAvatar->markForDeath(); + mControlAvatar = NULL; + LL_WARNS() << "Dead object owned a live control avatar" << LL_ENDL; + } + + clearInventoryListeners(); +} + +void LLViewerObject::deleteTEImages() +{ + delete[] mTEImages; + mTEImages = NULL; + + if (mTENormalMaps != NULL) + { + delete[] mTENormalMaps; + mTENormalMaps = NULL; + } + + if (mTESpecularMaps != NULL) + { + delete[] mTESpecularMaps; + mTESpecularMaps = NULL; + } +} + +void LLViewerObject::markDead() +{ + if (!mDead) + { + LL_PROFILE_ZONE_SCOPED; + //LL_INFOS() << "Marking self " << mLocalID << " as dead." << LL_ENDL; + + // Root object of this hierarchy unlinks itself. + if (getParent()) + { + ((LLViewerObject *)getParent())->removeChild(this); + } + LLUUID mesh_id; + { + LLVOAvatar *av = getAvatar(); + if (av && LLVOAvatar::getRiggedMeshID(this,mesh_id)) + { + // This case is needed for indirectly attached mesh objects. + av->updateAttachmentOverrides(); + } + } + if (getControlAvatar()) + { + unlinkControlAvatar(); + } + + // Mark itself as dead + mDead = true; + if(mRegionp) + { + mRegionp->removeFromCreatedList(getLocalID()); + } + gObjectList.cleanupReferences(this); + + LLViewerObject *childp; + while (mChildList.size() > 0) + { + childp = mChildList.back(); + if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR) + { + //LL_INFOS() << "Marking child " << childp->getLocalID() << " as dead." << LL_ENDL; + childp->setParent(NULL); // LLViewerObject::markDead 1 + childp->markDead(); + } + else + { + // make sure avatar is no longer parented, + // so we can properly set it's position + childp->setDrawableParent(NULL); + ((LLVOAvatar*)childp)->getOffObject(); + childp->setParent(NULL); // LLViewerObject::markDead 2 + } + mChildList.pop_back(); + } + + if (mDrawable.notNull()) + { + // Drawables are reference counted, mark as dead, then nuke the pointer. + mDrawable->markDead(); + mDrawable = NULL; + } + + if (mText) + { + mText->markDead(); + mText = NULL; + } + + if (mIcon) + { + mIcon->markDead(); + mIcon = NULL; + } + + if (mPartSourcep) + { + mPartSourcep->setDead(); + mPartSourcep = NULL; + } + + if (mAudioSourcep) + { + // Do some cleanup + if (gAudiop) + { + gAudiop->cleanupAudioSource(mAudioSourcep); + } + mAudioSourcep = NULL; + } + + if (flagAnimSource()) + { + if (isAgentAvatarValid()) + { + // stop motions associated with this object + gAgentAvatarp->stopMotionFromSource(mID); + } + } + + if (flagCameraSource()) + { + LLFollowCamMgr::getInstance()->removeFollowCamParams(mID); + } + + if (mReflectionProbe.notNull()) + { + mReflectionProbe->mViewerObject = nullptr; + mReflectionProbe = nullptr; + } + + sNumZombieObjects++; + } +} + +void LLViewerObject::dump() const +{ + LL_INFOS() << "Type: " << pCodeToString(mPrimitiveCode) << LL_ENDL; + LL_INFOS() << "Drawable: " << (LLDrawable *)mDrawable << LL_ENDL; + LL_INFOS() << "Update Age: " << LLFrameTimer::getElapsedSeconds() - mLastMessageUpdateSecs << LL_ENDL; + + LL_INFOS() << "Parent: " << getParent() << LL_ENDL; + LL_INFOS() << "ID: " << mID << LL_ENDL; + LL_INFOS() << "LocalID: " << mLocalID << LL_ENDL; + LL_INFOS() << "PositionRegion: " << getPositionRegion() << LL_ENDL; + LL_INFOS() << "PositionAgent: " << getPositionAgent() << LL_ENDL; + LL_INFOS() << "PositionGlobal: " << getPositionGlobal() << LL_ENDL; + LL_INFOS() << "Velocity: " << getVelocity() << LL_ENDL; + if (mDrawable.notNull() && + mDrawable->getNumFaces() && + mDrawable->getFace(0)) + { + LLFacePool *poolp = mDrawable->getFace(0)->getPool(); + if (poolp) + { + LL_INFOS() << "Pool: " << poolp << LL_ENDL; + LL_INFOS() << "Pool reference count: " << poolp->mReferences.size() << LL_ENDL; + } + } + //LL_INFOS() << "BoxTree Min: " << mDrawable->getBox()->getMin() << LL_ENDL; + //LL_INFOS() << "BoxTree Max: " << mDrawable->getBox()->getMin() << LL_ENDL; + /* + LL_INFOS() << "Velocity: " << getVelocity() << LL_ENDL; + LL_INFOS() << "AnyOwner: " << permAnyOwner() << " YouOwner: " << permYouOwner() << " Edit: " << mPermEdit << LL_ENDL; + LL_INFOS() << "UsePhysics: " << flagUsePhysics() << " CanSelect " << mbCanSelect << " UserSelected " << mUserSelected << LL_ENDL; + LL_INFOS() << "AppAngle: " << mAppAngle << LL_ENDL; + LL_INFOS() << "PixelArea: " << mPixelArea << LL_ENDL; + + char buffer[1000]; + char *key; + for (key = mNameValuePairs.getFirstKey(); key; key = mNameValuePairs.getNextKey() ) + { + mNameValuePairs[key]->printNameValue(buffer); + LL_INFOS() << buffer << LL_ENDL; + } + for (child_list_t::iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* child = *iter; + LL_INFOS() << " child " << child->getID() << LL_ENDL; + } + */ +} + +void LLViewerObject::printNameValuePairs() const +{ + for (name_value_map_t::const_iterator iter = mNameValuePairs.begin(); + iter != mNameValuePairs.end(); iter++) + { + LLNameValue* nv = iter->second; + LL_INFOS() << nv->printNameValue() << LL_ENDL; + } +} + +void LLViewerObject::initVOClasses() +{ + // Initialized shared class stuff first. + LLVOAvatar::initClass(); + LLVOTree::initClass(); + LL_INFOS() << "Viewer Object size: " << sizeof(LLViewerObject) << LL_ENDL; + LLVOGrass::initClass(); + LLVOWater::initClass(); + LLVOVolume::initClass(); + + initObjectDataMap(); +} + +void LLViewerObject::cleanupVOClasses() +{ + SUBSYSTEM_CLEANUP(LLVOGrass); + SUBSYSTEM_CLEANUP(LLVOWater); + SUBSYSTEM_CLEANUP(LLVOTree); + SUBSYSTEM_CLEANUP(LLVOAvatar); + SUBSYSTEM_CLEANUP(LLVOVolume); + + sObjectDataMap.clear(); +} + +//object data map for compressed && !OUT_TERSE_IMPROVED +//static +void LLViewerObject::initObjectDataMap() +{ + U32 count = 0; + + sObjectDataMap["ID"] = count; //full id //LLUUID + count += sizeof(LLUUID); + + sObjectDataMap["LocalID"] = count; //U32 + count += sizeof(U32); + + sObjectDataMap["PCode"] = count; //U8 + count += sizeof(U8); + + sObjectDataMap["State"] = count; //U8 + count += sizeof(U8); + + sObjectDataMap["CRC"] = count; //U32 + count += sizeof(U32); + + sObjectDataMap["Material"] = count; //U8 + count += sizeof(U8); + + sObjectDataMap["ClickAction"] = count; //U8 + count += sizeof(U8); + + sObjectDataMap["Scale"] = count; //LLVector3 + count += sizeof(LLVector3); + + sObjectDataMap["Pos"] = count; //LLVector3 + count += sizeof(LLVector3); + + sObjectDataMap["Rot"] = count; //LLVector3 + count += sizeof(LLVector3); + + sObjectDataMap["SpecialCode"] = count; //U32 + count += sizeof(U32); + + sObjectDataMap["Owner"] = count; //LLUUID + count += sizeof(LLUUID); + + sObjectDataMap["Omega"] = count; //LLVector3, when SpecialCode & 0x80 is set + count += sizeof(LLVector3); + + //ParentID is after Omega if there is Omega, otherwise is after Owner + sObjectDataMap["ParentID"] = count;//U32, when SpecialCode & 0x20 is set + count += sizeof(U32); + + //------- + //The rest items are not included here + //------- +} + +//static +void LLViewerObject::unpackVector3(LLDataPackerBinaryBuffer* dp, LLVector3& value, std::string name) +{ + dp->shift(sObjectDataMap[name]); + dp->unpackVector3(value, name.c_str()); + dp->reset(); +} + +//static +void LLViewerObject::unpackUUID(LLDataPackerBinaryBuffer* dp, LLUUID& value, std::string name) +{ + dp->shift(sObjectDataMap[name]); + dp->unpackUUID(value, name.c_str()); + dp->reset(); +} + +//static +void LLViewerObject::unpackU32(LLDataPackerBinaryBuffer* dp, U32& value, std::string name) +{ + dp->shift(sObjectDataMap[name]); + dp->unpackU32(value, name.c_str()); + dp->reset(); +} + +//static +void LLViewerObject::unpackU8(LLDataPackerBinaryBuffer* dp, U8& value, std::string name) +{ + dp->shift(sObjectDataMap[name]); + dp->unpackU8(value, name.c_str()); + dp->reset(); +} + +//static +U32 LLViewerObject::unpackParentID(LLDataPackerBinaryBuffer* dp, U32& parent_id) +{ + dp->shift(sObjectDataMap["SpecialCode"]); + U32 value; + dp->unpackU32(value, "SpecialCode"); + + parent_id = 0; + if(value & 0x20) + { + S32 offset = sObjectDataMap["ParentID"]; + if(!(value & 0x80)) + { + offset -= sizeof(LLVector3); + } + + dp->shift(offset); + dp->unpackU32(parent_id, "ParentID"); + } + dp->reset(); + + return parent_id; +} + +// Replaces all name value pairs with data from \n delimited list +// Does not update server +void LLViewerObject::setNameValueList(const std::string& name_value_list) +{ + // Clear out the old + for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ; + mNameValuePairs.clear(); + + // Bring in the new + std::string::size_type length = name_value_list.length(); + std::string::size_type start = 0; + while (start < length) + { + std::string::size_type end = name_value_list.find_first_of("\n", start); + if (end == std::string::npos) end = length; + if (end > start) + { + std::string tok = name_value_list.substr(start, end - start); + addNVPair(tok); + } + start = end+1; + } +} + +bool LLViewerObject::isAnySelected() const +{ + bool any_selected = isSelected(); + for (child_list_t::const_iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + const LLViewerObject* child = *iter; + any_selected = any_selected || child->isSelected(); + } + return any_selected; +} + +void LLViewerObject::setSelected(bool sel) +{ + mUserSelected = sel; + resetRot(); + + if (!sel) + { + setAllTESelected(false); + } +} + +// This method returns true if the object is over land owned by the +// agent. +bool LLViewerObject::isReturnable() +{ + if (isAttachment()) + { + return false; + } + + std::vector boxes; + boxes.push_back(LLBBox(getPositionRegion(), getRotationRegion(), getScale() * -0.5f, getScale() * 0.5f).getAxisAligned()); + for (child_list_t::iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* child = *iter; + boxes.push_back( LLBBox(child->getPositionRegion(), child->getRotationRegion(), child->getScale() * -0.5f, child->getScale() * 0.5f).getAxisAligned()); + } + + bool result = (mRegionp && mRegionp->objectIsReturnable(getPositionRegion(), boxes)) ? 1 : 0; + + if ( !result ) + { + //Get list of neighboring regions relative to this vo's region + std::vector uniqueRegions; + mRegionp->getNeighboringRegions( uniqueRegions ); + + //Build aabb's - for root and all children + std::vector returnables; + typedef std::vector::iterator RegionIt; + RegionIt regionStart = uniqueRegions.begin(); + RegionIt regionEnd = uniqueRegions.end(); + + for (; regionStart != regionEnd; ++regionStart ) + { + LLViewerRegion* pTargetRegion = *regionStart; + //Add the root vo as there may be no children and we still want + //to test for any edge overlap + buildReturnablesForChildrenVO( returnables, this, pTargetRegion ); + //Add it's children + for (child_list_t::iterator iter = mChildList.begin(); iter != mChildList.end(); iter++) + { + LLViewerObject* pChild = *iter; + buildReturnablesForChildrenVO( returnables, pChild, pTargetRegion ); + } + } + + //TBD#Eventually create a region -> box list map + typedef std::vector::iterator ReturnablesIt; + ReturnablesIt retCurrentIt = returnables.begin(); + ReturnablesIt retEndIt = returnables.end(); + + for ( ; retCurrentIt !=retEndIt; ++retCurrentIt ) + { + boxes.clear(); + LLViewerRegion* pRegion = (*retCurrentIt).pRegion; + boxes.push_back( (*retCurrentIt).box ); + bool retResult = pRegion + && pRegion->childrenObjectReturnable( boxes ) + && pRegion->canManageEstate(); + if ( retResult ) + { + result = true; + break; + } + } + } + return result; +} + +void LLViewerObject::buildReturnablesForChildrenVO( std::vector& returnables, LLViewerObject* pChild, LLViewerRegion* pTargetRegion ) +{ + if ( !pChild ) + { + LL_ERRS()<<"child viewerobject is NULL "<mChildList.begin(); iter != pChild->mChildList.end(); iter++) + { + LLViewerObject* pChildofChild = *iter; + buildReturnablesForChildrenVO( returnables, pChildofChild, pTargetRegion ); + } +} + +void LLViewerObject::constructAndAddReturnable( std::vector& returnables, LLViewerObject* pChild, LLViewerRegion* pTargetRegion ) +{ + + LLVector3 targetRegionPos; + targetRegionPos.setVec( pChild->getPositionGlobal() ); + + LLBBox childBBox = LLBBox( targetRegionPos, pChild->getRotationRegion(), pChild->getScale() * -0.5f, + pChild->getScale() * 0.5f).getAxisAligned(); + + LLVector3 edgeA = targetRegionPos + childBBox.getMinLocal(); + LLVector3 edgeB = targetRegionPos + childBBox.getMaxLocal(); + + LLVector3d edgeAd, edgeBd; + edgeAd.setVec(edgeA); + edgeBd.setVec(edgeB); + + //Only add the box when either of the extents are in a neighboring region + if ( pTargetRegion->pointInRegionGlobal( edgeAd ) || pTargetRegion->pointInRegionGlobal( edgeBd ) ) + { + PotentialReturnableObject returnableObj; + returnableObj.box = childBBox; + returnableObj.pRegion = pTargetRegion; + returnables.push_back( returnableObj ); + } +} + +bool LLViewerObject::crossesParcelBounds() +{ + std::vector boxes; + boxes.push_back(LLBBox(getPositionRegion(), getRotationRegion(), getScale() * -0.5f, getScale() * 0.5f).getAxisAligned()); + for (child_list_t::iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* child = *iter; + boxes.push_back(LLBBox(child->getPositionRegion(), child->getRotationRegion(), child->getScale() * -0.5f, child->getScale() * 0.5f).getAxisAligned()); + } + + return mRegionp && mRegionp->objectsCrossParcel(boxes); +} + +bool LLViewerObject::setParent(LLViewerObject* parent) +{ + if(mParent != parent) + { + LLViewerObject* old_parent = (LLViewerObject*)mParent ; + bool ret = LLPrimitive::setParent(parent); + if(ret && old_parent && parent) + { + old_parent->removeChild(this) ; + } + return ret ; + } + + return false ; +} + +void LLViewerObject::addChild(LLViewerObject *childp) +{ + for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i) + { + if (*i == childp) + { //already has child + return; + } + } + + if (!isAvatar()) + { + // propagate selection properties + childp->mbCanSelect = mbCanSelect; + } + + if(childp->setParent(this)) + { + mChildList.push_back(childp); + childp->afterReparent(); + + if (childp->isAvatar()) + { + mSeatCount++; + } + } +} + +void LLViewerObject::onReparent(LLViewerObject *old_parent, LLViewerObject *new_parent) +{ +} + +void LLViewerObject::afterReparent() +{ +} + +void LLViewerObject::removeChild(LLViewerObject *childp) +{ + for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i) + { + if (*i == childp) + { + if (!childp->isAvatar() && mDrawable.notNull() && mDrawable->isActive() && childp->mDrawable.notNull() && !isAvatar()) + { + gPipeline.markRebuild(childp->mDrawable, LLDrawable::REBUILD_VOLUME); + } + + mChildList.erase(i); + + if(childp->getParent() == this) + { + childp->setParent(NULL); + } + + if (childp->isAvatar()) + { + mSeatCount--; + } + break; + } + } + + if (childp->isSelected()) + { + LLSelectMgr::getInstance()->deselectObjectAndFamily(childp); + bool add_to_end = true; + LLSelectMgr::getInstance()->selectObjectAndFamily(childp, add_to_end); + } +} + +void LLViewerObject::addThisAndAllChildren(std::vector& objects) +{ + objects.push_back(this); + for (child_list_t::iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* child = *iter; + if (!child->isAvatar()) + { + child->addThisAndAllChildren(objects); + } + } +} + +void LLViewerObject::addThisAndNonJointChildren(std::vector& objects) +{ + objects.push_back(this); + // don't add any attachments when temporarily selecting avatar + if (isAvatar()) + { + return; + } + for (child_list_t::iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* child = *iter; + if ( (!child->isAvatar())) + { + child->addThisAndNonJointChildren(objects); + } + } +} + +bool LLViewerObject::isChild(LLViewerObject *childp) const +{ + for (child_list_t::const_iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* testchild = *iter; + if (testchild == childp) + return true; + } + return false; +} + +// returns true if at least one avatar is sitting on this object +bool LLViewerObject::isSeat() const +{ + return mSeatCount > 0; +} + +bool LLViewerObject::setDrawableParent(LLDrawable* parentp) +{ + if (mDrawable.isNull()) + { + return false; + } + + bool ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL); + if(!ret) + { + return false ; + } + LLDrawable* old_parent = mDrawable->mParent; + mDrawable->mParent = parentp; + + if (parentp && mDrawable->isActive()) + { + parentp->makeActive(); + parentp->setState(LLDrawable::ACTIVE_CHILD); + } + + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME); + if( (old_parent != parentp && old_parent) + || (parentp && parentp->isActive())) + { + // *TODO we should not be relying on setDrawable parent to call markMoved + gPipeline.markMoved(mDrawable, false); + } + else if (!mDrawable->isAvatar()) + { + mDrawable->updateXform(true); + /*if (!mDrawable->getSpatialGroup()) + { + mDrawable->movePartition(); + }*/ + } + + return ret; +} + +// Show or hide particles, icon and HUD +void LLViewerObject::hideExtraDisplayItems( bool hidden ) +{ + if( mPartSourcep.notNull() ) + { + LLViewerPartSourceScript *partSourceScript = mPartSourcep.get(); + partSourceScript->setSuspended( hidden ); + } + + if( mText.notNull() ) + { + LLHUDText *hudText = mText.get(); + hudText->setHidden( hidden ); + } + + if( mIcon.notNull() ) + { + LLHUDIcon *hudIcon = mIcon.get(); + hudIcon->setHidden( hidden ); + } +} + +U32 LLViewerObject::checkMediaURL(const std::string &media_url) +{ + U32 retval = (U32)0x0; + if (!mMedia && !media_url.empty()) + { + retval |= MEDIA_URL_ADDED; + mMedia = new LLViewerObjectMedia; + mMedia->mMediaURL = media_url; + mMedia->mMediaType = LLViewerObject::MEDIA_SET; + mMedia->mPassedWhitelist = false; + } + else if (mMedia) + { + if (media_url.empty()) + { + retval |= MEDIA_URL_REMOVED; + delete mMedia; + mMedia = NULL; + } + else if (mMedia->mMediaURL != media_url) // <-- This is an optimization. If they are equal don't bother with below's test. + { + /*if (! (LLTextureEntry::getAgentIDFromMediaVersionString(media_url) == gAgent.getID() && + LLTextureEntry::getVersionFromMediaVersionString(media_url) == + LLTextureEntry::getVersionFromMediaVersionString(mMedia->mMediaURL) + 1)) + */ + { + // If the media URL is different and WE were not the one who + // changed it, mark dirty. + retval |= MEDIA_URL_UPDATED; + } + mMedia->mMediaURL = media_url; + mMedia->mPassedWhitelist = false; + } + } + return retval; +} + +//extract spatial information from object update message +//return parent_id +//static +U32 LLViewerObject::extractSpatialExtents(LLDataPackerBinaryBuffer *dp, LLVector3& pos, LLVector3& scale, LLQuaternion& rot) +{ + U32 parent_id = 0; + LLViewerObject::unpackParentID(dp, parent_id); + + LLViewerObject::unpackVector3(dp, scale, "Scale"); + LLViewerObject::unpackVector3(dp, pos, "Pos"); + + LLVector3 vec; + LLViewerObject::unpackVector3(dp, vec, "Rot"); + rot.unpackFromVector3(vec); + + return parent_id; +} + +U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, + void **user_data, + U32 block_num, + const EObjectUpdateType update_type, + LLDataPacker *dp) +{ + LL_PROFILE_ZONE_SCOPED; + LL_DEBUGS_ONCE("SceneLoadTiming") << "Received viewer object data" << LL_ENDL; + + LL_DEBUGS("ObjectUpdate") << " mesgsys " << mesgsys << " dp " << dp << " id " << getID() << " update_type " << (S32) update_type << LL_ENDL; + dumpStack("ObjectUpdateStack"); + + // The new OBJECTDATA_FIELD_SIZE_124, OBJECTDATA_FIELD_SIZE_140, OBJECTDATA_FIELD_SIZE_80 + // and OBJECTDATA_FIELD_SIZE_64 lengths should be supported in the existing cases below. + // Each case should start at the beginning of the buffer and extract all known + // values, and ignore any unknown data at the end of the buffer. + // This allows new data in the future without breaking current viewers. + const S32 OBJECTDATA_FIELD_SIZE_140 = 140; // Full precision avatar update for future extended data + const S32 OBJECTDATA_FIELD_SIZE_124 = 124; // Full precision object update for future extended data + const S32 OBJECTDATA_FIELD_SIZE_76 = 76; // Full precision avatar update + const S32 OBJECTDATA_FIELD_SIZE_60 = 60; // Full precision object update + const S32 OBJECTDATA_FIELD_SIZE_80 = 80; // Terse avatar update, 16 bit precision for future extended data + const S32 OBJECTDATA_FIELD_SIZE_64 = 64; // Terse object update, 16 bit precision for future extended data + const S32 OBJECTDATA_FIELD_SIZE_48 = 48; // Terse avatar update, 16 bit precision + const S32 OBJECTDATA_FIELD_SIZE_32 = 32; // Terse object update, 16 bit precision + + U32 retval = 0x0; + + // If region is removed from the list it is also deleted. + if (!LLWorld::instance().isRegionListed(mRegionp)) + { + LL_WARNS() << "Updating object in an invalid region" << LL_ENDL; + return retval; + } + + // Coordinates of objects on simulators are region-local. + U64 region_handle = 0; + + if(mesgsys != NULL) + { + mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle); + LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromHandle(region_handle); + if(regionp != mRegionp && regionp && mRegionp)//region cross + { + //this is the redundant position and region update, but it is necessary in case the viewer misses the following + //position and region update messages from sim. + //this redundant update should not cause any problems. + LLVector3 delta_pos = mRegionp->getOriginAgent() - regionp->getOriginAgent(); + setPositionParent(getPosition() + delta_pos); //update to the new region position immediately. + setRegion(regionp) ; //change the region. + } + else + { + if(regionp != mRegionp) + { + if(mRegionp) + { + mRegionp->removeFromCreatedList(getLocalID()); + } + if(regionp) + { + regionp->addToCreatedList(getLocalID()); + } + } + mRegionp = regionp ; + } + } + + if (!mRegionp) + { + U32 x, y; + from_region_handle(region_handle, &x, &y); + + LL_WARNS("UpdateFail") << "Object has invalid region " << x << ":" << y << "!" << LL_ENDL; + return retval; + } + + F32 time_dilation = 1.f; + if(mesgsys != NULL) + { + U16 time_dilation16; + mesgsys->getU16Fast(_PREHASH_RegionData, _PREHASH_TimeDilation, time_dilation16); + time_dilation = ((F32) time_dilation16) / 65535.f; + mRegionp->setTimeDilation(time_dilation); + } + + // this will be used to determine if we've really changed position + // Use getPosition, not getPositionRegion, since this is what we're comparing directly against. + LLVector3 test_pos_parent = getPosition(); + + // This needs to match the largest size below. See switch(length) + U8 data[MAX_OBJECT_BINARY_DATA_SIZE]; + +#ifdef LL_BIG_ENDIAN + U16 valswizzle[4]; +#endif + U16 *val; + const F32 size = LLWorld::getInstance()->getRegionWidthInMeters(); + const F32 MAX_HEIGHT = LLWorld::getInstance()->getRegionMaxHeight(); + const F32 MIN_HEIGHT = LLWorld::getInstance()->getRegionMinHeight(); + S32 length = 0; + S32 count = 0; + S32 this_update_precision = 32; // in bits + + // Temporaries, because we need to compare w/ previous to set dirty flags... + LLVector3 new_pos_parent; + LLVector3 new_vel; + LLVector3 new_acc; + LLVector3 new_angv; + LLVector3 old_angv = getAngularVelocity(); + LLQuaternion new_rot; + LLVector3 new_scale = getScale(); + + U32 parent_id = 0; + U8 material = 0; + U8 click_action = 0; + U32 crc = 0; + + bool old_special_hover_cursor = specialHoverCursor(); + + LLViewerObject *cur_parentp = (LLViewerObject *)getParent(); + + if (cur_parentp) + { + parent_id = cur_parentp->mLocalID; + } + + if (!dp) + { + switch(update_type) + { + case OUT_FULL: + { +#ifdef DEBUG_UPDATE_TYPE + LL_INFOS() << "Full:" << getID() << LL_ENDL; +#endif + //clear cost and linkset cost + setObjectCostStale(); + if (isSelected()) + { + gFloaterTools->dirty(); + } + + LLUUID audio_uuid; + LLUUID owner_id; // only valid if audio_uuid or particle system is not null + F32 gain; + F32 cutoff; + U8 sound_flags; + + mesgsys->getU32Fast( _PREHASH_ObjectData, _PREHASH_CRC, crc, block_num); + mesgsys->getU32Fast( _PREHASH_ObjectData, _PREHASH_ParentID, parent_id, block_num); + mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_Sound, audio_uuid, block_num ); + // HACK: Owner id only valid if non-null sound id or particle system + mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id, block_num ); + mesgsys->getF32Fast( _PREHASH_ObjectData, _PREHASH_Gain, gain, block_num ); + mesgsys->getF32Fast( _PREHASH_ObjectData, _PREHASH_Radius, cutoff, block_num ); + mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_Flags, sound_flags, block_num ); + mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_Material, material, block_num ); + mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_ClickAction, click_action, block_num); + mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_Scale, new_scale, block_num ); + length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData); + mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num, MAX_OBJECT_BINARY_DATA_SIZE); + length = llmin(length, MAX_OBJECT_BINARY_DATA_SIZE); // getBinaryDataFast() safely fills the buffer to max_size + + mTotalCRC = crc; + // Might need to update mSourceMuted here to properly pick up new radius + mSoundCutOffRadius = cutoff; + + // Owner ID used for sound muting or particle system muting + setAttachedSound(audio_uuid, owner_id, gain, sound_flags); + + U8 old_material = getMaterial(); + if (old_material != material) + { + setMaterial(material); + if (mDrawable.notNull()) + { + gPipeline.markMoved(mDrawable, false); // undamped + } + } + setClickAction(click_action); + + count = 0; + LLVector4 collision_plane; + + switch(length) + { + case OBJECTDATA_FIELD_SIZE_140: + case OBJECTDATA_FIELD_SIZE_76: + // pull out collision normal for avatar + htolememcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); + ((LLVOAvatar*)this)->setFootPlane(collision_plane); + count += sizeof(LLVector4); + + case OBJECTDATA_FIELD_SIZE_124: + case OBJECTDATA_FIELD_SIZE_60: + this_update_precision = 32; + // this is a full precision update + // pos + htolememcpy(new_pos_parent.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); + count += sizeof(LLVector3); + // vel + htolememcpy((void*)getVelocity().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); + count += sizeof(LLVector3); + // acc + htolememcpy((void*)getAcceleration().mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); + count += sizeof(LLVector3); + // theta + { + LLVector3 vec; + htolememcpy(vec.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); + new_rot.unpackFromVector3(vec); + } + count += sizeof(LLVector3); + // omega + htolememcpy((void*)new_angv.mV, &data[count], MVT_LLVector3, sizeof(LLVector3)); + if (new_angv.isExactlyZero()) + { + // reset rotation time + resetRot(); + } + setAngularVelocity(new_angv); + count += sizeof(LLVector3); +#if LL_DARWIN + if (length == OBJECTDATA_FIELD_SIZE_76 || + length == OBJECTDATA_FIELD_SIZE_140) + { + setAngularVelocity(LLVector3::zero); + } +#endif + break; + + // length values 48, 32 and 16 were once in viewer code but + // are never sent by the SL simulator + default: + LL_WARNS("UpdateFail") << "Unexpected ObjectData buffer size " << length + << " for " << getID() << " with OUT_FULL message" << LL_ENDL; + } + + //////////////////////////////////////////////////// + // + // Here we handle data specific to the full message. + // + + U32 flags; + mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, block_num); + // clear all but local flags + mFlags &= FLAGS_LOCAL; + mFlags |= flags; + + U8 state; + mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); + mAttachmentState = state; + + // ...new objects that should come in selected need to be added to the selected list + mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0); + + // Set all name value pairs + S32 nv_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_NameValue); + if (nv_size > 0) + { + std::string name_value_list; + mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_NameValue, name_value_list, block_num); + setNameValueList(name_value_list); + } + + // Clear out any existing generic data + if (mData) + { + delete [] mData; + mData = NULL; + } + + // Dec 2023 new generic data: + // Trees work as before, this field contains genome data + // Not a tree: root objects send 1 byte with the number of + // total prims in the linkset + // If the generic data size is zero, then number of prims is 1 + // + // Viewers should not check for specific data sizes exactly, but if + // the field has data, process it from the start and ignore the remainder. + + // Check for appended generic data + const S32 GENERIC_DATA_BUFFER_SIZE = 16; + S32 data_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Data); + if (data_size > 0) + { // has generic data + if (getPCode() == LL_PCODE_LEGACY_TREE || getPCode() == LL_PCODE_TREE_NEW) + { + mData = new U8[data_size]; + mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, mData, data_size, block_num); + LL_DEBUGS("NewObjectData") << "Read " << data_size << " bytes tree genome data for " << getID() << ", pcode " + << getPCodeString() << ", value " << (S32) mData[0] << LL_ENDL; + } + else + { // Extract number of prims + U8 generic_data[GENERIC_DATA_BUFFER_SIZE]; + mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, + &generic_data[0], llmin(data_size, GENERIC_DATA_BUFFER_SIZE), block_num); + // This is sample code to extract the number of prims + // Future viewers should use it for their own purposes + if (!isAvatar()) + { + S32 num_prims = (S32) generic_data[0]; + LL_DEBUGS("NewObjectData") << "Root prim " << getID() << " has " + << num_prims << " prims in linkset" << LL_ENDL; + } + } + } + + S32 text_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Text); + if (text_size > 1) + { + // Setup object text + if (!mText) + { + initHudText(); + } + + std::string temp_string; + mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_Text, temp_string, block_num ); + + LLColor4U coloru; + mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextColor, coloru.mV, 4, block_num); + + // alpha was flipped so that it zero encoded better + coloru.mV[3] = 255 - coloru.mV[3]; + mText->setColor(LLColor4(coloru)); + mText->setString(temp_string); + + mHudText = temp_string; + mHudTextColor = LLColor4(coloru); + + setChanged(MOVED | SILHOUETTE); + } + else + { + if (mText.notNull()) + { + mText->markDead(); + mText = NULL; + } + mHudText.clear(); + } + + std::string media_url; + mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_MediaURL, media_url, block_num); + retval |= checkMediaURL(media_url); + + // + // Unpack particle system data + // + unpackParticleSource(block_num, owner_id); + + // Mark all extra parameters not used + std::unordered_map::iterator iter; + for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) + { + iter->second->in_use = false; + } + + // Unpack extra parameters + S32 size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ExtraParams); + if (size > 0) + { + U8 *buffer = new U8[size]; + mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ExtraParams, buffer, size, block_num); + LLDataPackerBinaryBuffer dp(buffer, size); + + U8 num_parameters; + dp.unpackU8(num_parameters, "num_params"); + U8 param_block[MAX_OBJECT_PARAMS_SIZE]; + for (U8 param=0; paramsecond->in_use) + { + // Send an update message in case it was formerly in use + parameterChanged(iter->first, iter->second->data, false, false); + } + } + + break; + } + + case OUT_TERSE_IMPROVED: + { +#ifdef DEBUG_UPDATE_TYPE + LL_INFOS() << "TI:" << getID() << LL_ENDL; +#endif + length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ObjectData); + mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ObjectData, data, length, block_num, MAX_OBJECT_BINARY_DATA_SIZE); + length = llmin(length, MAX_OBJECT_BINARY_DATA_SIZE); // getBinaryDataFast() safely fills the buffer to max_size + count = 0; + LLVector4 collision_plane; + + switch(length) + { + case OBJECTDATA_FIELD_SIZE_80: + case OBJECTDATA_FIELD_SIZE_48: + // pull out collision normal for avatar + htolememcpy(collision_plane.mV, &data[count], MVT_LLVector4, sizeof(LLVector4)); + ((LLVOAvatar*)this)->setFootPlane(collision_plane); + count += sizeof(LLVector4); + + case OBJECTDATA_FIELD_SIZE_64: + case OBJECTDATA_FIELD_SIZE_32: + // this is a terse 16 bit quantized update + this_update_precision = 16; + test_pos_parent.quantize16(-0.5f*size, 1.5f*size, MIN_HEIGHT, MAX_HEIGHT); + +#ifdef LL_BIG_ENDIAN + htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); + val = valswizzle; +#else + val = (U16 *) &data[count]; +#endif + count += sizeof(U16)*3; + new_pos_parent.mV[VX] = U16_to_F32(val[VX], -0.5f*size, 1.5f*size); + new_pos_parent.mV[VY] = U16_to_F32(val[VY], -0.5f*size, 1.5f*size); + new_pos_parent.mV[VZ] = U16_to_F32(val[VZ], MIN_HEIGHT, MAX_HEIGHT); + +#ifdef LL_BIG_ENDIAN + htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); + val = valswizzle; +#else + val = (U16 *) &data[count]; +#endif + count += sizeof(U16)*3; + setVelocity(U16_to_F32(val[VX], -size, size), + U16_to_F32(val[VY], -size, size), + U16_to_F32(val[VZ], -size, size)); + +#ifdef LL_BIG_ENDIAN + htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); + val = valswizzle; +#else + val = (U16 *) &data[count]; +#endif + count += sizeof(U16)*3; + setAcceleration(U16_to_F32(val[VX], -size, size), + U16_to_F32(val[VY], -size, size), + U16_to_F32(val[VZ], -size, size)); + +#ifdef LL_BIG_ENDIAN + htolememcpy(valswizzle, &data[count], MVT_U16Quat, 8); + val = valswizzle; +#else + val = (U16 *) &data[count]; +#endif + count += sizeof(U16)*4; + new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f); + new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f); + new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f); + new_rot.mQ[VW] = U16_to_F32(val[VW], -1.f, 1.f); + +#ifdef LL_BIG_ENDIAN + htolememcpy(valswizzle, &data[count], MVT_U16Vec3, 6); + val = valswizzle; +#else + val = (U16 *) &data[count]; +#endif + new_angv.set(U16_to_F32(val[VX], -size, size), + U16_to_F32(val[VY], -size, size), + U16_to_F32(val[VZ], -size, size)); + setAngularVelocity(new_angv); + break; + + // Previous viewers had code for length 76, 60 or 16 byte length + // with full precision or 8 bit quanitzation, but the + // SL servers will never send those data formats. If you ever see this + // warning in Second Life, please file a bug report + default: + LL_WARNS("UpdateFail") << "Unexpected ObjectData buffer size " << length << " for " << getID() + << " with OUT_FULL message" << LL_ENDL; + } + + U8 state; + mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); + mAttachmentState = state; + break; + } + + default: + LL_WARNS("UpdateFail") << "Unknown uncompressed update type " << update_type << " for " << getID() << LL_ENDL; + break; + } + } + else + { + // handle the compressed case - have dp datapacker + LLUUID sound_uuid; + LLUUID owner_id; + F32 gain = 0; + U8 sound_flags = 0; + F32 cutoff = 0; + + U16 val[4]; + + U8 state; + + dp->unpackU8(state, "State"); + mAttachmentState = state; + + switch(update_type) + { + case OUT_TERSE_IMPROVED: + { +#ifdef DEBUG_UPDATE_TYPE + LL_INFOS() << "CompTI:" << getID() << LL_ENDL; +#endif + U8 value; + dp->unpackU8(value, "agent"); + if (value) + { + LLVector4 collision_plane; + dp->unpackVector4(collision_plane, "Plane"); + ((LLVOAvatar*)this)->setFootPlane(collision_plane); + } + test_pos_parent = getPosition(); + dp->unpackVector3(new_pos_parent, "Pos"); + dp->unpackU16(val[VX], "VelX"); + dp->unpackU16(val[VY], "VelY"); + dp->unpackU16(val[VZ], "VelZ"); + setVelocity(U16_to_F32(val[VX], -128.f, 128.f), + U16_to_F32(val[VY], -128.f, 128.f), + U16_to_F32(val[VZ], -128.f, 128.f)); + dp->unpackU16(val[VX], "AccX"); + dp->unpackU16(val[VY], "AccY"); + dp->unpackU16(val[VZ], "AccZ"); + setAcceleration(U16_to_F32(val[VX], -64.f, 64.f), + U16_to_F32(val[VY], -64.f, 64.f), + U16_to_F32(val[VZ], -64.f, 64.f)); + + dp->unpackU16(val[VX], "ThetaX"); + dp->unpackU16(val[VY], "ThetaY"); + dp->unpackU16(val[VZ], "ThetaZ"); + dp->unpackU16(val[VS], "ThetaS"); + new_rot.mQ[VX] = U16_to_F32(val[VX], -1.f, 1.f); + new_rot.mQ[VY] = U16_to_F32(val[VY], -1.f, 1.f); + new_rot.mQ[VZ] = U16_to_F32(val[VZ], -1.f, 1.f); + new_rot.mQ[VS] = U16_to_F32(val[VS], -1.f, 1.f); + dp->unpackU16(val[VX], "AccX"); + dp->unpackU16(val[VY], "AccY"); + dp->unpackU16(val[VZ], "AccZ"); + new_angv.set(U16_to_F32(val[VX], -64.f, 64.f), + U16_to_F32(val[VY], -64.f, 64.f), + U16_to_F32(val[VZ], -64.f, 64.f)); + setAngularVelocity(new_angv); + } + break; + case OUT_FULL_COMPRESSED: + case OUT_FULL_CACHED: + { +#ifdef DEBUG_UPDATE_TYPE + LL_INFOS() << "CompFull:" << getID() << LL_ENDL; +#endif + setObjectCostStale(); + + if (isSelected()) + { + gFloaterTools->dirty(); + } + + dp->unpackU32(crc, "CRC"); + mTotalCRC = crc; + dp->unpackU8(material, "Material"); + U8 old_material = getMaterial(); + if (old_material != material) + { + setMaterial(material); + if (mDrawable.notNull()) + { + gPipeline.markMoved(mDrawable, false); // undamped + } + } + dp->unpackU8(click_action, "ClickAction"); + setClickAction(click_action); + dp->unpackVector3(new_scale, "Scale"); + dp->unpackVector3(new_pos_parent, "Pos"); + LLVector3 vec; + dp->unpackVector3(vec, "Rot"); + new_rot.unpackFromVector3(vec); + setAcceleration(LLVector3::zero); + + U32 value; + dp->unpackU32(value, "SpecialCode"); + dp->setPassFlags(value); + dp->unpackUUID(owner_id, "Owner"); + + mOwnerID = owner_id; + + if (value & 0x80) + { + dp->unpackVector3(new_angv, "Omega"); + setAngularVelocity(new_angv); + } + + if (value & 0x20) + { + dp->unpackU32(parent_id, "ParentID"); + } + else + { + parent_id = 0; + } + + S32 sp_size; + U32 size; + if (value & 0x2) + { + sp_size = 1; + delete [] mData; + mData = new U8[1]; + dp->unpackU8(((U8*)mData)[0], "TreeData"); + } + else if (value & 0x1) + { + dp->unpackU32(size, "ScratchPadSize"); + delete [] mData; + mData = new U8[size]; + dp->unpackBinaryData((U8 *)mData, sp_size, "PartData"); + } + else + { + mData = NULL; + } + + // Setup object text + if (!mText && (value & 0x4)) + { + initHudText(); + } + + if (value & 0x4) + { + std::string temp_string; + dp->unpackString(temp_string, "Text"); + LLColor4U coloru; + dp->unpackBinaryDataFixed(coloru.mV, 4, "Color"); + coloru.mV[3] = 255 - coloru.mV[3]; + mText->setColor(LLColor4(coloru)); + mText->setString(temp_string); + + mHudText = temp_string; + mHudTextColor = LLColor4(coloru); + + setChanged(TEXTURE); + } + else + { + if (mText.notNull()) + { + mText->markDead(); + mText = NULL; + } + mHudText.clear(); + } + + std::string media_url; + if (value & 0x200) + { + dp->unpackString(media_url, "MediaURL"); + } + retval |= checkMediaURL(media_url); + + // + // Unpack particle system data (legacy) + // + if (value & 0x8) + { + unpackParticleSource(*dp, owner_id, true); + } + else if (!(value & 0x400)) + { + deleteParticleSource(); + } + + // Mark all extra parameters not used + std::unordered_map::iterator iter; + for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) + { + iter->second->in_use = false; + } + + // Unpack extra params + U8 num_parameters; + dp->unpackU8(num_parameters, "num_params"); + U8 param_block[MAX_OBJECT_PARAMS_SIZE]; + for (U8 param=0; paramunpackU16(param_type, "param_type"); + dp->unpackBinaryData(param_block, param_size, "param_data"); + //LL_INFOS() << "Param type: " << param_type << ", Size: " << param_size << LL_ENDL; + LLDataPackerBinaryBuffer dp2(param_block, param_size); + unpackParameterEntry(param_type, &dp2); + } + + for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) + { + if (!iter->second->in_use) + { + // Send an update message in case it was formerly in use + parameterChanged(iter->first, iter->second->data, false, false); + } + } + + if (value & 0x10) + { + dp->unpackUUID(sound_uuid, "SoundUUID"); + dp->unpackF32(gain, "SoundGain"); + dp->unpackU8(sound_flags, "SoundFlags"); + dp->unpackF32(cutoff, "SoundRadius"); + } + + if (value & 0x100) + { + std::string name_value_list; + dp->unpackString(name_value_list, "NV"); + + setNameValueList(name_value_list); + } + + mTotalCRC = crc; + mSoundCutOffRadius = cutoff; + + setAttachedSound(sound_uuid, owner_id, gain, sound_flags); + + // only get these flags on updates from sim, not cached ones + // Preload these five flags for every object. + // Finer shades require the object to be selected, and the selection manager + // stores the extended permission info. + if(mesgsys != NULL) + { + U32 flags; + mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, block_num); + loadFlags(flags); + } + } + break; + + default: + LL_WARNS("UpdateFail") << "Unknown compressed update type " << update_type << " for " << getID() << LL_ENDL; + break; + } + } + + // + // Fix object parenting. + // + bool b_changed_status = false; + + if (OUT_TERSE_IMPROVED != update_type) + { + // We only need to update parenting on full updates, terse updates + // don't send parenting information. + if (!cur_parentp) + { + if (parent_id == 0) + { + // No parent now, no parent in message -> do nothing + } + else + { + // No parent now, new parent in message -> attach to that parent if possible + LLUUID parent_uuid; + + if(mesgsys != NULL) + { + LLViewerObjectList::getUUIDFromLocal(parent_uuid, + parent_id, + mesgsys->getSenderIP(), + mesgsys->getSenderPort()); + } + else + { + LLViewerObjectList::getUUIDFromLocal(parent_uuid, + parent_id, + mRegionp->getHost().getAddress(), + mRegionp->getHost().getPort()); + } + + LLViewerObject *sent_parentp = gObjectList.findObject(parent_uuid); + + // + // Check to see if we have the corresponding viewer object for the parent. + // + if (sent_parentp && sent_parentp->getParent() == this) + { + // Try to recover if we attempt to attach a parent to its child + LL_WARNS("UpdateFail") << "Attempt to attach a parent to it's child: " << this->getID() << " to " + << sent_parentp->getID() << LL_ENDL; + this->removeChild(sent_parentp); + sent_parentp->setDrawableParent(NULL); + } + + if (sent_parentp && (sent_parentp != this) && !sent_parentp->isDead()) + { + if (((LLViewerObject*)sent_parentp)->isAvatar()) + { + //LL_DEBUGS("Avatar") << "ATT got object update for attachment " << LL_ENDL; + } + + // + // We have a viewer object for the parent, and it's not dead. + // Do the actual reparenting here. + // + + // new parent is valid + b_changed_status = true; + // ...no current parent, so don't try to remove child + if (mDrawable.notNull()) + { + if (mDrawable->isDead() || !mDrawable->getVObj()) + { + LL_WARNS("UpdateFail") << "Drawable is dead or no VObj!" << LL_ENDL; + sent_parentp->addChild(this); + } + else + { + if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 1 + { + // Bad, we got a cycle somehow. + // Kill both the parent and the child, and + // set cache misses for both of them. + LL_WARNS("UpdateFail") << "Attempting to recover from parenting cycle! " + << "Killing " << sent_parentp->getID() << " and " << getID() + << ", Adding to cache miss list" << LL_ENDL; + setParent(NULL); + sent_parentp->setParent(NULL); + getRegion()->addCacheMissFull(getLocalID()); + getRegion()->addCacheMissFull(sent_parentp->getLocalID()); + gObjectList.killObject(sent_parentp); + gObjectList.killObject(this); + return retval; + } + sent_parentp->addChild(this); + // make sure this object gets a non-damped update + if (sent_parentp->mDrawable.notNull()) + { + gPipeline.markMoved(sent_parentp->mDrawable, false); // undamped + } + } + } + else + { + sent_parentp->addChild(this); + } + + // Show particles, icon and HUD + hideExtraDisplayItems( false ); + + setChanged(MOVED | SILHOUETTE); + } + else + { + // + // No corresponding viewer object for the parent, put the various + // pieces on the orphan list. + // + + //parent_id + U32 ip, port; + + if(mesgsys != NULL) + { + ip = mesgsys->getSenderIP(); + port = mesgsys->getSenderPort(); + } + else + { + ip = mRegionp->getHost().getAddress(); + port = mRegionp->getHost().getPort(); + } + gObjectList.orphanize(this, parent_id, ip, port); + + // Hide particles, icon and HUD + hideExtraDisplayItems( true ); + } + } + } + else + { + // BUG: this is a bad assumption once border crossing is alowed + if ( (parent_id == cur_parentp->mLocalID) + &&(update_type == OUT_TERSE_IMPROVED)) + { + // Parent now, same parent in message -> do nothing + + // Debugging for suspected problems with local ids. + //LLUUID parent_uuid; + //LLViewerObjectList::getUUIDFromLocal(parent_uuid, parent_id, mesgsys->getSenderIP(), mesgsys->getSenderPort() ); + //if (parent_uuid != cur_parentp->getID() ) + //{ + // LL_ERRS() << "Local ID match but UUID mismatch of viewer object" << LL_ENDL; + //} + } + else + { + // Parented now, different parent in message + LLViewerObject *sent_parentp; + if (parent_id == 0) + { + // + // This object is no longer parented, we sent in a zero parent ID. + // + sent_parentp = NULL; + } + else + { + LLUUID parent_uuid; + + if(mesgsys != NULL) + { + LLViewerObjectList::getUUIDFromLocal(parent_uuid, + parent_id, + gMessageSystem->getSenderIP(), + gMessageSystem->getSenderPort()); + } + else + { + LLViewerObjectList::getUUIDFromLocal(parent_uuid, + parent_id, + mRegionp->getHost().getAddress(), + mRegionp->getHost().getPort()); + } + sent_parentp = gObjectList.findObject(parent_uuid); + + if (isAvatar()) + { + // This logic is meant to handle the case where a sitting avatar has reached a new sim + // ahead of the object she was sitting on (which is common as objects are transfered through + // a slower route than agents)... + // In this case, the local id for the object will not be valid, since the viewer has not received + // a full update for the object from that sim yet, so we assume that the agent is still sitting + // where she was originally. --RN + if (!sent_parentp) + { + sent_parentp = cur_parentp; + } + } + else if (!sent_parentp) + { + // + // Switching parents, but we don't know the new parent. + // + U32 ip, port; + + if(mesgsys != NULL) + { + ip = mesgsys->getSenderIP(); + port = mesgsys->getSenderPort(); + } + else + { + ip = mRegionp->getHost().getAddress(); + port = mRegionp->getHost().getPort(); + } + + // We're an orphan, flag things appropriately. + gObjectList.orphanize(this, parent_id, ip, port); + } + } + + // Reattach if possible. + if (sent_parentp && sent_parentp != cur_parentp && sent_parentp != this) + { + // New parent is valid, detach and reattach + b_changed_status = true; + if (mDrawable.notNull()) + { + if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 2 + { + // Bad, we got a cycle somehow. + // Kill both the parent and the child, and + // set cache misses for both of them. + LL_WARNS() << "Attempting to recover from parenting cycle!" << LL_ENDL; + LL_WARNS() << "Killing " << sent_parentp->getID() << " and " << getID() << LL_ENDL; + LL_WARNS() << "Adding to cache miss list" << LL_ENDL; + setParent(NULL); + sent_parentp->setParent(NULL); + getRegion()->addCacheMissFull(getLocalID()); + getRegion()->addCacheMissFull(sent_parentp->getLocalID()); + gObjectList.killObject(sent_parentp); + gObjectList.killObject(this); + return retval; + } + // make sure this object gets a non-damped update + } + cur_parentp->removeChild(this); + sent_parentp->addChild(this); + setChanged(MOVED | SILHOUETTE); + sent_parentp->setChanged(MOVED | SILHOUETTE); + if (sent_parentp->mDrawable.notNull()) + { + gPipeline.markMoved(sent_parentp->mDrawable, false); // undamped + } + } + else if (!sent_parentp) + { + bool remove_parent = true; + // No new parent, or the parent that we sent doesn't exist on the viewer. + LLViewerObject *parentp = (LLViewerObject *)getParent(); + if (parentp) + { + if (parentp->getRegion() != getRegion()) + { + // This is probably an object flying across a region boundary, the + // object probably ISN'T being reparented, but just got an object + // update out of order (child update before parent). + //LL_INFOS() << "Don't reparent object handoffs!" << LL_ENDL; + remove_parent = false; + } + } + + if (remove_parent) + { + b_changed_status = true; + if (mDrawable.notNull()) + { + // clear parent to removeChild can put the drawable on the damped list + setDrawableParent(NULL); // LLViewerObject::processUpdateMessage 3 + } + + cur_parentp->removeChild(this); + + setChanged(MOVED | SILHOUETTE); + + if (mDrawable.notNull()) + { + // make sure this object gets a non-damped update + gPipeline.markMoved(mDrawable, false); // undamped + } + } + } + } + } + } + + new_rot.normQuat(); + + if (sPingInterpolate && mesgsys != NULL) + { + LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mesgsys->getSender()); + if (cdp) + { + // Note: delay is U32 and usually less then second, + // converting it into seconds with valueInUnits will result in 0 + F32 ping_delay = 0.5f * time_dilation * ( ((F32)cdp->getPingDelay().value()) * 0.001f + gFrameDTClamped); + LLVector3 diff = getVelocity() * ping_delay; + new_pos_parent += diff; + } + else + { + LL_WARNS() << "findCircuit() returned NULL; skipping interpolation" << LL_ENDL; + } + } + + ////////////////////////// + // + // Set the generic change flags... + // + // + + // If we're going to skip this message, why are we + // doing all the parenting, etc above? + if(mesgsys != NULL) + { + U32 packet_id = mesgsys->getCurrentRecvPacketID(); + if (packet_id < mLatestRecvPacketID && + mLatestRecvPacketID - packet_id < 65536) + { + //skip application of this message, it's old + return retval; + } + mLatestRecvPacketID = packet_id; + } + + // Set the change flags for scale + if (new_scale != getScale()) + { + setChanged(SCALED | SILHOUETTE); + setScale(new_scale); // Must follow setting permYouOwner() + } + + // first, let's see if the new position is actually a change + + //static S32 counter = 0; + + F32 vel_mag_sq = getVelocity().magVecSquared(); + F32 accel_mag_sq = getAcceleration().magVecSquared(); + + if ( ((b_changed_status)||(test_pos_parent != new_pos_parent)) + ||( (!isSelected()) + &&( (vel_mag_sq != 0.f) + ||(accel_mag_sq != 0.f) + ||(this_update_precision > mBestUpdatePrecision)))) + { + mBestUpdatePrecision = this_update_precision; + + LLVector3 diff = new_pos_parent - test_pos_parent ; + F32 mag_sqr = diff.magVecSquared() ; + if(llfinite(mag_sqr)) + { + setPositionParent(new_pos_parent); + } + else + { + LL_WARNS() << "Can not move the object/avatar to an infinite location!" << LL_ENDL ; + + retval |= INVALID_UPDATE ; + } + + if (mParent && ((LLViewerObject*)mParent)->isAvatar()) + { + // we have changed the position of an attachment, so we need to clamp it + LLVOAvatar *avatar = (LLVOAvatar*)mParent; + + avatar->clampAttachmentPositions(); + } + + // If we're snapping the position by more than 0.5m, update LLViewerStats::mAgentPositionSnaps + if ( asAvatar() && asAvatar()->isSelf() && (mag_sqr > 0.25f) ) + { + record(LLStatViewer::AGENT_POSITION_SNAP, LLUnit(diff.length())); + } + } + + if ((new_rot.isNotEqualEps(getRotation(), F_ALMOST_ZERO)) + || (new_angv != old_angv)) + { + if (new_rot != mPreviousRotation) + { + resetRot(); + } + else if (new_angv != old_angv) + { + if (flagUsePhysics()) + { + resetRot(); + } + else + { + resetRotTime(); + } + } + + // Remember the last rotation value + mPreviousRotation = new_rot; + + // Set the rotation of the object followed by adjusting for the accumulated angular velocity (llSetTargetOmega) + setRotation(new_rot * mAngularVelocityRot); + setChanged(ROTATED | SILHOUETTE); + } + + if ( gShowObjectUpdates ) + { + LLColor4 color; + if (update_type == OUT_TERSE_IMPROVED) + { + color.setVec(0.f, 0.f, 1.f, 1.f); + } + else + { + color.setVec(1.f, 0.f, 0.f, 1.f); + } + gPipeline.addDebugBlip(getPositionAgent(), color); + LL_DEBUGS("MessageBlip") << "Update type " << (S32)update_type << " blip for local " << mLocalID << " at " << getPositionAgent() << LL_ENDL; + } + + const F32 MAG_CUTOFF = F_APPROXIMATELY_ZERO; + + llassert(vel_mag_sq >= 0.f); + llassert(accel_mag_sq >= 0.f); + llassert(getAngularVelocity().magVecSquared() >= 0.f); + + if ((MAG_CUTOFF >= vel_mag_sq) && + (MAG_CUTOFF >= accel_mag_sq) && + (MAG_CUTOFF >= getAngularVelocity().magVecSquared())) + { + mStatic = true; // This object doesn't move! + } + else + { + mStatic = false; + } + +// BUG: This code leads to problems during group rotate and any scale operation. +// Small discepencies between the simulator and viewer representations cause the +// selection center to creep, leading to objects moving around the wrong center. +// +// Removing this, however, means that if someone else drags an object you have +// selected, your selection center and dialog boxes will be wrong. It also means +// that higher precision information on selected objects will be ignored. +// +// I believe the group rotation problem is fixed. JNC 1.21.2002 +// + // Additionally, if any child is selected, need to update the dialogs and selection + // center. + bool needs_refresh = mUserSelected; + for (child_list_t::iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* child = *iter; + needs_refresh = needs_refresh || child->mUserSelected; + } + + static LLCachedControl allow_select_avatar(gSavedSettings, "AllowSelectAvatar", false); + if (needs_refresh) + { + LLSelectMgr::getInstance()->updateSelectionCenter(); + dialog_refresh_all(); + } + else if (allow_select_avatar && asAvatar()) + { + // Override any avatar position updates received + // Works only if avatar was repositioned using build + // tools and build floater is visible + LLSelectMgr::getInstance()->overrideAvatarUpdates(); + } + + + // Mark update time as approx. now, with the ping delay. + // Ping delay is off because it's not set for velocity interpolation, causing + // much jumping and hopping around... + +// U32 ping_delay = mesgsys->mCircuitInfo.getPingDelay(); + mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds(); + mLastMessageUpdateSecs = mLastInterpUpdateSecs; + if (mDrawable.notNull()) + { + // Don't clear invisibility flag on update if still orphaned! + if (mDrawable->isState(LLDrawable::FORCE_INVISIBLE) && !mOrphaned) + { +// LL_DEBUGS() << "Clearing force invisible: " << mID << ":" << getPCodeString() << ":" << getPositionAgent() << LL_ENDL; + mDrawable->clearState(LLDrawable::FORCE_INVISIBLE); + gPipeline.markRebuild( mDrawable, LLDrawable::REBUILD_ALL); + } + } + + // Update special hover cursor status + bool special_hover_cursor = specialHoverCursor(); + if (old_special_hover_cursor != special_hover_cursor + && mDrawable.notNull()) + { + mDrawable->updateSpecialHoverCursor(special_hover_cursor); + } + + return retval; +} + +bool LLViewerObject::isActive() const +{ + return true; +} + +//load flags from cache or from message +void LLViewerObject::loadFlags(U32 flags) +{ + if(flags == (U32)(-1)) + { + return; //invalid + } + + // keep local flags and overwrite remote-controlled flags + mFlags = (mFlags & FLAGS_LOCAL) | flags; + + // ...new objects that should come in selected need to be added to the selected list + mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0); + return; +} + +void LLViewerObject::idleUpdate(LLAgent &agent, const F64 &frame_time) +{ + if (!mDead) + { + if (!mStatic && sVelocityInterpolate && !isSelected()) + { + // calculate dt from last update + F32 time_dilation = mRegionp ? mRegionp->getTimeDilation() : 1.0f; + F32 dt_raw = ((F64Seconds)frame_time - mLastInterpUpdateSecs).value(); + F32 dt = time_dilation * dt_raw; + + applyAngularVelocity(dt); + + if (isAttachment()) + { + mLastInterpUpdateSecs = (F64Seconds)frame_time; + return; + } + else + { // Move object based on it's velocity and rotation + interpolateLinearMotion(frame_time, dt); + } + } + + updateDrawable(false); + } +} + + +// Move an object due to idle-time viewer side updates by interpolating motion +void LLViewerObject::interpolateLinearMotion(const F64SecondsImplicit& frame_time, const F32SecondsImplicit& dt_seconds) +{ + // linear motion + // PHYSICS_TIMESTEP is used below to correct for the fact that the velocity in object + // updates represents the average velocity of the last timestep, rather than the final velocity. + // the time dilation above should guarantee that dt is never less than PHYSICS_TIMESTEP, theoretically + // + // *TODO: should also wrap linear accel/velocity in check + // to see if object is selected, instead of explicitly + // zeroing it out + + F32 dt = dt_seconds; + F64Seconds time_since_last_update = frame_time - mLastMessageUpdateSecs; + if (time_since_last_update <= (F64Seconds)0.0 || dt <= 0.f) + { + return; + } + + LLVector3 accel = getAcceleration(); + LLVector3 vel = getVelocity(); + + if (sMaxUpdateInterpolationTime <= (F64Seconds)0.0) + { // Old code path ... unbounded, simple interpolation + if (!(accel.isExactlyZero() && vel.isExactlyZero())) + { + LLVector3 pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt; + + // region local + setPositionRegion(pos + getPositionRegion()); + setVelocity(vel + accel*dt); + + // for objects that are spinning but not translating, make sure to flag them as having moved + setChanged(MOVED | SILHOUETTE); + } + } + else if (!accel.isExactlyZero() || !vel.isExactlyZero()) // object is moving + { // Object is moving, and hasn't been too long since we got an update from the server + + // Calculate predicted position and velocity + LLVector3 new_pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt; + LLVector3 new_v = accel * dt; + + if (time_since_last_update > sPhaseOutUpdateInterpolationTime && + sPhaseOutUpdateInterpolationTime > (F64Seconds)0.0) + { // Haven't seen a viewer update in a while, check to see if the circuit is still active + if (mRegionp) + { // The simulator will NOT send updates if the object continues normally on the path + // predicted by the velocity and the acceleration (often gravity) sent to the viewer + // So check to see if the circuit is blocked, which means the sim is likely in a long lag + LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit( mRegionp->getHost() ); + if (cdp) + { + // Find out how many seconds since last packet arrived on the circuit + F64Seconds time_since_last_packet = LLMessageSystem::getMessageTimeSeconds() - cdp->getLastPacketInTime(); + + if (!cdp->isAlive() || // Circuit is dead or blocked + cdp->isBlocked() || // or doesn't seem to be getting any packets + (time_since_last_packet > sPhaseOutUpdateInterpolationTime)) + { + // Start to reduce motion interpolation since we haven't seen a server update in a while + F64Seconds time_since_last_interpolation = frame_time - mLastInterpUpdateSecs; + F64 phase_out = 1.0; + if (time_since_last_update > sMaxUpdateInterpolationTime) + { // Past the time limit, so stop the object + phase_out = 0.0; + //LL_INFOS() << "Motion phase out to zero" << LL_ENDL; + + // Kill angular motion as well. Note - not adding this due to paranoia + // about stopping rotation for llTargetOmega objects and not having it restart + // setAngularVelocity(LLVector3::zero); + } + else if (mLastInterpUpdateSecs - mLastMessageUpdateSecs > sPhaseOutUpdateInterpolationTime) + { // Last update was already phased out a bit + phase_out = (sMaxUpdateInterpolationTime - time_since_last_update) / + (sMaxUpdateInterpolationTime - time_since_last_interpolation); + //LL_INFOS() << "Continuing motion phase out of " << (F32) phase_out << LL_ENDL; + } + else + { // Phase out from full value + phase_out = (sMaxUpdateInterpolationTime - time_since_last_update) / + (sMaxUpdateInterpolationTime - sPhaseOutUpdateInterpolationTime); + //LL_INFOS() << "Starting motion phase out of " << (F32) phase_out << LL_ENDL; + } + phase_out = llclamp(phase_out, 0.0, 1.0); + + new_pos = new_pos * ((F32) phase_out); + new_v = new_v * ((F32) phase_out); + } + } + } + } + + new_pos = new_pos + getPositionRegion(); + new_v = new_v + vel; + + + // Clamp interpolated position to minimum underground and maximum region height + LLVector3d new_pos_global = mRegionp->getPosGlobalFromRegion(new_pos); + F32 min_height; + if (isAvatar()) + { // Make a better guess about AVs not going underground + min_height = LLWorld::getInstance()->resolveLandHeightGlobal(new_pos_global); + min_height += (0.5f * getScale().mV[VZ]); + } + else + { // This will put the object underground, but we can't tell if it will stop + // at ground level or not + min_height = LLWorld::getInstance()->getMinAllowedZ(this, new_pos_global); + // Cap maximum height + new_pos.mV[VZ] = llmin(LLWorld::getInstance()->getRegionMaxHeight(), new_pos.mV[VZ]); + } + + new_pos.mV[VZ] = llmax(min_height, new_pos.mV[VZ]); + + // Check to see if it's going off the region + LLVector3 temp(new_pos.mV[VX], new_pos.mV[VY], 0.f); + if (temp.clamp(0.f, mRegionp->getWidth())) + { // Going off this region, so see if we might end up on another region + LLVector3d old_pos_global = mRegionp->getPosGlobalFromRegion(getPositionRegion()); + new_pos_global = mRegionp->getPosGlobalFromRegion(new_pos); // Re-fetch in case it got clipped above + + // Clip the positions to known regions + LLVector3d clip_pos_global = LLWorld::getInstance()->clipToVisibleRegions(old_pos_global, new_pos_global); + if (clip_pos_global != new_pos_global) + { + // Was clipped, so this means we hit a edge where there is no region to enter + LLVector3 clip_pos = mRegionp->getPosRegionFromGlobal(clip_pos_global); + LL_DEBUGS("Interpolate") << "Hit empty region edge, clipped predicted position to " + << clip_pos + << " from " << new_pos << LL_ENDL; + new_pos = clip_pos; + + // Stop motion and get server update for bouncing on the edge + new_v.clear(); + setAcceleration(LLVector3::zero); + } + else + { + // Check for how long we are crossing. + // Note: theoretically we can find time from velocity, acceleration and + // distance from border to new position, but it is not going to work + // if 'phase_out' activates + if (mRegionCrossExpire == 0) + { + // Workaround: we can't accurately figure out time when we cross border + // so just write down time 'after the fact', it is far from optimal in + // case of lags, but for lags sMaxUpdateInterpolationTime will kick in first + LL_DEBUGS("Interpolate") << "Predicted region crossing, new position " << new_pos << LL_ENDL; + mRegionCrossExpire = frame_time + sMaxRegionCrossingInterpolationTime; + } + else if (frame_time > mRegionCrossExpire) + { + // Predicting crossing over 1s, stop motion + // Stop motion + LL_DEBUGS("Interpolate") << "Predicting region crossing for too long, stopping at " << new_pos << LL_ENDL; + new_v.clear(); + setAcceleration(LLVector3::zero); + mRegionCrossExpire = 0; + } + } + } + else + { + mRegionCrossExpire = 0; + } + + // Set new position and velocity + setPositionRegion(new_pos); + setVelocity(new_v); + + // for objects that are spinning but not translating, make sure to flag them as having moved + setChanged(MOVED | SILHOUETTE); + } + + // Update the last time we did anything + mLastInterpUpdateSecs = frame_time; +} + + + +// delete an item in the inventory, but don't tell the server. This is +// used internally by remove, update, and savescript. +// This will only delete the first item with an item_id in the list +void LLViewerObject::deleteInventoryItem(const LLUUID& item_id) +{ + if(mInventory) + { + LLInventoryObject::object_list_t::iterator it = mInventory->begin(); + LLInventoryObject::object_list_t::iterator end = mInventory->end(); + for( ; it != end; ++it ) + { + if((*it)->getUUID() == item_id) + { + // This is safe only because we return immediatly. + mInventory->erase(it); // will deref and delete it + return; + } + } + doInventoryCallback(); + } +} + +void LLViewerObject::doUpdateInventory( + LLPointer& item, + U8 key, + bool is_new) +{ + LLViewerInventoryItem* old_item = NULL; + if(TASK_INVENTORY_ITEM_KEY == key) + { + old_item = (LLViewerInventoryItem*)getInventoryObject(item->getUUID()); + } + else if(TASK_INVENTORY_ASSET_KEY == key) + { + old_item = getInventoryItemByAsset(item->getAssetUUID()); + } + LLUUID item_id; + LLUUID new_owner; + LLUUID new_group; + bool group_owned = false; + if(old_item) + { + item_id = old_item->getUUID(); + new_owner = old_item->getPermissions().getOwner(); + new_group = old_item->getPermissions().getGroup(); + group_owned = old_item->getPermissions().isGroupOwned(); + old_item = NULL; + } + else + { + item_id = item->getUUID(); + } + if(!is_new && mInventory) + { + // Attempt to update the local inventory. If we can get the + // object perm, we have perfect visibility, so we want the + // serial number to match. Otherwise, take our best guess and + // make sure that the serial number does not match. + deleteInventoryItem(item_id); + LLPermissions perm(item->getPermissions()); + LLPermissions* obj_perm = LLSelectMgr::getInstance()->findObjectPermissions(this); + bool is_atomic = (S32)LLAssetType::AT_OBJECT != item->getType(); + if(obj_perm) + { + perm.setOwnerAndGroup(LLUUID::null, obj_perm->getOwner(), obj_perm->getGroup(), is_atomic); + } + else + { + if(group_owned) + { + perm.setOwnerAndGroup(LLUUID::null, new_owner, new_group, is_atomic); + } + else if(!new_owner.isNull()) + { + // The object used to be in inventory, so we can + // assume the owner and group will match what they are + // there. + perm.setOwnerAndGroup(LLUUID::null, new_owner, new_group, is_atomic); + } + // *FIX: can make an even better guess by using the mPermGroup flags + else if(permYouOwner()) + { + // best guess. + perm.setOwnerAndGroup(LLUUID::null, gAgent.getID(), item->getPermissions().getGroup(), is_atomic); + --mExpectedInventorySerialNum; + } + else + { + // dummy it up. + perm.setOwnerAndGroup(LLUUID::null, LLUUID::null, LLUUID::null, is_atomic); + --mExpectedInventorySerialNum; + } + } + LLViewerInventoryItem* oldItem = item; + LLViewerInventoryItem* new_item = new LLViewerInventoryItem(oldItem); + new_item->setPermissions(perm); + mInventory->push_front(new_item); + doInventoryCallback(); + ++mExpectedInventorySerialNum; + } + else if (is_new) + { + ++mExpectedInventorySerialNum; + } +} + +// save a script, which involves removing the old one, and rezzing +// in the new one. This method should be called with the asset id +// of the new and old script AFTER the bytecode has been saved. +void LLViewerObject::saveScript( + const LLViewerInventoryItem* item, + bool active, + bool is_new) +{ + /* + * XXXPAM Investigate not making this copy. Seems unecessary, but I'm unsure about the + * interaction with doUpdateInventory() called below. + */ + LL_DEBUGS() << "LLViewerObject::saveScript() " << item->getUUID() << " " << item->getAssetUUID() << LL_ENDL; + + LLPointer task_item = + new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(), + item->getAssetUUID(), item->getType(), + item->getInventoryType(), + item->getName(), item->getDescription(), + item->getSaleInfo(), item->getFlags(), + item->getCreationDate()); + task_item->setTransactionID(item->getTransactionID()); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RezScript); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); + msg->nextBlockFast(_PREHASH_UpdateBlock); + msg->addU32Fast(_PREHASH_ObjectLocalID, (mLocalID)); + U8 enabled = active; + msg->addBOOLFast(_PREHASH_Enabled, enabled); + msg->nextBlockFast(_PREHASH_InventoryBlock); + task_item->packMessage(msg); + msg->sendReliable(mRegionp->getHost()); + + // do the internal logic + doUpdateInventory(task_item, TASK_INVENTORY_ITEM_KEY, is_new); +} + +void LLViewerObject::moveInventory(const LLUUID& folder_id, + const LLUUID& item_id) +{ + LL_DEBUGS() << "LLViewerObject::moveInventory " << item_id << LL_ENDL; + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_MoveTaskInventory); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_FolderID, folder_id); + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addU32Fast(_PREHASH_LocalID, mLocalID); + msg->addUUIDFast(_PREHASH_ItemID, item_id); + msg->sendReliable(mRegionp->getHost()); + + LLInventoryObject* inv_obj = getInventoryObject(item_id); + if(inv_obj) + { + LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_obj; + if(!item->getPermissions().allowCopyBy(gAgent.getID())) + { + deleteInventoryItem(item_id); + ++mExpectedInventorySerialNum; + } + } +} + +void LLViewerObject::dirtyInventory() +{ + // If there aren't any LLVOInventoryListeners, we won't be + // able to update our mInventory when it comes back from the + // simulator, so we should not clear the inventory either. + if(mInventory && !mInventoryCallbacks.empty()) + { + mInventory->clear(); // will deref and delete entries + delete mInventory; + mInventory = NULL; + } + mInventoryDirty = true; +} + +void LLViewerObject::registerInventoryListener(LLVOInventoryListener* listener, void* user_data) +{ + LLInventoryCallbackInfo* info = new LLInventoryCallbackInfo; + info->mListener = listener; + info->mInventoryData = user_data; + mInventoryCallbacks.push_front(info); +} + +void LLViewerObject::removeInventoryListener(LLVOInventoryListener* listener) +{ + if (listener == NULL) + return; + for (callback_list_t::iterator iter = mInventoryCallbacks.begin(); + iter != mInventoryCallbacks.end(); ) + { + callback_list_t::iterator curiter = iter++; + LLInventoryCallbackInfo* info = *curiter; + if (info->mListener == listener) + { + delete info; + mInventoryCallbacks.erase(curiter); + break; + } + } +} + +bool LLViewerObject::isInventoryPending() +{ + return mInvRequestState != INVENTORY_REQUEST_STOPPED; +} + +void LLViewerObject::clearInventoryListeners() +{ + for_each(mInventoryCallbacks.begin(), mInventoryCallbacks.end(), DeletePointer()); + mInventoryCallbacks.clear(); +} + +bool LLViewerObject::hasInventoryListeners() +{ + return !mInventoryCallbacks.empty(); +} + +void LLViewerObject::requestInventory() +{ + if(mInventoryDirty && mInventory && !mInventoryCallbacks.empty()) + { + mInventory->clear(); // will deref and delete entries + delete mInventory; + mInventory = NULL; + } + + if(mInventory) + { + // inventory is either up to date or doesn't has a listener + // if it is dirty, leave it this way in case we gain a listener + doInventoryCallback(); + } + else + { + // since we are going to request it now + mInventoryDirty = false; + + // Note: throws away duplicate requests + fetchInventoryFromServer(); + } +} + +void LLViewerObject::fetchInventoryFromServer() +{ + if (!isInventoryPending()) + { + delete mInventory; + mInventory = NULL; + + // Results in processTaskInv + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RequestTaskInventory); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addU32Fast(_PREHASH_LocalID, mLocalID); + msg->sendReliable(mRegionp->getHost()); + + // This will get reset by doInventoryCallback or processTaskInv + mInvRequestState = INVENTORY_REQUEST_PENDING; + } +} + +void LLViewerObject::fetchInventoryDelayed(const F64 &time_seconds) +{ + // unless already waiting, drop previous request and shedule an update + if (mInvRequestState != INVENTORY_REQUEST_WAIT) + { + if (mInvRequestXFerId != 0) + { + // abort download. + gXferManager->abortRequestById(mInvRequestXFerId, -1); + mInvRequestXFerId = 0; + } + mInvRequestState = INVENTORY_REQUEST_WAIT; // affects isInventoryPending() + LLCoros::instance().launch("LLViewerObject::fetchInventoryDelayedCoro()", + boost::bind(&LLViewerObject::fetchInventoryDelayedCoro, mID, time_seconds)); + } +} + +//static +void LLViewerObject::fetchInventoryDelayedCoro(const LLUUID task_inv, const F64 time_seconds) +{ + llcoro::suspendUntilTimeout(time_seconds); + LLViewerObject *obj = gObjectList.findObject(task_inv); + if (obj) + { + // Might be good idea to prolong delay here in case expected serial changed. + // As it is, it will get a response with obsolete serial and will delay again. + + // drop waiting state to unlock isInventoryPending() + obj->mInvRequestState = INVENTORY_REQUEST_STOPPED; + obj->fetchInventoryFromServer(); + } +} + +LLControlAvatar *LLViewerObject::getControlAvatar() +{ + return getRootEdit()->mControlAvatar.get(); +} + +LLControlAvatar *LLViewerObject::getControlAvatar() const +{ + return getRootEdit()->mControlAvatar.get(); +} + +// Manage the control avatar state of a given object. +// Any object can be flagged as animated, but for performance reasons +// we don't want to incur the overhead of managing a control avatar +// unless this would have some user-visible consequence. That is, +// there should be at least one rigged mesh in the linkset. Operations +// that change the state of a linkset, such as linking or unlinking +// prims, can also mean that a control avatar needs to be added or +// removed. At the end, if there is a control avatar, we make sure +// that its animation state is current. +void LLViewerObject::updateControlAvatar() +{ + LLViewerObject *root = getRootEdit(); + bool is_animated_object = root->isAnimatedObject(); + bool has_control_avatar = getControlAvatar(); + if (!is_animated_object && !has_control_avatar) + { + return; + } + + // caller isn't supposed to operate on a dead object, + // avatar was already cleaned up + llassert(!isDead()); + + bool should_have_control_avatar = false; + if (is_animated_object) + { + bool any_rigged_mesh = root->isRiggedMesh(); + LLViewerObject::const_child_list_t& child_list = root->getChildren(); + for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); ++iter) + { + const LLViewerObject* child = *iter; + any_rigged_mesh = any_rigged_mesh || child->isRiggedMesh(); + } + should_have_control_avatar = is_animated_object && any_rigged_mesh; + } + + if (should_have_control_avatar && !has_control_avatar) + { + std::string vobj_name = llformat("Vol%p", root); + LL_DEBUGS("AnimatedObjects") << vobj_name << " calling linkControlAvatar()" << LL_ENDL; + root->linkControlAvatar(); + } + if (!should_have_control_avatar && has_control_avatar) + { + std::string vobj_name = llformat("Vol%p", root); + LL_DEBUGS("AnimatedObjects") << vobj_name << " calling unlinkControlAvatar()" << LL_ENDL; + root->unlinkControlAvatar(); + } + if (getControlAvatar()) + { + getControlAvatar()->updateAnimations(); + if (isSelected()) + { + LLSelectMgr::getInstance()->pauseAssociatedAvatars(); + } + } +} + +void LLViewerObject::linkControlAvatar() +{ + if (!getControlAvatar() && isRootEdit()) + { + LLVOVolume *volp = dynamic_cast(this); + if (!volp) + { + LL_WARNS() << "called with null or non-volume object" << LL_ENDL; + return; + } + mControlAvatar = LLControlAvatar::createControlAvatar(volp); + LL_DEBUGS("AnimatedObjects") << volp->getID() + << " created control av for " + << (S32) (1+volp->numChildren()) << " prims" << LL_ENDL; + } + LLControlAvatar *cav = getControlAvatar(); + if (cav) + { + cav->updateAttachmentOverrides(); + if (!cav->mPlaying) + { + cav->mPlaying = true; + //if (!cav->mRootVolp->isAnySelected()) + { + cav->updateVolumeGeom(); + cav->mRootVolp->recursiveMarkForUpdate(); + } + } + } + else + { + LL_WARNS() << "no control avatar found!" << LL_ENDL; + } +} + +void LLViewerObject::unlinkControlAvatar() +{ + if (getControlAvatar()) + { + getControlAvatar()->updateAttachmentOverrides(); + } + if (isRootEdit()) + { + // This will remove the entire linkset from the control avatar + if (mControlAvatar) + { + mControlAvatar->markForDeath(); + mControlAvatar = NULL; + } + } + // For non-root prims, removing from the linkset will + // automatically remove the control avatar connection. +} + +// virtual +bool LLViewerObject::isAnimatedObject() const +{ + return false; +} + +struct LLFilenameAndTask +{ + LLUUID mTaskID; + std::string mFilename; + + // for sequencing in case of multiple updates + S16 mSerial; +#ifdef _DEBUG + static S32 sCount; + LLFilenameAndTask() + { + ++sCount; + LL_DEBUGS() << "Constructing LLFilenameAndTask: " << sCount << LL_ENDL; + } + ~LLFilenameAndTask() + { + --sCount; + LL_DEBUGS() << "Destroying LLFilenameAndTask: " << sCount << LL_ENDL; + } +private: + LLFilenameAndTask(const LLFilenameAndTask& rhs); + const LLFilenameAndTask& operator=(const LLFilenameAndTask& rhs) const; +#endif +}; + +#ifdef _DEBUG +S32 LLFilenameAndTask::sCount = 0; +#endif + +// static +void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data) +{ + LLUUID task_id; + msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_TaskID, task_id); + LLViewerObject* object = gObjectList.findObject(task_id); + if (!object) + { + LL_WARNS() << "LLViewerObject::processTaskInv object " + << task_id << " does not exist." << LL_ENDL; + return; + } + + // we can receive multiple task updates simultaneously, make sure we will not rewrite newer with older update + S16 serial = 0; + msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, serial); + + if (serial == object->mInventorySerialNum + && serial < object->mExpectedInventorySerialNum) + { + // Loop Protection. + // We received same serial twice. + // Viewer did some changes to inventory that couldn't be saved server side + // or something went wrong to cause serial to be out of sync. + // Drop xfer and restart after some time, assign server's value as expected + LL_WARNS() << "Task inventory serial might be out of sync, server serial: " << serial << " client expected serial: " << object->mExpectedInventorySerialNum << LL_ENDL; + object->mExpectedInventorySerialNum = serial; + object->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_DESYNC); + } + else if (serial < object->mExpectedInventorySerialNum) + { + // Out of date message, record to current serial for loop protection, but do not load it + // Drop xfer and restart after some time + if (serial < object->mInventorySerialNum) + { + LL_WARNS() << "Task serial decreased. Potentially out of order packet or desync." << LL_ENDL; + } + object->mInventorySerialNum = serial; + object->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_OUTDATED); + } + else if (serial >= object->mExpectedInventorySerialNum) + { + LLFilenameAndTask* ft = new LLFilenameAndTask; + ft->mTaskID = task_id; + ft->mSerial = serial; + + // We received version we expected or newer. Load it. + object->mInventorySerialNum = ft->mSerial; + object->mExpectedInventorySerialNum = ft->mSerial; + + std::string unclean_filename; + msg->getStringFast(_PREHASH_InventoryData, _PREHASH_Filename, unclean_filename); + ft->mFilename = LLDir::getScrubbedFileName(unclean_filename); + + if (ft->mFilename.empty()) + { + LL_DEBUGS() << "Task has no inventory" << LL_ENDL; + // mock up some inventory to make a drop target. + if (object->mInventory) + { + object->mInventory->clear(); // will deref and delete it + } + else + { + object->mInventory = new LLInventoryObject::object_list_t(); + } + LLPointer obj; + obj = new LLInventoryObject(object->mID, LLUUID::null, + LLAssetType::AT_CATEGORY, + "Contents"); + object->mInventory->push_front(obj); + object->doInventoryCallback(); + delete ft; + return; + } + U64 new_id = gXferManager->requestFile(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ft->mFilename), + ft->mFilename, LL_PATH_CACHE, + object->mRegionp->getHost(), + true, + &LLViewerObject::processTaskInvFile, + (void**)ft, // This takes ownership of ft + LLXferManager::HIGH_PRIORITY); + if (object->mInvRequestState == INVENTORY_XFER) + { + if (new_id > 0 && new_id != object->mInvRequestXFerId) + { + // we started new download. + gXferManager->abortRequestById(object->mInvRequestXFerId, -1); + object->mInvRequestXFerId = new_id; + } + } + else + { + object->mInvRequestState = INVENTORY_XFER; + object->mInvRequestXFerId = new_id; + } + } +} + +void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code, LLExtStat ext_status) +{ + LLFilenameAndTask* ft = (LLFilenameAndTask*)user_data; + LLViewerObject* object = NULL; + + if (ft + && (0 == error_code) + && (object = gObjectList.findObject(ft->mTaskID)) + && ft->mSerial >= object->mInventorySerialNum) + { + object->mInventorySerialNum = ft->mSerial; + LL_DEBUGS() << "Receiving inventory task file for serial " << object->mInventorySerialNum << " taskid: " << ft->mTaskID << LL_ENDL; + if (ft->mSerial < object->mExpectedInventorySerialNum) + { + // User managed to change something while inventory was loading + LL_DEBUGS() << "Processing file that is potentially out of date for task: " << ft->mTaskID << LL_ENDL; + } + + if (object->loadTaskInvFile(ft->mFilename)) + { + + LLInventoryObject::object_list_t::iterator it = object->mInventory->begin(); + LLInventoryObject::object_list_t::iterator end = object->mInventory->end(); + std::list& pending_lst = object->mPendingInventoryItemsIDs; + + for (; it != end && pending_lst.size(); ++it) + { + LLViewerInventoryItem* item = dynamic_cast(it->get()); + if(item && item->getType() != LLAssetType::AT_CATEGORY) + { + std::list::iterator id_it = std::find(pending_lst.begin(), pending_lst.begin(), item->getAssetUUID()); + if (id_it != pending_lst.end()) + { + pending_lst.erase(id_it); + } + } + } + } + else + { + // MAINT-2597 - crash when trying to edit a no-mod object + // Somehow get an contents inventory response, but with an invalid stream (possibly 0 size?) + // Stated repro was specific to no-mod objects so failing without user interaction should be safe. + LL_WARNS() << "Trying to load invalid task inventory file. Ignoring file contents." << LL_ENDL; + } + } + else + { + // This Occurs When two requests were made, and the first one + // has already handled it. + LL_DEBUGS() << "Problem loading task inventory. Return code: " + << error_code << LL_ENDL; + } + delete ft; +} + +bool LLViewerObject::loadTaskInvFile(const std::string& filename) +{ + std::string filename_and_local_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, filename); + llifstream ifs(filename_and_local_path.c_str()); + if(ifs.good()) + { + U32 fail_count = 0; + char buffer[MAX_STRING]; /* Flawfinder: ignore */ + // *NOTE: This buffer size is hard coded into scanf() below. + char keyword[MAX_STRING]; /* Flawfinder: ignore */ + if(mInventory) + { + mInventory->clear(); // will deref and delete it + } + else + { + mInventory = new LLInventoryObject::object_list_t; + } + while(ifs.good()) + { + ifs.getline(buffer, MAX_STRING); + if (sscanf(buffer, " %254s", keyword) == EOF) /* Flawfinder: ignore */ + { + // Blank file? + LL_WARNS() << "Issue reading from file '" + << filename << "'" << LL_ENDL; + break; + } + else if(0 == strcmp("inv_item", keyword)) + { + LLPointer inv = new LLViewerInventoryItem; + inv->importLegacyStream(ifs); + mInventory->push_front(inv); + } + else if(0 == strcmp("inv_object", keyword)) + { + LLPointer inv = new LLInventoryObject; + inv->importLegacyStream(ifs); + inv->rename("Contents"); + mInventory->push_front(inv); + } + else if (fail_count >= MAX_INV_FILE_READ_FAILS) + { + LL_WARNS() << "Encountered too many unknowns while reading from file: '" + << filename << "'" << LL_ENDL; + break; + } + else + { + // Is there really a point to continue processing? We already failing to display full inventory + fail_count++; + LL_WARNS_ONCE() << "Unknown token while reading from inventory file. Token: '" + << keyword << "'" << LL_ENDL; + } + } + ifs.close(); + LLFile::remove(filename_and_local_path); + } + else + { + LL_WARNS() << "unable to load task inventory: " << filename_and_local_path + << LL_ENDL; + return false; + } + doInventoryCallback(); + + return true; +} + +void LLViewerObject::doInventoryCallback() +{ + for (callback_list_t::iterator iter = mInventoryCallbacks.begin(); + iter != mInventoryCallbacks.end(); ) + { + callback_list_t::iterator curiter = iter++; + LLInventoryCallbackInfo* info = *curiter; + if (info->mListener != NULL) + { + info->mListener->inventoryChanged(this, + mInventory, + mInventorySerialNum, + info->mInventoryData); + } + else + { + LL_INFOS() << "LLViewerObject::doInventoryCallback() deleting bad listener entry." << LL_ENDL; + delete info; + mInventoryCallbacks.erase(curiter); + } + } + + // release inventory loading state + mInvRequestXFerId = 0; + mInvRequestState = INVENTORY_REQUEST_STOPPED; +} + +void LLViewerObject::removeInventory(const LLUUID& item_id) +{ + // close associated floater properties + LLSD params; + params["id"] = item_id; + params["object"] = mID; + LLFloaterReg::hideInstance("item_properties", params); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RemoveTaskInventory); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addU32Fast(_PREHASH_LocalID, mLocalID); + msg->addUUIDFast(_PREHASH_ItemID, item_id); + msg->sendReliable(mRegionp->getHost()); + deleteInventoryItem(item_id); + ++mExpectedInventorySerialNum; +} + +bool LLViewerObject::isAssetInInventory(LLViewerInventoryItem* item, LLAssetType::EType type) +{ + bool result = false; + + if (item) + { + // For now mPendingInventoryItemsIDs only stores textures and materials + // but if it gets to store more types, it will need to verify type as well + // since null can be a shared default id and it is fine to need a null + // script and a null material simultaneously. + std::list::iterator begin = mPendingInventoryItemsIDs.begin(); + std::list::iterator end = mPendingInventoryItemsIDs.end(); + + bool is_fetching = std::find(begin, end, item->getAssetUUID()) != end; + + // null is the default asset for materials and default for scripts + // so need to check type as well + bool is_fetched = getInventoryItemByAsset(item->getAssetUUID(), type) != NULL; + + result = is_fetched || is_fetching; + } + + return result; +} + +void LLViewerObject::updateMaterialInventory(LLViewerInventoryItem* item, U8 key, bool is_new) +{ + if (!item) + { + return; + } + if (LLAssetType::AT_TEXTURE != item->getType() + && LLAssetType::AT_MATERIAL != item->getType()) + { + // Not supported + return; + } + + if (isAssetInInventory(item, item->getType())) + { + // already there + return; + } + + mPendingInventoryItemsIDs.push_back(item->getAssetUUID()); + updateInventory(item, key, is_new); +} + +void LLViewerObject::updateInventory( + LLViewerInventoryItem* item, + U8 key, + bool is_new) +{ + // This slices the object into what we're concerned about on the + // viewer. The simulator will take the permissions and transfer + // ownership. + LLPointer task_item = + new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(), + item->getAssetUUID(), item->getType(), + item->getInventoryType(), + item->getName(), item->getDescription(), + item->getSaleInfo(), + item->getFlags(), + item->getCreationDate()); + task_item->setTransactionID(item->getTransactionID()); + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_UpdateTaskInventory); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_UpdateData); + msg->addU32Fast(_PREHASH_LocalID, mLocalID); + msg->addU8Fast(_PREHASH_Key, key); + msg->nextBlockFast(_PREHASH_InventoryData); + task_item->packMessage(msg); + msg->sendReliable(mRegionp->getHost()); + + // do the internal logic + doUpdateInventory(task_item, key, is_new); +} + +void LLViewerObject::updateInventoryLocal(LLInventoryItem* item, U8 key) +{ + LLPointer task_item = + new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(), + item->getAssetUUID(), item->getType(), + item->getInventoryType(), + item->getName(), item->getDescription(), + item->getSaleInfo(), item->getFlags(), + item->getCreationDate()); + + // do the internal logic + const bool is_new = false; + doUpdateInventory(task_item, key, is_new); +} + +LLInventoryObject* LLViewerObject::getInventoryObject(const LLUUID& item_id) +{ + LLInventoryObject* rv = NULL; + if(mInventory) + { + LLInventoryObject::object_list_t::iterator it = mInventory->begin(); + LLInventoryObject::object_list_t::iterator end = mInventory->end(); + for ( ; it != end; ++it) + { + if((*it)->getUUID() == item_id) + { + rv = *it; + break; + } + } + } + return rv; +} + +LLInventoryItem* LLViewerObject::getInventoryItem(const LLUUID& item_id) +{ + LLInventoryObject* iobj = getInventoryObject(item_id); + if (!iobj || iobj->getType() == LLAssetType::AT_CATEGORY) + { + return NULL; + } + LLInventoryItem* item = dynamic_cast(iobj); + return item; +} + +void LLViewerObject::getInventoryContents(LLInventoryObject::object_list_t& objects) +{ + if(mInventory) + { + LLInventoryObject::object_list_t::iterator it = mInventory->begin(); + LLInventoryObject::object_list_t::iterator end = mInventory->end(); + for( ; it != end; ++it) + { + if ((*it)->getType() != LLAssetType::AT_CATEGORY) + { + objects.push_back(*it); + } + } + } +} + +LLInventoryObject* LLViewerObject::getInventoryRoot() +{ + if (!mInventory || !mInventory->size()) + { + return NULL; + } + return mInventory->back(); +} + +LLViewerInventoryItem* LLViewerObject::getInventoryItemByAsset(const LLUUID& asset_id) +{ + if (mInventoryDirty) + LL_WARNS() << "Peforming inventory lookup for object " << mID << " that has dirty inventory!" << LL_ENDL; + + LLViewerInventoryItem* rv = NULL; + if(mInventory) + { + LLViewerInventoryItem* item = NULL; + + LLInventoryObject::object_list_t::iterator it = mInventory->begin(); + LLInventoryObject::object_list_t::iterator end = mInventory->end(); + for( ; it != end; ++it) + { + LLInventoryObject* obj = *it; + if(obj->getType() != LLAssetType::AT_CATEGORY) + { + // *FIX: gank-ass down cast! + item = (LLViewerInventoryItem*)obj; + if(item->getAssetUUID() == asset_id) + { + rv = item; + break; + } + } + } + } + return rv; +} + +LLViewerInventoryItem* LLViewerObject::getInventoryItemByAsset(const LLUUID& asset_id, LLAssetType::EType type) +{ + if (mInventoryDirty) + LL_WARNS() << "Peforming inventory lookup for object " << mID << " that has dirty inventory!" << LL_ENDL; + + LLViewerInventoryItem* rv = NULL; + if (type == LLAssetType::AT_CATEGORY) + { + // Whatever called this shouldn't be trying to get a folder by asset + // categories don't have assets + llassert(0); + return rv; + } + + if (mInventory) + { + LLViewerInventoryItem* item = NULL; + + LLInventoryObject::object_list_t::iterator it = mInventory->begin(); + LLInventoryObject::object_list_t::iterator end = mInventory->end(); + for (; it != end; ++it) + { + LLInventoryObject* obj = *it; + if (obj->getType() == type) + { + // *FIX: gank-ass down cast! + item = (LLViewerInventoryItem*)obj; + if (item->getAssetUUID() == asset_id) + { + rv = item; + break; + } + } + } + } + return rv; +} + +void LLViewerObject::updateViewerInventoryAsset( + const LLViewerInventoryItem* item, + const LLUUID& new_asset) +{ + LLPointer task_item = + new LLViewerInventoryItem(item); + task_item->setAssetUUID(new_asset); + + // do the internal logic + doUpdateInventory(task_item, TASK_INVENTORY_ITEM_KEY, false); +} + +void LLViewerObject::setPixelAreaAndAngle(LLAgent &agent) +{ + if (getVolume()) + { //volumes calculate pixel area and angle per face + return; + } + + LLVector3 viewer_pos_agent = gAgentCamera.getCameraPositionAgent(); + LLVector3 pos_agent = getRenderPosition(); + + F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX]; + F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY]; + F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ]; + + F32 max_scale = getMaxScale(); + F32 mid_scale = getMidScale(); + F32 min_scale = getMinScale(); + + // IW: estimate - when close to large objects, computing range based on distance from center is no good + // to try to get a min distance from face, subtract min_scale/2 from the range. + // This means we'll load too much detail sometimes, but that's better than not enough + // I don't think there's a better way to do this without calculating distance per-poly + F32 range = sqrt(dx*dx + dy*dy + dz*dz) - min_scale/2; + + LLViewerCamera* camera = LLViewerCamera::getInstance(); + if (range < 0.001f || isHUDAttachment()) // range == zero + { + mAppAngle = 180.f; + mPixelArea = (F32)camera->getScreenPixelArea(); + } + else + { + mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG; + + F32 pixels_per_meter = camera->getPixelMeterRatio() / range; + + mPixelArea = (pixels_per_meter * max_scale) * (pixels_per_meter * mid_scale); + if (mPixelArea > camera->getScreenPixelArea()) + { + mAppAngle = 180.f; + mPixelArea = (F32)camera->getScreenPixelArea(); + } + } +} + +bool LLViewerObject::updateLOD() +{ + return false; +} + +bool LLViewerObject::updateGeometry(LLDrawable *drawable) +{ + return false; +} + +void LLViewerObject::updateGL() +{ + +} + +void LLViewerObject::updateFaceSize(S32 idx) +{ + +} + +LLDrawable* LLViewerObject::createDrawable(LLPipeline *pipeline) +{ + return NULL; +} + +void LLViewerObject::setScale(const LLVector3 &scale, bool damped) +{ + LLPrimitive::setScale(scale); + if (mDrawable.notNull()) + { + //encompass completely sheared objects by taking + //the most extreme point possible (<1,1,0.5>) + mDrawable->setRadius(LLVector3(1,1,0.5f).scaleVec(scale).magVec()); + updateDrawable(damped); + } + + if( (LL_PCODE_VOLUME == getPCode()) && !isDead() ) + { + if (permYouOwner() || (scale.magVecSquared() > (7.5f * 7.5f)) ) + { + if (!mOnMap) + { + llassert_always(LLWorld::getInstance()->getRegionFromHandle(getRegion()->getHandle())); + + gObjectList.addToMap(this); + mOnMap = true; + } + } + else + { + if (mOnMap) + { + gObjectList.removeFromMap(this); + mOnMap = false; + } + } + } +} + +void LLViewerObject::setObjectCostStale() +{ + mCostStale = true; + // *NOTE: This is harmlessly redundant for Blinn-Phong material updates, as + // the root prim currently gets set stale anyway due to other property + // updates. But it is needed for GLTF material ID updates. + // -Cosmic,2023-06-27 + getRootEdit()->mCostStale = true; +} + +void LLViewerObject::setObjectCost(F32 cost) +{ + mObjectCost = cost; + mCostStale = false; + + if (isSelected()) + { + gFloaterTools->dirty(); + } +} + +void LLViewerObject::setLinksetCost(F32 cost) +{ + mLinksetCost = cost; + mCostStale = false; + + bool needs_refresh = isSelected(); + child_list_t::iterator iter = mChildList.begin(); + while(iter != mChildList.end() && !needs_refresh) + { + LLViewerObject* child = *iter; + needs_refresh = child->isSelected(); + iter++; + } + + if (needs_refresh) + { + gFloaterTools->dirty(); + } +} + +void LLViewerObject::setPhysicsCost(F32 cost) +{ + mPhysicsCost = cost; + mCostStale = false; + + if (isSelected()) + { + gFloaterTools->dirty(); + } +} + +void LLViewerObject::setLinksetPhysicsCost(F32 cost) +{ + mLinksetPhysicsCost = cost; + mCostStale = false; + + if (isSelected()) + { + gFloaterTools->dirty(); + } +} + + +F32 LLViewerObject::getObjectCost() +{ + if (mCostStale) + { + gObjectList.updateObjectCost(this); + } + + return mObjectCost; +} + +F32 LLViewerObject::getLinksetCost() +{ + if (mCostStale) + { + gObjectList.updateObjectCost(this); + } + + return mLinksetCost; +} + +F32 LLViewerObject::getPhysicsCost() +{ + if (mCostStale) + { + gObjectList.updateObjectCost(this); + } + + return mPhysicsCost; +} + +F32 LLViewerObject::getLinksetPhysicsCost() +{ + if (mCostStale) + { + gObjectList.updateObjectCost(this); + } + + return mLinksetPhysicsCost; +} + +F32 LLViewerObject::recursiveGetEstTrianglesMax() const +{ + F32 est_tris = getEstTrianglesMax(); + for (child_list_t::const_iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + const LLViewerObject* child = *iter; + if (!child->isAvatar()) + { + est_tris += child->recursiveGetEstTrianglesMax(); + } + } + return est_tris; +} + +S32 LLViewerObject::getAnimatedObjectMaxTris() const +{ + S32 max_tris = 0; + if (gAgent.getRegion()) + { + LLSD features; + gAgent.getRegion()->getSimulatorFeatures(features); + if (features.has("AnimatedObjects")) + { + max_tris = features["AnimatedObjects"]["AnimatedObjectMaxTris"].asInteger(); + } + } + return max_tris; +} + +F32 LLViewerObject::getEstTrianglesMax() const +{ + return 0.f; +} + +F32 LLViewerObject::getEstTrianglesStreamingCost() const +{ + return 0.f; +} + +// virtual +F32 LLViewerObject::getStreamingCost() const +{ + return 0.f; +} + +// virtual +bool LLViewerObject::getCostData(LLMeshCostData& costs) const +{ + costs = LLMeshCostData(); + return false; +} + +U32 LLViewerObject::getTriangleCount(S32* vcount) const +{ + return 0; +} + +U32 LLViewerObject::getHighLODTriangleCount() +{ + return 0; +} + +U32 LLViewerObject::recursiveGetTriangleCount(S32* vcount) const +{ + S32 total_tris = getTriangleCount(vcount); + LLViewerObject::const_child_list_t& child_list = getChildren(); + for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); ++iter) + { + LLViewerObject* childp = *iter; + if (childp) + { + total_tris += childp->getTriangleCount(vcount); + } + } + return total_tris; +} + +// This is using the stored surface area for each volume (which +// defaults to 1.0 for the case of everything except a sculpt) and +// then scaling it linearly based on the largest dimension in the +// prim's scale. Should revisit at some point. +F32 LLViewerObject::recursiveGetScaledSurfaceArea() const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + F32 area = 0.f; + const LLDrawable* drawable = mDrawable; + if (drawable) + { + const LLVOVolume* volume = drawable->getVOVolume(); + if (volume) + { + if (volume->getVolume()) + { + const LLVector3& scale = volume->getScale(); + area += volume->getVolume()->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]); + } + LLViewerObject::const_child_list_t children = volume->getChildren(); + for (LLViewerObject::const_child_list_t::const_iterator child_iter = children.begin(); + child_iter != children.end(); + ++child_iter) + { + LLViewerObject* child_obj = *child_iter; + LLVOVolume *child = dynamic_cast( child_obj ); + if (child && child->getVolume()) + { + const LLVector3& scale = child->getScale(); + area += child->getVolume()->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]); + } + } + } + } + return area; +} + +void LLViewerObject::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax) +{ + LLVector4a center; + center.load3(getRenderPosition().mV); + LLVector4a size; + size.load3(getScale().mV); + newMin.setSub(center, size); + newMax.setAdd(center, size); + + mDrawable->setPositionGroup(center); +} + +F32 LLViewerObject::getBinRadius() +{ + if (mDrawable.notNull()) + { + const LLVector4a* ext = mDrawable->getSpatialExtents(); + LLVector4a diff; + diff.setSub(ext[1], ext[0]); + return diff.getLength3().getF32(); + } + + return getScale().magVec(); +} + +F32 LLViewerObject::getMaxScale() const +{ + return llmax(getScale().mV[VX],getScale().mV[VY], getScale().mV[VZ]); +} + +F32 LLViewerObject::getMinScale() const +{ + return llmin(getScale().mV[0],getScale().mV[1],getScale().mV[2]); +} + +F32 LLViewerObject::getMidScale() const +{ + if (getScale().mV[VX] < getScale().mV[VY]) + { + if (getScale().mV[VY] < getScale().mV[VZ]) + { + return getScale().mV[VY]; + } + else if (getScale().mV[VX] < getScale().mV[VZ]) + { + return getScale().mV[VZ]; + } + else + { + return getScale().mV[VX]; + } + } + else if (getScale().mV[VX] < getScale().mV[VZ]) + { + return getScale().mV[VX]; + } + else if (getScale().mV[VY] < getScale().mV[VZ]) + { + return getScale().mV[VZ]; + } + else + { + return getScale().mV[VY]; + } +} + + +void LLViewerObject::updateTextures() +{ +} + +void LLViewerObject::boostTexturePriority(bool boost_children /* = true */) +{ + if (isDead() || !getVolume()) + { + return; + } + + S32 i; + S32 tex_count = getNumTEs(); + for (i = 0; i < tex_count; i++) + { + getTEImage(i)->setBoostLevel(LLGLTexture::BOOST_SELECTED); + } + + if (isSculpted() && !isMesh()) + { + LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT); + LLUUID sculpt_id = sculpt_params->getSculptTexture(); + LLViewerTextureManager::getFetchedTexture(sculpt_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)->setBoostLevel(LLGLTexture::BOOST_SELECTED); + } + + if (boost_children) + { + for (child_list_t::iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* child = *iter; + child->boostTexturePriority(); + } + } +} + +void LLViewerObject::setLineWidthForWindowSize(S32 window_width) +{ + if (window_width < 700) + { + LLUI::setLineWidth(2.0f); + } + else if (window_width < 1100) + { + LLUI::setLineWidth(3.0f); + } + else if (window_width < 2000) + { + LLUI::setLineWidth(4.0f); + } + else + { + // _damn_, what a nice monitor! + LLUI::setLineWidth(5.0f); + } +} + +void LLViewerObject::increaseArrowLength() +{ +/* ??? + if (mAxisArrowLength == 50) + { + mAxisArrowLength = 100; + } + else + { + mAxisArrowLength = 150; + } +*/ +} + + +void LLViewerObject::decreaseArrowLength() +{ +/* ??? + if (mAxisArrowLength == 150) + { + mAxisArrowLength = 100; + } + else + { + mAxisArrowLength = 50; + } +*/ +} + +// Culled from newsim LLTask::addNVPair +void LLViewerObject::addNVPair(const std::string& data) +{ + // cout << "LLViewerObject::addNVPair() with ---" << data << "---" << endl; + LLNameValue *nv = new LLNameValue(data.c_str()); + +// char splat[MAX_STRING]; +// temp->printNameValue(splat); +// LL_INFOS() << "addNVPair " << splat << LL_ENDL; + + name_value_map_t::iterator iter = mNameValuePairs.find(nv->mName); + if (iter != mNameValuePairs.end()) + { + LLNameValue* foundnv = iter->second; + if (foundnv->mClass != NVC_READ_ONLY) + { + delete foundnv; + mNameValuePairs.erase(iter); + } + else + { + delete nv; +// LL_INFOS() << "Trying to write to Read Only NVPair " << temp->mName << " in addNVPair()" << LL_ENDL; + return; + } + } + mNameValuePairs[nv->mName] = nv; +} + +bool LLViewerObject::removeNVPair(const std::string& name) +{ + char* canonical_name = gNVNameTable.addString(name); + + LL_DEBUGS() << "LLViewerObject::removeNVPair(): " << name << LL_ENDL; + + name_value_map_t::iterator iter = mNameValuePairs.find(canonical_name); + if (iter != mNameValuePairs.end()) + { + if( mRegionp ) + { + LLNameValue* nv = iter->second; +/* + std::string buffer = nv->printNameValue(); + gMessageSystem->newMessageFast(_PREHASH_RemoveNameValuePair); + gMessageSystem->nextBlockFast(_PREHASH_TaskData); + gMessageSystem->addUUIDFast(_PREHASH_ID, mID); + + gMessageSystem->nextBlockFast(_PREHASH_NameValueData); + gMessageSystem->addStringFast(_PREHASH_NVPair, buffer); + + gMessageSystem->sendReliable( mRegionp->getHost() ); +*/ + // Remove the NV pair from the local list. + delete nv; + mNameValuePairs.erase(iter); + return true; + } + else + { + LL_DEBUGS() << "removeNVPair - No region for object" << LL_ENDL; + } + } + return false; +} + + +LLNameValue *LLViewerObject::getNVPair(const std::string& name) const +{ + char *canonical_name; + + canonical_name = gNVNameTable.addString(name); + + // If you access a map with a name that isn't in it, it will add the name and a null pointer. + // So first check if the data is in the map. + name_value_map_t::const_iterator iter = mNameValuePairs.find(canonical_name); + if (iter != mNameValuePairs.end()) + { + return iter->second; + } + else + { + return NULL; + } +} + +void LLViewerObject::updatePositionCaches() const +{ + // If region is removed from the list it is also deleted. + if(mRegionp && LLWorld::instance().isRegionListed(mRegionp)) + { + if (!isRoot()) + { + mPositionRegion = ((LLViewerObject *)getParent())->getPositionRegion() + getPosition() * getParent()->getRotation(); + mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion); + } + else + { + mPositionRegion = getPosition(); + mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion); + } + } +} + +const LLVector3d LLViewerObject::getPositionGlobal() const +{ + // If region is removed from the list it is also deleted. + if(mRegionp && LLWorld::instance().isRegionListed(mRegionp)) + { + LLVector3d position_global = mRegionp->getPosGlobalFromRegion(getPositionRegion()); + + if (isAttachment()) + { + position_global = gAgent.getPosGlobalFromAgent(getRenderPosition()); + } + return position_global; + } + else + { + LLVector3d position_global(getPosition()); + return position_global; + } +} + +const LLVector3 &LLViewerObject::getPositionAgent() const +{ + // If region is removed from the list it is also deleted. + if(mRegionp && LLWorld::instance().isRegionListed(mRegionp)) + { + if (mDrawable.notNull() && (!mDrawable->isRoot() && getParent())) + { + // Don't return cached position if you have a parent, recalc (until all dirtying is done correctly. + LLVector3 position_region; + position_region = ((LLViewerObject *)getParent())->getPositionRegion() + getPosition() * getParent()->getRotation(); + mPositionAgent = mRegionp->getPosAgentFromRegion(position_region); + } + else + { + mPositionAgent = mRegionp->getPosAgentFromRegion(getPosition()); + } + } + return mPositionAgent; +} + +const LLVector3 &LLViewerObject::getPositionRegion() const +{ + if (!isRoot()) + { + LLViewerObject *parent = (LLViewerObject *)getParent(); + mPositionRegion = parent->getPositionRegion() + (getPosition() * parent->getRotation()); + } + else + { + mPositionRegion = getPosition(); + } + + return mPositionRegion; +} + +const LLVector3 LLViewerObject::getPositionEdit() const +{ + if (isRootEdit()) + { + return getPosition(); + } + else + { + LLViewerObject *parent = (LLViewerObject *)getParent(); + LLVector3 position_edit = parent->getPositionEdit() + getPosition() * parent->getRotationEdit(); + return position_edit; + } +} + +const LLVector3 LLViewerObject::getRenderPosition() const +{ + if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED)) + { + LLControlAvatar *cav = getControlAvatar(); + if (isRoot() && cav) + { + F32 fixup; + if ( cav->hasPelvisFixup( fixup) ) + { + //Apply a pelvis fixup (as defined by the avs skin) + LLVector3 pos = mDrawable->getPositionAgent(); + pos[VZ] += fixup; + return pos; + } + } + LLVOAvatar* avatar = getAvatar(); + if ((avatar) && !getControlAvatar()) + { + return avatar->getPositionAgent(); + } + } + + if (mDrawable.isNull() || mDrawable->getGeneration() < 0) + { + return getPositionAgent(); + } + else + { + return mDrawable->getPositionAgent(); + } +} + +const LLVector3 LLViewerObject::getPivotPositionAgent() const +{ + return getRenderPosition(); +} + +const LLQuaternion LLViewerObject::getRenderRotation() const +{ + LLQuaternion ret; + if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED) && !isAnimatedObject()) + { + return ret; + } + + if (mDrawable.isNull() || mDrawable->isStatic()) + { + ret = getRotationEdit(); + } + else + { + if (!mDrawable->isRoot()) + { + ret = getRotation() * LLQuaternion(mDrawable->getParent()->getWorldMatrix()); + } + else + { + ret = LLQuaternion(mDrawable->getWorldMatrix()); + } + } + + return ret; +} + +const LLMatrix4 LLViewerObject::getRenderMatrix() const +{ + return mDrawable->getWorldMatrix(); +} + +const LLQuaternion LLViewerObject::getRotationRegion() const +{ + LLQuaternion global_rotation = getRotation(); + if (!((LLXform *)this)->isRoot()) + { + global_rotation = global_rotation * getParent()->getRotation(); + } + return global_rotation; +} + +const LLQuaternion LLViewerObject::getRotationEdit() const +{ + LLQuaternion global_rotation = getRotation(); + if (!((LLXform *)this)->isRootEdit()) + { + global_rotation = global_rotation * getParent()->getRotation(); + } + return global_rotation; +} + +void LLViewerObject::setPositionAbsoluteGlobal( const LLVector3d &pos_global, bool damped ) +{ + if (isAttachment()) + { + LLVector3 new_pos = mRegionp->getPosRegionFromGlobal(pos_global); + if (isRootEdit()) + { + new_pos -= mDrawable->mXform.getParent()->getWorldPosition(); + LLQuaternion world_rotation = mDrawable->mXform.getParent()->getWorldRotation(); + new_pos = new_pos * ~world_rotation; + } + else + { + LLViewerObject* parentp = (LLViewerObject*)getParent(); + new_pos -= parentp->getPositionAgent(); + new_pos = new_pos * ~parentp->getRotationRegion(); + } + LLViewerObject::setPosition(new_pos); + + if (mParent && ((LLViewerObject*)mParent)->isAvatar()) + { + // we have changed the position of an attachment, so we need to clamp it + LLVOAvatar *avatar = (LLVOAvatar*)mParent; + + avatar->clampAttachmentPositions(); + } + } + else + { + if( isRoot() ) + { + setPositionRegion(mRegionp->getPosRegionFromGlobal(pos_global)); + } + else + { + // the relative position with the parent is not constant + LLViewerObject* parent = (LLViewerObject *)getParent(); + //RN: this assumes we are only calling this function from the edit tools + gPipeline.updateMoveNormalAsync(parent->mDrawable); + + LLVector3 pos_local = mRegionp->getPosRegionFromGlobal(pos_global) - parent->getPositionRegion(); + pos_local = pos_local * ~parent->getRotationRegion(); + LLViewerObject::setPosition( pos_local ); + } + } + //RN: assumes we always want to snap the object when calling this function + gPipeline.updateMoveNormalAsync(mDrawable); +} + +void LLViewerObject::setPosition(const LLVector3 &pos, bool damped) +{ + if (getPosition() != pos) + { + setChanged(TRANSLATED | SILHOUETTE); + } + + LLXform::setPosition(pos); + updateDrawable(damped); + if (isRoot()) + { + // position caches need to be up to date on root objects + updatePositionCaches(); + } +} + +void LLViewerObject::setPositionGlobal(const LLVector3d &pos_global, bool damped) +{ + if (isAttachment()) + { + if (isRootEdit()) + { + LLVector3 newPos = mRegionp->getPosRegionFromGlobal(pos_global); + newPos = newPos - mDrawable->mXform.getParent()->getWorldPosition(); + + LLQuaternion invWorldRotation = mDrawable->mXform.getParent()->getWorldRotation(); + invWorldRotation.transQuat(); + + newPos = newPos * invWorldRotation; + LLViewerObject::setPosition(newPos); + } + else + { + // assumes parent is root editable (root of attachment) + LLVector3 newPos = mRegionp->getPosRegionFromGlobal(pos_global); + newPos = newPos - mDrawable->mXform.getParent()->getWorldPosition(); + LLVector3 delta_pos = newPos - getPosition(); + + LLQuaternion invRotation = mDrawable->getRotation(); + invRotation.transQuat(); + + delta_pos = delta_pos * invRotation; + + // *FIX: is this right? Shouldn't we be calling the + // LLViewerObject version of setPosition? + LLVector3 old_pos = mDrawable->mXform.getParent()->getPosition(); + mDrawable->mXform.getParent()->setPosition(old_pos + delta_pos); + setChanged(TRANSLATED | SILHOUETTE); + } + if (mParent && ((LLViewerObject*)mParent)->isAvatar()) + { + // we have changed the position of an attachment, so we need to clamp it + LLVOAvatar *avatar = (LLVOAvatar*)mParent; + + avatar->clampAttachmentPositions(); + } + } + else + { + if (isRoot()) + { + setPositionRegion(mRegionp->getPosRegionFromGlobal(pos_global)); + } + else + { + // the relative position with the parent is constant, but the parent's position needs to be changed + LLVector3d position_offset; + position_offset.setVec(getPosition()*getParent()->getRotation()); + LLVector3d new_pos_global = pos_global - position_offset; + ((LLViewerObject *)getParent())->setPositionGlobal(new_pos_global); + } + } + updateDrawable(damped); +} + + +void LLViewerObject::setPositionParent(const LLVector3 &pos_parent, bool damped) +{ + // Set position relative to parent, if no parent, relative to region + if (!isRoot()) + { + LLViewerObject::setPosition(pos_parent, damped); + //updateDrawable(damped); + } + else + { + setPositionRegion(pos_parent, damped); + } +} + +void LLViewerObject::setPositionRegion(const LLVector3 &pos_region, bool damped) +{ + if (!isRootEdit()) + { + LLViewerObject* parent = (LLViewerObject*) getParent(); + LLViewerObject::setPosition((pos_region-parent->getPositionRegion())*~parent->getRotationRegion()); + } + else + { + LLViewerObject::setPosition(pos_region); + mPositionRegion = pos_region; + mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion); + } +} + +void LLViewerObject::setPositionAgent(const LLVector3 &pos_agent, bool damped) +{ + LLVector3 pos_region = getRegion()->getPosRegionFromAgent(pos_agent); + setPositionRegion(pos_region, damped); +} + +// identical to setPositionRegion() except it checks for child-joints +// and doesn't also move the joint-parent +// TODO -- implement similar intelligence for joint-parents toward +// their joint-children +void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, bool damped) +{ + if (!isRootEdit()) + { + // the relative position with the parent is constant, but the parent's position needs to be changed + LLVector3 position_offset = getPosition() * getParent()->getRotation(); + + ((LLViewerObject *)getParent())->setPositionEdit(pos_edit - position_offset); + updateDrawable(damped); + } + else + { + LLViewerObject::setPosition(pos_edit, damped); + mPositionRegion = pos_edit; + mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion); + } +} + + +LLViewerObject* LLViewerObject::getRootEdit() const +{ + const LLViewerObject* root = this; + while (root->mParent + && !((LLViewerObject*)root->mParent)->isAvatar()) + { + root = (LLViewerObject*)root->mParent; + } + return (LLViewerObject*)root; +} + + +bool LLViewerObject::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, + S32 face, + bool pick_transparent, + bool pick_rigged, + bool pick_unselectable, + S32* face_hit, + LLVector4a* intersection, + LLVector2* tex_coord, + LLVector4a* normal, + LLVector4a* tangent) +{ + return false; +} + +bool LLViewerObject::lineSegmentBoundingBox(const LLVector4a& start, const LLVector4a& end) +{ + if (mDrawable.isNull() || mDrawable->isDead()) + { + return false; + } + + const LLVector4a* ext = mDrawable->getSpatialExtents(); + + //VECTORIZE THIS + LLVector4a center; + center.setAdd(ext[1], ext[0]); + center.mul(0.5f); + LLVector4a size; + size.setSub(ext[1], ext[0]); + size.mul(0.5f); + + return LLLineSegmentBoxIntersect(start, end, center, size); +} + +U8 LLViewerObject::getMediaType() const +{ + if (mMedia) + { + return mMedia->mMediaType; + } + else + { + return LLViewerObject::MEDIA_NONE; + } +} + +void LLViewerObject::setMediaType(U8 media_type) +{ + if (!mMedia) + { + // TODO what if we don't have a media pointer? + } + else if (mMedia->mMediaType != media_type) + { + mMedia->mMediaType = media_type; + + // TODO: update materials with new image + } +} + +std::string LLViewerObject::getMediaURL() const +{ + if (mMedia) + { + return mMedia->mMediaURL; + } + else + { + return std::string(); + } +} + +void LLViewerObject::setMediaURL(const std::string& media_url) +{ + if (!mMedia) + { + mMedia = new LLViewerObjectMedia; + mMedia->mMediaURL = media_url; + mMedia->mPassedWhitelist = false; + + // TODO: update materials with new image + } + else if (mMedia->mMediaURL != media_url) + { + mMedia->mMediaURL = media_url; + mMedia->mPassedWhitelist = false; + + // TODO: update materials with new image + } +} + +bool LLViewerObject::getMediaPassedWhitelist() const +{ + if (mMedia) + { + return mMedia->mPassedWhitelist; + } + else + { + return false; + } +} + +void LLViewerObject::setMediaPassedWhitelist(bool passed) +{ + if (mMedia) + { + mMedia->mPassedWhitelist = passed; + } +} + +bool LLViewerObject::setMaterial(const U8 material) +{ + bool res = LLPrimitive::setMaterial(material); + if (res) + { + setChanged(TEXTURE); + } + return res; +} + +void LLViewerObject::setNumTEs(const U8 num_tes) +{ + U32 i; + if (num_tes != getNumTEs()) + { + if (num_tes) + { + LLPointer *new_images; + new_images = new LLPointer[num_tes]; + + LLPointer *new_normmaps; + new_normmaps = new LLPointer[num_tes]; + + LLPointer *new_specmaps; + new_specmaps = new LLPointer[num_tes]; + for (i = 0; i < num_tes; i++) + { + if (i < getNumTEs()) + { + new_images[i] = mTEImages[i]; + new_normmaps[i] = mTENormalMaps[i]; + new_specmaps[i] = mTESpecularMaps[i]; + } + else if (getNumTEs()) + { + new_images[i] = mTEImages[getNumTEs()-1]; + new_normmaps[i] = mTENormalMaps[getNumTEs()-1]; + new_specmaps[i] = mTESpecularMaps[getNumTEs()-1]; + } + else + { + new_images[i] = NULL; + new_normmaps[i] = NULL; + new_specmaps[i] = NULL; + } + } + + deleteTEImages(); + + mTEImages = new_images; + mTENormalMaps = new_normmaps; + mTESpecularMaps = new_specmaps; + } + else + { + deleteTEImages(); + } + + S32 original_tes = getNumTEs(); + + LLPrimitive::setNumTEs(num_tes); + setChanged(TEXTURE); + + // touch up GLTF materials + if (original_tes > 0) + { + for (int i = original_tes; i < getNumTEs(); ++i) + { + LLTextureEntry* src = getTE(original_tes - 1); + LLTextureEntry* tep = getTE(i); + setRenderMaterialID(i, getRenderMaterialID(original_tes - 1), false); + + if (tep) + { + LLGLTFMaterial* base_material = src->getGLTFMaterial(); + LLGLTFMaterial* override_material = src->getGLTFMaterialOverride(); + if (base_material && override_material) + { + tep->setGLTFMaterialOverride(new LLGLTFMaterial(*override_material)); + + LLGLTFMaterial* render_material = new LLFetchedGLTFMaterial(); + *render_material = *base_material; + render_material->applyOverride(*override_material); + tep->setGLTFRenderMaterial(render_material); + } + } + } + } + + if (mDrawable.notNull()) + { + gPipeline.markTextured(mDrawable); + } + } +} + +void LLViewerObject::sendMaterialUpdate() const +{ + LLViewerRegion* regionp = getRegion(); + if(!regionp) return; + gMessageSystem->newMessageFast(_PREHASH_ObjectMaterial); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID ); + gMessageSystem->addU8Fast(_PREHASH_Material, getMaterial() ); + gMessageSystem->sendReliable( regionp->getHost() ); + +} + +//formerly send_object_shape(LLViewerObject *object) +void LLViewerObject::sendShapeUpdate() +{ + gMessageSystem->newMessageFast(_PREHASH_ObjectShape); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID ); + + LLVolumeMessage::packVolumeParams(&getVolume()->getParams(), gMessageSystem); + + LLViewerRegion *regionp = getRegion(); + gMessageSystem->sendReliable( regionp->getHost() ); +} + + +void LLViewerObject::sendTEUpdate() const +{ + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ObjectImage); + + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addU32Fast(_PREHASH_ObjectLocalID, mLocalID ); + if (mMedia) + { + msg->addString("MediaURL", mMedia->mMediaURL); + } + else + { + msg->addString("MediaURL", NULL); + } + + // TODO send media type + + packTEMessage(msg); + + LLViewerRegion *regionp = getRegion(); + msg->sendReliable( regionp->getHost() ); +} + +LLViewerTexture* LLViewerObject::getBakedTextureForMagicId(const LLUUID& id) +{ + if (!LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(id)) + { + return NULL; + } + + LLViewerObject *root = getRootEdit(); + if (root && root->isAnimatedObject()) + { + return LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + } + + LLVOAvatar* avatar = getAvatar(); + if (avatar && !isHUDAttachment()) + { + LLAvatarAppearanceDefines::EBakedTextureIndex texIndex = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::assetIdToBakedTextureIndex(id); + LLViewerTexture* bakedTexture = avatar->getBakedTexture(texIndex); + if (bakedTexture == NULL || bakedTexture->isMissingAsset()) + { + return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + } + else + { + return bakedTexture; + } + } + else + { + return LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + } + +} + +void LLViewerObject::updateAvatarMeshVisibility(const LLUUID& id, const LLUUID& old_id) +{ + if (id == old_id) + { + return; + } + + if (!LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(old_id) && !LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(id)) + { + return; + } + + LLVOAvatar* avatar = getAvatar(); + if (avatar) + { + avatar->updateMeshVisibility(); + } +} + + +void LLViewerObject::setTE(const U8 te, const LLTextureEntry& texture_entry) +{ + LLUUID old_image_id; + if (getTE(te)) + { + old_image_id = getTE(te)->getID(); + } + + LLPrimitive::setTE(te, texture_entry); + + const LLUUID& image_id = getTE(te)->getID(); + LLViewerTexture* bakedTexture = getBakedTextureForMagicId(image_id); + mTEImages[te] = bakedTexture ? bakedTexture : LLViewerTextureManager::getFetchedTexture(image_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + + updateAvatarMeshVisibility(image_id, old_image_id); + + updateTEMaterialTextures(te); +} + +void LLViewerObject::updateTEMaterialTextures(U8 te) +{ + if (getTE(te)->getMaterialParams().notNull()) + { + const LLUUID& norm_id = getTE(te)->getMaterialParams()->getNormalID(); + mTENormalMaps[te] = LLViewerTextureManager::getFetchedTexture(norm_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + + const LLUUID& spec_id = getTE(te)->getMaterialParams()->getSpecularID(); + mTESpecularMaps[te] = LLViewerTextureManager::getFetchedTexture(spec_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + } + + LLFetchedGLTFMaterial* mat = (LLFetchedGLTFMaterial*) getTE(te)->getGLTFRenderMaterial(); + llassert(mat == nullptr || dynamic_cast(getTE(te)->getGLTFRenderMaterial()) != nullptr); + LLUUID mat_id = getRenderMaterialID(te); + if (mat == nullptr && mat_id.notNull()) + { + mat = (LLFetchedGLTFMaterial*) gGLTFMaterialList.getMaterial(mat_id); + llassert(mat == nullptr || dynamic_cast(gGLTFMaterialList.getMaterial(mat_id)) != nullptr); + if (mat->isFetching()) + { // material is not loaded yet, rebuild draw info when the object finishes loading + mat->onMaterialComplete([id=getID()] + { + LLViewerObject* obj = gObjectList.findObject(id); + if (obj) + { + obj->markForUpdate(); + } + }); + } + getTE(te)->setGLTFMaterial(mat); + } + else if (mat_id.isNull() && mat != nullptr) + { + mat = nullptr; + getTE(te)->setGLTFMaterial(nullptr); + } + + auto fetch_texture = [this](const LLUUID& id) + { + LLViewerFetchedTexture* img = nullptr; + if (id.notNull()) + { + if (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(id)) + { + // TODO -- fall back to LLTextureEntry::mGLTFRenderMaterial when overriding with baked texture + LLViewerTexture* viewerTexture = getBakedTextureForMagicId(id); + img = viewerTexture ? dynamic_cast(viewerTexture) : nullptr; + } + else + { + img = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + img->addTextureStats(64.f * 64.f, true); + } + } + + return img; + }; + + if (mat != nullptr) + { + mat->mBaseColorTexture = fetch_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR]); + mat->mNormalTexture = fetch_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL]); + mat->mMetallicRoughnessTexture = fetch_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS]); + mat->mEmissiveTexture= fetch_texture(mat->mTextureId[LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE]); + } +} + +void LLViewerObject::refreshBakeTexture() +{ + for (int face_index = 0; face_index < getNumTEs(); face_index++) + { + LLTextureEntry* tex_entry = getTE(face_index); + if (tex_entry && LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::isBakedImageId(tex_entry->getID())) + { + const LLUUID& image_id = tex_entry->getID(); + LLViewerTexture* bakedTexture = getBakedTextureForMagicId(image_id); + changeTEImage(face_index, bakedTexture); + } + } +} + +void LLViewerObject::setTEImage(const U8 te, LLViewerTexture *imagep) +{ + if (mTEImages[te] != imagep) + { + LLUUID old_image_id = getTE(te) ? getTE(te)->getID() : LLUUID::null; + + LLPrimitive::setTETexture(te, imagep->getID()); + + LLViewerTexture* baked_texture = getBakedTextureForMagicId(imagep->getID()); + mTEImages[te] = baked_texture ? baked_texture : imagep; + updateAvatarMeshVisibility(imagep->getID(), old_image_id); + setChanged(TEXTURE); + if (mDrawable.notNull()) + { + gPipeline.markTextured(mDrawable); + } + } +} + +S32 LLViewerObject::setTETextureCore(const U8 te, LLViewerTexture *image) +{ + LLUUID old_image_id = getTE(te)->getID(); + const LLUUID& uuid = image ? image->getID() : LLUUID::null; + S32 retval = 0; + if (uuid != getTE(te)->getID() || + uuid == LLUUID::null) + { + retval = LLPrimitive::setTETexture(te, uuid); + LLViewerTexture* baked_texture = getBakedTextureForMagicId(uuid); + mTEImages[te] = baked_texture ? baked_texture : image; + updateAvatarMeshVisibility(uuid,old_image_id); + setChanged(TEXTURE); + if (mDrawable.notNull()) + { + gPipeline.markTextured(mDrawable); + } + } + return retval; +} + +S32 LLViewerObject::setTENormalMapCore(const U8 te, LLViewerTexture *image) +{ + S32 retval = TEM_CHANGE_TEXTURE; + const LLUUID& uuid = image ? image->getID() : LLUUID::null; + if (uuid != getTE(te)->getID() || + uuid == LLUUID::null) + { + LLTextureEntry* tep = getTE(te); + LLMaterial* mat = NULL; + if (tep) + { + mat = tep->getMaterialParams(); + } + + if (mat) + { + mat->setNormalID(uuid); + } + } + changeTENormalMap(te,image); + return retval; +} + +S32 LLViewerObject::setTESpecularMapCore(const U8 te, LLViewerTexture *image) +{ + S32 retval = TEM_CHANGE_TEXTURE; + const LLUUID& uuid = image ? image->getID() : LLUUID::null; + if (uuid != getTE(te)->getID() || + uuid == LLUUID::null) + { + LLTextureEntry* tep = getTE(te); + LLMaterial* mat = NULL; + if (tep) + { + mat = tep->getMaterialParams(); + } + + if (mat) + { + mat->setSpecularID(uuid); + } + } + changeTESpecularMap(te, image); + return retval; +} + +//virtual +void LLViewerObject::changeTEImage(S32 index, LLViewerTexture* new_image) +{ + if(index < 0 || index >= getNumTEs()) + { + return ; + } + mTEImages[index] = new_image ; +} + +void LLViewerObject::changeTENormalMap(S32 index, LLViewerTexture* new_image) +{ + if(index < 0 || index >= getNumTEs()) + { + return ; + } + mTENormalMaps[index] = new_image ; + refreshMaterials(); +} + +void LLViewerObject::changeTESpecularMap(S32 index, LLViewerTexture* new_image) +{ + if(index < 0 || index >= getNumTEs()) + { + return ; + } + mTESpecularMaps[index] = new_image ; + refreshMaterials(); +} + +S32 LLViewerObject::setTETexture(const U8 te, const LLUUID& uuid) +{ + // Invalid host == get from the agent's sim + LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture( + uuid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); + return setTETextureCore(te, image); +} + +S32 LLViewerObject::setTENormalMap(const U8 te, const LLUUID& uuid) +{ + LLViewerFetchedTexture *image = (uuid == LLUUID::null) ? NULL : LLViewerTextureManager::getFetchedTexture( + uuid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); + return setTENormalMapCore(te, image); +} + +S32 LLViewerObject::setTESpecularMap(const U8 te, const LLUUID& uuid) +{ + LLViewerFetchedTexture *image = (uuid == LLUUID::null) ? NULL : LLViewerTextureManager::getFetchedTexture( + uuid, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost()); + return setTESpecularMapCore(te, image); +} + +S32 LLViewerObject::setTEColor(const U8 te, const LLColor3& color) +{ + return setTEColor(te, LLColor4(color)); +} + +S32 LLViewerObject::setTEColor(const U8 te, const LLColor4& color) +{ + S32 retval = 0; + const LLTextureEntry *tep = getTE(te); + if (!tep) + { + LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; + } + else if (color != tep->getColor()) + { + retval = LLPrimitive::setTEColor(te, color); + if (mDrawable.notNull() && retval) + { + // These should only happen on updates which are not the initial update. + dirtyMesh(); + } + } + return retval; +} + +S32 LLViewerObject::setTEBumpmap(const U8 te, const U8 bump) +{ + S32 retval = 0; + const LLTextureEntry *tep = getTE(te); + if (!tep) + { + LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; + } + else if (bump != tep->getBumpmap()) + { + retval = LLPrimitive::setTEBumpmap(te, bump); + setChanged(TEXTURE); + if (mDrawable.notNull() && retval) + { + gPipeline.markTextured(mDrawable); + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY); + } + } + return retval; +} + +S32 LLViewerObject::setTETexGen(const U8 te, const U8 texgen) +{ + S32 retval = 0; + const LLTextureEntry *tep = getTE(te); + if (!tep) + { + LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; + } + else if (texgen != tep->getTexGen()) + { + retval = LLPrimitive::setTETexGen(te, texgen); + setChanged(TEXTURE); + } + return retval; +} + +S32 LLViewerObject::setTEMediaTexGen(const U8 te, const U8 media) +{ + S32 retval = 0; + const LLTextureEntry *tep = getTE(te); + if (!tep) + { + LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; + } + else if (media != tep->getMediaTexGen()) + { + retval = LLPrimitive::setTEMediaTexGen(te, media); + setChanged(TEXTURE); + } + return retval; +} + +S32 LLViewerObject::setTEShiny(const U8 te, const U8 shiny) +{ + S32 retval = 0; + const LLTextureEntry *tep = getTE(te); + if (!tep) + { + LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; + } + else if (shiny != tep->getShiny()) + { + retval = LLPrimitive::setTEShiny(te, shiny); + setChanged(TEXTURE); + } + return retval; +} + +S32 LLViewerObject::setTEFullbright(const U8 te, const U8 fullbright) +{ + S32 retval = 0; + const LLTextureEntry *tep = getTE(te); + if (!tep) + { + LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; + } + else if (fullbright != tep->getFullbright()) + { + retval = LLPrimitive::setTEFullbright(te, fullbright); + setChanged(TEXTURE); + if (mDrawable.notNull() && retval) + { + gPipeline.markTextured(mDrawable); + } + } + return retval; +} + + +S32 LLViewerObject::setTEMediaFlags(const U8 te, const U8 media_flags) +{ + // this might need work for media type + S32 retval = 0; + const LLTextureEntry *tep = getTE(te); + if (!tep) + { + LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; + } + else if (media_flags != tep->getMediaFlags()) + { + retval = LLPrimitive::setTEMediaFlags(te, media_flags); + setChanged(TEXTURE); + if (mDrawable.notNull() && retval) + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); + gPipeline.markTextured(mDrawable); + } + } + return retval; +} + +S32 LLViewerObject::setTEGlow(const U8 te, const F32 glow) +{ + S32 retval = 0; + const LLTextureEntry *tep = getTE(te); + if (!tep) + { + LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; + } + else if (glow != tep->getGlow()) + { + retval = LLPrimitive::setTEGlow(te, glow); + setChanged(TEXTURE); + if (mDrawable.notNull() && retval) + { + gPipeline.markTextured(mDrawable); + } + } + return retval; +} + +S32 LLViewerObject::setTEMaterialID(const U8 te, const LLMaterialID& pMaterialID) +{ + S32 retval = 0; + const LLTextureEntry *tep = getTE(te); + if (!tep) + { + LL_WARNS("Material") << "No texture entry for te " << (S32)te + << ", object " << mID + << ", material " << pMaterialID + << LL_ENDL; + } + //else if (pMaterialID != tep->getMaterialID()) + { + LL_DEBUGS("Material") << "Changing texture entry for te " << (S32)te + << ", object " << mID + << ", material " << pMaterialID + << LL_ENDL; + retval = LLPrimitive::setTEMaterialID(te, pMaterialID); + refreshMaterials(); + } + return retval; +} + +S32 LLViewerObject::setTEMaterialParams(const U8 te, const LLMaterialPtr pMaterialParams) +{ + S32 retval = 0; + const LLTextureEntry *tep = getTE(te); + if (!tep) + { + LL_WARNS() << "No texture entry for te " << (S32)te << ", object " << mID << LL_ENDL; + return 0; + } + + retval = LLPrimitive::setTEMaterialParams(te, pMaterialParams); + LL_DEBUGS("Material") << "Changing material params for te " << (S32)te + << ", object " << mID + << " (" << retval << ")" + << LL_ENDL; + setTENormalMap(te, (pMaterialParams) ? pMaterialParams->getNormalID() : LLUUID::null); + setTESpecularMap(te, (pMaterialParams) ? pMaterialParams->getSpecularID() : LLUUID::null); + + return retval; +} + +S32 LLViewerObject::setTEGLTFMaterialOverride(U8 te, LLGLTFMaterial* override_mat) +{ + LL_PROFILE_ZONE_SCOPED; + S32 retval = TEM_CHANGE_NONE; + + LLTextureEntry* tep = getTE(te); + if (!tep) + { // this could happen if the object is not fully formed yet + // returning TEM_CHANGE_NONE here signals to LLGLTFMaterialList to queue the override for later + return retval; + } + + LLFetchedGLTFMaterial* src_mat = (LLFetchedGLTFMaterial*) tep->getGLTFMaterial(); + llassert(src_mat == nullptr || dynamic_cast(tep->getGLTFMaterial()) != nullptr); + // if override mat exists, we must also have a source mat + if (!src_mat) + { + // we can get into this state if an override has arrived before the viewer has + // received or handled an update, return TEM_CHANGE_NONE to signal to LLGLTFMaterialList that it + // should queue the update for later + return retval; + } + + if(src_mat->isFetching()) + { + // if still fetching, we need to wait until it is done and try again + return retval; + } + + retval = tep->setGLTFMaterialOverride(override_mat); + + if (retval) + { + if (override_mat) + { + LLFetchedGLTFMaterial* render_mat = new LLFetchedGLTFMaterial(*src_mat); + render_mat->applyOverride(*override_mat); + tep->setGLTFRenderMaterial(render_mat); + retval = TEM_CHANGE_TEXTURE; + + for (LLGLTFMaterial::local_tex_map_t::value_type &val : override_mat->mTrackingIdToLocalTexture) + { + LLLocalBitmapMgr::getInstance()->associateGLTFMaterial(val.first, override_mat); + } + + } + else if (tep->setGLTFRenderMaterial(nullptr)) + { + retval = TEM_CHANGE_TEXTURE; + } + } + + return retval; +} + +void LLViewerObject::refreshMaterials() +{ + setChanged(TEXTURE); + if (mDrawable.notNull()) + { + gPipeline.markTextured(mDrawable); + } +} + +S32 LLViewerObject::setTEScale(const U8 te, const F32 s, const F32 t) +{ + S32 retval = 0; + retval = LLPrimitive::setTEScale(te, s, t); + setChanged(TEXTURE); + if (mDrawable.notNull() && retval) + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); + } + return retval; +} + +S32 LLViewerObject::setTEScaleS(const U8 te, const F32 s) +{ + S32 retval = LLPrimitive::setTEScaleS(te, s); + if (mDrawable.notNull() && retval) + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); + } + + return retval; +} + +S32 LLViewerObject::setTEScaleT(const U8 te, const F32 t) +{ + S32 retval = LLPrimitive::setTEScaleT(te, t); + if (mDrawable.notNull() && retval) + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); + } + + return retval; +} + +S32 LLViewerObject::setTEOffset(const U8 te, const F32 s, const F32 t) +{ + S32 retval = LLPrimitive::setTEOffset(te, s, t); + if (mDrawable.notNull() && retval) + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); + } + return retval; +} + +S32 LLViewerObject::setTEOffsetS(const U8 te, const F32 s) +{ + S32 retval = LLPrimitive::setTEOffsetS(te, s); + if (mDrawable.notNull() && retval) + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); + } + + return retval; +} + +S32 LLViewerObject::setTEOffsetT(const U8 te, const F32 t) +{ + S32 retval = LLPrimitive::setTEOffsetT(te, t); + if (mDrawable.notNull() && retval) + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); + } + + return retval; +} + +S32 LLViewerObject::setTERotation(const U8 te, const F32 r) +{ + S32 retval = LLPrimitive::setTERotation(te, r); + if (mDrawable.notNull() && retval) + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD); + shrinkWrap(); + } + return retval; +} + + +LLViewerTexture *LLViewerObject::getTEImage(const U8 face) const +{ +// llassert(mTEImages); + + if (face < getNumTEs()) + { + LLViewerTexture* image = mTEImages[face]; + if (image) + { + return image; + } + else + { + return (LLViewerTexture*)(LLViewerFetchedTexture::sDefaultImagep); + } + } + + LL_ERRS() << llformat("Requested Image from invalid face: %d/%d",face,getNumTEs()) << LL_ENDL; + + return NULL; +} + + +bool LLViewerObject::isImageAlphaBlended(const U8 te) const +{ + LLViewerTexture* image = getTEImage(te); + LLGLenum format = image ? image->getPrimaryFormat() : GL_RGB; + switch (format) + { + case GL_RGBA: + case GL_ALPHA: + { + return true; + } + break; + + case GL_RGB: break; + default: + { + LL_WARNS() << "Unexpected tex format in LLViewerObject::isImageAlphaBlended...returning no alpha." << LL_ENDL; + } + break; + } + + return false; +} + +LLViewerTexture *LLViewerObject::getTENormalMap(const U8 face) const +{ + // llassert(mTEImages); + + if (face < getNumTEs()) + { + LLViewerTexture* image = mTENormalMaps[face]; + if (image) + { + return image; + } + else + { + return (LLViewerTexture*)(LLViewerFetchedTexture::sDefaultImagep); + } + } + + LL_ERRS() << llformat("Requested Image from invalid face: %d/%d",face,getNumTEs()) << LL_ENDL; + + return NULL; +} + +LLViewerTexture *LLViewerObject::getTESpecularMap(const U8 face) const +{ + // llassert(mTEImages); + + if (face < getNumTEs()) + { + LLViewerTexture* image = mTESpecularMaps[face]; + if (image) + { + return image; + } + else + { + return (LLViewerTexture*)(LLViewerFetchedTexture::sDefaultImagep); + } + } + + LL_ERRS() << llformat("Requested Image from invalid face: %d/%d",face,getNumTEs()) << LL_ENDL; + + return NULL; +} + +void LLViewerObject::fitFaceTexture(const U8 face) +{ + LL_INFOS() << "fitFaceTexture not implemented" << LL_ENDL; +} + +LLBBox LLViewerObject::getBoundingBoxAgent() const +{ + LLVector3 position_agent; + LLQuaternion rot; + LLViewerObject* avatar_parent = NULL; + LLViewerObject* root_edit = (LLViewerObject*)getRootEdit(); + if (root_edit) + { + avatar_parent = (LLViewerObject*)root_edit->getParent(); + } + + if (avatar_parent && avatar_parent->isAvatar() && + root_edit && root_edit->mDrawable.notNull() && root_edit->mDrawable->getXform()->getParent()) + { + LLXform* parent_xform = root_edit->mDrawable->getXform()->getParent(); + position_agent = (getPositionEdit() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition(); + rot = getRotationEdit() * parent_xform->getWorldRotation(); + } + else + { + position_agent = getPositionAgent(); + rot = getRotationRegion(); + } + + return LLBBox( position_agent, rot, getScale() * -0.5f, getScale() * 0.5f ); +} + +U32 LLViewerObject::getNumVertices() const +{ + U32 num_vertices = 0; + if (mDrawable.notNull()) + { + S32 i, num_faces; + num_faces = mDrawable->getNumFaces(); + for (i = 0; i < num_faces; i++) + { + LLFace * facep = mDrawable->getFace(i); + if (facep) + { + num_vertices += facep->getGeomCount(); + } + } + } + return num_vertices; +} + +U32 LLViewerObject::getNumIndices() const +{ + U32 num_indices = 0; + if (mDrawable.notNull()) + { + S32 i, num_faces; + num_faces = mDrawable->getNumFaces(); + for (i = 0; i < num_faces; i++) + { + LLFace * facep = mDrawable->getFace(i); + if (facep) + { + num_indices += facep->getIndicesCount(); + } + } + } + return num_indices; +} + +// Find the number of instances of this object's inventory that are of the given type +S32 LLViewerObject::countInventoryContents(LLAssetType::EType type) +{ + S32 count = 0; + if( mInventory ) + { + LLInventoryObject::object_list_t::const_iterator it = mInventory->begin(); + LLInventoryObject::object_list_t::const_iterator end = mInventory->end(); + for( ; it != end ; ++it ) + { + if( (*it)->getType() == type ) + { + ++count; + } + } + } + return count; +} + +void LLViewerObject::setDebugText(const std::string &utf8text, const LLColor4& color) +{ + if (utf8text.empty() && !mText) + { + return; + } + + if (!mText) + { + initHudText(); + } + mText->setColor(color); + mText->setString(utf8text); + mText->setZCompare(false); + mText->setDoFade(false); + updateText(); +} + +void LLViewerObject::appendDebugText(const std::string &utf8text) +{ + if (utf8text.empty() && !mText) + { + return; + } + + if (!mText) + { + initHudText(); + } + mText->addLine(utf8text, LLColor4::white); + mText->setZCompare(false); + mText->setDoFade(false); + updateText(); +} + +void LLViewerObject::initHudText() +{ + mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT); + mText->setFont(LLFontGL::getFontSansSerif()); + mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP); + mText->setMaxLines(-1); + mText->setSourceObject(this); + mText->setOnHUDAttachment(isHUDAttachment()); +} + +void LLViewerObject::restoreHudText() +{ + if (mHudText.empty()) + { + if (mText) + { + mText->markDead(); + mText = NULL; + } + } + else + { + if (!mText) + { + initHudText(); + } + else + { + // Restore default values + mText->setZCompare(true); + mText->setDoFade(true); + } + mText->setColor(mHudTextColor); + mText->setString(mHudText); + } +} + +void LLViewerObject::setIcon(LLViewerTexture* icon_image) +{ + if (!mIcon) + { + mIcon = (LLHUDIcon *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_ICON); + mIcon->setSourceObject(this); + mIcon->setImage(icon_image); + // *TODO: make this user configurable + mIcon->setScale(0.03f); + } + else + { + mIcon->restartLifeTimer(); + } +} + +void LLViewerObject::clearIcon() +{ + if (mIcon) + { + mIcon = NULL; + } +} + +LLViewerObject* LLViewerObject::getSubParent() +{ + return (LLViewerObject*) getParent(); +} + +const LLViewerObject* LLViewerObject::getSubParent() const +{ + return (const LLViewerObject*) getParent(); +} + +bool LLViewerObject::isOnMap() +{ + return mOnMap; +} + + +void LLViewerObject::updateText() +{ + if (!isDead()) + { + if (mText.notNull()) + { + LLVOAvatar* avatar = getAvatar(); + if (avatar) + { + mText->setHidden(avatar->isInMuteList()); + } + + LLVector3 up_offset(0,0,0); + up_offset.mV[2] = getScale().mV[VZ]*0.6f; + + if (mDrawable.notNull()) + { + mText->setPositionAgent(getRenderPosition() + up_offset); + } + else + { + mText->setPositionAgent(getPositionAgent() + up_offset); + } + } + } +} + +bool LLViewerObject::isOwnerInMuteList(LLUUID id) +{ + LLUUID owner_id = id.isNull() ? mOwnerID : id; + if (isAvatar() || owner_id.isNull()) + { + return false; + } + bool muted = false; + F64 now = LLFrameTimer::getTotalSeconds(); + if (now < mCachedMuteListUpdateTime) + { + muted = mCachedOwnerInMuteList; + } + else + { + muted = LLMuteList::getInstance()->isMuted(owner_id); + + const F64 SECONDS_BETWEEN_MUTE_UPDATES = 1; + mCachedMuteListUpdateTime = now + SECONDS_BETWEEN_MUTE_UPDATES; + mCachedOwnerInMuteList = muted; + } + return muted; +} + +LLVOAvatar* LLViewerObject::asAvatar() +{ + return NULL; +} + +// If this object is directly or indirectly parented by an avatar, +// return it. Normally getAvatar() is the correct function to call; +// it will give the avatar used for skinning. The exception is with +// animated objects that are also attachments; in that case, +// getAvatar() will return the control avatar, used for skinning, and +// getAvatarAncestor will return the avatar to which the object is +// attached. +LLVOAvatar* LLViewerObject::getAvatarAncestor() +{ + LLViewerObject *pobj = (LLViewerObject*) getParent(); + while (pobj) + { + LLVOAvatar *av = pobj->asAvatar(); + if (av) + { + return av; + } + pobj = (LLViewerObject*) pobj->getParent(); + } + return NULL; +} + +bool LLViewerObject::isParticleSource() const +{ + return !mPartSourcep.isNull() && !mPartSourcep->isDead(); +} + +void LLViewerObject::setParticleSource(const LLPartSysData& particle_parameters, const LLUUID& owner_id) +{ + if (mPartSourcep) + { + deleteParticleSource(); + } + + LLPointer pss = LLViewerPartSourceScript::createPSS(this, particle_parameters); + mPartSourcep = pss; + + if (mPartSourcep) + { + mPartSourcep->setOwnerUUID(owner_id); + + if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID) + { + LLViewerTexture* image; + if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null) + { + image = LLViewerTextureManager::getFetchedTextureFromFile("pixiesmall.tga"); + } + else + { + image = LLViewerTextureManager::getFetchedTexture(mPartSourcep->mPartSysData.mPartImageID); + } + mPartSourcep->setImage(image); + } + } + LLViewerPartSim::getInstance()->addPartSource(pss); +} + +void LLViewerObject::unpackParticleSource(const S32 block_num, const LLUUID& owner_id) +{ + if (!mPartSourcep.isNull() && mPartSourcep->isDead()) + { + mPartSourcep = NULL; + } + if (mPartSourcep) + { + // If we've got one already, just update the existing source (or remove it) + if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, block_num)) + { + mPartSourcep->setDead(); + mPartSourcep = NULL; + } + } + else + { + LLPointer pss = LLViewerPartSourceScript::unpackPSS(this, NULL, block_num); + //If the owner is muted, don't create the system + if(LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagParticles)) return; + + // We need to be able to deal with a particle source that hasn't changed, but still got an update! + if (pss) + { +// LL_INFOS() << "Making particle system with owner " << owner_id << LL_ENDL; + pss->setOwnerUUID(owner_id); + mPartSourcep = pss; + LLViewerPartSim::getInstance()->addPartSource(pss); + } + } + if (mPartSourcep) + { + if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID) + { + LLViewerTexture* image; + if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null) + { + image = LLViewerTextureManager::getFetchedTextureFromFile("pixiesmall.j2c"); + } + else + { + image = LLViewerTextureManager::getFetchedTexture(mPartSourcep->mPartSysData.mPartImageID); + } + mPartSourcep->setImage(image); + } + } +} + +void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id, bool legacy) +{ + if (!mPartSourcep.isNull() && mPartSourcep->isDead()) + { + mPartSourcep = NULL; + } + if (mPartSourcep) + { + // If we've got one already, just update the existing source (or remove it) + if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, dp, legacy)) + { + mPartSourcep->setDead(); + mPartSourcep = NULL; + } + } + else + { + LLPointer pss = LLViewerPartSourceScript::unpackPSS(this, NULL, dp, legacy); + //If the owner is muted, don't create the system + if(LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagParticles)) return; + // We need to be able to deal with a particle source that hasn't changed, but still got an update! + if (pss) + { +// LL_INFOS() << "Making particle system with owner " << owner_id << LL_ENDL; + pss->setOwnerUUID(owner_id); + mPartSourcep = pss; + LLViewerPartSim::getInstance()->addPartSource(pss); + } + } + if (mPartSourcep) + { + if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID) + { + LLViewerTexture* image; + if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null) + { + image = LLViewerTextureManager::getFetchedTextureFromFile("pixiesmall.j2c"); + } + else + { + image = LLViewerTextureManager::getFetchedTexture(mPartSourcep->mPartSysData.mPartImageID); + } + mPartSourcep->setImage(image); + } + } +} + +void LLViewerObject::deleteParticleSource() +{ + if (mPartSourcep.notNull()) + { + mPartSourcep->setDead(); + mPartSourcep = NULL; + } +} + +// virtual +void LLViewerObject::updateDrawable(bool force_damped) +{ + if (!isChanged(MOVED)) + { //most common case, having an empty if case here makes for better branch prediction + } + else if (mDrawable.notNull() && + !mDrawable->isState(LLDrawable::ON_MOVE_LIST)) + { + bool damped_motion = + !isChanged(SHIFTED) && // not shifted between regions this frame and... + ( force_damped || // ...forced into damped motion by application logic or... + ( !isSelected() && // ...not selected and... + ( mDrawable->isRoot() || // ... is root or ... + (getParent() && !((LLViewerObject*)getParent())->isSelected())// ... parent is not selected and ... + ) && + getPCode() == LL_PCODE_VOLUME && // ...is a volume object and... + getVelocity().isExactlyZero() && // ...is not moving physically and... + mDrawable->getGeneration() != -1 // ...was not created this frame. + ) + ); + gPipeline.markMoved(mDrawable, damped_motion); + } + clearChanged(SHIFTED); +} + +// virtual, overridden by LLVOVolume +F32 LLViewerObject::getVObjRadius() const +{ + return mDrawable.notNull() ? mDrawable->getRadius() : 0.f; +} + +void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, const U8 flags) +{ + if (!gAudiop) + { + return; + } + + if (audio_uuid.isNull()) + { + if (!mAudioSourcep) + { + return; + } + if (mAudioSourcep->isLoop() && !mAudioSourcep->hasPendingPreloads()) + { + // We don't clear the sound if it's a loop, it'll go away on its own. + // At least, this appears to be how the scripts work. + // The attached sound ID is set to NULL to avoid it playing back when the + // object rezzes in on non-looping sounds. + //LL_INFOS() << "Clearing attached sound " << mAudioSourcep->getCurrentData()->getID() << LL_ENDL; + gAudiop->cleanupAudioSource(mAudioSourcep); + mAudioSourcep = NULL; + } + else if (flags & LL_SOUND_FLAG_STOP) + { + // Just shut off the sound + mAudioSourcep->stop(); + } + return; + } + if (flags & LL_SOUND_FLAG_LOOP + && mAudioSourcep && mAudioSourcep->isLoop() && mAudioSourcep->getCurrentData() + && mAudioSourcep->getCurrentData()->getID() == audio_uuid) + { + //LL_INFOS() << "Already playing this sound on a loop, ignoring" << LL_ENDL; + return; + } + + // don't clean up before previous sound is done. Solves: SL-33486 + if ( mAudioSourcep && mAudioSourcep->isDone() ) + { + gAudiop->cleanupAudioSource(mAudioSourcep); + mAudioSourcep = NULL; + } + + if (mAudioSourcep && mAudioSourcep->isMuted() && + mAudioSourcep->getCurrentData() && mAudioSourcep->getCurrentData()->getID() == audio_uuid) + { + //LL_INFOS() << "Already having this sound as muted sound, ignoring" << LL_ENDL; + return; + } + + getAudioSource(owner_id); + + if (mAudioSourcep) + { + bool queue = flags & LL_SOUND_FLAG_QUEUE; + mAudioGain = gain; + mAudioSourcep->setGain(gain); + mAudioSourcep->setLoop(flags & LL_SOUND_FLAG_LOOP); + mAudioSourcep->setSyncMaster(flags & LL_SOUND_FLAG_SYNC_MASTER); + mAudioSourcep->setSyncSlave(flags & LL_SOUND_FLAG_SYNC_SLAVE); + mAudioSourcep->setQueueSounds(queue); + if(!queue) // stop any current sound first to avoid "farts of doom" (SL-1541) -MG + { + mAudioSourcep->stop(); + } + + // Play this sound if region maturity permits + if( gAgent.canAccessMaturityAtGlobal(this->getPositionGlobal()) ) + { + //LL_INFOS() << "Playing attached sound " << audio_uuid << LL_ENDL; + // recheck cutoff radius in case this update was an object-update with new value + mAudioSourcep->checkCutOffRadius(); + mAudioSourcep->play(audio_uuid); + } + } +} + +LLAudioSource *LLViewerObject::getAudioSource(const LLUUID& owner_id) +{ + if (!mAudioSourcep) + { + // Arbitrary low gain for a sound that's not playing. + // This is used for sound preloads, for example. + LLAudioSourceVO *asvop = new LLAudioSourceVO(mID, owner_id, 0.01f, this); + + mAudioSourcep = asvop; + if(gAudiop) + { + gAudiop->addAudioSource(asvop); + } + } + + return mAudioSourcep; +} + +void LLViewerObject::adjustAudioGain(const F32 gain) +{ + if (mAudioSourcep) + { + mAudioGain = gain; + mAudioSourcep->setGain(mAudioGain); + } +} + +//---------------------------------------------------------------------------- + +bool LLViewerObject::unpackParameterEntry(U16 param_type, LLDataPacker *dp) +{ + if (LLNetworkData::PARAMS_MESH == param_type) + { + param_type = LLNetworkData::PARAMS_SCULPT; + } + ExtraParameter* param = getExtraParameterEntryCreate(param_type); + if (param) + { + param->data->unpack(*dp); + param->in_use = true; + parameterChanged(param_type, param->data, true, false); + return true; + } + else + { + return false; + } +} + +LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 param_type) +{ + LLNetworkData* new_block = NULL; + switch (param_type) + { + case LLNetworkData::PARAMS_FLEXIBLE: + { + new_block = new LLFlexibleObjectData(); + break; + } + case LLNetworkData::PARAMS_LIGHT: + { + new_block = new LLLightParams(); + break; + } + case LLNetworkData::PARAMS_SCULPT: + { + new_block = new LLSculptParams(); + break; + } + case LLNetworkData::PARAMS_LIGHT_IMAGE: + { + new_block = new LLLightImageParams(); + break; + } + case LLNetworkData::PARAMS_EXTENDED_MESH: + { + new_block = new LLExtendedMeshParams(); + break; + } + case LLNetworkData::PARAMS_RENDER_MATERIAL: + { + new_block = new LLRenderMaterialParams(); + break; + } + case LLNetworkData::PARAMS_REFLECTION_PROBE: + { + new_block = new LLReflectionProbeParams(); + break; + } + default: + { + LL_INFOS_ONCE() << "Unknown param type: " << param_type << LL_ENDL; + break; + } + }; + + if (new_block) + { + ExtraParameter* new_entry = new ExtraParameter; + new_entry->data = new_block; + new_entry->in_use = false; // not in use yet + llassert(mExtraParameterList[param_type] == nullptr); // leak -- redundantly allocated parameter entry + mExtraParameterList[param_type] = new_entry; + return new_entry; + } + return NULL; +} + +LLViewerObject::ExtraParameter* LLViewerObject::getExtraParameterEntry(U16 param_type) const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VIEWER; + std::unordered_map::const_iterator itor = mExtraParameterList.find(param_type); + if (itor != mExtraParameterList.end()) + { + return itor->second; + } + return NULL; +} + +LLViewerObject::ExtraParameter* LLViewerObject::getExtraParameterEntryCreate(U16 param_type) +{ + ExtraParameter* param = getExtraParameterEntry(param_type); + if (!param) + { + param = createNewParameterEntry(param_type); + } + return param; +} + +LLNetworkData* LLViewerObject::getParameterEntry(U16 param_type) const +{ + ExtraParameter* param = getExtraParameterEntry(param_type); + if (param) + { + return param->data; + } + else + { + return NULL; + } +} + +bool LLViewerObject::getParameterEntryInUse(U16 param_type) const +{ + ExtraParameter* param = getExtraParameterEntry(param_type); + if (param) + { + return param->in_use; + } + else + { + return false; + } +} + +bool LLViewerObject::setParameterEntry(U16 param_type, const LLNetworkData& new_value, bool local_origin) +{ + ExtraParameter* param = getExtraParameterEntryCreate(param_type); + if (param) + { + if (param->in_use && new_value == *(param->data)) + { + return false; + } + param->in_use = true; + param->data->copy(new_value); + parameterChanged(param_type, param->data, true, local_origin); + return true; + } + else + { + return false; + } +} + +// Assumed to be called locally +// If in_use is true, will crate a new extra parameter if none exists. +// Should always return true. +bool LLViewerObject::setParameterEntryInUse(U16 param_type, bool in_use, bool local_origin) +{ + ExtraParameter* param = getExtraParameterEntryCreate(param_type); + if (param && param->in_use != in_use) + { + param->in_use = in_use; + parameterChanged(param_type, param->data, in_use, local_origin); + return true; + } + return false; +} + +void LLViewerObject::parameterChanged(U16 param_type, bool local_origin) +{ + ExtraParameter* param = getExtraParameterEntry(param_type); + if (param) + { + parameterChanged(param_type, param->data, param->in_use, local_origin); + } +} + +void LLViewerObject::parameterChanged(U16 param_type, LLNetworkData* data, bool in_use, bool local_origin) +{ + if (local_origin) + { + // *NOTE: Do not send the render material ID in this way as it will get + // out-of-sync with other sent client data. + // See LLViewerObject::setRenderMaterialID and LLGLTFMaterialList + llassert(param_type != LLNetworkData::PARAMS_RENDER_MATERIAL); + + LLViewerRegion* regionp = getRegion(); + if(!regionp) return; + + // Change happened on the viewer. Send the change up + U8 tmp[MAX_OBJECT_PARAMS_SIZE]; + LLDataPackerBinaryBuffer dpb(tmp, MAX_OBJECT_PARAMS_SIZE); + if (data->pack(dpb)) + { + U32 datasize = (U32)dpb.getCurrentSize(); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ObjectExtraParams); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addU32Fast(_PREHASH_ObjectLocalID, mLocalID ); + + msg->addU16Fast(_PREHASH_ParamType, param_type); + msg->addBOOLFast(_PREHASH_ParamInUse, in_use); + + msg->addU32Fast(_PREHASH_ParamSize, datasize); + msg->addBinaryDataFast(_PREHASH_ParamData, tmp, datasize); + + msg->sendReliable( regionp->getHost() ); + } + else + { + LL_WARNS() << "Failed to send object extra parameters: " << param_type << LL_ENDL; + } + } + else + { + if (param_type == LLNetworkData::PARAMS_RENDER_MATERIAL) + { + const LLRenderMaterialParams* params = in_use ? (LLRenderMaterialParams*)getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL) : nullptr; + setRenderMaterialIDs(params, local_origin); + } + } +} + +void LLViewerObject::setDrawableState(U32 state, bool recursive) +{ + if (mDrawable) + { + mDrawable->setState(state); + } + if (recursive) + { + for (child_list_t::iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* child = *iter; + child->setDrawableState(state, recursive); + } + } +} + +void LLViewerObject::clearDrawableState(U32 state, bool recursive) +{ + if (mDrawable) + { + mDrawable->clearState(state); + } + if (recursive) + { + for (child_list_t::iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* child = *iter; + child->clearDrawableState(state, recursive); + } + } +} + +bool LLViewerObject::isDrawableState(U32 state, bool recursive) const +{ + bool matches = false; + if (mDrawable) + { + matches = mDrawable->isState(state); + } + if (recursive) + { + for (child_list_t::const_iterator iter = mChildList.begin(); + (iter != mChildList.end()) && matches; iter++) + { + LLViewerObject* child = *iter; + matches &= child->isDrawableState(state, recursive); + } + } + + return matches; +} + + + +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// RN: these functions assume a 2-level hierarchy +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +// Owned by anyone? +bool LLViewerObject::permAnyOwner() const +{ + if (isRootEdit()) + { + return flagObjectAnyOwner(); + } + else + { + return ((LLViewerObject*)getParent())->permAnyOwner(); + } +} +// Owned by this viewer? +bool LLViewerObject::permYouOwner() const +{ + if (isRootEdit()) + { +#ifdef HACKED_GODLIKE_VIEWER + return true; +#else +# ifdef TOGGLE_HACKED_GODLIKE_VIEWER + if (!LLGridManager::getInstance()->isInProductionGrid() + && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) + { + return true; + } +# endif + return flagObjectYouOwner(); +#endif + } + else + { + return ((LLViewerObject*)getParent())->permYouOwner(); + } +} + +// Owned by a group? +bool LLViewerObject::permGroupOwner() const +{ + if (isRootEdit()) + { + return flagObjectGroupOwned(); + } + else + { + return ((LLViewerObject*)getParent())->permGroupOwner(); + } +} + +// Can the owner edit +bool LLViewerObject::permOwnerModify() const +{ + if (isRootEdit()) + { +#ifdef HACKED_GODLIKE_VIEWER + return true; +#else +# ifdef TOGGLE_HACKED_GODLIKE_VIEWER + if (!LLGridManager::getInstance()->isInProductionGrid() + && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) + { + return true; + } +# endif + return flagObjectOwnerModify(); +#endif + } + else + { + return ((LLViewerObject*)getParent())->permOwnerModify(); + } +} + +// Can edit +bool LLViewerObject::permModify() const +{ + if (isRootEdit()) + { +#ifdef HACKED_GODLIKE_VIEWER + return true; +#else +# ifdef TOGGLE_HACKED_GODLIKE_VIEWER + if (!LLGridManager::getInstance()->isInProductionGrid() + && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) + { + return true; + } +# endif + return flagObjectModify(); +#endif + } + else + { + return ((LLViewerObject*)getParent())->permModify(); + } +} + +// Can copy +bool LLViewerObject::permCopy() const +{ + if (isRootEdit()) + { +#ifdef HACKED_GODLIKE_VIEWER + return true; +#else +# ifdef TOGGLE_HACKED_GODLIKE_VIEWER + if (!LLGridManager::getInstance()->isInProductionGrid() + && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) + { + return true; + } +# endif + return flagObjectCopy(); +#endif + } + else + { + return ((LLViewerObject*)getParent())->permCopy(); + } +} + +// Can move +bool LLViewerObject::permMove() const +{ + if (isRootEdit()) + { +#ifdef HACKED_GODLIKE_VIEWER + return true; +#else +# ifdef TOGGLE_HACKED_GODLIKE_VIEWER + if (!LLGridManager::getInstance()->isInProductionGrid() + && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) + { + return true; + } +# endif + return flagObjectMove(); +#endif + } + else + { + return ((LLViewerObject*)getParent())->permMove(); + } +} + +// Can be transferred +bool LLViewerObject::permTransfer() const +{ + if (isRootEdit()) + { +#ifdef HACKED_GODLIKE_VIEWER + return true; +#else +# ifdef TOGGLE_HACKED_GODLIKE_VIEWER + if (!LLGridManager::getInstance()->isInProductionGrid() + && (gAgent.getGodLevel() >= GOD_MAINTENANCE)) + { + return true; + } +# endif + return flagObjectTransfer(); +#endif + } + else + { + return ((LLViewerObject*)getParent())->permTransfer(); + } +} + +// Can only open objects that you own, or that someone has +// given you modify rights to. JC +bool LLViewerObject::allowOpen() const +{ + return !flagInventoryEmpty() && (permYouOwner() || permModify()); +} + +LLViewerObject::LLInventoryCallbackInfo::~LLInventoryCallbackInfo() +{ + if (mListener) + { + mListener->clearVOInventoryListener(); + } +} + +void LLViewerObject::updateVolume(const LLVolumeParams& volume_params) +{ + if (setVolume(volume_params, 1)) // *FIX: magic number, ack! + { + // Transmit the update to the simulator + sendShapeUpdate(); + markForUpdate(); + } +} + +void LLViewerObject::recursiveMarkForUpdate() +{ + for (LLViewerObject::child_list_t::iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* child = *iter; + child->markForUpdate(); + } + markForUpdate(); +} + +void LLViewerObject::markForUpdate() +{ + if (mDrawable.notNull()) + { + gPipeline.markTextured(mDrawable); + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY); + } +} + +bool LLViewerObject::isPermanentEnforced() const +{ + return flagObjectPermanent() && (mRegionp != gAgent.getRegion()) && !gAgent.isGodlike(); +} + +bool LLViewerObject::getIncludeInSearch() const +{ + return flagIncludeInSearch(); +} + +void LLViewerObject::setIncludeInSearch(bool include_in_search) +{ + setFlags(FLAGS_INCLUDE_IN_SEARCH, include_in_search); +} + +void LLViewerObject::setRegion(LLViewerRegion *regionp) +{ + if (!regionp) + { + LL_WARNS() << "viewer object set region to NULL" << LL_ENDL; + } + if(regionp != mRegionp) + { + if(mRegionp) + { + mRegionp->removeFromCreatedList(getLocalID()); + } + if(regionp) + { + regionp->addToCreatedList(getLocalID()); + } + } + + mLatestRecvPacketID = 0; + mRegionp = regionp; + + for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i) + { + LLViewerObject* child = *i; + child->setRegion(regionp); + } + + if (mControlAvatar) + { + mControlAvatar->setRegion(regionp); + } + + setChanged(MOVED | SILHOUETTE); + updateDrawable(false); +} + +// virtual +void LLViewerObject::updateRegion(LLViewerRegion *regionp) +{ +// if (regionp) +// { +// F64 now = LLFrameTimer::getElapsedSeconds(); +// LL_INFOS() << "Updating to region " << regionp->getName() +// << ", ms since last update message: " << (F32)((now - mLastMessageUpdateSecs) * 1000.0) +// << ", ms since last interpolation: " << (F32)((now - mLastInterpUpdateSecs) * 1000.0) +// << LL_ENDL; +// } +} + + +bool LLViewerObject::specialHoverCursor() const +{ + return flagUsePhysics() + || flagHandleTouch() + || (mClickAction != 0); +} + +void LLViewerObject::updateFlags(bool physics_changed) +{ + LLViewerRegion* regionp = getRegion(); + if(!regionp) return; + gMessageSystem->newMessage("ObjectFlagUpdate"); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, getLocalID() ); + gMessageSystem->addBOOLFast(_PREHASH_UsePhysics, flagUsePhysics() ); + gMessageSystem->addBOOL("IsTemporary", flagTemporaryOnRez() ); + gMessageSystem->addBOOL("IsPhantom", flagPhantom() ); + + // stinson 02/28/2012 : This CastsShadows bool is no longer used in either the viewer or the simulator + // The simulator code does not even unpack this value when the message is received. + // This could be potentially hijacked in the future for another use should the urgent need arise. + gMessageSystem->addBOOL("CastsShadows", false ); + + if (physics_changed) + { + gMessageSystem->nextBlock("ExtraPhysics"); + gMessageSystem->addU8("PhysicsShapeType", getPhysicsShapeType() ); + gMessageSystem->addF32("Density", getPhysicsDensity() ); + gMessageSystem->addF32("Friction", getPhysicsFriction() ); + gMessageSystem->addF32("Restitution", getPhysicsRestitution() ); + gMessageSystem->addF32("GravityMultiplier", getPhysicsGravity() ); + } + gMessageSystem->sendReliable( regionp->getHost() ); +} + +bool LLViewerObject::setFlags(U32 flags, bool state) +{ + bool setit = setFlagsWithoutUpdate(flags, state); + + // BUG: Sometimes viewer physics and simulator physics get + // out of sync. To fix this, always send update to simulator. +// if (setit) + { + updateFlags(); + } + return setit; +} + +bool LLViewerObject::setFlagsWithoutUpdate(U32 flags, bool state) +{ + bool setit = false; + if (state) + { + if ((mFlags & flags) != flags) + { + mFlags |= flags; + setit = true; + } + } + else + { + if ((mFlags & flags) != 0) + { + mFlags &= ~flags; + setit = true; + } + } + return setit; +} + +void LLViewerObject::setPhysicsShapeType(U8 type) +{ + mPhysicsShapeUnknown = false; + if (type != mPhysicsShapeType) + { + mPhysicsShapeType = type; + setObjectCostStale(); +} +} + +void LLViewerObject::setPhysicsGravity(F32 gravity) +{ + mPhysicsGravity = gravity; +} + +void LLViewerObject::setPhysicsFriction(F32 friction) +{ + mPhysicsFriction = friction; +} + +void LLViewerObject::setPhysicsDensity(F32 density) +{ + mPhysicsDensity = density; +} + +void LLViewerObject::setPhysicsRestitution(F32 restitution) +{ + mPhysicsRestitution = restitution; +} + +U8 LLViewerObject::getPhysicsShapeType() const +{ + if (mPhysicsShapeUnknown) + { + gObjectList.updatePhysicsFlags(this); + } + + return mPhysicsShapeType; +} + +void LLViewerObject::applyAngularVelocity(F32 dt) +{ + //do target omega here + mRotTime += dt; + LLVector3 ang_vel = getAngularVelocity(); + F32 omega = ang_vel.magVecSquared(); + F32 angle = 0.0f; + LLQuaternion dQ; + if (omega > 0.00001f) + { + omega = sqrt(omega); + angle = omega * dt; + + ang_vel *= 1.f/omega; + + // calculate the delta increment based on the object's angular velocity + dQ.setQuat(angle, ang_vel); + + // accumulate the angular velocity rotations to re-apply in the case of an object update + mAngularVelocityRot *= dQ; + + // Just apply the delta increment to the current rotation + setRotation(getRotation()*dQ); + setChanged(MOVED | SILHOUETTE); + } +} + +void LLViewerObject::resetRotTime() +{ + mRotTime = 0.0f; +} + +void LLViewerObject::resetRot() +{ + resetRotTime(); + + // Reset the accumulated angular velocity rotation + mAngularVelocityRot.loadIdentity(); +} + +U32 LLViewerObject::getPartitionType() const +{ + return LLViewerRegion::PARTITION_NONE; +} + +void LLViewerObject::dirtySpatialGroup() const +{ + if (mDrawable) + { + LLSpatialGroup* group = mDrawable->getSpatialGroup(); + if (group) + { + group->dirtyGeom(); + gPipeline.markRebuild(group); + } + } +} + +void LLViewerObject::dirtyMesh() +{ + if (mDrawable) + { + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL); + } +} + +F32 LLAlphaObject::getPartSize(S32 idx) +{ + return 0.f; +} + +void LLAlphaObject::getBlendFunc(S32 face, LLRender::eBlendFactor& src, LLRender::eBlendFactor& dst) +{ + +} + +// virtual +void LLStaticViewerObject::updateDrawable(bool force_damped) +{ + // Force an immediate rebuild on any update + if (mDrawable.notNull()) + { + mDrawable->updateXform(true); + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL); + } + clearChanged(SHIFTED); +} + +void LLViewerObject::saveUnselectedChildrenPosition(std::vector& positions) +{ + if(mChildList.empty() || !positions.empty()) + { + return ; + } + + for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* childp = *iter; + if (!childp->isSelected() && childp->mDrawable.notNull()) + { + positions.push_back(childp->getPositionEdit()); + } + } + + return ; +} + +void LLViewerObject::saveUnselectedChildrenRotation(std::vector& rotations) +{ + if(mChildList.empty()) + { + return ; + } + + for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* childp = *iter; + if (!childp->isSelected() && childp->mDrawable.notNull()) + { + rotations.push_back(childp->getRotationEdit()); + } + } + + return ; +} + +//counter-rotation +void LLViewerObject::resetChildrenRotationAndPosition(const std::vector& rotations, + const std::vector& positions) +{ + if(mChildList.empty()) + { + return ; + } + + S32 index = 0 ; + LLQuaternion inv_rotation = ~getRotationEdit() ; + LLVector3 offset = getPositionEdit() ; + for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* childp = *iter; + if (!childp->isSelected() && childp->mDrawable.notNull()) + { + if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR) + { + childp->setRotation(rotations[index] * inv_rotation); + childp->setPosition((positions[index] - offset) * inv_rotation); + LLManip::rebuild(childp); + } + else //avatar + { + LLVector3 reset_pos = (positions[index] - offset) * inv_rotation ; + LLQuaternion reset_rot = rotations[index] * inv_rotation ; + + ((LLVOAvatar*)childp)->mDrawable->mXform.setPosition(reset_pos); + ((LLVOAvatar*)childp)->mDrawable->mXform.setRotation(reset_rot) ; + + ((LLVOAvatar*)childp)->mDrawable->getVObj()->setPosition(reset_pos, true); + ((LLVOAvatar*)childp)->mDrawable->getVObj()->setRotation(reset_rot, true) ; + + LLManip::rebuild(childp); + } + index++; + } + } + + return ; +} + +//counter-translation +void LLViewerObject::resetChildrenPosition(const LLVector3& offset, bool simplified, bool skip_avatar_child) +{ + if(mChildList.empty()) + { + return ; + } + + LLVector3 child_offset; + if(simplified) //translation only, rotation matrix does not change + { + child_offset = offset * ~getRotation(); + } + else //rotation matrix might change too. + { + if (isAttachment() && mDrawable.notNull()) + { + LLXform* attachment_point_xform = mDrawable->getXform()->getParent(); + LLQuaternion parent_rotation = getRotation() * attachment_point_xform->getWorldRotation(); + child_offset = offset * ~parent_rotation; + } + else + { + child_offset = offset * ~getRenderRotation(); + } + } + + for (LLViewerObject::child_list_t::const_iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + LLViewerObject* childp = *iter; + + if (!childp->isSelected() && childp->mDrawable.notNull()) + { + if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR) + { + childp->setPosition(childp->getPosition() + child_offset); + LLManip::rebuild(childp); + } + else //avatar + { + if(!skip_avatar_child) + { + LLVector3 reset_pos = ((LLVOAvatar*)childp)->mDrawable->mXform.getPosition() + child_offset ; + + ((LLVOAvatar*)childp)->mDrawable->mXform.setPosition(reset_pos); + ((LLVOAvatar*)childp)->mDrawable->getVObj()->setPosition(reset_pos); + LLManip::rebuild(childp); + } + } + } + } + + return ; +} + +// virtual +bool LLViewerObject::isTempAttachment() const +{ + return (mID.notNull() && (mID == mAttachmentItemID)); +} + +bool LLViewerObject::isHiglightedOrBeacon() const +{ + if (LLFloaterReg::instanceVisible("beacons") && (gPipeline.getRenderBeacons() || gPipeline.getRenderHighlights())) + { + bool has_media = (getMediaType() == LLViewerObject::MEDIA_SET); + bool is_scripted = !isAvatar() && !getParent() && flagScripted(); + bool is_physical = !isAvatar() && flagUsePhysics(); + + return (isParticleSource() && gPipeline.getRenderParticleBeacons()) + || (isAudioSource() && gPipeline.getRenderSoundBeacons()) + || (has_media && gPipeline.getRenderMOAPBeacons()) + || (is_scripted && gPipeline.getRenderScriptedBeacons()) + || (is_scripted && flagHandleTouch() && gPipeline.getRenderScriptedTouchBeacons()) + || (is_physical && gPipeline.getRenderPhysicalBeacons()); + } + return false; +} + + +const LLUUID &LLViewerObject::getAttachmentItemID() const +{ + return mAttachmentItemID; +} + +void LLViewerObject::setAttachmentItemID(const LLUUID &id) +{ + mAttachmentItemID = id; +} + +EObjectUpdateType LLViewerObject::getLastUpdateType() const +{ + return mLastUpdateType; +} + +void LLViewerObject::setLastUpdateType(EObjectUpdateType last_update_type) +{ + mLastUpdateType = last_update_type; +} + +bool LLViewerObject::getLastUpdateCached() const +{ + return mLastUpdateCached; +} + +void LLViewerObject::setLastUpdateCached(bool last_update_cached) +{ + mLastUpdateCached = last_update_cached; +} + +const LLUUID &LLViewerObject::extractAttachmentItemID() +{ + LLUUID item_id = LLUUID::null; + LLNameValue* item_id_nv = getNVPair("AttachItemID"); + if( item_id_nv ) + { + const char* s = item_id_nv->getString(); + if( s ) + { + item_id.set(s); + } + } + setAttachmentItemID(item_id); + return getAttachmentItemID(); +} + +const std::string& LLViewerObject::getAttachmentItemName() const +{ + static std::string empty; + LLInventoryItem *item = gInventory.getItem(getAttachmentItemID()); + if (isAttachment() && item) + { + return item->getName(); + } + return empty; +} + +//virtual +LLVOAvatar* LLViewerObject::getAvatar() const +{ + if (getControlAvatar()) + { + return getControlAvatar(); + } + if (isAttachment()) + { + LLViewerObject* vobj = (LLViewerObject*) getParent(); + + while (vobj && !vobj->asAvatar()) + { + vobj = (LLViewerObject*) vobj->getParent(); + } + + return (LLVOAvatar*) vobj; + } + + return NULL; +} + +bool LLViewerObject::hasRenderMaterialParams() const +{ + return getParameterEntryInUse(LLNetworkData::PARAMS_RENDER_MATERIAL); +} + +void LLViewerObject::setHasRenderMaterialParams(bool has_materials) +{ + bool had_materials = hasRenderMaterialParams(); + + if (had_materials != has_materials) + { + if (has_materials) + { + setParameterEntryInUse(LLNetworkData::PARAMS_RENDER_MATERIAL, true, true); + } + else + { + setParameterEntryInUse(LLNetworkData::PARAMS_RENDER_MATERIAL, false, true); + } + } +} + +const LLUUID& LLViewerObject::getRenderMaterialID(U8 te) const +{ + LLRenderMaterialParams* param_block = (LLRenderMaterialParams*)getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL); + if (param_block) + { + return param_block->getMaterial(te); + } + + return LLUUID::null; +} + +void LLViewerObject::rebuildMaterial() +{ + llassert(!isDead()); + + faceMappingChanged(); + gPipeline.markTextured(mDrawable); +} + +void LLViewerObject::setRenderMaterialID(S32 te_in, const LLUUID& id, bool update_server, bool local_origin) +{ + // implementation is delicate + + // if update is bound for server, should always null out GLTFRenderMaterial and clear GLTFMaterialOverride even if ids haven't changed + // (the case where ids haven't changed indicates the user has reapplied the original material, in which case overrides should be dropped) + // otherwise, should only null out the render material where ids or overrides have changed + // (the case where ids have changed but overrides are still present is from unsynchronized updates from the simulator, or synchronized + // updates with solely transform overrides) + + llassert(!update_server || local_origin); + + S32 start_idx = 0; + S32 end_idx = getNumTEs(); + + if (te_in != -1) + { + start_idx = te_in; + end_idx = start_idx + 1; + } + + start_idx = llmax(start_idx, 0); + end_idx = llmin(end_idx, (S32) getNumTEs()); + + LLRenderMaterialParams* param_block = (LLRenderMaterialParams*)getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL); + if (!param_block && id.notNull()) + { // block doesn't exist, but it will need to + param_block = (LLRenderMaterialParams*)createNewParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL)->data; + } + + + LLFetchedGLTFMaterial* new_material = nullptr; + if (id.notNull()) + { + new_material = gGLTFMaterialList.getMaterial(id); + } + + // update local state + for (S32 te = start_idx; te < end_idx; ++te) + { + LLTextureEntry* tep = getTE(te); + + // If local_origin=false (i.e. it's from the server), we know the + // material has updated or been created, because extra params are + // checked for equality on unpacking. In that case, checking the + // material ID for inequality won't work, because the material ID has + // already been set. + bool material_changed = !local_origin || !param_block || id != param_block->getMaterial(te); + + if (update_server) + { + // Clear most overrides so the render material better matches the material + // ID (preserve transforms). If overrides become passthrough, set the overrides + // to nullptr. + if (tep->setBaseMaterial()) + { + material_changed = true; + } + } + + if (update_server || material_changed) + { + tep->setGLTFRenderMaterial(nullptr); + } + + if (new_material != tep->getGLTFMaterial()) + { + tep->setGLTFMaterial(new_material, !update_server); + } + + if (material_changed && new_material) + { + // Sometimes, the material may change out from underneath the overrides. + // This is usually due to the server sending a new material ID, but + // the overrides have not changed due to being only texture + // transforms. Re-apply the overrides to the render material here, + // if present. + const LLGLTFMaterial* override_material = tep->getGLTFMaterialOverride(); + if (override_material) + { + new_material->onMaterialComplete([obj_id = getID(), te]() + { + LLViewerObject* obj = gObjectList.findObject(obj_id); + if (!obj) { return; } + LLTextureEntry* tep = obj->getTE(te); + if (!tep) { return; } + const LLGLTFMaterial* new_material = tep->getGLTFMaterial(); + if (!new_material) { return; } + const LLGLTFMaterial* override_material = tep->getGLTFMaterialOverride(); + if (!override_material) { return; } + LLGLTFMaterial* render_material = new LLFetchedGLTFMaterial(); + *render_material = *new_material; + render_material->applyOverride(*override_material); + tep->setGLTFRenderMaterial(render_material); + }); + } + } + } + + // signal to render pipe that render batches must be rebuilt for this object + if (!new_material) + { + rebuildMaterial(); + } + else + { + new_material->onMaterialComplete([obj_id = getID()]() + { + LLViewerObject* obj = gObjectList.findObject(obj_id); + if (obj) + { + obj->rebuildMaterial(); + } + }); + } + + // predictively update LLRenderMaterialParams (don't wait for server) + if (param_block) + { // update existing parameter block + for (S32 te = start_idx; te < end_idx; ++te) + { + param_block->setMaterial(te, id); + } + } + + if (update_server) + { + // update via ModifyMaterialParams cap (server will echo back changes) + for (S32 te = start_idx; te < end_idx; ++te) + { + // This sends a cleared version of this object's current material + // override, but the override should already be cleared due to + // calling setBaseMaterial above. + LLGLTFMaterialList::queueApply(this, te, id); + } + } + + if (!update_server) + { + // Land impact may have changed + setObjectCostStale(); + } +} + +void LLViewerObject::setRenderMaterialIDs(const LLUUID& id) +{ + setRenderMaterialID(-1, id); +} + +void LLViewerObject::setRenderMaterialIDs(const LLRenderMaterialParams* material_params, bool local_origin) +{ + if (!local_origin) + { + for (S32 te = 0; te < getNumTEs(); ++te) + { + const LLUUID& id = material_params ? material_params->getMaterial(te) : LLUUID::null; + // We know material_params has updated or been created, because + // extra params are checked for equality on unpacking. + setRenderMaterialID(te, id, false, false); + } + } +} + +void LLViewerObject::shrinkWrap() +{ + if (!mShouldShrinkWrap) + { + mShouldShrinkWrap = true; + if (mDrawable) + { // we weren't shrink wrapped before but we are now, update the spatial partition + gPipeline.markPartitionMove(mDrawable); + } + } +} + +class ObjectPhysicsProperties : public LLHTTPNode +{ +public: + virtual void post( + ResponsePtr responder, + const LLSD& context, + const LLSD& input) const + { + LLSD object_data = input["body"]["ObjectData"]; + S32 num_entries = object_data.size(); + + for ( S32 i = 0; i < num_entries; i++ ) + { + LLSD& curr_object_data = object_data[i]; + U32 local_id = curr_object_data["LocalID"].asInteger(); + + // Iterate through nodes at end, since it can be on both the regular AND hover list + struct f : public LLSelectedNodeFunctor + { + U32 mID; + f(const U32& id) : mID(id) {} + virtual bool apply(LLSelectNode* node) + { + return (node->getObject() && node->getObject()->mLocalID == mID ); + } + } func(local_id); + + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(&func); + + if (node) + { + // The LLSD message builder doesn't know how to handle U8, so we need to send as S8 and cast + U8 type = (U8)curr_object_data["PhysicsShapeType"].asInteger(); + F32 density = (F32)curr_object_data["Density"].asReal(); + F32 friction = (F32)curr_object_data["Friction"].asReal(); + F32 restitution = (F32)curr_object_data["Restitution"].asReal(); + F32 gravity = (F32)curr_object_data["GravityMultiplier"].asReal(); + + node->getObject()->setPhysicsShapeType(type); + node->getObject()->setPhysicsGravity(gravity); + node->getObject()->setPhysicsFriction(friction); + node->getObject()->setPhysicsDensity(density); + node->getObject()->setPhysicsRestitution(restitution); + } + } + + dialog_refresh_all(); + }; +}; + +LLHTTPRegistration + gHTTPRegistrationObjectPhysicsProperties("/message/ObjectPhysicsProperties"); + -- cgit v1.2.3 From b42f9d836b4c0f7fbd4bdae1734021e2a09fdbe8 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 1 Jun 2024 15:49:26 +0200 Subject: Re-enable a lot of compiler warnings for MSVC and address the C4267 "possible loss of precision" warnings --- indra/newview/llviewerobject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llviewerobject.cpp') diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index c791a30d1a..de05054f4b 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -7335,7 +7335,7 @@ public: const LLSD& input) const { LLSD object_data = input["body"]["ObjectData"]; - S32 num_entries = object_data.size(); + S32 num_entries = static_cast(object_data.size()); for ( S32 i = 0; i < num_entries; i++ ) { -- cgit v1.2.3