Slide 1

Slide 1 text

1 A Reactive 3D Game Engine in Scala Aleksandar Prokopec @_axel22_

Slide 2

Slide 2 text

2 What’s a game engine?

Slide 3

Slide 3 text

3 Simulation

Slide 4

Slide 4 text

4 Real-time simulation

Slide 5

Slide 5 text

5 15 ms Real-time simulation

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

7 Input Simulator Interaction Renderer

Slide 8

Slide 8 text

Reactive Collections http://reactive-collections.com 8

Slide 9

Slide 9 text

9 Reactive values

Slide 10

Slide 10 text

Reactive[T] 10

Slide 11

Slide 11 text

val ticks: Reactive[Long] 11 ticks 1 1 2 2 3 3 4 4 60 60 61 61

Slide 12

Slide 12 text

ticks onEvent { x => log.debug(s”tick no.$x”) } 12 1 2 3 4 60 61 tick no.1 tick no.2 tick no.3 tick no.4 tick no.60 tick no.61 ...

Slide 13

Slide 13 text

ticks foreach { x => log.debug(s”tick no.$x”) } 13 1 2 3 4 60 61

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

15 Reactive combinators

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

60 61 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 0 0 0 1 1

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

val angle = secondsInDay.map(angleFunc) 23

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

26 val rotate = keys a ↓ shift ↓ a ↑ shift ↑ pgup ↓ pgup ↑ keys

Slide 27

Slide 27 text

27 val rotate = keys.filter(_ == PAGEUP) a ↓ shift ↓ a ↑ shift ↑ pgup ↓ pgup ↑ keys pgup ↓ pgup ↑ filter

Slide 28

Slide 28 text

28 val rotate = keys.filter(_ == PAGEUP) .map(_.down) a ↓ shift ↓ a ↑ shift ↑ pgup ↓ pgup ↑ keys pgup ↓ pgup ↑ filter true false map

Slide 29

Slide 29 text

29 if (rotate()) viewAngle += 1 true false map

Slide 30

Slide 30 text

30 Signals

Slide 31

Slide 31 text

31 Reactives are discrete

Slide 32

Slide 32 text

32 Signals are continuous

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

45 http://youtu.be/blG95W5uWQ8 Preview

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

50 http://youtu.be/NMVhirZLWmA Preview

Slide 51

Slide 51 text

51 Higher-order reactive values

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

54 val mids = mouse .filter(_.button == MIDDLE) mids ↓ ↓ ↓ ↑ ↑ ↑

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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]]

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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)

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

61 val drags = down .map(_ => mouse.until(up)) .map(_.map(_.xy)) drags 1, 1 2, 3 3, 5 4, 6 6, 9 9, 9

Slide 62

Slide 62 text

62 val drags = down .map(_ => mouse.until(up)) .map(_.map(_.xy)) .map(_.diffPast(_.xy - _.xy)) drags 0, 0 1, 2 1, 2 0, 0 2, 3 0, 0

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

65 http://youtu.be/RsMSZ7OH2fo Preview However, a lot of object allocations lead to GC issues.

Slide 66

Slide 66 text

66 Reactive mutators

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

69 Reactive[immutable.Matrix[T]]

Slide 70

Slide 70 text

70 val screenMat: Signal[Matrix] = (projMat zip viewMat)(_ * _) val invScreenMat = screenMat.map(_.inverse) (4*4*8 + 16 + 16)*4*100 = 64 kb/s

Slide 71

Slide 71 text

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) }

Slide 72

Slide 72 text

72 Reactive collections

Slide 73

Slide 73 text

73 http://youtu.be/ebbrAHNsexc Preview How do we model that a character is selected?

Slide 74

Slide 74 text

74 val selected: Reactive[Set[Character]]

Slide 75

Slide 75 text

75 val selected: ReactSet[Character]

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

78 A reactive collection is a pair of reactive values

Slide 79

Slide 79 text

79 val selected = new ReactHashSet[Character]

Slide 80

Slide 80 text

80

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

82

Slide 83

Slide 83 text

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) } }

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

95 Is Scala Ready?

Slide 96

Slide 96 text

96 YES!

Slide 97

Slide 97 text

97 Thank you!