Files
Hippolyzer/pyogp/lib/base/appearance.py

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$
"""