diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java index bf5280941..061f75455 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java @@ -1,8 +1,10 @@ package com.seibel.distanthorizons.api.interfaces.render; +import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; import java.util.List; import java.util.function.Consumer; @@ -27,6 +29,11 @@ public interface IDhApiRenderableBoxGroup extends List /** @return if active this group will render. */ boolean isActive(); + /** Sets whether this group should render with Screen Space Ambient Occlusioning. */ + void setSsaoEnabled(boolean ssaoEnabled); + /** @return if active this group will render with Screen Space Ambient Occlusioning. */ + boolean isSsaoEnabled(); + /** Sets where this group will render in the level. */ void setOriginBlockPos(DhApiVec3f pos); /** @return the block position in the level that all {@see DhApiRenderableBox} will render relative to. */ @@ -37,6 +44,7 @@ public interface IDhApiRenderableBoxGroup extends List * This is a good place to change the origin or notify of any box changes. */ void setPreRenderFunc(Consumer renderEventParam); + void setPostRenderFunc(Consumer renderEventParam); // TODO name? /** * If a cube's color, position, or other property is changed this method @@ -55,4 +63,7 @@ public interface IDhApiRenderableBoxGroup extends List void setBlockLight(int blockLight); int getBlockLight(); + void setShading(DhApiRenderableBoxGroupShading shading); + DhApiRenderableBoxGroupShading getShading(); + } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java new file mode 100644 index 000000000..396f73bc5 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java @@ -0,0 +1,93 @@ +package com.seibel.distanthorizons.api.objects.render; + + +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; + +import java.awt.*; + +/** + * @see IDhApiRenderableBoxGroup + * + * Shading values are multiplied against the color for each direction, + * for example:
+ * A shading value of 1.0 indicates the color is unchanged.
+ * A shading value of 0.0 changes the color to black.
+ * + * @author James Seibel + * @version 2024-7-7 + * @since API 3.0.0 + */ +public class DhApiRenderableBoxGroupShading +{ + /** negative X */ + public float north = 1.0f; + /** positive X */ + public float south = 1.0f; + + /** positive X */ + public float east = 1.0f; + /** negative X */ + public float west = 1.0f; + + /** positive Y */ + public float top = 1.0f; + /** negative Y */ + public float bottom = 1.0f; + + + + //==============// + // constructors // + //==============// + + public static DhApiRenderableBoxGroupShading getDefaultShaded() + { + DhApiRenderableBoxGroupShading shading = new DhApiRenderableBoxGroupShading(); + shading.setDefaultShaded(); + return shading; + } + + public static DhApiRenderableBoxGroupShading getUnshaded() + { + DhApiRenderableBoxGroupShading shading = new DhApiRenderableBoxGroupShading(); + shading.setUnshaded(); + return shading; + } + + + + //=========// + // methods // + //=========// + + /** + * Directions will have different brightness similar to Minecraft blocks.
+ * This is a good default for un-lit objects. + */ + public void setDefaultShaded() + { + this.north = 0.8f; + this.south = 0.8f; + this.east = 0.6f; + this.west = 0.6f; + this.top = 1.0f; + this.bottom = 0.5f; + } + + /** + * All directions render with the same brightness.
+ * This is best used for glowing objects like beacons. + */ + public void setUnshaded() + { + this.north = 1.0f; + this.south = 1.0f; + this.east = 1.0f; + this.west = 1.0f; + this.top = 1.0f; + this.bottom = 1.0f; + } + +} + diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java index 69a5ec429..899ef4ce6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java @@ -23,6 +23,8 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkModifiedEvent; import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dataObjects.transformers.ChunkToLodBuilder; import com.seibel.distanthorizons.core.file.fullDatafile.DelayedFullDataSourceSaveCache; @@ -30,6 +32,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.render.renderer.generic.CloudRenderHandler; import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory; import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; @@ -72,6 +75,8 @@ public abstract class AbstractDhLevel implements IDhLevel protected boolean beaconGroupBound = false; + protected CloudRenderHandler cloudRenderHandler; + //=============// @@ -85,6 +90,8 @@ public abstract class AbstractDhLevel implements IDhLevel this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(new ArrayList<>(0)); this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded()); + this.beaconBoxGroup.setPreRenderFunc((renderEventParam) -> this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get())); } protected void createAndSetSupportingRepos(File databaseFile) @@ -221,12 +228,13 @@ public abstract class AbstractDhLevel implements IDhLevel // should always be non-null, but just in case if (this.beaconBeamRepo != null - && genericObjectRenderer != null) + && genericObjectRenderer != null) { if (!this.beaconGroupBound) { this.beaconGroupBound = true; genericObjectRenderer.add(this.beaconBoxGroup); + this.cloudRenderHandler = new CloudRenderHandler(this, genericObjectRenderer); } @@ -319,6 +327,7 @@ public abstract class AbstractDhLevel implements IDhLevel { this.beaconGroupBound = true; genericObjectRenderer.add(this.beaconBoxGroup); + this.cloudRenderHandler = new CloudRenderHandler(this, genericObjectRenderer); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java index 0eda3a554..4648be93b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java @@ -303,9 +303,12 @@ public class LodRenderer ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam); this.bufferHandler.renderOpaque(this, renderEventParam); - // custom objects - profiler.popPush("Custom Objects"); - this.genericObjectRenderer.render(renderEventParam, profiler); + // custom objects with SSAO + if (Config.Client.Advanced.Graphics.GenericRendering.enableRendering.get()) + { + profiler.popPush("Custom Objects"); + this.genericObjectRenderer.render(renderEventParam, profiler, true); + } // SSAO @@ -316,6 +319,14 @@ public class LodRenderer } + // custom objects without SSAO + if (Config.Client.Advanced.Graphics.GenericRendering.enableRendering.get()) + { + profiler.popPush("Custom Objects"); + this.genericObjectRenderer.render(renderEventParam, profiler, false); + } + + if (Config.Client.Advanced.Graphics.Fog.drawMode.get() != EDhApiFogDrawMode.FOG_DISABLED) { profiler.popPush("LOD Fog"); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java new file mode 100644 index 000000000..3557bf5cf --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java @@ -0,0 +1,301 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.render.renderer.generic; + +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import org.apache.logging.log4j.Logger; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +public class CloudRenderHandler +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + private static final String CLOUD_RESOURCE_TEXTURE_PATH = "assets/distanthorizons/textures/clouds.png"; + // FIXME transparency sorting makes having transparent clouds impossible + private static final Color CLOUD_COLOR = new Color(255,255,255,255); + + private static final boolean DEBUG_BORDER_COLORS = false; + + /** + * How wide an individual box is.
+ * Measured in blocks. + */ + private static final int CLOUD_BOX_WIDTH = 128; + /** measured in blocks */ + private static final int CLOUD_BOX_THICKNESS = 32; + + private final IDhApiRenderableBoxGroup[][] boxGroupByOffset; + private final IDhLevel level; + + private float moveSpeedInBlocksPerSecond = 3.0f; + + + + //=============// + // constructor // + //=============// + + public CloudRenderHandler(IDhLevel level, GenericObjectRenderer renderer) + { + this.level = level; + + + // default to a single empty slot in case the texture is broken + boolean[][] cloudLocations = new boolean[1][1]; + try + { + cloudLocations = getCloudsFromTexture(); + } + catch (FileNotFoundException e) + { + LOGGER.error(e.getMessage(), e); + } + catch (IOException e) + { + LOGGER.error("Unexpected issue getting cloud texture, error: ["+e.getMessage()+"].", e); + } + + if (cloudLocations.length != 0 && + cloudLocations.length != cloudLocations[0].length) + { + LOGGER.warn("Non-square cloud texture found, some parts of the texture will be clipped off."); + } + + + + int textureWidth = cloudLocations.length; + ArrayList boxList = new ArrayList<>(512); + for (int x = 0; x < textureWidth; x ++) + { + for (int z = 0; z < textureWidth; z ++) + { + if (cloudLocations[x][z]) + { + int minX = x * CLOUD_BOX_WIDTH; + int minZ = z * CLOUD_BOX_WIDTH; + int maxX = minX + CLOUD_BOX_WIDTH; + int maxZ = minZ + CLOUD_BOX_WIDTH; + + Color color = CLOUD_COLOR; + if (DEBUG_BORDER_COLORS) + { + // equals is included so the board is 2 blocks wide, it makes it easier to see + if (x <= 1) { color = Color.RED; } + else if (x >= textureWidth-2) { color = Color.GREEN; } + if (z <= 1) { color = Color.BLUE; } + else if (z >= textureWidth-2) { color = Color.BLACK; } + } + + DhApiRenderableBox box = new DhApiRenderableBox( + new DhApiVec3f(minX, 0, minZ), + new DhApiVec3f(maxX, CLOUD_BOX_THICKNESS, maxZ), + color + ); + boxList.add(box); + } + } + } + + + // slightly lighter shading than the default + DhApiRenderableBoxGroupShading cloudShading = DhApiRenderableBoxGroupShading.getUnshaded(); + cloudShading.north = cloudShading.south = 0.9f; + cloudShading.east = cloudShading.west = 0.8f; + cloudShading.top = 1.0f; + cloudShading.bottom = 0.7f; + + this.boxGroupByOffset = new IDhApiRenderableBoxGroup[3][3]; + for (int x = -1; x <= 1; x++) + { + for (int z = -1; z <= 1; z++) + { + IDhApiRenderableBoxGroup boxGroup = GenericRenderObjectFactory.INSTANCE.createRelativePositionedGroup( + new DhApiVec3f(0, 0, 0), // the offset will be set during rendering + boxList); + boxGroup.setBlockLight(LodUtil.MIN_MC_LIGHT); + boxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + boxGroup.setSsaoEnabled(false); + boxGroup.setShading(cloudShading); + + CloudParams offset = new CloudParams(textureWidth, x, z); + boxGroup.setPreRenderFunc((renderParam) -> this.preRender(offset)); + + renderer.add(boxGroup); + this.boxGroupByOffset[x+1][z+1] = boxGroup; + } + } + } + + public void preRender(CloudParams clouds) + { + IDhApiRenderableBoxGroup boxGroup = this.boxGroupByOffset[clouds.instanceOffsetX+1][clouds.instanceOffsetZ+1]; + + boolean renderClouds = Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering.get(); + boxGroup.setActive(renderClouds); + if(!renderClouds) + { + return; + } + + + + //================// + // cloud movement // + //================// + + long currentTime = System.currentTimeMillis(); + float deltaTime = (currentTime - clouds.lastFrameTime) / 1000.0f; // Delta time in seconds + clouds.lastFrameTime = currentTime; + + float deltaX = this.moveSpeedInBlocksPerSecond * deltaTime; + // negative delta is to match vanilla's cloud movement + clouds.xOffset -= deltaX; + // wrap the cloud around after reaching the edge + clouds.xOffset %= clouds.widthInBlocks; + + + + //============================// + // camera movement and offset // + //============================// + + // camera position + int cameraPosX = (int)MC_RENDER.getCameraExactPosition().x; + int cameraPosZ = (int)MC_RENDER.getCameraExactPosition().z; + // offset the camera position by negative 1 width when below zero to fix off-by-one errors in the negative direction + if (cameraPosX < 0) { cameraPosX -= clouds.widthInBlocks; } + if (cameraPosZ < 0) { cameraPosZ -= clouds.widthInBlocks; } + + // determine how many cloud instances away from the origin we are + int cloudInstanceOffsetX = cameraPosX / (int)clouds.widthInBlocks; + int cloudInstanceOffsetZ = cameraPosZ / (int)clouds.widthInBlocks; + + // calculate the new offset + float xOffset = (cloudInstanceOffsetX * clouds.widthInBlocks); + float zOffset = (cloudInstanceOffsetZ * clouds.widthInBlocks); + + + + //==============// + // update group // + //==============// + + boxGroup.setOriginBlockPos( + new DhApiVec3f( + clouds.xOffset + (clouds.instanceOffsetX * clouds.widthInBlocks) + xOffset + clouds.halfWidthInBlocks, + this.level.getLevelWrapper().getHeight() + 200, + clouds.zOffset + (clouds.instanceOffsetZ * clouds.widthInBlocks) + zOffset + clouds.halfWidthInBlocks + ) + ); + } + + + + //==================// + // texture handling // + //==================// + + public static boolean[][] getCloudsFromTexture() throws FileNotFoundException, IOException + { + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + + boolean[][] whitePixels = null; + try(InputStream imageInputStream = loader.getResourceAsStream(CLOUD_RESOURCE_TEXTURE_PATH)) + { + if (imageInputStream == null) + { + throw new FileNotFoundException("Unable to find cloud texture at resource path: ["+CLOUD_RESOURCE_TEXTURE_PATH+"]."); + } + + BufferedImage image = ImageIO.read(imageInputStream); + + int width = image.getWidth(); + int height = image.getHeight(); + + whitePixels = new boolean[width][height]; + + for (int x = 0; x < width; x ++) + { + for (int z = 0; z < width; z ++) + { + Color color = new Color(image.getRGB(x,z)); + whitePixels[x][z] = color.equals(Color.WHITE); + } + } + } + + return whitePixels; + } + + + + //================// + // helper classes // + //================// + + private static class CloudParams + { + public final float textureWidth; + public final float widthInBlocks; + public final float halfWidthInBlocks; + + public final int instanceOffsetX; + public final int instanceOffsetZ; + + + public float xOffset = 0; + public float zOffset = 0; + + public long lastFrameTime = System.currentTimeMillis(); + + + + // constructor // + + public CloudParams(float textureWidth, int instanceOffsetX, int instanceOffsetZ) + { + this.textureWidth = textureWidth; + this.widthInBlocks = (this.textureWidth * CLOUD_BOX_WIDTH); + this.halfWidthInBlocks = this.widthInBlocks / 2; + + this.instanceOffsetX = instanceOffsetX; + this.instanceOffsetZ = instanceOffsetZ; + } + + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java index 54c4c40da..2ab9efdea 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java @@ -26,6 +26,7 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; @@ -79,8 +80,8 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister private boolean init = false; private ShaderProgram shader; - private GLVertexBuffer vertexBuffer; - private GLElementBuffer solidIndexBuffer; + private GLVertexBuffer boxVertexBuffer; + private GLElementBuffer boxIndexBuffer; private AbstractVertexAttribute va; private boolean useInstancedRendering; @@ -100,6 +101,13 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister private int skyLightUniform; private int blockLightUniform; + private int northShadingUniform; + private int southShadingUniform; + private int eastShadingUniform; + private int westShadingUniform; + private int topShadingUniform; + private int bottomShadingUniform; + private final ConcurrentHashMap boxGroupById = new ConcurrentHashMap<>(); @@ -108,37 +116,62 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister /** A box from 0,0,0 to 1,1,1 */ private static final float[] BOX_VERTICES = { // Pos x y z + + // min X, vertical face 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, + // max X, vertical face + 0, 1, 1, + 1, 1, 1, + 1, 0, 1, 0, 0, 1, + + // min Z, vertical face + 0, 0, 1, + 0, 0, 0, + 0, 1, 0, + 0, 1, 1, + // max Z, vertical face 1, 0, 1, 1, 1, 1, - 0, 1, 1, - }; - - private static final int[] SOLID_BOX_INDICES = { - // min Z, vertical face - 0, 3, 2, - 2, 1, 0, - // max Z, vertical face - 4, 5, 6, - 6, 7, 4, - - // min X, vertical face - 7, 3, 0, - 0, 4, 7, - // max X, vertical face - 2, 6, 5, - 5, 1, 2, + 1, 1, 0, + 1, 0, 0, // min Y, horizontal face - 1, 5, 4, - 4, 0, 1, + 0, 0, 1, + 1, 0, 1, + 1, 0, 0, + 0, 0, 0, // max Y, horizontal face - 3, 7, 6, - 6, 2, 3, + 0, 1, 1, + 1, 1, 1, + 1, 1, 0, + 0, 1, 0, + }; + + private static final int[] BOX_INDICES = { + // min X, vertical face + 2, 1, 0, + 0, 3, 2, + // max X, vertical face + 6, 5, 4, + 4, 7, 6, + + // min Z, vertical face + 10, 9, 8, + 8, 11, 10, + // max Z, vertical face + 14, 13, 12, + 12, 15, 14, + + // min Y, horizontal face + 18, 17, 16, + 16, 19, 18, + // max Y, horizontal face + 20, 21, 22, + 22, 23, 20, }; @@ -187,6 +220,14 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister this.lightMapUniform = this.shader.getUniformLocation("uLightMap"); this.skyLightUniform = this.shader.getUniformLocation("uSkyLight"); this.blockLightUniform = this.shader.getUniformLocation("uBlockLight"); + //this.shadingModeUniform = this.shader.getUniformLocation("uShadingMode"); + this.northShadingUniform = this.shader.getUniformLocation("uNorthShading"); + this.southShadingUniform = this.shader.getUniformLocation("uSouthShading"); + this.eastShadingUniform = this.shader.getUniformLocation("uEastShading"); + this.westShadingUniform = this.shader.getUniformLocation("uWestShading"); + this.topShadingUniform = this.shader.getUniformLocation("uTopShading"); + this.bottomShadingUniform = this.shader.getUniformLocation("uBottomShading"); + this.createBuffers(); @@ -202,19 +243,19 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister boxVerticesBuffer.order(ByteOrder.nativeOrder()); boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES); boxVerticesBuffer.rewind(); - this.vertexBuffer = new GLVertexBuffer(false); - this.vertexBuffer.bind(); - this.vertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES); + this.boxVertexBuffer = new GLVertexBuffer(false); + this.boxVertexBuffer.bind(); + this.boxVertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES); // box vertex indexes - ByteBuffer solidIndexBuffer = ByteBuffer.allocateDirect(SOLID_BOX_INDICES.length * Integer.BYTES); + ByteBuffer solidIndexBuffer = ByteBuffer.allocateDirect(BOX_INDICES.length * Integer.BYTES); solidIndexBuffer.order(ByteOrder.nativeOrder()); - solidIndexBuffer.asIntBuffer().put(SOLID_BOX_INDICES); + solidIndexBuffer.asIntBuffer().put(BOX_INDICES); solidIndexBuffer.rewind(); - this.solidIndexBuffer = new GLElementBuffer(false); - this.solidIndexBuffer.uploadBuffer(solidIndexBuffer, EDhApiGpuUploadMethod.DATA, SOLID_BOX_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW); - this.solidIndexBuffer.bind(); + this.boxIndexBuffer = new GLElementBuffer(false); + this.boxIndexBuffer.uploadBuffer(solidIndexBuffer, EDhApiGpuUploadMethod.DATA, BOX_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW); + this.boxIndexBuffer.bind(); } private void addGenericDebugObjects() @@ -345,7 +386,12 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister // rendering // //===========// - public void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler) + /** + * @param renderingWithSsao + * if true that means this render call is happening before the SSAO pass + * and any objects rendered in this pass will have SSAO applied to them. + */ + public void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao) { // render setup // profiler.push("setup"); @@ -362,9 +408,9 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister this.shader.bind(); this.va.bind(); - this.va.bindBufferToAllBindingPoints(this.vertexBuffer.getId()); + this.va.bindBufferToAllBindingPoints(this.boxVertexBuffer.getId()); - this.solidIndexBuffer.bind(); + this.boxIndexBuffer.bind(); Mat4f projectionMvmMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); projectionMvmMatrix.multiply(renderEventParam.dhModelViewMatrix); @@ -379,21 +425,27 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister Collection boxList = this.boxGroupById.values(); for (RenderableBoxGroup boxGroup : boxList) { - // ignore inactive groups - if (boxGroup != null && boxGroup.active) + // skip boxes that shouldn't render this pass + if (boxGroup.ssaoEnabled == renderingWithSsao) { profiler.popPush("render prep"); boxGroup.preRender(renderEventParam); - if (this.useInstancedRendering) + // ignore inactive groups + if (boxGroup.active) { - profiler.popPush("rendering"); - this.renderBoxGroupInstanced(boxGroup, camPos, projectionMvmMatrix); - } - else - { - profiler.popPush("rendering"); - this.renderBoxGroupDirect(boxGroup, projectionMvmMatrix, camPos); + if (this.useInstancedRendering) + { + profiler.popPush("rendering"); + this.renderBoxGroupInstanced(boxGroup, camPos, projectionMvmMatrix); + } + else + { + profiler.popPush("rendering"); + this.renderBoxGroupDirect(boxGroup, projectionMvmMatrix, camPos); + } + + boxGroup.postRender(renderEventParam); } } } @@ -442,6 +494,18 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister this.shader.setUniform(this.skyLightUniform, boxGroup.skyLight); this.shader.setUniform(this.blockLightUniform, boxGroup.blockLight); + DhApiRenderableBoxGroupShading shading = boxGroup.shading; + if (shading == null) + { + shading = DhApiRenderableBoxGroupShading.getUnshaded(); + } + this.shader.setUniform(this.northShadingUniform, shading.north); + this.shader.setUniform(this.southShadingUniform, shading.south); + this.shader.setUniform(this.eastShadingUniform, shading.east); + this.shader.setUniform(this.westShadingUniform, shading.west); + this.shader.setUniform(this.topShadingUniform, shading.top); + this.shader.setUniform(this.bottomShadingUniform, shading.bottom); + @@ -466,7 +530,7 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister // Draw instanced if (boxGroup.uploadedBoxCount > 0) { - GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, SOLID_BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, boxGroup.uploadedBoxCount); + GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, boxGroup.uploadedBoxCount); } @@ -512,7 +576,7 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister for (DhApiRenderableBox box : boxGroup) { - renderBox(boxGroup, box, transformMatrix, camPos); + this.renderBox(boxGroup, box, transformMatrix, camPos); } } private void renderBox( @@ -543,7 +607,7 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister this.shader.setUniform(this.directShaderColorUniform, box.color); - GL32.glDrawElements(GL32.GL_TRIANGLES, SOLID_BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0); + GL32.glDrawElements(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java index f8f3fe60d..7fc2c74cf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java @@ -4,6 +4,7 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.math.Vec3f; @@ -37,14 +38,18 @@ public class RenderableBoxGroup private final Vec3f originBlockPos; + + public boolean active = true; + public boolean ssaoEnabled = true; + private boolean vertexDataDirty = true; + public int skyLight = 15; public int blockLight = 0; + public DhApiRenderableBoxGroupShading shading = DhApiRenderableBoxGroupShading.getDefaultShaded(); @Nullable public Consumer beforeRenderFunc; - - private boolean vertexDataDirty = true; - public boolean active = true; + public Consumer afterRenderFunc; // instance data public int instanceTranslationVbo = 0; @@ -120,6 +125,9 @@ public class RenderableBoxGroup @Override public void setPreRenderFunc(Consumer func) { this.beforeRenderFunc = func; } + @Override + public void setPostRenderFunc(Consumer func) { this.afterRenderFunc = func; } + @Override public void triggerBoxChange() { this.vertexDataDirty = true; } @@ -128,6 +136,11 @@ public class RenderableBoxGroup @Override public boolean isActive() { return this.active; } + @Override + public void setSsaoEnabled(boolean ssaoEnabled) { this.ssaoEnabled = ssaoEnabled; } + @Override + public boolean isSsaoEnabled() { return this.ssaoEnabled; } + public void preRender(DhApiRenderParam renderEventParam) { if (this.beforeRenderFunc != null) @@ -135,10 +148,24 @@ public class RenderableBoxGroup this.beforeRenderFunc.accept(renderEventParam); } } + public void postRender(DhApiRenderParam renderEventParam) + { + if (this.afterRenderFunc != null) + { + this.afterRenderFunc.accept(renderEventParam); + } + } + + @Override + public void setShading(DhApiRenderableBoxGroupShading shading) { this.shading = shading; } + @Override + public DhApiRenderableBoxGroupShading getShading() { return this.shading; } - // overrides // + //================// + // List Overrides // + //================// @Override public DhApiRenderableBox get(int index) { return this.boxList.get(index); } diff --git a/core/src/main/resources/assets/distanthorizons/textures/clouds.png b/core/src/main/resources/assets/distanthorizons/textures/clouds.png new file mode 100644 index 000000000..825f4814e Binary files /dev/null and b/core/src/main/resources/assets/distanthorizons/textures/clouds.png differ diff --git a/core/src/main/resources/shaders/genericObject/instanced/vert.vert b/core/src/main/resources/shaders/genericObject/instanced/vert.vert index 42625d29f..919da6331 100644 --- a/core/src/main/resources/shaders/genericObject/instanced/vert.vert +++ b/core/src/main/resources/shaders/genericObject/instanced/vert.vert @@ -11,6 +11,13 @@ uniform int uSkyLight; uniform int uBlockLight; uniform sampler2D uLightMap; +uniform float uNorthShading; +uniform float uSouthShading; +uniform float uEastShading; +uniform float uWestShading; +uniform float uTopShading; +uniform float uBottomShading; + in vec3 vPosition; out vec4 fColor; @@ -41,6 +48,16 @@ void main() float blockLight = (float(uBlockLight)+0.5) / 16.0; float skyLight = (float(uSkyLight)+0.5) / 16.0; vec4 lightColor = vec4(texture(uLightMap, vec2(blockLight, skyLight)).xyz, 1.0); - + + fColor = lightColor * aColor; -} \ No newline at end of file + + // apply directional shading + if (gl_VertexID >= 0 && gl_VertexID < 4) { fColor.rgb *= uNorthShading; } + else if (gl_VertexID >= 4 && gl_VertexID < 8) { fColor.rgb *= uSouthShading; } + else if (gl_VertexID >= 8 && gl_VertexID < 12) { fColor.rgb *= uWestShading; } + else if (gl_VertexID >= 12 && gl_VertexID < 16) { fColor.rgb *= uEastShading; } + else if (gl_VertexID >= 16 && gl_VertexID < 20) { fColor.rgb *= uBottomShading; } + else if (gl_VertexID >= 20 && gl_VertexID < 24) { fColor.rgb *= uTopShading; } + +}