diff --git a/src/main/java/rs117/hd/HdPlugin.java b/src/main/java/rs117/hd/HdPlugin.java
index e08fa46dd5..2fa0481fd2 100644
--- a/src/main/java/rs117/hd/HdPlugin.java
+++ b/src/main/java/rs117/hd/HdPlugin.java
@@ -128,6 +128,7 @@
import rs117.hd.utils.ResourcePath;
import rs117.hd.utils.ShaderRecompile;
import rs117.hd.utils.buffer.GLBuffer;
+import rs117.hd.utils.buffer.GLTextureBuffer;
import rs117.hd.utils.jobs.GenericJob;
import rs117.hd.utils.jobs.JobSystem;
@@ -901,8 +902,10 @@ public ShaderIncludes getShaderIncludes() {
var includes = new ShaderIncludes()
.addIncludePath(SHADER_PATH)
.addInclude("VERSION_HEADER", OSType.getOSType() == OSType.Linux ? LINUX_VERSION_HEADER : WINDOWS_VERSION_HEADER)
+ .define("TEXEL_SIZE", GLTextureBuffer.isRGBASupported() ? 4 : 3)
.define("UI_SCALING_MODE", config.uiScalingMode())
.define("COLOR_BLINDNESS", config.colorBlindness())
+ .define("DITHER_FADE", config.ditherFading())
.define("APPLY_COLOR_FILTER", configColorFilter != ColorFilter.NONE)
.define("MATERIAL_COUNT", MaterialManager.MATERIALS.length)
.define("WATER_TYPE_COUNT", waterTypeManager.uboWaterTypes.getCount())
@@ -1814,6 +1817,7 @@ public void processPendingConfigChanges() {
case KEY_WIREFRAME:
case KEY_SHADOW_FILTERING:
case KEY_WINDOWS_HDR_CORRECTION:
+ case KEY_DITHER_FADING:
recompilePrograms = true;
break;
case KEY_ANTI_ALIASING_MODE:
diff --git a/src/main/java/rs117/hd/HdPluginConfig.java b/src/main/java/rs117/hd/HdPluginConfig.java
index b24ab75201..f72e36e3b7 100644
--- a/src/main/java/rs117/hd/HdPluginConfig.java
+++ b/src/main/java/rs117/hd/HdPluginConfig.java
@@ -314,12 +314,22 @@ default boolean flashingEffects()
return false;
}
+ String KEY_DITHER_FADING = "ditherFading";
+ @ConfigItem(
+ keyName = KEY_DITHER_FADING,
+ name = "Dither Fading",
+ description = "Whether to dither fade near plane geometry that would normally clip.",
+ position = 16,
+ section = generalSettings
+ )
+ default boolean ditherFading() { return true; }
+
@ConfigItem(
keyName = "fSaturation",
name = "Saturation",
description = "Controls the saturation of the final rendered image.
" +
"Intended to be kept between 0% and 120%.",
- position = 16,
+ position = 17,
section = generalSettings
)
@Units(Units.PERCENT)
@@ -339,7 +349,7 @@ default Saturation oldSaturationDropdown()
name = "Contrast",
description = "Controls the contrast of the final rendered image.
" +
"Intended to be kept between 90% and 110%.",
- position = 17,
+ position = 18,
section = generalSettings
)
@Units(Units.PERCENT)
@@ -366,7 +376,7 @@ default Contrast oldContrastDropdown()
description =
"Controls the brightness of the game, excluding UI.
" +
"Adjust until the circle on the left is barely visible.",
- position = 18,
+ position = 19,
section = generalSettings
)
default int brightness() {
diff --git a/src/main/java/rs117/hd/opengl/shader/SceneShaderProgram.java b/src/main/java/rs117/hd/opengl/shader/SceneShaderProgram.java
index 1c75140e78..cb2275a09a 100644
--- a/src/main/java/rs117/hd/opengl/shader/SceneShaderProgram.java
+++ b/src/main/java/rs117/hd/opengl/shader/SceneShaderProgram.java
@@ -4,6 +4,7 @@
import static rs117.hd.HdPlugin.TEXTURE_UNIT_GAME;
import static rs117.hd.HdPlugin.TEXTURE_UNIT_SHADOW_MAP;
import static rs117.hd.HdPlugin.TEXTURE_UNIT_TILED_LIGHTING_MAP;
+import static rs117.hd.renderer.zone.ZoneRenderer.TEXTURE_UNIT_MODEL_DATA;
import static rs117.hd.renderer.zone.ZoneRenderer.TEXTURE_UNIT_TEXTURED_FACES;
public class SceneShaderProgram extends ShaderProgram {
@@ -11,6 +12,7 @@ public class SceneShaderProgram extends ShaderProgram {
protected final UniformTexture uniShadowMap = addUniformTexture("shadowMap");
protected final UniformTexture uniTiledLightingTextureArray = addUniformTexture("tiledLightingArray");
protected final UniformTexture uniTextureFaces = addUniformTexture("textureFaces");
+ protected final UniformTexture uniModelData = addUniformTexture("modelData");
public SceneShaderProgram() {
super(t -> t
@@ -25,12 +27,14 @@ protected void initialize() {
uniShadowMap.set(TEXTURE_UNIT_SHADOW_MAP);
uniTiledLightingTextureArray.set(TEXTURE_UNIT_TILED_LIGHTING_MAP);
uniTextureFaces.set(TEXTURE_UNIT_TEXTURED_FACES);
+ uniModelData.set(TEXTURE_UNIT_MODEL_DATA);
}
public static class Legacy extends SceneShaderProgram {
Legacy() {
shaderTemplate.add(GL_GEOMETRY_SHADER, "scene_geom.glsl");
uniTextureFaces.ignoreMissing = true;
+ uniModelData.ignoreMissing = true;
}
}
}
diff --git a/src/main/java/rs117/hd/opengl/shader/ShadowShaderProgram.java b/src/main/java/rs117/hd/opengl/shader/ShadowShaderProgram.java
index 1663800a11..bdd8e42197 100644
--- a/src/main/java/rs117/hd/opengl/shader/ShadowShaderProgram.java
+++ b/src/main/java/rs117/hd/opengl/shader/ShadowShaderProgram.java
@@ -5,11 +5,13 @@
import static org.lwjgl.opengl.GL33C.*;
import static rs117.hd.HdPlugin.TEXTURE_UNIT_GAME;
+import static rs117.hd.renderer.zone.ZoneRenderer.TEXTURE_UNIT_MODEL_DATA;
import static rs117.hd.renderer.zone.ZoneRenderer.TEXTURE_UNIT_TEXTURED_FACES;
public abstract class ShadowShaderProgram extends ShaderProgram {
protected final UniformTexture uniTextureArray = addUniformTexture("textureArray");
protected final UniformTexture uniTextureFaces = addUniformTexture("textureFaces");
+ protected final UniformTexture uniModelData = addUniformTexture("modelData");
protected ShadowMode mode;
@@ -23,6 +25,7 @@ public abstract class ShadowShaderProgram extends ShaderProgram {
protected void initialize() {
uniTextureArray.set(TEXTURE_UNIT_GAME);
uniTextureFaces.set(TEXTURE_UNIT_TEXTURED_FACES);
+ uniModelData.set(TEXTURE_UNIT_MODEL_DATA);
}
@Override
@@ -47,6 +50,8 @@ public static class Legacy extends ShadowShaderProgram {
public Legacy setMode(ShadowMode mode) {
this.mode = mode;
uniTextureArray.ignoreMissing = mode != ShadowMode.DETAILED;
+ uniTextureFaces.ignoreMissing = true;
+ uniModelData.ignoreMissing = true;
return this;
}
diff --git a/src/main/java/rs117/hd/renderer/zone/DynamicModelVAO.java b/src/main/java/rs117/hd/renderer/zone/DynamicModelVAO.java
index 5e1dccb3c0..2da210c6bf 100644
--- a/src/main/java/rs117/hd/renderer/zone/DynamicModelVAO.java
+++ b/src/main/java/rs117/hd/renderer/zone/DynamicModelVAO.java
@@ -17,6 +17,7 @@
import static rs117.hd.HdPlugin.NVIDIA_GPU;
import static rs117.hd.HdPlugin.SUPPORTS_INDIRECT_DRAW;
import static rs117.hd.HdPlugin.SUPPORTS_STORAGE_BUFFERS;
+import static rs117.hd.renderer.zone.ZoneRenderer.TEXTURE_UNIT_MODEL_DATA;
import static rs117.hd.renderer.zone.ZoneRenderer.TEXTURE_UNIT_TEXTURED_FACES;
import static rs117.hd.utils.MathUtils.*;
import static rs117.hd.utils.buffer.GLBuffer.STORAGE_IMMUTABLE;
@@ -37,20 +38,23 @@ public class DynamicModelVAO implements Destructible {
// Metadata format
// worldViewIndex int
// dummy sceneOffset ivec2 for macOS workaround
- static final int METADATA_SIZE = 12;
+ // fade float
+ static final int METADATA_SIZE = 16;
@Getter
private int vao;
private final GLBuffer vboRender;
private final GLBuffer vboStaging;
- private final GLTextureBuffer tbo;
+ private final GLTextureBuffer tboF;
+ private final GLTextureBuffer tboM;
private final ArrayDeque usedViews = new ArrayDeque<>();
private final ArrayDeque freeViews = new ArrayDeque<>();
private final GLMappedBufferIntWriter vboWriter;
- private final GLMappedBufferIntWriter tboWriter;
+ private final GLMappedBufferIntWriter tboFWriter;
+ private final GLMappedBufferIntWriter tboMWriter;
private boolean isMapped = false;
private int[] drawOffsets = new int[16];
@@ -81,15 +85,18 @@ public class DynamicModelVAO implements Destructible {
}
this.vboWriter = new GLMappedBufferIntWriter(this.vboStaging);
- this.tbo = new GLTextureBuffer("VAO::TBO::" + name, GL_STREAM_DRAW, STORAGE_PERSISTENT | STORAGE_IMMUTABLE | STORAGE_WRITE);
- this.tboWriter = new GLMappedBufferIntWriter(this.tbo);
+ this.tboF = new GLTextureBuffer("VAO::TexturedFaces::" + name, GL_STREAM_DRAW, STORAGE_PERSISTENT | STORAGE_IMMUTABLE | STORAGE_WRITE);
+ this.tboM = new GLTextureBuffer("VAO::ModelData::" + name, GL_STREAM_DRAW, STORAGE_PERSISTENT | STORAGE_IMMUTABLE | STORAGE_WRITE);
+ this.tboFWriter = new GLMappedBufferIntWriter(this.tboF);
+ this.tboMWriter = new GLMappedBufferIntWriter(this.tboM);
}
public boolean hasStagingBuffer() { return vboRender != vboStaging; }
void initialize() {
vao = glGenVertexArrays();
- tbo.initialize(INITIAL_SIZE);
+ tboF.initialize(INITIAL_SIZE);
+ tboM.initialize(INITIAL_SIZE);
vboRender.initialize(INITIAL_SIZE);
if (vboRender != vboStaging)
vboStaging.initialize(INITIAL_SIZE);
@@ -143,7 +150,8 @@ void bindRenderVAO() {
void map() {
vboWriter.map(false);
- tboWriter.map(false);
+ tboFWriter.map(false);
+ tboMWriter.map(false);
reset();
isMapped = true;
@@ -152,7 +160,8 @@ void map() {
synchronized void unmap(boolean coalesce) {
final int renderVBOId = vboRender.id;
long vboWrittenBytes = vboWriter.flush();
- tboWriter.flush();
+ tboFWriter.flush();
+ tboMWriter.flush();
if (drawRangeCount > 0) {
mergeRanges();
@@ -192,10 +201,12 @@ synchronized void unmap(boolean coalesce) {
@Override
public void destroy() {
vboWriter.destroy();
- tboWriter.destroy();
+ tboFWriter.destroy();
+ tboMWriter.destroy();
vboRender.destroy();
vboStaging.destroy();
- tbo.destroy();
+ tboF.destroy();
+ tboM.destroy();
if (vao != 0)
glDeleteVertexArrays(vao);
@@ -231,9 +242,11 @@ public synchronized View beginDraw(int drawIdx, int faceCount) {
if (view == null)
view = new View();
view.vbo = vboWriter.reserve(faceCount * 3 * VERT_SIZE_INTS);
- view.tbo = tboWriter.reserve(faceCount * 9);
+ view.tboF = tboFWriter.reserve(faceCount * 4);
+ view.tboM = tboMWriter.reserve(Zone.MODEL_DATA_SIZE / Integer.BYTES);
view.vao = vao;
- view.tboTexId = tbo.getTexId();
+ view.tboFId = tboF.getTexId();
+ view.tboMId = tboM.getTexId();
view.drawIdx = drawIdx;
return view;
@@ -252,7 +265,8 @@ private synchronized void endDraw(View view) {
// Clear ReservedViews before returning to pool
view.vbo = null;
- view.tbo = null;
+ view.tboF = null;
+ view.tboM = null;
usedViews.add(view);
}
@@ -280,7 +294,8 @@ void draw(CommandBuffer cmd) {
return;
cmd.BindVertexArray(vao);
- cmd.BindTextureUnit(GL_TEXTURE_BUFFER, tbo.getTexId(), TEXTURE_UNIT_TEXTURED_FACES);
+ cmd.BindTextureUnit(GL_TEXTURE_BUFFER, tboF.getTexId(), TEXTURE_UNIT_TEXTURED_FACES);
+ cmd.BindTextureUnit(GL_TEXTURE_BUFFER, tboM.getTexId(), TEXTURE_UNIT_MODEL_DATA);
if (drawRangeCount == 1) {
if (GL_CAPS.OpenGL40 && SUPPORTS_INDIRECT_DRAW) {
@@ -307,9 +322,11 @@ void reset() {
public final class View {
public ReservedView vbo;
- public ReservedView tbo;
+ public ReservedView tboF;
+ public ReservedView tboM;
public int vao;
- public int tboTexId;
+ public int tboFId;
+ public int tboMId;
private int drawIdx;
public int getStartOffset() {
diff --git a/src/main/java/rs117/hd/renderer/zone/ModelStreamingManager.java b/src/main/java/rs117/hd/renderer/zone/ModelStreamingManager.java
index 7b9a941558..b4099985d2 100644
--- a/src/main/java/rs117/hd/renderer/zone/ModelStreamingManager.java
+++ b/src/main/java/rs117/hd/renderer/zone/ModelStreamingManager.java
@@ -174,6 +174,9 @@ public void drawTemp(Projection worldProjection, Scene scene, GameObject gameObj
int zz = (gameObject.getY() >> 10) + offset;
Zone zone = ctx.zones[zx][zz];
+ if(!zone.initialized || zone.fadingAlpha > 1.0f)
+ return;
+
m.calculateBoundsCylinder();
final float[] objectWorldPos = vec4(streamingContext.objectWorldPos, x, y, z, 1.0f);
@@ -328,12 +331,15 @@ private void uploadTempModel(
(!sceneManager.isRoot(ctx) || zone.inShadowFrustum)
) {
final DynamicModelVAO.View shadowView = ctx.beginDraw(VAO_SHADOW, culledFaces.length);
+ int shadowModelIdx = SceneUploader.writeDynamicModelData(shadowView.tboM, x, y, z, m, modelOverride, zone);
sceneUploader.uploadTempModel(
culledFaces,
m,
modelOverride,
preOrientation,
orientation,
+ shadowModelIdx,
+ shadowModelIdx,
true,
shadowView,
shadowView
@@ -351,12 +357,17 @@ private void uploadTempModel(
final DynamicModelVAO.View opaqueView = ctx.beginDraw(isPlayer ? VAO_PLAYER : VAO_OPAQUE, drawIndex, opaqueFaceCount);
final DynamicModelVAO.View alphaView = alphaFaceCount > 0 ? ctx.beginDraw(VAO_ALPHA, alphaFaceCount) : opaqueView;
+ final int opaqueModelIdx = SceneUploader.writeDynamicModelData(opaqueView.tboM, x, y, z, m, modelOverride, zone);
+ final int alphaModelIdx = alphaFaceCount > 0 ? SceneUploader.writeDynamicModelData(alphaView.tboM, x, y, z, m, modelOverride, zone) : opaqueModelIdx;
+
sceneUploader.uploadTempModel(
visibleFaces,
m,
modelOverride,
preOrientation,
orientation,
+ opaqueModelIdx,
+ alphaModelIdx,
isSquashed,
opaqueView,
alphaView
@@ -401,7 +412,7 @@ public void drawDynamic(
int zz = (z >> 10) + offset;
Zone zone = ctx.zones[zx][zz];
- if (!zone.initialized)
+ if (!zone.initialized || zone.fadingAlpha > 1.0f)
return;
final StreamingContext streamingContext = context(renderThreadId);
@@ -590,12 +601,15 @@ private void uploadDynamicModel(
(!sceneManager.isRoot(ctx) || zone.inShadowFrustum)
) {
final DynamicModelVAO.View shadowView = ctx.beginDraw(VAO_SHADOW, culledFaces.length);
+ final int shadowModelIdx = SceneUploader.writeDynamicModelData(shadowView.tboM, x, y, z, m, modelOverride, zone);
sceneUploader.uploadTempModel(
culledFaces,
m,
modelOverride,
preOrientation,
orient,
+ shadowModelIdx,
+ shadowModelIdx,
true,
shadowView,
shadowView
@@ -610,12 +624,17 @@ private void uploadDynamicModel(
final DynamicModelVAO.View opaqueView = ctx.beginDraw(VAO_OPAQUE, drawIndex, opaqueFaceCount);
final DynamicModelVAO.View alphaView = alphaFaceCount > 0 ? ctx.beginDraw(VAO_ALPHA, alphaFaceCount) : opaqueView;
+ final int opaqueModelIdx = SceneUploader.writeDynamicModelData(opaqueView.tboM, x, y, z, m, modelOverride, zone);
+ final int alphaModelIdx = alphaFaceCount > 0 ? SceneUploader.writeDynamicModelData(alphaView.tboM, x, y, z, m, modelOverride, zone) : opaqueModelIdx;
+
sceneUploader.uploadTempModel(
visibleFaces,
m,
modelOverride,
preOrientation,
orient,
+ opaqueModelIdx,
+ alphaModelIdx,
isSquashed,
opaqueView,
alphaView
diff --git a/src/main/java/rs117/hd/renderer/zone/SceneManager.java b/src/main/java/rs117/hd/renderer/zone/SceneManager.java
index f7258f3620..6fd9b784a7 100644
--- a/src/main/java/rs117/hd/renderer/zone/SceneManager.java
+++ b/src/main/java/rs117/hd/renderer/zone/SceneManager.java
@@ -589,17 +589,19 @@ public synchronized void loadScene(WorldView worldView, Scene scene) {
}
}
- long timeMs = System.currentTimeMillis();
+ //long timeMs = System.currentTimeMillis();
for (SortedZone sorted : sortedZones) {
Zone newZone = injector.getInstance(Zone.class);
newZone.dirty = sorted.zone.dirty;
if (staggerLoad) {
+ if(!sorted.zone.cull)
+ newZone.fadingAlpha = saturate(sorted.dist / 15.0f) * 3.0f; // Fade in new chunks that are appearing out of the fog
+
// Reuse the old zone while uploading a correct one
sorted.zone.cull = false;
sorted.zone.uploadJob = ZoneUploadJob
- .build(ctx, nextSceneContext, newZone, false, sorted.x, sorted.z);
- sorted.zone.uploadJob.revealAfterTimestampMs =
- timeMs + ceil(clamp(sorted.dist / 15.0f, 0.25f, 1.5f) * 1000.0f);
+ .build(ctx, nextSceneContext, newZone, false, sorted.x, sorted.z)
+ .queue(ctx.streamingGroup, generateSceneDataTask);
} else {
nextZones[sorted.x][sorted.z] = newZone;
ZoneUploadJob
diff --git a/src/main/java/rs117/hd/renderer/zone/SceneUploader.java b/src/main/java/rs117/hd/renderer/zone/SceneUploader.java
index 160bab014e..68d0b9bb02 100644
--- a/src/main/java/rs117/hd/renderer/zone/SceneUploader.java
+++ b/src/main/java/rs117/hd/renderer/zone/SceneUploader.java
@@ -24,6 +24,7 @@
*/
package rs117.hd.renderer.zone;
+import java.nio.IntBuffer;
import java.util.HashSet;
import java.util.Set;
import javax.inject.Inject;
@@ -46,6 +47,7 @@
import rs117.hd.scene.water_types.WaterType;
import rs117.hd.utils.HDUtils;
import rs117.hd.utils.ModelHash;
+import rs117.hd.utils.buffer.GLMappedBufferIntWriter;
import rs117.hd.utils.buffer.GpuIntBuffer;
import rs117.hd.utils.collections.ConcurrentPool;
import rs117.hd.utils.collections.PrimitiveIntArray;
@@ -196,6 +198,7 @@ public void uploadZone(ZoneSceneContext ctx, Zone zone, int mzx, int mzz) throws
var vb = zone.vboO != null ? new GpuIntBuffer(zone.vboO.mapped()) : null;
var ab = zone.vboA != null ? new GpuIntBuffer(zone.vboA.mapped()) : null;
var fb = zone.tboF != null ? new GpuIntBuffer(zone.tboF.mapped()) : null;
+ var md = zone.tboM != null ? new GpuIntBuffer(zone.tboM.mapped()) : null;
assert fb != null;
roofIds.clear();
@@ -217,13 +220,13 @@ public void uploadZone(ZoneSceneContext ctx, Zone zone, int mzx, int mzz) throws
this.level = z;
if (z == 0) {
- uploadZoneLevel(ctx, zone, mzx, mzz, 0, false, roofIds, vb, ab, fb);
- uploadZoneLevel(ctx, zone, mzx, mzz, 0, true, roofIds, vb, ab, fb);
- uploadZoneLevel(ctx, zone, mzx, mzz, 1, true, roofIds, vb, ab, fb);
- uploadZoneLevel(ctx, zone, mzx, mzz, 2, true, roofIds, vb, ab, fb);
- uploadZoneLevel(ctx, zone, mzx, mzz, 3, true, roofIds, vb, ab, fb);
+ uploadZoneLevel(ctx, zone, mzx, mzz, 0, false, roofIds, vb, ab, fb, md);
+ uploadZoneLevel(ctx, zone, mzx, mzz, 0, true, roofIds, vb, ab, fb, md);
+ uploadZoneLevel(ctx, zone, mzx, mzz, 1, true, roofIds, vb, ab, fb, md);
+ uploadZoneLevel(ctx, zone, mzx, mzz, 2, true, roofIds, vb, ab, fb, md);
+ uploadZoneLevel(ctx, zone, mzx, mzz, 3, true, roofIds, vb, ab, fb, md);
} else {
- uploadZoneLevel(ctx, zone, mzx, mzz, z, false, roofIds, vb, ab, fb);
+ uploadZoneLevel(ctx, zone, mzx, mzz, z, false, roofIds, vb, ab, fb, md);
}
if (vb != null) {
@@ -249,7 +252,8 @@ private void uploadZoneLevel(
Set roofIds,
GpuIntBuffer vb,
GpuIntBuffer ab,
- GpuIntBuffer fb
+ GpuIntBuffer fb,
+ GpuIntBuffer md
) throws InterruptedException {
int ridx = 0;
@@ -257,7 +261,7 @@ private void uploadZoneLevel(
for (int id : roofIds) {
int pos = vb != null ? vb.position() : 0;
- uploadZoneLevelRoof(ctx, zone, mzx, mzz, level, id, visbelow, vb, ab, fb);
+ uploadZoneLevelRoof(ctx, zone, mzx, mzz, level, id, visbelow, vb, ab, fb, md);
int endpos = vb != null ? vb.position() : 0;
@@ -270,7 +274,7 @@ private void uploadZoneLevel(
}
// upload everything else
- uploadZoneLevelRoof(ctx, zone, mzx, mzz, level, 0, visbelow, vb, ab, fb);
+ uploadZoneLevelRoof(ctx, zone, mzx, mzz, level, 0, visbelow, vb, ab, fb, md);
}
private void uploadZoneLevelRoof(
@@ -283,7 +287,8 @@ private void uploadZoneLevelRoof(
boolean visbelow,
GpuIntBuffer vb,
GpuIntBuffer ab,
- GpuIntBuffer fb
+ GpuIntBuffer fb,
+ GpuIntBuffer md
) throws InterruptedException {
this.basex = (mzx - (ctx.sceneOffset >> 3)) << 10;
this.basez = (mzz - (ctx.sceneOffset >> 3)) << 10;
@@ -317,7 +322,7 @@ private void uploadZoneLevelRoof(
this.rid = rid;
if (onBeforeProcessTile != null)
onBeforeProcessTile.invoke(t, false);
- uploadZoneTile(ctx, zone, t, false, false, vb, ab, fb);
+ uploadZoneTile(ctx, zone, t, false, false, vb, ab, fb, md);
}
}
}
@@ -344,7 +349,7 @@ private void uploadZoneWater(
if (t != null) {
if (onBeforeProcessTile != null)
onBeforeProcessTile.invoke(t, false);
- uploadZoneTile(ctx, zone, t, false, true, vb, null, fb);
+ uploadZoneTile(ctx, zone, t, false, true, vb, null, fb, null);
}
}
}
@@ -464,7 +469,8 @@ private void uploadZoneTile(
boolean onlyWaterSurface,
GpuIntBuffer vertexBuffer,
GpuIntBuffer alphaBuffer,
- GpuIntBuffer textureBuffer
+ GpuIntBuffer textureBuffer,
+ GpuIntBuffer modelBuffer
) {
var tilePoint = t.getSceneLocation();
int tileExX = tilePoint.getX() + ctx.sceneOffset;
@@ -496,11 +502,11 @@ private void uploadZoneTile(
uploadTileModel(ctx, t, model, onlyWaterSurface, tileExX, tileExY, tileZ, basex, basez, vertexBuffer, textureBuffer);
if (!onlyWaterSurface)
- uploadZoneTileRenderables(ctx, zone, t, vertexBuffer, alphaBuffer, textureBuffer);
+ uploadZoneTileRenderables(ctx, zone, t, vertexBuffer, alphaBuffer, textureBuffer, modelBuffer);
Tile bridge = t.getBridge();
if (bridge != null)
- uploadZoneTile(ctx, zone, bridge, true, onlyWaterSurface, vertexBuffer, alphaBuffer, textureBuffer);
+ uploadZoneTile(ctx, zone, bridge, true, onlyWaterSurface, vertexBuffer, alphaBuffer, textureBuffer, modelBuffer);
}
private void uploadZoneTileRenderables(
@@ -509,7 +515,8 @@ private void uploadZoneTileRenderables(
Tile t,
GpuIntBuffer vertexBuffer,
GpuIntBuffer alphaBuffer,
- GpuIntBuffer textureBuffer
+ GpuIntBuffer textureBuffer,
+ GpuIntBuffer modelBuffer
) {
WallObject wallObject = t.getWallObject();
if (wallObject != null && renderCallbackManager.drawObject(ctx.scene, wallObject)) {
@@ -533,7 +540,8 @@ private void uploadZoneTileRenderables(
wallObject.getId(),
vertexBuffer,
alphaBuffer,
- textureBuffer
+ textureBuffer,
+ modelBuffer
);
Renderable renderable2 = wallObject.getRenderable2();
@@ -555,7 +563,8 @@ private void uploadZoneTileRenderables(
wallObject.getId(),
vertexBuffer,
alphaBuffer,
- textureBuffer
+ textureBuffer,
+ modelBuffer
);
}
@@ -582,7 +591,8 @@ private void uploadZoneTileRenderables(
decorativeObject.getId(),
vertexBuffer,
alphaBuffer,
- textureBuffer
+ textureBuffer,
+ modelBuffer
);
Renderable renderable2 = decorativeObject.getRenderable2();
@@ -604,7 +614,8 @@ private void uploadZoneTileRenderables(
decorativeObject.getId(),
vertexBuffer,
alphaBuffer,
- textureBuffer
+ textureBuffer,
+ modelBuffer
);
}
@@ -626,7 +637,8 @@ private void uploadZoneTileRenderables(
groundObject.getId(),
vertexBuffer,
alphaBuffer,
- textureBuffer
+ textureBuffer,
+ modelBuffer
);
}
@@ -659,7 +671,8 @@ private void uploadZoneTileRenderables(
gameObject.getId(),
vertexBuffer,
alphaBuffer,
- textureBuffer
+ textureBuffer,
+ modelBuffer
);
}
}
@@ -688,6 +701,7 @@ private void estimateRenderableSize(Zone z, Renderable r, ModelOverride modelOve
z.sizeA += faceCount;
}
z.sizeF += faceCount;
+ z.sizeM++;
}
private void uploadZoneRenderable(
@@ -708,7 +722,8 @@ private void uploadZoneRenderable(
int id,
GpuIntBuffer opaqueBuffer,
GpuIntBuffer alphaBuffer,
- GpuIntBuffer textureBuffer
+ GpuIntBuffer textureBuffer,
+ GpuIntBuffer modelBuffer
) {
Model model;
if (r instanceof Model) {
@@ -737,12 +752,13 @@ private void uploadZoneRenderable(
int alphaStart = alphaBuffer != null ? alphaBuffer.position() : 0;
try {
uploadStaticModel(
- ctx, tile, model, modelOverride, uuid,
+ ctx, tile, model, modelOverride, zone, uuid,
preOrientation, orient,
x - basex, y, z - basez,
opaqueBuffer,
alphaBuffer,
- textureBuffer
+ textureBuffer,
+ modelBuffer
);
} catch (Throwable ex) {
log.warn(
@@ -777,6 +793,7 @@ private void uploadZoneRenderable(
materialManager,
zone.glVaoA,
zone.tboF.getTexId(),
+ zone.tboM.getTexId(),
model, modelOverride, alphaStart, alphaEnd,
x - basex, y, z - basez,
lx, lz, ux, uz,
@@ -1341,18 +1358,49 @@ private void uploadTileModel(
}
}
+ private static void writeModelData(IntBuffer modelBuffer, int x, int y, int z, Model model, ModelOverride override, Zone zone, boolean isStatic) {
+ modelBuffer
+ .put(x)
+ .put(y)
+ .put(z)
+ .put(model.getModelHeight())
+ .put(Float.floatToIntBits(isStatic ? -1.0f : saturate(zone.fadingAlpha)));
+ }
+
+ public static int writeStaticModelData(IntBuffer modelBuffer, int x, int y, int z, Model model, ModelOverride override, Zone zone) {
+ final int modelDataSizeInts = Zone.MODEL_DATA_SIZE / Integer.BYTES;
+ final int modelIdx = modelBuffer.position() / modelDataSizeInts;
+
+ writeModelData(modelBuffer, x, y, z, model, override, zone, true);
+
+ assert modelBuffer.position() % modelDataSizeInts == 0;
+ return modelIdx + 1;
+ }
+
+ public static int writeDynamicModelData(GLMappedBufferIntWriter.ReservedView view, int x, int y, int z, Model model, ModelOverride override, Zone zone) {
+ final int modelDataSizeInts = Zone.MODEL_DATA_SIZE / Integer.BYTES;
+ final int modelIdx = view.getBufferOffsetInts() / modelDataSizeInts;
+
+ writeModelData(view.getBuffer(), x, y, z, model, override, zone, false);
+
+ assert view.getBufferOffsetInts() % modelDataSizeInts == 0;
+ return modelIdx + 1;
+ }
+
// scene upload
private int uploadStaticModel(
ZoneSceneContext ctx,
Tile tile,
Model model,
ModelOverride modelOverride,
+ Zone zone,
int uuid,
int preOrientation, int orientation,
int x, int y, int z,
GpuIntBuffer opaqueBuffer,
GpuIntBuffer alphaBuffer,
- GpuIntBuffer textureBuffer
+ GpuIntBuffer textureBuffer,
+ GpuIntBuffer modelBuffer
) {
if (writeCache == null)
writeCache = new VertexWriteCache.Collection();
@@ -1442,6 +1490,8 @@ private int uploadStaticModel(
final Material baseMaterial = modelOverride.baseMaterial;
final Material textureMaterial = modelOverride.textureMaterial;
+ final int modelIdx = writeStaticModelData(modelBuffer.getBuffer(), x, y, z, model, modelOverride, zone);
+
int len = 0;
for (int face = 0; face < faceCount; ++face) {
int color1 = color1s[face];
@@ -1673,31 +1723,27 @@ private int uploadStaticModel(
color2 |= packedAlphaBiasHsl;
color3 |= packedAlphaBiasHsl;
- final int texturedFaceIdx = tb.putFace(
- color1, color2, color3,
- materialData, materialData, materialData,
- 0, 0, 0
- );
+ final int texturedFaceIdx = tb.putModelFace(color1, color2, color3, materialData);
vb.putStaticVertex(
vx1, vy1, vz1,
faceUVs[0], faceUVs[1], faceUVs[2],
modelNormals[0], modelNormals[1], modelNormals[2],
- texturedFaceIdx
+ texturedFaceIdx, modelIdx
);
vb.putStaticVertex(
vx2, vy2, vz2,
faceUVs[4], faceUVs[5], faceUVs[6],
modelNormals[3], modelNormals[4], modelNormals[5],
- texturedFaceIdx
+ texturedFaceIdx, modelIdx
);
vb.putStaticVertex(
vx3, vy3, vz3,
faceUVs[8], faceUVs[9], faceUVs[10],
modelNormals[6], modelNormals[7], modelNormals[8],
- texturedFaceIdx
+ texturedFaceIdx, modelIdx
);
len += 3;
}
@@ -1932,6 +1978,8 @@ public void uploadTempModel(
ModelOverride modelOverride,
int preOrientation,
int orientation,
+ int opaqueModelIdx,
+ int alphaModelIdx,
boolean isShadow,
DynamicModelVAO.View opaqueView,
DynamicModelVAO.View alphaView
@@ -1941,8 +1989,8 @@ public void uploadTempModel(
writeCache.setOutputBuffers(
opaqueView.vbo.getBuffer(),
alphaView.vbo.getBuffer(),
- opaqueView.tbo.getBuffer(),
- alphaView.tbo.getBuffer()
+ opaqueView.tboF.getBuffer(),
+ alphaView.tboF.getBuffer()
);
final int[] indices1 = model.getFaceIndices1();
@@ -2095,29 +2143,26 @@ else if (color3 == -1)
color2 |= packedAlphaBiasHsl;
color3 |= packedAlphaBiasHsl;
- final int texturedFaceIdx = tb.putFace(
- color1, color2, color3,
- materialData, materialData, materialData,
- 0, 0, 0
- );
+ final int modelIdx = hasAlpha ? alphaModelIdx : opaqueModelIdx;
+ final int texturedFaceIdx = tb.putModelFace(color1, color2, color3, materialData);
vb.putVertex(
modelLocalI[vertexOffsetA], modelLocalI[vertexOffsetA + 1], modelLocalI[vertexOffsetA + 2],
faceUVs[0], faceUVs[1], faceUVs[2],
faceNormals[0], faceNormals[1], faceNormals[2],
- texturedFaceIdx
+ texturedFaceIdx, modelIdx
);
vb.putVertex(
modelLocalI[vertexOffsetB], modelLocalI[vertexOffsetB + 1], modelLocalI[vertexOffsetB + 2],
faceUVs[4], faceUVs[5], faceUVs[6],
faceNormals[3], faceNormals[4], faceNormals[5],
- texturedFaceIdx
+ texturedFaceIdx, modelIdx
);
vb.putVertex(
modelLocalI[vertexOffsetC], modelLocalI[vertexOffsetC + 1], modelLocalI[vertexOffsetC + 2],
faceUVs[8], faceUVs[9], faceUVs[10],
faceNormals[6], faceNormals[7], faceNormals[8],
- texturedFaceIdx
+ texturedFaceIdx, modelIdx
);
}
diff --git a/src/main/java/rs117/hd/renderer/zone/VertexWriteCache.java b/src/main/java/rs117/hd/renderer/zone/VertexWriteCache.java
index b72914f70c..91c674e7ba 100644
--- a/src/main/java/rs117/hd/renderer/zone/VertexWriteCache.java
+++ b/src/main/java/rs117/hd/renderer/zone/VertexWriteCache.java
@@ -38,7 +38,7 @@ private void flushAndGrow() {
stagingBuffer = new int[min(stagingBuffer.length * 2, maxCapacity)];
}
- public int putFace(
+ public int putStaticFace(
int alphaBiasHslA, int alphaBiasHslB, int alphaBiasHslC,
int materialDataA, int materialDataB, int materialDataC,
int terrainDataA, int terrainDataB, int terrainDataC
@@ -46,7 +46,7 @@ public int putFace(
if (stagingPosition + 9 > stagingBuffer.length)
flushAndGrow();
- final int textureFaceIdx = (outputBuffer.position() + stagingPosition) / 3;
+ final int textureFaceIdx = outputBuffer.position() + stagingPosition;
final int[] stagingBuffer = this.stagingBuffer;
final int stagingPosition = this.stagingPosition;
@@ -64,14 +64,32 @@ public int putFace(
this.stagingPosition += 9;
- return textureFaceIdx;
+ return textureFaceIdx << 1;
+ }
+
+ public int putModelFace(int alphaBiasHslA, int alphaBiasHslB, int alphaBiasHslC, int materialData) {
+ if (stagingPosition + 4 > stagingBuffer.length)
+ flushAndGrow();
+
+ final int textureFaceIdx = outputBuffer.position() + stagingPosition;
+
+ final int[] stagingBuffer = this.stagingBuffer;
+ final int stagingPosition = this.stagingPosition;
+
+ stagingBuffer[stagingPosition] = alphaBiasHslA;
+ stagingBuffer[stagingPosition + 1] = alphaBiasHslB;
+ stagingBuffer[stagingPosition + 2] = alphaBiasHslC;
+ stagingBuffer[stagingPosition + 3] = materialData;
+
+ this.stagingPosition += 4;
+ return 1 | textureFaceIdx << 1;
}
public void putVertex(
int x, int y, int z,
float u, float v, float w,
int nx, int ny, int nz,
- int textureFaceIdx
+ int textureFaceIdx, int modelIdx
) {
if (stagingPosition + 8 > stagingBuffer.length)
flushAndGrow();
@@ -79,13 +97,15 @@ public void putVertex(
final int[] stagingBuffer = this.stagingBuffer;
final int stagingPosition = this.stagingPosition;
+ assert modelIdx < 0xFFFF;
+
stagingBuffer[stagingPosition] = x;
stagingBuffer[stagingPosition + 1] = y;
stagingBuffer[stagingPosition + 2] = z;
stagingBuffer[stagingPosition + 3] = float16(v) << 16 | float16(u);
stagingBuffer[stagingPosition + 4] = float16(w);
stagingBuffer[stagingPosition + 5] = (ny & 0xFFFF) << 16 | nx & 0xFFFF;
- stagingBuffer[stagingPosition + 6] = nz & 0xFFFF;
+ stagingBuffer[stagingPosition + 6] = (modelIdx & 0xFFFF) << 16 | nz & 0xFFFF;
stagingBuffer[stagingPosition + 7] = textureFaceIdx;
this.stagingPosition += 8;
@@ -95,7 +115,7 @@ public void putStaticVertex(
int x, int y, int z,
float u, float v, float w,
int nx, int ny, int nz,
- int textureFaceIdx
+ int textureFaceIdx, int modelIdx
) {
if (stagingPosition + 7 > stagingBuffer.length)
flushAndGrow();
@@ -109,7 +129,7 @@ public void putStaticVertex(
stagingBuffer[stagingPosition + 3] = float16(w);
// Unnormalized normals, assumed to be within short max
stagingBuffer[stagingPosition + 4] = (ny & 0xFFFF) << 16 | nx & 0xFFFF;
- stagingBuffer[stagingPosition + 5] = nz & 0xFFFF;
+ stagingBuffer[stagingPosition + 5] = (modelIdx & 0xFFFF) << 16 | nz & 0xFFFF;
stagingBuffer[stagingPosition + 6] = textureFaceIdx;
this.stagingPosition += 7;
diff --git a/src/main/java/rs117/hd/renderer/zone/WorldViewContext.java b/src/main/java/rs117/hd/renderer/zone/WorldViewContext.java
index 429c67f3d1..79a79dba86 100644
--- a/src/main/java/rs117/hd/renderer/zone/WorldViewContext.java
+++ b/src/main/java/rs117/hd/renderer/zone/WorldViewContext.java
@@ -27,6 +27,7 @@
import static rs117.hd.renderer.zone.DynamicModelVAO.METADATA_SIZE;
import static rs117.hd.renderer.zone.SceneManager.NUM_ZONES;
import static rs117.hd.renderer.zone.ZoneRenderer.FRAMES_IN_FLIGHT;
+import static rs117.hd.utils.MathUtils.*;
@Slf4j
public class WorldViewContext {
@@ -193,15 +194,6 @@ void handleZoneSwap(int zx, int zz, boolean queue) {
if (uploadTask == null)
return;
- if (!uploadTask.isQueued()) {
- if (queue && uploadTask.revealAfterTimestampMs < System.currentTimeMillis()) {
- log.trace("queueing zone({}): [{}-{},{}]", uploadTask.zone.hashCode(), worldViewId, zx, zz);
- uploadTask.revealAfterTimestampMs = 0;
- uploadTask.queue(streamingGroup, sceneManager.getGenerateSceneDataTask());
- }
- return;
- }
-
if (uploadTask.isDone()) {
curZone.uploadJob = null;
if (uploadTask.ranToCompletion() && !uploadTask.wasCancelled()) {
@@ -241,9 +233,18 @@ void handleZoneSwap(int zx, int zz, boolean queue) {
}
void processZoneSwaps() {
- for (int x = 0; x < sizeX; x++)
- for (int z = 0; z < sizeZ; z++)
+ for (int x = 0; x < sizeX; x++) {
+ for (int z = 0; z < sizeZ; z++) {
handleZoneSwap(x, z, true);
+
+ final Zone zone = zones[x][z];
+ if(zone.fadingAlpha <= 0)
+ continue;
+
+ zone.fadingAlpha = max(0.0f, zone.fadingAlpha - plugin.deltaTime);
+ zone.setMetadata(this, sceneContext, x, z);
+ }
+ }
}
void processZoneRebuilds() {
@@ -311,7 +312,6 @@ void invalidate() {
void invalidateZone(int zx, int zz) {
Zone curZone = zones[zx][zz];
- long revealAfterTimestampMs = 0;
if (curZone.uploadJob != null) {
Zone pendingZone = curZone.uploadJob.zone;
log.trace(
@@ -322,7 +322,6 @@ void invalidateZone(int zx, int zz) {
zz,
pendingZone.hashCode()
);
- revealAfterTimestampMs = curZone.uploadJob.revealAfterTimestampMs;
curZone.uploadJob.cancel();
curZone.uploadJob.release();
@@ -334,10 +333,6 @@ void invalidateZone(int zx, int zz) {
newZone.dirty = zones[zx][zz].dirty;
curZone.uploadJob = ZoneUploadJob.build(this, sceneContext, newZone, false, zx, zz);
- curZone.uploadJob.revealAfterTimestampMs = revealAfterTimestampMs;
-
- // Queue right away, so we can wait for it while in the POH in order to hide building mode placeholders
- if (sceneContext.isInHouse || revealAfterTimestampMs <= 0)
- curZone.uploadJob.queue(invalidationGroup, sceneManager.getGenerateSceneDataTask());
+ curZone.uploadJob.queue(invalidationGroup, sceneManager.getGenerateSceneDataTask());
}
}
diff --git a/src/main/java/rs117/hd/renderer/zone/Zone.java b/src/main/java/rs117/hd/renderer/zone/Zone.java
index 97f8004142..f3440ac91a 100644
--- a/src/main/java/rs117/hd/renderer/zone/Zone.java
+++ b/src/main/java/rs117/hd/renderer/zone/Zone.java
@@ -32,6 +32,7 @@
import static rs117.hd.HdPlugin.GL_CAPS;
import static rs117.hd.HdPlugin.SUPPORTS_INDIRECT_DRAW;
import static rs117.hd.HdPlugin.checkGLErrors;
+import static rs117.hd.renderer.zone.ZoneRenderer.TEXTURE_UNIT_MODEL_DATA;
import static rs117.hd.renderer.zone.ZoneRenderer.TEXTURE_UNIT_TEXTURED_FACES;
import static rs117.hd.renderer.zone.ZoneRenderer.eboAlpha;
import static rs117.hd.utils.MathUtils.*;
@@ -53,10 +54,16 @@ public class Zone implements Destructible {
// terrainData ivec3
public static final int TEXTURE_SIZE = 36;
+ // position vec3
+ // height float
+ // fade float
+ public static final int MODEL_DATA_SIZE = 20;
+
// Metadata format
// worldViewIndex int int
// sceneOffset int vec2(x, y)
- public static final int METADATA_SIZE = 12;
+ // fade float float
+ public static final int METADATA_SIZE = 16;
public static final int LEVEL_WATER_SURFACE = 4;
@@ -67,10 +74,10 @@ public class Zone implements Destructible {
public int glVaoA;
public int bufLenA;
- public int sizeO, sizeA, sizeF;
+ public int sizeO, sizeA, sizeF, sizeM;
@Nullable
public GLBuffer vboO, vboA, vboM;
- public GLTextureBuffer tboF;
+ public GLTextureBuffer tboF, tboM;
public boolean initialized; // whether the zone vao and vbos are ready
public boolean cull; // whether the zone is queued for deletion
@@ -87,6 +94,7 @@ public class Zone implements Destructible {
final StaticAlphaSortingJob alphaSortingJob = new StaticAlphaSortingJob();
ZoneUploadJob uploadJob;
+ float fadingAlpha;
int[] levelOffsets = new int[5]; // buffer pos in ints for the end of the level
@@ -97,7 +105,7 @@ public class Zone implements Destructible {
final List alphaModels = new ArrayList<>(0);
final ConcurrentLinkedQueue pendingModelJobs = new ConcurrentLinkedQueue<>();
- public void initialize(GLBuffer o, GLBuffer a, GLTextureBuffer f) {
+ public void initialize(GLBuffer o, GLBuffer a, GLTextureBuffer f, GLTextureBuffer m) {
assert glVao == 0;
assert glVaoA == 0;
if (o == null && a == null || f == null)
@@ -119,6 +127,7 @@ public void initialize(GLBuffer o, GLBuffer a, GLTextureBuffer f) {
}
tboF = f;
+ tboM = m;
}
public static void freeZones(@Nullable Zone[][] zones) {
@@ -160,6 +169,11 @@ public void destroy() {
tboF = null;
}
+ if (tboM != null) {
+ tboM.destroy();
+ tboM = null;
+ }
+
if (glVao != 0) {
glDeleteVertexArrays(glVao);
glVao = 0;
@@ -218,6 +232,8 @@ public void unmap() {
vboA.unmap();
if (tboF != null)
tboF.unmap();
+ if(tboM != null)
+ tboM.unmap();
if (vboO != null) {
this.bufLen = vboO.mapped().byteView().position() / VERT_SIZE;
@@ -260,6 +276,11 @@ private void setupVao(int vao, int buffer, int metadata) {
glVertexAttribDivisor(7, 1);
glVertexAttribIPointer(7, 2, GL_INT, METADATA_SIZE, 4);
+ // Scene offset
+ glEnableVertexAttribArray(8);
+ glVertexAttribDivisor(8, 1);
+ glVertexAttribPointer(8, 1, GL_FLOAT, false, METADATA_SIZE, 12);
+
checkGLErrors();
glBindVertexArray(0);
@@ -270,14 +291,16 @@ public void setMetadata(WorldViewContext viewContext, SceneContext sceneContext,
if (vboM == null)
return;
+ float fade = saturate(fadingAlpha);
int baseX = (mx - (sceneContext.sceneOffset >> 3)) << 10;
int baseZ = (mz - (sceneContext.sceneOffset >> 3)) << 10;
try (MemoryStack stack = MemoryStack.stackPush()) {
- IntBuffer buf = stack.mallocInt(3)
+ IntBuffer buf = stack.mallocInt(METADATA_SIZE / Integer.BYTES)
.put(viewContext.uboWorldViewStruct != null ? viewContext.uboWorldViewStruct.worldViewIdx + 1 : 0)
.put(baseX)
- .put(baseZ);
+ .put(baseZ)
+ .put(Float.floatToIntBits(fade));
buf.flip();
vboM.upload(buf);
}
@@ -319,6 +342,8 @@ private void convertForDraw(int vertSize) {
}
void renderOpaque(CommandBuffer cmd, WorldViewContext ctx, boolean roofShadows) {
+ if(fadingAlpha > 1.0)
+ return;
drawIdx = 0;
int currentLevel = ctx.level;
@@ -372,10 +397,14 @@ void renderOpaque(CommandBuffer cmd, WorldViewContext ctx, boolean roofShadows)
lastDrawMode = STATIC_UNSORTED;
lastVao = glVao;
lastTboF = tboF.getTexId();
+ lastTboM = tboM != null ? tboM.getTexId() : 0;
flush(cmd);
}
void renderOpaqueLevel(CommandBuffer cmd, int level) {
+ if(fadingAlpha > 1.0)
+ return;
+
drawIdx = 0;
pushRange(this.levelOffsets[level - 1], this.levelOffsets[level]);
@@ -386,6 +415,7 @@ void renderOpaqueLevel(CommandBuffer cmd, int level) {
lastDrawMode = STATIC_UNSORTED;
lastVao = glVao;
lastTboF = tboF.getTexId();
+ lastTboM = tboM != null ? tboM.getTexId() : 0;
flush(cmd);
}
@@ -411,6 +441,7 @@ public static class AlphaModel {
short rid;
int vao;
int tboF;
+ int tboM;
byte level;
byte lx, lz, ux, uz; // lower/upper zone coords
byte zofx, zofz; // for temp alpha models, offset of source zone from target zone
@@ -443,7 +474,8 @@ boolean isTemp() {
void setView(DynamicModelVAO.View view) {
vao = view.vao;
- tboF = view.tboTexId;
+ tboF = view.tboFId;
+ tboM = view.tboMId;
startpos = view.getStartOffset();
endpos = view.getEndOffset();
}
@@ -456,6 +488,7 @@ void addAlphaModel(
MaterialManager materialManager,
int vao,
int tboF,
+ int tboM,
Model model,
ModelOverride modelOverride,
int startpos,
@@ -481,6 +514,7 @@ void addAlphaModel(
m.z = (short) z;
m.vao = vao;
m.tboF = tboF;
+ m.tboM = tboM;
m.rid = (short) rid;
m.level = (byte) level;
if (lx > -1) {
@@ -624,7 +658,7 @@ synchronized AlphaModel requestTempAlphaModel(ModelOverride modelOverride, int l
m.y = (short) y;
m.z = (short) z;
m.level = (byte) level;
- m.vao = m.tboF = m.rid = m.lx = m.lz = m.ux = m.uz = -1;
+ m.vao = m.tboF = m.tboM = m.rid = m.lx = m.lz = m.ux = m.uz = -1;
m.flags = 0;
m.zofx = m.zofz = 0;
alphaModels.add(m);
@@ -657,6 +691,7 @@ synchronized void postAlphaPass() {
private static int lastDrawMode;
private static int lastVao;
private static int lastTboF;
+ private static int lastTboM;
private static int lastzx, lastzz;
private static final class AlphaSortPredicate implements ToIntFunction {
@@ -707,7 +742,7 @@ void renderAlpha(
boolean isShadowPass,
boolean includeRoof
) {
- if (alphaModels.isEmpty())
+ if (alphaModels.isEmpty() || fadingAlpha > 1.0)
return;
int minLevel = ctx.minLevel;
@@ -747,6 +782,7 @@ void renderAlpha(
if (lastDrawMode != drawMode ||
lastVao != m.vao ||
lastTboF != m.tboF ||
+ lastTboM != m.tboM ||
lastzx != (zx - m.zofx) ||
lastzz != (zz - m.zofz)
) {
@@ -754,6 +790,7 @@ void renderAlpha(
lastDrawMode = drawMode;
lastVao = m.vao;
lastTboF = m.tboF;
+ lastTboM = m.tboM;
lastzx = zx - m.zofx;
lastzz = zz - m.zofz;
}
@@ -797,6 +834,7 @@ private void flush(CommandBuffer cmd) {
long byteOffset = 4L * (eboAlphaOffset - vertexCount);
cmd.BindVertexArray(lastVao, eboAlpha);
cmd.BindTextureUnit(GL_TEXTURE_BUFFER, lastTboF, TEXTURE_UNIT_TEXTURED_FACES);
+ cmd.BindTextureUnit(GL_TEXTURE_BUFFER, lastTboM, TEXTURE_UNIT_MODEL_DATA);
// The EBO & IDO is bound by in ZoneRenderer
if (GL_CAPS.OpenGL40 && SUPPORTS_INDIRECT_DRAW) {
cmd.DrawElementsIndirect(GL_TRIANGLES, vertexCount, (int) (byteOffset / 4L), ZoneRenderer.indirectDrawCmdsStaging);
@@ -809,6 +847,7 @@ private void flush(CommandBuffer cmd) {
convertForDraw(lastDrawMode == STATIC_UNSORTED ? VERT_SIZE : DynamicModelVAO.VERT_SIZE);
cmd.BindVertexArray(lastVao);
cmd.BindTextureUnit(GL_TEXTURE_BUFFER, lastTboF, TEXTURE_UNIT_TEXTURED_FACES);
+ cmd.BindTextureUnit(GL_TEXTURE_BUFFER, lastTboM, TEXTURE_UNIT_MODEL_DATA);
if (drawIdx == 1) {
if (GL_CAPS.OpenGL40 && SUPPORTS_INDIRECT_DRAW) {
cmd.DrawArraysIndirect(GL_TRIANGLES, drawOff[0], drawEnd[0], ZoneRenderer.indirectDrawCmdsStaging);
@@ -880,6 +919,7 @@ synchronized void multizoneLocs(SceneContext ctx, int zx, int zz, Camera camera,
m2.z = m.z;
m2.vao = m.vao;
m2.tboF = m.tboF;
+ m2.tboM = m.tboM;
m2.rid = m.rid;
m2.level = m.level;
m2.lx = m.lx;
diff --git a/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java b/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java
index be35d19b5c..c4daf422c1 100644
--- a/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java
+++ b/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java
@@ -91,6 +91,7 @@ public class ZoneRenderer implements Renderer {
private static int TEXTURE_UNIT_COUNT = HdPlugin.TEXTURE_UNIT_COUNT;
public static final int TEXTURE_UNIT_TEXTURED_FACES = GL_TEXTURE0 + TEXTURE_UNIT_COUNT++;
+ public static final int TEXTURE_UNIT_MODEL_DATA = GL_TEXTURE0 + TEXTURE_UNIT_COUNT++;
private static int UNIFORM_BLOCK_COUNT = HdPlugin.UNIFORM_BLOCK_COUNT;
public static final int UNIFORM_BLOCK_WORLD_VIEWS = UNIFORM_BLOCK_COUNT++;
diff --git a/src/main/java/rs117/hd/renderer/zone/ZoneUploadJob.java b/src/main/java/rs117/hd/renderer/zone/ZoneUploadJob.java
index e7f41ee7a0..b7db764ef5 100644
--- a/src/main/java/rs117/hd/renderer/zone/ZoneUploadJob.java
+++ b/src/main/java/rs117/hd/renderer/zone/ZoneUploadJob.java
@@ -20,7 +20,6 @@ public final class ZoneUploadJob extends Job {
Zone zone;
int x, z;
- long revealAfterTimestampMs;
boolean shouldUnmap;
@Override
@@ -72,12 +71,20 @@ private void mapZoneVertexBuffers() {
GLTextureBuffer f = null;
sz = zone.sizeF * Zone.TEXTURE_SIZE;
if (sz > 0) {
- f = new GLTextureBuffer("Zone::TBO", GL_STATIC_DRAW);
+ f = new GLTextureBuffer("Zone::TexturedFaces", GL_STATIC_DRAW);
f.initialize(sz);
f.map(MAP_WRITE);
}
- zone.initialize(o, a, f);
+ GLTextureBuffer m = null;
+ sz = zone.sizeM * Zone.MODEL_DATA_SIZE;
+ if (sz > 0) {
+ m = new GLTextureBuffer("Zone::ModelData", GL_STATIC_DRAW);
+ m.initialize(sz);
+ m.map(MAP_WRITE);
+ }
+
+ zone.initialize(o, a, f, m);
zone.setMetadata(viewContext, sceneContext, x, z);
} catch (Throwable ex) {
log.warn(
@@ -108,7 +115,6 @@ protected void onReleased() {
sceneContext = null;
zone.uploadJob = null;
zone = null;
- revealAfterTimestampMs = 0;
assert !POOL.contains(this) : "Task is already in pool";
POOL.add(this);
}
diff --git a/src/main/java/rs117/hd/utils/buffer/GLTextureBuffer.java b/src/main/java/rs117/hd/utils/buffer/GLTextureBuffer.java
index 1b58183741..713c993c93 100644
--- a/src/main/java/rs117/hd/utils/buffer/GLTextureBuffer.java
+++ b/src/main/java/rs117/hd/utils/buffer/GLTextureBuffer.java
@@ -1,55 +1,62 @@
package rs117.hd.utils.buffer;
import lombok.Getter;
+import rs117.hd.HdPlugin;
import static org.lwjgl.opengl.GL33C.*;
+import static rs117.hd.HdPlugin.GL_CAPS;
public class GLTextureBuffer extends GLBuffer {
+
+ public static boolean isRGBASupported() { return HdPlugin.GL_CAPS.GL_ARB_texture_buffer_object; }
+
@Getter
private int texId;
+ private final int internalFormat;
+
public GLTextureBuffer(String name, int usage) {
this(name, usage, 0);
}
public GLTextureBuffer(String name, int usage, int storageFlags) {
super(name, GL_TEXTURE_BUFFER, usage, storageFlags);
+
+ internalFormat = isRGBASupported() ? GL_RGBA32I : GL_RGB32I;
}
@Override
public GLTextureBuffer initialize(long initialCapacity) {
super.initialize(initialCapacity);
- // Create texture
texId = glGenTextures();
glBindTexture(target, texId);
-
- // RGB32 signed integer texture buffer
- glTexBuffer(target, GL_RGB32I, id);
-
+ glTexBuffer(target, internalFormat, id);
glBindTexture(target, 0);
+
return this;
}
@Override
public boolean ensureCapacity(long byteOffset, long numBytes) {
int oldId = id;
- boolean resized = super.ensureCapacity(byteOffset, numBytes);
+ final boolean resized = super.ensureCapacity(byteOffset, numBytes);
+
if (oldId != id) {
glBindTexture(target, texId);
- glTexBuffer(target, GL_RGB32I, id);
+ glTexBuffer(target, internalFormat, id);
glBindTexture(target, 0);
}
+
return resized;
}
@Override
public void destroy() {
- if (texId != 0) {
+ if (texId != 0)
glDeleteTextures(texId);
- texId = 0;
- }
+ texId = 0;
super.destroy();
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/rs117/hd/utils/buffer/GpuIntBuffer.java b/src/main/java/rs117/hd/utils/buffer/GpuIntBuffer.java
index cdf5c6e79a..c3a543c973 100644
--- a/src/main/java/rs117/hd/utils/buffer/GpuIntBuffer.java
+++ b/src/main/java/rs117/hd/utils/buffer/GpuIntBuffer.java
@@ -145,7 +145,7 @@ public static int putFace(
int materialDataA, int materialDataB, int materialDataC,
int terrainDataA, int terrainDataB, int terrainDataC
) {
- final int textureFaceIdx = buffer.position() / 3;
+ final int textureFaceIdx = buffer.position();
buffer.put(alphaBiasHslA);
buffer.put(alphaBiasHslB);
buffer.put(alphaBiasHslC);
@@ -157,7 +157,7 @@ public static int putFace(
buffer.put(terrainDataA); // TODO: Remove?
buffer.put(terrainDataB);
buffer.put(terrainDataC);
- return textureFaceIdx;
+ return textureFaceIdx << 1;
}
public int position() {
diff --git a/src/main/resources/rs117/hd/scene_frag.glsl b/src/main/resources/rs117/hd/scene_frag.glsl
index bcc9ac06f0..2f5de40194 100644
--- a/src/main/resources/rs117/hd/scene_frag.glsl
+++ b/src/main/resources/rs117/hd/scene_frag.glsl
@@ -32,6 +32,8 @@
#define DISPLAY_SHADOWS 0
#define DISPLAY_LIGHTING 0
+#define NEAR_PLANE_DITHER_START 0.2
+
#include
#include
#include
@@ -50,8 +52,11 @@ flat in ivec3 fAlphaBiasHsl;
flat in ivec3 fMaterialData;
flat in ivec3 fTerrainData;
-#if FLAT_SHADING && ZONE_RENDERER
- flat in vec3 fFlatNormal;
+#if ZONE_RENDERER
+ #if FLAT_SHADING
+ flat in vec3 fFlatNormal;
+ #endif
+ flat in float fFade;
#endif
in FragmentData {
@@ -84,6 +89,17 @@ vec2 worldUvs(float scale) {
void main() {
vec3 downDir = vec3(0, -1, 0);
+#if DITHER_FADE && ZONE_RENDERER
+ float viewZ = 1.0 - gl_FragCoord.z;
+ if (fFade > 0.0 || viewZ < NEAR_PLANE_DITHER_START) {
+ float fadeAmount = mix(1.0 - saturate(viewZ / NEAR_PLANE_DITHER_START), fFade, saturate(fFade));
+ float threshold = smoothstep(0.0, 1.0, pow(fadeAmount, 1.35));
+ float noise = interleavedGradientNoise(gl_FragCoord.xy);
+ if (noise < threshold)
+ discard;
+ }
+#endif
+
// View & light directions are from the fragment to the camera/light
vec3 viewDir = normalize(cameraPos - IN.position);
diff --git a/src/main/resources/rs117/hd/scene_vert.glsl b/src/main/resources/rs117/hd/scene_vert.glsl
index 06b5268582..0f9b638ba0 100644
--- a/src/main/resources/rs117/hd/scene_vert.glsl
+++ b/src/main/resources/rs117/hd/scene_vert.glsl
@@ -27,20 +27,22 @@
#include
#include
+#include
+#include
#include
#include
+#include
layout (location = 0) in vec3 vPosition;
#if ZONE_RENDERER
layout (location = 1) in vec4 vUv;
layout (location = 2) in vec4 vNormal;
- layout (location = 3) in int vTextureFaceIdx;
+ layout (location = 3) in int vPackedTextureFace;
layout (location = 6) in int vWorldViewId;
layout (location = 7) in ivec2 vSceneBase;
-
- uniform isamplerBuffer textureFaces;
+ layout (location = 8) in float vFade;
#else
layout (location = 1) in vec3 vUv;
layout (location = 2) in vec3 vNormal;
@@ -51,6 +53,7 @@ layout (location = 0) in vec3 vPosition;
#if ZONE_RENDERER
flat out int fWorldViewId;
+ flat out float fFade;
flat out ivec3 fAlphaBiasHsl;
flat out ivec3 fMaterialData;
flat out ivec3 fTerrainData;
@@ -67,32 +70,39 @@ layout (location = 0) in vec3 vPosition;
} OUT;
void main() {
+ fWorldViewId = vWorldViewId;
+
int vertex = gl_VertexID % 3;
- bool isProvoking = vertex == 2;
- int materialData = 0;
- int alphaBiasHsl = 0;
-
- if (isProvoking) {
- // Only the Provoking vertex needs to fetch the face data
- fAlphaBiasHsl = texelFetch(textureFaces, vTextureFaceIdx).xyz;
- fMaterialData = texelFetch(textureFaces, vTextureFaceIdx + 1).xyz;
- fTerrainData = texelFetch(textureFaces, vTextureFaceIdx + 2).xyz;
- fWorldViewId = vWorldViewId;
- alphaBiasHsl = fAlphaBiasHsl[vertex];
- materialData = fMaterialData[vertex];
+ int alphaBiasHsl;
+ int materialData;
+
+ if(isModelFace(vPackedTextureFace)) {
+ ModelFaceData faceData = getModelFaceData(getFaceOffset(vPackedTextureFace));
+ fAlphaBiasHsl = faceData.AlphaBiasHsl;
+ fMaterialData = ivec3(faceData.MaterialData);
+ fTerrainData = ivec3(0);
+ alphaBiasHsl = faceData.AlphaBiasHsl[vertex];
+ materialData = faceData.MaterialData;
} else {
- // All outputs must be written to for macOS compatibility
- fAlphaBiasHsl = ivec3(0);
- fMaterialData = ivec3(0);
- fTerrainData = ivec3(0);
- fWorldViewId = 0;
- alphaBiasHsl = texelFetch(textureFaces, vTextureFaceIdx)[vertex];
- materialData = texelFetch(textureFaces, vTextureFaceIdx + 1)[vertex];
+ StaticFaceData faceData = getStaticFaceData(getFaceOffset(vPackedTextureFace));
+ fAlphaBiasHsl = faceData.AlphaBiasHsl;
+ fMaterialData = faceData.MaterialData;
+ fTerrainData = faceData.TerrainData;
+ alphaBiasHsl = faceData.AlphaBiasHsl[vertex];
+ materialData = faceData.MaterialData[vertex];
+ fFade = vFade;
}
vec3 sceneOffset = vec3(vSceneBase.x, 0, vSceneBase.y);
vec3 worldNormal = vNormal.xyz;
vec3 worldPosition = sceneOffset + vPosition;
+
+ int modelIdx = int(vNormal.w);
+ if(modelIdx > 0) {
+ ModelData modelData = getModelData(modelIdx);
+ fFade = max(modelData.fade, fFade);
+ }
+
if (vWorldViewId != -1) {
mat4x3 worldViewProjection = mat4x3(getWorldViewProjection(vWorldViewId));
worldPosition = worldViewProjection * vec4(worldPosition, 1.0);
diff --git a/src/main/resources/rs117/hd/shadow_vert.glsl b/src/main/resources/rs117/hd/shadow_vert.glsl
index 82ead2102d..b632a38ae5 100644
--- a/src/main/resources/rs117/hd/shadow_vert.glsl
+++ b/src/main/resources/rs117/hd/shadow_vert.glsl
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#include
@@ -35,12 +36,10 @@ layout (location = 0) in vec3 vPosition;
#if ZONE_RENDERER
layout (location = 1) in vec4 vUv;
- layout (location = 3) in int vTextureFaceIdx;
+ layout (location = 3) in int vPackedTextureFace;
layout (location = 6) in int vWorldViewId;
layout (location = 7) in ivec2 vSceneBase;
- uniform isamplerBuffer textureFaces;
-
#if SHADOW_MODE == SHADOW_MODE_DETAILED
out vec4 fUvw;
flat out int fMaterialData;
@@ -52,9 +51,22 @@ layout (location = 0) in vec3 vPosition;
void main() {
int vertex = gl_VertexID % 3;
- int alphaBiasHsl = texelFetch(textureFaces, vTextureFaceIdx)[vertex];
- int materialData = texelFetch(textureFaces, vTextureFaceIdx + 1)[vertex];
- int terrainData = texelFetch(textureFaces, vTextureFaceIdx + 2)[vertex];
+ int alphaBiasHsl;
+ int materialData;
+ int terrainData;
+ int faceDataOffset;
+
+ if(isModelFace(vPackedTextureFace)) {
+ ModelFaceData faceData = getModelFaceData(getFaceOffset(vPackedTextureFace));
+ alphaBiasHsl = faceData.AlphaBiasHsl[vertex];
+ materialData = faceData.MaterialData;
+ terrainData = 0;
+ } else {
+ StaticFaceData faceData = getStaticFaceData(getFaceOffset(vPackedTextureFace));
+ alphaBiasHsl = faceData.AlphaBiasHsl[vertex];
+ materialData = faceData.MaterialData[vertex];
+ terrainData = faceData.TerrainData[vertex];
+ }
int waterTypeIndex = terrainData >> 3 & 0xFF;
float opacity = 1 - (alphaBiasHsl >> 24 & 0xFF) / float(0xFF);
diff --git a/src/main/resources/rs117/hd/uniforms/model_data.glsl b/src/main/resources/rs117/hd/uniforms/model_data.glsl
new file mode 100644
index 0000000000..ed5aaa6d0d
--- /dev/null
+++ b/src/main/resources/rs117/hd/uniforms/model_data.glsl
@@ -0,0 +1,27 @@
+#pragma once
+
+#include
+
+#define MODEL_DATA_SIZE 5
+#define PARSER_TARGET_BUFFER modelData
+
+uniform isamplerBuffer modelData;
+
+struct ModelData {
+ ivec3 position;
+ int height;
+ float fade; // Used by Dynamic Models
+};
+
+
+BEGIN_BUFFER_PARSER(readModelData, ModelData)
+ READ_IVEC3(position)
+ READ_INT(height)
+ READ_FLOAT(fade)
+END_BUFFER_PARSER()
+
+ModelData getModelData(int modelIdx) {
+ return readModelData((modelIdx - 1) * MODEL_DATA_SIZE);
+}
+
+#undef PARSER_TARGET_BUFFER
\ No newline at end of file
diff --git a/src/main/resources/rs117/hd/uniforms/texture_faces.glsl b/src/main/resources/rs117/hd/uniforms/texture_faces.glsl
new file mode 100644
index 0000000000..8801f9ae9d
--- /dev/null
+++ b/src/main/resources/rs117/hd/uniforms/texture_faces.glsl
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+
+#define PARSER_TARGET_BUFFER textureFaces
+
+uniform isamplerBuffer textureFaces;
+
+struct StaticFaceData {
+ ivec3 AlphaBiasHsl;
+ ivec3 MaterialData;
+ ivec3 TerrainData;
+};
+
+struct ModelFaceData {
+ ivec3 AlphaBiasHsl;
+ int MaterialData;
+};
+
+bool isModelFace(int packedFaceData) {
+ return (packedFaceData & 1) == 1;
+}
+
+int getFaceOffset(int packedFaceData) {
+ return packedFaceData >> 1;
+}
+
+BEGIN_BUFFER_PARSER(getStaticFaceData,StaticFaceData)
+ READ_IVEC3(AlphaBiasHsl)
+ READ_IVEC3(MaterialData)
+ READ_IVEC3(TerrainData)
+END_BUFFER_PARSER()
+
+BEGIN_BUFFER_PARSER(getModelFaceData,ModelFaceData)
+ READ_IVEC3(AlphaBiasHsl)
+ READ_INT(MaterialData)
+END_BUFFER_PARSER()
+
+#undef PARSER_TARGET_BUFFER
\ No newline at end of file
diff --git a/src/main/resources/rs117/hd/utils/constants.glsl b/src/main/resources/rs117/hd/utils/constants.glsl
index 1ef390944e..7d19663951 100644
--- a/src/main/resources/rs117/hd/utils/constants.glsl
+++ b/src/main/resources/rs117/hd/utils/constants.glsl
@@ -62,6 +62,7 @@
#include FLAT_SHADING
#include APPLY_COLOR_FILTER
#include WIREFRAME
+#include DITHER_FADE
#include WIND_DISPLACEMENT
#include WIND_DISPLACEMENT_NOISE_RESOLUTION
#include CHARACTER_DISPLACEMENT
diff --git a/src/main/resources/rs117/hd/utils/misc.glsl b/src/main/resources/rs117/hd/utils/misc.glsl
index 6e5406693b..14e822a422 100644
--- a/src/main/resources/rs117/hd/utils/misc.glsl
+++ b/src/main/resources/rs117/hd/utils/misc.glsl
@@ -112,6 +112,66 @@ void undoVanillaShading(inout int hsl, vec3 unrotatedNormal) {
}
#endif
+// 2x2 Bayer via bit permutation
+// Generates ordered dithering Bayer matrix without a lookup table
+float bayer2x2(vec2 pixelCoord) {
+ uvec2 p = uvec2(pixelCoord) & 1u;
+ uint v =
+ ((p.x & 1u) << 1u) |
+ ((p.y & 1u) << 0u);
+
+ return (float(v) + 0.5) * (1.0 / 4.0);
+}
+
+// 4x4 Bayer via bit permutation
+// Generates ordered dithering Bayer matrix without a lookup table
+float bayer4x4(vec2 pixelCoord) {
+ uvec2 p = uvec2(pixelCoord) & 3u;
+ uint v =
+ ((p.x & 1u) << 3u) |
+ ((p.y & 1u) << 2u) |
+ ((p.x & 2u) << 0u) |
+ ((p.y & 2u) >> 1u);
+
+ return (float(v) + 0.5) * (1.0 / 16.0);
+}
+
+// 8x8 Bayer via bit permutation
+// Generates ordered dithering Bayer matrix without a lookup table
+float bayer8x8(vec2 pixelCoord) {
+ uvec2 p = uvec2(pixelCoord) & 7u;
+ uint v =
+ ((p.x & 1u) << 5u) |
+ ((p.y & 1u) << 4u) |
+ ((p.x & 2u) << 2u) |
+ ((p.y & 2u) << 1u) |
+ ((p.x & 4u) >> 1u) |
+ ((p.y & 4u) >> 2u);
+
+ return (float(v) + 0.5) * (1.0 / 64.0);
+}
+
+// Based on https://www.shadertoy.com/view/4t2cRt (merger doctrine)
+// Returns a dither value (0.0 or 1.0) based on coords & opacity
+bool orderedDither4x4(vec2 pixelCoord, float opacity, float scaleFactor) {
+ float threshold = bayer4x4(pixelCoord / scaleFactor);
+ return threshold < clamp(opacity, 0.0, 1.0);
+}
+
+bool orderedDither2x2(vec2 pixelCoord, float opacity, float scaleFactor) {
+ float threshold = bayer2x2(pixelCoord / scaleFactor);
+ return threshold < clamp(opacity, 0.0, 1.0);
+}
+
+bool orderedDither8x8(vec2 pixelCoord, float opacity, float scaleFactor) {
+ float threshold = bayer8x8(pixelCoord / scaleFactor);
+ return threshold < clamp(opacity, 0.0, 1.0);
+}
+
+float interleavedGradientNoise(vec2 p) {
+ return fract(52.9829189 * fract(dot(p, vec2(0.06711056, 0.00583715))));
+}
+
// 2D Random
float hash(in vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
diff --git a/src/main/resources/rs117/hd/utils/texture_buffer_reader.glsl b/src/main/resources/rs117/hd/utils/texture_buffer_reader.glsl
new file mode 100644
index 0000000000..d85e300788
--- /dev/null
+++ b/src/main/resources/rs117/hd/utils/texture_buffer_reader.glsl
@@ -0,0 +1,187 @@
+#pragma once
+
+// Number of scalar components per fetched texel.
+// Valid range: 1-4.
+#include TEXEL_SIZE
+#ifndef TEXEL_SIZE
+ #define TEXEL_SIZE 4
+#endif
+
+#if TEXEL_SIZE < 1 || TEXEL_SIZE > 4
+ #error TEXEL_SIZE must be between 1 and 4
+#endif
+
+// Sequential reader for tightly-packed scalar data stored in a buffer texture.
+//
+// Layout assumptions:
+// - Data is packed scalar-by-scalar with NO padding.
+// - Floats are stored as IEEE-754 bit patterns inside integer components.
+// - TEXEL_SIZE defines how many usable components exist per fetched texel.
+//
+// Example packed stream:
+// [int][float][vec3][ivec2]...
+//
+// This reader caches the currently loaded texel to avoid redundant texelFetch
+// calls during sequential access.
+
+struct TexBufferReader {
+ // Cached texel data.
+ // Always ivec4 regardless of TEXEL_SIZE.
+ ivec4 data;
+
+ // Current scalar position in stream.
+ int position;
+
+ // Currently cached texel index.
+ int loadedTexel;
+};
+
+TexBufferReader buildTexBufferReader(
+ int position
+) {
+ TexBufferReader reader;
+
+ reader.position = position;
+
+ reader.data = ivec4(0);
+ reader.loadedTexel = -1;
+
+ return reader;
+}
+
+int readInt(isamplerBuffer buf, inout TexBufferReader reader) {
+#if TEXEL_SIZE == 4
+ int texelIndex = reader.position >> 2;
+ int component = reader.position & 3;
+#else
+ int texelIndex = reader.position / TEXEL_SIZE;
+ int component = reader.position % TEXEL_SIZE;
+#endif
+
+ if (texelIndex != reader.loadedTexel) {
+ reader.data = texelFetch(buf, texelIndex);
+ reader.loadedTexel = texelIndex;
+ }
+
+ reader.position++;
+
+ switch (component) {
+ case 0: return reader.data.x;
+ case 1: return reader.data.y;
+ case 2: return reader.data.z;
+ default: return reader.data.w;
+ }
+}
+
+uint readUInt(isamplerBuffer buf, inout TexBufferReader reader) {
+ return uint(readInt(buf, reader));
+}
+
+float readFloat(isamplerBuffer buf, inout TexBufferReader reader) {
+ return intBitsToFloat(readInt(buf, reader));
+}
+
+bool readBool(isamplerBuffer buf, inout TexBufferReader reader) {
+ return readInt(buf, reader) != 0;
+}
+
+ivec2 readIVec2(isamplerBuffer buf, inout TexBufferReader reader) {
+ return ivec2(
+ readInt(buf, reader),
+ readInt(buf, reader)
+ );
+}
+
+ivec3 readIVec3(isamplerBuffer buf, inout TexBufferReader reader) {
+ return ivec3(
+ readInt(buf, reader),
+ readInt(buf, reader),
+ readInt(buf, reader)
+ );
+}
+
+ivec4 readIVec4(isamplerBuffer buf, inout TexBufferReader reader) {
+ return ivec4(
+ readInt(buf, reader),
+ readInt(buf, reader),
+ readInt(buf, reader),
+ readInt(buf, reader)
+ );
+}
+
+uvec2 readUVec2(isamplerBuffer buf, inout TexBufferReader reader) {
+ return uvec2(
+ readUInt(buf, reader),
+ readUInt(buf, reader)
+ );
+}
+
+uvec3 readUVec3(isamplerBuffer buf, inout TexBufferReader reader) {
+ return uvec3(
+ readUInt(buf, reader),
+ readUInt(buf, reader),
+ readUInt(buf, reader)
+ );
+}
+
+uvec4 readUVec4(isamplerBuffer buf, inout TexBufferReader reader) {
+ return uvec4(
+ readUInt(buf, reader),
+ readUInt(buf, reader),
+ readUInt(buf, reader),
+ readUInt(buf, reader)
+ );
+}
+
+vec2 readVec2(isamplerBuffer buf, inout TexBufferReader reader) {
+ return vec2(
+ readFloat(buf, reader),
+ readFloat(buf, reader)
+ );
+}
+
+vec3 readVec3(isamplerBuffer buf, inout TexBufferReader reader) {
+ return vec3(
+ readFloat(buf, reader),
+ readFloat(buf, reader),
+ readFloat(buf, reader)
+ );
+}
+
+vec4 readVec4(isamplerBuffer buf, inout TexBufferReader reader) {
+ return vec4(
+ readFloat(buf, reader),
+ readFloat(buf, reader),
+ readFloat(buf, reader),
+ readFloat(buf, reader)
+ );
+}
+
+void skipScalars(inout TexBufferReader reader, int count) {
+ reader.position += count;
+}
+
+void rewindReader(inout TexBufferReader reader, int position) {
+ reader.position = position;
+}
+
+#define BEGIN_BUFFER_PARSER(FuncName, StructType) \
+StructType FuncName(int offset) { \
+ TexBufferReader reader = \
+ buildTexBufferReader(offset); \
+ \
+ StructType data;
+
+#define END_BUFFER_PARSER() \
+ return data; \
+}
+
+#define READ_INT(field) data.field = readInt(PARSER_TARGET_BUFFER, reader);
+#define READ_FLOAT(field) data.field = readFloat(PARSER_TARGET_BUFFER, reader);
+#define READ_BOOL(field) data.field = readBool(PARSER_TARGET_BUFFER, reader);
+#define READ_IVEC2(field) data.field = readIVec2(PARSER_TARGET_BUFFER, reader);
+#define READ_IVEC3(field) data.field = readIVec3(PARSER_TARGET_BUFFER, reader);
+#define READ_IVEC4(field) data.field = readIVec4(PARSER_TARGET_BUFFER, reader);
+#define READ_VEC2(field) data.field = readVec2(PARSER_TARGET_BUFFER, reader);
+#define READ_VEC3(field) data.field = readVec3(PARSER_TARGET_BUFFER, reader);
+#define READ_VEC4(field) data.field = readVec4(PARSER_TARGET_BUFFER, reader);
\ No newline at end of file