DistributedCartesianGrid.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. from pandac.PandaModules import *
  2. from direct.interval.IntervalGlobal import *
  3. from direct.distributed.DistributedNode import DistributedNode
  4. from direct.task import Task
  5. from direct.gui import DirectGuiGlobals
  6. from direct.showbase.EventGroup import EventGroup
  7. if __debug__:
  8. # For grid drawing
  9. from direct.directtools.DirectGeometry import *
  10. from direct.showbase.PythonUtil import randFloat
  11. from CartesianGridBase import CartesianGridBase
  12. class DistributedCartesianGrid(DistributedNode, CartesianGridBase):
  13. notify = directNotify.newCategory("DistributedCartesianGrid")
  14. notify.setDebug(0)
  15. VisualizeGrid = config.GetBool("visualize-cartesian-grid", 0)
  16. RuleSeparator = ":"
  17. def __init__(self, cr):
  18. DistributedNode.__init__(self, cr)
  19. # Let the derived classes instantiate the NodePath
  20. self.visAvatar = None
  21. self.gridVisContext = None
  22. # Do we have grid lines visualized?
  23. if __debug__:
  24. self.haveGridLines = 0
  25. def generate(self):
  26. DistributedNode.generate(self)
  27. def disable(self):
  28. DistributedNode.disable(self)
  29. self.stopProcessVisibility()
  30. def delete(self):
  31. DistributedNode.delete(self)
  32. # TODO: when teleporting off an island...
  33. taskMgr.remove(self.taskName("processVisibility"))
  34. def isGridParent(self):
  35. # If this distributed object is a DistributedGrid return 1. 0 by default
  36. return 1
  37. def setParentingRules(self, style, rule):
  38. assert self.notify.debug("setParentingRules: style: %s, rule: %s" % (style, rule))
  39. rules = rule.split(self.RuleSeparator)
  40. assert len(rules) == 3
  41. self.style = style
  42. self.startingZone = int(rules[0])
  43. self.gridSize = int(rules[1])
  44. self.viewingRadius = int(rules[2])
  45. # Store the center of the grid
  46. cx = self.cellWidth * self.gridSize/2.0
  47. self.centerPos = Vec3(cx, cx, 0)
  48. if __debug__:
  49. if self.VisualizeGrid:
  50. self.visualizeGrid()
  51. def getCenterPos(self):
  52. return self.centerPos
  53. def startProcessVisibility(self, avatar):
  54. assert not self.cr._noNewInterests
  55. if self.cr.noNewInterests():
  56. self.notify.warning(
  57. 'startProcessVisibility(%s): tried to open a new interest during logout'
  58. % self.doId)
  59. return
  60. self.stopProcessVisibility()
  61. self.acceptOnce(self.cr.StopVisibilityEvent, self.stopProcessVisibility)
  62. self.visAvatar = avatar
  63. self.visZone = None
  64. self.gridVisContext = self.cr.addInterest(
  65. self.getDoId(), 0, self.uniqueName("visibility"))
  66. taskMgr.add(
  67. self.processVisibility, self.taskName("processVisibility"))
  68. def stopProcessVisibility(self, clearAll=False, event=None):
  69. self.ignore(self.cr.StopVisibilityEvent)
  70. taskMgr.remove(self.taskName("processVisibility"))
  71. if event is not None:
  72. eventGroup = EventGroup('DistCartesianGrid.stopProcessVis',
  73. doneEvent=event)
  74. if self.gridVisContext is not None:
  75. if event is not None:
  76. removeEvent = eventGroup.newEvent('%s.removeInterest' % self.doId)
  77. else:
  78. removeEvent = None
  79. self.cr.removeInterest(self.gridVisContext, removeEvent)
  80. self.gridVisContext = None
  81. else:
  82. # if we were given an event but we have not interest open,
  83. # just send the event right away
  84. if event is not None:
  85. messenger.send(event)
  86. self.visAvatar = None
  87. self.visZone = None
  88. # sometimes we also need to remove vis avatar from
  89. # my parent if it is also a grid
  90. if (clearAll):
  91. if event is not None:
  92. parentEvent = eventGroup.newEvent('%s.parent.removeInterest' % self.doId)
  93. else:
  94. parentEvent = None
  95. self.cr.doId2do[self.parentId].worldGrid.stopProcessVisibility(event=parentEvent)
  96. def processVisibility(self, task):
  97. pos = self.visAvatar.getPos(self)
  98. # Check to make sure our x and y are positive
  99. dx = self.cellWidth * self.gridSize * .5
  100. x = pos[0] + dx
  101. y = pos[1] + dx
  102. col = x // self.cellWidth
  103. row = y // self.cellWidth
  104. assert self.notify.debug(
  105. "processVisibility: %s: avatar pos: %s %s" % (self.doId, x, y))
  106. if (row < 0) or (col < 0) or (row > self.gridSize) or (col > self.gridSize):
  107. assert self.notify.debug("processVisibility: %s: not on the grid" % (self.doId))
  108. # If we are viewingRadius away from this entire grid,
  109. # remove interest in any current visZone we may have
  110. if self.gridVisContext:
  111. self.cr.removeInterest(self.gridVisContext)
  112. self.visZone = None
  113. self.gridVisContext = None
  114. return Task.cont
  115. # Compute which zone we are in
  116. zoneId = int(self.startingZone + ((row * self.gridSize) + col))
  117. assert self.notify.debug("processVisibility: %s: row: %s col: %s zoneId: %s" %
  118. (self.doId, row, col, zoneId))
  119. if (zoneId == self.visZone):
  120. assert self.notify.debug(
  121. "processVisibility: %s: interest did not change" % (self.doId))
  122. return Task.cont
  123. else:
  124. assert self.notify.debug(
  125. "processVisibility: %s: new interest" % (self.doId))
  126. self.visZone = zoneId
  127. if not self.gridVisContext:
  128. self.gridVisContext = self.cr.addInterest(
  129. self.getDoId(), self.visZone,
  130. self.uniqueName("visibility"))
  131. else:
  132. assert self.notify.debug(
  133. "processVisibility: %s: altering interest to zoneId: %s" %
  134. (self.doId, zoneId))
  135. self.cr.alterInterest(
  136. self.gridVisContext, self.getDoId(), self.visZone)
  137. # If the visAvatar is parented to this grid, also do a
  138. # setLocation
  139. parentId = self.visAvatar.parentId
  140. oldZoneId = self.visAvatar.zoneId
  141. assert self.notify.debug(
  142. "processVisibility: %s: parentId: %s oldZoneId: %s" %
  143. (self.doId, parentId, oldZoneId))
  144. if parentId == self.doId:
  145. assert self.notify.debug(
  146. "processVisibility: %s: changing location" %
  147. (self.doId))
  148. self.handleAvatarZoneChange(self.visAvatar, zoneId)
  149. return Task.cont
  150. # Take an avatar (or other object) from somewhere in the world and
  151. # wrtReparent him to the grid.
  152. def addObjectToGrid(self, av):
  153. self.notify.debug("addObjectToGrid %s" % av)
  154. # Get our pos relative to the island grid
  155. pos = av.getPos(self)
  156. # Figure out what zone in that island grid
  157. zoneId = self.getZoneFromXYZ(pos)
  158. # Do the wrtReparenting to the grid node
  159. self.handleAvatarZoneChange(av, zoneId)
  160. def removeObjectFromGrid(self, av):
  161. self.notify.debug("removeObjectFromGrid %s" % av)
  162. # TODO: WHAT LOCATION SHOULD WE SET THIS TO?
  163. #av.reparentTo(hidden)
  164. if (av.getParent().compareTo(self) == 0):
  165. # only detach if object is directly parented
  166. av.detachNode()
  167. #av.b_setLocation(0, 0)
  168. def handleAvatarZoneChange(self, av, zoneId):
  169. assert self.notify.debug("handleAvatarZoneChange(%s, %s)" % (av.doId, zoneId))
  170. # This method can be overridden by derived classes that
  171. # want to do some special management when the avatar changes
  172. # zones.
  173. # Make sure this is a valid zone
  174. if not self.isValidZone(zoneId):
  175. self.notify.warning("handleAvatarZoneChange: not a valid zone (%s)" % zoneId)
  176. return
  177. # Set the location on the server
  178. av.b_setLocation(self.doId, zoneId)
  179. ##################################################
  180. # Visualization Tools
  181. ##################################################
  182. if __debug__:
  183. def initializeGridLines(self):
  184. # Grid Lines
  185. self.gridColor = VBase4(0.4 + randFloat(0.4),
  186. 0.4 + randFloat(0.4),
  187. 0.4 + randFloat(0.4),
  188. 1)
  189. # A Dark version of the grid color
  190. color = self.gridColor * 0.5
  191. color.setW(1)
  192. self.lines = self.attachNewNode('gridLines')
  193. self.minorLines = LineNodePath(self.lines)
  194. self.minorLines.lineNode.setName('minorLines')
  195. self.minorLines.setColor(color)
  196. self.minorLines.setThickness(1)
  197. self.majorLines = LineNodePath(self.lines)
  198. self.majorLines.lineNode.setName('majorLines')
  199. self.majorLines.setColor(color)
  200. self.majorLines.setThickness(5)
  201. self.centerLines = LineNodePath(self.lines)
  202. self.centerLines.lineNode.setName('centerLines')
  203. self.centerLines.setColor(VBase4(1, 0, 0, 0))
  204. self.centerLines.setThickness(3)
  205. # Load up grid parts to initialize grid object
  206. # Polygon used to mark grid plane
  207. # self.gridBack = loader.loadModel('models/misc/gridBack')
  208. # self.gridBack.reparentTo(self)
  209. # self.gridBack.setColor(0.2, 0.2, 0.2, 0.5)
  210. self.cellLabelParent = None
  211. self.markerParent = None
  212. self.haveGridLines = 1
  213. def updateGrid(self):
  214. # Update grid lines based upon current grid spacing and grid size
  215. # First reset existing grid lines
  216. self.minorLines.reset()
  217. self.majorLines.reset()
  218. self.centerLines.reset()
  219. # Now redraw lines
  220. numLines = self.gridSize
  221. scaledSize = numLines * self.cellWidth / 2.0
  222. center = self.centerLines
  223. minor = self.minorLines
  224. major = self.majorLines
  225. cw = self.cellWidth
  226. dx = cw * self.gridSize * .5
  227. for i in range(numLines+1):
  228. icw = i * cw - dx
  229. if i == numLines/2:
  230. center.moveTo(icw, -scaledSize, 0)
  231. center.drawTo(icw, scaledSize, 0)
  232. center.moveTo(-scaledSize, icw, 0)
  233. center.drawTo(scaledSize, icw, 0)
  234. else:
  235. if (i % 5) == 0:
  236. major.moveTo(icw, -scaledSize, 0)
  237. major.drawTo(icw, scaledSize, 0)
  238. major.moveTo(-scaledSize, icw, 0)
  239. major.drawTo(scaledSize, icw, 0)
  240. else:
  241. minor.moveTo(icw, -scaledSize, 0)
  242. minor.drawTo(icw, scaledSize, 0)
  243. minor.moveTo(-scaledSize, icw, 0)
  244. minor.drawTo(scaledSize, icw, 0)
  245. center.create()
  246. minor.create()
  247. major.create()
  248. # self.gridBack.setScale(scaledSize)
  249. self.labelCells()
  250. def labelCells(self):
  251. if self.cellLabelParent:
  252. self.cellLabelParent.removeNode()
  253. self.cellLabelParent = self.attachNewNode('cellLabels')
  254. cw = self.cellWidth
  255. scale = cw / 10.0
  256. dx = cw * self.gridSize * .5
  257. font = DirectGuiGlobals.getDefaultFont()
  258. color = self.gridColor
  259. for i in range(self.gridSize):
  260. for j in range(self.gridSize):
  261. zoneId = self.startingZone + ((j * self.gridSize) + i)
  262. zoneStr = str(zoneId)
  263. textNode = TextNode(zoneStr)
  264. textNode.setText(zoneStr)
  265. textNode.setFont(font)
  266. textNode.setTextColor(color)
  267. textNode.setAlign(TextNode.ACenter)
  268. genTextNode = textNode.generate()
  269. textNodePath = self.cellLabelParent.attachNewNode(genTextNode)
  270. # Place the text node in the center of the cell
  271. textNodePath.setPosHprScale((i * cw - dx) + (cw * 0.5), # x
  272. (j * cw - dx) + (cw * 0.5), # y
  273. 3.0, # z
  274. # Lay them down flat
  275. 0, -90, 0, # hpr
  276. scale, scale, scale)
  277. self.cellLabelParent.flattenLight()
  278. def markCells(self):
  279. if self.markerParent:
  280. self.markerParent.removeNode()
  281. self.markerParent = self.attachNewNode('markers')
  282. self.cellMarkers = []
  283. dx = self.cellWidth * self.gridSize * .5
  284. for i in range(self.gridSize):
  285. for j in range(self.gridSize):
  286. marker = loader.loadModelCopy("models/misc/smiley")
  287. marker.reparentTo(self.markerParent)
  288. marker.setPos(i * self.cellWidth - dx,
  289. j * self.cellWidth - dx,
  290. 1.0)
  291. marker.setScale(5)
  292. self.cellMarkers.append(marker)
  293. def unmarkCells(self):
  294. if self.markerParent:
  295. self.markerParent.removeNode()
  296. self.markerParent = None
  297. def visualizeGrid(self):
  298. if not self.haveGridLines:
  299. self.initializeGridLines()
  300. self.updateGrid()