ScalaDays 2014 - Reactive Scala 3D Game Engine

97
1 Reactive 3D Game Engine in Scal Aleksandar Prokopec @_axel22_

description

Slides for the Reactive 3D Game Engine presented at ScalaDays 2014. Shows the demo of the 3D engine, followed by the description of the reactive 3D game engine - how reactive dependencies between input, time and game logic are expressed, how to deal with GC issues, how to model game state using Reactive Collections.

Transcript of ScalaDays 2014 - Reactive Scala 3D Game Engine

Page 1: ScalaDays 2014 - Reactive Scala 3D Game Engine

1

A Reactive 3D Game Engine in Scala

Aleksandar Prokopec@_axel22_

Page 2: ScalaDays 2014 - Reactive Scala 3D Game Engine

2

What’s a game engine?

Page 3: ScalaDays 2014 - Reactive Scala 3D Game Engine

3

Simulation

Page 4: ScalaDays 2014 - Reactive Scala 3D Game Engine

4

Real-time simulation

Page 5: ScalaDays 2014 - Reactive Scala 3D Game Engine

5

15 ms

Real-time simulation

Page 6: ScalaDays 2014 - Reactive Scala 3D Game Engine

6

Demo first!http://youtu.be/pRCzSRhifLs

Page 7: ScalaDays 2014 - Reactive Scala 3D Game Engine

7

Input Simulator

Interaction

Renderer

Page 8: ScalaDays 2014 - Reactive Scala 3D Game Engine

Reactive Collectionshttp://reactive-collections.com

8

Page 9: ScalaDays 2014 - Reactive Scala 3D Game Engine

9

Reactive values

Page 10: ScalaDays 2014 - Reactive Scala 3D Game Engine

Reactive[T]

10

Page 11: ScalaDays 2014 - Reactive Scala 3D Game Engine

val ticks: Reactive[Long]

11

ticks 1

1

2

2

3

3

4

4

60

60

61

61

Page 12: ScalaDays 2014 - Reactive Scala 3D Game Engine

ticks onEvent { x => log.debug(s”tick no.$x”)}

12

1 2 3 4 60 61

tick no.1tick no.2tick no.3tick no.4

tick no.60tick no.61

...

Page 13: ScalaDays 2014 - Reactive Scala 3D Game Engine

ticks foreach { x => log.debug(s”tick no.$x”)}

13

1 2 3 4 60 61

Page 14: ScalaDays 2014 - Reactive Scala 3D Game Engine

14

for (x <- ticks) { log.debug(s”tick no.$x”)}

Page 15: ScalaDays 2014 - Reactive Scala 3D Game Engine

15

Reactive combinators

Page 16: ScalaDays 2014 - Reactive Scala 3D Game Engine

for (x <- ticks) yield { x / 60 }

16

Page 17: ScalaDays 2014 - Reactive Scala 3D Game Engine

val seconds: Reactive[Long] = for (x <- ticks) yield { x / 60 }

17

Page 18: ScalaDays 2014 - Reactive Scala 3D Game Engine

6061

val seconds: Reactive[Long] = for (x <- ticks) yield { x / 60 }

18

ticks 1

1

2

2

3

3 60 61

seconds

0 0 0 1 1

ticks

seconds

00011

Page 19: ScalaDays 2014 - Reactive Scala 3D Game Engine

val days: Reactive[Long] = seconds.map(_ / 86400)

19

Page 20: ScalaDays 2014 - Reactive Scala 3D Game Engine

val days: Reactive[Long] = seconds.map(_ / 86400)val secondsToday =

20

Page 21: ScalaDays 2014 - Reactive Scala 3D Game Engine

val days: Reactive[Long] = seconds.map(_ / 86400)val secondsToday = (seconds zip days) { (s, d) => s – d * 86400 } 21

Page 22: ScalaDays 2014 - Reactive Scala 3D Game Engine

val days: Reactive[Long] = seconds.map(_ / 86400)val secondsToday = (seconds zip days) { _ – _ * 86400 }

22

seconds days

secondsToday

Page 23: ScalaDays 2014 - Reactive Scala 3D Game Engine

val angle = secondsInDay.map(angleFunc)

23

Page 24: ScalaDays 2014 - Reactive Scala 3D Game Engine

val angle = secondsInDay.map(angleFunc)val light = secondsInDay.map(lightFunc)

24

Page 25: ScalaDays 2014 - Reactive Scala 3D Game Engine

25

https://www.youtube.com/watch?v=5g7DvNEs6K8&feature=youtu.be

Preview

Page 26: ScalaDays 2014 - Reactive Scala 3D Game Engine

26

val rotate = keys

a ↓shift ↓ a ↑ shift ↑pgup ↓ pgup ↑keys

Page 27: ScalaDays 2014 - Reactive Scala 3D Game Engine

27

val rotate = keys.filter(_ == PAGEUP)

a ↓shift ↓ a ↑ shift ↑pgup ↓ pgup ↑keys

pgup ↓ pgup ↑filter

Page 28: ScalaDays 2014 - Reactive Scala 3D Game Engine

28

val rotate = keys.filter(_ == PAGEUP) .map(_.down)

a ↓shift ↓ a ↑ shift ↑pgup ↓ pgup ↑keys

pgup ↓ pgup ↑filter

true falsemap

Page 29: ScalaDays 2014 - Reactive Scala 3D Game Engine

29

if (rotate()) viewAngle += 1

true falsemap

Page 30: ScalaDays 2014 - Reactive Scala 3D Game Engine

30

Signals

Page 31: ScalaDays 2014 - Reactive Scala 3D Game Engine

31

Reactives arediscrete

Page 32: ScalaDays 2014 - Reactive Scala 3D Game Engine

32

Signals are continuous

Page 33: ScalaDays 2014 - Reactive Scala 3D Game Engine

33

trait Signal[T]extends Reactive[T] { def apply(): T}

Page 34: ScalaDays 2014 - Reactive Scala 3D Game Engine

34

val rotate = keys.filter(_ == PAGEUP) .map(_.down) .signal(false)

true falsemap

signal

Page 35: ScalaDays 2014 - Reactive Scala 3D Game Engine

35

val rotate: Signal[Boolean] = keys.filter(_ == PAGEUP) .map(_.down) .signal(false)

true falsemap

signal

Page 36: ScalaDays 2014 - Reactive Scala 3D Game Engine

36

val rotate: Signal[Boolean]val ticks: Reactive[Long]

ticks

Page 37: ScalaDays 2014 - Reactive Scala 3D Game Engine

37

val rotate: Signal[Boolean]val ticks: Reactive[Long]

ticks

rotate

Page 38: ScalaDays 2014 - Reactive Scala 3D Game Engine

38

val rotate: Signal[Boolean]val ticks: Reactive[Long]val viewAngle: Signal[Double] =

ticks

rotate

viewAngle

Page 39: ScalaDays 2014 - Reactive Scala 3D Game Engine

39

List(1, 2, 3).scanLeft(0)(_ + _)

Page 40: ScalaDays 2014 - Reactive Scala 3D Game Engine

40

List(1, 2, 3).scanLeft(0)(_ + _)

→ List(0, 1, 3, 6)

Page 41: ScalaDays 2014 - Reactive Scala 3D Game Engine

41

def scanLeft[S](z: S)(f: (S, T) => S) : List[S]

Page 42: ScalaDays 2014 - Reactive Scala 3D Game Engine

42

def scanLeft[S](z: S)(f: (S, T) => S) : List[S]

def scanPast[S](z: S)(f: (S, T) => S) : Signal[S]

Page 43: ScalaDays 2014 - Reactive Scala 3D Game Engine

43

val rotate: Signal[Boolean]val ticks: Reactive[Long]val viewAngle: Signal[Double] = ticks.scanPast(0.0)

ticks

rotate

viewAngle

Page 44: ScalaDays 2014 - Reactive Scala 3D Game Engine

44

val rotate: Signal[Boolean]val ticks: Reactive[Long]val viewAngle: Signal[Double] = ticks.scanPast(0.0) { (a, _) => if (rotate()) a + 1 else a }

ticks

rotate

viewAngle

Page 45: ScalaDays 2014 - Reactive Scala 3D Game Engine

45

http://youtu.be/blG95W5uWQ8

Preview

Page 46: ScalaDays 2014 - Reactive Scala 3D Game Engine

46

val velocity = ticks.scanPast(0.0) { (v, _) => }val viewAngle =

Page 47: ScalaDays 2014 - Reactive Scala 3D Game Engine

47

val velocity = ticks.scanPast(0.0) { (v, _) => if (rotate()) v + 1 }val viewAngle =

Page 48: ScalaDays 2014 - Reactive Scala 3D Game Engine

48

val velocity = ticks.scanPast(0.0) { (v, _) => if (rotate()) v + 1 else v – 0.5 }val viewAngle =

Page 49: ScalaDays 2014 - Reactive Scala 3D Game Engine

49

val velocity = ticks.scanPast(0.0) { (v, _) => if (rotate()) v + 1 else v – 0.5 }val viewAngle = velocity.scanPast(0.0)(_ + _)

Page 50: ScalaDays 2014 - Reactive Scala 3D Game Engine

50

http://youtu.be/NMVhirZLWmA

Preview

Page 51: ScalaDays 2014 - Reactive Scala 3D Game Engine

51

Higher-orderreactive values

Page 52: ScalaDays 2014 - Reactive Scala 3D Game Engine

52

(T => S) => (List[S] => List[T])

Page 53: ScalaDays 2014 - Reactive Scala 3D Game Engine

53

(T => S) => (List[S] => List[T])

Reactive[Reactive[S]]

Page 54: ScalaDays 2014 - Reactive Scala 3D Game Engine

54

val mids = mouse .filter(_.button == MIDDLE)

mids ↓ ↓ ↓↑ ↑ ↑

Page 55: ScalaDays 2014 - Reactive Scala 3D Game Engine

55

val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)

mids ↓ ↓ ↓

up

down ↓ ↓ ↓

↑ ↑ ↑

↑ ↑ ↑

Page 56: ScalaDays 2014 - Reactive Scala 3D Game Engine

56

val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)val drags = down .map(_ => mouse)

up

down ↓ ↓ ↓

↑ ↑ ↑

Page 57: ScalaDays 2014 - Reactive Scala 3D Game Engine

57

val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)val drags = down .map(_ => mouse)

up

down ↓ ↓ ↓

↑ ↑ ↑

Reactive[Reactive[MouseEvent]]

Page 58: ScalaDays 2014 - Reactive Scala 3D Game Engine

58

val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)val drags = down .map(_ => mouse)

up

down ↓ ↓ ↓

↑ ↑ ↑

drags

Page 59: ScalaDays 2014 - Reactive Scala 3D Game Engine

drags

up ↑ ↑ ↑

59

val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)val drags = down .map(_ => mouse.until(up))

down ↓ ↓ ↓

mouse.until(up)

Page 60: ScalaDays 2014 - Reactive Scala 3D Game Engine

60

val mids = mouse .filter(_.button == MIDDLE)val up = mids.filter(!_.down)val down = mids.filter(_.down)val drags = down .map(_ => mouse.until(up))

down ↓ ↓ ↓

up ↑ ↑ ↑

drags

Page 61: ScalaDays 2014 - Reactive Scala 3D Game Engine

61

val drags = down .map(_ => mouse.until(up)) .map(_.map(_.xy))

drags

1, 12, 3

3, 5

4, 6

6, 9

9, 9

Page 62: ScalaDays 2014 - Reactive Scala 3D Game Engine

62

val drags = down .map(_ => mouse.until(up)) .map(_.map(_.xy)) .map(_.diffPast(_.xy - _.xy))

drags

0, 01, 2

1, 2

0, 0

2, 3

0, 0

Page 63: ScalaDays 2014 - Reactive Scala 3D Game Engine

63

val drags = down .map(_ => mouse.until(up)) .map(_.map(_.xy)) .map(_.diffPast(_.xy - _.xy)) .concat()

drags 0, 0 1, 2 1, 2 0, 0 2, 3 0, 0

Page 64: ScalaDays 2014 - Reactive Scala 3D Game Engine

64

val drags = down .map(_ => mouse.until(up)) .map(_.map(_.xy)) .map(_.diffPast(_.xy - _.xy)) .concat()val pos = drags.scanPast((0, 0))(_ + _)

drags 0, 0 1, 2 1, 2 0, 0 2, 3 0, 0

pos 0, 0 1, 2 2, 4 2, 4 4, 7 4, 7

Page 65: ScalaDays 2014 - Reactive Scala 3D Game Engine

65

http://youtu.be/RsMSZ7OH2fo

Preview

However, a lot of object allocations lead to GC issues.

Page 66: ScalaDays 2014 - Reactive Scala 3D Game Engine

66

Reactive mutators

Page 67: ScalaDays 2014 - Reactive Scala 3D Game Engine

67

class Matrix { def apply(x: Int, y: Int): Double def update(x: Int, y: Int, v: Double)}

Page 68: ScalaDays 2014 - Reactive Scala 3D Game Engine

68

val screenMat: Signal[Matrix] = (projMat zip viewMat)(_ * _)val invScreenMat = screenMat.map(_.inverse)

Page 69: ScalaDays 2014 - Reactive Scala 3D Game Engine

69

Reactive[immutable.Matrix[T]]

Page 70: ScalaDays 2014 - Reactive Scala 3D Game Engine

70

val screenMat: Signal[Matrix] = (projMat zip viewMat)(_ * _)val invScreenMat = screenMat.map(_.inverse)

(4*4*8 + 16 + 16)*4*100 = 64 kb/s

Page 71: ScalaDays 2014 - Reactive Scala 3D Game Engine

71

val screenMat = Mutable(new Matrix)(projMat, viewMat).mutate(screenMat) { (p, v) => screenMat().assignMul(p, v)}val invScreenMat = Mutable(new Matrix)screenMat.mutate(invScreenMat) { m => invScreenMat().assignInv(m)}

Page 72: ScalaDays 2014 - Reactive Scala 3D Game Engine

72

Reactive collections

Page 73: ScalaDays 2014 - Reactive Scala 3D Game Engine

73

http://youtu.be/ebbrAHNsexc

Preview

How do we model that a character is selected?

Page 74: ScalaDays 2014 - Reactive Scala 3D Game Engine

74

val selected: Reactive[Set[Character]]

Page 75: ScalaDays 2014 - Reactive Scala 3D Game Engine

75

val selected: ReactSet[Character]

Page 76: ScalaDays 2014 - Reactive Scala 3D Game Engine

76

trait ReactSet[T]extends ReactContainer[T] { def apply(x: T): Boolean}

Page 77: ScalaDays 2014 - Reactive Scala 3D Game Engine

77

trait ReactContainer[T] { def inserts: Reactive[T] def removes: Reactive[T]}

Page 78: ScalaDays 2014 - Reactive Scala 3D Game Engine

78

A reactive collectionis a pair

of reactive values

Page 79: ScalaDays 2014 - Reactive Scala 3D Game Engine

79

val selected = new ReactHashSet[Character]

Page 80: ScalaDays 2014 - Reactive Scala 3D Game Engine

80

Page 81: ScalaDays 2014 - Reactive Scala 3D Game Engine

81

val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c)))

Page 82: ScalaDays 2014 - Reactive Scala 3D Game Engine

82

Page 83: ScalaDays 2014 - Reactive Scala 3D Game Engine

83

class ReactContainer[T] { self => def inserts: Reactive[T] def removes: Reactive[T]

def map[S](f: T => S) = new ReactContainer[S] { def inserts: Reactive[T] = self.inserts.map(f) def removes: Reactive[T] = self.removes.map(f) }}

Page 84: ScalaDays 2014 - Reactive Scala 3D Game Engine

84

val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .to[ReactHashMap]

Page 85: ScalaDays 2014 - Reactive Scala 3D Game Engine

85

val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .to[ReactHashMap]

Page 86: ScalaDays 2014 - Reactive Scala 3D Game Engine

86

val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .to[ReactHashMap]

Page 87: ScalaDays 2014 - Reactive Scala 3D Game Engine

87

val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .to[ReactHashMap]

Page 88: ScalaDays 2014 - Reactive Scala 3D Game Engine

88

val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .react.to[ReactHashMap]

Page 89: ScalaDays 2014 - Reactive Scala 3D Game Engine

89

val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .react.to[ReactHashMap]

Page 90: ScalaDays 2014 - Reactive Scala 3D Game Engine

90

val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .react.to[ReactHashMap]

Page 91: ScalaDays 2014 - Reactive Scala 3D Game Engine

91

val selected = new ReactHashSet[Character]val decorations = selected .map(c => (c, decoFor(c))) .react.to[ReactHashMap]

Page 92: ScalaDays 2014 - Reactive Scala 3D Game Engine

92

• reactive mutators• reactive collections• @specialized• Scala Macros• shipping computations to the GPU

Page 93: ScalaDays 2014 - Reactive Scala 3D Game Engine

93

http://storm-enroute.com/macrogl/

MacroGL

Page 94: ScalaDays 2014 - Reactive Scala 3D Game Engine

94

https://www.youtube.com/watch?v=UHCeXdxkx70

GC Preview

Page 95: ScalaDays 2014 - Reactive Scala 3D Game Engine

95

Is Scala Ready?

Page 96: ScalaDays 2014 - Reactive Scala 3D Game Engine

96

YES!

Page 97: ScalaDays 2014 - Reactive Scala 3D Game Engine

97

Thank you!