/// <summary> /// TextureEntry must have it's face textures in a specific order for avatars. /// Should be called at least once before sending an AgentSetAppearance packet. /// </summary> protected void UpdateAgentTextureEntryOrder() { // Correct the order of the textures foreach (uint faceid in AgentTextureEntry.FaceTextures.Keys) { if (faceid > 18) { Client.Log("Unknown order for FaceID: " + faceid + Environment.NewLine + "Your wearables define a face that we don't know the order of. Please " + "capture a AgentSetAppearance packet for your current outfit and submit to " + "[email protected], thanks!", Helpers.LogLevel.Info); break; } } //Re-order texture faces to match Linden Labs internal data structure. LLObject.TextureEntry te2 = new LLObject.TextureEntry(AgentTextureEntry.DefaultTexture.TextureID); te2.CreateFace(18).TextureID = AgentTextureEntry.GetFace(18).TextureID; te2.CreateFace(17).TextureID = AgentTextureEntry.GetFace(17).TextureID; te2.CreateFace(16).TextureID = AgentTextureEntry.GetFace(16).TextureID; te2.CreateFace(15).TextureID = AgentTextureEntry.GetFace(15).TextureID; te2.CreateFace(14).TextureID = AgentTextureEntry.GetFace(14).TextureID; te2.CreateFace(13).TextureID = AgentTextureEntry.GetFace(13).TextureID; te2.CreateFace(12).TextureID = AgentTextureEntry.GetFace(12).TextureID; // I wonder if shoes are somewhere in here? te2.CreateFace(7).TextureID = AgentTextureEntry.GetFace(7).TextureID; te2.CreateFace(6).TextureID = AgentTextureEntry.GetFace(6).TextureID; te2.CreateFace(5).TextureID = AgentTextureEntry.GetFace(5).TextureID; te2.CreateFace(4).TextureID = AgentTextureEntry.GetFace(4).TextureID; te2.CreateFace(3).TextureID = AgentTextureEntry.GetFace(3).TextureID; te2.CreateFace(2).TextureID = AgentTextureEntry.GetFace(2).TextureID; te2.CreateFace(1).TextureID = AgentTextureEntry.GetFace(1).TextureID; te2.CreateFace(0).TextureID = AgentTextureEntry.GetFace(0).TextureID; AgentTextureEntry = te2; }
private void SendAgentSetAppearance() { AgentSetAppearancePacket set = new AgentSetAppearancePacket(); set.AgentData.AgentID = Client.Self.AgentID; set.AgentData.SessionID = Client.Self.SessionID; set.AgentData.SerialNum = SetAppearanceSerialNum++; set.VisualParam = new AgentSetAppearancePacket.VisualParamBlock[VisualParams.Params.Count]; float AgentSizeVPHeight = 0.0f; float AgentSizeVPHeelHeight = 0.0f; float AgentSizeVPPlatformHeight = 0.0f; float AgentSizeVPHeadSize = 0.0f; float AgentSizeVPLegLength = 0.0f; float AgentSizeVPNeckLength = 0.0f; float AgentSizeVPHipLength = 0.0f; lock (Wearables.Dictionary) { // Only for debugging output int count = 0, vpIndex = 0; // Build the visual param array foreach (KeyValuePair<int, VisualParam> kvp in VisualParams.Params) { bool found = false; set.VisualParam[vpIndex] = new AgentSetAppearancePacket.VisualParamBlock(); VisualParam vp = kvp.Value; // Try and find this value in our collection of downloaded wearables foreach (WearableData data in Wearables.Dictionary.Values) { if (data.Asset != null && data.Asset.Params.ContainsKey(vp.ParamID)) { set.VisualParam[vpIndex].ParamValue = Helpers.FloatToByte(data.Asset.Params[vp.ParamID], vp.MinValue, vp.MaxValue); found = true; count++; switch (vp.ParamID) { case 33: AgentSizeVPHeight = data.Asset.Params[vp.ParamID]; break; case 198: AgentSizeVPHeelHeight = data.Asset.Params[vp.ParamID]; break; case 503: AgentSizeVPPlatformHeight = data.Asset.Params[vp.ParamID]; break; case 682: AgentSizeVPHeadSize = data.Asset.Params[vp.ParamID]; break; case 692: AgentSizeVPLegLength = data.Asset.Params[vp.ParamID]; break; case 756: AgentSizeVPNeckLength = data.Asset.Params[vp.ParamID]; break; case 842: AgentSizeVPHipLength = data.Asset.Params[vp.ParamID]; break; } break; } } // Use a default value if we don't have one set for it if (!found) { set.VisualParam[vpIndex].ParamValue = Helpers.FloatToByte(vp.DefaultValue, vp.MinValue, vp.MaxValue); switch (vp.ParamID) { case 33: AgentSizeVPHeight = vp.DefaultValue; break; case 198: AgentSizeVPHeelHeight = vp.DefaultValue; break; case 503: AgentSizeVPPlatformHeight = vp.DefaultValue; break; case 682: AgentSizeVPHeadSize = vp.DefaultValue; break; case 692: AgentSizeVPLegLength = vp.DefaultValue; break; case 756: AgentSizeVPNeckLength = vp.DefaultValue; break; case 842: AgentSizeVPHipLength = vp.DefaultValue; break; } } vpIndex++; } Client.DebugLog("AgentSetAppearance contains " + count + " VisualParams"); // Build the texture entry for our agent LLObject.TextureEntry te = new LLObject.TextureEntry(DEFAULT_AVATAR_TEXTURE); // Put our AgentTextures array in to TextureEntry lock (AgentTextures) { for (uint i = 0; i < AgentTextures.Length; i++) { if (AgentTextures[i] != LLUUID.Zero) { LLObject.TextureEntryFace face = te.CreateFace(i); face.TextureID = AgentTextures[i]; } } } foreach (WearableData data in Wearables.Dictionary.Values) { if (data.Asset != null) { foreach (KeyValuePair<TextureIndex, LLUUID> texture in data.Asset.Textures) { LLObject.TextureEntryFace face = te.CreateFace((uint)texture.Key); face.TextureID = texture.Value; Client.DebugLog("Setting texture " + ((TextureIndex)texture.Key).ToString() + " to " + texture.Value.ToString()); } } } // Set the packet TextureEntry set.ObjectData.TextureEntry = te.ToBytes(); } // FIXME: Our hackish algorithm is making squished avatars. See // http://www.libsecondlife.org/wiki/Agent_Size for discussion of the correct algorithm //float height = Helpers.ByteToFloat(set.VisualParam[33].ParamValue, VisualParams.Params[33].MinValue, // VisualParams.Params[33].MaxValue); // Takes into account the Shoe Heel/Platform offsets but not the Head Size Offset. But seems to work. double AgentSizeBase = 1.706; // The calculation for the Head Size scalar may be incorrect. But seems to work. double AgentHeight = AgentSizeBase + (AgentSizeVPLegLength * .1918) + (AgentSizeVPHipLength * .0375) + (AgentSizeVPHeight * .12022) + (AgentSizeVPHeadSize * .01117) + (AgentSizeVPNeckLength * .038) + (AgentSizeVPHeelHeight * .08) + (AgentSizeVPPlatformHeight * .07); set.AgentData.Size = new LLVector3(0.45f, 0.6f, (float)AgentHeight); // TODO: Account for not having all the textures baked yet set.WearableData = new AgentSetAppearancePacket.WearableDataBlock[BAKED_TEXTURE_COUNT]; // Build hashes for each of the bake layers from the individual components for (int bakedIndex = 0; bakedIndex < BAKED_TEXTURE_COUNT; bakedIndex++) { LLUUID hash = new LLUUID(); for (int wearableIndex = 0; wearableIndex < WEARABLES_PER_LAYER; wearableIndex++) { WearableType type = WEARABLE_BAKE_MAP[bakedIndex][wearableIndex]; LLUUID assetID = GetWearableAsset(type); // Build a hash of all the texture asset IDs in this baking layer if (assetID != LLUUID.Zero) hash ^= assetID; } if (hash != LLUUID.Zero) { // Hash with our secret value for this baked layer hash ^= BAKED_TEXTURE_HASH[bakedIndex]; } // Tell the server what cached texture assetID to use for each bake layer set.WearableData[bakedIndex] = new AgentSetAppearancePacket.WearableDataBlock(); set.WearableData[bakedIndex].TextureIndex = (byte)bakedIndex; set.WearableData[bakedIndex].CacheID = hash; } // Finally, send the packet Client.Network.SendPacket(set); }
public void TextureEntry() { LLObject.TextureEntry te = new LLObject.TextureEntry(LLUUID.Random()); LLObject.TextureEntryFace face = te.CreateFace(0); face.Bump = Bumpiness.Concrete; face.Fullbright = true; face.MediaFlags = true; face.OffsetU = 0.5f; face.OffsetV = -0.5f; face.RepeatU = 3.0f; face.RepeatV = 4.0f; face.RGBA = new LLColor(0f, 0.25f, 0.75f, 1f); face.Rotation = 1.5f; face.Shiny = Shininess.Medium; face.TexMapType = MappingType.Planar; face.TextureID = LLUUID.Random(); byte[] teBytes = te.ToBytes(); LLObject.TextureEntry te2 = new LLObject.TextureEntry(teBytes, 0, teBytes.Length); byte[] teBytes2 = te2.ToBytes(); Assert.IsTrue(teBytes.Length == teBytes2.Length); for (int i = 0; i < teBytes.Length; i++) { Assert.IsTrue(teBytes[i] == teBytes2[i], "Byte " + i + " is not equal"); } }
private void SendAgentSetAppearance() { AgentSetAppearancePacket set = new AgentSetAppearancePacket(); set.AgentData.AgentID = Client.Self.AgentID; set.AgentData.SessionID = Client.Self.SessionID; set.AgentData.SerialNum = SetAppearanceSerialNum++; set.VisualParam = new AgentSetAppearancePacket.VisualParamBlock[VisualParams.Params.Count]; lock (Wearables) { // Only for debugging output int count = 0, vpIndex = 0; // Build the visual param array foreach (KeyValuePair<int,VisualParam> kvp in VisualParams.Params) { bool found = false; set.VisualParam[vpIndex] = new AgentSetAppearancePacket.VisualParamBlock(); VisualParam vp = kvp.Value; // Try and find this value in our collection of downloaded wearables foreach (WearableData data in Wearables.Values) { if (data.Asset != null && data.Asset.Params.ContainsKey(vp.ParamID)) { set.VisualParam[vpIndex].ParamValue = Helpers.FloatToByte(data.Asset.Params[vp.ParamID], vp.MinValue, vp.MaxValue); found = true; count++; break; } } // Use a default value if we don't have one set for it if (!found) { set.VisualParam[vpIndex].ParamValue = Helpers.FloatToByte(vp.DefaultValue, vp.MinValue, vp.MaxValue); } vpIndex++; } Client.DebugLog("AgentSetAppearance contains " + count + " VisualParams"); // Build the texture entry for our agent LLObject.TextureEntry te = new LLObject.TextureEntry(DEFAULT_AVATAR_TEXTURE); // Put our AgentTextures array in to TextureEntry lock (AgentTextures) { for (uint i = 0; i < AgentTextures.Length; i++) { if (AgentTextures[i] != LLUUID.Zero) { LLObject.TextureEntryFace face = te.CreateFace(i); face.TextureID = AgentTextures[i]; } } } foreach (WearableData data in Wearables.Values) { if (data.Asset != null) { foreach (KeyValuePair<TextureIndex, LLUUID> texture in data.Asset.Textures) { LLObject.TextureEntryFace face = te.CreateFace((uint)texture.Key); face.TextureID = texture.Value; Client.DebugLog("Setting texture " + ((TextureIndex)texture.Key).ToString() + " to " + texture.Value.ToString()); } } } // Set the packet TextureEntry set.ObjectData.TextureEntry = te.ToBytes(); } // FIXME: Our hackish algorithm is making squished avatars. See // http://www.libsecondlife.org/wiki/Agent_Size for discussion of the correct algorithm float height = Helpers.ByteToFloat(set.VisualParam[33].ParamValue, VisualParams.Params[33].MinValue, VisualParams.Params[33].MaxValue); set.AgentData.Size = new LLVector3(0.45f, 0.6f, 1.50856f + ((height / 255.0f) * (2.025506f - 1.50856f))); // TODO: Account for not having all the textures baked yet set.WearableData = new AgentSetAppearancePacket.WearableDataBlock[BAKED_TEXTURE_COUNT]; // Build hashes for each of the bake layers from the individual components for (int bakedIndex = 0; bakedIndex < BAKED_TEXTURE_COUNT; bakedIndex++) { LLUUID hash = new LLUUID(); for (int wearableIndex = 0; wearableIndex < WEARABLES_PER_LAYER; wearableIndex++) { WearableType type = WEARABLE_BAKE_MAP[bakedIndex][wearableIndex]; LLUUID assetID = GetWearableAsset(type); // Build a hash of all the texture asset IDs in this baking layer if (assetID != LLUUID.Zero) hash ^= assetID; } if (hash != LLUUID.Zero) { // Hash with our secret value for this baked layer hash ^= BAKED_TEXTURE_HASH[bakedIndex]; } // Tell the server what cached texture assetID to use for each bake layer set.WearableData[bakedIndex] = new AgentSetAppearancePacket.WearableDataBlock(); set.WearableData[bakedIndex].TextureIndex = (byte)bakedIndex; set.WearableData[bakedIndex].CacheID = hash; } // Finally, send the packet Client.Network.SendPacket(set); }