DirectGeometry.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. from panda3d.core import *
  2. from DirectGlobals import *
  3. from DirectUtil import *
  4. import math
  5. class LineNodePath(NodePath):
  6. def __init__(self, parent = None, name = None,
  7. thickness = 1.0, colorVec = VBase4(1)):
  8. # Initialize the superclass
  9. NodePath.__init__(self)
  10. if parent is None:
  11. parent = hidden
  12. # Attach a geomNode to the parent and set self to be
  13. # the resulting node path
  14. self.lineNode = GeomNode("lineNode")
  15. self.assign(parent.attachNewNode(self.lineNode))
  16. if name:
  17. self.setName(name)
  18. # Create a lineSegs object to hold the line
  19. ls = self.lineSegs = LineSegs()
  20. # Initialize the lineSegs parameters
  21. ls.setThickness(thickness)
  22. ls.setColor(colorVec)
  23. def moveTo(self, *_args):
  24. apply(self.lineSegs.moveTo, _args)
  25. def drawTo(self, *_args):
  26. apply(self.lineSegs.drawTo, _args)
  27. def create(self, frameAccurate = 0):
  28. self.lineSegs.create(self.lineNode, frameAccurate)
  29. def reset(self):
  30. self.lineSegs.reset()
  31. self.lineNode.removeAllGeoms()
  32. def isEmpty(self):
  33. return self.lineSegs.isEmpty()
  34. def setThickness(self, thickness):
  35. self.lineSegs.setThickness(thickness)
  36. def setColor(self, *_args):
  37. apply(self.lineSegs.setColor, _args)
  38. def setVertex(self, *_args):
  39. apply(self.lineSegs.setVertex, _args)
  40. def setVertexColor(self, vertex, *_args):
  41. apply(self.lineSegs.setVertexColor, (vertex,) + _args)
  42. def getCurrentPosition(self):
  43. return self.lineSegs.getCurrentPosition()
  44. def getNumVertices(self):
  45. return self.lineSegs.getNumVertices()
  46. def getVertex(self, index):
  47. return self.lineSegs.getVertex(index)
  48. def getVertexColor(self):
  49. return self.lineSegs.getVertexColor()
  50. def drawArrow(self, sv, ev, arrowAngle, arrowLength):
  51. """
  52. Do the work of moving the cursor around to draw an arrow from
  53. sv to ev. Hack: the arrows take the z value of the end point
  54. """
  55. self.moveTo(sv)
  56. self.drawTo(ev)
  57. v = sv - ev
  58. # Find the angle of the line
  59. angle = math.atan2(v[1], v[0])
  60. # Get the arrow angles
  61. a1 = angle + deg2Rad(arrowAngle)
  62. a2 = angle - deg2Rad(arrowAngle)
  63. # Get the arrow points
  64. a1x = arrowLength * math.cos(a1)
  65. a1y = arrowLength * math.sin(a1)
  66. a2x = arrowLength * math.cos(a2)
  67. a2y = arrowLength * math.sin(a2)
  68. z = ev[2]
  69. self.moveTo(ev)
  70. self.drawTo(Point3(ev + Point3(a1x, a1y, z)))
  71. self.moveTo(ev)
  72. self.drawTo(Point3(ev + Point3(a2x, a2y, z)))
  73. def drawArrow2d(self, sv, ev, arrowAngle, arrowLength):
  74. """
  75. Do the work of moving the cursor around to draw an arrow from
  76. sv to ev. Hack: the arrows take the z value of the end point
  77. """
  78. self.moveTo(sv)
  79. self.drawTo(ev)
  80. v = sv - ev
  81. # Find the angle of the line
  82. angle = math.atan2(v[2], v[0])
  83. # Get the arrow angles
  84. a1 = angle + deg2Rad(arrowAngle)
  85. a2 = angle - deg2Rad(arrowAngle)
  86. # Get the arrow points
  87. a1x = arrowLength * math.cos(a1)
  88. a1y = arrowLength * math.sin(a1)
  89. a2x = arrowLength * math.cos(a2)
  90. a2y = arrowLength * math.sin(a2)
  91. self.moveTo(ev)
  92. self.drawTo(Point3(ev + Point3(a1x, 0.0, a1y)))
  93. self.moveTo(ev)
  94. self.drawTo(Point3(ev + Point3(a2x, 0.0, a2y)))
  95. def drawLines(self, lineList):
  96. """
  97. Given a list of lists of points, draw a separate line for each list
  98. """
  99. for pointList in lineList:
  100. apply(self.moveTo, pointList[0])
  101. for point in pointList[1:]:
  102. apply(self.drawTo, point)
  103. ##
  104. ## Given a point in space, and a direction, find the point of intersection
  105. ## of that ray with a plane at the specified origin, with the specified normal
  106. def planeIntersect (lineOrigin, lineDir, planeOrigin, normal):
  107. t = 0
  108. offset = planeOrigin - lineOrigin
  109. t = offset.dot(normal) / lineDir.dot(normal)
  110. hitPt = lineDir * t
  111. return hitPt + lineOrigin
  112. def getNearProjectionPoint(nodePath):
  113. # Find the position of the projection of the specified node path
  114. # on the near plane
  115. origin = nodePath.getPos(base.direct.camera)
  116. # project this onto near plane
  117. if origin[1] != 0.0:
  118. return origin * (base.direct.dr.near / origin[1])
  119. else:
  120. # Object is coplaner with camera, just return something reasonable
  121. return Point3(0, base.direct.dr.near, 0)
  122. def getScreenXY(nodePath):
  123. # Where does the node path's projection fall on the near plane
  124. nearVec = getNearProjectionPoint(nodePath)
  125. # Clamp these coordinates to visible screen
  126. nearX = CLAMP(nearVec[0], base.direct.dr.left, base.direct.dr.right)
  127. nearY = CLAMP(nearVec[2], base.direct.dr.bottom, base.direct.dr.top)
  128. # What percentage of the distance across the screen is this?
  129. percentX = (nearX - base.direct.dr.left)/base.direct.dr.nearWidth
  130. percentY = (nearY - base.direct.dr.bottom)/base.direct.dr.nearHeight
  131. # Map this percentage to the same -1 to 1 space as the mouse
  132. screenXY = Vec3((2 * percentX) - 1.0, nearVec[1], (2 * percentY) - 1.0)
  133. # Return the resulting value
  134. return screenXY
  135. def getCrankAngle(center):
  136. # Used to compute current angle of mouse (relative to the coa's
  137. # origin) in screen space
  138. x = base.direct.dr.mouseX - center[0]
  139. y = base.direct.dr.mouseY - center[2]
  140. return (180 + rad2Deg(math.atan2(y, x)))
  141. def relHpr(nodePath, base, h, p, r):
  142. # Compute nodePath2newNodePath relative to base coordinate system
  143. # nodePath2base
  144. mNodePath2Base = nodePath.getMat(base)
  145. # delta scale, orientation, and position matrix
  146. mBase2NewBase = Mat4(Mat4.identMat()) # [gjeon] fixed to give required argument
  147. composeMatrix(mBase2NewBase, UNIT_VEC, VBase3(h, p, r), ZERO_VEC,
  148. CSDefault)
  149. # base2nodePath
  150. mBase2NodePath = base.getMat(nodePath)
  151. # nodePath2 Parent
  152. mNodePath2Parent = nodePath.getMat()
  153. # Compose the result
  154. resultMat = mNodePath2Base * mBase2NewBase
  155. resultMat = resultMat * mBase2NodePath
  156. resultMat = resultMat * mNodePath2Parent
  157. # Extract and apply the hpr
  158. hpr = Vec3(0)
  159. decomposeMatrix(resultMat, VBase3(), hpr, VBase3(),
  160. CSDefault)
  161. nodePath.setHpr(hpr)
  162. # Quaternion interpolation
  163. def qSlerp(startQuat, endQuat, t):
  164. startQ = Quat(startQuat)
  165. destQuat = Quat(Quat.identQuat())
  166. # Calc dot product
  167. cosOmega = (startQ.getI() * endQuat.getI() +
  168. startQ.getJ() * endQuat.getJ() +
  169. startQ.getK() * endQuat.getK() +
  170. startQ.getR() * endQuat.getR())
  171. # If the above dot product is negative, it would be better to
  172. # go between the negative of the initial and the final, so that
  173. # we take the shorter path.
  174. if cosOmega < 0.0:
  175. cosOmega *= -1
  176. startQ.setI(-1 * startQ.getI())
  177. startQ.setJ(-1 * startQ.getJ())
  178. startQ.setK(-1 * startQ.getK())
  179. startQ.setR(-1 * startQ.getR())
  180. if ((1.0 + cosOmega) > Q_EPSILON):
  181. # usual case
  182. if ((1.0 - cosOmega) > Q_EPSILON):
  183. # usual case
  184. omega = math.acos(cosOmega)
  185. sinOmega = math.sin(omega)
  186. startScale = math.sin((1.0 - t) * omega)/sinOmega
  187. endScale = math.sin(t * omega)/sinOmega
  188. else:
  189. # ends very close
  190. startScale = 1.0 - t
  191. endScale = t
  192. destQuat.setI(startScale * startQ.getI() +
  193. endScale * endQuat.getI())
  194. destQuat.setJ(startScale * startQ.getJ() +
  195. endScale * endQuat.getJ())
  196. destQuat.setK(startScale * startQ.getK() +
  197. endScale * endQuat.getK())
  198. destQuat.setR(startScale * startQ.getR() +
  199. endScale * endQuat.getR())
  200. else:
  201. # ends nearly opposite
  202. destQuat.setI(-startQ.getJ())
  203. destQuat.setJ(startQ.getI())
  204. destQuat.setK(-startQ.getR())
  205. destQuat.setR(startQ.getK())
  206. startScale = math.sin((0.5 - t) * math.pi)
  207. endScale = math.sin(t * math.pi)
  208. destQuat.setI(startScale * startQ.getI() +
  209. endScale * endQuat.getI())
  210. destQuat.setJ(startScale * startQ.getJ() +
  211. endScale * endQuat.getJ())
  212. destQuat.setK(startScale * startQ.getK() +
  213. endScale * endQuat.getK())
  214. return destQuat