Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions schemas/materials.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@
"type": "number",
"description": "The gloss of specular reflections. Defaults to 0. Commonly ranges from 10 to 500."
},
"hexTilingScale": {
"type": "number",
"description": "The scale of the hexagonal tiling pattern. Defaults to 0.0, i.e. no tiling."
},
"hexTilingBlend": {
"type": "number",
"description": "The blending of the hexagonal tiling pattern. Defaults to 4.0."
},
"hexTilingMode": {
"type": "string",
"description": "",
"enum": [
"OFFSET",
"OFFSET_WITH_MIRROR",
"OFFSET_WITH_ROTATION"
]
},
"scrollSpeed": {
"type": "array",
"description": "The duration in seconds, per UV direction, before texture scrolling repeats. Defaults to [ 0, 0 ], which disables scrolling.",
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/rs117/hd/config/HexTilingMode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package rs117.hd.config;

public enum HexTilingMode {
OFFSET,
OFFSET_WITH_MIRROR,
OFFSET_WITH_ROTATION
}
2 changes: 2 additions & 0 deletions src/main/java/rs117/hd/opengl/uniforms/UBOMaterials.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public static class MaterialStruct extends StructProperty {
public Property displacementScale = addProperty(PropertyType.Float, "displacementScale");
public Property specularStrength = addProperty(PropertyType.Float, "specularStrength");
public Property specularGloss = addProperty(PropertyType.Float, "specularGloss");
public Property hexTilingScale = addProperty(PropertyType.Float, "hexTilingScale");
public Property hexTilingBlend = addProperty(PropertyType.Float, "hexTilingBlend");
public Property flowMapStrength = addProperty(PropertyType.Float, "flowMapStrength");
public Property flowMapDuration = addProperty(PropertyType.FVec2, "flowMapDuration");
public Property scrollDuration = addProperty(PropertyType.FVec2, "scrollDuration");
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/rs117/hd/scene/materials/Material.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import rs117.hd.config.HexTilingMode;
import rs117.hd.opengl.uniforms.UBOMaterials;
import rs117.hd.scene.MaterialManager;
import rs117.hd.scene.model_overrides.ModelOverride;
Expand Down Expand Up @@ -60,6 +61,9 @@ public class Material {
@JsonAdapter(ColorUtils.LinearAdapter.class)
public float brightness = 1;
private float displacementScale = .1f;
private float hexTilingScale = 0.0f;
private float hexTilingBlend = 4.0f;
private HexTilingMode hexTilingMode = HexTilingMode.OFFSET_WITH_ROTATION;
private float flowMapStrength;
private float[] flowMapDuration = { 0, 0 };
private float specularStrength;
Expand Down Expand Up @@ -209,6 +213,7 @@ public void fillMaterialStruct(
struct.flowMap.set(getTextureLayer(flowMap));
struct.shadowAlphaMap.set(getTextureLayer(shadowAlphaMap));
struct.flags.set(
(hexTilingMode.ordinal() & 0x3) << 3 |
(overrideBaseColor ? 1 : 0) << 2 |
(unlit ? 1 : 0) << 1 |
(hasTransparency ? 1 : 0)
Expand All @@ -217,6 +222,8 @@ public void fillMaterialStruct(
struct.displacementScale.set(displacementScale);
struct.specularStrength.set(specularStrength);
struct.specularGloss.set(specularGloss);
struct.hexTilingScale.set(hexTilingScale);
struct.hexTilingBlend.set(hexTilingBlend);
struct.flowMapStrength.set(flowMapStrength);
struct.flowMapDuration.set(flowMapDuration);
struct.scrollDuration.set(scrollSpeedX, scrollSpeedY);
Expand Down
10 changes: 8 additions & 2 deletions src/main/resources/rs117/hd/scene/materials.json
Original file line number Diff line number Diff line change
Expand Up @@ -1325,7 +1325,9 @@
"name": "GRAVEL",
"normalMap": "GRAVEL_N",
"specularStrength": 0.4,
"specularGloss": 130.0
"specularGloss": 130.0,
"hexTilingScale": 1.25,
"hexTilingBlend": 2.5
},
{
"name": "VERTICAL_GRAVEL",
Expand Down Expand Up @@ -1425,7 +1427,8 @@
"name": "GRUNGE_1",
"normalMap": "GRUNGE_1_N",
"specularStrength": 0.25,
"specularGloss": 30.0
"specularGloss": 30.0,
"hexTilingScale": 4.25
},
{
"name": "GRUNGE_1_LIGHT",
Expand Down Expand Up @@ -3026,6 +3029,9 @@
"normalMap": "HD_HAY_N",
"specularStrength": 0.175,
"specularGloss": 15.0,
"hexTilingScale": 1.5,
"hexTilingBlend": 1.5,
"hexTilingMode": "OFFSET_WITH_MIRROR",
"materialsToReplace": [
"HAY"
],
Expand Down
31 changes: 21 additions & 10 deletions src/main/resources/rs117/hd/scene_frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#define DISPLAY_TANGENT 0
#define DISPLAY_SHADOWS 0
#define DISPLAY_LIGHTING 0
#define DISPLAY_HEX 0

#include <uniforms/global.glsl>
#include <uniforms/world_views.glsl>
Expand Down Expand Up @@ -81,6 +82,7 @@ vec2 worldUvs(float scale) {
#include <utils/fog.glsl>
#include <utils/wireframe.glsl>
#include <utils/lights.glsl>
#include <utils/hex_tiling.glsl>

void main() {
vec3 downDir = vec3(0, -1, 0);
Expand Down Expand Up @@ -158,7 +160,6 @@ void main() {
uv3 += uvFlow * flowMapStrength;

// Set up tangent-space transformation matrix

vec3 N;
#if FLAT_SHADING && ZONE_RENDERER
N = normalize(fFlatNormal);
Expand Down Expand Up @@ -205,6 +206,16 @@ void main() {
fragPos += TBN * fragDelta;
#endif

// Build HexData, if Terrain use world space XZ otherwise UVs
HexData hex1 = buildHexData(uv1, IN.position / TILE_SIZE, material1.hexTilingScale, material1.hexTilingBlend, getMaterialHexTilingMode(material1));
HexData hex2 = buildHexData(uv2, IN.position / TILE_SIZE, material2.hexTilingScale, material2.hexTilingBlend, getMaterialHexTilingMode(material2));
HexData hex3 = buildHexData(uv3, IN.position / TILE_SIZE, material3.hexTilingScale, material3.hexTilingBlend, getMaterialHexTilingMode(material3));

#if DISPLAY_HEX
FragColor = vec4(debugHex(hex1) * IN.texBlend.x + debugHex(hex2) * IN.texBlend.y + debugHex(hex3) * IN.texBlend.z, 1.0);
if (DISPLAY_HEX == 1) return; // Redundant, for syntax highlighting in IntelliJ
#endif

vec3 hsl1 = unpackRawHsl(fAlphaBiasHsl[0]);
vec3 hsl2 = unpackRawHsl(fAlphaBiasHsl[1]);
vec3 hsl3 = unpackRawHsl(fAlphaBiasHsl[2]);
Expand Down Expand Up @@ -237,9 +248,9 @@ void main() {
#endif

// get diffuse textures
vec4 texColor1 = colorMap1 == -1 ? vec4(1) : texture(textureArray, vec3(uv1, colorMap1), mipBias);
vec4 texColor2 = colorMap2 == -1 ? vec4(1) : texture(textureArray, vec3(uv2, colorMap2), mipBias);
vec4 texColor3 = colorMap3 == -1 ? vec4(1) : texture(textureArray, vec3(uv3, colorMap3), mipBias);
vec4 texColor1 = colorMap1 == -1 ? vec4(1) : sampleHex(textureArray, vec3(uv1, colorMap1), hex1);
vec4 texColor2 = colorMap2 == -1 ? vec4(1) : sampleHex(textureArray, vec3(uv2, colorMap2), hex2);
vec4 texColor3 = colorMap3 == -1 ? vec4(1) : sampleHex(textureArray, vec3(uv3, colorMap3), hex3);
texColor1.rgb *= material1.brightness;
texColor2.rgb *= material2.brightness;
texColor3.rgb *= material3.brightness;
Expand Down Expand Up @@ -347,9 +358,9 @@ void main() {
vec3 vSpecularGloss = vec3(material1.specularGloss, material2.specularGloss, material3.specularGloss);
vec3 vSpecularStrength = vec3(material1.specularStrength, material2.specularStrength, material3.specularStrength);
vSpecularStrength *= vec3(
material1.roughnessMap == -1 ? 1 : linearToSrgb(texture(textureArray, vec3(uv1, material1.roughnessMap)).r),
material2.roughnessMap == -1 ? 1 : linearToSrgb(texture(textureArray, vec3(uv2, material2.roughnessMap)).r),
material3.roughnessMap == -1 ? 1 : linearToSrgb(texture(textureArray, vec3(uv3, material3.roughnessMap)).r)
material1.roughnessMap == -1 ? 1 : linearToSrgb(sampleHex(textureArray, vec3(uv1, material1.roughnessMap), hex1).r),
material2.roughnessMap == -1 ? 1 : linearToSrgb(sampleHex(textureArray, vec3(uv2, material2.roughnessMap), hex2).r),
material3.roughnessMap == -1 ? 1 : linearToSrgb(sampleHex(textureArray, vec3(uv3, material3.roughnessMap), hex3).r)
);

// apply specular highlights to anything semi-transparent
Expand All @@ -372,9 +383,9 @@ void main() {
vec3 ambientLightOut = ambientColor * ambientStrength;

float aoFactor =
IN.texBlend.x * (material1.ambientOcclusionMap == -1 ? 1 : texture(textureArray, vec3(uv1, material1.ambientOcclusionMap)).r) +
IN.texBlend.y * (material2.ambientOcclusionMap == -1 ? 1 : texture(textureArray, vec3(uv2, material2.ambientOcclusionMap)).r) +
IN.texBlend.z * (material3.ambientOcclusionMap == -1 ? 1 : texture(textureArray, vec3(uv3, material3.ambientOcclusionMap)).r);
IN.texBlend.x * (material1.ambientOcclusionMap == -1 ? 1 : sampleHex(textureArray, vec3(uv1, material1.ambientOcclusionMap), hex1).r) +
IN.texBlend.y * (material2.ambientOcclusionMap == -1 ? 1 : sampleHex(textureArray, vec3(uv2, material2.ambientOcclusionMap), hex2).r) +
IN.texBlend.z * (material3.ambientOcclusionMap == -1 ? 1 : sampleHex(textureArray, vec3(uv3, material3.ambientOcclusionMap), hex3).r);
ambientLightOut *= aoFactor;

// directional light
Expand Down
8 changes: 7 additions & 1 deletion src/main/resources/rs117/hd/uniforms/materials.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ struct Material {
int ambientOcclusionMap;
int flowMap;
int shadowAlphaMap;
int flags; // overrideBaseColor << 2 | unlit << 1 | hasTransparency
int flags; // hexTilingMode (3 bits) << 3 | overrideBaseColor << 2 | unlit << 1 | hasTransparency
float brightness;
float displacementScale;
float specularStrength;
float specularGloss;
float hexTilingScale;
float hexTilingBlend;
float flowMapStrength;
vec2 flowMapDuration;
vec2 scrollDuration;
Expand All @@ -38,3 +40,7 @@ int getMaterialIsUnlit(const Material material) {
bool getMaterialHasTransparency(const Material material) {
return (material.flags & 1) == 1;
}

int getMaterialHexTilingMode(const Material material) {
return (material.flags >> 3) & 0x3;
}
180 changes: 180 additions & 0 deletions src/main/resources/rs117/hd/utils/hex_tiling.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#pragma once

#include <utils/misc.glsl>

#define HEX_UV_OFFSET_ONLY 0
#define HEX_UV_OFFSET_WITH_MIRROR 1
#define HEX_UV_OFFSET_WITH_ROTATE 2
#define HEX_UV_MODE HEX_UV_OFFSET_WITH_ROTATE

#define HEX_EPS 0.001
#define HEX_DOMINANT 0.95

struct HexData {
vec3 weights; // Barycentric weights for triangle interpolation
vec2 uv[3]; // Perturbed UV coordinates for each vertex
vec2 vertex[3]; // Hexagonal cell vertex positions
bool enabled; // Flag indicating if hex computation is valid
vec2 dPdx; // Partial derivative for gradient-aware sampling
vec2 dPdy; // Partial derivative for gradient-aware sampling
int dominantIdx; // Precomputed dominant vertex index
};

// Converts regular UV space into skewed hex space
const mat2 HEX_MATRIX = mat2(
1.7320508, -1.0,
0.0, 2.0
);

// Perturb UV coordinates for a hex vertex to create variation
vec2 makeUV(vec2 uv, vec2 vertexPos, int mode) {
vec4 h = hash24(vertexPos);
vec2 p = uv;

if(mode == HEX_UV_OFFSET_WITH_ROTATE) {
p -= 0.5;

// Discreate Rotation
float angle = h.x * 6.28318530718;
float s = sin(angle);
float c = cos(angle);
p = vec2(
c * p.x - s * p.y,
s * p.x + c * p.y
);

p += 0.5;
} else if(mode == HEX_UV_OFFSET_WITH_MIRROR) {
// Flip along U/W
vec2 flipMask = step(0.5, h.xy) * 2.0 - 1.0;
p *= flipMask;
}

// Offset & Scale
float scaleJitter = mix(0.85, 1.15, h.z);
return p * scaleJitter + h.w;
}

HexData buildHexData(vec2 uv, vec3 fragPos, float scale, float blend, int mode) {
HexData h;
if (scale <= 0.0) {
h.enabled = false;
return h;
}
h.enabled = true;

// Derivatives (shared across samples)
h.dPdx = dFdx(fragPos.xz);
h.dPdy = dFdy(fragPos.xz);

vec2 skew = fragPos.xz * scale * HEX_MATRIX;
vec2 base = floor(skew);
vec2 f = fract(skew);

vec3 temp = vec3(f, 0.0);
temp.z = 1.0 - temp.x - temp.y;

float s = step(0.0, -temp.z);
float s2 = 2.0 * s - 1.0;
temp *= s2;

// Triangle vertices
vec2 v1 = base + vec2(s, s);
vec2 v2 = base + vec2(s, 1.0 - s);
vec2 v3 = base + vec2(1.0 - s, s);

h.vertex[0] = v1;
h.vertex[1] = v2;
h.vertex[2] = v3;

// Barycentric weights
vec3 w = vec3(-temp.z, s - temp.y, s - temp.x);
w = max(w, 0.0);

// Sharpen blend
w = pow(w, vec3(7.0 * blend));

// Normalize
float invSum = 1.0 / (w.x + w.y + w.z);
h.weights = w * invSum;

// UVs
h.uv[0] = makeUV(uv, v1, mode);
h.uv[1] = makeUV(uv, v2, mode);
h.uv[2] = makeUV(uv, v3, mode);

float maxW = max(max(h.weights.x, h.weights.y), h.weights.z);
if (maxW > HEX_DOMINANT) {
// Determine which weight is largest
h.dominantIdx = (h.weights.x > h.weights.y)
? (h.weights.x > h.weights.z ? 0 : 2)
: (h.weights.y > h.weights.z ? 1 : 2);
} else {
h.dominantIdx = -1; // no dominant vertex
}

return h;
}

vec4 sampleHex(sampler2D tex, HexData h) {
if (!h.enabled)
return texture(tex, h.uv[0]);

if (h.dominantIdx >= 0)
return textureGrad(tex, h.uv[h.dominantIdx], h.dPdx, h.dPdy);

vec4 c0 = textureGrad(tex, h.uv[0], h.dPdx, h.dPdy);
vec4 c1 = textureGrad(tex, h.uv[1], h.dPdx, h.dPdy);
vec4 c2 = textureGrad(tex, h.uv[2], h.dPdx, h.dPdy);

return c0 * h.weights.x + c1 * h.weights.y + c2 * h.weights.z;
}

vec3 sampleHexRGB(sampler2D tex, HexData h) {
return sampleHex(tex, h).rgb;
}

vec4 sampleHex(sampler2DArray tex, vec3 uvw, HexData h) {
if (!h.enabled)
return texture(tex, uvw);

if (h.dominantIdx >= 0)
return textureGrad(tex, vec3(h.uv[h.dominantIdx], uvw.z), h.dPdx, h.dPdy);

vec4 c0 = textureGrad(tex, vec3(h.uv[0], uvw.z), h.dPdx, h.dPdy);
vec4 c1 = textureGrad(tex, vec3(h.uv[1], uvw.z), h.dPdx, h.dPdy);
vec4 c2 = textureGrad(tex, vec3(h.uv[2], uvw.z), h.dPdx, h.dPdy);

return c0 * h.weights.x + c1 * h.weights.y + c2 * h.weights.z;
}

vec3 sampleHexRGB(sampler2DArray tex, vec3 uvw, HexData h) {
return sampleHex(tex, uvw, h).rgb;
}

vec3 debugHex(HexData h) {
if (!h.enabled) return vec3(0.0);

vec3 W = h.weights;

vec2 v1 = h.vertex[0];
vec2 v2 = h.vertex[1];
vec2 v3 = h.vertex[2];

vec3 res = vec3(0.0);

int i1 = int(v1.x - v1.y) % 3;
if (i1 < 0) i1 += 3;

int hi = (i1 < 2) ? (i1 + 1) : 0;
int lo = (i1 > 0) ? (i1 - 1) : 2;

int i2 = (v1.x < v3.x) ? lo : hi;
int i3 = (v1.x < v3.x) ? hi : lo;

res.x = (i3 == 0) ? W.z : ((i2 == 0) ? W.y : W.x);
res.y = (i3 == 1) ? W.z : ((i2 == 1) ? W.y : W.x);
res.z = (i3 == 2) ? W.z : ((i2 == 2) ? W.y : W.x);

return res;
}
Loading