diff --git a/src/main/kotlin/solve/parsers/factories/PlaneFactory.kt b/src/main/kotlin/solve/parsers/factories/PlaneFactory.kt index 30402d67c..1ab1d8819 100644 --- a/src/main/kotlin/solve/parsers/factories/PlaneFactory.kt +++ b/src/main/kotlin/solve/parsers/factories/PlaneFactory.kt @@ -14,7 +14,6 @@ object PlaneFactory : LandmarkFactory { const val ColorBitsNumber = 8 const val BackgroundColor = 0 - private fun convertSeparateToWholeRGB(r: UByte, g: UByte, b: UByte): Int = - (r.toInt() shl ColorBitsNumber * 2) + (g.toInt() shl ColorBitsNumber) + b.toInt() - - private fun getPlanePixelColor( - pixelIndex: Int, - imageByteDataArray: ByteArray, - segmentsType: ColorSegmentsType - ): Int { - val segmentsByteOffset = segmentsType.segmentsByteOffset - - return convertSeparateToWholeRGB( - imageByteDataArray[pixelIndex + segmentsByteOffset + 2].toUByte(), - imageByteDataArray[pixelIndex + segmentsByteOffset + 1].toUByte(), - imageByteDataArray[pixelIndex + segmentsByteOffset].toUByte() - ) - } - - private fun getImageByteDataArray(image: BufferedImage) = (image.data.dataBuffer as DataBufferByte).data - - private inline fun BufferedImage.forEachPixelColor(action: (index: Int, color: Int) -> Unit) { - val imageByteDataArray = getImageByteDataArray(this) - val colorSegmentsType: ColorSegmentsType = - ColorSegmentsType.getColorSegmentsType(colorModel.numComponents) ?: return - val colorComponentsNumber = colorSegmentsType.colorComponentsNumber - - for (i in imageByteDataArray.indices step colorComponentsNumber) { - val pixelColor = getPlanePixelColor(i, imageByteDataArray, colorSegmentsType) - action(i / colorComponentsNumber, pixelColor) - } - } - override fun parse(filePath: String): List { val bufferedImage = loadBufferedImage(filePath) ?: return emptyList() @@ -75,7 +44,7 @@ object ImagePlanesParser : Parser { } } - return planePoints.keys.map { Plane(it, planePoints[it] ?: emptyList()) } + return planePoints.keys.map { Plane(it) } } override fun extractUIDs(filePath: String): List { @@ -92,4 +61,51 @@ object ImagePlanesParser : Parser { return uids.toList() } + + fun getPixelColor(filePath: String, pixelPosition: Point) : Int? { + val bufferedImage = loadBufferedImage(filePath) ?: return null + + return bufferedImage.getPixelColor(pixelPosition) + } + + private fun convertSeparateToWholeRGB(r: UByte, g: UByte, b: UByte): Int = + (r.toInt() shl ColorBitsNumber * 2) + (g.toInt() shl ColorBitsNumber) + b.toInt() + + private fun getImageByteDataPixelColor( + pixelIndex: Int, + imageByteDataArray: ByteArray, + segmentsType: ColorSegmentsType + ): Int { + val segmentsByteOffset = segmentsType.segmentsByteOffset + + return convertSeparateToWholeRGB( + imageByteDataArray[pixelIndex + segmentsByteOffset + 2].toUByte(), + imageByteDataArray[pixelIndex + segmentsByteOffset + 1].toUByte(), + imageByteDataArray[pixelIndex + segmentsByteOffset].toUByte() + ) + } + + private fun getImageByteDataArray(image: BufferedImage) = (image.data.dataBuffer as DataBufferByte).data + + private inline fun BufferedImage.forEachPixelColor(action: (index: Int, color: Int) -> Unit) { + val imageByteDataArray = getImageByteDataArray(this) + val colorSegmentsType: ColorSegmentsType = + ColorSegmentsType.getColorSegmentsType(colorModel.numComponents) ?: return + val colorComponentsNumber = colorSegmentsType.colorComponentsNumber + + for (i in imageByteDataArray.indices step colorComponentsNumber) { + val pixelColor = getImageByteDataPixelColor(i, imageByteDataArray, colorSegmentsType) + action(i / colorComponentsNumber, pixelColor) + } + } + + private fun BufferedImage.getPixelColor(pixelPosition: Point) : Int? { + val imageByteDataArray = getImageByteDataArray(this) + val colorSegmentsType: ColorSegmentsType = + ColorSegmentsType.getColorSegmentsType(colorModel.numComponents) ?: return null + val colorComponentsNumber = colorSegmentsType.colorComponentsNumber + val pixelFirstComponentIndex = (pixelPosition.y * this.width + pixelPosition.x) * colorComponentsNumber + + return getImageByteDataPixelColor(pixelFirstComponentIndex, imageByteDataArray, colorSegmentsType) + } } diff --git a/src/main/kotlin/solve/parsers/structures/Plane.kt b/src/main/kotlin/solve/parsers/structures/Plane.kt index 85c21337b..181b5ef5e 100644 --- a/src/main/kotlin/solve/parsers/structures/Plane.kt +++ b/src/main/kotlin/solve/parsers/structures/Plane.kt @@ -1,5 +1,3 @@ package solve.parsers.structures -import solve.scene.model.Point - -data class Plane(val uid: Long, val points: List) +data class Plane(val uid: Long) diff --git a/src/main/kotlin/solve/rendering/canvas/SceneCanvas.kt b/src/main/kotlin/solve/rendering/canvas/SceneCanvas.kt index be1a3b5b2..1216183e3 100644 --- a/src/main/kotlin/solve/rendering/canvas/SceneCanvas.kt +++ b/src/main/kotlin/solve/rendering/canvas/SceneCanvas.kt @@ -6,6 +6,7 @@ import javafx.scene.input.Clipboard import javafx.scene.input.ClipboardContent import org.joml.Vector2f import org.joml.Vector2i +import solve.parsers.planes.ImagePlanesParser import solve.rendering.engine.Window import solve.rendering.engine.core.input.MouseButton import solve.rendering.engine.core.input.MouseInputHandler @@ -21,8 +22,9 @@ import solve.rendering.engine.utils.times import solve.rendering.engine.utils.toFloatVector import solve.rendering.engine.utils.toIntVector import solve.scene.controller.SceneController -import solve.scene.model.Landmark import solve.scene.model.Layer +import solve.scene.model.LayerState +import solve.scene.model.Point import solve.scene.model.Scene import solve.scene.model.VisualizationFrame import solve.scene.view.association.AssociationAdorner @@ -198,7 +200,8 @@ class SceneCanvas : OpenGLCanvas() { } val interactingVisualizationFrame = lastFramesSelection[frameIndex] - var clickedLandmark: Landmark? = null + var clickedLandmarkLayerState: LayerState? = null + var clickedLandmarkUID = 0L interactingVisualizationFrame.layers.forEach { layer -> when (layer) { @@ -215,7 +218,9 @@ class SceneCanvas : OpenGLCanvas() { return@forEach } - clickedLandmark = pointLandmarks[landmarkIndex] + val clickedLandmark = pointLandmarks[landmarkIndex] + clickedLandmarkLayerState = clickedLandmark.layerState + clickedLandmarkUID = clickedLandmark.uid } is Layer.LineLayer -> { val lineLandmarks = layer.getLandmarks() @@ -230,17 +235,33 @@ class SceneCanvas : OpenGLCanvas() { return@forEach } - clickedLandmark = lineLandmarks[landmarkIndex] + val clickedLandmark = lineLandmarks[landmarkIndex] + clickedLandmarkLayerState = clickedLandmark.layerState + clickedLandmarkUID = clickedLandmark.uid + } + is Layer.PlanesLayer -> { + val framePlaneUIDs = ImagePlanesParser.extractUIDs(layer.filePath.toString()) + val clickedPixelIntegerColor = ImagePlanesParser.getPixelColor( + layer.filePath.toString(), + Point(frameInteractionPixel.x.toShort(), frameInteractionPixel.y.toShort()) + )?.toLong() ?: return@forEach + + // The integer pixel color is equal to UID of the corresponding plane. + if (!framePlaneUIDs.contains(clickedPixelIntegerColor)) + return@forEach + + clickedLandmarkLayerState = layer.layerState + clickedLandmarkUID = clickedPixelIntegerColor } - is Layer.PlaneLayer -> {} } } - val selectedLandmark = clickedLandmark ?: return - if (selectedLandmark.layerState.selectedLandmarksUIDs.contains(selectedLandmark.uid)) { - selectedLandmark.layerState.deselectLandmark(selectedLandmark.uid) + val selectedLandmarkLayerState = clickedLandmarkLayerState ?: return + + if (selectedLandmarkLayerState.selectedLandmarksUIDs.contains(clickedLandmarkUID)) { + selectedLandmarkLayerState.deselectLandmark(clickedLandmarkUID) } else { - selectedLandmark.layerState.selectLandmark(selectedLandmark.uid) + selectedLandmarkLayerState.selectLandmark(clickedLandmarkUID) } } @@ -267,7 +288,7 @@ class SceneCanvas : OpenGLCanvas() { val addingRenderer = when (layer) { is Layer.PointLayer -> PointsLayerRenderer(window) { sceneController?.scene } is Layer.LineLayer -> LinesLayerRenderer(window) { sceneController?.scene } - is Layer.PlaneLayer -> PlanesLayerRenderer(window) { sceneController?.scene } + is Layer.PlanesLayer -> PlanesLayerRenderer(window) { sceneController?.scene } } addingRenderer.setNewSceneFrames(scene.frames, framesSize.toFloatVector()) engineScene?.addLandmarkRenderer(addingRenderer, layer) diff --git a/src/main/kotlin/solve/rendering/engine/core/input/MouseInputHandler.kt b/src/main/kotlin/solve/rendering/engine/core/input/MouseInputHandler.kt index eb1110f7e..a842b82c3 100644 --- a/src/main/kotlin/solve/rendering/engine/core/input/MouseInputHandler.kt +++ b/src/main/kotlin/solve/rendering/engine/core/input/MouseInputHandler.kt @@ -1,16 +1,21 @@ package solve.rendering.engine.core.input +import javafx.scene.robot.Robot import org.joml.Vector2i import solve.rendering.engine.utils.minus import solve.rendering.engine.utils.pointToSegmentDistance import solve.rendering.engine.utils.toFloatVector import solve.rendering.engine.utils.toVector2i import solve.scene.model.Landmark +import java.awt.Color +import java.awt.MouseInfo object MouseInputHandler { private const val LineHandledDistanceMultiplier = 4f private const val PointHandledDistanceMultiplier = 1.5f + private const val ColorSegmentMaxValue = 255 + fun indexOfClickedLineLandmark( lineLandmarks: List, clickedPixelCoordinate: Vector2i, @@ -57,4 +62,17 @@ object MouseInputHandler { return minKeypointDistanceIndex } + + fun getClickedPixelColor() : Color { + val pointerLocation = MouseInfo.getPointerInfo().location + val robot = Robot() + + val javaFXColor = robot.getPixelColor(pointerLocation.x.toDouble(), pointerLocation.y.toDouble()) + + return Color( + (javaFXColor.red * ColorSegmentMaxValue).toInt(), + (javaFXColor.green * ColorSegmentMaxValue).toInt(), + (javaFXColor.blue * ColorSegmentMaxValue).toInt() + ) + } } diff --git a/src/main/kotlin/solve/rendering/engine/core/renderers/PlanesLayerRenderer.kt b/src/main/kotlin/solve/rendering/engine/core/renderers/PlanesLayerRenderer.kt index 00d33a67d..3369d92a5 100644 --- a/src/main/kotlin/solve/rendering/engine/core/renderers/PlanesLayerRenderer.kt +++ b/src/main/kotlin/solve/rendering/engine/core/renderers/PlanesLayerRenderer.kt @@ -13,17 +13,21 @@ import solve.rendering.engine.shader.ShaderAttributeType import solve.rendering.engine.shader.ShaderProgram import solve.rendering.engine.shader.ShaderType import solve.rendering.engine.utils.plus -import solve.scene.model.Layer.PlaneLayer +import solve.scene.model.Layer.PlanesLayer +import solve.scene.model.LayerState import solve.scene.model.Scene class PlanesLayerRenderer( window: Window, getScene: () -> Scene? ) : LandmarkLayerRenderer(window, getScene) { - private var visiblePlaneLayers = emptyList() - private var visiblePlaneLayersToTexturesMap = mutableMapOf() + private var visiblePlanesLayers = emptyList() + private var visiblePlanesLayersToTexturesMap = mutableMapOf() private var visiblePlaneLayersTextures = listOf() + private val planeLayersState: LayerState? + get() = layers.filterIsInstance().firstOrNull()?.layerState + override val maxBatchSize = 1000 private var needToInitializePlaneTextures = false @@ -55,43 +59,61 @@ class PlanesLayerRenderer( override fun uploadUniforms(shaderProgram: ShaderProgram) { shaderProgram.uploadMatrix4f(ProjectionUniformName, window.calculateProjectionMatrix()) shaderProgram.uploadIntArray(TexturesUniformName, texturesIndices) + + val layersState = planeLayersState + var interactingPlanesNumber = 0 + if (layersState != null) { + val interactingPlanesUIDs = layersState.selectedLandmarksUIDs.union( + layersState.hoveredLandmarkUIDs + ).distinct() + shaderProgram.uploadIntArray( + InteractingPlanesUIDsUniformName, + interactingPlanesUIDs.map { it.toInt() }.toIntArray() + ) + val planesOpacity = interactingPlanesUIDs.map { + 1f - layersState.getLandmarkHighlightingProgress(it) + }.toFloatArray() + shaderProgram.uploadFloatArray(InteractingPlanesOpacityUniformName, planesOpacity) + interactingPlanesNumber = interactingPlanesUIDs.count() + } + shaderProgram.uploadInt(InteractingPlanesNumberUniformName, interactingPlanesNumber) } override fun delete() { - visiblePlaneLayersToTexturesMap.values.forEach { it.delete() } - visiblePlaneLayersToTexturesMap.clear() + visiblePlanesLayersToTexturesMap.values.forEach { it.delete() } + visiblePlanesLayersToTexturesMap.clear() super.delete() } override fun beforeRender() { super.beforeRender() - visiblePlaneLayers = visibleLayers.filterIsInstance() + visiblePlanesLayers = visibleLayers.filterIsInstance() - val hiddenVisiblePlaneLayers = hiddenVisibleLayersInCurrentFrame.filterIsInstance() - val newVisiblePlaneLayers = newVisibleLayersInCurrentFrame.filterIsInstance() + val hiddenVisiblePlanesLayers = hiddenVisibleLayersInCurrentFrame.filterIsInstance() + val newVisiblePlanesLayers = newVisibleLayersInCurrentFrame.filterIsInstance() - hiddenVisiblePlaneLayers.forEach { - visiblePlaneLayersToTexturesMap[it]?.delete() - visiblePlaneLayersToTexturesMap.remove(it) + hiddenVisiblePlanesLayers.forEach { + visiblePlanesLayersToTexturesMap[it]?.delete() + visiblePlanesLayersToTexturesMap.remove(it) } - newVisiblePlaneLayers.forEach { - visiblePlaneLayersToTexturesMap[it] = Texture2D(it.filePath.toString(), TextureFilterType.PixelPerfect) + newVisiblePlanesLayers.forEach { + visiblePlanesLayersToTexturesMap[it] = Texture2D(it.filePath.toString(), TextureFilterType.PixelPerfect) } - visiblePlaneLayersTextures = visiblePlaneLayersToTexturesMap.keys.sortedBy { - visiblePlaneLayers.indexOf(it) - }.mapNotNull { visiblePlaneLayersToTexturesMap[it] } + visiblePlaneLayersTextures = visiblePlanesLayersToTexturesMap.keys.sortedBy { + visiblePlanesLayers.indexOf(it) + }.mapNotNull { visiblePlanesLayersToTexturesMap[it] } - val firstPlaneLayer = layers.filterIsInstance().firstOrNull() ?: return - renderPriority = getScene()?.indexOf(firstPlaneLayer.settings) ?: return + val firstPlanesLayer = layers.filterIsInstance().firstOrNull() ?: return + renderPriority = getScene()?.indexOf(firstPlanesLayer.settings) ?: return } override fun updateBatchesData() { - val firstLayer = visiblePlaneLayers.firstOrNull() ?: return + val firstLayer = visiblePlanesLayers.firstOrNull() ?: return if (!firstLayer.settings.enabled) { return } - visiblePlaneLayers.forEachIndexed { visibleLayerIndex, _ -> + visiblePlanesLayers.forEachIndexed { visibleLayerIndex, _ -> val selectionLayerIndex = visibleLayersSelectionIndices[visibleLayerIndex] val planeLayerTexture = visiblePlaneLayersTextures[visibleLayerIndex] val batch = getAvailableBatch(planeLayerTexture, 0) @@ -108,6 +130,9 @@ class PlanesLayerRenderer( } companion object { private const val TexturesUniformName = "uTextures" + private const val InteractingPlanesUIDsUniformName = "uInteractingPlanesUIDs" + private const val InteractingPlanesOpacityUniformName = "uInteractingPlanesOpacity" + private const val InteractingPlanesNumberUniformName = "uInteractingPlanesNumber" private val texturesIndices = intArrayOf(0, 1, 2, 3, 4, 5, 6, 7) diff --git a/src/main/kotlin/solve/scene/SceneFacade.kt b/src/main/kotlin/solve/scene/SceneFacade.kt index 5d2faaa1b..fe52fcace 100644 --- a/src/main/kotlin/solve/scene/SceneFacade.kt +++ b/src/main/kotlin/solve/scene/SceneFacade.kt @@ -2,6 +2,7 @@ package solve.scene import javafx.beans.property.SimpleObjectProperty import solve.parsers.factories.LineFactory +import solve.parsers.factories.PlaneFactory import solve.parsers.factories.PointFactory import solve.parsers.lines.CSVLinesParser import solve.parsers.points.CSVPointsParser @@ -11,6 +12,7 @@ import solve.project.model.ProjectFrame import solve.project.model.ProjectLayer import solve.scene.controller.SceneController import solve.scene.model.ColorManager +import solve.scene.model.Landmark import solve.scene.model.Layer import solve.scene.model.LayerSettings import solve.scene.model.LayerState @@ -57,7 +59,7 @@ object SceneFacade { val layerStates = layers.map { projectLayer -> LayerState(projectLayer.name) } val visualizationFrames = frames.map { projectFrame -> projectFrame.toVisualizationFrame(layerStates) } - val scene = Scene(visualizationFrames, layersSettings) + val scene = Scene(visualizationFrames, layersSettings, layerStates) controller.setScene(scene, keepSettings) } @@ -100,10 +102,11 @@ object SceneFacade { } } - LayerKind.Plane -> Layer.PlaneLayer( + LayerKind.Plane -> Layer.PlanesLayer( file.projectLayer.name, layerSettings as LayerSettings.PlaneLayerSettings, - file.path + file.path, + layerState ) } } diff --git a/src/main/kotlin/solve/scene/controller/SceneController.kt b/src/main/kotlin/solve/scene/controller/SceneController.kt index 5debcc4a5..7c6970d1c 100644 --- a/src/main/kotlin/solve/scene/controller/SceneController.kt +++ b/src/main/kotlin/solve/scene/controller/SceneController.kt @@ -32,7 +32,7 @@ class SceneController : Controller() { /** * Scene data object. */ - val sceneProperty = SimpleObjectProperty(Scene(emptyList(), emptyList())) + val sceneProperty = SimpleObjectProperty(Scene(emptyList(), emptyList(), emptyList())) val scene: Scene get() = sceneProperty.value diff --git a/src/main/kotlin/solve/scene/model/Landmark.kt b/src/main/kotlin/solve/scene/model/Landmark.kt index 003a23868..5604537c9 100644 --- a/src/main/kotlin/solve/scene/model/Landmark.kt +++ b/src/main/kotlin/solve/scene/model/Landmark.kt @@ -25,7 +25,6 @@ sealed class Landmark(val uid: Long, open val layerSettings: LayerSettings, val class Plane( uid: Long, override val layerSettings: LayerSettings.PlaneLayerSettings, - layerState: LayerState, - val points: List + layerState: LayerState ) : Landmark(uid, layerSettings, layerState) } diff --git a/src/main/kotlin/solve/scene/model/Layer.kt b/src/main/kotlin/solve/scene/model/Layer.kt index 9371f3170..df14f673f 100644 --- a/src/main/kotlin/solve/scene/model/Layer.kt +++ b/src/main/kotlin/solve/scene/model/Layer.kt @@ -26,10 +26,11 @@ sealed class Layer( override val getLandmarks: () -> List ) : Layer(name, settings, getLandmarks) - class PlaneLayer( + // A PlanesLayer contains all frame frames. + class PlanesLayer( name: String, override val settings: LayerSettings.PlaneLayerSettings, val filePath: Path, - override val getLandmarks: () -> List = { emptyList() } - ) : Layer(name, settings, getLandmarks) + val layerState: LayerState + ) : Layer(name, settings, { emptyList() }) } diff --git a/src/main/kotlin/solve/scene/model/LayerState.kt b/src/main/kotlin/solve/scene/model/LayerState.kt index b7c87617d..a5f6d47fb 100644 --- a/src/main/kotlin/solve/scene/model/LayerState.kt +++ b/src/main/kotlin/solve/scene/model/LayerState.kt @@ -35,8 +35,9 @@ class LayerState(val name: String) { return } - _selectedLandmarksUIDs.remove(landmarkUID) - unhighlightLandmark(landmarkUID) + unhighlightLandmark(landmarkUID, onFinished = { + _selectedLandmarksUIDs.remove(landmarkUID) + }) } fun hoverLandmark(landmarkUID: Long) { @@ -50,30 +51,40 @@ class LayerState(val name: String) { return } - _hoveredLandmarksUIDs.remove(landmarkUID) - unhighlightLandmark(landmarkUID) + unhighlightLandmark(landmarkUID, onFinished = { + _hoveredLandmarksUIDs.remove(landmarkUID) + println("unhovered") + }) } fun getLandmarkHighlightingProgress(landmarkUID: Long): Float { return landmarksHighlightingProgress[landmarkUID]?.highlightingProperty?.value ?: 0f } - private fun highlightLandmark(landmarkUID: Long) { + private fun highlightLandmark(landmarkUID: Long, onFinished: () -> Unit = { }) { if (!landmarksHighlightingProgress.contains(landmarkUID)) { val highlightingProperty = floatProperty(0f) landmarksHighlightingProgress[landmarkUID] = LandmarkHighlightingData(highlightingProperty, PropertyTranslator(highlightingProperty)) } - landmarksHighlightingProgress[landmarkUID]?.propertyTranslator?.translateTo(1f, HighlightDurationMillis) + landmarksHighlightingProgress[landmarkUID]?.propertyTranslator?.translateTo( + 1f, + HighlightDurationMillis, + onFinished + ) } - private fun unhighlightLandmark(landmarkUID: Long) { + private fun unhighlightLandmark(landmarkUID: Long, onFinished: () -> Unit = { }) { if (!landmarksHighlightingProgress.contains(landmarkUID)) { return } - landmarksHighlightingProgress[landmarkUID]?.propertyTranslator?.translateTo(0f, HighlightDurationMillis) + landmarksHighlightingProgress[landmarkUID]?.propertyTranslator?.translateTo( + 0f, + HighlightDurationMillis, + onFinished + ) } companion object { diff --git a/src/main/kotlin/solve/scene/model/Scene.kt b/src/main/kotlin/solve/scene/model/Scene.kt index d230e6871..535698746 100644 --- a/src/main/kotlin/solve/scene/model/Scene.kt +++ b/src/main/kotlin/solve/scene/model/Scene.kt @@ -12,7 +12,8 @@ import solve.utils.structures.Size as DoubleSize */ class Scene( val frames: List, - val layerSettings: List + val layerSettings: List, + val layerStates: List ) : OrderManager { /** * Size of images in the project. Assumes that all images have the same size, but there is no guarantee. diff --git a/src/main/kotlin/solve/scene/view/association/AssociationManager.kt b/src/main/kotlin/solve/scene/view/association/AssociationManager.kt index 4f4f9e1b1..167bb278d 100644 --- a/src/main/kotlin/solve/scene/view/association/AssociationManager.kt +++ b/src/main/kotlin/solve/scene/view/association/AssociationManager.kt @@ -28,7 +28,6 @@ class AssociationManager { if (associatingKeypointLayerIndex !in firstFrameKeypointLayers.indices || associatingKeypointLayerIndex !in secondFramePointLayers.indices ) { - println("There are no point layer with a given index to associate!") return } diff --git a/src/main/kotlin/solve/utils/PropertyTranslator.kt b/src/main/kotlin/solve/utils/PropertyTranslator.kt index 9181fc32d..2ce370fe2 100644 --- a/src/main/kotlin/solve/utils/PropertyTranslator.kt +++ b/src/main/kotlin/solve/utils/PropertyTranslator.kt @@ -9,12 +9,18 @@ import javafx.util.Duration class PropertyTranslator(private val property: WritableValue) { var activeTimeline: Timeline? = null - fun translateTo(value: T, durationMillis: Long) { - activeTimeline?.stop() + fun translateTo(value: T, durationMillis: Long, onFinished: () -> Unit = { }) { + val currentActiveTimeline = activeTimeline + if (currentActiveTimeline != null) { + activeTimeline?.stop() + } val timeline = Timeline() timeline.keyFrames.add(KeyFrame(Duration.millis(durationMillis.toDouble()), KeyValue(property, value))) timeline.play() - timeline.setOnFinished { onActiveTimelineFinished() } + timeline.setOnFinished { + onActiveTimelineFinished() + onFinished() + } activeTimeline = timeline } diff --git a/src/main/resources/engine/shaders/landmark/plane/plane.frag b/src/main/resources/engine/shaders/landmark/plane/plane.frag index 723d3fbe3..54768e64a 100644 --- a/src/main/resources/engine/shaders/landmark/plane/plane.frag +++ b/src/main/resources/engine/shaders/landmark/plane/plane.frag @@ -1,12 +1,26 @@ #version 330 core +#define PLANES_MAX_NUMBER 128 + in vec2 fTexCoords; in float fTexId; uniform sampler2D uTextures[8]; +uniform int uInteractingPlanesUIDs[PLANES_MAX_NUMBER]; +uniform float uInteractingPlanesOpacity[PLANES_MAX_NUMBER]; +uniform int uInteractingPlanesNumber; + out vec4 color; +int getIntegerColor(vec3 rgbColor) { + int red = int(rgbColor.x * 255.0); + int green = int(rgbColor.y * 255.0); + int blue = int(rgbColor.z * 255.0); + + return red * 256 * 256 + green * 256 + blue; +} + void main() { if (fTexId > 0) { @@ -38,6 +52,18 @@ void main() break; } + int intColor = getIntegerColor(color.xyz); + for (int i = 0; i < uInteractingPlanesNumber; ++i) { + if (intColor == uInteractingPlanesUIDs[i]) { + float alpha = uInteractingPlanesOpacity[i]; + color.w = uInteractingPlanesOpacity[i]; + color.x *= uInteractingPlanesOpacity[i]; + color.y *= uInteractingPlanesOpacity[i]; + color.z *= uInteractingPlanesOpacity[i]; + break; + } + } + if (color == vec4(0, 0, 0, 1)) { discard; }