All Downloads are FREE. Search and download functionalities are using the official Maven repository.

indigo.platform.Platform.scala Maven / Gradle / Ivy

The newest version!
package indigo.platform

import indigo.platform.assets.AssetCollection
import indigo.platform.assets.AtlasId
import indigo.platform.assets.DynamicText
import indigo.platform.assets.ImageRef
import indigo.platform.assets.TextureAtlas
import indigo.platform.assets.TextureAtlasFunctions
import indigo.platform.events.GlobalEventStream
import indigo.platform.events.WorldEvents
import indigo.platform.input.GamepadInputCaptureImpl
import indigo.platform.renderer.Renderer
import indigo.platform.renderer.RendererInitialiser
import indigo.platform.renderer.shared.LoadedTextureAsset
import indigo.shared.IndigoLogger
import indigo.shared.Outcome
import indigo.shared.assets.AssetName
import indigo.shared.config.GameConfig
import indigo.shared.datatypes.Vector2
import indigo.shared.events.FullScreenEnterError
import indigo.shared.events.FullScreenEntered
import indigo.shared.events.FullScreenExitError
import indigo.shared.events.FullScreenExited
import indigo.shared.platform.AssetMapping
import indigo.shared.platform.RendererConfig
import indigo.shared.platform.TextureRefAndOffset
import indigo.shared.shader.RawShaderCode
import org.scalajs.dom
import org.scalajs.dom.Element
import org.scalajs.dom.html.Canvas
import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits._

import scala.util.Failure
import scala.util.Success

class Platform(
    parentElement: Element,
    gameConfig: GameConfig,
    globalEventStream: GlobalEventStream,
    dynamicText: DynamicText
) extends PlatformFullScreen {

  val rendererInit: RendererInitialiser =
    new RendererInitialiser(gameConfig.advanced.renderingTechnology, globalEventStream, dynamicText)

  @SuppressWarnings(Array("scalafix:DisableSyntax.null", "scalafix:DisableSyntax.var"))
  private var _canvas: Canvas = null
  @SuppressWarnings(Array("scalafix:DisableSyntax.var"))
  private var _running: Boolean         = true
  private val _worldEvents: WorldEvents = new WorldEvents

  def initialise(
      firstRun: Boolean,
      shaders: Set[RawShaderCode],
      assetCollection: AssetCollection
  ): Outcome[(Renderer, AssetMapping)] =
    for {
      textureAtlas        <- createTextureAtlas(assetCollection)
      loadedTextureAssets <- extractLoadedTextures(textureAtlas)
      assetMapping        <- setupAssetMapping(textureAtlas)
      canvas              <- createCanvas(firstRun, parentElement, gameConfig)
      _                   <- listenToWorldEvents(firstRun, canvas, gameConfig, globalEventStream)
      renderer            <- startRenderer(gameConfig, loadedTextureAssets, canvas, shaders)
      _ = _canvas = canvas
    } yield (renderer, assetMapping)

  def tick(loop: Double => Unit): Unit =
    if _running then dom.window.requestAnimationFrame(loop)
    ()

  def delay(amount: Double, thunk: () => Unit): Unit =
    dom.window.setTimeout(thunk, amount)

  def kill(): Unit =
    _running = false
    _worldEvents.kill()
    GamepadInputCaptureImpl.kill()
    ()

  def createTextureAtlas(assetCollection: AssetCollection): Outcome[TextureAtlas] =
    Outcome(
      TextureAtlas.create(
        assetCollection.images.map(i => ImageRef(i.name, i.data.width, i.data.height, i.tag)).toList,
        (name: AssetName) => assetCollection.images.find(_.name == name),
        TextureAtlasFunctions.createAtlasData
      )
    )

  def extractLoadedTextures(textureAtlas: TextureAtlas): Outcome[List[LoadedTextureAsset]] =
    Outcome(
      textureAtlas.atlases.toList
        .map { case (atlasId, atlas) => atlas.imageData.map(data => new LoadedTextureAsset(AtlasId(atlasId), data)) }
        .collect { case Some(s) => s }
    )

  def setupAssetMapping(textureAtlas: TextureAtlas): Outcome[AssetMapping] =
    Outcome(
      new AssetMapping(
        mappings = textureAtlas.legend
          .map { case (name, atlasIndex) =>
            name -> TextureRefAndOffset(
              atlasName = atlasIndex.id,
              atlasSize = textureAtlas.atlases
                .get(atlasIndex.id.toString)
                .map(_.size.value)
                .map(i => Vector2(i.toDouble))
                .getOrElse(Vector2.one),
              offset = atlasIndex.offset.toVector,
              size = atlasIndex.size.toVector
            )
          }
      )
    )

  private given CanEqual[Option[Element], Option[Element]] = CanEqual.derived

  def createCanvas(firstRun: Boolean, parentElement: Element, gameConfig: GameConfig): Outcome[Canvas] =
    if firstRun then
      Outcome(
        rendererInit.createCanvas(
          gameConfig.viewport.width,
          gameConfig.viewport.height,
          parentElement
        )
      )
    else Outcome(_canvas)

  def listenToWorldEvents(
      firstRun: Boolean,
      canvas: Canvas,
      gameConfig: GameConfig,
      globalEventStream: GlobalEventStream
  ): Outcome[Unit] =
    Outcome {
      if firstRun then
        IndigoLogger.info("Starting world events")
        _worldEvents.init(
          canvas,
          gameConfig.resizePolicy,
          gameConfig.magnification,
          gameConfig.advanced.disableContextMenu,
          globalEventStream
        )
        GamepadInputCaptureImpl.init()
      else IndigoLogger.info("Re-using existing world events")
    }

  def startRenderer(
      gameConfig: GameConfig,
      loadedTextureAssets: List[LoadedTextureAsset],
      canvas: Canvas,
      shaders: Set[RawShaderCode]
  ): Outcome[Renderer] =
    Outcome {
      IndigoLogger.info("Starting renderer")
      rendererInit.setup(
        new RendererConfig(
          renderingTechnology = gameConfig.advanced.renderingTechnology,
          clearColor = gameConfig.clearColor,
          magnification = gameConfig.magnification,
          maxBatchSize = gameConfig.advanced.batchSize,
          antiAliasing = gameConfig.advanced.antiAliasing,
          premultipliedAlpha = gameConfig.advanced.premultipliedAlpha,
          transparentBackground = gameConfig.transparentBackground
        ),
        loadedTextureAssets,
        canvas,
        shaders
      )
    }

  // @SuppressWarnings(Array("scalafix:DisableSyntax.asInstanceOf"))
  def toggleFullScreen(): Unit =
    if (Option(dom.document.fullscreenElement).isEmpty)
      enterFullScreen()
    else
      exitFullScreen()

  // @SuppressWarnings(Array("scalafix:DisableSyntax.asInstanceOf"))
  def enterFullScreen(): Unit =
    _canvas.requestFullscreen().toFuture.onComplete {
      case Success(()) =>
        globalEventStream.pushGlobalEvent(FullScreenEntered)

      case Failure(_) =>
        globalEventStream.pushGlobalEvent(FullScreenEnterError)
    }

  // @SuppressWarnings(Array("scalafix:DisableSyntax.asInstanceOf"))
  def exitFullScreen(): Unit =
    dom.document.exitFullscreen().toFuture.onComplete {
      case Success(()) =>
        globalEventStream.pushGlobalEvent(FullScreenExited)

      case Failure(_) =>
        globalEventStream.pushGlobalEvent(FullScreenExitError)
    }
}

trait PlatformFullScreen {
  def toggleFullScreen(): Unit
  def enterFullScreen(): Unit
  def exitFullScreen(): Unit
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy