Transitions.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. """Undocumented Module"""
  2. __all__ = ['Transitions']
  3. from pandac.PandaModules import *
  4. from direct.gui.DirectGui import *
  5. from direct.task import Task
  6. from direct.interval.IntervalGlobal import *
  7. class Transitions:
  8. # These may be reassigned before the fade or iris transitions are
  9. # actually invoked to change the models that will be used.
  10. IrisModelName = "models/misc/iris"
  11. FadeModelName = "models/misc/fade"
  12. def __init__(self, loader,
  13. model=None,
  14. scale=3.0,
  15. pos=Vec3(0, 0, 0)):
  16. self.transitionIval = None
  17. self.letterboxIval = None
  18. self.iris = None
  19. self.fade = None
  20. self.letterbox = None
  21. self.fadeModel = model
  22. self.imagePos = pos
  23. if model:
  24. self.alphaOff = Vec4(1, 1, 1, 0)
  25. self.alphaOn = Vec4(1, 1, 1, 1)
  26. model.setTransparency(1)
  27. self.lerpFunc = LerpColorScaleInterval
  28. else:
  29. self.alphaOff = Vec4(0, 0, 0, 0)
  30. self.alphaOn = Vec4(0, 0, 0, 1)
  31. self.lerpFunc = LerpColorInterval
  32. self.irisTaskName = "irisTask"
  33. self.fadeTaskName = "fadeTask"
  34. self.letterboxTaskName = "letterboxTask"
  35. def __del__(self):
  36. if self.fadeModel:
  37. self.fadeModel.removeNode()
  38. self.fadeModel = None
  39. ##################################################
  40. # Fade
  41. ##################################################
  42. # We can set a custom model for the fade before using it for the first time
  43. def setFadeModel(self, model, scale=1.0):
  44. self.fadeModel = model
  45. # We have to change some default parameters for a custom fadeModel
  46. self.alphaOn = Vec4(1, 1, 1, 1)
  47. # Reload fade if its already been created
  48. if self.fade:
  49. del self.fade
  50. self.fade = None
  51. self.loadFade()
  52. def loadFade(self):
  53. if not self.fadeModel:
  54. self.fadeModel = loader.loadModel(self.FadeModelName)
  55. if self.fade == None:
  56. # We create a DirectFrame for the fade polygon, instead of
  57. # simply loading the polygon model and using it directly,
  58. # so that it will also obscure mouse events for objects
  59. # positioned behind it.
  60. self.fade = DirectFrame(
  61. parent = hidden,
  62. guiId = 'fade',
  63. relief = None,
  64. image = self.fadeModel,
  65. image_scale = 2,
  66. state = DGG.NORMAL,
  67. )
  68. self.fade.setBin('unsorted', 0)
  69. self.fade.setColor(0,0,0,0)
  70. def getFadeInIval(self, t=0.5, finishIval=None):
  71. """
  72. Returns an interval without starting it. This is particularly useful in
  73. cutscenes, so when the cutsceneIval is escaped out of we can finish the fade immediately
  74. """
  75. #self.noTransitions() masad: this creates a one frame pop, is it necessary?
  76. self.loadFade()
  77. transitionIval = Sequence(Func(self.fade.reparentTo, render2d, FADE_SORT_INDEX),
  78. self.lerpFunc(self.fade, t,
  79. self.alphaOff,
  80. # self.alphaOn,
  81. ),
  82. Func(self.fade.detachNode),
  83. name = self.fadeTaskName,
  84. )
  85. if finishIval:
  86. transitionIval.append(finishIval)
  87. return transitionIval
  88. def getFadeOutIval(self, t=0.5, finishIval=None):
  89. """
  90. Create a sequence that lerps the color out, then
  91. parents the fade to hidden
  92. """
  93. self.noTransitions()
  94. self.loadFade()
  95. transitionIval = Sequence(Func(self.fade.reparentTo,render2d,FADE_SORT_INDEX),
  96. self.lerpFunc(self.fade, t,
  97. self.alphaOn,
  98. # self.alphaOff,
  99. ),
  100. name = self.fadeTaskName,
  101. )
  102. if finishIval:
  103. transitionIval.append(finishIval)
  104. return transitionIval
  105. def fadeIn(self, t=0.5, finishIval=None):
  106. """
  107. Play a fade in transition over t seconds.
  108. Places a polygon on the aspect2d plane then lerps the color
  109. from black to transparent. When the color lerp is finished, it
  110. parents the fade polygon to hidden.
  111. """
  112. if (t == 0):
  113. # Fade in immediately with no lerp
  114. #print "transitiosn: fadeIn 0.0"
  115. self.noTransitions()
  116. self.loadFade()
  117. self.fade.detachNode()
  118. else:
  119. # Create a sequence that lerps the color out, then
  120. # parents the fade to hidden
  121. self.transitionIval = self.getFadeInIval(t, finishIval)
  122. self.transitionIval.start()
  123. def fadeOut(self, t=0.5, finishIval=None):
  124. """
  125. Play a fade out transition over t seconds.
  126. Places a polygon on the aspect2d plane then lerps the color
  127. from transparent to full black. When the color lerp is finished,
  128. it leaves the fade polygon covering the aspect2d plane until you
  129. fadeIn or call noFade.
  130. lerp
  131. """
  132. if base.config.GetBool('no-loading-screen',0):
  133. return
  134. if (t == 0):
  135. # Fade out immediately with no lerp
  136. self.noTransitions()
  137. self.loadFade()
  138. self.fade.reparentTo(render2d, FADE_SORT_INDEX)
  139. self.fade.setColor(self.alphaOn)
  140. else:
  141. # Create a sequence that lerps the color out, then
  142. # parents the fade to hidden
  143. self.transitionIval = self.getFadeOutIval(t,finishIval)
  144. self.transitionIval.start()
  145. def fadeOutActive(self):
  146. return self.fade and self.fade.getColor()[3] > 0
  147. def fadeScreen(self, alpha=0.5):
  148. """
  149. Put a semitransparent screen over the camera plane
  150. to darken out the world. Useful for drawing attention to
  151. a dialog box for instance
  152. """
  153. #print "transitiosn: fadeScreen"
  154. self.noTransitions()
  155. self.loadFade()
  156. self.fade.reparentTo(render2d, FADE_SORT_INDEX)
  157. self.fade.setColor(self.alphaOn[0],
  158. self.alphaOn[1],
  159. self.alphaOn[2],
  160. alpha)
  161. def fadeScreenColor(self, color):
  162. """
  163. Put a semitransparent screen over the camera plane
  164. to darken out the world. Useful for drawing attention to
  165. a dialog box for instance
  166. """
  167. #print "transitiosn: fadeScreenColor"
  168. self.noTransitions()
  169. self.loadFade()
  170. self.fade.reparentTo(render2d, FADE_SORT_INDEX)
  171. self.fade.setColor(color)
  172. def noFade(self):
  173. """
  174. Removes any current fade tasks and parents the fade polygon away
  175. """
  176. #print "transitiosn: noFade"
  177. if self.transitionIval:
  178. self.transitionIval.pause()
  179. self.transitionIval = None
  180. if self.fade:
  181. # Make sure to reset the color, since fadeOutActive() is looking at it
  182. self.fade.setColor(self.alphaOff)
  183. self.fade.detachNode()
  184. def setFadeColor(self, r, g, b):
  185. self.alphaOn.set(r, g, b, 1)
  186. self.alphaOff.set(r, g, b, 0)
  187. ##################################################
  188. # Iris
  189. ##################################################
  190. def loadIris(self):
  191. if self.iris == None:
  192. self.iris = loader.loadModel(self.IrisModelName)
  193. self.iris.setPos(0, 0, 0)
  194. def irisIn(self, t=0.5, finishIval=None):
  195. """
  196. Play an iris in transition over t seconds.
  197. Places a polygon on the aspect2d plane then lerps the scale
  198. of the iris polygon up so it looks like we iris in. When the
  199. scale lerp is finished, it parents the iris polygon to hidden.
  200. """
  201. self.noTransitions()
  202. self.loadIris()
  203. if (t == 0):
  204. self.iris.detachNode()
  205. else:
  206. self.iris.reparentTo(aspect2d, FADE_SORT_INDEX)
  207. self.transitionIval = Sequence(LerpScaleInterval(self.iris, t,
  208. scale = 0.18,
  209. startScale = 0.01),
  210. Func(self.iris.detachNode),
  211. name = self.irisTaskName,
  212. )
  213. if finishIval:
  214. self.transitionIval.append(finishIval)
  215. self.transitionIval.start()
  216. def irisOut(self, t=0.5, finishIval=None):
  217. """
  218. Play an iris out transition over t seconds.
  219. Places a polygon on the aspect2d plane then lerps the scale
  220. of the iris down so it looks like we iris out. When the scale
  221. lerp is finished, it leaves the iris polygon covering the
  222. aspect2d plane until you irisIn or call noIris.
  223. """
  224. self.noTransitions()
  225. self.loadIris()
  226. self.loadFade() # we need this to cover up the hole.
  227. if (t == 0):
  228. self.iris.detachNode()
  229. self.fadeOut(0)
  230. else:
  231. self.iris.reparentTo(aspect2d, FADE_SORT_INDEX)
  232. self.transitionIval = Sequence(LerpScaleInterval(self.iris, t,
  233. scale = 0.01,
  234. startScale = 0.18),
  235. Func(self.iris.detachNode),
  236. # Use the fade to cover up the hole that the iris would leave
  237. Func(self.fadeOut, 0),
  238. name = self.irisTaskName,
  239. )
  240. if finishIval:
  241. self.transitionIval.append(finishIval)
  242. self.transitionIval.start()
  243. def noIris(self):
  244. """
  245. Removes any current iris tasks and parents the iris polygon away
  246. """
  247. if self.transitionIval:
  248. self.transitionIval.pause()
  249. self.transitionIval = None
  250. if self.iris != None:
  251. self.iris.detachNode()
  252. # Actually we need to remove the fade too,
  253. # because the iris effect uses it.
  254. self.noFade()
  255. def noTransitions(self):
  256. """
  257. This call should immediately remove any and all transitions running
  258. """
  259. self.noFade()
  260. self.noIris()
  261. # Letterbox is not really a transition, it is a screen overlay
  262. # self.noLetterbox()
  263. ##################################################
  264. # Letterbox
  265. ##################################################
  266. def loadLetterbox(self):
  267. if not self.letterbox:
  268. # We create a DirectFrame for the fade polygon, instead of
  269. # simply loading the polygon model and using it directly,
  270. # so that it will also obscure mouse events for objects
  271. # positioned behind it.
  272. self.letterbox = NodePath("letterbox")
  273. # Allow fade in and out of the bars
  274. self.letterbox.setTransparency(1)
  275. # Allow DirectLabels to be parented to the letterbox sensibly
  276. self.letterbox.setBin('unsorted', 0)
  277. # Allow a custom look to the letterbox graphic.
  278. # TODO: This model isn't available everywhere. We should
  279. # pass it in as a parameter. In the meantime, at least
  280. # set the LoaderOptions so there will be no error message
  281. # if it fails to load.
  282. options = LoaderOptions()
  283. options.setFlags(options.getFlags() & ~options.LFReportErrors)
  284. button = loader.loadModel('models/gui/toplevel_gui',
  285. loaderOptions = options)
  286. barImage = None
  287. if button:
  288. barImage = button.find('**/generic_button')
  289. self.letterboxTop = DirectFrame(
  290. parent = self.letterbox,
  291. guiId = 'letterboxTop',
  292. relief = DGG.FLAT,
  293. state = DGG.NORMAL,
  294. frameColor = (0, 0, 0, 1),
  295. borderWidth = (0, 0),
  296. frameSize = (-1, 1, 0, 0.2),
  297. pos = (0, 0, 0.8),
  298. image = barImage,
  299. image_scale = (2.25,1,.5),
  300. image_pos = (0,0,.1),
  301. image_color = (0.3,0.3,0.3,1),
  302. sortOrder = 0,
  303. )
  304. self.letterboxBottom = DirectFrame(
  305. parent = self.letterbox,
  306. guiId = 'letterboxBottom',
  307. relief = DGG.FLAT,
  308. state = DGG.NORMAL,
  309. frameColor = (0, 0, 0, 1),
  310. borderWidth = (0, 0),
  311. frameSize = (-1, 1, 0, 0.2),
  312. pos = (0, 0, -1),
  313. image = barImage,
  314. image_scale = (2.25,1,.5),
  315. image_pos = (0,0,.1),
  316. image_color = (0.3,0.3,0.3,1),
  317. sortOrder = 0,
  318. )
  319. # masad: always place these at the bottom of render
  320. self.letterboxTop.setBin('sorted',0)
  321. self.letterboxBottom.setBin('sorted',0)
  322. self.letterbox.reparentTo(render2d, -1)
  323. self.letterboxOff(0)
  324. def noLetterbox(self):
  325. """
  326. Removes any current letterbox tasks and parents the letterbox polygon away
  327. """
  328. if self.letterboxIval:
  329. self.letterboxIval.pause()
  330. self.letterboxIval = None
  331. if self.letterbox:
  332. self.letterbox.stash()
  333. def letterboxOn(self, t=0.25, finishIval=None):
  334. """
  335. Move black bars in over t seconds.
  336. """
  337. self.noLetterbox()
  338. self.loadLetterbox()
  339. self.letterbox.unstash()
  340. if (t == 0):
  341. self.letterboxBottom.setPos(0, 0, -1)
  342. self.letterboxTop.setPos(0, 0, 0.8)
  343. else:
  344. self.letterboxIval = Sequence(Parallel(
  345. LerpPosInterval(self.letterboxBottom,
  346. t,
  347. pos = Vec3(0, 0, -1),
  348. #startPos = Vec3(0, 0, -1.2),
  349. ),
  350. LerpPosInterval(self.letterboxTop,
  351. t,
  352. pos = Vec3(0, 0, 0.8),
  353. # startPos = Vec3(0, 0, 1),
  354. ),
  355. ),
  356. name = self.letterboxTaskName,
  357. )
  358. if finishIval:
  359. self.letterboxIval.append(finishIval)
  360. self.letterboxIval.start()
  361. def letterboxOff(self, t=0.25, finishIval=None):
  362. """
  363. Move black bars away over t seconds.
  364. """
  365. self.noLetterbox()
  366. self.loadLetterbox()
  367. self.letterbox.unstash()
  368. if (t == 0):
  369. self.letterbox.stash()
  370. else:
  371. self.letterboxIval = Sequence(Parallel(
  372. LerpPosInterval(self.letterboxBottom,
  373. t,
  374. pos = Vec3(0, 0, -1.2),
  375. # startPos = Vec3(0, 0, -1),
  376. ),
  377. LerpPosInterval(self.letterboxTop,
  378. t,
  379. pos = Vec3(0, 0, 1),
  380. # startPos = Vec3(0, 0, 0.8),
  381. ),
  382. ),
  383. Func(self.letterbox.stash),
  384. Func(messenger.send,'letterboxOff'),
  385. name = self.letterboxTaskName,
  386. )
  387. if finishIval:
  388. self.letterboxIval.append(finishIval)
  389. self.letterboxIval.start()