# standard python libs from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG import uuid #related from eventlet import api # pyogp from pyogp.lib.base.datamanager import DataManager # pyogp messaging from pyogp.lib.base.message.message_handler import MessageHandler from pyogp.lib.base.message.packets import * from pyogp.lib.base.utilities.helpers import Helpers from pyogp.lib.base.exc import NotImplemented from pyogp.lib.base.objects import Object from pyogp.lib.base.params import VisualParams from pyogp.lib.base.datatypes import * from pyogp.lib.base.utilities.enums import BakedIndex, TextureIndex, WearableMap # initialize logging logger = getLogger('pyogp.lib.base.appearance') log = logger.log class AppearanceManager(DataManager): """The AppearanceManager class handles appearance of an Agent() instance Sample implementations: Tests: """ def __init__(self, agent, settings = None): """ initialize the appearance manager TODO Fix the Z by generating actual height """ super(AppearanceManager, self).__init__(agent, settings) self.AgentSetSerialNum = 1 self.AgentCachedSerialNum = 1 self.wearables = [] #indexed by WearableType for i in range(TextureIndex.TEX_COUNT): self.wearables.append(Wearable(i)) self.helpers = Helpers() self.bakedTextures = [] #indexed by TextureIndex for i in range(BakedIndex.BAKED_COUNT): self.bakedTextures.append(BakedTexture(i)) self.params = VisualParams().params self.TextureEntry = "" self.Size = Vector3(X = 0.45, Y = 0.60, Z = 1.14 ) # Z which is Height needs to be calculated using params def enable_callbacks(self): """ enables the calback handlers for this AppearanceManager """ onAgentWearablesUpdate_received = self.agent.region.message_handler.register('AgentWearablesUpdate') onAgentWearablesUpdate_received.subscribe(self.onAgentWearablesUpdate) onAgentCachedTextureResponse_received = self.agent.region.message_handler.register('AgentCachedTextureResponse') onAgentCachedTextureResponse_received.subscribe(self.onAgentCachedTextureResponse) #onAvatarAppearance_received = self.agent.region.message_handler.register('AvatarAppearance') #onAvatarAppearance_received.subscribe(self.onAvatarAppearance) self.request_agent_wearables() ''' onAgentDataUpdate_received = self.agent.region.message_handler.register('AgentDataUpdate') onAgentDataUpdate_received.subscribe(self.helpers.log_packet, self) ''' def request_agent_wearables(self): """ Asks the simulator what the avatar is wearing """ if self.agent.agent_id == None or self.agent.session_id == None or \ str(self.agent.agent_id) == str(UUID()) or \ str(self.agent.session_id) == str(UUID()): log(WARNING, "Agent has either no agent_id or session_id, message not sent") return packet = AgentWearablesRequestPacket() packet.AgentData['AgentID'] = self.agent.agent_id packet.AgentData['SessionID'] = self.agent.session_id self.agent.region.enqueue_message(packet()) def onAgentWearablesUpdate(self, packet): """ Automatically tells simulator avatar is wearing the wearables from the AgentWearables update packet. This message should only be received once. Error Checking: make sure agent and session id are correct make sure this method is only called once. #TODO download wearables using assetIDs, upload wearables if wearing none """ self.verifyAgentData(packet) #log(INFO, "Got AgentWearablesUpdate: %s" % packet) for wearable in packet.blocks['WearableData']: wearableType = wearable.get_variable('WearableType').data itemID = wearable.get_variable('ItemID').data assetID = wearable.get_variable('AssetID').data self.wearables[wearableType].ItemID = itemID self.wearables[wearableType].AssetID = assetID self.send_AgentIsNowWearing() self.send_AgentCachedTexture() def send_AgentIsNowWearing(self): """ Tell the simulator that avatar is wearing initial items """ packet = AgentIsNowWearingPacket() packet.AgentData['AgentID'] = self.agent.agent_id packet.AgentData['SessionID'] = self.agent.session_id for wearable in self.wearables: WearableData = {} WearableData['ItemID'] = wearable.ItemID WearableData['WearableType'] = wearable.WearableType packet.WearableDataBlocks.append(WearableData) self.agent.region.enqueue_message(packet(), True) def send_AgentSetAppearance(self): """ Informs simulator how avatar looks """ packet = AgentSetAppearancePacket() #AgentData packet.AgentData['AgentID'] = self.agent.agent_id packet.AgentData['SessionID'] = self.agent.session_id packet.AgentData['SerialNum'] = self.AgentSetSerialNum packet.AgentData['Size'] = Vector3(X = 0.45, Y = 0.60, Z = 1.14) #Hard code? From Height in avatar_lad.xml #WearableData for bakedTexture in self.bakedTextures: WearableData = {} WearableData['CacheID'] = bakedTexture.TextureID WearableData['TextureIndex'] = bakedTexture.bakedIndex packet.WearableDataBlocks.append(WearableData) #ObjectData packet.ObjectData['TextureEntry'] = self.TextureEntry #VisualParam paramkeys = self.params.keys() paramkeys.sort() #since param id is not sent the parameters should be in sorted order for paramkey in paramkeys: if self.params[paramkey].group == 0: paramBlock = {} paramBlock['ParamValue'] = self.params[paramkey].floatToByte() packet.VisualParamBlocks.append(paramBlock) self.agent.region.enqueue_message(packet()) self.AgentSetSerialNum += 1 def onAvatarTextureUpdate(self, packet): raise NotImplemented("onAvatarTextureUpdate") def onAvatarAppearance(self, packet): """ Informs viewer how other avatars look """ raise NotImplemented("onAvatarAppearance") def send_AgentCachedTexture(self): """ Ask the simulator what baked textures it has cached. TODO Create a one-shot callback? """ #AgentData packet = AgentCachedTexturePacket() packet.AgentData['AgentID'] = self.agent.agent_id packet.AgentData['SessionID'] = self.agent.session_id packet.AgentData['SerialNum'] = self.AgentCachedSerialNum #WearableData for i in range(BakedIndex.BAKED_COUNT): wearableData = {} wearableData['ID'] = self.get_hash(i) wearableData['TextureIndex'] = i packet.WearableDataBlocks.append(wearableData) self.agent.region.enqueue_message(packet(), True) self.AgentCachedSerialNum += 1 def get_hash(self, bakedIndex): """ Creates a hash using the assetIDs for each wearable in a baked layer """ wearable_map = WearableMap().map hash = UUID() for wearable_index in wearable_map[bakedIndex]: hash ^= self.wearables[wearable_index].AssetID if str(hash) != '00000000-0000-0000-0000-000000000000': hash ^= self.bakedTextures[bakedIndex].Hash return hash def onAgentCachedTextureResponse(self, packet): """ Update the bakedTextures with their TextureIDs and HostNames and call send_AgentSetAppearance """ #log(INFO, "AgentCachedTextureRespose received: %s" % packet) for bakedTexture in packet.blocks['WearableData']: bakedIndex = bakedTexture.get_variable('TextureIndex').data self.bakedTextures[bakedIndex].TextureID = bakedTexture.get_variable('TextureID').data self.bakedTextures[bakedIndex].HostName = bakedTexture.get_variable('HostName').data self.send_AgentSetAppearance() def verifyAgentData(self, packet): """ verifies that packet refers to this agent """ pAgentID = packet.blocks['AgentData'][0].get_variable("AgentID").data pSessionID = packet.blocks['AgentData'][0].get_variable("SessionID").data if str(pAgentID) != str(self.agent.agent_id): log(WARNING, "%s packet does not have an AgentID", packet.name) class Wearable(object): """ Represents 1 of the 13 wearables an avatar can wear """ def __init__(self, WearableType = None, ItemID = UUID(), AssetID = UUID()): self.WearableType = WearableType self.ItemID = ItemID self.AssetID = AssetID class BakedTexture(object): """ Represents 1 of the 6 baked textures of an avatar """ def __init__(self, bakedIndex, TextureID = UUID()): self.bakedIndex = bakedIndex self.TextureID = TextureID self.HostName = None if bakedIndex == BakedIndex.BAKED_HEAD: self.Hash = UUID("18ded8d6-bcfc-e415-8539-944c0f5ea7a6") elif bakedIndex == BakedIndex.BAKED_UPPER: self.Hash = UUID("338c29e3-3024-4dbb-998d-7c04cf4fa88f") elif bakedIndex == BakedIndex.BAKED_LOWER: self.Hash = UUID("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f") elif bakedIndex == BakedIndex.BAKED_EYES: self.Hash = UUID("b2cf28af-b840-1071-3c6a-78085d8128b5") elif bakedIndex == BakedIndex.BAKED_SKIRT: self.Hash = UUID("ea800387-ea1a-14e0-56cb-24f2022f969a") elif bakedIndex == BakedIndex.BAKED_HAIR: self.Hash = UUID("0af1ef7c-ad24-11dd-8790-001f5bf833e8") else: self.Hash = UUID() class AvatarTexture(object): """ Represents 1 of the 21 baked and not baked textures of an avatar. """ def __init__(self, TextureIndex, TextureID = None): self.TextureIndex = TextureIndex self.TextureID = TextureID """ Contributors can be viewed at: http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt $LicenseInfo:firstyear=2008&license=apachev2$ Copyright 2009, Linden Research, Inc. Licensed under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 or in http://svn.secondlife.com/svn/linden/projects/2008/pyogp/LICENSE.txt $/LicenseInfo$ """