Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin DSL Magic for Augmented Reality

Kotlin DSL Magic for Augmentedย Reality

GDG Dev Fest London 2018

Giovanni Laquidara

December 07, 2018
Tweet

More Decks by Giovanni Laquidara

Other Decks in Science

Transcript

  1. AR can bring anything to you. AR can *bring* anything

    to you. It adds computer-generated information and objects to your everyday world. @joaolaq
  2. ARCore provides SDKs for many of the most popular development

    environments. These SDKs provide native APIs for all of the essential AR features like motion tracking, environmental understanding, and light estimation. With these capabilities you can build entirely new AR experiences or enhance existing apps with AR features. Use your favourite environment @joaolaq
  3. Sceneform Sceneform is a 3D framework that makes it easy

    for Java developers to build augmented reality apps. โ€ข A high-level scene graph API โ€ข A realistic physically based renderer provided by Filament โ€ข An Android Studio plugin for importing, viewing, and building 3D assets https://developers.google.com/ar/develop/java/sceneform/
  4. Inspiration <html> <head> <title> A Simple HTML Document </title> </head>

    <body> <p>This is a very simple HTML document</p> <p>It only has two paragraphs</p> </body> </html>
  5. Domain Specific Languages by Martin Fowler (with Rebecca Parsons) Domain

    Specific Languages (DSLs) have been around since I've been in computing, but it's hard to find much information about how to work with them. DSLs are small languages, focused on a particular aspect of a software system. You can't build a whole program with a DSL, but you often use multiple DSLs in a system mainly written in a general purpose language.
  6. Internal DSL โ€ข Internal DSLs are particular ways of using

    a host language to give the host language the feel of a particular language. โ€ข External DSLs have their own custom syntax and you write a full parser to process them.
  7. Kotlin can help us with โ€ข Use of lambdas outside

    of method parentheses โ€ข Lambdas with receivers โ€ข Builders โ€ข Infix โ€ข DSL Marker
  8. lambdas outside of method parentheses. fun scene(block: (Scene) -> Unit)

    : Scene { val s = Scene() block(s) return s }
  9. A scene now val scene = scene { it.nodes =

    listOf(Node(), Node(), Node()) }
  10. Lambda with receiver fun scene(block: Scene.() -> Unit): Scene {

    val s = Scene() s.block() return s } // Or better fun scene(block: Scene.() -> Unit): Scene = Scene().apply { block }
  11. A scene now val scene = scene { nodes =

    listOf(Node(), Node(), Node()) }
  12. Add an Anchor Node // SceneBuilder fun anchorNode(anchor: Anchor, setup:

    AnchorNodeBuilder.() -> Unit = {}) { val nodeBuilder = AnchorNodeBuilder(anchor) nodeBuilder.setup() nodes += nodeBuilder.build() }
  13. AnchorNodeBuilder // SceneBuilder class AnchorNodeBuilder(var anchor: Anchor?) { private val

    nodes = mutableListOf<Node>() fun build(): AnchorNode { anchor?.let { return AnchorNode(anchor).apply { nodes.forEach { it.setParent(this)} } } ?: throw IllegalArgumentException("Anchor cannot be null") }
  14. Our Scene now val scene = scene { anchorNode(anchora) {

    node { transformationSystem = fragment.transformationSystem model = renderable } } }
  15. Add a Transformable Node // AnchorNodeBuilder fun node(transformationSystem: TransformationSystem? =

    null, model: Renderable? = null, setup: NodeBuilder.() -> Unit = {}) { val nodeBuilder = NodeBuilder(transformationSystem, model) nodeBuilder.setup() nodes += nodeBuilder.build() }
  16. How to build a Node class NodeBuilder(var transformationSystem: TransformationSystem?, var

    model: Renderable?) { fun build(): Node { transformationSystem?.let { return TransformableNode(transformationSystem).apply { renderable = model } } ?: throw IllegalArgumentException("TransformationSystem cannot be null") } }
  17. Generalize open class NodeBuilder(var position: Vector3?, var scale: Vector3?, var

    model: Renderable?) { protected open val nodes = mutableListOf<Node>() open fun build(): Node { return Node().apply { if (position != null) localPosition = position if (scale != null) localScale = scale if (model != null) renderable = model nodes.forEach { it.setParent(this) } } }
  18. Generalize โ€ฆโ€ฆ.. fun node(position: Vector3? = null, scale: Vector3? =

    null, model: Renderable? = null, setup: NodeBuilder.() -> Unit = {}) { val nodeBuilder = NodeBuilder(position, scale, model) nodeBuilder.setup() nodes += nodeBuilder.build() } }
  19. Generalize class AnchorNodeBuilder(var anchor: Anchor?) : NodeBuilder(null, null, null) {

    override fun build(): AnchorNode { anchor?.let { return AnchorNode(anchor).apply { nodes.forEach { it.setParent(this) } } } ?: throw IllegalArgumentException("Anchor cannot be null") } }
  20. DSL Marker ( Context control ) @DslMarker annotation class ArDsl

    @ArDsl class NodeBuilder(var transformationSystem: TransformationSystem?, var model: Renderable?) {
  21. Future to couroutines import kotlinx.coroutines.future.await private suspend fun placeObject(fragment: ArFragment,

    anchor: Anchor, model: Uri) { val renderableFuture = ModelRenderable.builder() .setSource(fragment.context, model) .build() try { addNodeToScene(fragment, anchor, renderableFuture.await())
  22. Complex sample val scene = scene { anchorNode { anchor

    = anchora node { node { position = Vector3(0.0f, 0.5f, 0.0f) scale = Vector3(0.5f, 0.5f, 0.5f) model = sunRenderable node { position = Vector3(1.0f * AU_TO_METERS, 0.0f, 0.0f) scale = Vector3(0.05f, 0.05f, 0.05f) model = earthRenderable } }
  23. With Kotlin super powers โ€ข Use of lambdas outside of

    method parentheses โ€ข Lambdas with receivers โ€ข Builders โ€ข Infix โ€ข DSL Marker โ€ข Operator Overloading โ€ข Extension functions