From 9cc66fe3bf22ea0fe5ddf4a4fc398329fd3834ff Mon Sep 17 00:00:00 2001 From: Rahel Arnold Date: Mon, 8 Jul 2024 19:24:16 +0200 Subject: [PATCH 01/40] init rendering work from @net-cscience-raphael (see Cineast) --- .../content/element/PointCloudContent.kt | 21 ++ .../content/factory/CachedContentFactory.kt | 5 + .../vitrivr/engine/core/model/mesh/Texture.kt | 2 +- .../org/vitrivr/engine/model3d/data/Pair.java | 36 ++ .../model3d/data/render/lwjgl/Renderer.java | 2 + .../data/render/lwjgl/engine/Engine.java | 209 +++++++++++ .../data/render/lwjgl/engine/EngineLogic.java | 61 ++++ .../data/render/lwjgl/glmodel/GLMaterial.java | 87 +++++ .../data/render/lwjgl/glmodel/GLMesh.java | 127 +++++++ .../data/render/lwjgl/glmodel/GLModel.java | 86 +++++ .../data/render/lwjgl/glmodel/GLScene.java | 156 +++++++++ .../data/render/lwjgl/glmodel/GLTexture.java | 95 +++++ .../render/lwjgl/glmodel/GLTextureCache.java | 56 +++ .../data/render/lwjgl/glmodel/IGLModel.java | 47 +++ .../data/render/lwjgl/render/Render.java | 66 ++++ .../render/lwjgl/render/RenderOptions.java | 35 ++ .../data/render/lwjgl/render/SceneRender.java | 148 ++++++++ .../render/lwjgl/render/ShaderProgram.java | 149 ++++++++ .../data/render/lwjgl/render/UniformsMap.java | 107 ++++++ .../renderer/LWJGLOffscreenRenderer.java | 296 ++++++++++++++++ .../render/lwjgl/renderer/RenderActions.java | 14 + .../render/lwjgl/renderer/RenderData.java | 16 + .../data/render/lwjgl/renderer/RenderJob.java | 147 ++++++++ .../render/lwjgl/renderer/RenderStates.java | 18 + .../render/lwjgl/renderer/RenderWorker.java | 249 +++++++++++++ .../data/render/lwjgl/scene/Camera.java | 292 +++++++++++++++ .../data/render/lwjgl/scene/Direction.java | 13 + .../render/lwjgl/scene/LightfieldCamera.java | 88 +++++ .../data/render/lwjgl/scene/Projection.java | 59 ++++ .../data/render/lwjgl/scene/Scene.java | 92 +++++ .../render/lwjgl/util/datatype/Variant.java | 75 ++++ .../lwjgl/util/datatype/VariantException.java | 27 ++ .../lwjgl/util/fsm/abstractworker/Job.java | 188 ++++++++++ .../fsm/abstractworker/JobControlCommand.java | 23 ++ .../util/fsm/abstractworker/JobType.java | 29 ++ .../util/fsm/abstractworker/StateEnter.java | 21 ++ .../util/fsm/abstractworker/StateLeave.java | 21 ++ .../fsm/abstractworker/StateProvider.java | 16 + .../StateProviderAnnotationParser.java | 178 ++++++++++ .../StateProviderException.java | 25 ++ .../fsm/abstractworker/StateTransition.java | 23 ++ .../lwjgl/util/fsm/abstractworker/Worker.java | 217 ++++++++++++ .../fsm/controller/FiniteStateMachine.java | 78 +++++ .../FiniteStateMachineException.java | 25 ++ .../render/lwjgl/util/fsm/model/Action.java | 96 +++++ .../render/lwjgl/util/fsm/model/Graph.java | 141 ++++++++ .../render/lwjgl/util/fsm/model/State.java | 60 ++++ .../lwjgl/util/fsm/model/Transition.java | 79 +++++ .../data/render/lwjgl/window/Window.java | 210 +++++++++++ .../render/lwjgl/window/WindowOptions.java | 48 +++ .../model3d/data/texturemodel/Entity.java | 186 ++++++++++ .../model3d/data/texturemodel/IModel.java | 38 ++ .../model3d/data/texturemodel/Material.java | 145 ++++++++ .../model3d/data/texturemodel/Mesh.java | 220 ++++++++++++ .../model3d/data/texturemodel/Model.java | 126 +++++++ .../data/texturemodel/ModelLoader.java | 331 ++++++++++++++++++ .../model3d/data/texturemodel/Texture.java | 57 +++ .../texturemodel/util/MinimalBoundingBox.java | 235 +++++++++++++ .../engine/model3d/data}/util/MeshMathUtil.kt | 2 +- .../model3d/data/util/math/MathConstants.java | 58 +++ .../EntopyCalculationMethod.java | 20 ++ .../EntropyOptimizerStrategy.java | 15 + .../ModelEntropyOptimizer.java | 284 +++++++++++++++ .../entropyoptimizer/OptimizerOptions.java | 39 +++ .../viewpoint/ViewpointHelper.java | 129 +++++++ .../viewpoint/ViewpointStrategy.java | 43 +++ .../model3d/decoder/PointCloudDecoder.kt | 71 ++++ .../engine/model3d/model/voxel/Voxelizer.kt | 2 +- .../renderer/lwjgl/models/default/default.png | Bin 0 -> 68 bytes .../lwjgl/models/unit-cube/Cube_Text.bin | Bin 0 -> 1128 bytes .../lwjgl/models/unit-cube/Cube_Text.gltf | 143 ++++++++ .../renderer/lwjgl/models/unit-cube/cube.png | Bin 0 -> 1195086 bytes .../renderer/lwjgl/shaders/scene.frag | 18 + .../renderer/lwjgl/shaders/scene.vert | 16 + 74 files changed, 6534 insertions(+), 3 deletions(-) create mode 100644 vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/content/element/PointCloudContent.kt create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/Pair.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/Renderer.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/engine/Engine.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/engine/EngineLogic.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLMaterial.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLMesh.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLModel.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLScene.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLTexture.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLTextureCache.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/IGLModel.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/Render.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/RenderOptions.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/SceneRender.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/ShaderProgram.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/UniformsMap.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/LWJGLOffscreenRenderer.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderActions.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderData.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderJob.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderStates.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderWorker.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/scene/Camera.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/scene/Direction.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/scene/LightfieldCamera.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/scene/Projection.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/scene/Scene.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/datatype/Variant.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/datatype/VariantException.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/abstractworker/Job.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/abstractworker/JobControlCommand.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/abstractworker/JobType.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/abstractworker/StateEnter.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/abstractworker/StateLeave.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/abstractworker/StateProvider.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/abstractworker/StateProviderAnnotationParser.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/abstractworker/StateProviderException.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/abstractworker/StateTransition.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/abstractworker/Worker.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/controller/FiniteStateMachine.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/controller/FiniteStateMachineException.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/model/Action.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/model/Graph.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/model/State.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/util/fsm/model/Transition.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/window/Window.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/window/WindowOptions.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/texturemodel/Entity.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/texturemodel/IModel.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/texturemodel/Material.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/texturemodel/Mesh.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/texturemodel/Model.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/texturemodel/ModelLoader.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/texturemodel/Texture.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/texturemodel/util/MinimalBoundingBox.java rename vitrivr-engine-module-m3d/src/main/{kotlin/org/vitrivr/engine/model3d => java/org/vitrivr/engine/model3d/data}/util/MeshMathUtil.kt (99%) create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/util/math/MathConstants.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/util/texturemodel/entropyoptimizer/EntopyCalculationMethod.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/util/texturemodel/entropyoptimizer/EntropyOptimizerStrategy.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/util/texturemodel/entropyoptimizer/ModelEntropyOptimizer.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/util/texturemodel/entropyoptimizer/OptimizerOptions.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/util/texturemodel/viewpoint/ViewpointHelper.java create mode 100644 vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/util/texturemodel/viewpoint/ViewpointStrategy.java create mode 100644 vitrivr-engine-module-m3d/src/main/kotlin/org/vitrivr/engine/model3d/decoder/PointCloudDecoder.kt create mode 100644 vitrivr-engine-module-m3d/src/main/resources/renderer/lwjgl/models/default/default.png create mode 100644 vitrivr-engine-module-m3d/src/main/resources/renderer/lwjgl/models/unit-cube/Cube_Text.bin create mode 100644 vitrivr-engine-module-m3d/src/main/resources/renderer/lwjgl/models/unit-cube/Cube_Text.gltf create mode 100644 vitrivr-engine-module-m3d/src/main/resources/renderer/lwjgl/models/unit-cube/cube.png create mode 100644 vitrivr-engine-module-m3d/src/main/resources/renderer/lwjgl/shaders/scene.frag create mode 100644 vitrivr-engine-module-m3d/src/main/resources/renderer/lwjgl/shaders/scene.vert diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/content/element/PointCloudContent.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/content/element/PointCloudContent.kt new file mode 100644 index 000000000..2c7375c5f --- /dev/null +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/content/element/PointCloudContent.kt @@ -0,0 +1,21 @@ +package org.vitrivr.engine.core.model.content.element + +import org.vitrivr.engine.core.model.content.ContentType +import org.vitrivr.engine.core.model.mesh.Model3D +import java.awt.image.BufferedImage + +/** + * A 3D [ContentElement]. + * + * @author Rahel Arnold + * @version 1.0.0 + */ +interface PointCloudContent: ContentElement{ + /** The [ContentType] of a [PointCloudContent] is always [ContentType.POINT_CLOUD]. */ + + val id: String + get() = this.content.id + + override val type: ContentType + get() = ContentType.POINT_CLOUD +} \ No newline at end of file diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/content/factory/CachedContentFactory.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/content/factory/CachedContentFactory.kt index 3dac1e44e..1885cbf05 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/content/factory/CachedContentFactory.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/content/factory/CachedContentFactory.kt @@ -122,6 +122,11 @@ class CachedContentFactory : ContentFactoriesFactory { TODO() } + override fun newPointCloudContent(pointCloud: Model3D): PointCloudContent { + check(!this.closed) { "CachedContentFactory has been closed." } + TODO() + } + /** * A [PhantomReference] implementation that allows for the puring of [CachedContent] from the cache. diff --git a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/mesh/Texture.kt b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/mesh/Texture.kt index ef719a814..ecc51b48f 100644 --- a/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/mesh/Texture.kt +++ b/vitrivr-engine-core/src/main/kotlin/org/vitrivr/engine/core/model/mesh/Texture.kt @@ -15,7 +15,7 @@ import java.nio.file.Path data class Texture(val path: Path = DEFAULT_TEXTURE) { companion object { /** Default texture path. Points to a png with one white pixel with 100% opacity. */ - val DEFAULT_TEXTURE: Path = Path.of("./resources/renderer/lwjgl/models/default/default.png") + val DEFAULT_TEXTURE: Path = Path.of("vitrivr-engine-module-m3d/src/main/resources/renderer/lwjgl/models/default/default.png") } /** diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/Pair.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/Pair.java new file mode 100644 index 000000000..5e36c8bd8 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/Pair.java @@ -0,0 +1,36 @@ +package org.vitrivr.engine.model3d.data; + +import java.util.Objects; + +public class Pair { + + public K first; + public V second; + + public Pair(K first, V second) { + this.first = first; + this.second = second; + } + + @Override + public String toString() { + return "Pair(" + this.first + ", " + this.second + ")"; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Pair pair = (Pair) o; + return Objects.equals(first, pair.first) && Objects.equals(second, pair.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/Renderer.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/Renderer.java new file mode 100644 index 000000000..cb983f5a9 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/Renderer.java @@ -0,0 +1,2 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl;public class Renderer { +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/engine/Engine.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/engine/Engine.java new file mode 100644 index 000000000..e64a27921 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/engine/Engine.java @@ -0,0 +1,209 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.engine; + +import org.vitrivr.engine.model3d.data.render.lwjgl.scene.Camera; +import org.vitrivr.engine.model3d.data.render.lwjgl.scene.Scene; +import org.vitrivr.engine.model3d.data.render.lwjgl.render.Render; +import org.vitrivr.engine.model3d.data.render.lwjgl.render.RenderOptions; +import org.vitrivr.engine.model3d.data.render.lwjgl.glmodel.GLScene; +import org.vitrivr.engine.model3d.data.render.lwjgl.window.Window; +import org.vitrivr.engine.model3d.data.render.lwjgl.window.WindowOptions; + +/** + * The engine is the main class of the rendering engine. + * It holds the window, the scene and the render object. + * It provides a render loop for continuous rendering and a runOnce method to render a single frame rendering. + */ +public class Engine { + /** + * The window object. + */ + private final Window window; + /** + * Indicates whether the engine is running in continuous rendering mode. + */ + private boolean running; + /** + * The render object. + */ + private final Render render; + /** + * The scene object. + */ + private final GLScene scene; + /** + * The application logic. Connects the engine to the overlaying application. + * The Engine calls the methods of the appLogic object depending on the engine state. + */ + private final EngineLogic appLogic; + /** + * The target frames per second. + */ + private final int targetFps; + /** + * The target updates per second. (e.g inputs rotation,...) + */ + private final int targetUps; + + /** + * Creates a new engine + * @param windowTitle The title of the window. + * @param opts The window options. + * @param appLogic The application logic. + */ + public Engine(String windowTitle, WindowOptions opts, EngineLogic appLogic) { + this.window = new Window(windowTitle, opts, () -> { + this.resize(); + return null; + }); + this.targetFps = opts.fps; + this.targetUps = opts.ups; + this.appLogic = appLogic; + this.render = new Render(); + this.scene = new GLScene( new Scene(this.window.getWidth(), this.window.getHeight())); + this.appLogic.init(this.window, this.scene, this.render); + this.running = true; + } + + /** + * Sets the render options. + * Must be called before render is called. + * @param options The render options. + */ + public void setRenderOptions(RenderOptions options){ + this.render.setOptions(options); + } + + +/** + * Refreshes the engine. + * Is called when the engine is stopped and has to be ready to start again. + */ + public void refresh() { + this.appLogic.cleanup(); + this.render.cleanup(); + this.scene.cleanup(); + } + + + /** + * Releases all resources and terminates the engine. + * Is called when the engine is stopped and all resources have to be released. + */ + public void clear() { + // Calls the registered app for cleaning up + this.appLogic.cleanup(); + this.render.cleanup(); + this.scene.cleanup(); + this.window.cleanup(); + } + + /** + * Starts the engine in continuous rendering mode. + */ + public void start() { + this.running = true; + this.run(); + } + + /** + * Stops the continuous rendering mode. + */ + public void stop() { + this.running = false; + } + + /** + * Runs a single frame rendering. + */ + public void runOnce() { + this.window.pollEvents(); + this.appLogic.beforeRender(this.window, this.scene, this.render); + this.render.render(this.window, this.scene); + this.appLogic.afterRender(this.window, this.scene, this.render); + this.window.update(); + } + + /** + * Run mode runs permanently until the engine is stopped. + * 1. Poll events + * 2. Input + * 3. Update + * 4. Render + * 5. Update window + */ + public void run() { + var initialTime = System.currentTimeMillis(); + // maximum elapsed time between updates + var timeU = 1000.0f / this.targetUps; + // maximum elapsed time between render calls + var timeR = this.targetFps > 0 ? 1000.0f / this.targetFps : 0; + var deltaUpdate = 0.0f; + var deltaFps = 0.0f; + + var updateTime = initialTime; + + while (this.running && !this.window.windowShouldClose()) { + this.window.pollEvents(); + + var now = System.currentTimeMillis(); + + // relation between actual and elapsed time. 1 if equal. + deltaUpdate += (now - initialTime) / timeU; + deltaFps += (now - initialTime) / timeR; + + // If passed maximum elapsed time for render, process user input + if (this.targetFps <= 0 || deltaFps >= 1) { + this.appLogic.input(this.window, this.scene, now - initialTime); + } + + // If passed maximum elapsed time for update, update the scene + if (deltaUpdate >= 1) { + var diffTimeMillis = now - updateTime; + this.appLogic.update(this.window, this.scene, diffTimeMillis); + updateTime = now; + deltaUpdate--; + } + + // If passed maximum elapsed time for render, render the scene + if (this.targetFps <= 0 || deltaFps >= 1) { + this.appLogic.beforeRender(this.window, this.scene, this.render); + this.render.render(this.window, this.scene); + deltaFps--; + this.window.update(); + this.appLogic.afterRender(this.window, this.scene, this.render); + } + } + this.refresh(); + } + + /** + * Resizes the window. + */ + public void resize() { + this.scene.resize(this.window.getWidth(), this.window.getHeight()); + } + + /** + * Returns the camera object. + * @return The camera object. + */ + public Camera getCamera() { + return this.scene.getCamera(); + } + + /** + * Returns the window object. + * @return The window object. + */ + public Window getWindow() { + return this.window; + } + + /** + * Returns the scene object. + * @return The scene object. + */ + public GLScene getScene() { + return this.scene; + } +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/engine/EngineLogic.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/engine/EngineLogic.java new file mode 100644 index 000000000..c96b0b070 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/engine/EngineLogic.java @@ -0,0 +1,61 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.engine; + + +import org.vitrivr.engine.model3d.data.render.lwjgl.glmodel.GLScene; +import org.vitrivr.engine.model3d.data.render.lwjgl.render.Render; +import org.vitrivr.engine.model3d.data.render.lwjgl.window.Window; + + +/** + * The EngineLogic provides methods to be called by the engine on certain states. + */ +public abstract class EngineLogic { + + /** + * Is called from the engine as first step during refresh and cleanup + * @implSpec DO NOT CALL ENGINE LOGIC METHODS IN THIS METHOD + * DO NOT CALL THIS METHOD FROM EXTENDING CLASS + */ + protected abstract void cleanup(); + + /** + * Is called once at the initialization of the engine. + * @implSpec DO NOT CALL ENGINE LOGIC METHODS IN THIS METHOD + * DO NOT CALL THIS METHOD FROM EXTENDING CLASS + */ + protected abstract void init(Window window, GLScene scene, Render render); + + /** + * Is called from the engine before the render method. + * @implSpec DO NOT CALL ENGINE LOGIC METHODS IN THIS METHOD + * DO NOT CALL THIS METHOD FROM EXTENDING CLASS + */ + protected abstract void beforeRender(Window window, GLScene scene, Render render); + + /** + * Is called from the engine after the render method. + * @implSpec DO NOT CALL ENGINE LOGIC METHODS IN THIS METHOD + * DO NOT CALL THIS METHOD FROM EXTENDING CLASS + */ + protected abstract void afterRender(Window window, GLScene scene, Render render); + + /** + * This method is called every frame. + * This is only used in continuous rendering. + * The purpose is to do some input handling. + * Could be use for optimize view angles on a fast manner. + * @implSpec DO NOT CALL ENGINE LOGIC METHODS IN THIS METHOD + * DO NOT CALL THIS METHOD FROM EXTENDING CLASS + */ + protected abstract void input(Window window, GLScene scene, long diffTimeMillis); + + /** + * After Engine run This method is called every frame. + * This is only used in continuous rendering. + * The purpose is to process some life output. + * Could be use for optimize view angles on a fast manner. + * @implSpec DO NOT CALL ENGINE LOGIC METHODS IN THIS METHOD + * DO NOT CALL THIS METHOD FROM EXTENDING CLASS + */ + protected abstract void update(Window window, GLScene scene, long diffTimeMillis); +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLMaterial.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLMaterial.java new file mode 100644 index 000000000..768af9ce0 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLMaterial.java @@ -0,0 +1,87 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.glmodel; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.joml.Vector4f; +import org.vitrivr.engine.model3d.data.texturemodel.Material; + + +/** + * The GLMaterial class is a wrapper for the {@link Material} class. + *
    + *
  • Material -> GLMaterial( Material )
  • + *
+ *

+ * The purpose is to bring the generic Material in an OpenGl context + * {@link Material} -> {@link GLMaterial} + */ +public class GLMaterial { + + private static final Logger LOGGER = LogManager.getLogger(); + /** + * The contained meshes in gl context + */ + private final List meshes; + /** + * The contained texture in gl context + */ + private final GLTexture texture; + /** + * The material that is wrapped by this gl material. + */ + private final Material material; + + /** + * Creates a new GLMaterial from a material. + * + * @param material The material that is wrapped by this gl material. + */ + public GLMaterial(Material material) { + this.meshes = new ArrayList<>(); + this.material = material; + this.material.getMeshes().forEach(mesh -> this.meshes.add(new GLMesh(mesh))); + this.texture = new GLTexture(this.material.getTexture()); + } + + /** + * Cleans up the gl material and calls all underlying cleanup methods. + * Removes only the references to wrapped generic meshes and texture. + * Hence, the material could be used by another extraction task this method does not close the generic meshes or texture. + */ + public void cleanup() { + this.meshes.forEach(GLMesh::cleanup); + this.meshes.clear(); + this.texture.cleanup(); + LOGGER.trace("Cleaned-up GLMaterial"); + } + + /** + * Returns the gl meshes of this gl material. + * + * @return The unmodifiable list of gl meshes of this gl material. + */ + public List getMeshes() { + return Collections.unmodifiableList(this.meshes); + } + + /** + * Returns the gl texture of this gl material. + * + * @return The gl texture of this gl material. + */ + public GLTexture getTexture() { + return this.texture; + } + + /** + * Returns the color from wrapped generic material. + * + * @return The color from wrapped generic material. (r,g,b,opacity) + */ + public Vector4f getDiffuseColor() { + return this.material.getDiffuseColor(); + } +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLMesh.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLMesh.java new file mode 100644 index 000000000..a6e369317 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLMesh.java @@ -0,0 +1,127 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.glmodel; + +import java.util.ArrayList; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.lwjgl.opengl.GL30; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.vitrivr.engine.model3d.data.texturemodel.Mesh; + +/** + * The GLMesh class is a wrapper for the {@link Mesh} class. + *

    + *
  • Mesh -> GLMesh( Mesh )
  • + *
+ *

+ * The purpose is to bring the generic Mesh in an OpenGl context + * {@link Mesh} -> {@link GLMesh} + */ +public class GLMesh { + + private static final Logger LOGGER = LogManager.getLogger(); + + /** + * The wrapped generic mesh in gl context + */ + private final Mesh mesh; + /** + * The list of Vertex Buffer Object (VBO) ids + */ + private final List vboIdList; + /** + * The Vertex Array Object (VAO) id + */ + private final int vaoId; + + + /** + * Creates a new GLMesh from a mesh. + *

    + *
  1. Bind Vertex Array Object
  2. + *
  3. Generate, allocate and initialize Vertex (Positions) Buffer
  4. + *
  5. Generate, allocate and initialize Texture Coordinates Buffer
  6. + *
  7. Generate, allocate and initialize Index Buffer
  8. + *
  9. Unbind Vertex Array Object
  10. + *
+ * @param mesh The mesh that is wrapped by this gl mesh. + */ + public GLMesh(Mesh mesh) { + this.mesh = mesh; + this.vboIdList = new ArrayList<>(); + + try (var memoryStack = MemoryStack.stackPush()) { + + this.vaoId = GL30.glGenVertexArrays(); + GL30.glBindVertexArray(this.vaoId); + + // Positions VBO + int vboId = GL30.glGenBuffers(); + this.vboIdList.add(vboId); + var positionsBuffer = memoryStack.callocFloat(this.mesh.getPositions().length); + positionsBuffer.put(0, this.mesh.getPositions()); + GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vboId); + GL30.glBufferData(GL30.GL_ARRAY_BUFFER, positionsBuffer, GL30.GL_STATIC_DRAW); + GL30.glEnableVertexAttribArray(0); + GL30.glVertexAttribPointer(0, 3, GL30.GL_FLOAT, false, 0, 0); + + // Textures VBO (Vertex Buffer Object) + vboId = GL30.glGenBuffers(); + this.vboIdList.add(vboId); + var textureCoordinatesBuffer = MemoryUtil.memAllocFloat(this.mesh.getTextureCoords().length); + textureCoordinatesBuffer.put(0, this.mesh.getTextureCoords()); + GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vboId); + GL30.glBufferData(GL30.GL_ARRAY_BUFFER, textureCoordinatesBuffer, GL30.GL_STATIC_DRAW); + GL30.glEnableVertexAttribArray(1); + GL30.glVertexAttribPointer(1, 2, GL30.GL_FLOAT, false, 0, 0); + + // Index VBO (Vertex Buffer Object) + vboId = GL30.glGenBuffers(); + this.vboIdList.add(vboId); + var idxBuffer = memoryStack.callocInt(this.mesh.getIdx().length); + idxBuffer.put(0, this.mesh.getIdx()); + GL30.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, vboId); + GL30.glBufferData(GL30.GL_ELEMENT_ARRAY_BUFFER, idxBuffer, GL30.GL_STATIC_DRAW); + + GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0); + GL30.glBindVertexArray(0); + } + } + + /** + * Cleans up the gl mesh and calls all underlying cleanup methods. + * Removes only the references to VBOs and VAOs. + * Removes the Vertex Array Object (VAO) and all Vertex Buffer Object (VBO) ids. + */ + public void cleanup() { + this.vboIdList.forEach(GL30::glDeleteBuffers); + GL30.glDeleteVertexArrays(this.vaoId); + this.vboIdList.clear(); + LOGGER.trace("Cleaned-up GLMesh"); + } + + /** + * Returns the number of vertices of the wrapped generic mesh. + * @return The number of vertices of the wrapped generic mesh. + */ + public int getNumVertices() { + return this.mesh.getNumVertices(); + } + + /** + * Returns the Vertex Array Object (VAO) id. + * @return The Vertex Array Object (VAO) id. + */ + public final int getVaoId() { + return this.vaoId; + } + + /** + * Returns the ID of the wrapped generic mesh. + * @return The ID of the wrapped generic mesh. + */ + public String getId() { + return this.mesh.getId(); + } +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLModel.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLModel.java new file mode 100644 index 000000000..ea095c549 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLModel.java @@ -0,0 +1,86 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.glmodel; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.vitrivr.engine.model3d.data.texturemodel.Entity; +import org.vitrivr.engine.model3d.data.texturemodel.IModel; + +/** + * The GLModel class is a wrapper for the {@link IModel} class. + *
    + *
  • IModel -> GLModel( IModel )
  • + *
+ *

+ * The purpose is to bring the generic IModel in an OpenGl context + * {@link IModel} -> {@link GLModel} + */ +public class GLModel implements IGLModel { + private static final Logger LOGGER = LogManager.getLogger(); + + /** + * The model that is wrapped by this gl model. + */ + private final IModel model; + + /** + * The contained materials in gl context + */ + private final List materials; + + /** + * Creates a new GLModel from a model. + * + * @param model The model that is wrapped by this gl model. + */ + public GLModel(IModel model) { + this.model = model; + this.materials = new ArrayList<>(); + this.model.getMaterials().forEach(material -> this.materials.add(new GLMaterial(material))); + } + + /** + * {@inheritDoc} + */ + @Override + public List getEntities() { + return Collections.unmodifiableList(this.model.getEntities()); + } + + /** + * {@inheritDoc} + */ + @Override + public void addEntity(Entity entity) { + this.model.addEntity(entity); + } + + /** + * {@inheritDoc} + */ + @Override + public void cleanup() { + this.materials.forEach(GLMaterial::cleanup); + this.materials.clear(); + LOGGER.debug("GLModel cleaned up"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getId() { + return this.model.getId(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getMaterials() { + return Collections.unmodifiableList(this.materials); + } + +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLScene.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLScene.java new file mode 100644 index 000000000..74cdac5cb --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLScene.java @@ -0,0 +1,156 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.glmodel; + +import java.util.HashMap; +import java.util.Map; + +import org.vitrivr.engine.model3d.data.render.lwjgl.scene.Camera; +import org.vitrivr.engine.model3d.data.render.lwjgl.scene.Projection; +import org.vitrivr.engine.model3d.data.render.lwjgl.scene.Scene; +import org.vitrivr.engine.model3d.data.texturemodel.Entity; +import org.vitrivr.engine.model3d.data.texturemodel.IModel; + +/** + * The GLScene class ist the top most class of the gl model hierarchy. + * The gl model hierarchy is used as a wrapper for the model hierarchy. + * Therefore, each gl class has a corresponding model class. + * The generic class has to be provided in the constructor. + *

    + *
  • Scene -> GLScene( Scene )
  • + *
  • Model -> GlModel( IModel )
  • + *
  • Material -> GLMaterial( Material )
  • + *
  • Mesh -> GLMesh( Mesh )
  • + *
  • Texture -> GLTexture( Texture )
  • + *
+ * + * The purpose is to bring the generic model in an OpenGl context + * {@link Scene} -> {@link GLScene} + */ +public class GLScene { + + /** + * The scene that is wrapped by this gl scene. + */ + private final Scene scene; + /** + * The wrapped GlModels that are wrapped by this gl scene. + */ + private final Map models; + + /** + * The texture cache that is used by this gl scene. + * Textures are cached to avoid loading the same texture multiple times. + * Has no corresponding generic class. + */ + private final GLTextureCache textureCache; + + /** + * Creates a new GLScene from a scene. + * + * @param scene The scene that is wrapped by this gl scene. + */ + public GLScene(Scene scene) { + this.models = new HashMap<>(); + this.textureCache = new GLTextureCache(); + this.scene = scene; + this.updateGlSceneFromScene(); + } + + /** + * Adds a model to the scene. + * + * @param model The model that is added to the scene. + */ + public void addModel(IModel model) { + this.scene.addModel(model); + this.updateGlSceneFromScene(); + } + + /** + * Updates the gl scene from the scene. + * It updates the gl scene content to match the scene content. + */ + private void updateGlSceneFromScene() { + this.scene.getModels().forEach((k, v) -> this.models.putIfAbsent(k, new GLModel(v))); + this.models.forEach( + (k, v) -> this.models.get(k).getMaterials() + .forEach(mat -> this.textureCache.addTextureIfAbsent(mat.getTexture()))); + } + + /** + * Adds an entity to the corresponding model. + * @param entity The entity that is added to the model. + */ + public void addEntity(Entity entity) { + var modelId = entity.getModelId(); + var model = this.models.get(modelId); + if (model == null) { + throw new RuntimeException("Model not found: " + modelId); + } + model.addEntity(entity); + } + + /** + * Returns the gl models of the gl scene. + * @return The gl models of the gl scene. + */ + public Map getModels() { + return this.models; + } + + /** + * Returns the texture cache of the gl scene. + * @return The texture cache of the gl scene. + */ + public GLTextureCache getTextureCache() { + return this.textureCache; + } + + /** + * Returns the projection of the wrapped generic scene. + * @return The projection of the wrapped generic scene. + */ + public Projection getProjection() { + return this.scene.getProjection(); + } + + /** + * Returns the camera of the wrapped generic scene. + * @return The camera of the wrapped generic scene. + */ + public Camera getCamera() { + return this.scene.getCamera(); + } + + /** + * Clears the models of the gl scene but not containing resources. + * Removes the references to the wrapped generic models and textures. + * Hence, the models could be used by another extraction task this method does not close the models or textures. + * Can be used to only remove Models temporarily from gl scene. + */ + @SuppressWarnings("unused") + public void clearModels() { + this.cleanup(); + this.models.clear(); + } + + /** + * Cleans up the gl scene and calls all underlying cleanup methods. + * Removes only the references to wrapped generic models and textures. + * Hence, the model could be used by another extraction task this method does not close the generic models or textures. + */ + public void cleanup() { + this.models.values().forEach(IGLModel::cleanup); + this.models.clear(); + this.textureCache.cleanup(); + } + + /** + * Resizes the projection of the wrapped generic scene. + * @param width The new width of the projection. + * @param height The new height of the projection. + */ + public void resize(int width, int height) { + this.scene.getProjection().updateProjMatrix(width, height); + } + +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLTexture.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLTexture.java new file mode 100644 index 000000000..2c2d8c48a --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLTexture.java @@ -0,0 +1,95 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.glmodel; + +import java.nio.ByteBuffer; +import org.lwjgl.opengl.GL30; +import org.lwjgl.stb.STBImage; +import org.lwjgl.system.MemoryStack; +import org.vitrivr.engine.model3d.data.texturemodel.Texture; + +/** + * The GLTexture class is a wrapper for the {@link Texture} class. + *
    + *
  • Texture -> GLTexture( Texture )
  • + *
+ *

+ * The purpose is to bring the generic Mesh in an OpenGl context + * {@link Texture} -> {@link GLTexture} + */ +public class GLTexture { + + /** + * The id of the texture used to bind the texture to the Gl context + */ + private int textureId; + /** + * The wrapped generic texture in gl context + */ + private final Texture texture; + + /** + * Creates a new GLTexture from a texture. + *

    + *
  1. Load the texture from the file
  2. + *
  3. Allocate the texture buffer
  4. + *
  5. Load the texture into the buffer
  6. + *
+ * @param texture The texture that is wrapped by this gl texture. + */ + public GLTexture(Texture texture) { + this.texture = texture; + try (var memoryStack = MemoryStack.stackPush()) { + var w = memoryStack.mallocInt(1); + var h = memoryStack.mallocInt(1); + var channels = memoryStack.mallocInt(1); + + var imageBuffer = STBImage.stbi_load(this.texture.getTexturePath(), w, h, channels, 4); + if (imageBuffer == null) { + throw new RuntimeException("Could not load texture file: " + this.texture.getTexturePath()); + } + this.generateTexture(w.get(), h.get(), imageBuffer); + STBImage.stbi_image_free(imageBuffer); + } + } + + /** + * Binds the GLTexture to the Gl context + */ + public void bind() { + GL30.glBindTexture(GL30.GL_TEXTURE_2D, this.textureId); + } + + /** + * Cleans the GLTexture + * Does not affect the underlying texture + * Removes the texture from the GPU + */ + public void cleanup() { + GL30.glDeleteTextures(this.textureId); + } + + /** + * Generates the texture in the Gl context + * @param width The width of the texture + * @param height The height of the texture + * @param texture The texture buffer + */ + private void generateTexture(int width, int height, ByteBuffer texture) { + this.textureId = GL30.glGenTextures(); + GL30.glBindTexture(GL30.GL_TEXTURE_2D, this.textureId); + GL30.glPixelStorei(GL30.GL_UNPACK_ALIGNMENT, 1); + GL30.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MIN_FILTER, GL30.GL_NEAREST); + GL30.glTexParameteri(GL30.GL_TEXTURE_2D, GL30.GL_TEXTURE_MAG_FILTER, GL30.GL_NEAREST); + GL30.glTexImage2D(GL30.GL_TEXTURE_2D, 0, GL30.GL_RGBA, width, height, 0, GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE, + texture); + GL30.glGenerateMipmap(GL30.GL_TEXTURE_2D); + } + + /** + * Returns the texture path of the underlying wrapped texture + * @return The texture path of the underlying wrapped texture + */ + public String getTexturePath() { + return this.texture.getTexturePath(); + } +} + diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLTextureCache.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLTextureCache.java new file mode 100644 index 000000000..7fd140ace --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/GLTextureCache.java @@ -0,0 +1,56 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.glmodel; + +import java.util.HashMap; +import java.util.Map; +import org.vitrivr.engine.model3d.data.texturemodel.Texture; + +/** + * A cache for textures + * Prevents the same texture from being loaded multiple times + */ +public class GLTextureCache { + + /** + * The cache of textures + */ + private final Map textures; + + /** + * Creates a new texture cache + * Adds a default texture to the cache + */ + public GLTextureCache() { + this.textures = new HashMap<>(); + var texture = new Texture(); + this.textures.put(texture.getTexturePath(), new GLTexture(texture)); + this.textures.put("default", new GLTexture(texture)); + } + + /** + * Cleans up the texture cache + * Cleans the registered textures and clears the cache + */ + public void cleanup() { + this.textures.values().forEach(GLTexture::cleanup); + this.textures.clear(); + } + + /** + * Adds a texture to the cache if it is not already present + * + * @param texture Texture to add + */ + public void addTextureIfAbsent(GLTexture texture) { + this.textures.putIfAbsent(texture.getTexturePath(), texture); + } + + /** + * Returns the gl texture with the given texture path + * + * @param texturePath Path of the texture + * @return The texture with the given texture path + */ + public GLTexture getTexture(String texturePath) { + return this.textures.get(texturePath); + } +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/IGLModel.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/IGLModel.java new file mode 100644 index 000000000..23391f590 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/glmodel/IGLModel.java @@ -0,0 +1,47 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.glmodel; + +import java.util.List; +import org.vitrivr.engine.model3d.data.texturemodel.Entity; +import org.vitrivr.engine.model3d.data.texturemodel.IModel; + +/** + * The Interface IGLModel provides functionality for a arbitrary model used in the OpenGl context. + * Itr is the context related counterpart to the {@link IModel} interface. + */ +public interface IGLModel{ + + /** + * Returns the entities of the wrapped generic model. + * + * @return The entities of the wrapped generic model. + */ + List getEntities(); + + /** + * Adds an entity to the wrapped generic model. + * + * @param entity The entity to be added. + */ + void addEntity(Entity entity); + + /** + * Cleans up the gl model and calls all underlying cleanup methods. + * Removes only the references to wrapped generic materials + * Hence, the model could be used by another extraction task this method does not close the generic model. + */ + void cleanup(); + + /** + * Returns the id of the wrapped generic model. + * + * @return The id of the wrapped generic model. + */ + String getId(); + + /** + * Returns the gl materials of the gl model. + * + * @return The gl materials of the gl model. + */ + List getMaterials(); +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/Render.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/Render.java new file mode 100644 index 000000000..18f1f82fc --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/Render.java @@ -0,0 +1,66 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.render; + + +import org.vitrivr.engine.model3d.data.render.lwjgl.glmodel.GLScene; +import org.vitrivr.engine.model3d.data.render.lwjgl.window.Window; +import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GL30; + +/** + * This class holds the render logic for the LWJGL engine + * Holds the {@link SceneRender} which loads shaders + */ +public class Render { + + /** + * Instance of the scene render + * @see SceneRender + */ + private final SceneRender sceneRender; + + /** + * Instance of the render options + * @see RenderOptions + */ + private RenderOptions options; + + /** + * Create a render instance Set up the Render options for OpenGL + */ + public Render() { + GL.createCapabilities(); + GL30.glEnable(GL30.GL_DEPTH_TEST); + GL30.glEnable(GL30.GL_CULL_FACE); + GL30.glCullFace(GL30.GL_BACK); + this.sceneRender = new SceneRender(); + } + + /** + * Set the render options {@link RenderOptions} + * + * @param options see {@link RenderOptions} + */ + public void setOptions(RenderOptions options) { + this.options = options; + } + + /** + * Releases all resources + */ + public void cleanup() { + this.sceneRender.cleanup(); + this.options = null; + } + + /** + * Renders a given Scene in a Given Window + * + * @param window GL (offscreen) window instance {@link Window} + * @param scene GL Scene (containing all models) {@link GLScene} + */ + public void render(Window window, GLScene scene) { + GL30.glClear(GL30.GL_COLOR_BUFFER_BIT | GL30.GL_DEPTH_BUFFER_BIT); + GL30.glViewport(0, 0, window.getWidth(), window.getHeight()); + this.sceneRender.render(scene, this.options); + } +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/RenderOptions.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/RenderOptions.java new file mode 100644 index 000000000..9e9a4573d --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/RenderOptions.java @@ -0,0 +1,35 @@ + +package org.vitrivr.engine.model3d.data.render.lwjgl.render; + +import java.util.function.Function; +import org.joml.Vector4f; + +/** + * RenderOptions + *
    + *
  • Used to switch on or off the texture rendering
  • + *
  • Used to switch on or off the coloring rendering
  • + *
  • Returns the color for the given value
  • + *
  • Can be used to colorize the model custom
  • + *
+ */ +public class RenderOptions { + + /** + * Used to switch on or off the texture rendering + */ + public boolean showTextures = true; + + /** + * Used to switch on or off the coloring rendering For future face coloring + */ + @SuppressWarnings("unused") + public boolean showColor = false; + + /** + * Returns the color for the given value Can be used to colorize the model custom + */ + public Function colorfunction = + (v) -> new Vector4f(v, v, v, 1f); + +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/SceneRender.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/SceneRender.java new file mode 100644 index 000000000..8fcefae71 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/SceneRender.java @@ -0,0 +1,148 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.render; + +import java.util.ArrayList; +import org.lwjgl.opengl.GL30; +import org.vitrivr.engine.model3d.data.render.lwjgl.glmodel.GLScene; +import org.vitrivr.engine.model3d.data.render.lwjgl.glmodel.GLTexture; +import org.vitrivr.engine.model3d.data.render.lwjgl.render.ShaderProgram.ShaderModuleData; + +/** + * SceneRender + *
    + *
  • Renders the scene
  • + *
  • Loads the scene shader
  • + *
  • Creates the uniforms
  • + *
  • Binds the Model
  • + *
  • Binds the Texture
  • + *
+ */ +public class SceneRender { + + /** + * Resource path to the scene shader program + */ + private static final String VERTEX_SHADER_PATH = "vitrivr-engine-module-m3d/src/main/resources/renderer/lwjgl/shaders/scene.vert"; + /** + * Resource path to the fragment shader program + */ + private static final String FRAGMENT_SHADER_PATH = "vitrivr-engine-module-m3d/src/main/resources/renderer/lwjgl/shaders/scene.frag"; + /** + * Instance of the scene shader program + */ + private final ShaderProgram shaderProgram; + /** + * Uniforms for the scene shader + */ + private UniformsMap uniformsMap; + + /** + * SceneRender. During construction: Loads the scene shader from the resources + */ + public SceneRender() { + var shaderModuleDataList = new ArrayList(); + shaderModuleDataList.add(new ShaderModuleData(VERTEX_SHADER_PATH, GL30.GL_VERTEX_SHADER)); + shaderModuleDataList.add(new ShaderModuleData(FRAGMENT_SHADER_PATH, GL30.GL_FRAGMENT_SHADER)); + this.shaderProgram = new ShaderProgram(shaderModuleDataList); + this.createUniforms(); + } + + /** + * Creates the uniforms for the scene shader creates the following uniforms: + *
    + *
  • projectionMatrix
  • + *
  • modelMatrix
  • + *
  • viewMatrix
  • + *
  • txtSampler
  • + *
  • material.diffuse
  • + *
+ */ + private void createUniforms() { + this.uniformsMap = new UniformsMap(this.shaderProgram.getProgramId()); + this.uniformsMap.createUniform("projectionMatrix"); + this.uniformsMap.createUniform("modelMatrix"); + this.uniformsMap.createUniform("viewMatrix"); + this.uniformsMap.createUniform("txtSampler"); + this.uniformsMap.createUniform("material.diffuse"); + } + + /** + * Releases all resources + *
    + *
  • Releases the shader program
  • + *
  • Releases the uniforms
  • + *
+ */ + public void cleanup() { + this.shaderProgram.cleanup(); + this.uniformsMap.cleanup(); + this.uniformsMap = null; + } + + /** + * Renders the Models in the scene + * Creates standard render options + * @param scene Scene to render + */ + public void render(GLScene scene) { + this.render(scene, new RenderOptions()); + } + + /** + * Renders the Models in the scene + *
    + *
  • Binds projection matrix
  • + *
  • Binds view matrix
  • + *
  • Binds texture sampler
  • + *
+ * Further, iterate over all models in the scene + *
    + *
  • Iterate over all materials in the model
  • + *
  • Sets texture or color function
  • + *
  • Iterate over all meshes in the material
  • + *
  • Binds the mesh
  • + *
  • Iterate over all entities to draw the mesh
  • + *
  • Binds the model matrix
  • + *
  • Draws the mesh
  • + *
  • Unbinds
  • + *
+ * @param scene Scene to render + * @param opt Render options + */ + public void render(GLScene scene, RenderOptions opt) { + this.shaderProgram.bind(); + + this.uniformsMap.setUniform("projectionMatrix", scene.getProjection().getProjMatrix()); + this.uniformsMap.setUniform("viewMatrix", scene.getCamera().getViewMatrix()); + this.uniformsMap.setUniform("txtSampler", 0); + + var models = scene.getModels().values(); + var textures = scene.getTextureCache(); + + for (var model : models) { + var entities = model.getEntities(); + for (var material : model.getMaterials()) { + GLTexture texture; + // Either draw texture or use color function + if (opt.showTextures) { + this.uniformsMap.setUniform("material.diffuse", material.getDiffuseColor()); + texture = textures.getTexture(material.getTexture().getTexturePath()); + } else { + this.uniformsMap.setUniform("material.diffuse", opt.colorfunction.apply(1f)); + texture = textures.getTexture("default"); + } + GL30.glActiveTexture(GL30.GL_TEXTURE0); + texture.bind(); + for (var mesh : material.getMeshes()) { + GL30.glBindVertexArray(mesh.getVaoId()); + for (var entity : entities) { + this.uniformsMap.setUniform("modelMatrix", entity.getModelMatrix()); + GL30.glDrawElements(GL30.GL_TRIANGLES, mesh.getNumVertices(), GL30.GL_UNSIGNED_INT, 0); + } + } + } + } + GL30.glBindVertexArray(0); + + this.shaderProgram.unbind(); + } +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/ShaderProgram.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/ShaderProgram.java new file mode 100644 index 000000000..8f728c645 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/ShaderProgram.java @@ -0,0 +1,149 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.render; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import org.lwjgl.opengl.GL30; + + +/** + * ShaderProgram + * Loads a shader program from the resources to the GL context + */ +public class ShaderProgram { + + /** + * Shader Program ID, is used to bind and release the program from the GL context + */ + private final int programId; + + /** + * Creates a new ShaderProgram + * Takes a list of ShaderModuleData (usually from Scene Renderer which loads the shaders from the resources during construction + * Creates a new ShaderProgram in GL context, links the shaders and validates the program + * For Shader creation, the following steps are performed: + *
    + *
  • Reads the shader file
  • + *
  • Creates a new shader in the GL context {@link ShaderProgram#createShader(String, int)}
  • + *
  • Compiles the shader
  • + *
  • Attaches the shader to the program
  • + *
  • Links the program
  • + *
  • Binds the program to the GL context
  • + *
+ * @param shaderModuleDataList List of ShaderModuleData + */ + public ShaderProgram(List shaderModuleDataList) { + this.programId = GL30.glCreateProgram(); + if (this.programId == 0) { + throw new RuntimeException("Could not Create Shader"); + } + var shaderModules = new ArrayList(); + shaderModuleDataList.forEach(s -> shaderModules.add(this.createShader(ShaderProgram.readShaderFile(s.shaderFile), s.shaderType))); + this.link(shaderModules); + } + + /** + * Binds the ShaderProgram to the GL context + */ + public void bind() { + GL30.glUseProgram(this.programId); + } + + /** + * Unbinds the ShaderProgram from the GL context + */ + public void unbind() { + GL30.glUseProgram(0); + } + + /** + * Unbinds the ShaderProgram from the GL context + * Deletes the ShaderProgram from the GL context + */ + public void cleanup() { + this.unbind(); + if (this.programId != 0) { + GL30.glDeleteProgram(this.programId); + } + } + + /** + * Creates a new Shader in the GL context + * Compiles the shader + * Attaches the shader to the program + * @return the shader id + */ + protected int createShader(String shaderCode, int shaderType) { + int shaderId = GL30.glCreateShader(shaderType); + if (shaderId == 0) { + throw new RuntimeException("Error creating Shader"); + } + GL30.glShaderSource(shaderId, shaderCode); + GL30.glCompileShader(shaderId); + + if (GL30.glGetShaderi(shaderId, GL30.GL_COMPILE_STATUS) == 0) { + throw new RuntimeException("Error Compiling Shader"); + } + GL30.glAttachShader(this.programId, shaderId); + return shaderId; + } + + /** + * Links the program + * Deletes the shaders + * @param shaderModules List of shader ids + */ + private void link(List shaderModules) { + GL30.glLinkProgram(this.programId); + if (GL30.glGetProgrami(this.programId, GL30.GL_LINK_STATUS) == 0) { + throw new RuntimeException("Error linking Shader"); + } + shaderModules.forEach(s -> GL30.glDetachShader(this.programId, s)); + shaderModules.forEach(GL30::glDeleteShader); + //this.validate(); + } + + /** + * Validates the program + * Throws an exception if the program is not valid + */ + public void validate() { + GL30.glValidateProgram(this.programId); + if (GL30.glGetProgrami(this.programId, GL30.GL_VALIDATE_STATUS) == 0) { + throw new RuntimeException("Error validate Shader"); + } + } + + /** + * Returns the program id + * @return program id + */ + public int getProgramId() { + return this.programId; + } + + /** + * Reads the shader file + * @param filePath Path to the shader file + * @return String containing the shader code + */ + public static String readShaderFile(String filePath) { + String str; + try { + str = new String(Files.readAllBytes(Paths.get(filePath))); + } catch (IOException ex) { + throw new RuntimeException("Error reading file"); + } + return str; + } + + /** + * RECORD for ShaderModuleData + * @param shaderFile + * @param shaderType + */ + public record ShaderModuleData(String shaderFile, int shaderType) { + } +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/UniformsMap.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/UniformsMap.java new file mode 100644 index 000000000..111dc1f2c --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/render/UniformsMap.java @@ -0,0 +1,107 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.render; + +import java.util.HashMap; +import org.joml.Matrix4f; +import org.joml.Vector4f; +import org.lwjgl.opengl.GL30; +import org.lwjgl.system.MemoryStack; + +/** + * Holds a hashmap for used uniforms are global variables in the shader e.g. projectionMatrix, modelMatrix, viewMatrix, txtSampler, material.diffuse + * + * @see https://www.khronos.org/opengl/wiki/Uniform_(GLSL) + * @see "./resources/renderer/lwjgl/shaders/scene.vert" + * @see "./resources/renderer/lwjgl/shaders/scene.frag" + */ +public class UniformsMap { + + /** + * HashMap for the uniforms Key: Uniform name Value: Uniform location in the shader + */ + private final HashMap uniforms; + + /** + * Program id of the shader + */ + private final int programId; + + /** + * Instantiate a new UniformsMap + * + * @param programId Program id of the shader {@link ShaderProgram#getProgramId} + */ + public UniformsMap(int programId) { + this.programId = programId; + this.uniforms = new HashMap<>(); + } + + /** + * Creates a new uniform + * + * @param uniformName Name of the uniform + */ + public void createUniform(String uniformName) { + var uniformLocation = GL30.glGetUniformLocation(this.programId, uniformName); + if (uniformLocation < 0) { + throw new RuntimeException("Could not find uniform:" + uniformName); + } + this.uniforms.put(uniformName, uniformLocation); + } + + /** + * Sets the value of a uniform to gl context + * + * @param uniformName Name of the uniform + * @param value Value of the uniform + */ + public void setUniform(String uniformName, int value) { + GL30.glUniform1i(this.getUniformLocation(uniformName), value); + } + + /** + * Returns the location of the uniform from the hashmap + * + * @param uniformName name of the uniform + * @return location of the uniform + */ + private int getUniformLocation(String uniformName) { + var location = this.uniforms.get(uniformName); + if (location == null) { + throw new RuntimeException("Could not find uniform:" + uniformName); + } + return location; + } + + /** + * Sets the value 4 float vector of a uniform to gl context + * + * @param uniformName Name of the uniform + * @param value Value of the uniform + */ + public void setUniform(String uniformName, Vector4f value) { + GL30.glUniform4f(this.getUniformLocation(uniformName), value.x, value.y, value.z, value.w); + } + + /** + * Sets the value 4*4 float matrix of a uniform to gl context + * + * @param uniformName Name of the uniform + * @param value Value of the uniform + */ + public void setUniform(String uniformName, Matrix4f value) { + try (var memoryStack = MemoryStack.stackPush()) { + var location = this.uniforms.get(uniformName); + if (location == null) { + throw new RuntimeException("Could not find uniform:" + uniformName); + } + GL30.glUniformMatrix4fv(location, false, value.get(memoryStack.mallocFloat(16))); + } + } + + /** + * Cleans up the uniforms + */ + public void cleanup() { + this.uniforms.clear(); + } +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/LWJGLOffscreenRenderer.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/LWJGLOffscreenRenderer.java new file mode 100644 index 000000000..0264c5256 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/LWJGLOffscreenRenderer.java @@ -0,0 +1,296 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.renderer; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.util.concurrent.LinkedTransferQueue; + +import org.joml.Vector3f; +import org.vitrivr.engine.model3d.data.render.Renderer; +import org.vitrivr.engine.model3d.data.render.lwjgl.engine.Engine; +import org.vitrivr.engine.model3d.data.render.lwjgl.engine.EngineLogic; +import org.vitrivr.engine.model3d.data.render.lwjgl.scene.LightfieldCamera; +import org.vitrivr.engine.model3d.data.render.lwjgl.glmodel.GLScene; +import org.vitrivr.engine.model3d.data.render.lwjgl.render.RenderOptions; +import org.vitrivr.engine.model3d.data.render.lwjgl.window.Window; +import org.vitrivr.engine.model3d.data.render.lwjgl.window.WindowOptions; +import org.vitrivr.engine.model3d.data.texturemodel.Entity; +import org.vitrivr.engine.model3d.data.texturemodel.IModel; +import org.vitrivr.engine.model3d.data.texturemodel.Model; +import org.vitrivr.engine.model3d.data.render.lwjgl.render.Render; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * This is the top most class of the LWJGL for Java 3D renderer. Its main function is to provide an interface between Engine and the outside world. It sets up the {@link Engine} and provides the interface to the outside world. {@link Renderer} It extends the abstract class {@link EngineLogic} which allows the instanced engine to call methods depending on the engine state. + */ +@SuppressWarnings("deprecation") +public class LWJGLOffscreenRenderer extends EngineLogic implements Renderer { + + private static final Logger LOGGER = LogManager.getLogger(); + + /** + * The (offscreen) window options for the engine. + */ + private WindowOptions windowOptions; + /** + * The engine instance. + */ + private Engine engine; + + /** + * The model queue. From this queue the renderer takes the next model to render. + */ + private final LinkedTransferQueue modelQueue; + + /** + * The image queue. In this queue the renderer puts the rendered images. + */ + private final LinkedTransferQueue imageQueue; + + + /** + * Constructor for the LWJGLOffscreenRenderer. Initializes the model queue and the image queue. + */ + public LWJGLOffscreenRenderer() { + this.modelQueue = new LinkedTransferQueue<>(); + this.imageQueue = new LinkedTransferQueue<>(); + LOGGER.trace("LWJGLOffscreenRenderer created"); + } + + /** + * Sets the window options for the engine. + * + * @param opts The window options. + */ + public void setWindowOptions(WindowOptions opts) { + this.windowOptions = opts; + } + + /** + * Sets the render options for the engine. + * + * @param opts The render options. + */ + public void setRenderOptions(RenderOptions opts) { + this.engine.setRenderOptions(opts); + } + + /** + * Starts the engine with given window options. Registers the LWJGLOffscreenRenderer as the engine logic. + */ + public void startEngine() { + var name = "LWJGLOffscreenRenderer"; + this.engine = new Engine(name, this.windowOptions, this); + } + + /** + * Starts the rendering process. + */ + @Override + public void render() { + this.engine.runOnce(); + LOGGER.trace("LWJGLOffscreenRenderer rendered"); + } + + + /** + * Is called once at the initialization of the engine. DO NOT CALL ENGINE METHODS IN THIS METHOD DO NOT CALL THIS METHOD FROM THIS CLASS + */ + @Override + protected void init(Window window, GLScene scene, Render render) { + scene.getCamera().setPosition(0, 0, 1); + } + + /** + * Is called from the engine before the render method. DO NOT CALL ENGINE METHODS IN THIS METHOD DO NOT CALL THIS METHOD FROM THIS CLASS + */ + @Override + protected void beforeRender(Window window, GLScene scene, Render render) { + this.loadNextModelFromQueueToScene(window, scene); + } + + /** + * Is called from the engine after the render method. DO NOT CALL ENGINE METHODS IN THIS METHOD DO NOT CALL THIS METHOD FROM THIS CLASS + */ + @Override + protected void afterRender(Window window, GLScene scene, Render render) { + var lfc = new LightfieldCamera(this.windowOptions); + this.imageQueue.add(lfc.takeLightfieldImage()); + } + + /** + * Is called from the engine as first step during refresh and cleanup DO NOT CALL ENGINE METHODS IN THIS METHOD DO NOT CALL THIS METHOD FROM THIS CLASS + */ + @Override + protected void cleanup() { + LOGGER.trace("LWJGLOffscreenRenderer cleaned"); + } + + + /** + * This method is called every frame. This is only used in continuous rendering. The purpose is to do some input handling. Could be use for optimize view angles on a fast manner. DO NOT CALL ENGINE METHODS IN THIS METHOD DO NOT CALL THIS METHOD FROM THIS CLASS + */ + @Override + protected void input(Window window, GLScene scene, long diffTimeMillis) { + scene.getModels().forEach((k, v) -> v.getEntities().forEach(Entity::updateModelMatrix)); + } + + /** + * After Engine run This method is called every frame. This is only used in continuous rendering. The purpose is to process some life output. Could be use for optimize view angles on a fast manner. DO NOT CALL ENGINE METHODS IN THIS METHOD DO NOT CALL THIS METHOD FROM THIS CLASS + */ + @Override + protected void update(Window window, GLScene scene, long diffTimeMillis) { + } + + /** + * This method is called to load the next model into the provided scene. + * + * @param scene The scene to put the model in. + */ + @SuppressWarnings("unused") + private void loadNextModelFromQueueToScene(Window window, GLScene scene) { + if (!this.modelQueue.isEmpty()) { + var model = (Model) this.modelQueue.poll(); + if (model.getEntities().size() == 0) { + var entity = new Entity("cube", model.getId()); + model.addEntityNorm(entity); + } + //cleans all current models from the scene + scene.cleanup(); + //adds the new model to the scene + scene.addModel(model); + } + scene.getModels().forEach((k, v) -> v.getEntities().forEach(Entity::updateModelMatrix)); + } + + /** + * Moves the camera in the scene with given deltas in cartesian coordinates. Look at the origin. + */ + public void moveCameraOrbit(float dx, float dy, float dz) { + this.engine.getCamera().moveOrbit(dx, dy, dz); + } + + /** + * Sets the camera in the scene to cartesian coordinates. Look at the origin. + */ + public void setCameraOrbit(float x, float y, float z) { + this.engine.getCamera().setOrbit(x, y, z); + } + + /** + * Moves the camera in the scene with given deltas in cartesian coordinates. Keep the orientation. + */ + @SuppressWarnings("unused") + public void setCameraPosition(float x, float y, float z) { + this.engine.getCamera().setPosition(x, y, z); + } + + /** + * Set position of the camera and look at the origin. Camera will stay aligned to the y plane. + */ + public void lookFromAtO(float x, float y, float z) { + var lookFrom = new Vector3f(x, y, z); + var lookAt = new Vector3f(0, 0, 0); + + this.engine.getCamera().setPositionAndOrientation(lookFrom, lookAt); + + } + + /** + * Set position and orientation of the camera. + * + * @deprecated Old renderer implementation. Not needed. Use quaternion instead. + */ + @Override + @Deprecated + public void positionCamera(double ex, double ey, double ez, + double cx, double cy, double cz, + double upx, double upy, double upz) { + this.engine.getCamera().setPositionAndOrientation( + new Vector3f((float) ex, (float) ey, (float) ez), + new Vector3f((float) cx, (float) cy, (float) cz), + new Vector3f((float) upx, (float) upy, (float) upz)); + } + + + /** + * Returns the aspect ratio of the window. + */ + @SuppressWarnings("unused") + public float getAspect() { + return (float) this.windowOptions.width / (float) this.windowOptions.height; + } + + /** + * Interface to outside to add a model to the scene. + */ + @Override + public void assemble(IModel model) { + this.modelQueue.add(model); + } + + /** + * Interface to outside to get a rendered image. + */ + @Override + public BufferedImage obtain() { + return this.imageQueue.poll(); + } + + /** + * This method disposes the engine. Window is destroyed and all resources are freed. + */ + @Override + public void clear(Color color) { + this.clear(); + } + + /** + * This method disposes the engine. Window is destroyed and all resources are freed. + */ + @Override + public void clear() { + this.engine.clear(); + this.engine = null; + } + + /** + * Retains control of the Renderer. While a Thread retains a renderer, no other thread should be allowed to use it! + * + * @deprecated Old renderer implementation. Indicates that the renderer should be retained. + */ + @Override + @Deprecated + public boolean retain() { + return true; + } + + /** + * Releases control of the Renderer, making it usable by other Threads again. + * + * @deprecated Old renderer implementation. Indicates that the renderer should be retained. + */ + @Override + @Deprecated + public void release() { + this.engine.clear(); + } + + /** + * Returns the width of the window. + * + * @return The width of the window. (in pixels) + */ + public int getWidth() { + return this.windowOptions.width; + } + + /** + * Returns the height of the window. + * + * @return The height of the window. (in pixels) + */ + public int getHeight() { + return this.windowOptions.height; + } +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderActions.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderActions.java new file mode 100644 index 000000000..c4eb767e9 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderActions.java @@ -0,0 +1,14 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.renderer; + +/** + * Actions used of the Render Workflow + * {@link RenderWorker} + */ +public final class RenderActions { + + public static final String SETUP = "SETUP"; + public static final String RENDER = "RENDER"; + public static final String ROTATE = "ROTATE"; + public static final String LOOKAT = "LOOKAT"; + public static final String LOOKAT_FROM = "LOOKAT_FROM"; +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderData.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderData.java new file mode 100644 index 000000000..746534967 --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderData.java @@ -0,0 +1,16 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.renderer; + +/** + * Data used of the Render Workflow + * {@link RenderWorker} + */ +public final class RenderData { + + public static final String MODEL = "MODEL"; + public static final String VECTOR = "VECTOR"; + public static final String VECTORS = "VECTORS"; + public static final String IMAGE = "IMAGE"; + public static final String WINDOWS_OPTIONS = "WINDOWS_OPTIONS"; + public static final String RENDER_OPTIONS = "RENDER_OPTIONS"; + +} diff --git a/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderJob.java b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderJob.java new file mode 100644 index 000000000..78aedfbbf --- /dev/null +++ b/vitrivr-engine-module-m3d/src/main/java/org/vitrivr/engine/model3d/data/render/lwjgl/renderer/RenderJob.java @@ -0,0 +1,147 @@ +package org.vitrivr.engine.model3d.data.render.lwjgl.renderer; + +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.BlockingDeque; +import java.util.concurrent.LinkedBlockingDeque; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.joml.Vector3f; +import org.vitrivr.engine.model3d.data.render.lwjgl.util.datatype.Variant; +import org.vitrivr.engine.model3d.data.render.lwjgl.util.fsm.abstractworker.Job; +import org.vitrivr.engine.model3d.data.render.lwjgl.util.fsm.abstractworker.JobControlCommand; +import org.vitrivr.engine.model3d.data.render.lwjgl.util.fsm.abstractworker.JobType; +import org.vitrivr.engine.model3d.data.render.lwjgl.util.fsm.model.Action; +import org.vitrivr.engine.model3d.data.texturemodel.IModel; +import org.vitrivr.engine.model3d.data.render.lwjgl.render.RenderOptions; +import org.vitrivr.engine.model3d.data.render.lwjgl.window.WindowOptions; + +/** + * The RenderJob is a job which is responsible for rendering a model. + *

+ * This job extends the abstract class Job. + *

+ * It provides constructors for the different types of jobs. + * ORDER Job to render a model. + * COMMAND Job signals caller that the job is done or an error occurred. + * RESULT Job contains the result of the rendering. + */ +public class RenderJob extends Job { + + private static final Logger LOGGER = LogManager.getLogger(); + + /** + * Creates a new ORDER RenderJob with the given action sequence and data (containing the model to render). + */ + public RenderJob(BlockingDeque actions, Variant data) { + super(actions, data); + } + + /** + * Creates a new RESPONSE RenderJob with the rendered image. + */ + public RenderJob(Variant data) { + super(data); + } + + /** + * Creates a new CONTROL RenderJob with the given command. + */ + public RenderJob(JobControlCommand command) { + super(command); + } + + + /** + * Static method to create a standard render job. + *

+ * @see RenderJob#performStandardRenderJob(BlockingDeque, IModel, LinkedList, WindowOptions, RenderOptions) + */ + public static List performStandardRenderJob(BlockingDeque renderJobQueue, IModel model, double[][] cameraPositions, WindowOptions windowOptions, RenderOptions renderOptions) { + var cameraPositionVectors = new LinkedList(); + for (double[] cameraPosition : cameraPositions) { + assert cameraPosition.length == 3; + cameraPositionVectors.add(new Vector3f((float) cameraPosition[0], (float) cameraPosition[1], (float) cameraPosition[2])); + } + return performStandardRenderJob(renderJobQueue, model, cameraPositionVectors, windowOptions, renderOptions); + } + + /** + * Static method to create a standard render job. + *

+ *