DistributedCartesianGrid.py 14 KB

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