root/projects/2008/pyogp/pyogp.lib.base/branches/kotler_tests/pyogp/lib/base/appearance.py

Revision 2475, 11.6 kB (checked in by kotler.linden, 5 months ago)

minor refactoring

Line 
1 # standard python libs
2 from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG
3 import uuid
4
5 #related
6 from eventlet import api
7
8 # pyogp
9 from pyogp.lib.base.datamanager import DataManager
10 # pyogp messaging
11 from pyogp.lib.base.message.message_handler import MessageHandler
12 from pyogp.lib.base.message.packets import *
13 from pyogp.lib.base.utilities.helpers import Helpers
14 from pyogp.lib.base.exc import NotImplemented
15 from pyogp.lib.base.objects import Object
16 from pyogp.lib.base.params import VisualParams
17 from pyogp.lib.base.datatypes import *
18 from pyogp.lib.base.utilities.enums import BakedIndex, TextureIndex, WearableMap
19
20 # initialize logging
21 logger = getLogger('pyogp.lib.base.appearance')
22 log = logger.log
23
24 class AppearanceManager(DataManager):
25     """The AppearanceManager class handles appearance of an Agent() instance
26
27     Sample implementations:
28     Tests:
29     """
30
31     def __init__(self, settings = None, agent = None):
32         """
33         initialize the appearance manager
34         TODO Fix the Z by generating actual height
35         """
36         super(AppearanceManager, self).__init__(settings, agent)
37         self.AgentSetSerialNum = 1
38         self.AgentCachedSerialNum = 1
39         self.wearables = [] #indexed by WearableType
40         for i in range(TextureIndex.TEX_COUNT):
41             self.wearables.append(Wearable(i))
42         self.helpers = Helpers()
43         self.bakedTextures = [] #indexed by TextureIndex
44         for i in range(BakedIndex.BAKED_COUNT):
45             self.bakedTextures.append(BakedTexture(i))
46         self.params = VisualParams().params
47         self.TextureEntry = ""
48         self.Size = Vector3(X = 0.45, Y = 0.60, Z = 1.14 ) # Z which is Height needs to be calculated using params
49        
50     def enable_callbacks(self):
51         """
52         enables the calback handlers for this AppearanceManager
53         """
54         onAgentWearablesUpdate_received = self.agent.region.message_handler.register('AgentWearablesUpdate')
55         onAgentWearablesUpdate_received.subscribe(self.onAgentWearablesUpdate)
56         onAgentCachedTextureResponse_received = self.agent.region.message_handler.register('AgentCachedTextureResponse')
57         onAgentCachedTextureResponse_received.subscribe(self.onAgentCachedTextureResponse)
58         onAvatarAppearance_received = self.agent.region.message_handler.register('AvatarAppearance')
59         onAvatarAppearance_received.subscribe(self.onAvatarAppearance)
60         self.request_agent_wearables()
61         '''
62         onAgentDataUpdate_received = self.agent.region.message_handler.register('AgentDataUpdate')
63         onAgentDataUpdate_received.subscribe(self.helpers.log_packet, self)
64         '''
65    
66
67     def request_agent_wearables(self):
68         """
69         Asks the simulator what the avatar is wearing
70         """
71         if self.agent.agent_id == None or self.agent.session_id == None or \
72               str(self.agent.agent_id) == str(UUID()) or \
73               str(self.agent.session_id) == str(UUID()):
74            log(WARNING, "Agent has either no agent_id or session_id, message not sent")
75            return
76        
77         packet = AgentWearablesRequestPacket()
78        
79         packet.AgentData['AgentID'] = self.agent.agent_id
80         packet.AgentData['SessionID'] = self.agent.session_id
81
82         self.agent.region.enqueue_message(packet())
83
84     def onAgentWearablesUpdate(self, packet):
85         """
86         Automatically tells simulator avatar is wearing the wearables from
87         the AgentWearables update packet.
88         This message should only be received once.
89        
90         Error Checking: make sure agent and session id are correct
91         make sure this method is only called once.
92
93         #TODO download wearables using assetIDs, upload wearables if wearing none
94         """
95        
96         self.verifyAgentData(packet)
97         #log(INFO, "Got AgentWearablesUpdate: %s" % packet)
98         for wearable in packet.blocks['WearableData']:
99             wearableType = wearable.get_variable('WearableType').data
100             itemID = wearable.get_variable('ItemID').data
101             assetID = wearable.get_variable('AssetID').data
102             self.wearables[wearableType].ItemID = itemID
103             self.wearables[wearableType].AssetID = assetID
104         self.send_AgentIsNowWearing()
105         self.send_AgentCachedTexture()
106                
107                
108     def send_AgentIsNowWearing(self):
109         """
110         Tell the simulator that avatar is wearing initial items
111         """
112         packet = AgentIsNowWearingPacket()
113
114         packet.AgentData['AgentID'] = self.agent.agent_id
115         packet.AgentData['SessionID'] = self.agent.session_id
116         for wearable in self.wearables:
117             WearableData = {}
118             WearableData['ItemID'] = wearable.ItemID
119             WearableData['WearableType'] = wearable.WearableType
120             packet.WearableDataBlocks.append(WearableData)
121         self.agent.region.enqueue_message(packet(), True)
122    
123     def send_AgentSetAppearance(self):
124         """
125         Informs simulator how avatar looks
126         """
127         packet = AgentSetAppearancePacket()
128         #AgentData
129         packet.AgentData['AgentID'] = self.agent.agent_id
130         packet.AgentData['SessionID'] = self.agent.session_id
131         packet.AgentData['SerialNum'] = self.AgentSetSerialNum
132         packet.AgentData['Size'] = Vector3(X = 0.45, Y = 0.60, Z = 1.14) #Hard code? From Height in avatar_lad.xml
133
134         #WearableData
135         for bakedTexture in self.bakedTextures:
136             WearableData = {}
137             WearableData['CacheID'] = bakedTexture.TextureID
138             WearableData['TextureIndex'] = bakedTexture.bakedIndex
139             packet.WearableDataBlocks.append(WearableData)
140        
141         #ObjectData
142         packet.ObjectData['TextureEntry'] = self.TextureEntry
143
144         #VisualParam
145         paramkeys = self.params.keys()
146         paramkeys.sort() #since param id is not sent the parameters should be in sorted order
147         for paramkey in paramkeys:
148             if self.params[paramkey].group == 0:
149                 paramBlock = {}
150                 paramBlock['ParamValue'] = self.params[paramkey].floatToByte()
151                 packet.VisualParamBlocks.append(paramBlock)
152         self.agent.region.enqueue_message(packet())
153         self.AgentSetSerialNum += 1
154    
155     def onAvatarTextureUpdate(self, packet):
156         raise NotImplemented("onAvatarTextureUpdate")
157
158     def onAvatarAppearance(self, packet):
159         """
160         Informs viewer how other avatars look
161         """
162         #log(INFO, "AvatarAppearance packet received: %s" % packet)
163         if self.settings.ENABLE_LOOK_LIKE_NEARBY_AVATAR and self.AgentSetSerialNum <= 2:
164             self.lookLikeNearbyAvatar(packet)
165    
166     def lookLikeNearbyAvatar(self, packet):
167         """
168         Tells the simulator that this agent is using the TextureEntry and Visual Params
169         of a nearby avatar.  Uses first AvatarAppearance packet recv'd
170         """     
171         self.TextureEntry = (packet.blocks["ObjectData"].pop()).get_variable('TextureEntry').data
172         recvparams = packet.blocks["VisualParam"]
173         paramkeys = self.params.keys()
174         paramkeys.sort() #since param id is not sent the parameters should be in sorted order
175         for paramkey in paramkeys:
176             if self.params[paramkey].group == 0:
177                 self.params[paramkey].byteToFloat(recvparams.pop(0).get_variable("ParamValue").data)
178         self.send_AgentSetAppearance()
179            
180     def send_AgentCachedTexture(self):
181         """
182         Ask the simulator what baked textures it has cached.
183         TODO Create a one-shot callback?
184         """
185         #AgentData
186         packet = AgentCachedTexturePacket()
187         packet.AgentData['AgentID'] = self.agent.agent_id
188         packet.AgentData['SessionID'] = self.agent.session_id
189         packet.AgentData['SerialNum'] = self.AgentCachedSerialNum
190
191         #WearableData
192         for i in range(BakedIndex.BAKED_COUNT):
193             wearableData = {}
194             wearableData['ID'] = self.get_hash(i)
195             wearableData['TextureIndex'] = i
196             packet.WearableDataBlocks.append(wearableData)
197         self.agent.region.enqueue_message(packet(), True)
198         self.AgentCachedSerialNum += 1
199
200     def get_hash(self, bakedIndex):
201         """
202         Creates a hash using the assetIDs for each wearable in a baked layer
203         """
204         wearable_map = WearableMap().map
205         hash = UUID()
206         for wearable_index in wearable_map[bakedIndex]:
207             hash ^= self.wearables[wearable_index].AssetID
208         if str(hash) != '00000000-0000-0000-0000-000000000000':
209             hash ^= self.bakedTextures[bakedIndex].Hash
210         return hash
211        
212     def onAgentCachedTextureResponse(self, packet):
213         """
214         Update the bakedTextures with their TextureIDs and HostNames and call
215         send_AgentSetAppearance
216         """
217         #log(INFO, "AgentCachedTextureRespose received: %s" % packet)
218         for bakedTexture in packet.blocks['WearableData']:
219             bakedIndex = bakedTexture.get_variable('TextureIndex').data
220             self.bakedTextures[bakedIndex].TextureID = bakedTexture.get_variable('TextureID').data
221             self.bakedTextures[bakedIndex].HostName = bakedTexture.get_variable('HostName').data
222         self.send_AgentSetAppearance()
223
224     def verifyAgentData(self, packet):
225         """
226         verifies that packet refers to this agent
227         """
228         pAgentID = packet.blocks['AgentData'][0].get_variable("AgentID").data
229         pSessionID = packet.blocks['AgentData'][0].get_variable("SessionID").data
230         if  str(pAgentID) != str(self.agent.agent_id):
231             log(WARNING, "%s packet does not have an AgentID", packet.name)
232         if str(pSessionID) != str(self.agent.session_id):
233             log(WARNING, "%s packet does not have a SessionID" % packet.name)
234        
235 class Wearable(object):
236     """
237     Represents 1 of the 13 wearables an avatar can wear
238     """
239     def __init__(self, WearableType = None, ItemID = UUID(), AssetID = UUID()):
240         self.WearableType = WearableType
241         self.ItemID = ItemID
242         self.AssetID = AssetID
243
244
245 class BakedTexture(object):
246     """
247     Represents 1 of the 6 baked textures of an avatar
248     """
249     def __init__(self, bakedIndex, TextureID = UUID()):
250         self.bakedIndex = bakedIndex
251         self.TextureID = TextureID
252         self.HostName = None
253            
254         if bakedIndex == BakedIndex.BAKED_HEAD:
255             self.Hash = UUID("18ded8d6-bcfc-e415-8539-944c0f5ea7a6")
256         elif bakedIndex == BakedIndex.BAKED_UPPER:
257             self.Hash = UUID("338c29e3-3024-4dbb-998d-7c04cf4fa88f")
258         elif bakedIndex == BakedIndex.BAKED_LOWER:
259             self.Hash = UUID("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f")
260         elif bakedIndex == BakedIndex.BAKED_EYES:
261             self.Hash = UUID("b2cf28af-b840-1071-3c6a-78085d8128b5")
262         elif bakedIndex == BakedIndex.BAKED_SKIRT:
263             self.Hash = UUID("ea800387-ea1a-14e0-56cb-24f2022f969a")
264         elif bakedIndex == BakedIndex.BAKED_HAIR:
265             self.Hash = UUID("0af1ef7c-ad24-11dd-8790-001f5bf833e8")
266         else:
267             self.Hash = UUID()
268    
269 class AvatarTexture(object):
270     """
271     Represents 1 of the 21 baked and not baked textures of an avatar.
272     """
273     def __init__(self, TextureIndex, TextureID = None):
274         self.TextureIndex = TextureIndex
275         self.TextureID = TextureID
276
277
278
279 """
280 Contributors can be viewed at:
281 http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt
282
283 $LicenseInfo:firstyear=2008&license=apachev2$
284
285 Copyright 2009, Linden Research, Inc.
286
287 Licensed under the Apache License, Version 2.0 (the "License").
288 You may obtain a copy of the License at:
289     http://www.apache.org/licenses/LICENSE-2.0
290 or in
291     http://svn.secondlife.com/svn/linden/projects/2008/pyogp/LICENSE.txt
292
293 $/LicenseInfo$
294 """
295
Note: See TracBrowser for help on using the browser.