Testing out JmonkeyEngine to make a game in Scala with Akka Actors within a pure FP layer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

874 lines
25 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
  1. package wow.doge.mygame
  2. import scala.jdk.CollectionConverters._
  3. import scala.reflect.ClassTag
  4. import akka.actor.typed.ActorRef
  5. import akka.actor.typed.Scheduler
  6. import akka.util.Timeout
  7. import com.jayfella.jme.jfx.JavaFxUI
  8. import com.jme3.app.Application
  9. import com.jme3.app.SimpleApplication
  10. import com.jme3.app.state.AppState
  11. import com.jme3.app.state.AppStateManager
  12. import com.jme3.asset.AssetLocator
  13. import com.jme3.asset.AssetManager
  14. import com.jme3.bullet.BulletAppState
  15. import com.jme3.bullet.PhysicsSpace
  16. import com.jme3.bullet.PhysicsTickListener
  17. import com.jme3.bullet.collision.PhysicsCollisionEvent
  18. import com.jme3.bullet.collision.PhysicsCollisionListener
  19. import com.jme3.bullet.control.BetterCharacterControl
  20. import com.jme3.input.Action
  21. import com.jme3.input.InputManager
  22. import com.jme3.input.controls.ActionListener
  23. import com.jme3.input.controls.AnalogListener
  24. import com.jme3.input.controls.InputListener
  25. import com.jme3.input.controls.Trigger
  26. import com.jme3.light.Light
  27. import com.jme3.material.Material
  28. import com.jme3.math.Vector3f
  29. import com.jme3.scene.CameraNode
  30. import com.jme3.scene.Geometry
  31. import com.jme3.scene.Node
  32. import com.jme3.scene.SceneGraphVisitor
  33. import com.jme3.scene.Spatial
  34. import com.jme3.scene.control.CameraControl.ControlDirection
  35. import com.jme3.scene.control.Control
  36. import com.simsilica.es.EntityComponent
  37. import com.simsilica.es.EntityData
  38. import com.simsilica.es.EntityId
  39. import enumeratum._
  40. import io.odin.meta.Position
  41. import io.odin.meta.Render
  42. import monix.bio.Task
  43. import monix.bio.UIO
  44. import monix.execution.Ack
  45. import monix.execution.Ack.Continue
  46. import monix.execution.Ack.Stop
  47. import monix.execution.Cancelable
  48. import monix.execution.cancelables.SingleAssignCancelable
  49. import monix.reactive.Observable
  50. import monix.reactive.OverflowStrategy
  51. import monix.reactive.observers.Subscriber
  52. import org.slf4j.Logger
  53. import wow.doge.mygame.math.ImVector3f
  54. import wow.doge.mygame.state.MyBaseState
  55. final case class ActionEvent(binding: Action, value: Boolean, tpf: Float)
  56. final case class EnumActionEvent[T <: EnumEntry](
  57. binding: T,
  58. value: Boolean,
  59. tpf: Float
  60. )
  61. final case class AnalogEvent(binding: Action, value: Float, tpf: Float)
  62. final case class EnumAnalogEvent[T <: EnumEntry](
  63. binding: T,
  64. value: Float,
  65. tpf: Float
  66. )
  67. final class PrePhysicsTickEvent(val space: PhysicsSpace) extends AnyVal
  68. final class PhysicsTickEvent(val space: PhysicsSpace) extends AnyVal
  69. package object implicits {
  70. type PrePhysicsTickEvent = PhysicsTickEvent
  71. type PhysicsTickObservable =
  72. Observable[Either[PrePhysicsTickEvent, PhysicsTickEvent]]
  73. implicit final class JMEAppExt(private val app: Application) extends AnyVal {
  74. def enqueueR(cb: () => Unit) =
  75. app.enqueue(new Runnable {
  76. override def run() = cb()
  77. })
  78. }
  79. implicit final class StateManagerExt(private val asm: AppStateManager)
  80. extends AnyVal {
  81. def state[S <: AppState]()(implicit c: ClassTag[S]): S =
  82. asm.getState(c.runtimeClass.asInstanceOf[Class[S]])
  83. // def appStates = asm.getStates()
  84. }
  85. implicit final class SimpleApplicationExt[T <: SimpleApplication](
  86. private val sa: T
  87. ) extends AnyVal {
  88. def stateManager: AppStateManager = sa.getStateManager()
  89. def inputManager: InputManager = sa.getInputManager()
  90. def assetManager: AssetManager = sa.getAssetManager()
  91. def guiNode = sa.getGuiNode()
  92. def flyCam = Option(sa.getFlyByCamera())
  93. def camera = sa.getCamera()
  94. def viewPort = sa.getViewPort()
  95. def rootNode = sa.getRootNode()
  96. def observableTick: Observable[Float] =
  97. Observable.create(OverflowStrategy.Unbounded) { sub =>
  98. val c = SingleAssignCancelable()
  99. val as = new MyBaseState {
  100. override def init(): Unit = {}
  101. override def update(tpf: Float) = {
  102. if (sub.onNext(tpf) == Ack.Stop)
  103. c.cancel()
  104. }
  105. override def stop(): Unit = {}
  106. override def onEnable() = {}
  107. override def onDisable() = {}
  108. }
  109. sa.stateManager.attach(as)
  110. c := Cancelable(() => sa.stateManager.detach(as))
  111. c
  112. }
  113. }
  114. implicit final class AssetManagerExt(private val am: AssetManager)
  115. extends AnyVal {
  116. def registerLocator(
  117. assetPath: os.RelPath,
  118. locator: Class[_ <: AssetLocator]
  119. ): Unit = {
  120. am.registerLocator(assetPath.toString(), locator)
  121. }
  122. def loadModel(assetPath: os.RelPath): Spatial = {
  123. am.loadModel(assetPath.toString())
  124. }
  125. }
  126. implicit final class BulletAppStateExt(private val bas: BulletAppState)
  127. extends AnyVal {
  128. def physicsSpace = bas.getPhysicsSpace()
  129. def speed = bas.getSpeed()
  130. }
  131. implicit final class BetterCharacterControlExt(
  132. private val bcc: BetterCharacterControl
  133. ) {
  134. def withJumpForce(force: ImVector3f) = {
  135. bcc.setJumpForce(force.mutable)
  136. bcc
  137. }
  138. }
  139. implicit final class SpatialExt[T <: Spatial](private val spat: T)
  140. extends AnyVal {
  141. // def asRef = Ref[Task].of(spat)
  142. }
  143. implicit final class NodeExt[T <: Node](private val n: T) extends AnyVal {
  144. /**
  145. * Attaches the given child
  146. *
  147. * @param s
  148. * @return
  149. */
  150. def withChild(s: Spatial): Node = {
  151. n.attachChild(s)
  152. n
  153. }
  154. /**
  155. * @return Gets the list of children as a monix observable
  156. */
  157. def observableChildren =
  158. Observable.fromIterable(n.getChildren().asScala)
  159. /**
  160. * @return A copy of the list of children of this node as a lazy list
  161. */
  162. def children = LazyList.from(n.getChildren().asScala)
  163. /**
  164. * Attach given children
  165. *
  166. * @param lst
  167. */
  168. def withChildren(lst: Spatial*): Node = {
  169. for (c <- lst) n.attachChild(c)
  170. n
  171. }
  172. def +=(spatial: Spatial) = n.attachChild(spatial)
  173. def :+(spatial: Spatial) = {
  174. n += spatial
  175. n
  176. }
  177. def :+(light: Light) = {
  178. n.addLight(light)
  179. n
  180. }
  181. def -=(spatial: Spatial) = n.detachChild(spatial)
  182. def :-(spatial: Spatial) = {
  183. n -= spatial
  184. n
  185. }
  186. def :-(light: Light) = {
  187. n.removeLight(light)
  188. n
  189. }
  190. def depthFirst(cb: Spatial => Unit) =
  191. n.depthFirstTraversal(new SceneGraphVisitor() {
  192. override def visit(s: Spatial) = cb(s)
  193. })
  194. def observableDepthFirst(): Observable[Spatial] = {
  195. def loop(
  196. subscriber: Subscriber[Spatial],
  197. spatials: LazyList[Spatial]
  198. ): Task[Unit] =
  199. spatials match {
  200. // spatial can be either a node or a geometry, but it's not a sealed trait
  201. case head #:: tail =>
  202. head match {
  203. case g: Geometry =>
  204. Task.deferFuture(subscriber.onNext(g)).flatMap {
  205. case Continue =>
  206. loop(subscriber, tail)
  207. case Stop =>
  208. Task(subscriber.onComplete())
  209. }
  210. case node: Node =>
  211. val children = node.children
  212. Task.deferFuture(subscriber.onNext(node)).flatMap {
  213. case Continue =>
  214. loop(subscriber, children #::: tail)
  215. case Stop =>
  216. Task(subscriber.onComplete())
  217. }
  218. // case _ => loop(subscriber, tail)
  219. }
  220. case LazyList() => Task.unit
  221. }
  222. Observable.create(OverflowStrategy.Unbounded) { sub =>
  223. implicit val sched = sub.scheduler
  224. loop(sub, LazyList(n)).runToFuture
  225. }
  226. }
  227. def breadthFirst(cb: Spatial => Unit) =
  228. n.breadthFirstTraversal(new SceneGraphVisitor() {
  229. override def visit(s: Spatial) = cb(s)
  230. })
  231. def observableBreadthFirst(): Observable[Spatial] = {
  232. def loop(
  233. subscriber: Subscriber[Spatial],
  234. spatials: LazyList[Spatial]
  235. ): Task[Unit] =
  236. spatials match {
  237. // spatial can be either a node or a geometry, but it's not a sealed trait
  238. case head #:: tail =>
  239. head match {
  240. case g: Geometry =>
  241. Task.deferFuture(subscriber.onNext(g)).flatMap {
  242. case Continue =>
  243. loop(subscriber, tail)
  244. case Stop =>
  245. Task(subscriber.onComplete())
  246. }
  247. case node: Node =>
  248. val children = node.children
  249. Task.deferFuture(subscriber.onNext(node)).flatMap {
  250. case Continue =>
  251. loop(subscriber, tail #::: children)
  252. case Stop =>
  253. Task(subscriber.onComplete())
  254. }
  255. case unknown =>
  256. Task.deferFuture(subscriber.onNext(unknown)).flatMap {
  257. case Continue =>
  258. loop(subscriber, tail)
  259. case Stop =>
  260. Task(subscriber.onComplete())
  261. }
  262. }
  263. case LazyList() => Task.unit
  264. }
  265. Observable.create(OverflowStrategy.Unbounded) { sub =>
  266. implicit val sched = sub.scheduler
  267. loop(sub, LazyList(n)).runToFuture
  268. }
  269. }
  270. def withControl[C <: Control](ctrl: C) = {
  271. n.addControl(ctrl)
  272. n
  273. }
  274. def withLocalTranslation(dir: ImVector3f) = {
  275. n.setLocalTranslation(dir.mutable)
  276. n
  277. }
  278. def withRotate(xAngle: Float, yAngle: Float, zAngle: Float) = {
  279. n.rotate(xAngle, yAngle, zAngle)
  280. n
  281. }
  282. def localRotation = n.getLocalRotation()
  283. def localTranslation = n.getLocalTranslation()
  284. }
  285. implicit final class CameraNodeExt(private val cn: CameraNode) {
  286. def withControlDir(controlDir: ControlDirection) = {
  287. cn.setControlDir(controlDir)
  288. cn
  289. }
  290. def withLookAt(position: ImVector3f, upVector: ImVector3f) = {
  291. cn.lookAt(position.mutable, upVector.mutable)
  292. cn
  293. }
  294. }
  295. implicit final class GeometryExt(private val geom: Geometry) {
  296. def withMaterial(mat: Material) = {
  297. geom.setMaterial(mat)
  298. geom
  299. }
  300. }
  301. implicit final class EntityDataExt(private val ed: EntityData)
  302. extends AnyVal {
  303. def query = new EntityQuery(ed)
  304. // def entities[T <: EntityComponent](entities: Seq[T])
  305. }
  306. implicit final class EntityExt(private val e: EntityId) extends AnyVal {
  307. def withComponents(classes: EntityComponent*)(implicit
  308. ed: EntityData
  309. ): EntityId = {
  310. ed.setComponents(e, classes: _*)
  311. e
  312. }
  313. }
  314. implicit final class ActorRefExt[Req](private val a: ActorRef[Req])
  315. extends AnyVal {
  316. import akka.actor.typed.scaladsl.AskPattern._
  317. /**
  318. * Same as [[ask]] but returns a [[Task]]
  319. */
  320. def askL[Res](
  321. replyTo: ActorRef[Res] => Req
  322. )(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] = {
  323. Task.deferFuture(a.ask(replyTo)(timeout, scheduler))
  324. }
  325. def ??[Res](
  326. replyTo: ActorRef[Res] => Req
  327. )(implicit timeout: Timeout, scheduler: Scheduler): Task[Res] =
  328. askL(replyTo)
  329. /**
  330. * Same as [[tell]], but wrapped in a Task
  331. *
  332. * @param msg
  333. * @return
  334. */
  335. def tellL(msg: Req) = UIO(a.tell(msg))
  336. /**
  337. * Same as [[tell]], but wrapped in a Task
  338. *
  339. * @param msg
  340. * @return
  341. */
  342. def !!(msg: Req) = tellL(msg)
  343. }
  344. implicit final class InputManagerExt(private val inputManager: InputManager)
  345. extends AnyVal {
  346. /**
  347. * Create a new mapping to the given triggers.
  348. *
  349. * <p>
  350. * The given mapping will be assigned to the given triggers, when
  351. * any of the triggers given raise an event, the listeners
  352. * registered to the mappings will receive appropriate events.
  353. *
  354. * @param mappingName The mapping name to assign.
  355. * @param triggers The triggers to which the mapping is to be registered.
  356. */
  357. def withMapping(mapping: String, triggers: Trigger*): InputManager = {
  358. inputManager.addMapping(mapping, triggers: _*)
  359. inputManager
  360. }
  361. def withMapping[T <: EnumEntry](
  362. mapping: T,
  363. triggers: Trigger*
  364. ): InputManager = {
  365. inputManager.addMapping(mapping.entryName, triggers: _*)
  366. inputManager
  367. }
  368. def withListener(listener: InputListener, mappings: String*) = {
  369. inputManager.addListener(listener, mappings: _*)
  370. inputManager
  371. }
  372. /**
  373. * Creates new mappings from the values of the given Enum
  374. *
  375. * <p>
  376. * The given mapping will be assigned to the given triggers, when
  377. * any of the triggers given raise an event, the listeners
  378. * registered to the mappings will receive appropriate events.
  379. *
  380. * @param mappingName The mapping name to assign.
  381. * @param mappingFn Function from enum values to the sequence of trigers.
  382. *
  383. * @example
  384. *
  385. * {{{
  386. *
  387. * sealed trait PlayerAnalogMovementInput extends EnumEntry with UpperSnakecase
  388. * object PlayerAnalogMovementInput extends Enum[PlayerAnalogMovementInput] {
  389. * val values = findValues
  390. * case object TurnRight extends PlayerAnalogMovementInput
  391. * case object TurnLeft extends PlayerAnalogMovementInput
  392. * }
  393. *
  394. * {
  395. * inputManager.withEnumMappings(PlayerAnalogMovementInput) {
  396. * case PlayerAnalogMovementInput.TurnRight =>
  397. * Seq(new KeyTrigger(KeyInput.KEY_RIGHT))
  398. * case PlayerAnalogMovementInput.TurnLeft =>
  399. * Seq(new KeyTrigger(KeyInput.KEY_LEFT))
  400. * }
  401. * }
  402. * }}}
  403. */
  404. def withEnumMappings[T <: EnumEntry](
  405. mappingEnum: Enum[T]
  406. )(mappingFn: T => Seq[Trigger]): InputManager = {
  407. for (entry <- mappingEnum.values) {
  408. val mappings = mappingFn(entry)
  409. inputManager.addMapping(entry.entryName, mappings: _*)
  410. }
  411. inputManager
  412. }
  413. /**
  414. * Create an observable which emits the given mappings as elements of an observable
  415. *
  416. * @param mappingNames
  417. * @return Observable of action events
  418. *
  419. * @see [[ActionEvent]]
  420. * @see [[enumObservableAction]]
  421. */
  422. def observableAction(mappingNames: String*): Observable[ActionEvent] = {
  423. Observable.create(OverflowStrategy.DropOld(10)) { sub =>
  424. val c = SingleAssignCancelable()
  425. val al = new ActionListener {
  426. override def onAction(
  427. binding: String,
  428. value: Boolean,
  429. tpf: Float
  430. ): Unit = {
  431. if (
  432. sub.onNext(ActionEvent(Action(binding), value, tpf)) == Ack.Stop
  433. ) {
  434. sub.onComplete()
  435. c.cancel()
  436. }
  437. }
  438. }
  439. inputManager.addListener(al, mappingNames: _*)
  440. c := Cancelable(() => inputManager.removeListener(al))
  441. c
  442. }
  443. }
  444. /**
  445. * <p>
  446. * Create an observable which emits the values of the given
  447. * enum as elements of an observable
  448. *
  449. * @param mappingNames
  450. * @return Observable of enum values
  451. *
  452. * @example {{{
  453. * inputManager
  454. * .enumObservableAction(PlayerMovementInput)
  455. * .doOnNext { action =>
  456. * action.binding match {
  457. * case PlayerMovementInput.WalkLeft => Task {/* your actions */}
  458. * }
  459. * }
  460. * }}}
  461. *
  462. * @see [[EnumActionEvent]]
  463. * @see [[enumAnalogObservable]]
  464. */
  465. def enumObservableAction[T <: EnumEntry](
  466. mappingEnum: Enum[T]
  467. ): Observable[EnumActionEvent[T]] = {
  468. Observable.create(OverflowStrategy.DropOld(10)) { sub =>
  469. val c = SingleAssignCancelable()
  470. val entryNames = mappingEnum.values.map(_.entryName)
  471. val al = new ActionListener {
  472. override def onAction(
  473. binding: String,
  474. value: Boolean,
  475. tpf: Float
  476. ): Unit = {
  477. mappingEnum.withNameOption(binding).foreach { b =>
  478. if (sub.onNext(EnumActionEvent(b, value, tpf)) == Ack.Stop) {
  479. sub.onComplete()
  480. c.cancel()
  481. }
  482. }
  483. }
  484. }
  485. inputManager.addListener(al, entryNames: _*)
  486. c := Cancelable(() => inputManager.removeListener(al))
  487. c
  488. }
  489. }
  490. def enumEntryObservableAction[T <: EnumEntry](
  491. mappingEnumEntry: T
  492. ): Observable[EnumActionEvent[T]] = {
  493. Observable.create(OverflowStrategy.DropOld(10)) { sub =>
  494. val c = SingleAssignCancelable()
  495. val al = new ActionListener {
  496. override def onAction(
  497. binding: String,
  498. value: Boolean,
  499. tpf: Float
  500. ): Unit = {
  501. if (
  502. sub.onNext(
  503. EnumActionEvent(mappingEnumEntry, value, tpf)
  504. ) == Ack.Stop
  505. ) {
  506. sub.onComplete()
  507. c.cancel()
  508. }
  509. }
  510. }
  511. inputManager.addListener(al, mappingEnumEntry.entryName)
  512. c := Cancelable(() => inputManager.removeListener(al))
  513. c
  514. }
  515. }
  516. def analogObservable(mappingNames: String*): Observable[AnalogEvent] = {
  517. Observable.create(OverflowStrategy.DropOld(50)) { sub =>
  518. val c = SingleAssignCancelable()
  519. val al = new AnalogListener {
  520. override def onAnalog(
  521. binding: String,
  522. value: Float,
  523. tpf: Float
  524. ): Unit = {
  525. if (
  526. sub.onNext(AnalogEvent(Action(binding), value, tpf)) == Ack.Stop
  527. ) {
  528. sub.onComplete()
  529. c.cancel()
  530. }
  531. }
  532. }
  533. inputManager.addListener(al, mappingNames: _*)
  534. c := Cancelable(() => inputManager.removeListener(al))
  535. c
  536. }
  537. }
  538. def enumAnalogObservable[T <: EnumEntry](
  539. mappingEnum: Enum[T]
  540. ): Observable[EnumAnalogEvent[T]] = {
  541. Observable.create(OverflowStrategy.DropOld(50)) { sub =>
  542. val c = SingleAssignCancelable()
  543. val entryNames = mappingEnum.values.map(_.entryName)
  544. val al = new AnalogListener {
  545. override def onAnalog(
  546. binding: String,
  547. value: Float,
  548. tpf: Float
  549. ): Unit = {
  550. mappingEnum.withNameOption(binding).foreach { b =>
  551. if (sub.onNext(EnumAnalogEvent(b, value, tpf)) == Ack.Stop) {
  552. sub.onComplete()
  553. c.cancel()
  554. }
  555. }
  556. }
  557. }
  558. inputManager.addListener(al, entryNames: _*)
  559. c := Cancelable(() => inputManager.removeListener(al))
  560. c
  561. }
  562. }
  563. }
  564. implicit final class PhysicsSpaceExt(private val space: PhysicsSpace)
  565. extends AnyVal {
  566. def collisionObservable(): Observable[PhysicsCollisionEvent] = {
  567. Observable.create(OverflowStrategy.DropOld(50)) { sub =>
  568. val c = SingleAssignCancelable()
  569. val cl = new PhysicsCollisionListener {
  570. override def collision(event: PhysicsCollisionEvent): Unit = {
  571. if (sub.onNext(event) == Ack.Stop) {
  572. sub.onComplete()
  573. c.cancel()
  574. }
  575. }
  576. }
  577. space.addCollisionListener(cl)
  578. c := Cancelable(() => space.removeCollisionListener(cl))
  579. c
  580. }
  581. }
  582. def prePhysicsTickObservable(): Observable[PrePhysicsTickEvent] = {
  583. Observable.create(OverflowStrategy.DropOld(50)) { sub =>
  584. val c = SingleAssignCancelable()
  585. val cl = new PhysicsTickListener {
  586. override def prePhysicsTick(space: PhysicsSpace, tpf: Float): Unit = {
  587. val event = new PrePhysicsTickEvent(space)
  588. if (sub.onNext(event) == Ack.Stop) {
  589. sub.onComplete()
  590. c.cancel()
  591. }
  592. }
  593. override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {}
  594. }
  595. space.addTickListener(cl)
  596. c := Cancelable(() => space.removeTickListener(cl))
  597. c
  598. }
  599. }
  600. def physicsTickObservable(): Observable[PhysicsTickEvent] = {
  601. Observable.create(OverflowStrategy.DropOld(50)) { sub =>
  602. val c = SingleAssignCancelable()
  603. val cl = new PhysicsTickListener {
  604. override def prePhysicsTick(
  605. space: PhysicsSpace,
  606. tpf: Float
  607. ): Unit = {}
  608. override def physicsTick(space: PhysicsSpace, tpf: Float): Unit = {
  609. val event = new PhysicsTickEvent(space)
  610. if (sub.onNext(event) == Ack.Stop) {
  611. sub.onComplete()
  612. c.cancel()
  613. }
  614. }
  615. }
  616. space.addTickListener(cl)
  617. c := Cancelable(() => space.removeTickListener(cl))
  618. c
  619. }
  620. }
  621. //TODO Consider creating a typeclass for this
  622. def +=(anyObject: Any) = space.add(anyObject)
  623. def :+(anyObject: Any) = {
  624. space.add(anyObject)
  625. space
  626. }
  627. def -(anyObject: Any) = {
  628. space.remove(anyObject)
  629. space
  630. }
  631. def +=(spatial: Spatial) = space.addAll(spatial)
  632. def :+(spatial: Spatial) = {
  633. space.addAll(spatial)
  634. space
  635. }
  636. def -(spatial: Spatial) = {
  637. space.removeAll(spatial)
  638. space
  639. }
  640. // def asRef = Ref[Task].of(space)
  641. }
  642. implicit final class Vector3fExt(private val v: Vector3f) extends AnyVal {
  643. //TODO add more operations
  644. def +=(that: Vector3f) = v.addLocal(that)
  645. def +=(f: Float) = v.addLocal(f, f, f)
  646. def +=(that: ImVector3f) = v.addLocal(that.x, that.y, that.z)
  647. def +=:(that: ImVector3f) = v += that
  648. def *=(that: Vector3f) = v.multLocal(that)
  649. def *=(that: ImVector3f) = v.multLocal(that.x, that.y, that.z)
  650. def *=:(that: ImVector3f) = v *= that
  651. def -=(that: Vector3f) = v.subtractLocal(that)
  652. def -=(that: ImVector3f) = v.subtractLocal(that.x, that.y, that.z)
  653. def -=:(that: ImVector3f) = v *= that
  654. def /=(that: Vector3f) = v.divideLocal(that)
  655. def /=(that: ImVector3f) = v.divideLocal(that.mutable)
  656. def /=:(that: ImVector3f) = v *= that
  657. def unary_- = v.negateLocal()
  658. def immutable = ImVector3f(v.x, v.y, v.z)
  659. }
  660. implicit final class ImVector3fExt(private val v: ImVector3f) extends AnyVal {
  661. def +(that: ImVector3f) = v.copy(v.x + that.x, v.y + that.y, v.z + that.z)
  662. def +(f: Float): ImVector3f = v.copy(v.x + f, v.y + f, v.z + f)
  663. def *(that: ImVector3f) = v.copy(v.x * that.x, v.y * that.y, v.z * that.z)
  664. def *(f: Float): ImVector3f =
  665. v.copy(v.x * f, v.y * f, v.z * f)
  666. // v * ImVector3f(f, f, f)
  667. def -(that: ImVector3f) = v.copy(v.x - that.x, v.y - that.y, v.z - that.z)
  668. def -(f: Float): ImVector3f = v.copy(v.x - f, v.y - f, v.z - f)
  669. def /(that: ImVector3f) = v.copy(v.x / that.x, v.y / that.y, v.z / that.z)
  670. def /(f: Float): ImVector3f = v.copy(v.x / f, v.y / f, v.z / f)
  671. def unary_- = v.copy(-v.x, -v.y, -v.z)
  672. def mutable = new Vector3f(v.x, v.y, v.z)
  673. // /**
  674. // * alias for [[cross]] product
  675. // */
  676. // def |*|() = ()
  677. }
  678. // val TasktoUIO = new FunctionK[Task, UIO] {
  679. // def apply[T](f: Task[T]): UIO[T] =
  680. // f.hideErrors
  681. // }
  682. implicit final class JavaFxUIExt(private val jfxui: JavaFxUI) extends AnyVal {
  683. def +=(node: scalafx.scene.Node) = jfxui.attachChild(node)
  684. def -=(node: scalafx.scene.Node) = jfxui.detachChild(node)
  685. }
  686. implicit class AkkaLoggerExt(private val logger: Logger) extends AnyVal {
  687. private def logP[T](
  688. x: sourcecode.Text[T],
  689. tag: String = "",
  690. width: Int = 100,
  691. height: Int = 500,
  692. indent: Int = 2,
  693. initialOffset: Int = 0
  694. )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
  695. val tagStrs =
  696. if (tag.isEmpty) Seq.empty
  697. else Seq(fansi.Color.Cyan(tag), fansi.Str(" "))
  698. // "".slice(1, -1)
  699. val prefix = Seq(
  700. fansi.Color.Magenta(fileName.value),
  701. fansi.Str(":"),
  702. fansi.Color.Green(line.value.toString),
  703. fansi.Str(" "),
  704. fansi.Color.Cyan(x.source),
  705. fansi.Str(": ")
  706. ) ++ tagStrs
  707. fansi.Str.join(
  708. prefix ++ pprint.tokenize(x.value, width, height, indent).toSeq: _*
  709. )
  710. }
  711. def warnP[T](
  712. s: sourcecode.Text[T]
  713. )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
  714. logger.warn(logP(s).render)
  715. s
  716. }
  717. def errorP[T](
  718. s: sourcecode.Text[T]
  719. )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
  720. logger.error(logP(s).render)
  721. s
  722. }
  723. def infoP[T](
  724. s: sourcecode.Text[T]
  725. )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
  726. logger.info(logP(s).render)
  727. s
  728. }
  729. def debugP[T](
  730. s: sourcecode.Text[T]
  731. )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
  732. logger.debug(logP(s).render)
  733. s
  734. }
  735. def traceP[T](
  736. s: sourcecode.Text[T]
  737. )(implicit line: sourcecode.Line, fileName: sourcecode.FileName) = {
  738. logger.trace(logP(s).render)
  739. s
  740. }
  741. }
  742. implicit class odinLoggerExt(private val logger: io.odin.Logger[Task])
  743. extends AnyVal {
  744. def debugU[M](msg: => M)(implicit render: Render[M], position: Position) =
  745. logger.debug(msg).hideErrors
  746. def infoU[M](msg: => M)(implicit render: Render[M], position: Position) =
  747. logger.info(msg).hideErrors
  748. def traceU[M](msg: => M)(implicit render: Render[M], position: Position) =
  749. logger.trace(msg).hideErrors
  750. def warnU[M](msg: => M)(implicit render: Render[M], position: Position) =
  751. logger.warn(msg).hideErrors
  752. def errorU[M](msg: => M)(implicit render: Render[M], position: Position) =
  753. logger.error(msg).hideErrors
  754. }
  755. }