Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #787: IndigoShader, expose ability to add UniformBlocks #790

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions indigo/indigo/src/main/scala/indigo/IndigoShader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package indigo

import indigo.entry.StandardFrameProcessor
import indigo.gameengine.GameEngine
import indigo.shared.shader.UniformBlock
import indigo.shared.shader.library
import indigo.shared.shader.library.IndigoUV.BlendFragmentEnvReference
import indigo.shared.subsystems.SubSystemsRegister
Expand All @@ -11,18 +12,12 @@ import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits._
import scala.concurrent.Future

/** A trait representing a shader that fills the available window.
*
* You can override a number of the details in this trait using launch flags, including:
*
* - width - starting width of the shader
* - height - starting height of the shader
* - channel0 - path to an image
* - channel1 - path to an image
* - channel2 - path to an image
* - channel3 - path to an image
*/
trait IndigoShader extends GameLauncher[IndigoShaderModel, IndigoShaderModel, Unit] {

given [A](using toUBO: ToUniformBlock[A]): Conversion[A, UniformBlock] with
def apply(value: A): UniformBlock = toUBO.toUniformBlock(value)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be the first and only implicit conversion in Indigo. 😛

Based on the comment above uniformBlocks below, saves the user from doing:

final case class CustomData(color: RGBA, customTime: Seconds) extends FragmentEnvReference derives ToUniformBlock
def uniformBlocks: Batch[UniformBlock] = Batch(
  summon[ToUniformBlock[CustomData]].toUniformBlock(CustomData(RGBA.Magenta, 0.seconds))
)


private val Channel0Name: String = "channel0"
private val Channel1Name: String = "channel1"
private val Channel2Name: String = "channel2"
Expand Down Expand Up @@ -52,6 +47,33 @@ trait IndigoShader extends GameLauncher[IndigoShaderModel, IndigoShaderModel, Un
*/
def channel3: Option[AssetPath]

/** The uniform blocks (data) you want to pass to your shader. Example:
*
* ```scala
* import indigo.*
* import indigo.syntax.shaders.*
* import ultraviolet.syntax.*
*
* final case class CustomData(color: vec4, customTime: Float) extends FragmentEnvReference derives ToUniformBlock
* def uniformBlocks: Batch[UniformBlock] = Batch(CustomData(RGBA.Magenta.toUVVec4, 0.seconds.toFloat))
* ```
*
* As long as the field types in your case class are ultraviolet types, you can pass them to your shader, see
* Ultraviolet docs for more info.
*
* Many standard Indigo types are supported for the data fields, but you will need a separate case class for the
* Shader side of the data contract definition, i.e. This is valid too:
*
* ```scala
* // For use with Indigo's shader setup. Note: derives ToUniformBlock, but doesn't need to extend FragmentEnvReference
* final case class CustomDataIndigo(color: RGBA, customTime: Seconds) derives ToUniformBlock
*
* // For use with Ultraviolet's UBO definitions. Note extends FragmentEnvReference, but doesn't derive ToUniformBlock
* final case class CustomDataUV(color: vec4, customTime: Float) extends FragmentEnvReference
* ```
*/
def uniformBlocks: Batch[UniformBlock]

/** The shader you want to render
*/
def shader: ShaderProgram
Expand Down Expand Up @@ -139,7 +161,7 @@ trait IndigoShader extends GameLauncher[IndigoShaderModel, IndigoShaderModel, Un
model.viewport,
ShaderData(
shader.id,
Batch.empty,
uniformBlocks,
model.channel0,
model.channel1,
model.channel2,
Expand Down
56 changes: 56 additions & 0 deletions indigo/indigo/src/main/scala/indigo/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,59 @@ object syntax:
export SignalFunction.multiply
end animations

object shaders:

extension (c: RGBA)
def toUVVec4: ultraviolet.syntax.vec4 =
ultraviolet.syntax.vec4(c.r.toFloat, c.g.toFloat, c.b.toFloat, c.a.toFloat)
extension (c: RGB)
def toUVVec3: ultraviolet.syntax.vec3 =
ultraviolet.syntax.vec3(c.r.toFloat, c.g.toFloat, c.b.toFloat)
extension (p: Point)
def toUVVec2: ultraviolet.syntax.vec2 =
ultraviolet.syntax.vec2(p.x.toFloat, p.y.toFloat)
extension (s: Size)
def toUVVec2: ultraviolet.syntax.vec2 =
ultraviolet.syntax.vec2(s.width.toFloat, s.height.toFloat)
extension (v: Vector2)
def toUVVec2: ultraviolet.syntax.vec2 =
ultraviolet.syntax.vec2(v.x.toFloat, v.y.toFloat)
extension (v: Vector3)
def toUVVec3: ultraviolet.syntax.vec3 =
ultraviolet.syntax.vec3(v.x.toFloat, v.y.toFloat, v.z.toFloat)
extension (v: Vector4)
def toUVVec4: ultraviolet.syntax.vec4 =
ultraviolet.syntax.vec4(v.x.toFloat, v.y.toFloat, v.z.toFloat, v.w.toFloat)
extension (r: Rectangle)
def toUVVec4: ultraviolet.syntax.vec4 =
ultraviolet.syntax.vec4(r.x.toFloat, r.y.toFloat, r.width.toFloat, r.height.toFloat)
extension (m: Matrix4)
def toUVMat4: ultraviolet.syntax.mat4 =
ultraviolet.syntax.mat4(m.toArray.map(_.toFloat))
extension (d: Depth) def toUVFloat: Float = d.toFloat
extension (m: Millis) def toUVFloat: Float = m.toFloat
extension (r: Radians) def toUVFloat: Float = r.toFloat
extension (s: Seconds)
@targetName("ext_Seconds_toUVFloat")
def toUVFloat: Float = s.toFloat
extension (d: Double)
@targetName("ext_Double_toUVFloat")
def toUVFloat: Float = d.toFloat
extension (i: Int)
@targetName("ext_Int_toUVFloat")
def toUVFloat: Float = i.toFloat
extension (l: Long)
@targetName("ext_Long_toUVFloat")
def toUVFloat: Float = l.toFloat
extension (a: Array[Float])
def toUVArray: ultraviolet.syntax.array[Singleton & Int, Float] =
ultraviolet.syntax.array(a)
extension (a: scalajs.js.Array[Float])
def toUVArray: ultraviolet.syntax.array[Singleton & Int, Float] =
ultraviolet.syntax.array(a.toArray)

end shaders

end syntax

object mutable:
Expand Down Expand Up @@ -218,6 +271,9 @@ val ShaderId: shared.shader.ShaderId.type = shared.shader.ShaderId
type ToUniformBlock[A] = shared.shader.ToUniformBlock[A]
val ToUniformBlock: shared.shader.ToUniformBlock.type = shared.shader.ToUniformBlock

type UniformBlock = shared.shader.UniformBlock
val UniformBlock: shared.shader.UniformBlock.type = shared.shader.UniformBlock

val StandardShaders: shared.shader.StandardShaders.type = shared.shader.StandardShaders

type Outcome[T] = shared.Outcome[T]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ object Depth:
@targetName("-_Int")
def -(other: Int): Depth = Depth(d - other)

def toDouble: Double = d
def toDouble: Double = d.toDouble
def toFloat: Float = d.toFloat
def toInt: Int = d.toInt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ object ToUniformBlock:
case given ShaderTypeOf[T] => summonInline[ShaderTypeOf[T]]
case _ =>
error(
"Unsupported type. Only supported types in Indigo shaders are: Int, Long, Float, Double, RGBA, RGB, Point, Size, Vertex, Vector2, Vector3, Vector4, Rectangle, Matrix4, Depth, Radians, Millis, Seconds, Array[Float], js.Array[Float]"
"Unsupported shader uniform type. Supported types From Scala (Int, Long, Float, Double), Indigo [RGBA, RGB, Point, Size, Vertex, Vector2, Vector3, Vector4, Rectangle, Matrix4, Depth, Radians, Millis, Seconds, Array[Float], js.Array[Float]], and UltraViolet [vec2, vec3, vec4, mat4]. However, if you intend to use the same case class for both Indigo and UltraViolet, you should stick to Float + the UltraViolet types."
)
}

Expand All @@ -62,6 +62,22 @@ object ToUniformBlock:

object ShaderTypeOf:

given ShaderTypeOf[ultraviolet.syntax.vec2] with
def toShaderPrimitive(value: ultraviolet.syntax.vec2): ShaderPrimitive =
ShaderPrimitive.vec2(value.x, value.y)

given ShaderTypeOf[ultraviolet.syntax.vec3] with
def toShaderPrimitive(value: ultraviolet.syntax.vec3): ShaderPrimitive =
ShaderPrimitive.vec3(value.x, value.y, value.z)

given ShaderTypeOf[ultraviolet.syntax.vec4] with
def toShaderPrimitive(value: ultraviolet.syntax.vec4): ShaderPrimitive =
ShaderPrimitive.vec4(value.x, value.y, value.z, value.w)

given ShaderTypeOf[ultraviolet.syntax.mat4] with
def toShaderPrimitive(value: ultraviolet.syntax.mat4): ShaderPrimitive =
ShaderPrimitive.mat4(value.mat)

given ShaderTypeOf[Int] with
def toShaderPrimitive(value: Int): ShaderPrimitive =
ShaderPrimitive.float(value)
Expand Down
35 changes: 29 additions & 6 deletions indigo/shader/src/main/scala/com/example/shader/ShaderGame.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import indigo.*
import indigo.syntax.shaders.*
import ultraviolet.syntax.*

import scala.scalajs.js.annotation._

Expand All @@ -12,9 +14,34 @@ object ShaderGame extends IndigoShader:
val channel2: Option[AssetPath] = None
val channel3: Option[AssetPath] = None

val uniformBlocks: Batch[UniformBlock] =
Batch(CustomData(RGBA.Magenta.toUVVec4))

val shader: ShaderProgram =
// ShowImage.shader
SeascapeShader.shader
ShaderWithData.shader
// ShowImage.shader
// SeascapeShader.shader

final case class CustomData(CUSTOM_COLOR: vec4) extends FragmentEnvReference derives ToUniformBlock
object CustomData:
val reference =
CustomData(vec4(0.0f))

object ShaderWithData:

val shader: UltravioletShader =
UltravioletShader.entityFragment(
ShaderId("shader with data"),
EntityShader.fragment[CustomData](shaderWithData, CustomData.reference)
)

inline def shaderWithData: Shader[CustomData, Unit] =
Shader[CustomData] { env =>
ubo[CustomData]

def fragment(color: vec4): vec4 =
env.CUSTOM_COLOR
}

object ShowImage:

Expand All @@ -24,8 +51,6 @@ object ShowImage:
EntityShader.fragment[FragmentEnv](showImage, FragmentEnv.reference)
)

import ultraviolet.syntax.*

inline def showImage: Shader[FragmentEnv, Unit] =
Shader[FragmentEnv] { env =>
def fragment(color: vec4): vec4 =
Expand Down Expand Up @@ -55,8 +80,6 @@ object VoronoiShader:
EntityShader.fragment[FragmentEnv](voronoi, FragmentEnv.reference)
)

import ultraviolet.syntax.*

// Ported from: https://www.youtube.com/watch?v=l-07BXzNdPw&feature=youtu.be
@SuppressWarnings(Array("scalafix:DisableSyntax.var"))
inline def voronoi: Shader[FragmentEnv, Unit] =
Expand Down
Loading