Skip to content

Commit

Permalink
Fixed #10: Added TerminalMaterial
Browse files Browse the repository at this point in the history
  • Loading branch information
davesmith00000 committed Nov 8, 2023
1 parent 0aa681d commit 932051d
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 9 deletions.
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,9 @@ This library provides three mechanisms to do that:

### `TerminalText`

This is a material for use with Indigo's standard `Text` primitive.
This is a material for use with Indigo's standard `Text` primitive. In addition to foreground and background `TerminalText` also supports a solid colour dropshadow.

Looks great but has two problems:

1. Changing colors mid-artwork (e.g. setting the text red and the border blue) is a pain, you need to use another `Text` instance and make them line up!.
2. This process allocates a lot during the rendering process, and probably won't scale very well.
Looks great but has a glaring problem: Changing colors mid-artwork (e.g. setting the text red and the border blue) is a pain, you need to use another `Text` instance and make them line up!

Great for pop-up menus, and monochrome sections of ASCII art, or maps that aren't too big. After that, a new strategy may be needed.

Expand All @@ -98,9 +95,7 @@ The terminal emulators are the basis of the library, and provide a simple 'termi

### `TerminalEmulator` with `TerminalEntity`

This is the most flexible way to render, but not the fastest.

The `TerminalEmulator` in conjunction with the `TerminalEntity` works in a completely different way. Here a special shader is going to draw all the ASCII characters out in a continuous image, and you can interleave colors any time you like with no performance cost. This moves processing costs away from the rendering pipeline but incurs a penalty on the CPU side.
The `TerminalEmulator` in conjunction with the `TerminalEntity` works in a completely different way to the other methods. Here a special shader (does not support foreground alpha or drop shadows!) is going to draw all the ASCII characters out in a continuous image, and you can interleave colors any time you like with no performance cost. This moves processing costs away from the rendering pipeline but incurs a penalty on the CPU side.

The terminal emulator... emulates a simple terminal interface allowing you to put and get characters. Terminals can be merged and drawn.

Expand All @@ -124,6 +119,10 @@ This is as capable as the previous method, but does not have the 4096 tile limit

[Example](https://github.com/PurpleKingdomGames/roguelike-starterkit/blob/main/demo/src/main/scala/demo/CloneTilesScene.scala)

### `TerminalMaterial`

This material is a near drop-in replacement for `TerminalText`. It does not support the drop shadow feature, but has a leaner implementation for performance sensitive contexts. Supposed to be used with the `TerminalEmulator`'s `toCloneTiles` method.

## Extras

The starter kit also provides:
Expand Down
2 changes: 1 addition & 1 deletion demo/src/main/scala/demo/CloneTilesScene.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ object CloneTilesScene extends Scene[Unit, Unit, Unit]:
): Outcome[SceneUpdateFragment] =
val tiles =
terminal.toCloneTiles(CloneId("demo"), Point.zero, RoguelikeTiles.Size10x10.charCrops) { (fg, bg) =>
Graphic(10, 10, TerminalText(Assets.tileMap, fg, bg))
Graphic(10, 10, TerminalMaterial(Assets.tileMap, fg, bg))
}

Outcome(
Expand Down
1 change: 1 addition & 0 deletions demo/src/main/scala/demo/RogueLikeGame.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ object RogueLikeGame extends IndigoGame[Unit, Unit, Unit, Unit]:
.withShaders(
TerminalEntity.shader(maxTileCount),
TerminalText.standardShader,
TerminalMaterial.standardShader,
TerminalTextScene.customShader(ShaderId("my shader"))
)
.withSubSystems(FPSCounter(Point(10, 350)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ val MapTile: terminal.MapTile.type = terminal.MapTile
type TerminalText = terminal.TerminalText
val TerminalText: terminal.TerminalText.type = terminal.TerminalText

type TerminalMaterial = terminal.TerminalMaterial
val TerminalMaterial: terminal.TerminalMaterial.type = terminal.TerminalMaterial

type TerminalClones = terminal.TerminalClones
val TerminalClones: terminal.TerminalClones.type = terminal.TerminalClones

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package io.indigoengine.roguelike.starterkit.terminal

import indigo.*
import indigo.syntax.shaders.*

/** `TerminalMaterial` is a revised and leaner version of `TerminalText`, aimed at use with
* `CloneTiles`. It removes the dubious drop shadow functionality, requires less shader data, and
* has simpler shader logic.
*/
final case class TerminalMaterial(
tileMap: AssetName,
foreground: RGBA,
background: RGBA,
mask: RGBA,
shaderId: Option[ShaderId]
) extends Material:

def withForeground(newColor: RGBA): TerminalMaterial =
this.copy(foreground = newColor)
def withForeground(newColor: RGB): TerminalMaterial =
withForeground(newColor.toRGBA)

def withBackground(newColor: RGBA): TerminalMaterial =
this.copy(background = newColor)
def withBackground(newColor: RGB): TerminalMaterial =
withBackground(newColor.toRGBA)

def withMask(newColor: RGBA): TerminalMaterial =
this.copy(mask = newColor)
def withMask(newColor: RGB): TerminalMaterial =
withMask(newColor.toRGBA)

def withShaderId(newShaderId: ShaderId): TerminalMaterial =
this.copy(shaderId = Option(newShaderId))

def toShaderData: ShaderData =
ShaderData(
shaderId.getOrElse(TerminalMaterial.shaderId),
Batch(
UniformBlock(
UniformBlockName("RogueLikeTextData"),
Batch(
Uniform("FOREGROUND") -> foreground.asVec4,
Uniform("BACKGROUND") -> background.asVec4,
Uniform("MASK") -> mask.asVec4
)
)
),
Some(tileMap),
None,
None,
None
)

object TerminalMaterial:

val defaultMask: RGBA =
RGBA.Magenta

val shaderId: ShaderId =
ShaderId("roguelike standard terminal material")

def standardShader: UltravioletShader =
UltravioletShader.entityFragment(
shaderId,
EntityShader.fragment[ShaderImpl.Env](
ShaderImpl.frag,
ShaderImpl.Env.ref
)
)

def apply(tileMap: AssetName): TerminalMaterial =
TerminalMaterial(tileMap, RGBA.White, RGBA.Zero, defaultMask, None)

def apply(tileMap: AssetName, color: RGBA): TerminalMaterial =
TerminalMaterial(tileMap, color, RGBA.Zero, defaultMask, None)

def apply(tileMap: AssetName, foreground: RGBA, background: RGBA): TerminalMaterial =
TerminalMaterial(tileMap, foreground, background, defaultMask, None)

def apply(
tileMap: AssetName,
foreground: RGBA,
background: RGBA,
mask: RGBA
): TerminalMaterial =
TerminalMaterial(tileMap, foreground, background, mask, None)

object ShaderImpl:

import ultraviolet.syntax.*

final case class Env(
FOREGROUND: vec4,
BACKGROUND: vec4,
MASK: vec4
) extends FragmentEnvReference

object Env:
val ref =
Env(vec4(0.0f), vec4(0.0f), vec4(0.0f))

final case class RogueLikeTextData(
FOREGROUND: vec4,
BACKGROUND: vec4,
MASK: vec4
)

inline def frag: Shader[Env, Unit] =
Shader[Env] { env =>
ubo[RogueLikeTextData]

// Calculate the distance from the color to the mask, and snap to 0.0 or 1.0 over a low threshold.
def maskAmount(color: vec4, mask: vec4): Float =
val v = abs(color - mask)
step(0.001f, v.x + v.y + v.z + v.w)

def fragment(color: vec4): vec4 =
val bg = vec4(env.BACKGROUND.rgb * env.BACKGROUND.a, env.BACKGROUND.a)
val tint = env.CHANNEL_0 * env.FOREGROUND
val fg = vec4(tint.rgb * tint.a, tint.a)

mix(bg, fg, maskAmount(env.CHANNEL_0, env.MASK))
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package io.indigoengine.roguelike.starterkit.terminal
import indigo.*
import indigo.syntax.shaders.*

/** The original Terminal text material, designed for use with `Text` entities. Supports approximate drop shadows.
*/
final case class TerminalText(
tileMap: AssetName,
foreground: RGBA,
Expand Down

0 comments on commit 932051d

Please sign in to comment.