@@ -19,7 +19,9 @@ import indigo.shared.scenegraph.DirectionLight
1919import indigo .shared .scenegraph .EntityNode
2020import indigo .shared .scenegraph .Falloff
2121import indigo .shared .scenegraph .Graphic
22+ import indigo .shared .scenegraph .Layer
2223import indigo .shared .scenegraph .LayerEntry
24+ import indigo .shared .scenegraph .LayerKey
2325import indigo .shared .scenegraph .Light
2426import indigo .shared .scenegraph .PointLight
2527import indigo .shared .scenegraph .SceneUpdateFragment
@@ -29,6 +31,7 @@ import indigo.shared.scenegraph.Sprite
2931import indigo .shared .shader .ShaderData
3032import indigo .shared .time .GameTime
3133
34+ import scala .annotation .tailrec
3235import scala .scalajs .js .JSConverters .*
3336
3437final class SceneProcessor (
@@ -112,16 +115,13 @@ final class SceneProcessor(
112115 }
113116
114117 val displayLayers : scalajs.js.Array [(DisplayLayer , scalajs.js.Array [(String , DisplayObject )])] =
115- scene.layers
116- .flatMap(l =>
117- l.toBatch
118- .filter(content => content.visible.getOrElse( true ))
118+ SceneProcessor
119+ .compactLayers(scene.layers)
120+ .flatMap((maybeLayerKey, contentLayers) =>
121+ contentLayers
119122 .map(
120123 (
121- l match {
122- case LayerEntry .Keyed (tag, _) => Some (tag)
123- case _ => None
124- },
124+ maybeLayerKey,
125125 _
126126 )
127127 )
@@ -329,6 +329,69 @@ object SceneProcessor {
329329 data = DisplayObjectConversions .packUBO(ub.uniforms, ub.uniformHash, false )
330330 )
331331 }
332+
333+ /** Compact layers by squashing layers that have the same properties.
334+ */
335+ def compactLayers (layerEntries : Batch [LayerEntry ]): Batch [(Option [LayerKey ], Batch [Layer .Content ])] =
336+ layerEntries.map {
337+ case LayerEntry .NoKey (layer : Layer .Content ) =>
338+ val ls = if layer.visible.getOrElse(true ) then Batch (layer) else Batch .empty
339+
340+ (None , ls)
341+
342+ case LayerEntry .NoKey (stack : Layer .Stack ) =>
343+ val ls = compactContentLayers(stack.toBatch)
344+
345+ (None , ls)
346+
347+ case LayerEntry .Keyed (key, layer : Layer .Content ) =>
348+ val ls = if layer.visible.getOrElse(true ) then Batch (layer) else Batch .empty
349+
350+ (Option (key), ls)
351+
352+ case LayerEntry .Keyed (key, stack : Layer .Stack ) =>
353+ val ls = compactContentLayers(stack.toBatch)
354+
355+ (Option (key), ls)
356+ }
357+
358+ def compactContentLayers (contentLayers : Batch [Layer .Content ]): Batch [Layer .Content ] =
359+ @ tailrec
360+ def rec (remaining : Batch [Layer .Content ], current : Layer .Content , acc : Batch [Layer .Content ]): Batch [Layer .Content ] =
361+ if remaining.length == 0 then acc :+ current
362+ else
363+ val head = remaining.head
364+ val tail = remaining.tail
365+
366+ if head.visible.exists(_ == false ) then rec(tail, current, acc)
367+ else if canCompactLayers(current, head) then
368+ rec(
369+ tail,
370+ current.copy(nodes = current.nodes ++ head.nodes, cloneBlanks = current.cloneBlanks ++ head.cloneBlanks),
371+ acc
372+ )
373+ else rec(tail, head, acc :+ current)
374+
375+ val contentLayersFirstIsVisible =
376+ contentLayers.dropWhile(l => l.visible.exists(_ == false ))
377+
378+ if contentLayersFirstIsVisible.length < 2 then contentLayersFirstIsVisible
379+ else
380+ val head = contentLayersFirstIsVisible.head
381+ val tail = contentLayersFirstIsVisible.tail
382+
383+ rec(tail, head, Batch .empty)
384+
385+ /** The rule is that if the two layers have all the same properties, ignoring the scene nodes and clone blanks, then
386+ * we can compact them.
387+ */
388+ def canCompactLayers (a : Layer .Content , b : Layer .Content ): Boolean =
389+ a.lights == b.lights &&
390+ a.magnification == b.magnification &&
391+ a.visible == b.visible &&
392+ a.blending == b.blending &&
393+ a.camera == b.camera
394+
332395}
333396
334397final case class LightData (
0 commit comments