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

ScalaDays 2014 - Reactive Scala 3D Game Engine

Embed Size (px)

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.

Citation preview

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!