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