From cdf65294f91eb9d3cc91849c6e2cf9dccb6a22bb Mon Sep 17 00:00:00 2001 From: Christian Williams Date: Thu, 28 Jan 2021 13:13:46 -0800 Subject: [PATCH 1/7] Rename RenderTarget to FixtureRenderTarget and extract superclass. We'll eventually have a DirectRenderTarget used for direct-to-screen previews. --- .../kotlin/baaahs/fixtures/DeviceType.kt | 4 ++-- .../kotlin/baaahs/fixtures/FixtureManager.kt | 3 ++- .../baaahs/fixtures/PixelArrayDevice.kt | 24 +++++++++++++------ .../baaahs/gl/render/ModelRenderEngine.kt | 20 +++++++++------- .../kotlin/baaahs/gl/render/RenderManager.kt | 4 ++-- .../kotlin/baaahs/gl/render/RenderTarget.kt | 23 ++++++++++++------ .../gl/render/PerPixelDataSourceForTest.kt | 1 + .../baaahs/gl/render/RenderEngineTest.kt | 2 +- .../baaahs/fixtures/FixtureManagerSpec.kt | 4 ++-- 9 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/commonMain/kotlin/baaahs/fixtures/DeviceType.kt b/src/commonMain/kotlin/baaahs/fixtures/DeviceType.kt index e4becd667d..2be1e8f0f3 100644 --- a/src/commonMain/kotlin/baaahs/fixtures/DeviceType.kt +++ b/src/commonMain/kotlin/baaahs/fixtures/DeviceType.kt @@ -5,7 +5,7 @@ import baaahs.gl.GlContext import baaahs.gl.data.ProgramFeed import baaahs.gl.glsl.GlslProgram import baaahs.gl.patch.ContentType -import baaahs.gl.render.RenderTarget +import baaahs.gl.render.FixtureRenderTarget import baaahs.glsl.Uniform import baaahs.show.DataSourceBuilder import baaahs.show.Shader @@ -108,7 +108,7 @@ class FloatsParamBuffer(val id: String, val stride: Int, private val gl: GlConte } fun scoped( - renderTarget: RenderTarget, + renderTarget: FixtureRenderTarget, callback: ((Int) -> Float)? = null ) = object : BufferView { val offset = renderTarget.pixel0Index diff --git a/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt b/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt index 577047e53f..5e46e950e5 100644 --- a/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt +++ b/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt @@ -3,6 +3,7 @@ package baaahs.fixtures import baaahs.getBang import baaahs.gl.glsl.GlslProgram import baaahs.gl.patch.PatchResolver +import baaahs.gl.render.FixtureRenderTarget import baaahs.gl.render.RenderManager import baaahs.gl.render.RenderTarget import baaahs.show.live.ActivePatchSet @@ -12,7 +13,7 @@ import baaahs.util.Logger class FixtureManager( private val renderManager: RenderManager, - private val renderTargets: MutableMap = hashMapOf() + private val renderTargets: MutableMap = hashMapOf() ) { private val frameListeners: MutableList<() -> Unit> = arrayListOf() private val changedFixtures = mutableListOf() diff --git a/src/commonMain/kotlin/baaahs/fixtures/PixelArrayDevice.kt b/src/commonMain/kotlin/baaahs/fixtures/PixelArrayDevice.kt index 7b0ca586be..5443df9bfb 100644 --- a/src/commonMain/kotlin/baaahs/fixtures/PixelArrayDevice.kt +++ b/src/commonMain/kotlin/baaahs/fixtures/PixelArrayDevice.kt @@ -8,6 +8,7 @@ import baaahs.gl.data.* import baaahs.gl.glsl.GlslProgram import baaahs.gl.glsl.GlslType import baaahs.gl.patch.ContentType +import baaahs.gl.render.FixtureRenderTarget import baaahs.gl.render.RenderTarget import baaahs.gl.shader.InputPort import baaahs.glsl.Uniform @@ -17,6 +18,7 @@ import baaahs.plugin.classSerializer import baaahs.show.DataSource import baaahs.show.DataSourceBuilder import baaahs.show.Shader +import baaahs.util.Logger import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @@ -119,14 +121,18 @@ class PixelLocationFeed( override val buffer = FloatsParamBuffer(id, 3, gl) override fun setOnBuffer(renderTarget: RenderTarget) = run { - val pixelLocations = renderTarget.fixture.pixelLocations - buffer.scoped(renderTarget).also { view -> - for (pixelIndex in 0 until min(pixelLocations.size, renderTarget.pixelCount)) { - val location = pixelLocations[pixelIndex] - view[pixelIndex, 0] = location.x - view[pixelIndex, 1] = location.y - view[pixelIndex, 2] = location.z + if (renderTarget is FixtureRenderTarget) { + val pixelLocations = renderTarget.fixture.pixelLocations + buffer.scoped(renderTarget).also { view -> + for (pixelIndex in 0 until min(pixelLocations.size, renderTarget.pixelCount)) { + val location = pixelLocations[pixelIndex] + view[pixelIndex, 0] = location.x + view[pixelIndex, 1] = location.y + view[pixelIndex, 2] = location.z + } } + } else { + logger.warn { "Attempted to set per-pixel data for a non-FixtureRenderTarget, but that's impossible!" } } Unit } @@ -144,4 +150,8 @@ class PixelLocationFeed( override fun release() { refCounter.release() } + + companion object { + private val logger = Logger() + } } \ No newline at end of file diff --git a/src/commonMain/kotlin/baaahs/gl/render/ModelRenderEngine.kt b/src/commonMain/kotlin/baaahs/gl/render/ModelRenderEngine.kt index cde27e51a8..5d427ccc45 100644 --- a/src/commonMain/kotlin/baaahs/gl/render/ModelRenderEngine.kt +++ b/src/commonMain/kotlin/baaahs/gl/render/ModelRenderEngine.kt @@ -22,13 +22,13 @@ class ModelRenderEngine( private val deviceType: DeviceType, private val minTextureWidth: Int = 16 ) : RenderEngine(gl) { - private val renderTargetsToAdd: MutableList = mutableListOf() - private val renderTargetsToRemove: MutableList = mutableListOf() + private val renderTargetsToAdd: MutableList = mutableListOf() + private val renderTargetsToRemove: MutableList = mutableListOf() var pixelCount: Int = 0 var nextPixelOffset: Int = 0 var nextRectOffset: Int = 0 - private val renderTargets: MutableList = mutableListOf() + private val renderTargets: MutableList = mutableListOf() private var renderPlan: DeviceTypeRenderPlan? = null private val resultBuffers = gl.runInContext { @@ -46,7 +46,7 @@ class ModelRenderEngine( // Workaround for compile error on case-insensitive FS: init { arrangement = gl.runInContext { Arrangement(0, emptyList()) } } - fun addFixture(fixture: Fixture): RenderTarget { + fun addFixture(fixture: Fixture): FixtureRenderTarget { if (fixture.deviceType != deviceType) { throw IllegalArgumentException( "This RenderEngine can't accept ${fixture.deviceType} devices, only $deviceType." @@ -58,7 +58,7 @@ class ModelRenderEngine( fbMaxPixWidth, fixture ) - val renderTarget = RenderTarget( + val renderTarget = FixtureRenderTarget( fixture, nextRectOffset, rects, modelInfo, fixture.pixelCount, nextPixelOffset, resultBuffers ) nextPixelOffset += fixture.pixelCount @@ -68,7 +68,7 @@ class ModelRenderEngine( return renderTarget } - fun removeRenderTarget(renderTarget: RenderTarget) { + fun removeRenderTarget(renderTarget: FixtureRenderTarget) { renderTargetsToRemove.add(renderTarget) } @@ -81,7 +81,7 @@ class ModelRenderEngine( engineFeed.maybeResizeAndPopulate(arrangement, renderTargets) } - private fun EngineFeed.maybeResizeAndPopulate(arrangement: Arrangement?, renderTargets: List) { + private fun EngineFeed.maybeResizeAndPopulate(arrangement: Arrangement?, renderTargets: List) { if (this is PerPixelEngineFeed) { resize(arrangement?.safeWidth ?: 1, arrangement?.safeHeight ?: 1) { renderTargets.forEach { renderTarget -> @@ -151,7 +151,7 @@ class ModelRenderEngine( val Int.bufHeight: Int get() = this / fbMaxPixWidth + 1 val Int.bufSize: Int get() = bufWidth * bufHeight - inner class Arrangement(val pixelCount: Int, addedRenderTargets: List) { + inner class Arrangement(val pixelCount: Int, addedRenderTargets: List) { init { logger.info { "Creating ${deviceType::class.simpleName} arrangement with $pixelCount pixels" } } @@ -201,6 +201,7 @@ class ModelRenderEngine( quad.prepareToRender(program.vertexAttribLocation) { programRenderPlan.renderTargets.forEach { renderTarget -> + renderTarget as FixtureRenderTarget renderTarget.usingProgram(program) program.aboutToRenderFixture(renderTarget) quad.renderRects(renderTarget) @@ -208,6 +209,7 @@ class ModelRenderEngine( } } else { programRenderPlan.renderTargets.forEach { renderTarget -> + renderTarget as FixtureRenderTarget renderTarget.usingProgram(null) } } @@ -249,7 +251,7 @@ class ModelRenderEngine( return rects } - fun Quad.renderRects(renderTarget: RenderTarget) { + fun Quad.renderRects(renderTarget: FixtureRenderTarget) { renderTarget.rects.indices.forEach { i -> this.renderRect(renderTarget.rect0Index + i) } diff --git a/src/commonMain/kotlin/baaahs/gl/render/RenderManager.kt b/src/commonMain/kotlin/baaahs/gl/render/RenderManager.kt index 7eab858062..9d812af7ce 100644 --- a/src/commonMain/kotlin/baaahs/gl/render/RenderManager.kt +++ b/src/commonMain/kotlin/baaahs/gl/render/RenderManager.kt @@ -25,11 +25,11 @@ class RenderManager( renderEngines.values.forEach { it.draw() } } - fun addFixture(fixture: Fixture): RenderTarget { + fun addFixture(fixture: Fixture): FixtureRenderTarget { return getEngineFor(fixture.deviceType).addFixture(fixture) } - fun removeRenderTarget(renderTarget: RenderTarget) { + fun removeRenderTarget(renderTarget: FixtureRenderTarget) { getEngineFor(renderTarget.fixture.deviceType).removeRenderTarget(renderTarget) } diff --git a/src/commonMain/kotlin/baaahs/gl/render/RenderTarget.kt b/src/commonMain/kotlin/baaahs/gl/render/RenderTarget.kt index 4d902b3ab7..d93c8f1d3c 100644 --- a/src/commonMain/kotlin/baaahs/gl/render/RenderTarget.kt +++ b/src/commonMain/kotlin/baaahs/gl/render/RenderTarget.kt @@ -5,25 +5,34 @@ import baaahs.fixtures.ResultBuffer import baaahs.gl.glsl.GlslProgram import baaahs.model.ModelInfo -class RenderTarget( - val fixture: Fixture, +interface RenderTarget { + val fixture: Fixture + val modelInfo: ModelInfo + val pixelCount: Int + + fun sendFrame() + fun release() +} + +class FixtureRenderTarget( + override val fixture: Fixture, val rect0Index: Int, val rects: List, // these are in pixels, (0,0) at top left - val modelInfo: ModelInfo, - val pixelCount: Int, + override val modelInfo: ModelInfo, + override val pixelCount: Int, val pixel0Index: Int, resultBuffers: List -) { +) : RenderTarget { var program: GlslProgram? = null private set val resultViews = resultBuffers.map { it.getView(pixel0Index, pixelCount) } - fun sendFrame() { + override fun sendFrame() { fixture.transport.send(fixture, resultViews) } - fun release() { + override fun release() { program = null } diff --git a/src/commonTest/kotlin/baaahs/gl/render/PerPixelDataSourceForTest.kt b/src/commonTest/kotlin/baaahs/gl/render/PerPixelDataSourceForTest.kt index 796c06a5c5..4be909b1fc 100644 --- a/src/commonTest/kotlin/baaahs/gl/render/PerPixelDataSourceForTest.kt +++ b/src/commonTest/kotlin/baaahs/gl/render/PerPixelDataSourceForTest.kt @@ -40,6 +40,7 @@ class PerPixelDataSourceForTest(val updateMode: UpdateMode) : DataSource { override val buffer: FloatsParamBuffer = FloatsParamBuffer("---", 1, gl) override fun setOnBuffer(renderTarget: RenderTarget) = run { + renderTarget as FixtureRenderTarget counter++ buffer.scoped(renderTarget) { pixelIndex -> counter * 10 + pixelIndex diff --git a/src/commonTest/kotlin/baaahs/gl/render/RenderEngineTest.kt b/src/commonTest/kotlin/baaahs/gl/render/RenderEngineTest.kt index 9519f37809..75eed09c7d 100644 --- a/src/commonTest/kotlin/baaahs/gl/render/RenderEngineTest.kt +++ b/src/commonTest/kotlin/baaahs/gl/render/RenderEngineTest.kt @@ -301,5 +301,5 @@ private val directXyProjection = Shader( """.trimIndent() ) -val RenderTarget.colors: ColorResultType.ColorResultView +val FixtureRenderTarget.colors: ColorResultType.ColorResultView get() = PixelArrayDevice.getColorResults(this.resultViews) \ No newline at end of file diff --git a/src/jvmTest/kotlin/baaahs/fixtures/FixtureManagerSpec.kt b/src/jvmTest/kotlin/baaahs/fixtures/FixtureManagerSpec.kt index 8baeef8865..d619ff1c14 100644 --- a/src/jvmTest/kotlin/baaahs/fixtures/FixtureManagerSpec.kt +++ b/src/jvmTest/kotlin/baaahs/fixtures/FixtureManagerSpec.kt @@ -7,8 +7,8 @@ import baaahs.gl.glsl.GlslType import baaahs.gl.override import baaahs.gl.patch.ContentType import baaahs.gl.render.DeviceTypeForTest +import baaahs.gl.render.FixtureRenderTarget import baaahs.gl.render.RenderManager -import baaahs.gl.render.RenderTarget import baaahs.gl.shader.OpenShader import baaahs.gl.shader.OutputPort import baaahs.gl.testToolchain @@ -31,7 +31,7 @@ object FixtureManagerSpec : Spek({ val modelEntities by value { emptyList() } val model by value { fakeModel(modelEntities) } val renderManager by value { RenderManager(model) { FakeGlContext() } } - val renderTargets by value { linkedMapOf() } + val renderTargets by value { linkedMapOf() } val fixtureManager by value { FixtureManager(renderManager, renderTargets) } // Maintain stable fixture order for test. context("when fixtures of multiple types have been added") { From 3d9d5b5717a78d71a79a0a6c81e2250322f115c8 Mon Sep 17 00:00:00 2001 From: Christian Williams Date: Thu, 28 Jan 2021 15:42:18 -0800 Subject: [PATCH 2/7] Refactor PatchResolver to take datasources map later. --- .../kotlin/baaahs/fixtures/FixtureManager.kt | 5 +- .../kotlin/baaahs/gl/patch/PatchResolver.kt | 70 ++++++++++++------- .../kotlin/baaahs/gl/patch/PortDiagram.kt | 24 ++++--- .../kotlin/baaahs/glsl/GuruMeditationError.kt | 4 +- .../kotlin/baaahs/show/mutable/MutableShow.kt | 4 +- .../baaahs/gl/patch/PatchResolverSpec.kt | 4 +- 6 files changed, 67 insertions(+), 44 deletions(-) diff --git a/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt b/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt index 5e46e950e5..6ed7630f6f 100644 --- a/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt +++ b/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt @@ -99,8 +99,9 @@ class FixtureManager( val elapsedMs = timeSync { val patchResolution = PatchResolver( - openShow.allDataSources, renderManager, renderTargets.values, activePatchSet) - currentRenderPlan = patchResolution.createRenderPlan { _, dataSource -> + renderTargets.values, activePatchSet, renderManager + ) + currentRenderPlan = patchResolution.createRenderPlan(openShow.allDataSources) { _, dataSource -> openShow.feeds.getBang(dataSource, "data feed") } } diff --git a/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt b/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt index 71d48ffa0f..e5ae787026 100644 --- a/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt +++ b/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt @@ -8,6 +8,7 @@ import baaahs.fixtures.RenderPlan import baaahs.gl.glsl.CompilationException import baaahs.gl.glsl.FeedResolver import baaahs.gl.glsl.GlslException +import baaahs.gl.glsl.GlslProgram import baaahs.gl.render.RenderManager import baaahs.gl.render.RenderTarget import baaahs.glsl.GuruMeditationError @@ -18,15 +19,42 @@ import baaahs.show.live.OpenPatch import baaahs.util.Logger class PatchResolver( - private val dataSources: Map, - private val renderManager: RenderManager, - private val renderTargets: Collection, + renderTargets: Collection, + activePatchSet: ActivePatchSet, + private val renderManager: RenderManager +) : BasePatchResolver(renderTargets, activePatchSet) { + override fun buildProgram( + linkedPatch: LinkedPatch, + deviceType: DeviceType, + feedResolver: FeedResolver + ) = try { + renderManager.compile(deviceType, linkedPatch, feedResolver) + } catch (e: GlslException) { + logger.error(e) { "Error preparing program" } + if (e is CompilationException) { + e.source?.let { ShowRunner.logger.info { it } } + } + + renderManager.compile( + deviceType, GuruMeditationError(deviceType).linkedPatch, feedResolver + ) + } + + companion object { + private val logger = Logger() + + fun buildPortDiagram(vararg patches: OpenPatch) = PortDiagram(patches.toList()) + } +} + +abstract class BasePatchResolver( + renderTargets: Collection, private val activePatchSet: ActivePatchSet ) { val portDiagrams = renderTargets .groupBy { it.fixture.deviceType } - .mapValues { (deviceType, renderTargets) -> + .mapValues { (_, renderTargets) -> val patchSetsByKey = mutableMapOf() val renderTargetsByKey = mutableMapOf>() @@ -41,16 +69,23 @@ class PatchResolver( } patchSetsByKey.map { (key, patchSet) -> - PortDiagram(dataSources, patchSet) to + PortDiagram(patchSet) to renderTargetsByKey[key]!! as List } } - fun createRenderPlan(feedResolver: FeedResolver): RenderPlan { + fun createRenderPlan( + dataSources: Map, + feedResolver: FeedResolver + ): RenderPlan { return RenderPlan( portDiagrams.mapValues { (deviceType, devicePortDiagrams) -> val programsRenderPlans = devicePortDiagrams.map { (portDiagram, renderTargets) -> - val linkedPatch = portDiagram.resolvePatch(ShaderChannel.Main, deviceType.resultContentType) + val linkedPatch = portDiagram.resolvePatch( + ShaderChannel.Main, + deviceType.resultContentType, + dataSources + ) val program = linkedPatch?.let { buildProgram(it, deviceType, feedResolver) } @@ -63,28 +98,11 @@ class PatchResolver( ) } - private fun buildProgram( + abstract fun buildProgram( linkedPatch: LinkedPatch, deviceType: DeviceType, feedResolver: FeedResolver - ) = try { - renderManager.compile(deviceType, linkedPatch, feedResolver) - } catch (e: GlslException) { - logger.error(e) { "Error preparing program" } - if (e is CompilationException) { - e.source?.let { ShowRunner.logger.info { it } } - } - - renderManager.compile( - deviceType, GuruMeditationError(deviceType).linkedPatch, feedResolver - ) - } - - companion object { - private val logger = Logger() - fun buildPortDiagram(dataSources: Map, vararg patches: OpenPatch) = - PortDiagram(dataSources, patches.toList()) - } + ): GlslProgram } private typealias PatchSet = List \ No newline at end of file diff --git a/src/commonMain/kotlin/baaahs/gl/patch/PortDiagram.kt b/src/commonMain/kotlin/baaahs/gl/patch/PortDiagram.kt index 3baf6553c9..4b6f9838fa 100644 --- a/src/commonMain/kotlin/baaahs/gl/patch/PortDiagram.kt +++ b/src/commonMain/kotlin/baaahs/gl/patch/PortDiagram.kt @@ -7,13 +7,7 @@ import baaahs.show.live.LiveShaderInstance import baaahs.show.live.OpenPatch import baaahs.util.Logger -class PortDiagram( - dataSources: Map, - val patches: List -) { - private val dataSourceChannelLinks = dataSources.map { (id, dataSource) -> - (id to dataSource.contentType) to LiveShaderInstance.DataSourceLink(dataSource, id) - }.associate { it } +class PortDiagram(val patches: List) { private val candidates: Map private val resolvedNodes = hashMapOf() @@ -44,9 +38,13 @@ class PortDiagram( this.candidates = candidates.mapValues { (_, entries) -> Candidates(entries) } } - fun resolvePatch(shaderChannel: ShaderChannel, contentType: ContentType): LinkedPatch? { + fun resolvePatch( + shaderChannel: ShaderChannel, + contentType: ContentType, + dataSources: Map + ): LinkedPatch? { + val resolver = Resolver(dataSources) val track = Track(shaderChannel, contentType) - val resolver = Resolver() val rootProgramNode = resolver.resolve(track) return if (rootProgramNode != null) { @@ -94,7 +92,13 @@ class PortDiagram( } } - inner class Resolver { + inner class Resolver( + dataSources: Map + ) { + private val dataSourceChannelLinks = dataSources.map { (id, dataSource) -> + (id to dataSource.contentType) to LiveShaderInstance.DataSourceLink(dataSource, id) + }.associate { it } + private val trackResolvers = mutableMapOf() private val breadcrumbs = mutableListOf() private val currentBreadcrumb get() = breadcrumbs.last() diff --git a/src/commonMain/kotlin/baaahs/glsl/GuruMeditationError.kt b/src/commonMain/kotlin/baaahs/glsl/GuruMeditationError.kt index 09e9695b76..c6f037eb6b 100644 --- a/src/commonMain/kotlin/baaahs/glsl/GuruMeditationError.kt +++ b/src/commonMain/kotlin/baaahs/glsl/GuruMeditationError.kt @@ -35,8 +35,8 @@ class GuruMeditationError(deviceType: DeviceType) { val showPlayer = FakeShowPlayer(toolchain) val openShow = ShowOpener(toolchain, show, showPlayer).openShow() val openPatch = openShow.patches.only("patch") - linkedPatch = PatchResolver.buildPortDiagram(openShow.allDataSources, openPatch) - .resolvePatch(ShaderChannel.Main, deviceType.resultContentType) + linkedPatch = PatchResolver.buildPortDiagram(openPatch) + .resolvePatch(ShaderChannel.Main, deviceType.resultContentType, openShow.allDataSources) ?: error("Couldn't build guru meditation error patch.") } } diff --git a/src/commonMain/kotlin/baaahs/show/mutable/MutableShow.kt b/src/commonMain/kotlin/baaahs/show/mutable/MutableShow.kt index 6f0d3689a4..3ff75ac201 100644 --- a/src/commonMain/kotlin/baaahs/show/mutable/MutableShow.kt +++ b/src/commonMain/kotlin/baaahs/show/mutable/MutableShow.kt @@ -319,8 +319,8 @@ class MutablePatch { .getResolvedShaderInstances() val openPatch = OpenPatch(resolvedShaderInstances.values.toList(), surfaces) - val portDiagram = PatchResolver.buildPortDiagram(showBuilder.getDataSources(), openPatch) - return portDiagram.resolvePatch(ShaderChannel.Main, resultContentType) + val portDiagram = PatchResolver.buildPortDiagram(openPatch) + return portDiagram.resolvePatch(ShaderChannel.Main, resultContentType, showBuilder.getDataSources()) } fun addShaderInstance(mutableShaderInstance: MutableShaderInstance): MutablePatch { diff --git a/src/commonTest/kotlin/baaahs/gl/patch/PatchResolverSpec.kt b/src/commonTest/kotlin/baaahs/gl/patch/PatchResolverSpec.kt index 0ae6336f28..034bf1b7df 100644 --- a/src/commonTest/kotlin/baaahs/gl/patch/PatchResolverSpec.kt +++ b/src/commonTest/kotlin/baaahs/gl/patch/PatchResolverSpec.kt @@ -503,10 +503,10 @@ private fun generateLinkedPatch(dataSources: Map, activePatc val renderManager = RenderManager(model) { FakeGlContext() } val fixture = model.allEntities.first() val renderTarget = renderManager.addFixture(fakeFixture(1, fixture)) - val patchResolution = PatchResolver(dataSources, renderManager, listOf(renderTarget), activePatchSet) + val patchResolution = PatchResolver(listOf(renderTarget), activePatchSet, renderManager) val portDiagram = patchResolution.portDiagrams .getBang(PixelArrayDevice, "device type") .only("port diagram to render targets") .first - return portDiagram.resolvePatch(ShaderChannel.Main, Color)!! + return portDiagram.resolvePatch(ShaderChannel.Main, Color, dataSources)!! } From b211a5a9c9404d689f75cf44ae31057a44892e39 Mon Sep 17 00:00:00 2001 From: Christian Williams Date: Fri, 29 Jan 2021 11:59:34 -0800 Subject: [PATCH 3/7] Rename ClientShowPlayer to ClientStageManager (to match server side). --- .../client/{ClientShowPlayer.kt => ClientStageManager.kt} | 2 +- src/jsMain/kotlin/baaahs/app/ui/AppIndex.kt | 4 ++-- src/jsMain/kotlin/baaahs/client/WebClient.kt | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/commonMain/kotlin/baaahs/client/{ClientShowPlayer.kt => ClientStageManager.kt} (98%) diff --git a/src/commonMain/kotlin/baaahs/client/ClientShowPlayer.kt b/src/commonMain/kotlin/baaahs/client/ClientStageManager.kt similarity index 98% rename from src/commonMain/kotlin/baaahs/client/ClientShowPlayer.kt rename to src/commonMain/kotlin/baaahs/client/ClientStageManager.kt index 4118ba0f80..947ac4d04c 100644 --- a/src/commonMain/kotlin/baaahs/client/ClientShowPlayer.kt +++ b/src/commonMain/kotlin/baaahs/client/ClientStageManager.kt @@ -6,7 +6,7 @@ import baaahs.model.ModelInfo import baaahs.show.DataSource import kotlinx.serialization.json.JsonElement -class ClientShowPlayer( +class ClientStageManager( toolchain: Toolchain, private val pubSub: PubSub.Client, modelInfo: ModelInfo diff --git a/src/jsMain/kotlin/baaahs/app/ui/AppIndex.kt b/src/jsMain/kotlin/baaahs/app/ui/AppIndex.kt index 0e032cf956..5850c86b96 100644 --- a/src/jsMain/kotlin/baaahs/app/ui/AppIndex.kt +++ b/src/jsMain/kotlin/baaahs/app/ui/AppIndex.kt @@ -72,7 +72,7 @@ val AppIndex = xComponent("AppIndex") { props -> val myAppContext = memo(theme) { jsObject { - this.showPlayer = props.showPlayer + this.showPlayer = props.stageManager this.dragNDrop = dragNDrop this.webClient = webClient this.plugins = webClient.plugins @@ -362,7 +362,7 @@ external interface AppIndexProps : RProps { var id: String var webClient: WebClient.Facade var undoStack: UndoStack - var showPlayer: ShowPlayer + var stageManager: ShowPlayer } fun RBuilder.appIndex(handler: RHandler): ReactElement = diff --git a/src/jsMain/kotlin/baaahs/client/WebClient.kt b/src/jsMain/kotlin/baaahs/client/WebClient.kt index 7a96c96245..b344568885 100644 --- a/src/jsMain/kotlin/baaahs/client/WebClient.kt +++ b/src/jsMain/kotlin/baaahs/client/WebClient.kt @@ -56,7 +56,7 @@ class WebClient( facade.notifyChanged() } - private val showPlayer = ClientShowPlayer(toolchain, pubSub, model) + private val stageManager = ClientStageManager(toolchain, pubSub, model) private val showEditStateChannel = pubSub.subscribe( ShowEditorState.createTopic(toolchain.plugins, remoteFsSerializer) @@ -121,7 +121,7 @@ class WebClient( val newShowState = showEditorState?.showState val newIsUnsaved = showEditorState?.isUnsaved ?: false val newFile = showEditorState?.file - val newOpenShow = newShow?.let { showPlayer.openShow(newShow, newShowState) } + val newOpenShow = newShow?.let { stageManager.openShow(newShow, newShowState) } openShow?.release() openShow = newOpenShow this.show = newShow @@ -137,7 +137,7 @@ class WebClient( this.id = "Client Window" this.webClient = facade this.undoStack = this@WebClient.undoStack - this.showPlayer = this@WebClient.showPlayer + this.stageManager = this@WebClient.stageManager }) } From c35265ca708879c7127b4075507dd6337c7c2059 Mon Sep 17 00:00:00 2001 From: Christian Williams Date: Wed, 10 Feb 2021 16:11:05 -0800 Subject: [PATCH 4/7] Vague stab at encapsulating concerns inside VizSurface. --- src/jsMain/kotlin/baaahs/visualizer/Visualizer.kt | 11 ++++++++--- src/jsMain/kotlin/baaahs/visualizer/VizSurface.kt | 11 +++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/jsMain/kotlin/baaahs/visualizer/Visualizer.kt b/src/jsMain/kotlin/baaahs/visualizer/Visualizer.kt index 8bb12dae29..473869685c 100644 --- a/src/jsMain/kotlin/baaahs/visualizer/Visualizer.kt +++ b/src/jsMain/kotlin/baaahs/visualizer/Visualizer.kt @@ -1,6 +1,6 @@ package baaahs.visualizer -import baaahs.* +import baaahs.JsMapperUi import baaahs.model.Model import baaahs.model.MovingHead import baaahs.sim.FakeDmxUniverse @@ -8,6 +8,7 @@ import baaahs.util.Clock import baaahs.util.Framerate import baaahs.util.asMillis import baaahs.visualizer.movers.VizMovingHead +import baaahs.window import org.w3c.dom.HTMLDivElement import org.w3c.dom.events.MouseEvent import three.js.* @@ -37,7 +38,7 @@ class Visualizer(model: Model, private val clock: Clock) : JsMapperUi.StatusList set(isRunning) { field = isRunning - vizPanels.forEach { panel -> panel.faceMaterial.transparent = !isRunning } + vizPanels.forEach { it.mapperIsRunning = isRunning } if (isRunning) { rotate = false @@ -66,7 +67,7 @@ class Visualizer(model: Model, private val clock: Clock) : JsMapperUi.StatusList private val rendererListeners = mutableListOf<() -> Unit>() - private var vizPanels = mutableListOf() + private var vizPanels = mutableListOf() init { scene.add(camera) @@ -220,6 +221,10 @@ class Visualizer(model: Model, private val clock: Clock) : JsMapperUi.StatusList mapperIsRunning = isRunning } + interface FixtureViz { + var mapperIsRunning: Boolean + } + interface FrameListener { @JsName("onFrameReady") fun onFrameReady(scene: Scene, camera: Camera) diff --git a/src/jsMain/kotlin/baaahs/visualizer/VizSurface.kt b/src/jsMain/kotlin/baaahs/visualizer/VizSurface.kt index 445f56d49a..c39474afb5 100644 --- a/src/jsMain/kotlin/baaahs/visualizer/VizSurface.kt +++ b/src/jsMain/kotlin/baaahs/visualizer/VizSurface.kt @@ -59,10 +59,10 @@ class SurfaceGeometry(surface: Model.Surface) { } } -class VizSurface(val surfaceGeometry: SurfaceGeometry, val scene: Scene) { +class VizSurface(val surfaceGeometry: SurfaceGeometry, val scene: Scene) : Visualizer.FixtureViz { val name: String get() = surfaceGeometry.name private val lineMaterial = LineBasicMaterial().apply { color.set(0xaaaaaa) } - internal var faceMaterial = MeshBasicMaterial().apply { color.set(0x222222) } + private var faceMaterial = MeshBasicMaterial().apply { color.set(0x222222) } private val mesh = Mesh(surfaceGeometry.geometry, this.faceMaterial) private val lines: List> val panelNormal: Vector3 get() = surfaceGeometry.panelNormal @@ -94,6 +94,13 @@ class VizSurface(val surfaceGeometry: SurfaceGeometry, val scene: Scene) { } } + override var mapperIsRunning: Boolean = false + get() = field + set(isRunning) { + field = isRunning + faceMaterial.transparent = !isRunning + } + fun getPixelLocationsInPanelSpace(): Array? { return vizPixels?.getPixelLocationsInPanelSpace(this) } From 7e32bbd3adf600b777dbd653410b8acdeeb91d82 Mon Sep 17 00:00:00 2001 From: Christian Williams Date: Thu, 11 Feb 2021 19:22:58 -0800 Subject: [PATCH 5/7] Refactor patch resolution a bit. --- src/commonMain/kotlin/baaahs/ShowRunner.kt | 2 +- .../kotlin/baaahs/fixtures/FixtureManager.kt | 14 ++----- .../kotlin/baaahs/gl/patch/PatchResolver.kt | 3 +- .../kotlin/baaahs/show/live/OpenShow.kt | 37 +++++++++++++++++-- .../baaahs/fixtures/FixtureManagerSpec.kt | 4 +- 5 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/commonMain/kotlin/baaahs/ShowRunner.kt b/src/commonMain/kotlin/baaahs/ShowRunner.kt index c8fa913d4b..cd961b38b8 100644 --- a/src/commonMain/kotlin/baaahs/ShowRunner.kt +++ b/src/commonMain/kotlin/baaahs/ShowRunner.kt @@ -50,7 +50,7 @@ class ShowRunner( fixtureManager.activePatchSetChanged(openShow.activePatchSet()) } - return fixtureManager.maybeUpdateRenderPlans(openShow) + return fixtureManager.maybeUpdateRenderPlans() } fun release() { diff --git a/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt b/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt index 6ed7630f6f..2e2706aae8 100644 --- a/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt +++ b/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt @@ -1,13 +1,10 @@ package baaahs.fixtures -import baaahs.getBang import baaahs.gl.glsl.GlslProgram -import baaahs.gl.patch.PatchResolver import baaahs.gl.render.FixtureRenderTarget import baaahs.gl.render.RenderManager import baaahs.gl.render.RenderTarget import baaahs.show.live.ActivePatchSet -import baaahs.show.live.OpenShow import baaahs.timeSync import baaahs.util.Logger @@ -19,7 +16,7 @@ class FixtureManager( private val changedFixtures = mutableListOf() private var totalFixtures = 0 - private var currentActivePatchSet: ActivePatchSet = ActivePatchSet(emptyList()) + private var currentActivePatchSet: ActivePatchSet = ActivePatchSet.Empty private var activePatchSetChanged = false internal var currentRenderPlan: RenderPlan? = null @@ -89,7 +86,7 @@ class FixtureManager( } } - fun maybeUpdateRenderPlans(openShow: OpenShow): Boolean { + fun maybeUpdateRenderPlans(): Boolean { var remapFixtures = incorporateFixtureChanges() // Maybe build new shaders. @@ -98,12 +95,7 @@ class FixtureManager( val activePatchSet = currentActivePatchSet val elapsedMs = timeSync { - val patchResolution = PatchResolver( - renderTargets.values, activePatchSet, renderManager - ) - currentRenderPlan = patchResolution.createRenderPlan(openShow.allDataSources) { _, dataSource -> - openShow.feeds.getBang(dataSource, "data feed") - } + currentRenderPlan = activePatchSet.createRenderPlan(renderManager, renderTargets.values) } logger.info { diff --git a/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt b/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt index e5ae787026..8e140678c6 100644 --- a/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt +++ b/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt @@ -59,8 +59,7 @@ abstract class BasePatchResolver( val renderTargetsByKey = mutableMapOf>() renderTargets.forEach { renderTarget -> - val patchSet = activePatchSet.activePatches - .filter { patch -> patch.matches(renderTarget.fixture) } + val patchSet = activePatchSet.forFixture(renderTarget.fixture) val key = patchSet.joinToString(":") { it.serial.toString(16) } patchSetsByKey[key] = patchSet diff --git a/src/commonMain/kotlin/baaahs/show/live/OpenShow.kt b/src/commonMain/kotlin/baaahs/show/live/OpenShow.kt index 5313f3dd91..b990df0710 100644 --- a/src/commonMain/kotlin/baaahs/show/live/OpenShow.kt +++ b/src/commonMain/kotlin/baaahs/show/live/OpenShow.kt @@ -1,6 +1,12 @@ package baaahs.show.live import baaahs.* +import baaahs.fixtures.Fixture +import baaahs.fixtures.RenderPlan +import baaahs.gl.data.Feed +import baaahs.gl.patch.PatchResolver +import baaahs.gl.render.FixtureRenderTarget +import baaahs.gl.render.RenderManager import baaahs.show.DataSource import baaahs.show.Show import baaahs.show.mutable.MutableShow @@ -49,7 +55,7 @@ class OpenShow( MutableShow(show).apply(block) fun activePatchSet(): ActivePatchSet { - val builder = ActivePatchSetBuilder() + val builder = ActivePatchSetBuilder(this) addTo(builder, 0) return builder.build() } @@ -94,9 +100,30 @@ class OpenShow( } } -data class ActivePatchSet(val activePatches: List) +data class ActivePatchSet( + internal val activePatches: List, + private val allDataSources: Map, + private val feeds: Map +) { + fun createRenderPlan( + renderManager: RenderManager, + renderTargets: MutableCollection + ): RenderPlan { + val patchResolution = PatchResolver(renderTargets, this, renderManager) + return patchResolution.createRenderPlan(allDataSources) { _, dataSource -> + feeds.getBang(dataSource, "data feed") + } + } + + fun forFixture(fixture: Fixture): List = + activePatches.filter { patch -> patch.matches(fixture) } + + companion object { + val Empty = ActivePatchSet(emptyList(), emptyMap(), emptyMap()) + } +} -class ActivePatchSetBuilder { +class ActivePatchSetBuilder(private val openShow: OpenShow) { private val items = arrayListOf() private var nextSerial = 0 @@ -111,7 +138,9 @@ class ActivePatchSetBuilder { .thenBy { it.layoutContainerId } .thenBy { it.serial } ).map { it.patchHolder } - .flatMap { it.patches } + .flatMap { it.patches }, + openShow.allDataSources, + openShow.feeds ) } diff --git a/src/jvmTest/kotlin/baaahs/fixtures/FixtureManagerSpec.kt b/src/jvmTest/kotlin/baaahs/fixtures/FixtureManagerSpec.kt index d619ff1c14..312964807a 100644 --- a/src/jvmTest/kotlin/baaahs/fixtures/FixtureManagerSpec.kt +++ b/src/jvmTest/kotlin/baaahs/fixtures/FixtureManagerSpec.kt @@ -93,7 +93,7 @@ object FixtureManagerSpec : Spek({ beforeEachTest { fixtureManager.activePatchSetChanged(activePatchSet) - val updated = fixtureManager.maybeUpdateRenderPlans(openShow) + val updated = fixtureManager.maybeUpdateRenderPlans() expect(updated).toBe(true) } @@ -121,7 +121,7 @@ object FixtureManagerSpec : Spek({ expect(renderPlan.keys).toBe(setOf(fogMachineDevice)) fixtureManager.fixturesChanged(listOf(vuzuvela1), emptyList()) - val updated = fixtureManager.maybeUpdateRenderPlans(openShow) + val updated = fixtureManager.maybeUpdateRenderPlans() expect(updated).toBe(true) } From 130c76bf72e444817e3ab85da4e6fa3c79244f6f Mon Sep 17 00:00:00 2001 From: Christian Williams Date: Thu, 11 Feb 2021 19:50:18 -0800 Subject: [PATCH 6/7] Hook up client-side preview rendering. --- src/commonMain/kotlin/baaahs/ShowPlayer.kt | 6 ++ .../baaahs/client/ClientStageManager.kt | 41 ++++++++- .../kotlin/baaahs/fixtures/FixtureManager.kt | 4 +- .../kotlin/baaahs/gl/glsl/GlslProgram.kt | 3 - .../kotlin/baaahs/gl/patch/PatchResolver.kt | 6 +- .../kotlin/baaahs/show/live/OpenShow.kt | 2 +- src/jsMain/kotlin/baaahs/app/ui/AppContext.kt | 4 +- src/jsMain/kotlin/baaahs/app/ui/AppIndex.kt | 4 +- .../baaahs/app/ui/controls/Visualizer.kt | 22 ++--- .../baaahs/app/ui/preview/ClientPreview.kt | 85 +++++++++++++++++++ src/jsMain/kotlin/baaahs/client/WebClient.kt | 3 +- .../kotlin/baaahs/visualizer/Visualizer.kt | 41 ++++++--- 12 files changed, 177 insertions(+), 44 deletions(-) create mode 100644 src/jsMain/kotlin/baaahs/app/ui/preview/ClientPreview.kt diff --git a/src/commonMain/kotlin/baaahs/ShowPlayer.kt b/src/commonMain/kotlin/baaahs/ShowPlayer.kt index c3b27b0426..aaa3d26cc8 100644 --- a/src/commonMain/kotlin/baaahs/ShowPlayer.kt +++ b/src/commonMain/kotlin/baaahs/ShowPlayer.kt @@ -13,6 +13,12 @@ import baaahs.show.live.ShowOpener interface ShowPlayer { val toolchain: Toolchain + + /** + * This is for [baaahs.plugin.CorePlugin.ModelInfoDataSource], but we should probably find + * a better way to get it. Don't add more uses. + */ + @Deprecated("Get it some other way", level = DeprecationLevel.WARNING) val modelInfo: ModelInfo val dataSources: List diff --git a/src/commonMain/kotlin/baaahs/client/ClientStageManager.kt b/src/commonMain/kotlin/baaahs/client/ClientStageManager.kt index 947ac4d04c..c7b2290d53 100644 --- a/src/commonMain/kotlin/baaahs/client/ClientStageManager.kt +++ b/src/commonMain/kotlin/baaahs/client/ClientStageManager.kt @@ -2,16 +2,35 @@ package baaahs.client import baaahs.* import baaahs.gl.Toolchain -import baaahs.model.ModelInfo +import baaahs.model.Model import baaahs.show.DataSource +import baaahs.show.Show +import baaahs.show.live.ActivePatchSet +import baaahs.show.live.OpenShow import kotlinx.serialization.json.JsonElement class ClientStageManager( toolchain: Toolchain, private val pubSub: PubSub.Client, - modelInfo: ModelInfo -) : BaseShowPlayer(toolchain, modelInfo) { + model: Model +) : BaseShowPlayer(toolchain, model) { private val gadgets: MutableMap = mutableMapOf() + private val listeners = mutableListOf() + private var openShow: OpenShow? = null + val activePatchSet: ActivePatchSet + get() = openShow!!.activePatchSet() + + private fun checkForChanges() { + listeners.forEach { it.onPatchSetChanged() } + } + + override fun openShow(show: Show, showState: ShowState?): OpenShow { + return super.openShow(show, showState) + .also { + openShow = it + checkForChanges() + } + } override fun registerGadget(id: String, gadget: T, controlledDataSource: DataSource?) { gadgets[id] = ClientGadget(id, pubSub, gadget) @@ -23,7 +42,15 @@ class ClientStageManager( return gadgets.getBang(id, "gadget").gadget as T } - private class ClientGadget( + fun addListener(listener: Listener) { + listeners.add(listener) + } + + fun removeListener(listener: Listener) { + listeners.remove(listener) + } + + private inner class ClientGadget( id: String, pubSub: PubSub.Client, val gadget: Gadget @@ -41,6 +68,7 @@ class ClientStageManager( gadget.withoutTriggering(gadgetListener) { gadget.state.putAll(json) gadget.changed() + checkForChanges() } } } @@ -48,6 +76,11 @@ class ClientStageManager( // GadgetListener callback. fun onGadgetChange(g: Gadget) { channel.onChange(g.state) + checkForChanges() } } + + interface Listener { + fun onPatchSetChanged() + } } \ No newline at end of file diff --git a/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt b/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt index 2e2706aae8..502e0c03dc 100644 --- a/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt +++ b/src/commonMain/kotlin/baaahs/fixtures/FixtureManager.kt @@ -10,8 +10,9 @@ import baaahs.util.Logger class FixtureManager( private val renderManager: RenderManager, - private val renderTargets: MutableMap = hashMapOf() + initialRenderTargets: Map = emptyMap() ) { + private val renderTargets: MutableMap = initialRenderTargets.toMutableMap() private val frameListeners: MutableList<() -> Unit> = arrayListOf() private val changedFixtures = mutableListOf() private var totalFixtures = 0 @@ -19,6 +20,7 @@ class FixtureManager( private var currentActivePatchSet: ActivePatchSet = ActivePatchSet.Empty private var activePatchSetChanged = false internal var currentRenderPlan: RenderPlan? = null + private set fun addFrameListener(callback: () -> Unit) { frameListeners.add(callback) diff --git a/src/commonMain/kotlin/baaahs/gl/glsl/GlslProgram.kt b/src/commonMain/kotlin/baaahs/gl/glsl/GlslProgram.kt index e9fce1b807..c6c3fb25c2 100644 --- a/src/commonMain/kotlin/baaahs/gl/glsl/GlslProgram.kt +++ b/src/commonMain/kotlin/baaahs/gl/glsl/GlslProgram.kt @@ -7,7 +7,6 @@ import baaahs.gl.patch.LinkedPatch import baaahs.gl.render.RenderTarget import baaahs.glsl.Uniform import baaahs.show.DataSource -import baaahs.show.OutputPortRef import baaahs.show.UpdateMode import baaahs.util.Logger import com.danielgergely.kgl.Kgl @@ -126,8 +125,6 @@ class GlslProgram( companion object { private val logger = Logger("GlslProgram") - val PixelColor = OutputPortRef("sm_result") - val vertexShader = """ precision lowp float; diff --git a/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt b/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt index 8e140678c6..117f9e6c0a 100644 --- a/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt +++ b/src/commonMain/kotlin/baaahs/gl/patch/PatchResolver.kt @@ -56,20 +56,20 @@ abstract class BasePatchResolver( .groupBy { it.fixture.deviceType } .mapValues { (_, renderTargets) -> val patchSetsByKey = mutableMapOf() - val renderTargetsByKey = mutableMapOf>() + val renderTargetsByPatchSetKey = mutableMapOf>() renderTargets.forEach { renderTarget -> val patchSet = activePatchSet.forFixture(renderTarget.fixture) val key = patchSet.joinToString(":") { it.serial.toString(16) } patchSetsByKey[key] = patchSet - renderTargetsByKey.getOrPut(key) { mutableListOf() } + renderTargetsByPatchSetKey.getOrPut(key) { mutableListOf() } .add(renderTarget) } patchSetsByKey.map { (key, patchSet) -> PortDiagram(patchSet) to - renderTargetsByKey[key]!! as List + renderTargetsByPatchSetKey[key]!! as List } } diff --git a/src/commonMain/kotlin/baaahs/show/live/OpenShow.kt b/src/commonMain/kotlin/baaahs/show/live/OpenShow.kt index b990df0710..e629c2919b 100644 --- a/src/commonMain/kotlin/baaahs/show/live/OpenShow.kt +++ b/src/commonMain/kotlin/baaahs/show/live/OpenShow.kt @@ -107,7 +107,7 @@ data class ActivePatchSet( ) { fun createRenderPlan( renderManager: RenderManager, - renderTargets: MutableCollection + renderTargets: Collection ): RenderPlan { val patchResolution = PatchResolver(renderTargets, this, renderManager) return patchResolution.createRenderPlan(allDataSources) { _, dataSource -> diff --git a/src/jsMain/kotlin/baaahs/app/ui/AppContext.kt b/src/jsMain/kotlin/baaahs/app/ui/AppContext.kt index f3a568802f..7691c41f92 100644 --- a/src/jsMain/kotlin/baaahs/app/ui/AppContext.kt +++ b/src/jsMain/kotlin/baaahs/app/ui/AppContext.kt @@ -1,6 +1,6 @@ package baaahs.app.ui -import baaahs.ShowPlayer +import baaahs.client.ClientStageManager import baaahs.client.WebClient import baaahs.gl.Toolchain import baaahs.plugin.Plugins @@ -18,7 +18,7 @@ val appContext = createContext() * No need to include them in React watch lists. */ external interface AppContext { - var showPlayer: ShowPlayer + var showPlayer: ClientStageManager var dragNDrop: ReactBeautifulDragNDrop var webClient: WebClient.Facade var plugins: Plugins diff --git a/src/jsMain/kotlin/baaahs/app/ui/AppIndex.kt b/src/jsMain/kotlin/baaahs/app/ui/AppIndex.kt index 5850c86b96..d596a4e513 100644 --- a/src/jsMain/kotlin/baaahs/app/ui/AppIndex.kt +++ b/src/jsMain/kotlin/baaahs/app/ui/AppIndex.kt @@ -1,10 +1,10 @@ package baaahs.app.ui import baaahs.ShowEditorState -import baaahs.ShowPlayer import baaahs.app.ui.editor.EditableManager import baaahs.app.ui.editor.editableManagerUi import baaahs.app.ui.editor.layoutEditorDialog +import baaahs.client.ClientStageManager import baaahs.client.WebClient import baaahs.gl.withCache import baaahs.io.Fs @@ -362,7 +362,7 @@ external interface AppIndexProps : RProps { var id: String var webClient: WebClient.Facade var undoStack: UndoStack - var stageManager: ShowPlayer + var stageManager: ClientStageManager } fun RBuilder.appIndex(handler: RHandler): ReactElement = diff --git a/src/jsMain/kotlin/baaahs/app/ui/controls/Visualizer.kt b/src/jsMain/kotlin/baaahs/app/ui/controls/Visualizer.kt index 4eda8eee05..0b4657b328 100644 --- a/src/jsMain/kotlin/baaahs/app/ui/controls/Visualizer.kt +++ b/src/jsMain/kotlin/baaahs/app/ui/controls/Visualizer.kt @@ -1,12 +1,12 @@ package baaahs.app.ui.controls import baaahs.app.ui.appContext +import baaahs.app.ui.preview.ClientPreview import baaahs.jsx.useResizeListener import baaahs.show.live.ControlProps import baaahs.show.live.OpenVisualizerControl import baaahs.ui.on import baaahs.ui.xComponent -import baaahs.visualizer.SurfaceGeometry import materialui.components.card.card import materialui.components.paper.enums.PaperStyle import org.w3c.dom.Element @@ -16,24 +16,20 @@ import react.* val Visualizer = xComponent("Visualizer") { props -> val appContext = useContext(appContext) + val clientPreview by state { + ClientPreview(appContext.webClient.model, appContext.showPlayer, appContext.clock) + } + val rootEl = ref() - val visualizer by state { - val model = appContext.webClient.model - baaahs.visualizer.Visualizer(model, appContext.clock) - .also { viz -> - model.allSurfaces.forEach { surface -> - val vizSurface = viz.addSurface(SurfaceGeometry(surface)) - // TODO: Bind this to renderer output. - } - - } - .facade } - visualizer.rotate = props.visualizerControl.rotate + clientPreview.visualizer.rotate = props.visualizerControl.rotate + val visualizer = clientPreview.visualizer onMount { visualizer.container = rootEl.current as HTMLDivElement + withCleanup { visualizer.container = null + clientPreview.detach() } } diff --git a/src/jsMain/kotlin/baaahs/app/ui/preview/ClientPreview.kt b/src/jsMain/kotlin/baaahs/app/ui/preview/ClientPreview.kt new file mode 100644 index 0000000000..bf525438a7 --- /dev/null +++ b/src/jsMain/kotlin/baaahs/app/ui/preview/ClientPreview.kt @@ -0,0 +1,85 @@ +package baaahs.app.ui.preview + +import baaahs.client.ClientStageManager +import baaahs.fixtures.* +import baaahs.geom.Vector3F +import baaahs.gl.GlBase +import baaahs.gl.render.RenderManager +import baaahs.model.Model +import baaahs.util.Clock +import baaahs.visualizer.SurfaceGeometry +import baaahs.visualizer.SwirlyPixelArranger +import baaahs.visualizer.Visualizer +import baaahs.visualizer.VizPixels + +class ClientPreview( + model: Model, + private val stageManager: ClientStageManager, + clock: Clock +) : ClientStageManager.Listener { + private val glContext = GlBase.jsManager.createContext() + private val renderManager = RenderManager(model) { glContext } + private val fixtureManager = FixtureManager(renderManager) + private val realVisualizer = Visualizer(model, clock) + private var patchSetChanged = true + + val visualizer: Visualizer.Facade get() = realVisualizer.facade + + init { + val pixelArranger = SwirlyPixelArranger(0.2f, 3f) + val allFixtures = model.allSurfaces.map { surface -> + val surfaceGeometry = SurfaceGeometry(surface) + val pixelPositions = pixelArranger.arrangePixels(surfaceGeometry) + val vizSurface = realVisualizer.addSurface(surfaceGeometry) + val vizPixels = VizPixels(vizSurface, pixelPositions) + vizSurface.vizPixels = vizPixels + + Fixture( + surface, + pixelPositions.size, + pixelPositions.map { Vector3F(it.x.toFloat(), it.y.toFloat(), it.z.toFloat()) }, + surface.deviceType, + surface.name, + object : Transport { + override val name: String + get() = surface.name + + override fun send(fixture: Fixture, resultViews: List) { + val resultColors = + PixelArrayDevice.getColorResults(resultViews) + for (i in vizPixels.indices) { + vizPixels[i] = resultColors[i] + } + } + } + ) + } + + fixtureManager.fixturesChanged(allFixtures, emptyList()) + + stageManager.addListener(this) + + realVisualizer.addPrerenderListener { + checkForPatchSetChange() + renderManager.draw() + fixtureManager.sendFrame() + } + } + + private fun checkForPatchSetChange() { + if (patchSetChanged) { + patchSetChanged = false + + fixtureManager.activePatchSetChanged(stageManager.activePatchSet) + fixtureManager.maybeUpdateRenderPlans() + } + } + + fun detach() { + stageManager.removeListener(this) + } + + override fun onPatchSetChanged() { + patchSetChanged = true + } +} \ No newline at end of file diff --git a/src/jsMain/kotlin/baaahs/client/WebClient.kt b/src/jsMain/kotlin/baaahs/client/WebClient.kt index b344568885..2b5cdecef5 100644 --- a/src/jsMain/kotlin/baaahs/client/WebClient.kt +++ b/src/jsMain/kotlin/baaahs/client/WebClient.kt @@ -3,7 +3,6 @@ package baaahs.client import baaahs.* import baaahs.app.ui.AppIndex import baaahs.app.ui.AppIndexProps -import baaahs.gl.GlBase import baaahs.gl.RootToolchain import baaahs.gl.Toolchain import baaahs.io.Fs @@ -25,6 +24,7 @@ import kotlinext.js.jsObject import kotlinx.serialization.modules.SerializersModule import react.ReactElement import react.createElement + class WebClient( network: Network, pinkyAddress: Network.Address, @@ -40,7 +40,6 @@ class WebClient( pubSub.addStateChangeListener(pubSubListener) } - private val glslContext = GlBase.jsManager.createContext() private val model = Pluggables.getModel() private var show: Show? = null diff --git a/src/jsMain/kotlin/baaahs/visualizer/Visualizer.kt b/src/jsMain/kotlin/baaahs/visualizer/Visualizer.kt index 473869685c..da1fadc61c 100644 --- a/src/jsMain/kotlin/baaahs/visualizer/Visualizer.kt +++ b/src/jsMain/kotlin/baaahs/visualizer/Visualizer.kt @@ -45,6 +45,7 @@ class Visualizer(model: Model, private val clock: Clock) : JsMapperUi.StatusList } } + private val prerenderListeners = mutableListOf<() -> Unit>() private val frameListeners = mutableListOf() private var controls: OrbitControls? = null @@ -58,15 +59,12 @@ class Visualizer(model: Model, private val clock: Clock) : JsMapperUi.StatusList } private val geom = Geometry() - private var obj: Object3D = Object3D() private val pointMaterial = PointsMaterial().apply { color.set(0xffffff) } private val raycaster = Raycaster() private var mouse: Vector2? = null private val sphere: Mesh<*, *> - private val rendererListeners = mutableListOf<() -> Unit>() - private var vizPanels = mutableListOf() init { @@ -118,6 +116,14 @@ class Visualizer(model: Model, private val clock: Clock) : JsMapperUi.StatusList controls = null } + fun addPrerenderListener(callback: () -> Unit) { + prerenderListeners.add(callback) + } + + fun removePrerenderListener(callback: () -> Unit) { + prerenderListeners.remove(callback) + } + fun addFrameListener(frameListener: FrameListener) { frameListeners.add(frameListener) } @@ -149,23 +155,26 @@ class Visualizer(model: Model, private val clock: Clock) : JsMapperUi.StatusList } private fun startRender() { + pointAtModel() + + stopRendering = false + render() + } + + private fun pointAtModel() { geom.computeBoundingSphere() - this.obj = Points(geom, pointMaterial) - scene.add(obj) + scene.add(Points(geom, pointMaterial)) val target = geom.boundingSphere!!.center.clone() controls?.target = target camera.lookAt(target) - - stopRendering = false - render() } fun render() { if (stopRendering) return - window.setTimeout(fun() { - window.requestAnimationFrame { render() } - }, REFRESH_DELAY) + requestAnimationFrame() + + prerenderListeners.forEach { value -> value.invoke() } mouse?.let { mouseClick -> mouse = null @@ -197,7 +206,12 @@ class Visualizer(model: Model, private val clock: Clock) : JsMapperUi.StatusList facade.framerate.elapsed((clock.now() - startTime).asMillis().toInt()) frameListeners.forEach { f -> f.onFrameReady(scene, camera) } - rendererListeners.forEach { value -> value() } + } + + private fun requestAnimationFrame() { + window.setTimeout(fun() { + window.requestAnimationFrame { render() } + }, DEFAULT_REFRESH_DELAY) } // vector.applyMatrix(object.matrixWorld).project(camera) to get 2d x,y coord @@ -247,11 +261,12 @@ class Visualizer(model: Model, private val clock: Clock) : JsMapperUi.StatusList val framerate = Framerate() + fun onAnimationFrame() = this@Visualizer.render() fun resize() = this@Visualizer.resize() fun onMouseDown(event: MouseEvent) = this@Visualizer.onMouseDown(event) } companion object { - private const val REFRESH_DELAY = 50 // ms + private const val DEFAULT_REFRESH_DELAY = 50 // ms } } From 64cc21725a747c7a88ab7575b20784747748569d Mon Sep 17 00:00:00 2001 From: Christian Williams Date: Fri, 12 Feb 2021 12:28:38 -0800 Subject: [PATCH 7/7] Hook up moving heads to client preview visualizer. --- .../baaahs/app/ui/preview/ClientPreview.kt | 90 +++++++++++++------ 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/src/jsMain/kotlin/baaahs/app/ui/preview/ClientPreview.kt b/src/jsMain/kotlin/baaahs/app/ui/preview/ClientPreview.kt index bf525438a7..f268fed7f7 100644 --- a/src/jsMain/kotlin/baaahs/app/ui/preview/ClientPreview.kt +++ b/src/jsMain/kotlin/baaahs/app/ui/preview/ClientPreview.kt @@ -6,11 +6,14 @@ import baaahs.geom.Vector3F import baaahs.gl.GlBase import baaahs.gl.render.RenderManager import baaahs.model.Model +import baaahs.model.MovingHead +import baaahs.sim.FakeDmxUniverse import baaahs.util.Clock import baaahs.visualizer.SurfaceGeometry import baaahs.visualizer.SwirlyPixelArranger import baaahs.visualizer.Visualizer import baaahs.visualizer.VizPixels +import three.js.Vector3 class ClientPreview( model: Model, @@ -20,49 +23,56 @@ class ClientPreview( private val glContext = GlBase.jsManager.createContext() private val renderManager = RenderManager(model) { glContext } private val fixtureManager = FixtureManager(renderManager) - private val realVisualizer = Visualizer(model, clock) + private val theVisualizer = Visualizer(model, clock) private var patchSetChanged = true - val visualizer: Visualizer.Facade get() = realVisualizer.facade + val visualizer: Visualizer.Facade get() = theVisualizer.facade init { val pixelArranger = SwirlyPixelArranger(0.2f, 3f) - val allFixtures = model.allSurfaces.map { surface -> - val surfaceGeometry = SurfaceGeometry(surface) - val pixelPositions = pixelArranger.arrangePixels(surfaceGeometry) - val vizSurface = realVisualizer.addSurface(surfaceGeometry) - val vizPixels = VizPixels(vizSurface, pixelPositions) - vizSurface.vizPixels = vizPixels - - Fixture( - surface, - pixelPositions.size, - pixelPositions.map { Vector3F(it.x.toFloat(), it.y.toFloat(), it.z.toFloat()) }, - surface.deviceType, - surface.name, - object : Transport { - override val name: String - get() = surface.name - - override fun send(fixture: Fixture, resultViews: List) { - val resultColors = - PixelArrayDevice.getColorResults(resultViews) - for (i in vizPixels.indices) { - vizPixels[i] = resultColors[i] + val dmxUniverse = FakeDmxUniverse() + + val allFixtures = model.allEntities.map { entity -> + when (entity) { + is Model.Surface -> { + val surfaceGeometry = SurfaceGeometry(entity) + // TODO: it'd be nice if actual pixel locations were used. For now we make them up. + val pixelPositions = pixelArranger.arrangePixels(surfaceGeometry) + val vizSurface = theVisualizer.addSurface(surfaceGeometry) + val vizPixels = VizPixels(vizSurface, pixelPositions) + vizSurface.vizPixels = vizPixels + + createFixture(entity, pixelPositions, PixelArrayPreviewTransport(entity, vizPixels)) + } + is MovingHead -> { + theVisualizer.addMovingHead(entity, dmxUniverse) + val movingHeadBuffer = entity.newBuffer(dmxUniverse) + createFixture(entity, emptyArray(), object : Transport { + override val name: String + get() = entity.name + + override fun send(fixture: Fixture, resultViews: List) { + val params = MovingHeadDevice.getResults(resultViews)[0] + movingHeadBuffer.pan = params.pan + movingHeadBuffer.tilt = params.tilt + movingHeadBuffer.colorWheelPosition = params.colorWheel + movingHeadBuffer.dimmer = params.dimmer } - } + }) } - ) + else -> error("Unknown model entity type $entity") + } } fixtureManager.fixturesChanged(allFixtures, emptyList()) stageManager.addListener(this) - realVisualizer.addPrerenderListener { + theVisualizer.addPrerenderListener { checkForPatchSetChange() renderManager.draw() fixtureManager.sendFrame() + dmxUniverse.sendFrame() } } @@ -82,4 +92,30 @@ class ClientPreview( override fun onPatchSetChanged() { patchSetChanged = true } + + private fun createFixture(entity: Model.Entity, pixelPositions: Array, transport: Transport) = + Fixture( + entity, + maxOf(1, pixelPositions.size), // One pixel, even if no pixels, e.g. for moving heads. + pixelPositions.map { Vector3F(it.x.toFloat(), it.y.toFloat(), it.z.toFloat()) }, + entity.deviceType, + entity.name, + transport + ) + + class PixelArrayPreviewTransport( + private val surface: Model.Surface, + private val vizPixels: VizPixels + ) : Transport { + override val name: String + get() = surface.name + + override fun send(fixture: Fixture, resultViews: List) { + val resultColors = + PixelArrayDevice.getColorResults(resultViews) + for (i in vizPixels.indices) { + vizPixels[i] = resultColors[i] + } + } + } } \ No newline at end of file