seGeometry.py 9.1 KB

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