diff --git a/build.gradle b/build.gradle index 51cef4f4a21..05d8101c0d5 100644 --- a/build.gradle +++ b/build.gradle @@ -79,6 +79,7 @@ ext { subDirLibs = 'libs' LwjglVersion = '3.3.1' + rustCoreVersion = '0.0.1' } @@ -114,6 +115,11 @@ dependencies { // Natives for JNBullet natives group: 'org.terasology.jnbullet', name: 'JNBullet', version: '1.0.2', ext: 'zip' + // Natives for core + natives(group: 'org.terasology.rust', name: 'core-rust', version: rustCoreVersion){ + transitive = true + } + } task extractWindowsNatives(type: Copy) { @@ -159,6 +165,16 @@ task extractNativeBulletNatives(type: Copy) { into("$dirNatives") } +task extractRustCoreNatives(type:Copy) { + description = "Extracts the JNBullet natives from the downloaded zip" + from configurations.natives + from { + configurations.natives.collect { it.getName().contains('core-rust') ? zipTree(it) : [] } + } + into ("$dirNatives") +} + + task extractNatives { description = "Extracts all the native lwjgl libraries from the downloaded zip" @@ -167,6 +183,7 @@ task extractNatives { dependsOn extractMacOSXNatives dependsOn extractJNLuaNatives dependsOn extractNativeBulletNatives + dependsOn extractRustCoreNatives } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/engine/build.gradle b/engine/build.gradle index e10ec9d3b40..1aa40c4fbbe 100644 --- a/engine/build.gradle +++ b/engine/build.gradle @@ -8,6 +8,7 @@ plugins { id "org.jetbrains.gradle.plugin.idea-ext" id "com.google.protobuf" id "terasology-common" + } // Grab all the common stuff like plugins to use, artifact repositories, code analysis config, etc @@ -150,6 +151,7 @@ dependencies { api group: 'org.terasology.nui', name: 'nui-reflect', version: '3.0.0' api group: 'org.terasology.nui', name: 'nui-gestalt7', version: '3.0.0' + api group: 'org.terasology.rust', name: 'core', version: rustCoreVersion // Wildcard dependency to catch any libs provided with the project (remote repo preferred instead) api fileTree(dir: 'libs', include: '*.jar') diff --git a/engine/src/main/java/org/terasology/engine/core/TerasologyEngine.java b/engine/src/main/java/org/terasology/engine/core/TerasologyEngine.java index 7e989e34910..6e48cb76912 100644 --- a/engine/src/main/java/org/terasology/engine/core/TerasologyEngine.java +++ b/engine/src/main/java/org/terasology/engine/core/TerasologyEngine.java @@ -43,6 +43,7 @@ import org.terasology.engine.recording.RecordAndReplayUtils; import org.terasology.engine.registry.CoreRegistry; import org.terasology.engine.rendering.gltf.ByteBufferAsset; +import org.terasology.engine.rust.EngineKernel; import org.terasology.engine.version.TerasologyVersion; import org.terasology.engine.world.block.loader.BlockFamilyDefinition; import org.terasology.engine.world.block.loader.BlockFamilyDefinitionData; @@ -161,6 +162,7 @@ public TerasologyEngine(TimeSubsystem timeSubsystem, Collection rootContext.put(CharacterStateEventPositionMap.class, characterStateEventPositionMap); DirectionAndOriginPosRecorderList directionAndOriginPosRecorderList = new DirectionAndOriginPosRecorderList(); rootContext.put(DirectionAndOriginPosRecorderList.class, directionAndOriginPosRecorderList); + /* * We can't load the engine without core registry yet. * e.g. the statically created MaterialLoader needs the CoreRegistry to get the AssetManager. @@ -564,6 +566,7 @@ public void cleanup() { */ @Override public void shutdown() { + shutdownRequested = true; } diff --git a/engine/src/main/java/org/terasology/engine/core/modes/loadProcesses/InitialiseWorld.java b/engine/src/main/java/org/terasology/engine/core/modes/loadProcesses/InitialiseWorld.java index ca3928ca025..c1b637c598f 100644 --- a/engine/src/main/java/org/terasology/engine/core/modes/loadProcesses/InitialiseWorld.java +++ b/engine/src/main/java/org/terasology/engine/core/modes/loadProcesses/InitialiseWorld.java @@ -31,6 +31,7 @@ import org.terasology.engine.rendering.backdrop.BackdropProvider; import org.terasology.engine.rendering.backdrop.Skysphere; import org.terasology.engine.rendering.cameras.Camera; +import org.terasology.engine.rendering.world.DeferredRenderer; import org.terasology.engine.rendering.world.WorldRenderer; import org.terasology.engine.utilities.random.FastRandom; import org.terasology.engine.world.BlockEntityRegistry; @@ -159,8 +160,7 @@ public boolean step() { BackdropProvider backdropProvider = skysphere; context.put(BackdropProvider.class, backdropProvider); - RenderingSubsystemFactory engineSubsystemFactory = context.get(RenderingSubsystemFactory.class); - WorldRenderer worldRenderer = engineSubsystemFactory.createWorldRenderer(context); + WorldRenderer worldRenderer = new DeferredRenderer(context); context.put(WorldRenderer.class, worldRenderer); // TODO: These shouldn't be done here, nor so strongly tied to the world renderer diff --git a/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglDisplayDevice.java b/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglDisplayDevice.java index dda04be1427..73134f69486 100644 --- a/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglDisplayDevice.java +++ b/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglDisplayDevice.java @@ -46,17 +46,17 @@ public LwjglDisplayDevice(Context context) { @Override public boolean hasFocus() { - return GLFW.GLFW_TRUE == GLFW.glfwGetWindowAttrib(GLFW.glfwGetCurrentContext(), GLFW.GLFW_FOCUSED); + return GLFW.GLFW_TRUE == GLFW.glfwGetWindowAttrib(LwjglGraphics.primaryWindow, GLFW.GLFW_FOCUSED); } @Override public boolean isCloseRequested() { - return GLFW.glfwWindowShouldClose(GLFW.glfwGetCurrentContext()); + return GLFW.glfwWindowShouldClose(LwjglGraphics.primaryWindow); } @Override public boolean isFullscreen() { - return MemoryUtil.NULL != GLFW.glfwGetWindowMonitor(GLFW.glfwGetCurrentContext()); + return MemoryUtil.NULL != GLFW.glfwGetWindowMonitor(LwjglGraphics.primaryWindow); } @Override @@ -79,7 +79,7 @@ public void setDisplayModeSetting(DisplayModeSetting displayModeSetting) { } public void setDisplayModeSetting(DisplayModeSetting displayModeSetting, boolean resize) { - long window = GLFW.glfwGetCurrentContext(); +// long window = GLFW.glfwGetCurrentContext(); switch (displayModeSetting) { case FULLSCREEN: updateFullScreenDisplay(); @@ -88,21 +88,21 @@ public void setDisplayModeSetting(DisplayModeSetting displayModeSetting, boolean break; case WINDOWED_FULLSCREEN: GLFWVidMode vidMode = desktopResolution.get(); - GLFW.glfwSetWindowMonitor(window, + GLFW.glfwSetWindowMonitor(LwjglGraphics.primaryWindow, MemoryUtil.NULL, 0, 0, vidMode.width(), vidMode.height(), GLFW.GLFW_DONT_CARE); - GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE); + GLFW.glfwSetWindowAttrib(LwjglGraphics.primaryWindow, GLFW.GLFW_DECORATED, GLFW.GLFW_FALSE); config.setDisplayModeSetting(displayModeSetting); config.setWindowedFullscreen(true); break; case WINDOWED: - GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_DECORATED, GLFW.GLFW_TRUE); - GLFW.glfwSetWindowAttrib(window, GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE); - GLFW.glfwSetWindowMonitor(window, + GLFW.glfwSetWindowAttrib(LwjglGraphics.primaryWindow, GLFW.GLFW_DECORATED, GLFW.GLFW_TRUE); + GLFW.glfwSetWindowAttrib(LwjglGraphics.primaryWindow, GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE); + GLFW.glfwSetWindowMonitor(LwjglGraphics.primaryWindow, MemoryUtil.NULL, config.getWindowPosX(), config.getWindowPosY(), @@ -150,7 +150,7 @@ private void updateWindow() { if (isWindowDirty) { int[] windowWidth = new int[1]; int[] windowHeight = new int[1]; - GLFW.glfwGetWindowSize(GLFW.glfwGetCurrentContext(), windowWidth, windowHeight); + GLFW.glfwGetWindowSize(LwjglGraphics.primaryWindow, windowWidth, windowHeight); this.windowWidth = windowWidth[0]; this.windowHeight = windowHeight[0]; isWindowDirty = false; @@ -178,18 +178,18 @@ public boolean isHeadless() { @Override public void prepareToRender() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } @Override public DisplayDeviceInfo getInfo() { - LwjglGraphicsUtil.updateDisplayDeviceInfo(displayDeviceInfo); +// LwjglGraphicsUtil.updateDisplayDeviceInfo(displayDeviceInfo); return displayDeviceInfo; } public void update() { processMessages(); - GLFW.glfwSwapBuffers(GLFW.glfwGetCurrentContext()); +// GLFW.glfwSwapBuffers(GLFW.glfwGetCurrentContext()); isWindowDirty = true; } @@ -199,10 +199,10 @@ private void updateViewport() { } protected void updateViewport(int width, int height) { - glViewport(0, 0, width, height); +// glViewport(0, 0, width, height); //If the screen is minimized, resolution change is stopped to avoid the width and height of FBO being set to 0. - boolean isMinimized = GLFW.glfwGetWindowAttrib(GLFW.glfwGetCurrentContext(), GLFW.GLFW_ICONIFIED) == GLFW.GLFW_TRUE; + boolean isMinimized = GLFW.glfwGetWindowAttrib(LwjglGraphics.primaryWindow, GLFW.GLFW_ICONIFIED) == GLFW.GLFW_TRUE; int i = isMinimized ? 0 : 1; propertyChangeSupport.firePropertyChange(DISPLAY_RESOLUTION_CHANGE, i, 1); } @@ -217,9 +217,8 @@ private GLFWVidMode getFullScreenDisplayMode() { } private void updateFullScreenDisplay() { - long window = GLFW.glfwGetCurrentContext(); GLFWVidMode vidMode = getFullScreenDisplayMode(); - GLFW.glfwSetWindowMonitor(window, + GLFW.glfwSetWindowMonitor(LwjglGraphics.primaryWindow, GLFW.glfwGetPrimaryMonitor(), 0, 0, diff --git a/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphics.java b/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphics.java index e17063912c1..6e4d6d90877 100644 --- a/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphics.java +++ b/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphics.java @@ -5,7 +5,8 @@ import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import org.lwjgl.glfw.GLFWImage; -import org.lwjgl.opengl.GL43; +import org.lwjgl.glfw.GLFWNativeWin32; +import org.lwjgl.glfw.GLFWNativeX11; import org.lwjgl.system.MemoryUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,8 +18,31 @@ import org.terasology.engine.core.subsystem.DisplayDevice; import org.terasology.engine.rendering.ShaderManager; import org.terasology.engine.rendering.ShaderManagerLwjgl; -import org.terasology.engine.rendering.nui.internal.LwjglCanvasRenderer; +import org.terasology.engine.rendering.assets.animation.MeshAnimation; +import org.terasology.engine.rendering.assets.animation.MeshAnimationBundle; +import org.terasology.engine.rendering.assets.animation.MeshAnimationImpl; +import org.terasology.engine.rendering.assets.atlas.Atlas; +import org.terasology.engine.rendering.assets.font.Font; +import org.terasology.engine.rendering.assets.font.FontImpl; +import org.terasology.engine.rendering.assets.material.Material; +import org.terasology.engine.rendering.assets.mesh.Mesh; +import org.terasology.engine.rendering.assets.shader.Shader; +import org.terasology.engine.rendering.assets.skeletalmesh.SkeletalMesh; +import org.terasology.engine.rendering.assets.texture.PNGTextureFormat; +import org.terasology.engine.rendering.assets.texture.Texture; +import org.terasology.engine.rendering.assets.texture.TextureData; +import org.terasology.engine.rendering.assets.texture.subtexture.Subtexture; +import org.terasology.engine.rendering.nui.internal.WgpuCanvasRenderer; +import org.terasology.engine.rendering.opengl.GLSLMaterial; +import org.terasology.engine.rendering.opengl.GLSLShader; +import org.terasology.engine.rendering.opengl.OpenGLMesh; +import org.terasology.engine.rendering.opengl.OpenGLSkeletalMesh; +import org.terasology.engine.rendering.opengl.WgpuTexture; +import org.terasology.engine.rust.EngineKernel; import org.terasology.engine.utilities.OS; +import org.terasology.gestalt.assets.AssetType; +import org.terasology.gestalt.assets.ResourceUrn; +import org.terasology.gestalt.assets.module.ModuleAssetScanner; import org.terasology.gestalt.assets.module.ModuleAwareAssetTypeManager; import org.terasology.nui.canvas.CanvasRenderer; @@ -29,6 +53,10 @@ public class LwjglGraphics extends BaseLwjglSubsystem { private static final Logger logger = LoggerFactory.getLogger(LwjglGraphics.class); + // we don't use context so we need to + public static long primaryWindow = 0; + private EngineKernel kernel = null; + private Context context; private RenderingConfig config; @@ -55,70 +83,57 @@ public void initialise(GameEngine gameEngine, Context rootContext) { @Override public void registerCoreAssetTypes(ModuleAwareAssetTypeManager assetTypeManager) { - graphics.registerCoreAssetTypes(assetTypeManager); + + AssetType texture = assetTypeManager.createAssetType(Texture.class, + (ResourceUrn urn, AssetType assetType, TextureData data) -> { + WgpuTexture.TextureResources resources = new WgpuTexture.TextureResources( + data, + this.kernel.resource.createTexture(WgpuTexture.createDesc(data), data.getBuffers()[0]) + ); + + return new WgpuTexture(this.kernel, urn, assetType, resources); + }, "textures", "fonts"); + assetTypeManager.getAssetFileDataProducer(texture).addAssetFormat( + new PNGTextureFormat(Texture.FilterMode.NEAREST, path -> { + if (path.getPath().get(0).equals(ModuleAssetScanner.OVERRIDE_FOLDER)) { + return path.getPath().get(2).equals("textures"); + } else { + return path.getPath().get(1).equals("textures"); + } + })); + assetTypeManager.getAssetFileDataProducer(texture).addAssetFormat( + new PNGTextureFormat(Texture.FilterMode.LINEAR, path -> { + if (path.getPath().get(0).equals(ModuleAssetScanner.OVERRIDE_FOLDER)) { + return path.getPath().get(2).equals("fonts"); + } else { + return path.getPath().get(1).equals("fonts"); + } + })); + assetTypeManager.createAssetType(Font.class, + FontImpl::new, "fonts"); + + assetTypeManager.createAssetType(Shader.class, (urn, assetType, data) -> GLSLShader.create(urn, assetType, data, graphics), "shaders"); + assetTypeManager.createAssetType(Material.class, (urn, assetType, data) -> + GLSLMaterial.create(urn, graphics, assetType, data), + "materials"); + assetTypeManager.createAssetType(Mesh.class, (urn, assetType, data) -> OpenGLMesh.create(urn, assetType, data, graphics), + "mesh"); + assetTypeManager.createAssetType(SkeletalMesh.class, + (urn, assetType, data) -> + OpenGLSkeletalMesh.create(urn, assetType, data, graphics), + "skeletalMesh"); + assetTypeManager.createAssetType(MeshAnimation.class, MeshAnimationImpl::new, + "animations", "skeletalMesh"); + assetTypeManager.createAssetType(Atlas.class, Atlas::new, "atlas"); + assetTypeManager.createAssetType(MeshAnimationBundle.class, MeshAnimationBundle::new, + "skeletalMesh", "animations"); + assetTypeManager.createAssetType(Subtexture.class, Subtexture::new); } @Override public void postInitialise(Context rootContext) { graphics.registerRenderingSubsystem(context); - initGLFW(); - initWindow(); - initOpenGL(); - - context.put(ShaderManager.class, new ShaderManagerLwjgl()); - context.put(CanvasRenderer.class, new LwjglCanvasRenderer(context)); - } - - @Override - public void postUpdate(GameState currentState, float delta) { - graphics.processActions(); - - boolean gameWindowIsMinimized = GLFW.glfwGetWindowAttrib(GLFW.glfwGetCurrentContext(), GLFW.GLFW_ICONIFIED) == GLFW.GLFW_TRUE; - if (!gameWindowIsMinimized) { - currentState.render(); - } - - lwjglDisplay.update(); - int frameLimit = context.get(Config.class).getRendering().getFrameLimit(); - if (frameLimit > 0) { - Lwjgl2Sync.sync(frameLimit); - } - if (lwjglDisplay.isCloseRequested()) { - engine.shutdown(); - } - } - - @Override - public void preShutdown() { - long window = GLFW.glfwGetCurrentContext(); - if (window != MemoryUtil.NULL) { - boolean isVisible = GLFW.glfwGetWindowAttrib(window, GLFW.GLFW_VISIBLE) == GLFW.GLFW_TRUE; - boolean isFullScreen = lwjglDisplay.isFullscreen(); - if (!isFullScreen && isVisible) { - int[] xBuffer = new int[1]; - int[] yBuffer = new int[1]; - GLFW.glfwGetWindowPos(window, xBuffer, yBuffer); - int[] widthBuffer = new int[1]; - int[] heightBuffer = new int[1]; - GLFW.glfwGetWindowSize(window, widthBuffer, heightBuffer); - - if (widthBuffer[0] > 0 && heightBuffer[0] > 0 && xBuffer[0] > 0 && yBuffer[0] > 0) { - config.setWindowWidth(widthBuffer[0]); - config.setWindowHeight(heightBuffer[0]); - config.setWindowPosX(xBuffer[0]); - config.setWindowPosY(yBuffer[0]); - } - } - } - } - - @Override - public void shutdown() { - GLFW.glfwTerminate(); - } - - private void initGLFW() { if (!GLFW.glfwInit()) { throw new RuntimeException("Failed to initialize GLFW"); } @@ -128,33 +143,35 @@ private void initGLFW() { GLFW.glfwWindowHint(GLFW.GLFW_COCOA_GRAPHICS_SWITCHING, GLFW.GLFW_TRUE); GLFW.glfwWindowHint(GLFW.GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW.GLFW_FALSE); GLFW.glfwWindowHint(GLFW.GLFW_DEPTH_BITS, config.getPixelFormat()); - + GLFW.glfwWindowHint(GLFW.GLFW_CLIENT_API, GLFW.GLFW_NO_API); if (config.getDebug().isEnabled()) { GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_DEBUG_CONTEXT, GLFW.GLFW_TRUE); } - GLFW.glfwSetErrorCallback(new GLFWErrorCallback()); - } - - private void initWindow() { logger.info("Initializing display (if last line in log then likely the game crashed from an issue with your " + "video card)"); - // set opengl core profile to 3.3 - GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 3); - GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 3); - GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE); - long window = GLFW.glfwCreateWindow( config.getWindowWidth(), config.getWindowHeight(), "Terasology Alpha", 0, 0); - if (window == 0) { - throw new RuntimeException("Failed to create window"); + primaryWindow = window; + + EngineKernel.EngineKernelBuild builder = new EngineKernel.EngineKernelBuild(); + switch (GLFW.glfwGetPlatform()) { + case GLFW.GLFW_PLATFORM_X11: + builder.configureX11Window(GLFWNativeX11.glfwGetX11Window(window), GLFWNativeX11.glfwGetX11Display()); + break; + case GLFW.GLFW_PLATFORM_WIN32: + builder.configureWin32Window(GLFWNativeWin32.glfwGetWin32Window(window), GLFWNativeWin32.nglfwGetWin32Adapter(window)); + break; + default: + throw new RuntimeException("missing platform: " + GLFW.glfwGetPlatform()); } + this.kernel = new EngineKernel(builder); + context.put(EngineKernel.class, this.kernel); + this.kernel.resizeSurface(lwjglDisplay.getWidth(), lwjglDisplay.getHeight()); - GLFW.glfwMakeContextCurrent(window); - - if (!config.isVSync()) { - GLFW.glfwSwapInterval(0); + if (window == 0) { + throw new RuntimeException("Failed to create window"); } if (OS.get() != OS.MACOSX) { @@ -180,26 +197,65 @@ private void initWindow() { } lwjglDisplay.setDisplayModeSetting(config.getDisplayModeSetting(), false); - GLFW.glfwShowWindow(window); - } - - private void initOpenGL() { - logger.info("Initializing OpenGL"); - LwjglGraphicsUtil.checkOpenGL(); - GLFW.glfwSetFramebufferSizeCallback(GLFW.glfwGetCurrentContext(), new GLFWFramebufferSizeCallback() { + GLFW.glfwSetFramebufferSizeCallback(LwjglGraphics.primaryWindow, new GLFWFramebufferSizeCallback() { @Override public void invoke(long window, int width, int height) { lwjglDisplay.updateViewport(width, height); + kernel.resizeSurface(width, height); } }); - LwjglGraphicsUtil.initOpenGLParams(); - if (config.getDebug().isEnabled()) { - try { - GL43.glDebugMessageCallback(new DebugCallback(), MemoryUtil.NULL); - } catch (IllegalStateException e) { - logger.warn("Unable to specify DebugCallback to receive debugging messages from the GL."); + + context.put(ShaderManager.class, new ShaderManagerLwjgl()); + context.put(CanvasRenderer.class, new WgpuCanvasRenderer(context)); + } + + @Override + public void postUpdate(GameState currentState, float delta) { + graphics.processActions(); + + boolean gameWindowIsMinimized = GLFW.glfwGetWindowAttrib(LwjglGraphics.primaryWindow, GLFW.GLFW_ICONIFIED) == GLFW.GLFW_TRUE; + if (!gameWindowIsMinimized) { + this.kernel.cmdPrepare(); + currentState.render(); + this.kernel.cmdDispatch(); + } + + lwjglDisplay.update(); + int frameLimit = context.get(Config.class).getRendering().getFrameLimit(); + if (frameLimit > 0) { + Lwjgl2Sync.sync(frameLimit); + } + if (lwjglDisplay.isCloseRequested()) { + engine.shutdown(); + } + } + + @Override + public void preShutdown() { + if (primaryWindow != MemoryUtil.NULL) { + boolean isVisible = GLFW.glfwGetWindowAttrib(primaryWindow, GLFW.GLFW_VISIBLE) == GLFW.GLFW_TRUE; + boolean isFullScreen = lwjglDisplay.isFullscreen(); + if (!isFullScreen && isVisible) { + int[] xBuffer = new int[1]; + int[] yBuffer = new int[1]; + GLFW.glfwGetWindowPos(primaryWindow, xBuffer, yBuffer); + int[] widthBuffer = new int[1]; + int[] heightBuffer = new int[1]; + GLFW.glfwGetWindowSize(primaryWindow, widthBuffer, heightBuffer); + + if (widthBuffer[0] > 0 && heightBuffer[0] > 0 && xBuffer[0] > 0 && yBuffer[0] > 0) { + config.setWindowWidth(widthBuffer[0]); + config.setWindowHeight(heightBuffer[0]); + config.setWindowPosX(xBuffer[0]); + config.setWindowPosY(yBuffer[0]); + } } } } + + @Override + public void shutdown() { + GLFW.glfwTerminate(); + } } diff --git a/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphicsManager.java b/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphicsManager.java index b71b200f9fb..f8aab8b7cf5 100644 --- a/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphicsManager.java +++ b/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphicsManager.java @@ -4,47 +4,18 @@ import com.google.common.collect.Lists; import com.google.common.collect.Queues; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL12; import org.terasology.engine.context.Context; import org.terasology.engine.core.GameThread; import org.terasology.engine.core.subsystem.DisplayDeviceInfo; import org.terasology.engine.core.subsystem.RenderingSubsystemFactory; -import org.terasology.engine.rendering.assets.animation.MeshAnimation; -import org.terasology.engine.rendering.assets.animation.MeshAnimationBundle; -import org.terasology.engine.rendering.assets.animation.MeshAnimationImpl; -import org.terasology.engine.rendering.assets.atlas.Atlas; -import org.terasology.engine.rendering.assets.font.Font; -import org.terasology.engine.rendering.assets.font.FontImpl; -import org.terasology.engine.rendering.assets.material.Material; -import org.terasology.engine.rendering.assets.mesh.Mesh; -import org.terasology.engine.rendering.assets.shader.Shader; -import org.terasology.engine.rendering.assets.skeletalmesh.SkeletalMesh; -import org.terasology.engine.rendering.assets.texture.PNGTextureFormat; import org.terasology.engine.rendering.assets.texture.Texture; -import org.terasology.engine.rendering.assets.texture.TextureData; -import org.terasology.engine.rendering.assets.texture.subtexture.Subtexture; -import org.terasology.engine.rendering.opengl.GLSLMaterial; -import org.terasology.engine.rendering.opengl.GLSLShader; -import org.terasology.engine.rendering.opengl.OpenGLMesh; -import org.terasology.engine.rendering.opengl.OpenGLSkeletalMesh; -import org.terasology.engine.rendering.opengl.OpenGLTexture; -import org.terasology.gestalt.assets.AssetType; -import org.terasology.gestalt.assets.module.ModuleAssetScanner; -import org.terasology.gestalt.assets.module.ModuleAwareAssetTypeManager; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.BlockingDeque; import java.util.function.Consumer; -import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; -import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_S; -import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_T; -import static org.lwjgl.opengl.GL11.glBindTexture; import static org.lwjgl.opengl.GL11.glDeleteTextures; -import static org.lwjgl.opengl.GL11.glGenTextures; -import static org.lwjgl.opengl.GL11.glTexParameterf; public class LwjglGraphicsManager implements LwjglGraphicsProcessing { @@ -57,54 +28,11 @@ public void setThreadMode(ThreadMode threadMode) { this.threadMode = threadMode; } - public void registerCoreAssetTypes(ModuleAwareAssetTypeManager assetTypeManager) { - // cast lambdas explicitly to avoid inconsistent compiler behavior wrt. type inference - assetTypeManager.createAssetType(Font.class, - FontImpl::new, "fonts"); - AssetType texture = assetTypeManager.createAssetType(Texture.class, - (urn, assetType, data) -> (OpenGLTexture.create(urn, assetType, data, this)), "textures", "fonts"); - - assetTypeManager.getAssetFileDataProducer(texture).addAssetFormat( - new PNGTextureFormat(Texture.FilterMode.NEAREST, path -> { - if (path.getPath().get(0).equals(ModuleAssetScanner.OVERRIDE_FOLDER)) { - return path.getPath().get(2).equals("textures"); - } else { - return path.getPath().get(1).equals("textures"); - } - })); - assetTypeManager.getAssetFileDataProducer(texture).addAssetFormat( - new PNGTextureFormat(Texture.FilterMode.LINEAR, path -> { - if (path.getPath().get(0).equals(ModuleAssetScanner.OVERRIDE_FOLDER)) { - return path.getPath().get(2).equals("fonts"); - } else { - return path.getPath().get(1).equals("fonts"); - } - })); - assetTypeManager.createAssetType(Shader.class, (urn, assetType, data) -> GLSLShader.create(urn, assetType, data, this), "shaders"); - assetTypeManager.createAssetType(Material.class, (urn, assetType, data) -> - GLSLMaterial.create(urn, this, assetType, data), - "materials"); - assetTypeManager.createAssetType(Mesh.class, (urn, assetType, data) -> OpenGLMesh.create(urn, assetType, data, this), - "mesh"); - assetTypeManager.createAssetType(SkeletalMesh.class, - (urn, assetType, data) -> - OpenGLSkeletalMesh.create(urn, assetType, data, this), - "skeletalMesh"); - assetTypeManager.createAssetType(MeshAnimation.class, MeshAnimationImpl::new, - "animations", "skeletalMesh"); - assetTypeManager.createAssetType(Atlas.class, Atlas::new, "atlas"); - assetTypeManager.createAssetType(MeshAnimationBundle.class, MeshAnimationBundle::new, - "skeletalMesh", "animations"); - assetTypeManager.createAssetType(Subtexture.class, Subtexture::new); - } - public void registerRenderingSubsystem(Context context) { context.put(RenderingSubsystemFactory.class, new LwjglRenderingSubsystemFactory()); } public void processActions() { - LwjglGraphicsUtil.updateDisplayDeviceInfo(displayDeviceInfo); - if (!displayThreadActions.isEmpty()) { List actions = Lists.newArrayListWithExpectedSize(displayThreadActions.size()); displayThreadActions.drainTo(actions); @@ -122,68 +50,68 @@ public void asynchToDisplayThread(Runnable action) { public void createTexture3D(ByteBuffer alignedBuffer, Texture.WrapMode wrapMode, Texture.FilterMode filterMode, int size, Consumer idConsumer) { - asynchToDisplayThread(() -> { - int id = glGenTextures(); - reloadTexture3D(id, alignedBuffer, wrapMode, filterMode, size); - idConsumer.accept(id); - }); +// asynchToDisplayThread(() -> { +// int id = glGenTextures(); +// reloadTexture3D(id, alignedBuffer, wrapMode, filterMode, size); +// idConsumer.accept(id); +// }); } public void reloadTexture3D(int id, ByteBuffer alignedBuffer, Texture.WrapMode wrapMode, Texture.FilterMode filterMode, int size) { - asynchToDisplayThread(() -> { - glBindTexture(GL12.GL_TEXTURE_3D, id); - - glTexParameterf(GL12.GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, LwjglGraphicsUtil.getGLMode(wrapMode)); - glTexParameterf(GL12.GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, LwjglGraphicsUtil.getGLMode(wrapMode)); - glTexParameterf(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_WRAP_R, LwjglGraphicsUtil.getGLMode(wrapMode)); - - GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_MIN_FILTER, - LwjglGraphicsUtil.getGlMinFilter(filterMode)); - GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_MAG_FILTER, - LwjglGraphicsUtil.getGlMagFilter(filterMode)); - - GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); - GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_MAX_LEVEL, 0); - - GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, GL11.GL_RGBA, size, size, size, 0, GL11.GL_RGBA, - GL11.GL_UNSIGNED_BYTE, alignedBuffer); - }); +// asynchToDisplayThread(() -> { +// glBindTexture(GL12.GL_TEXTURE_3D, id); +// +// glTexParameterf(GL12.GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, LwjglGraphicsUtil.getGLMode(wrapMode)); +// glTexParameterf(GL12.GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, LwjglGraphicsUtil.getGLMode(wrapMode)); +// glTexParameterf(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_WRAP_R, LwjglGraphicsUtil.getGLMode(wrapMode)); +// +// GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_MIN_FILTER, +// LwjglGraphicsUtil.getGlMinFilter(filterMode)); +// GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_MAG_FILTER, +// LwjglGraphicsUtil.getGlMagFilter(filterMode)); +// +// GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); +// GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_MAX_LEVEL, 0); +// +// GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0, GL11.GL_RGBA, size, size, size, 0, GL11.GL_RGBA, +// GL11.GL_UNSIGNED_BYTE, alignedBuffer); +// }); } public void createTexture2D(ByteBuffer[] buffers, Texture.WrapMode wrapMode, Texture.FilterMode filterMode, int width, int height, Consumer idConsumer) { - asynchToDisplayThread(() -> { - int id = glGenTextures(); - reloadTexture2D(id, buffers, wrapMode, filterMode, width, height); - idConsumer.accept(id); - }); +// asynchToDisplayThread(() -> { +// int id = glGenTextures(); +// reloadTexture2D(id, buffers, wrapMode, filterMode, width, height); +// idConsumer.accept(id); +// }); } public void reloadTexture2D(int id, ByteBuffer[] buffers, Texture.WrapMode wrapMode, Texture.FilterMode filterMode, int width, int height) { - asynchToDisplayThread(() -> { - glBindTexture(GL11.GL_TEXTURE_2D, id); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, LwjglGraphicsUtil.getGLMode(wrapMode)); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, LwjglGraphicsUtil.getGLMode(wrapMode)); - GL11.glTexParameteri(GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, - LwjglGraphicsUtil.getGlMinFilter(filterMode)); - GL11.glTexParameteri(GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, - LwjglGraphicsUtil.getGlMagFilter(filterMode)); - GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, buffers.length - 1); - - if (buffers.length > 0) { - for (int i = 0; i < buffers.length; i++) { - GL11.glTexImage2D(GL11.GL_TEXTURE_2D, i, GL11.GL_RGBA, width >> i, height >> i, 0, GL11.GL_RGBA, - GL11.GL_UNSIGNED_BYTE, buffers[i]); - } - } else { - GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, - GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null); - } - }); +// asynchToDisplayThread(() -> { +// glBindTexture(GL11.GL_TEXTURE_2D, id); +// +// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, LwjglGraphicsUtil.getGLMode(wrapMode)); +// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, LwjglGraphicsUtil.getGLMode(wrapMode)); +// GL11.glTexParameteri(GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, +// LwjglGraphicsUtil.getGlMinFilter(filterMode)); +// GL11.glTexParameteri(GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, +// LwjglGraphicsUtil.getGlMagFilter(filterMode)); +// GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); +// GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, buffers.length - 1); +// +// if (buffers.length > 0) { +// for (int i = 0; i < buffers.length; i++) { +// GL11.glTexImage2D(GL11.GL_TEXTURE_2D, i, GL11.GL_RGBA, width >> i, height >> i, 0, GL11.GL_RGBA, +// GL11.GL_UNSIGNED_BYTE, buffers[i]); +// } +// } else { +// GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, +// GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null); +// } +// }); } public void disposeTexture(int id) { diff --git a/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphicsUtil.java b/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphicsUtil.java index 29c514ffebd..025214d23f8 100644 --- a/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphicsUtil.java +++ b/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglGraphicsUtil.java @@ -97,71 +97,4 @@ public static GLFWImage convertToGLFWFormat(BufferedImage image) { return result; } - public static void initOpenGLParams() { - glEnable(GL_CULL_FACE); - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - } - - public static void checkOpenGL() { - GLCapabilities capabilities = GL.createCapabilities(); - boolean[] requiredCapabilities = { - capabilities.OpenGL12, - capabilities.OpenGL14, - capabilities.OpenGL15, - capabilities.OpenGL20, - capabilities.OpenGL21, - capabilities.OpenGL30, - capabilities.OpenGL32, - capabilities.OpenGL33, - }; - - String[] capabilityNames = { - "OpenGL12", - "OpenGL14", - "OpenGL15", - "OpenGL20", - "OpenGL21", - "OpenGL30", - "OpenGL32", - "OpenGL33", - }; - - boolean canRunTheGame = true; - StringBuilder missingCapabilitiesMessage = new StringBuilder(); - - for (int index = 0; index < requiredCapabilities.length; index++) { - if (!requiredCapabilities[index]) { - missingCapabilitiesMessage.append(" - ").append(capabilityNames[index]).append("\n"); - canRunTheGame = false; - } - } - - if (!canRunTheGame) { - String completeErrorMessage = completeErrorMessage(missingCapabilitiesMessage.toString()); - throw new IllegalStateException(completeErrorMessage); - } - } - - private static String completeErrorMessage(String errorMessage) { - return "\n" + - "\nThe following OpenGL versions/extensions are required but are not supported by your GPU driver:\n" + - "\n" + - errorMessage + - "\n" + - "GPU Information:\n" + - "\n" + - " Vendor: " + GL11.glGetString(GL11.GL_VENDOR) + "\n" + - " Model: " + GL11.glGetString(GL11.GL_RENDERER) + "\n" + - " Driver: " + GL11.glGetString(GL11.GL_VERSION) + "\n" + - "\n" + - "Try updating the driver to the latest version available.\n" + - "If that fails you might need to use a different GPU (graphics card). Sorry!\n"; - } - - public static void updateDisplayDeviceInfo(DisplayDeviceInfo deviceInfo) { - deviceInfo.setOpenGlVendor(GL11.glGetString(GL11.GL_VENDOR)); - deviceInfo.setOpenGlVersion(GL11.glGetString(GL11.GL_VERSION)); - deviceInfo.setOpenGlRenderer(GL11.glGetString(GL11.GL_RENDERER)); - } } diff --git a/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglInput.java b/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglInput.java index 77245b4db04..bdb576dbc51 100644 --- a/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglInput.java +++ b/engine/src/main/java/org/terasology/engine/core/subsystem/lwjgl/LwjglInput.java @@ -51,9 +51,9 @@ private void initControls() { LwjglControllerDevice controllerDevice = new LwjglControllerDevice(controllerConfig); inputSystem.setControllerDevice(controllerDevice); - long window = GLFW.glfwGetCurrentContext(); - ((LwjglKeyboardDevice) inputSystem.getKeyboard()).registerToLwjglWindow(window); - ((LwjglMouseDevice) inputSystem.getMouseDevice()).registerToLwjglWindow(window); +// long window = GLFW.glfwGetCurrentContext(); + ((LwjglKeyboardDevice) inputSystem.getKeyboard()).registerToLwjglWindow(LwjglGraphics.primaryWindow); + ((LwjglMouseDevice) inputSystem.getMouseDevice()).registerToLwjglWindow(LwjglGraphics.primaryWindow); } private void updateInputConfig() { diff --git a/engine/src/main/java/org/terasology/engine/input/lwjgl/LwjglMouseDevice.java b/engine/src/main/java/org/terasology/engine/input/lwjgl/LwjglMouseDevice.java index 104baa6b228..03fdf21cef9 100644 --- a/engine/src/main/java/org/terasology/engine/input/lwjgl/LwjglMouseDevice.java +++ b/engine/src/main/java/org/terasology/engine/input/lwjgl/LwjglMouseDevice.java @@ -11,6 +11,7 @@ import org.lwjgl.BufferUtils; import org.lwjgl.glfw.GLFW; import org.terasology.engine.config.RenderingConfig; +import org.terasology.engine.core.subsystem.lwjgl.LwjglGraphics; import org.terasology.input.ButtonState; import org.terasology.input.InputType; import org.terasology.input.MouseInput; @@ -55,11 +56,11 @@ public void registerToLwjglWindow(long window) { @Override public void update() { - long window = GLFW.glfwGetCurrentContext(); +// long window = GLFW.glfwGetCurrentContext(); DoubleBuffer mouseX = BufferUtils.createDoubleBuffer(1); DoubleBuffer mouseY = BufferUtils.createDoubleBuffer(1); - GLFW.glfwGetCursorPos(window, mouseX, mouseY); + GLFW.glfwGetCursorPos(LwjglGraphics.primaryWindow, mouseX, mouseY); double x = mouseX.get(0); double y = mouseY.get(0); @@ -95,7 +96,7 @@ public boolean isVisible() { public void setGrabbed(boolean newGrabbed) { if (newGrabbed != mouseGrabbed) { mouseGrabbed = newGrabbed; - GLFW.glfwSetInputMode(GLFW.glfwGetCurrentContext(), GLFW.GLFW_CURSOR, + GLFW.glfwSetInputMode(LwjglGraphics.primaryWindow, GLFW.GLFW_CURSOR, newGrabbed ? GLFW.GLFW_CURSOR_DISABLED : GLFW.GLFW_CURSOR_NORMAL); } } diff --git a/engine/src/main/java/org/terasology/engine/rendering/dag/ModuleRendering.java b/engine/src/main/java/org/terasology/engine/rendering/dag/ModuleRendering.java index 2e0172e07fd..64c2baaf170 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/dag/ModuleRendering.java +++ b/engine/src/main/java/org/terasology/engine/rendering/dag/ModuleRendering.java @@ -26,7 +26,7 @@ public abstract class ModuleRendering { protected Context context; protected ModuleManager moduleManager; protected Name providingModule; - protected RenderGraph renderGraph; +// protected RenderGraph renderGraph; protected WorldRenderer worldRenderer; protected Boolean isEnabled = true; @@ -61,7 +61,7 @@ public void toggleEnabled() { } public void initialise() { - renderGraph = context.get(RenderGraph.class); +// renderGraph = context.get(RenderGraph.class); worldRenderer = context.get(WorldRenderer.class); } diff --git a/engine/src/main/java/org/terasology/engine/rendering/nui/internal/WgpuCanvasRenderer.java b/engine/src/main/java/org/terasology/engine/rendering/nui/internal/WgpuCanvasRenderer.java new file mode 100644 index 00000000000..417a445591a --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/rendering/nui/internal/WgpuCanvasRenderer.java @@ -0,0 +1,324 @@ +// Copyright 2023 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.rendering.nui.internal; + +import org.joml.Quaternionfc; +import org.joml.Vector2fc; +import org.joml.Vector2i; +import org.joml.Vector2ic; +import org.joml.Vector3fc; +import org.terasology.engine.context.Context; +import org.terasology.engine.core.subsystem.DisplayDevice; +import org.terasology.engine.rendering.assets.font.FontCharacter; +import org.terasology.engine.rendering.assets.material.Material; +import org.terasology.engine.rendering.assets.mesh.Mesh; +import org.terasology.engine.rendering.opengl.FrameBufferObject; +import org.terasology.engine.rendering.opengl.WgpuTexture; +import org.terasology.engine.rust.EngineKernel; +import org.terasology.gestalt.assets.ResourceUrn; +import org.terasology.joml.geom.Rectanglef; +import org.terasology.joml.geom.Rectanglei; +import org.terasology.math.TeraMath; +import org.terasology.nui.Border; +import org.terasology.nui.Colorc; +import org.terasology.nui.HorizontalAlign; +import org.terasology.nui.ScaleMode; +import org.terasology.nui.TextLineBuilder; +import org.terasology.nui.UITextureRegion; +import org.terasology.nui.VerticalAlign; +import org.terasology.nui.asset.font.Font; +import reactor.function.Consumer4; + +import java.util.List; +import java.util.Optional; + +public class WgpuCanvasRenderer implements TerasologyCanvasRenderer { + private static final float SHADOW_DEPTH = -2; + private static final int SHADOW_HORIZONTAL_OFFSET = 1; + private static final int SHADOW_VERTICAL_OFFSET = 1; + private static final int UNKNOWN = -1; + private DisplayDevice displayDevice; + private final EngineKernel kernel; + + public WgpuCanvasRenderer(Context context) { + this.kernel = context.get(EngineKernel.class); + this.displayDevice = context.get(DisplayDevice.class); + } + + @Override + public void preRender() { + + } + + @Override + public void postRender() { + + } + + @Override + public Vector2i getTargetSize() { + return new Vector2i(displayDevice.getWidth(), displayDevice.getHeight()); + } + + @Override + public void crop(Rectanglei cropRegion) { + kernel.ui.cmdUISetCrop(Optional.of(new Rectanglef(cropRegion.minX(), cropRegion.minY(), cropRegion.maxX(), cropRegion.maxY()))); + } + + @Override + public void drawLine(int sx, int sy, int ex, int ey, Colorc color) { + + } + + @Override + public void drawTexture(UITextureRegion texture, Colorc color, ScaleMode mode, Rectanglei absoluteRegion, float ux, float uy, float uw, float uh, float alpha) { + if (texture instanceof WgpuTexture) { + Vector2fc size = ((WgpuTexture) texture).getTeraTexture().getSize(); + + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(ux, uy , ux + uw, uy + uh), + new Rectanglef(absoluteRegion.minX(), absoluteRegion.minY(), absoluteRegion.maxX(), absoluteRegion.maxY())); + } + + } + + @Override + public void drawText(String text, Font font, HorizontalAlign hAlign, VerticalAlign vAlign, Rectanglei absoluteRegion, Colorc color, Colorc shadowColor, float alpha, boolean underlined) { + + List lines = TextLineBuilder.getLines(font, text, absoluteRegion.getSizeX()); + Vector2i offset = new Vector2i(absoluteRegion.minX, absoluteRegion.minY); + offset.y += vAlign.getOffset(lines.size() * font.getLineHeight(), absoluteRegion.lengthY()); + + org.terasology.engine.rendering.assets.font.Font + fnt = (org.terasology.engine.rendering.assets.font.Font) font; + int y = 0; + + for (String line : lines) { + int w = font.getWidth(line); + int x = hAlign.getOffset(w, absoluteRegion.getSizeX()); + for (char c : line.toCharArray()) { + FontCharacter character = fnt.getCharacterData(c); + float top = y + character.getyOffset(); + float bottom = top + character.getHeight(); + float left = x + character.getxOffset(); + float right = left + character.getWidth(); + + float texTop = character.getY(); + float texBottom = texTop + character.getTexHeight(); + float texLeft = character.getX(); + float texRight = texLeft + character.getTexWidth(); + + if (shadowColor.a() != 0) { + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) character.getPage()).getTeraTexture(), + new Rectanglef(texLeft, texTop, texRight, texBottom), + new Rectanglef(left + offset.x, top + offset.y, right + offset.x, bottom + offset.y) + .translate(SHADOW_HORIZONTAL_OFFSET, SHADOW_VERTICAL_OFFSET), + shadowColor.rgba()); + } + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) character.getPage()).getTeraTexture(), + new Rectanglef(texLeft, texTop, texRight, texBottom), + new Rectanglef(left + offset.x, top + offset.y, right + offset.x, bottom + offset.y)); + x += character.getxAdvance(); + } + y += font.getLineHeight(); + } + } + + + @Override + public void drawTextureBordered(UITextureRegion texture, Rectanglei absoluteRegion, Border border, boolean tile, float ux, float uy, float uw, float uh, float alpha) { + Vector2i textureSize = new Vector2i(TeraMath.ceilToInt(texture.getWidth() * uw), + TeraMath.ceilToInt(texture.getHeight() * uh)); + Rectanglei region = new Rectanglei(absoluteRegion); + + if (texture instanceof WgpuTexture) { + Consumer4 cmdDrawTiles = (Rectanglei drawRegion, Rectanglef subDrawRegion, Vector2i tileSize, + Rectanglef subTextureRegion) -> { + int tileW = tileSize.x; + int tileH = tileSize.y; + int horizTiles = TeraMath.fastAbs((drawRegion.getSizeX() - 1) / tileW) + 1; + int vertTiles = TeraMath.fastAbs((drawRegion.getSizeY() - 1) / tileH) + 1; + + int offsetX = (drawRegion.getSizeX() - horizTiles * tileW) / 2; + int offsetY = (drawRegion.getSizeY() - vertTiles * tileH) / 2; + + for (int tileY = 0; tileY < vertTiles; tileY++) { + for (int tileX = 0; tileX < horizTiles; tileX++) { + int left = offsetX + tileW * tileX; + int top = offsetY + tileH * tileY; + + float vertLeft = + subDrawRegion.minX + subDrawRegion.getSizeX() * Math.max((float) left / drawRegion.getSizeX(), 0); + float vertTop = + subDrawRegion.minY + subDrawRegion.getSizeY() * Math.max((float) top / drawRegion.getSizeY(), 0); + float vertRight = + subDrawRegion.minX + subDrawRegion.getSizeX() * Math.min((float) (left + tileW) / drawRegion.getSizeX(), 1); + float vertBottom = + subDrawRegion.minY + subDrawRegion.getSizeY() * Math.min((float) (top + tileH) / drawRegion.getSizeY(), 1); + float texCoordLeft = + subTextureRegion.minX + subTextureRegion.getSizeX() * (Math.max(left, 0) - left) / tileW; + float texCoordTop = + subTextureRegion.minY + subTextureRegion.getSizeY() * (Math.max(top, 0) - top) / tileH; + float texCoordRight = subTextureRegion.minX + subTextureRegion.getSizeX() * (Math.min(left + tileW, + drawRegion.getSizeX()) - left) / tileW; + float texCoordBottom = subTextureRegion.minY + subTextureRegion.getSizeY() * (Math.min(top + tileH, + drawRegion.getSizeY()) - top) / tileH; + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(texCoordLeft, texCoordTop, texCoordRight, texCoordBottom), + new Rectanglef(vertLeft, vertTop, vertRight, vertBottom)); + } + } + }; + + float topTex = (float) border.getTop() / textureSize.y; + float leftTex = (float) border.getLeft() / textureSize.x; + float bottomTex = 1f - (float) border.getBottom() / textureSize.y; + float rightTex = 1f - (float) border.getRight() / textureSize.x; + int centerHoriz = region.getSizeX() - border.getTotalWidth(); + int centerVert = region.getSizeY() - border.getTotalHeight(); + + float top = (float) border.getTop() / region.getSizeY(); + float left = (float) border.getLeft() / region.getSizeX(); + float bottom = 1f - (float) border.getBottom() / region.getSizeY(); + float right = 1f - (float) border.getRight() / region.getSizeX(); + + + if (border.getTop() != 0) { + if (border.getLeft() != 0) { + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(0, 0, leftTex, topTex), + new Rectanglef(0, 0, left, top) + .scale(region.getSizeX(), region.getSizeY()) + .translate(region.minX, region.minY)); + } + if (tile) { + cmdDrawTiles.accept(new Rectanglei(border.getLeft(), 0).setSize(centerHoriz, border.getTop()), + new Rectanglef(left, 0, right, top), + new Vector2i(textureSize.x - border.getTotalWidth(), border.getTop()), + new Rectanglef(leftTex, 0, rightTex, topTex)); + } else { + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(leftTex, 0, rightTex, topTex), + new Rectanglef(left, 0, right, top) + .scale(region.getSizeX(), region.getSizeY()) + .translate(region.minX, region.minY)); + } + if (border.getRight() != 0) { + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(rightTex, 0, 1, topTex), + new Rectanglef(right, 0, 1, top) + .scale(region.getSizeX(), region.getSizeY()) + .translate(region.minX, region.minY)); + } + } + + if (border.getLeft() != 0) { + if (tile) { + cmdDrawTiles.accept(new Rectanglei(0, border.getTop()).setSize(border.getLeft(), centerVert), + new Rectanglef(0, top, left, bottom), + new Vector2i(border.getLeft(), textureSize.y - border.getTotalHeight()), + new Rectanglef(0, topTex, leftTex, bottomTex)); + } else { + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(0, topTex, leftTex, bottomTex), + new Rectanglef( 0, top, left, bottom) + .scale(region.getSizeX(), region.getSizeY()) + .translate(region.minX, region.minY)); + } + } + + if (tile) { + cmdDrawTiles.accept(new Rectanglei(border.getLeft(), border.getTop()).setSize(centerHoriz, centerVert), + new Rectanglef(left, top, right, bottom), + new Vector2i(textureSize.x - border.getTotalWidth(), textureSize.y - border.getTotalHeight()), + new Rectanglef(leftTex, topTex, rightTex, bottomTex)); + } else { + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(leftTex, topTex, rightTex, bottomTex), + new Rectanglef(left, top, right, bottom) + .scale(region.getSizeX(), region.getSizeY()) + .translate(region.minX, region.minY)); + } + + if (border.getRight() != 0) { + if (tile) { + cmdDrawTiles.accept( + new Rectanglei(region.getSizeX() - border.getRight(), border.getTop()).setSize(border.getRight(), centerVert), + new Rectanglef(right, top, 1, bottom), + new Vector2i(border.getRight(), textureSize.y - border.getTotalHeight()), + new Rectanglef(rightTex, topTex, 1, bottomTex)); + } else { + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(rightTex, topTex, 1, bottomTex), + new Rectanglef(right, top, 1, bottom) + .scale(region.getSizeX(), region.getSizeY()) + .translate(region.minX, region.minY)); + } + } + + if (border.getBottom() != 0) { + if (border.getLeft() != 0) { + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(0, bottomTex, leftTex, 1), + new Rectanglef(0, bottom, left, 1) + .scale(region.getSizeX(), region.getSizeY()) + .translate(region.minX, region.minY)); + } + if (tile) { + cmdDrawTiles.accept( + new Rectanglei(border.getLeft(), region.getSizeY() - border.getBottom()).setSize(centerHoriz, border.getBottom()), + new Rectanglef(left, bottom, right, 1), + new Vector2i(textureSize.x - border.getTotalWidth(), border.getBottom()), + new Rectanglef(leftTex, bottomTex, rightTex, 1)); + } else { + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(leftTex, bottomTex, rightTex, 1), + new Rectanglef(left, bottom, right, 1) + .scale(region.getSizeX(), region.getSizeY()) + .translate(region.minX, region.minY)); + } + if (border.getRight() != 0) { + kernel.ui.cmdUIDrawTexture( + ((WgpuTexture) texture).getTeraTexture(), + new Rectanglef(rightTex, bottomTex, 1, 1), + new Rectanglef(right, bottom, 1, 1) + .scale(region.getSizeX(), region.getSizeY()) + .translate(region.minX, region.minY)); + } + } + } + } + + @Override + public void setUiScale(float uiScale) { + + } + + @Override + public FrameBufferObject getFBO(ResourceUrn urn, Vector2ic size) { + return null; + } + + @Override + public void drawMesh(Mesh mesh, Material material, Rectanglei drawRegion, Rectanglei cropRegion, Quaternionfc rotation, Vector3fc offset, float scale, float alpha) { + + } + + @Override + public void drawMaterialAt(Material material, Rectanglei drawRegion) { + + } +} diff --git a/engine/src/main/java/org/terasology/engine/rendering/opengl/GLSLMaterial.java b/engine/src/main/java/org/terasology/engine/rendering/opengl/GLSLMaterial.java index 3a65a2d0bc3..4c5645ffba2 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/opengl/GLSLMaterial.java +++ b/engine/src/main/java/org/terasology/engine/rendering/opengl/GLSLMaterial.java @@ -15,8 +15,6 @@ import org.joml.Matrix3fc; import org.joml.Matrix4fc; import org.lwjgl.BufferUtils; -import org.lwjgl.opengl.GL13; -import org.lwjgl.opengl.GL20; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.engine.core.GameThread; @@ -37,7 +35,6 @@ import java.util.Collections; import java.util.EnumSet; import java.util.Map; -import java.util.Set; public class GLSLMaterial extends BaseMaterial { @@ -50,7 +47,6 @@ public class GLSLMaterial extends BaseMaterial { private TIntObjectMap textureMap = new TIntObjectHashMap<>(); private GLSLShader shader; - private boolean activeFeaturesChanged; private TObjectIntMap uniformLocationMap = new TObjectIntHashMap<>(); private EnumSet activeFeatures = Sets.newEnumSet(Collections.emptyList(), ShaderProgramFeature.class); @@ -83,14 +79,14 @@ public static GLSLMaterial create(ResourceUrn urn, LwjglGraphicsProcessing graph @Override public void enable() { - if (shaderManager.getActiveMaterial() != this || activeFeaturesChanged) { - GL13.glActiveTexture(GL13.GL_TEXTURE0); - GL20.glUseProgram(getActiveShaderProgramId()); - - // Make sure the shader manager knows that this program is currently active - shaderManager.setActiveMaterial(this); - activeFeaturesChanged = false; - } +// if (shaderManager.getActiveMaterial() != this || activeFeaturesChanged) { +// GL13.glActiveTexture(GL13.GL_TEXTURE0); +// GL20.glUseProgram(getActiveShaderProgramId()); +// +// // Make sure the shader manager knows that this program is currently active +// shaderManager.setActiveMaterial(this); +// activeFeaturesChanged = false; +// } } @Override @@ -123,25 +119,25 @@ public boolean isRenderable() { @Override public void recompile() { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - GL20.glDeleteProgram(it.value()); - } - disposalAction.shaderPrograms.clear(); - uniformLocationMap.clear(); - bindMap.clear(); - - disposalAction.shaderPrograms.put(0, shader.linkShaderProgram(0)); - for (Set permutation : Sets.powerSet(shader.getAvailableFeatures())) { - int featureMask = ShaderProgramFeature.getBitset(permutation); - disposalAction.shaderPrograms.put(featureMask, shader.linkShaderProgram(featureMask)); - } - - //resolves #966 - //Some of the uniforms are not updated constantly between frames - //this function will rebind any uniforms that are not bound - rebindVariables(materialData); +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// GL20.glDeleteProgram(it.value()); +// } +// disposalAction.shaderPrograms.clear(); +// uniformLocationMap.clear(); +// bindMap.clear(); +// +// disposalAction.shaderPrograms.put(0, shader.linkShaderProgram(0)); +// for (Set permutation : Sets.powerSet(shader.getAvailableFeatures())) { +// int featureMask = ShaderProgramFeature.getBitset(permutation); +// disposalAction.shaderPrograms.put(featureMask, shader.linkShaderProgram(featureMask)); +// } +// +// //resolves #966 +// //Some of the uniforms are not updated constantly between frames +// //this function will rebind any uniforms that are not bound +// rebindVariables(materialData); } @Override @@ -226,7 +222,7 @@ public void activateFeature(ShaderProgramFeature feature) { if (shader.getAvailableFeatures().contains(feature)) { activeFeatures.add(feature); activeFeaturesMask = ShaderProgramFeature.getBitset(activeFeatures); - activeFeaturesChanged = true; +// activeFeaturesChanged = true; } else { logger.error("Attempt to activate unsupported feature {} for material {} using shader {}", feature, getUrn(), shader.getUrn()); } @@ -236,7 +232,7 @@ public void activateFeature(ShaderProgramFeature feature) { public void deactivateFeature(ShaderProgramFeature feature) { if (activeFeatures.remove(feature)) { activeFeaturesMask = ShaderProgramFeature.getBitset(activeFeatures); - activeFeaturesChanged = true; +// activeFeaturesChanged = true; } } @@ -252,353 +248,341 @@ public boolean supportsFeature(ShaderProgramFeature feature) { @Override public void setFloat(String desc, float f, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniform1f(id, f); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniform1f(id, f); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniform1f(id, f); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniform1f(id, f); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setFloat1(String desc, FloatBuffer buffer, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniform1fv(id, buffer); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniform1fv(id, buffer); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniform1fv(id, buffer); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniform1fv(id, buffer); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setFloat2(String desc, float f1, float f2, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniform2f(id, f1, f2); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniform2f(id, f1, f2); - } - - restoreStateAfterUniformsSet(); - } +// return; +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniform2f(id, f1, f2); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniform2f(id, f1, f2); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setFloat2(String desc, FloatBuffer buffer, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniform2fv(id, buffer); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniform2fv(id, buffer); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniform2fv(id, buffer); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniform2fv(id, buffer); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setFloat3(String desc, float f1, float f2, float f3, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniform3f(id, f1, f2, f3); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniform3f(id, f1, f2, f3); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniform3f(id, f1, f2, f3); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniform3f(id, f1, f2, f3); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setFloat3(String desc, FloatBuffer buffer, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniform3fv(id, buffer); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniform3fv(id, buffer); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniform3fv(id, buffer); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniform3fv(id, buffer); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setFloat4(String desc, float f1, float f2, float f3, float f4, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniform4f(id, f1, f2, f3, f4); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniform4f(id, f1, f2, f3, f4); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniform4f(id, f1, f2, f3, f4); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniform4f(id, f1, f2, f3, f4); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setFloat4(String desc, FloatBuffer buffer, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniform4fv(id, buffer); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniform4fv(id, buffer); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniform4fv(id, buffer); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniform4fv(id, buffer); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setInt(String desc, int i, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniform1i(id, i); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniform1i(id, i); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniform1i(id, i); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniform1i(id, i); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setBoolean(String desc, boolean value, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniform1i(id, value ? 1 : 0); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniform1i(id, value ? 1 : 0); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniform1i(id, value ? 1 : 0); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniform1i(id, value ? 1 : 0); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setMatrix3(String desc, Matrix3fc value, boolean currentOnly) { - if (isDisposed()) { - return; - } - matrixBuffer.rewind(); - value.get(matrixBuffer); - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniformMatrix3fv(id, false, matrixBuffer); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniformMatrix3fv(id, false, matrixBuffer); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// matrixBuffer.rewind(); +// value.get(matrixBuffer); +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniformMatrix3fv(id, false, matrixBuffer); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniformMatrix3fv(id, false, matrixBuffer); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setMatrix3(String desc, FloatBuffer value, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniformMatrix3fv(id, false, value); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniformMatrix3fv(id, false, value); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniformMatrix3fv(id, false, value); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniformMatrix3fv(id, false, value); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setMatrix4(String desc, Matrix4fc value, boolean currentOnly) { - if (isDisposed()) { - return; - } - matrixBuffer.rewind(); - value.get(matrixBuffer); - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniformMatrix4fv(id, false, matrixBuffer); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniformMatrix4fv(id, false, matrixBuffer); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// matrixBuffer.rewind(); +// value.get(matrixBuffer); +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniformMatrix4fv(id, false, matrixBuffer); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniformMatrix4fv(id, false, matrixBuffer); +// } +// +// restoreStateAfterUniformsSet(); +// } } @Override public void setMatrix4(String desc, FloatBuffer value, boolean currentOnly) { - if (isDisposed()) { - return; - } - if (currentOnly) { - enable(); - int id = getUniformLocation(getActiveShaderProgramId(), desc); - GL20.glUniformMatrix4fv(id, false, value); - } else { - TIntIntIterator it = disposalAction.shaderPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - - GL20.glUseProgram(it.value()); - int id = getUniformLocation(it.value(), desc); - GL20.glUniformMatrix4fv(id, false, value); - } - - restoreStateAfterUniformsSet(); - } +// if (isDisposed()) { +// return; +// } +// if (currentOnly) { +// enable(); +// int id = getUniformLocation(getActiveShaderProgramId(), desc); +// GL20.glUniformMatrix4fv(id, false, value); +// } else { +// TIntIntIterator it = disposalAction.shaderPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// +// GL20.glUseProgram(it.value()); +// int id = getUniformLocation(it.value(), desc); +// GL20.glUniformMatrix4fv(id, false, value); +// } +// +// restoreStateAfterUniformsSet(); +// } } private int getActiveShaderProgramId() { return disposalAction.shaderPrograms.get(activeFeaturesMask); } - private int getUniformLocation(int activeShaderProgramId, String desc) { - UniformId id = new UniformId(activeShaderProgramId, desc); - - if (uniformLocationMap.containsKey(id)) { - return uniformLocationMap.get(id); - } - - int loc = GL20.glGetUniformLocation(activeShaderProgramId, desc); - uniformLocationMap.put(id, loc); - - return loc; - } - private void restoreStateAfterUniformsSet() { - if (shaderManager.getActiveMaterial() == this) { - GL20.glUseProgram(getActiveShaderProgramId()); - } else { - enable(); - } +// if (shaderManager.getActiveMaterial() == this) { +// GL20.glUseProgram(getActiveShaderProgramId()); +// } else { +// enable(); +// } } private static final class UniformId { @@ -647,22 +631,22 @@ private static class DisposalAction implements DisposableResource { @Override public void close() { - try { - GameThread.synch(() -> { - logger.debug("Disposing material {}.", urn); - final TIntIntMap deletedPrograms = new TIntIntHashMap(shaderPrograms); - graphicsProcessing.asynchToDisplayThread(() -> { - TIntIntIterator it = deletedPrograms.iterator(); - while (it.hasNext()) { - it.advance(); - GL20.glDeleteProgram(it.value()); - } - }); - shaderPrograms.clear(); - }); - } catch (InterruptedException e) { - logger.error("Failed to dispose {}", urn, e); - } +// try { +// GameThread.synch(() -> { +// logger.debug("Disposing material {}.", urn); +// final TIntIntMap deletedPrograms = new TIntIntHashMap(shaderPrograms); +// graphicsProcessing.asynchToDisplayThread(() -> { +// TIntIntIterator it = deletedPrograms.iterator(); +// while (it.hasNext()) { +// it.advance(); +// GL20.glDeleteProgram(it.value()); +// } +// }); +// shaderPrograms.clear(); +// }); +// } catch (InterruptedException e) { +// logger.error("Failed to dispose {}", urn, e); +// } } } } diff --git a/engine/src/main/java/org/terasology/engine/rendering/opengl/GLSLShader.java b/engine/src/main/java/org/terasology/engine/rendering/opengl/GLSLShader.java index c29a538c519..53911864a36 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/opengl/GLSLShader.java +++ b/engine/src/main/java/org/terasology/engine/rendering/opengl/GLSLShader.java @@ -116,25 +116,9 @@ Set getAvailableFeatures() { return availableFeatures; } - // made package-private after CheckStyle suggestion - int linkShaderProgram(int featureHash) { - int shaderProgram = GL20.glCreateProgram(); - - GL20.glAttachShader(shaderProgram, disposalAction.fragmentPrograms.get(featureHash)); - GL20.glAttachShader(shaderProgram, disposalAction.vertexPrograms.get(featureHash)); - if (shaderProgramBase.getGeometryProgram() != null) { - GL20.glAttachShader(shaderProgram, disposalAction.geometryPrograms.get(featureHash)); - } - GL20.glLinkProgram(shaderProgram); - GL20.glValidateProgram(shaderProgram); - return shaderProgram; - } @Override public void recompile() { - graphicsProcessing.asynchToDisplayThread(() -> { - registerAllShaderPermutations(); - }); } @@ -257,29 +241,6 @@ private void updateAvailableFeatures() { } } - /** - * Compiles all combination of available features and stores them in two maps for - * lookup based on a unique hash of features. - */ - private void registerAllShaderPermutations() { - Set> allPermutations = Sets.powerSet(availableFeatures); - - for (Set permutation : allPermutations) { - int featureHash = ShaderProgramFeature.getBitset(permutation); - - int fragShaderId = compileShader(GL20.GL_FRAGMENT_SHADER, permutation); - int vertShaderId = compileShader(GL20.GL_VERTEX_SHADER, permutation); - if (shaderProgramBase.getGeometryProgram() != null) { - int geomShaderId = compileShader(GL32.GL_GEOMETRY_SHADER, permutation); - disposalAction.geometryPrograms.put(featureHash, geomShaderId); - } - - disposalAction.fragmentPrograms.put(featureHash, fragShaderId); - disposalAction.vertexPrograms.put(featureHash, vertShaderId); - } - - logger.debug("Compiled {} permutations for {}.", allPermutations.size(), getUrn()); - } private String assembleShader(int type, Set features) { StringBuilder shader = createShaderBuilder(); @@ -365,19 +326,20 @@ protected void doReload(ShaderData data) { try { GameThread.synch(() -> { logger.debug("Recompiling shader {}.", getUrn()); - - disposalAction.disposeData(); - shaderProgramBase = data; - parameters.clear(); - for (ShaderParameterMetadata metadata : shaderProgramBase.getParameterMetadata()) { - parameters.put(metadata.getName(), metadata); - } - updateAvailableFeatures(); - try { - registerAllShaderPermutations(); - } catch (RuntimeException e) { - logger.warn(e.getMessage()); - } + //TODO implement shader +// +// disposalAction.disposeData(); +// shaderProgramBase = data; +// parameters.clear(); +// for (ShaderParameterMetadata metadata : shaderProgramBase.getParameterMetadata()) { +// parameters.put(metadata.getName(), metadata); +// } +// updateAvailableFeatures(); +// try { +// registerAllShaderPermutations(); +// } catch (RuntimeException e) { +// logger.warn(e.getMessage()); +// } }); } catch (InterruptedException e) { logger.error("Failed to reload {}", getUrn(), e); diff --git a/engine/src/main/java/org/terasology/engine/rendering/opengl/LwjglFrameBufferObject.java b/engine/src/main/java/org/terasology/engine/rendering/opengl/LwjglFrameBufferObject.java index 19dc3916485..b8b81260ced 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/opengl/LwjglFrameBufferObject.java +++ b/engine/src/main/java/org/terasology/engine/rendering/opengl/LwjglFrameBufferObject.java @@ -12,7 +12,6 @@ import org.terasology.engine.core.subsystem.DisplayDevice; import org.terasology.engine.rendering.assets.texture.Texture; import org.terasology.engine.rendering.assets.texture.TextureData; -import org.terasology.engine.rendering.nui.internal.LwjglCanvasRenderer; import org.terasology.engine.utilities.Assets; import org.terasology.gestalt.assets.ResourceUrn; @@ -27,7 +26,6 @@ public class LwjglFrameBufferObject implements FrameBufferObject { private int frame; private Vector2ic size; private IntBuffer vp; - private LwjglCanvasRenderer canvasRenderer; private final Matrix4fStack transforms; private final Matrix4f projectionMatrix; diff --git a/engine/src/main/java/org/terasology/engine/rendering/opengl/OpenGLMesh.java b/engine/src/main/java/org/terasology/engine/rendering/opengl/OpenGLMesh.java index 0a52d1f7e67..e023d58b494 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/opengl/OpenGLMesh.java +++ b/engine/src/main/java/org/terasology/engine/rendering/opengl/OpenGLMesh.java @@ -92,26 +92,26 @@ public void render() { private void buildMesh(MeshData newData) { - if (this.disposalAction.vao == 0) { - this.disposalAction.vao = GL30.glGenVertexArrays(); - this.disposalAction.vbo = GL30.glGenBuffers(); - this.disposalAction.ebo = GL30.glGenBuffers(); - } - - allocationType = newData.allocationType(); - drawMode = newData.getMode(); - - GL30.glBindVertexArray(this.disposalAction.vao); - positions = newData.positions(); - this.state = buildVBO(this.disposalAction.vbo, allocationType, newData.vertexResources()); - - IndexResource indexResource = newData.indexResource(); - this.indexCount = indexResource.indices(); - GL30.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, this.disposalAction.ebo); - indexResource.writeBuffer((buffer) -> GL30.glBufferData(GL30.GL_ELEMENT_ARRAY_BUFFER, buffer, GL30.GL_STATIC_DRAW)); - - GL30.glBindVertexArray(0); - getBound(aabb); +// if (this.disposalAction.vao == 0) { +// this.disposalAction.vao = GL30.glGenVertexArrays(); +// this.disposalAction.vbo = GL30.glGenBuffers(); +// this.disposalAction.ebo = GL30.glGenBuffers(); +// } +// +// allocationType = newData.allocationType(); +// drawMode = newData.getMode(); +// +// GL30.glBindVertexArray(this.disposalAction.vao); +// positions = newData.positions(); +// this.state = buildVBO(this.disposalAction.vbo, allocationType, newData.vertexResources()); +// +// IndexResource indexResource = newData.indexResource(); +// this.indexCount = indexResource.indices(); +// GL30.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, this.disposalAction.ebo); +// indexResource.writeBuffer((buffer) -> GL30.glBufferData(GL30.GL_ELEMENT_ARRAY_BUFFER, buffer, GL30.GL_STATIC_DRAW)); +// +// GL30.glBindVertexArray(0); +// getBound(aabb); } private static class DisposalAction implements DisposableResource { @@ -127,18 +127,18 @@ private static class DisposalAction implements DisposableResource { } public void dispose() { - if (vao != 0) { - GL30.glDeleteVertexArrays(vao); - } - if (vbo != 0) { - GL30.glDeleteBuffers(vbo); - } - if (ebo != 0) { - GL30.glDeleteBuffers(ebo); - } - vao = 0; - vbo = 0; - ebo = 0; +// if (vao != 0) { +// GL30.glDeleteVertexArrays(vao); +// } +// if (vbo != 0) { +// GL30.glDeleteBuffers(vbo); +// } +// if (ebo != 0) { +// GL30.glDeleteBuffers(ebo); +// } +// vao = 0; +// vbo = 0; +// ebo = 0; } @Override diff --git a/engine/src/main/java/org/terasology/engine/rendering/opengl/OpenGLTexture.java b/engine/src/main/java/org/terasology/engine/rendering/opengl/OpenGLTexture.java deleted file mode 100644 index 13a89423715..00000000000 --- a/engine/src/main/java/org/terasology/engine/rendering/opengl/OpenGLTexture.java +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 -package org.terasology.engine.rendering.opengl; - -import com.google.common.collect.Lists; -import org.joml.Vector2i; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.terasology.gestalt.assets.AssetType; -import org.terasology.gestalt.assets.DisposableResource; -import org.terasology.gestalt.assets.ResourceUrn; -import org.terasology.engine.core.subsystem.lwjgl.LwjglGraphicsManager; -import org.terasology.joml.geom.Rectanglef; -import org.terasology.joml.geom.Rectanglei; -import org.terasology.engine.rendering.assets.texture.Texture; -import org.terasology.engine.rendering.assets.texture.TextureData; - -import java.nio.ByteBuffer; -import java.util.List; - -public class OpenGLTexture extends Texture { - - private static final Logger logger = LoggerFactory.getLogger(OpenGLTexture.class); - - private final TextureResources resources; - - public OpenGLTexture(ResourceUrn urn, AssetType assetType, TextureData data, TextureResources textureResources) { - super(urn, assetType, textureResources); - this.resources = textureResources; - reload(data); - } - - public static OpenGLTexture create(ResourceUrn urn, AssetType assetType, TextureData data, LwjglGraphicsManager graphicsManager) { - return new OpenGLTexture(urn, assetType, data, new TextureResources(graphicsManager)); - } - - - public void setId(int id) { - resources.id = id; - } - - @Override - protected void doReload(TextureData data) { - switch (data.getType()) { - // TODO: reconsider how 3D textures handled (probably separate asset implementation with common interface? - case TEXTURE3D: - if (data.getWidth() % data.getHeight() != 0 || data.getWidth() / data.getHeight() != data.getHeight()) { - throw new RuntimeException("3D texture must be cubic (height^3) - width must thus be a multiple of height"); - } - int size = data.getHeight(); - - final int byteLength = 4 * 16 * 16 * 16; - final int strideX = 16 * 4; - final int strideY = 16 * 16 * 4; - final int strideZ = 4; - - ByteBuffer alignedBuffer = ByteBuffer.allocateDirect(byteLength); - for (int x = 0; x < size; x++) { - for (int y = 0; y < size; y++) { - for (int z = 0; z < size; z++) { - final int index = x * strideX + z * strideZ + strideY * y; - - alignedBuffer.put(data.getBuffers()[0].get(index)); - alignedBuffer.put(data.getBuffers()[0].get(index + 1)); - alignedBuffer.put(data.getBuffers()[0].get(index + 2)); - alignedBuffer.put(data.getBuffers()[0].get(index + 3)); - } - } - } - alignedBuffer.flip(); - - resources.loadedTextureInfo = new LoadedTextureInfo(size, size, size, data); - - if (resources.id == 0) { - resources.graphicsManager.createTexture3D(alignedBuffer, getWrapMode(), getFilterMode(), - size, (newId) -> { - synchronized (this) { - if (resources.id != 0) { - resources.graphicsManager.disposeTexture(resources.id); - } - if (isDisposed()) { - resources.graphicsManager.disposeTexture(newId); - } else { - resources.id = newId; - logger.debug("Bound texture '{}' - {}", getUrn(), resources.id); - } - } - }); - } else { - resources.graphicsManager.reloadTexture3D(resources.id, alignedBuffer, getWrapMode(), getFilterMode(), size); - } - break; - default: - int width = data.getWidth(); - int height = data.getHeight(); - resources.loadedTextureInfo = new LoadedTextureInfo(width, height, 1, data); - if (resources.id == 0) { - resources.graphicsManager.createTexture2D(data.getBuffers(), getWrapMode(), getFilterMode(), width, height, (newId) -> { - synchronized (this) { - if (resources.id != 0) { - resources.graphicsManager.disposeTexture(resources.id); - } - if (isDisposed()) { - resources.graphicsManager.disposeTexture(newId); - } else { - resources.id = newId; - logger.debug("Bound texture '{}' - {}", getUrn(), resources.id); - } - } - }); - } else { - resources.graphicsManager.reloadTexture2D(resources.id, data.getBuffers(), getWrapMode(), getFilterMode(), width, height); - } - break; - } - } - - @Override - public int getId() { - return resources.id; - } - - @Override - public int getDepth() { - if (resources.loadedTextureInfo != null) { - return resources.loadedTextureInfo.getDepth(); - } - return 0; - } - - @Override - public int getWidth() { - if (resources.loadedTextureInfo != null) { - return resources.loadedTextureInfo.getWidth(); - } - return 0; - } - - @Override - public int getHeight() { - if (resources.loadedTextureInfo != null) { - return resources.loadedTextureInfo.getHeight(); - } - return 0; - } - - @Override - public Vector2i size() { - return new Vector2i(getWidth(), getHeight()); - } - - @Override - public Texture.WrapMode getWrapMode() { - return resources.loadedTextureInfo.getWrapMode(); - } - - @Override - public FilterMode getFilterMode() { - return resources.loadedTextureInfo.getFilterMode(); - } - - @Override - public TextureData getData() { - return new TextureData(resources.loadedTextureInfo.getTextureData()); - } - - @Override - public Texture getTexture() { - return this; - } - - @Override - public Rectanglef getRegion() { - return new Rectanglef(FULL_TEXTURE_REGION); // object is not guarded - } - - @Override - public Rectanglei getPixelRegion() { - return new Rectanglei(0, 0, getWidth(), getHeight()); - } - - @Override - public synchronized void subscribeToDisposal(DisposableResource subscriber) { - resources.disposalSubscribers.add(subscriber); - } - - @Override - public synchronized void unsubscribeToDisposal(DisposableResource subscriber) { - resources.disposalSubscribers.remove(subscriber); - } - - @Override - public boolean isLoaded() { - return resources.id != 0; - } - - private static class LoadedTextureInfo { - private final int width; - private final int height; - private final int depth; - private final Texture.WrapMode wrapMode; - private final Texture.FilterMode filterMode; - private final TextureData textureData; - - LoadedTextureInfo(int width, int height, int depth, TextureData data) { - this.width = width; - this.height = height; - this.depth = depth; - this.wrapMode = data.getWrapMode(); - this.filterMode = data.getFilterMode(); - this.textureData = data; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public int getDepth() { - return depth; - } - - public Texture.WrapMode getWrapMode() { - return wrapMode; - } - - public Texture.FilterMode getFilterMode() { - return filterMode; - } - - public TextureData getTextureData() { - return textureData; - } - } - - private static class TextureResources implements DisposableResource { - - private final LwjglGraphicsManager graphicsManager; - private volatile int id; - private volatile LoadedTextureInfo loadedTextureInfo; - - private final List disposalSubscribers = Lists.newArrayList(); - - TextureResources(LwjglGraphicsManager graphicsManager) { - this.graphicsManager = graphicsManager; - } - - - @Override - public void close() { - if (loadedTextureInfo != null) { - disposalSubscribers.forEach(DisposableResource::close); - graphicsManager.disposeTexture(id); - loadedTextureInfo = null; - id = 0; - } - } - } -} diff --git a/engine/src/main/java/org/terasology/engine/rendering/opengl/WgpuTexture.java b/engine/src/main/java/org/terasology/engine/rendering/opengl/WgpuTexture.java new file mode 100644 index 00000000000..0e411602032 --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/rendering/opengl/WgpuTexture.java @@ -0,0 +1,135 @@ +// Copyright 2023 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.rendering.opengl; + +import com.google.common.collect.Lists; +import org.joml.Vector2i; +import org.terasology.engine.rendering.assets.texture.Texture; +import org.terasology.engine.rendering.assets.texture.TextureData; +import org.terasology.engine.rust.EngineKernel; +import org.terasology.engine.rust.TeraTexture; +import org.terasology.gestalt.assets.AssetType; +import org.terasology.gestalt.assets.DisposableResource; +import org.terasology.gestalt.assets.ResourceUrn; +import org.terasology.joml.geom.Rectanglef; +import org.terasology.joml.geom.Rectanglei; + +import java.util.List; + + +public class WgpuTexture extends Texture { + private TextureResources resource; + private final EngineKernel kernel; + public static TeraTexture.TextureDesc createDesc(TextureData data) { + TeraTexture.TextureDesc desc = new TeraTexture.TextureDesc(); + desc.setFormat(TeraTexture.ImageFormat.R8G8B8A8_UNORM) + .setDim(TeraTexture.TextureDimension.DIM_2D) + .setWidth(data.getWidth()) + .setHeight(data.getHeight()) + .setLayers(1); + return desc; + } + + public WgpuTexture(EngineKernel kernel, ResourceUrn urn, AssetType assetType, TextureResources textureResources) { + super(urn, assetType); + this.kernel = kernel; + this.resource = textureResources; + } + + @Override + protected void doReload(TextureData data) { + this.resource = new TextureResources(data, this.kernel.resource.createTexture(createDesc(data), data.getBuffers()[0])); + } + + @Override + public WrapMode getWrapMode() { + return WrapMode.CLAMP; + } + + @Override + public FilterMode getFilterMode() { + return FilterMode.NEAREST; + } + + @Override + public TextureData getData() { + return this.resource.data; + } + + @Override + public int getId() { + return 0; + } + + @Override + public int getDepth() { + return 1; + } + + @Override + public boolean isLoaded() { + return true; + } + + @Override + public void subscribeToDisposal(DisposableResource subscriber) { + this.resource.disposalSubscribers.add(subscriber); + } + + @Override + public void unsubscribeToDisposal(DisposableResource subscriber) { + this.resource.disposalSubscribers.remove(subscriber); + } + + @Override + public Texture getTexture() { + return this; + } + + @Override + public Rectanglef getRegion() { + return new Rectanglef(FULL_TEXTURE_REGION); // object is not guarded + } + + @Override + public Rectanglei getPixelRegion() { + return new Rectanglei(0, 0, getWidth(), getHeight()); + } + + @Override + public int getWidth() { + return this.resource.data.getWidth(); + } + + @Override + public int getHeight() { + return this.resource.data.getHeight(); + } + + @Override + public Vector2i size() { + return new Vector2i(this.resource.data.getWidth(), this.resource.data.getHeight()); + } + + public TeraTexture getTeraTexture() { + return this.resource.texture; + } + + public static class TextureResources implements DisposableResource { + private final TeraTexture texture; + private final List disposalSubscribers = Lists.newArrayList(); + private final TextureData data; + + public TextureResources(TextureData data, TeraTexture texture) { + this.data = data; + this.texture = texture; + } + + @Override + public void close() { + disposalSubscribers.forEach(DisposableResource::close); + this.texture.dispose(); + } + } +} diff --git a/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMesh.java b/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMesh.java index c4a36c154af..0c05bcbd3a0 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMesh.java +++ b/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkMesh.java @@ -97,6 +97,14 @@ class VertexElements { public static final int COLOR_INDEX = 8; // vec4 public final VertexResource buffer; + +// public final VertexResource positionBuffer; +// public final VertexResource NormalBuffer; +// public final VertexResource uvBuffer; +// public final VertexResource ColroBuffer; +// public final VertexResource AttributeBuffer; + + public final IndexResource indices = new IndexResource(); public final VertexAttributeBinding position; diff --git a/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkTessellator.java b/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkTessellator.java index c6ca1835c58..a64404b8db2 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkTessellator.java +++ b/engine/src/main/java/org/terasology/engine/rendering/primitives/ChunkTessellator.java @@ -5,6 +5,7 @@ import com.google.common.base.Stopwatch; import org.joml.Vector3f; import org.terasology.engine.monitoring.PerformanceMonitor; +import org.terasology.engine.rust.EngineKernel; import org.terasology.engine.world.ChunkView; import org.terasology.engine.world.block.Block; import org.terasology.engine.world.chunks.Chunks; @@ -18,9 +19,10 @@ public final class ChunkTessellator { private static int statVertexArrayUpdateCount; + private final EngineKernel kernel; - public ChunkTessellator() { - + public ChunkTessellator(EngineKernel kernel) { + this.kernel = kernel; } public ChunkMesh generateMesh(ChunkView chunkView) { diff --git a/engine/src/main/java/org/terasology/engine/rendering/world/DeferredRenderer.java b/engine/src/main/java/org/terasology/engine/rendering/world/DeferredRenderer.java new file mode 100644 index 00000000000..9663aa86a52 --- /dev/null +++ b/engine/src/main/java/org/terasology/engine/rendering/world/DeferredRenderer.java @@ -0,0 +1,347 @@ +// Copyright 2023 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.engine.rendering.world; + +import org.joml.Vector3f; +import org.joml.Vector3ic; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terasology.engine.config.Config; +import org.terasology.engine.config.RenderingConfig; +import org.terasology.engine.context.Context; +import org.terasology.engine.core.GameEngine; +import org.terasology.engine.core.modes.StateMainMenu; +import org.terasology.engine.core.module.ModuleManager; +import org.terasology.engine.core.module.rendering.RenderingModuleRegistry; +import org.terasology.engine.core.subsystem.DisplayDevice; +import org.terasology.engine.logic.console.Console; +import org.terasology.engine.logic.console.commandSystem.MethodCommand; +import org.terasology.engine.logic.players.LocalPlayerSystem; +import org.terasology.engine.rendering.ShaderManager; +import org.terasology.engine.rendering.assets.material.Material; +import org.terasology.engine.rendering.backdrop.BackdropProvider; +import org.terasology.engine.rendering.cameras.Camera; +import org.terasology.engine.rendering.cameras.PerspectiveCamera; +import org.terasology.engine.rendering.dag.ModuleRendering; +import org.terasology.engine.rendering.dag.RenderGraph; +import org.terasology.engine.rendering.opengl.ScreenGrabber; +import org.terasology.engine.rendering.primitives.ChunkTessellator; +import org.terasology.engine.rendering.world.viewDistance.ViewDistance; +import org.terasology.engine.rust.EngineKernel; +import org.terasology.engine.utilities.Assets; +import org.terasology.engine.world.WorldProvider; +import org.terasology.engine.world.block.BlockManager; +import org.terasology.engine.world.chunks.ChunkProvider; +import org.terasology.engine.world.chunks.LodChunkProvider; +import org.terasology.engine.world.chunks.blockdata.ExtraBlockDataManager; +import org.terasology.engine.world.generator.ScalableWorldGenerator; +import org.terasology.engine.world.generator.WorldGenerator; +import org.terasology.math.TeraMath; + +import java.util.List; + +public class DeferredRenderer implements WorldRenderer { + /* + * Presumably, the eye height should be context.get(Config.class).getPlayer().getEyeHeight() above the ground plane. + * It's not, so for now, we use this factor to adjust for the disparity. + */ + private static final Logger logger = LoggerFactory.getLogger(DeferredRenderer.class); + private static final float GROUND_PLANE_HEIGHT_DISPARITY = -0.7f; + private RenderingModuleRegistry renderingModuleRegistry; + + private final RenderQueuesHelper renderQueues; + private final Context context; + private final BackdropProvider backdropProvider; + private final WorldProvider worldProvider; + private final RenderableWorld renderableWorld; + private final ShaderManager shaderManager; + private final EngineKernel kernel; + private final Camera playerCamera; + + private float timeSmoothedMainLightIntensity; + + private float millisecondsSinceRenderingStart; + private float secondsSinceLastFrame; + private int statChunkMeshEmpty; + private int statChunkNotReady; + private int statRenderedTriangles; + + private final RenderingConfig renderingConfig; + private final Console console; + + + /** + * Instantiates a WorldRenderer implementation. + *

+ * This particular implementation works as deferred shader. The scene is rendered multiple times per frame in a + * number of separate passes (each stored in GPU buffers) and the passes are combined throughout the rendering + * pipeline to calculate per-pixel lighting and other effects. + *

+ * Transparencies are handled through alpha rejection (i.e. ground plants) and alpha-based blending. An exception to + * this is water, which is handled separately to allow for reflections and refractions, if enabled. + *

+ * By the time it is fully instantiated this implementation is already connected to all the support objects it + * requires and is ready to render via the render(RenderingStage) method. + * + * @param context a context object, to obtain instances of classes such as the rendering config. + */ + public DeferredRenderer(Context context) { + this.context = context; + this.worldProvider = context.get(WorldProvider.class); + this.backdropProvider = context.get(BackdropProvider.class); + this.renderingConfig = context.get(Config.class).getRendering(); + this.shaderManager = context.get(ShaderManager.class); + this.kernel = context.get(EngineKernel.class); + playerCamera = new PerspectiveCamera(renderingConfig, context.get(DisplayDevice.class)); +// currentRenderingStage = RenderingStage.MONO; + // TODO: won't need localPlayerSystem here once camera is in the ES proper + LocalPlayerSystem localPlayerSystem = context.get(LocalPlayerSystem.class); + localPlayerSystem.setPlayerCamera(playerCamera); + + context.put(ChunkTessellator.class, new ChunkTessellator(this.kernel)); + + ChunkProvider chunkProvider = context.get(ChunkProvider.class); + ChunkTessellator chunkTessellator = context.get(ChunkTessellator.class); + BlockManager blockManager = context.get(BlockManager.class); + ExtraBlockDataManager extraDataManager = context.get(ExtraBlockDataManager.class); + Config config = context.get(Config.class); + + + WorldGenerator worldGenerator = context.get(WorldGenerator.class); + LodChunkProvider lodChunkProvider = null; + if (worldGenerator instanceof ScalableWorldGenerator) { + lodChunkProvider = new LodChunkProvider(chunkProvider, blockManager, extraDataManager, + (ScalableWorldGenerator) worldGenerator, chunkTessellator); + } + this.renderableWorld = new RenderableWorldImpl(this, lodChunkProvider, chunkProvider, chunkTessellator, worldProvider, config, playerCamera); + renderQueues = renderableWorld.getRenderQueues(); + + initRenderingSupport(); + + initRenderingModules(); + + console = context.get(Console.class); + MethodCommand.registerAvailable(this, console, context); + } + + private void initRenderingSupport() { + ScreenGrabber screenGrabber = new ScreenGrabber(context); + context.put(ScreenGrabber.class, screenGrabber); + +// displayResolutionDependentFbo = new DisplayResolutionDependentFbo( +// context.get(Config.class).getRendering(), screenGrabber, context.get(DisplayDevice.class)); +// context.put(DisplayResolutionDependentFbo.class, displayResolutionDependentFbo); + + shaderManager.initShaders(); + + context.put(WorldRenderer.class, this); + context.put(RenderQueuesHelper.class, renderQueues); + context.put(RenderableWorld.class, renderableWorld); + } + + + private void initRenderingModules() { + renderingModuleRegistry = context.get(RenderingModuleRegistry.class); + + // registry not populated by new ModuleRendering instances in UI, populate now + if (renderingModuleRegistry.getOrderedRenderingModules().isEmpty()) { + List renderingModules = renderingModuleRegistry.updateRenderingModulesOrder( + context.get(ModuleManager.class).getEnvironment(), context); + if (renderingModules.isEmpty()) { + GameEngine gameEngine = context.get(GameEngine.class); + gameEngine.changeState(new StateMainMenu("No rendering module loaded, unable to render. Try enabling " + + "CoreRendering.")); + } + } else { // registry populated by new ModuleRendering instances in UI + // Switch module's context from gamecreation subcontext to gamerunning context + renderingModuleRegistry.updateModulesContext(context); + } + + for (ModuleRendering moduleRenderingInstance : renderingModuleRegistry.getOrderedRenderingModules()) { + if (moduleRenderingInstance.isEnabled()) { + logger.info(String.format("\nInitialising rendering class %s from %s module.\n", + moduleRenderingInstance.getClass().getSimpleName(), + moduleRenderingInstance.getProvidingModule())); + moduleRenderingInstance.initialise(); + } + } + + requestTaskListRefresh(); + } + + + @Override + public float getSecondsSinceLastFrame() { + return secondsSinceLastFrame; + } + + @Override + public Material getMaterial(String assetId) { + return Assets.getMaterial(assetId).orElseThrow(() -> + new RuntimeException("Failed to resolve required asset: '" + assetId + "'")); + } + + @Override + public void onChunkLoaded(Vector3ic pos) { + renderableWorld.onChunkLoaded(pos); + } + + @Override + public void onChunkUnloaded(Vector3ic pos) { + renderableWorld.onChunkUnloaded(pos); + } + + @Override + public boolean pregenerateChunks() { + return renderableWorld.pregenerateChunks(); + } + + @Override + public void update(float deltaInSeconds) { + secondsSinceLastFrame += deltaInSeconds; + } + + @Override + public void increaseTrianglesCount(int increase) { + statRenderedTriangles += increase; + } + + @Override + public void increaseNotReadyChunkCount(int increase) { + statChunkNotReady += increase; + } + + + /** + * TODO: update javadocs This method triggers the execution of the rendering pipeline and, eventually, sends the + * output to the display or to a file, when grabbing a screenshot. + *

+ * In this particular implementation this method can be called once per frame, when rendering to a standard display, + * or twice, each time with a different rendering stage, when rendering to the head mounted display. + *

+ * PerformanceMonitor.startActivity/endActivity statements are used in this method and in those it executes, to + * provide statistics regarding the ongoing rendering and its individual steps (i.e. rendering shadows, reflections, + * 2D filters...). + * + * @param renderingStage "MONO" for standard rendering and "LEFT_EYE" or "RIGHT_EYE" for stereoscopic + * displays. + */ + @Override + public void render(RenderingStage renderingStage) { + statChunkMeshEmpty = 0; + statChunkNotReady = 0; + statRenderedTriangles = 0; + + timeSmoothedMainLightIntensity = TeraMath.lerp(timeSmoothedMainLightIntensity, + getMainLightIntensityAt(playerCamera.getPosition()), secondsSinceLastFrame); + + playerCamera.update(secondsSinceLastFrame); + + renderableWorld.update(); + secondsSinceLastFrame = 0; + millisecondsSinceRenderingStart += secondsSinceLastFrame * 1000; // updates the variable animations are + + playerCamera.updateFrustum(); + + // this line needs to be here as deep down it relies on the camera's frustrum, updated just above. + renderableWorld.queueVisibleChunks(true); + + //TODO: implement rendering logic + + playerCamera.updatePrevViewProjectionMatrix(); + } + + @Override + public void requestTaskListRefresh() { + } + + @Override + public boolean isFirstRenderingStageForCurrentFrame() { + return true; + } + + /** + * Disposes of support objects used by this implementation. + */ + @Override + public void dispose() { + renderableWorld.dispose(); + worldProvider.dispose(); + } + + @Override + public void setViewDistance(ViewDistance viewDistance, int chunkLods) { + renderableWorld.updateChunksInProximity(viewDistance, chunkLods); + } + + @Override + public float getTimeSmoothedMainLightIntensity() { + return timeSmoothedMainLightIntensity; + } + + @Override + public float getRenderingLightIntensityAt(Vector3f pos) { + float rawLightValueSun = worldProvider.getSunlight(pos) / 15.0f; + float rawLightValueBlock = worldProvider.getLight(pos) / 15.0f; + + float lightValueSun = + (float) Math.pow(BLOCK_LIGHT_SUN_POW, (1.0f - rawLightValueSun) * 16.0) * rawLightValueSun; + lightValueSun *= backdropProvider.getDaylight(); + // TODO: Hardcoded factor and value to compensate for daylight tint and night brightness + lightValueSun *= 0.9f; + lightValueSun += 0.05f; + + float lightValueBlock = + (float) Math.pow(BLOCK_LIGHT_POW, (1.0f - (double) rawLightValueBlock) * 16.0f) * rawLightValueBlock * BLOCK_INTENSITY_FACTOR; + + return Math.max(lightValueBlock, lightValueSun); + } + + @Override + public float getMainLightIntensityAt(Vector3f position) { + return backdropProvider.getDaylight() * worldProvider.getSunlight(position) / 15.0f; + } + + @Override + public float getBlockLightIntensityAt(Vector3f position) { + return worldProvider.getLight(position) / 15.0f; + } + + @Override + public String getMetrics() { + String stringToReturn = ""; + stringToReturn += renderableWorld.getMetrics(); + stringToReturn += "Empty Mesh Chunks: "; + stringToReturn += statChunkMeshEmpty; + stringToReturn += "\n"; + stringToReturn += "Unready Chunks: "; + stringToReturn += statChunkNotReady; + stringToReturn += "\n"; + stringToReturn += "Rendered Triangles: "; + stringToReturn += statRenderedTriangles; + stringToReturn += "\n"; + return stringToReturn; + } + + @Override + public float getMillisecondsSinceRenderingStart() { + return millisecondsSinceRenderingStart; + } + + @Override + public Camera getActiveCamera() { + return playerCamera; + } + + @Override + public RenderingStage getCurrentRenderStage() { + return RenderingStage.LEFT_EYE; + } + + @Override + public RenderGraph getRenderGraph() { + return null; + } + +} diff --git a/engine/src/main/java/org/terasology/engine/rendering/world/RenderableWorldImpl.java b/engine/src/main/java/org/terasology/engine/rendering/world/RenderableWorldImpl.java index e513ac59243..b57317c1521 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/world/RenderableWorldImpl.java +++ b/engine/src/main/java/org/terasology/engine/rendering/world/RenderableWorldImpl.java @@ -92,7 +92,6 @@ class RenderableWorldImpl implements RenderableWorld { new PriorityQueue<>(MAX_LOADABLE_CHUNKS, frontToBackComparator), new PriorityQueue<>(MAX_LOADABLE_CHUNKS, backToFrontComparator)); - } @Override diff --git a/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java b/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java index 9c31f081178..51c0f427dd4 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java +++ b/engine/src/main/java/org/terasology/engine/rendering/world/WorldRendererImpl.java @@ -129,7 +129,8 @@ public WorldRendererImpl(Context context) { LocalPlayerSystem localPlayerSystem = context.get(LocalPlayerSystem.class); localPlayerSystem.setPlayerCamera(playerCamera); - context.put(ChunkTessellator.class, new ChunkTessellator()); + context.put(ChunkTessellator.class, new ChunkTessellator(null + )); ChunkProvider chunkProvider = context.get(ChunkProvider.class); ChunkTessellator chunkTessellator = context.get(ChunkTessellator.class); @@ -337,7 +338,7 @@ public void render(RenderingStage renderingStage) { renderPipelineTaskList.forEach(RenderPipelineTask::process); // this line re-establish Terasology defaults, so that the rest of the application can rely on them. - LwjglGraphicsUtil.initOpenGLParams(); +// LwjglGraphicsUtil.initOpenGLParams(); playerCamera.updatePrevViewProjectionMatrix(); } diff --git a/facades/PC/src/main/java/org/terasology/engine/Terasology.java b/facades/PC/src/main/java/org/terasology/engine/Terasology.java index 433380f9f00..28246f82b4a 100644 --- a/facades/PC/src/main/java/org/terasology/engine/Terasology.java +++ b/facades/PC/src/main/java/org/terasology/engine/Terasology.java @@ -163,7 +163,7 @@ public Integer call() throws IOException { } splashScreen.post("Java Runtime " + System.getProperty("java.version") + " loaded"); - try { +// try { TerasologyEngineBuilder builder = new TerasologyEngineBuilder(); populateSubsystems(builder); TerasologyEngine engine = builder.build(); @@ -187,12 +187,12 @@ public Integer call() throws IOException { } engine.run(nextState); - } catch (Throwable e) { - // also catch Errors such as UnsatisfiedLink, NoSuchMethodError, etc. - splashScreen.close(); - reportException(e); - return 1; - } +// } catch (Throwable e) { +// // also catch Errors such as UnsatisfiedLink, NoSuchMethodError, etc. +// splashScreen.close(); +// reportException(e); +// return 1; +// } return 0; }