279 lines
11 KiB
Python
279 lines
11 KiB
Python
# 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$
|
|
"""
|
|
|