DistributedCartesianGrid.py 13 KB

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