diff --git a/.gitignore b/.gitignore index 3c37caf..70af2d3 100644 --- a/.gitignore +++ b/.gitignore @@ -112,7 +112,11 @@ gradle-app.setting **/build/ # Common working directory -run/ +!/run +/run/* +!/run/resourcepacks +/run/resourcepacks/* +!/run/resourcepacks/lumi # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) -!gradle-wrapper.jar +!gradle-wrapper.jar \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..db1d400 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Respackopts +Respackopts provides resource packs with config menus\ +By default it integrates with frex (canvas shaders) and libcd (use this as reference)\ +An example for the frex/canvas integration can be found [here](https://gitlab.com/JFronny/respackopts/-/tree/master/run/resourcepacks/lumi) + +# Using Respackopts +## Users +You will just need to install Respackopts. A menu button will appear besides all supported resourcepacks. +## Resource pack authors +You will need to define a respackopts conf as seen [here](https://gitlab.com/JFronny/respackopts/-/blob/master/run/resourcepacks/lumi/assets/respackopts/conf.json) + +This config may include: +- boolean values, expressed as Json bools +- number values, expressed as Json numbers +- string values (not accessible from default integrations), expressed as Json strings +- enums, expressed as Json arrays containing simple strings + +The version property defines the format version. It will be incremented if changes to the layout of the config are made\ +I will likely attempt to support older versions if I decide to make an incompatible change.\ +To use the information from the config you will need to use and integration module/plugin.\ +For the ones included by default: +### Canvas +Respackopts defines a config supplier, you can include it with `#include respackopts:config_supplier`\ +Values can be accessed with `packId_entryId` for basic elements.\ +Enum values can be accessed with `packId_entryId_enumKey`.\ +Examples of using these are available [here](https://gitlab.com/JFronny/respackopts/-/tree/master/run/resourcepacks/lumi) +### LibCD +The LibCD integration defines a condition named `respackopts:cfg` that takes a string describing the location of a bool as `packId:entryId` + +There have been issues with LibCD in development, so it might be smart not to use it. +## Mod developers +All data is available in static HashMaps in `io.gitlab.jfronny.respackopts.Respackopts`.\ +To save information, call `Respackopts.save()`, `Respackopts.load()` to load. +`boolVals`, `numVals`, `strVals` and `enumKeys` use the resource pack ID as their first key, +then the property name. After that you get the value. Enums are expressed as numbers with enum key names in enumKeys.\ +If you need code to be ran after changes are made to the state, add an action to `saveActions`.\ +This is currently used for the Canvas/FREX integration, which uses it to generate shader code. \ No newline at end of file diff --git a/run/resourcepacks/lumi/assets/canvas/shaders/internal/material_main.frag b/run/resourcepacks/lumi/assets/canvas/shaders/internal/material_main.frag new file mode 100644 index 0000000..8b9231a --- /dev/null +++ b/run/resourcepacks/lumi/assets/canvas/shaders/internal/material_main.frag @@ -0,0 +1,377 @@ +/* + * Lumi Lights - A shader pack for Canvas + * Copyright (c) 2020 spiralhalo and Contributors + * + * See `README.md` for license notice. + */ + +#include canvas:shaders/internal/header.glsl +#include canvas:shaders/internal/varying.glsl +#include canvas:shaders/internal/diffuse.glsl +#include canvas:shaders/internal/flags.glsl +#include canvas:shaders/internal/fog.glsl +#include frex:shaders/api/world.glsl +#include frex:shaders/api/player.glsl +#include frex:shaders/api/material.glsl +#include frex:shaders/api/fragment.glsl +#include frex:shaders/api/sampler.glsl +#include frex:shaders/lib/math.glsl +#include frex:shaders/lib/color.glsl +#include canvas:shaders/internal/program.glsl +#include lumi:shaders/api/varying.glsl + +#include canvas:apitarget +#include respackopts:config_supplier + +/****************************************************** + canvas:shaders/internal/material_main.frag +******************************************************/ + +#define M_PI 3.1415926535897932384626433832795 + +const float hdr_sunStr = 3; +const float hdr_moonStr = 0.4; +const float hdr_blockStr = 1.5; +const float hdr_handHeldStr = 0.9; +const float hdr_skylessStr = 0.2; +const float hdr_baseMinStr = 0.0; +const float hdr_baseMaxStr = 0.25; +const float hdr_emissiveStr = 1; +const float hdr_relAmbient = 0.09; +const float hdr_relSunHorizon = 0.5; +const float hdr_zWobbleDefault = 0.1; +const float hdr_finalMult = 1; +const float hdr_gamma = 2.2; + +float hdr_gammaAdjust(float x){ + return pow(x, hdr_gamma); +} + +vec3 hdr_gammaAdjust(vec3 x){ + return pow(x, vec3(hdr_gamma)); +} + +vec3 hdr_reinhardJodieTonemap(in vec3 v) { + float l = frx_luminance(v); + vec3 tv = v / (1.0f + v); + return mix(v / (1.0f + l), tv, tv); +} + +vec3 hdr_vibrantTonemap(in vec3 v){ + return v / (frx_luminance(v) + vec3(1.0)); +} + +void _cv_startFragment(inout frx_FragmentData data) { + int cv_programId = _cv_fragmentProgramId(); +#include canvas:startfragment +} + +float l2_clampScale(float e0, float e1, float v){ + return clamp((v-e0)/(e1-e0), 0.0, 1.0); +} + +float l2_max3(vec3 vec){ + return max(vec.x, max(vec.y, vec.z)); +} + +// vec3 l2_what(vec3 rgb){ +// return vec3(0.4123910 * rgb.r + 0.3575840 * rgb.g + 0.1804810 * rgb.b, +// 0.2126390 * rgb.r + 0.7151690 * rgb.g + 0.0721923 * rgb.b, +// 0.0193308 * rgb.r + 0.1191950 * rgb.g + 0.9505320 * rgb.b); +// } + +vec3 l2_blockLight(float blockLight){ + float bl = l2_clampScale(0.03125, 1.0, blockLight); + bl *= bl * hdr_blockStr; + vec3 block = hdr_gammaAdjust(vec3(bl, bl*0.875, bl*0.75)); + +#if HANDHELD_LIGHT_RADIUS != 0 + vec4 held = frx_heldLight(); + if (held.w > 0.0) { + float hl = l2_clampScale(held.w * HANDHELD_LIGHT_RADIUS, 0.0, gl_FogFragCoord); + hl *= hl * hdr_handHeldStr; + + return block + hdr_gammaAdjust(held.rgb * hl); + } +#endif + + return block; +} + +vec3 l2_emissiveLight(float emissivity){ + return vec3(hdr_gammaAdjust(emissivity) * hdr_emissiveStr); +} + +float l2_skyLight(float skyLight, float intensity) +{ + float sl = l2_clampScale(0.03125, 1.0, skyLight); + return hdr_gammaAdjust(sl) * intensity; +} + +vec3 l2_ambientColor(float time){ + vec3 ambientColor = hdr_gammaAdjust(vec3(0.6, 0.9, 1.0)) * hdr_sunStr * hdr_relAmbient; + vec3 sunriseAmbient = hdr_gammaAdjust(vec3(1.0, 0.8, 0.4)) * hdr_sunStr * hdr_relAmbient * hdr_relSunHorizon; + vec3 sunsetAmbient = hdr_gammaAdjust(vec3(1.0, 0.6, 0.2)) * hdr_sunStr * hdr_relAmbient * hdr_relSunHorizon; + vec3 nightAmbient = hdr_gammaAdjust(vec3(1.0, 1.0, 2.0)) * hdr_moonStr * hdr_relAmbient; + if(time > 0.94){ + ambientColor = mix(nightAmbient, sunriseAmbient, l2_clampScale(0.94, 0.98, time)); + } else if(time > 0.52){ + ambientColor = mix(sunsetAmbient, nightAmbient, l2_clampScale(0.52, 0.56, time)); + } else if(time > 0.48){ + ambientColor = mix(ambientColor, sunsetAmbient, l2_clampScale(0.48, 0.5, time)); + } else if(time < 0.02){ + ambientColor = mix(ambientColor, sunriseAmbient, l2_clampScale(0.02, 0, time)); + } + return ambientColor; +} + +vec3 l2_skyAmbient(float skyLight, float time, float intensity){ + float sa = l2_skyLight(skyLight, intensity) * 2.5; + return sa * l2_ambientColor(time); +} + +float l2_userBrightness(){ + float base = texture2D(frxs_lightmap, vec2(0.03125, 0.03125)).r; + // if(frx_isWorldTheNether()){ + // return smoothstep(0.15/*0.207 no true darkness in nether*/, 0.577, base); + // } else if (frx_isWorldTheEnd(){ + // return smoothstep(0.18/*0.271 no true darkness in the end*/, 0.685, base); + // } else { + // return smoothstep(0.053, 0.135, base); + // } + + // Simplify nether/the end check + if(frx_worldHasSkylight()){ + return smoothstep(0.053, 0.135, base); + } else { + return smoothstep(0.15, 0.63, base); + } +} + +vec3 l2_skylessLightColor(){ + return hdr_gammaAdjust(vec3(1.0)); +} + +vec3 l2_dimensionColor(){ + if (frx_isWorldTheNether()) { + float min_col = min(min(gl_Fog.color.rgb.x, gl_Fog.color.rgb.y), gl_Fog.color.rgb.z); + float max_col = max(max(gl_Fog.color.rgb.x, gl_Fog.color.rgb.y), gl_Fog.color.rgb.z); + float sat = 0.0; + if (max_col != 0.0) { + sat = (max_col-min_col)/max_col; + } + + return hdr_gammaAdjust(clamp((gl_Fog.color.rgb*(1/max_col))+pow(sat,2)/2, 0.0, 1.0)); + } + else { + return hdr_gammaAdjust(vec3(0.8, 0.7, 1.0)); + } +} + +vec3 l2_skylessLight(vec3 normal){ + if(frx_worldHasSkylight()){ + return vec3(0); + } else { + float yalign = dot(normal,vec3(0, 0.977358, 0.211593)); // a bit towards z for more interesting effect + yalign = frx_isSkyDarkened()?abs(yalign):max(0,yalign); + return yalign * hdr_skylessStr * l2_skylessLightColor() * l2_userBrightness(); + } +} + +vec3 l2_baseAmbient(){ + if(frx_worldHasSkylight()){ + return vec3(0.1) * mix(hdr_baseMinStr, hdr_baseMaxStr, l2_userBrightness()); + } else { + return l2_dimensionColor() * mix(hdr_baseMinStr, hdr_baseMaxStr, l2_userBrightness()); + } +} + +vec3 l2_sunColor(float time){ + vec3 sunColor = hdr_gammaAdjust(vec3(1.0, 1.0, lumi_sunColorFactor)) * hdr_sunStr; + vec3 sunriseColor = hdr_gammaAdjust(vec3(1.0, 0.8, 0.4)) * hdr_sunStr * hdr_relSunHorizon; + vec3 sunsetColor = hdr_gammaAdjust(vec3(1.0, 0.6, 0.4)) * hdr_sunStr * hdr_relSunHorizon; + if(time > 0.94){ + sunColor = sunriseColor; + } else if(time > 0.56){ + sunColor = vec3(0); // pitch black at night + } else if(time > 0.54){ + sunColor = mix(sunsetColor, vec3(0), l2_clampScale(0.54, 0.56, time)); + } else if(time > 0.5){ + sunColor = sunsetColor; + } else if(time > 0.48){ + sunColor = mix(sunColor, sunsetColor, l2_clampScale(0.48, 0.5, time)); + } else if(time < 0.02){ + sunColor = mix(sunColor, sunriseColor, l2_clampScale(0.02, 0, time)); + } + return sunColor; +} + +vec3 l2_vanillaSunDir(in float time, float zWobble){ + + // wrap time to account for sunrise + time -= (time >= 0.75) ? 1.0 : 0.0; + + // supposed offset of sunset/sunrise from 0/12000 daytime. might get better result with datamining? + float sunHorizonDur = 0.04; + + // angle of sun in radians + float angleRad = l2_clampScale(-sunHorizonDur, 0.5+sunHorizonDur, time) * M_PI; + + return normalize(vec3(cos(angleRad), sin(angleRad), zWobble)); +} + +vec3 l2_sunLight(float skyLight, in float time, float intensity, float rainGradient, vec3 diffuseNormal){ + + // wrap time to account for sunrise + float customTime = (time >= 0.75) ? (time - 1.0) : time; + + float customIntensity = l2_clampScale(-0.08, 0.00, customTime); + + if(customTime >= 0.25){ + customIntensity = l2_clampScale(0.58, 0.5, customTime); + } + + customIntensity *= mix(1.0, 0.0, rainGradient); + + float sl = l2_skyLight(skyLight, max(customIntensity, intensity)); + + // direct sun light doesn't reach into dark spot as much as sky ambient + sl = frx_smootherstep(0.5,1.0,sl); + + // zWobble is added to make more interesting looking diffuse light + // TODO: might be fun to use frx_worldDay() with sine wave for the zWobble to simulate annual sun position change + sl *= max(0.0, dot(l2_vanillaSunDir(time, hdr_zWobbleDefault), diffuseNormal)); + return sl * l2_sunColor(time); +} + +vec3 l2_moonLight(float skyLight, float time, float intensity, vec3 diffuseNormal){ + float ml = l2_skyLight(skyLight, intensity) * frx_moonSize() * hdr_moonStr; + float aRad = l2_clampScale(0.56, 0.94, time) * M_PI; + ml *= max(0.0, dot(vec3(cos(aRad), sin(aRad), 0), diffuseNormal)); + if(time < 0.58){ + ml *= l2_clampScale(0.54, 0.58, time); + } else if(time > 0.92){ + ml *= l2_clampScale(0.96, 0.92, time); + } + return vec3(ml); +} + +float l2_specular(float time, vec3 aNormal, vec3 aPos, vec3 cameraPos, float power) +{ + // calculate sun position (0 zWobble to make it look accurate with vanilla sun visuals) + vec3 sunDir = l2_vanillaSunDir(time, 0); + + // obtain the direction of the camera + vec3 viewDir = normalize(cameraPos - aPos); + + // calculate the specular light + return pow(max(0.0, dot(reflect(-sunDir, aNormal), viewDir)),power); +} + +float l2_ao(frx_FragmentData fragData) { +#if AO_SHADING_MODE != AO_MODE_NONE + float ao = fragData.ao ? _cvv_ao : 1.0; + return hdr_gammaAdjust(min(1.0, ao + fragData.emissivity)); +#else + return 1.0; +#endif +} + +void main() { + frx_FragmentData fragData = frx_FragmentData ( + texture2D(frxs_spriteAltas, _cvv_texcoord, _cv_getFlag(_CV_FLAG_UNMIPPED) * -4.0), + _cvv_color, + frx_matEmissive() ? 1.0 : 0.0, + !frx_matDisableDiffuse(), + !frx_matDisableAo(), + _cvv_normal, + _cvv_lightcoord + ); + + _cv_startFragment(fragData); + + vec4 a = fragData.spriteColor * fragData.vertexColor; + float bloom = fragData.emissivity; // separate bloom from emissivity + + if(frx_isGui()){ +#if DIFFUSE_SHADING_MODE != DIFFUSE_MODE_NONE + if(fragData.diffuse){ + float diffuse = mix(_cvv_diffuse, 1, fragData.emissivity); + vec3 shading = mix(vec3(0.5, 0.4, 0.8) * diffuse * diffuse, vec3(1.0), diffuse); + a.rgb *= shading; + } +#endif + } else { + a.rgb = hdr_gammaAdjust(a.rgb); + + // If diffuse is disabled (e.g. grass) then the normal points up by default + float ao = l2_ao(fragData); + vec3 diffuseNormal = fragData.diffuse?fragData.vertexNormal * frx_normalModelMatrix():vec3(0,1,0); + vec3 block = l2_blockLight(fragData.light.x); + vec3 sun = l2_sunLight(fragData.light.y, frx_worldTime(), frx_ambientIntensity(), frx_rainGradient(), diffuseNormal); + vec3 moon = l2_moonLight(fragData.light.y, frx_worldTime(), frx_ambientIntensity(), diffuseNormal); + vec3 skyAmbient = l2_skyAmbient(fragData.light.y, frx_worldTime(), frx_ambientIntensity()); + vec3 emissive = l2_emissiveLight(fragData.emissivity); + vec3 nether = l2_skylessLight(diffuseNormal); + + vec3 light = block + moon + l2_baseAmbient() + skyAmbient + sun + nether; + light *= ao; // AO is supposed to be applied to ambient only, but things look better with AO on everything except for emissive light + light += emissive; + + vec3 specular = vec3(0.0); + if (wwv_specPower > 0.01) { + vec3 specularNormal = fragData.vertexNormal * frx_normalModelMatrix(); + + float skyAccess = smoothstep(0.89, 1.0, fragData.light.y); + + vec3 fragPos = frx_var0.xyz; + vec3 cameraPos = frx_var1.xyz; + vec3 sunDir = l2_vanillaSunDir(frx_worldTime(), 0); + vec3 sun = l2_sunLight(fragData.light.y, frx_worldTime(), frx_ambientIntensity(), frx_rainGradient(), sunDir); + + float specularAmount = l2_specular(frx_worldTime(), specularNormal, fragPos, cameraPos, wwv_specPower); + + specular = sun * specularAmount * skyAccess; + } + + a.rgb *= light; + a.rgb += specular; + + float specularLuminance = frx_luminance(specular); + a.a += specularLuminance; + bloom += specularLuminance; + + a.rgb *= hdr_finalMult; +#if lumi_tonemap == lumi_tonemap_vibrant + a.rgb = pow(hdr_vibrantTonemap(a.rgb), vec3(1.0 / hdr_gamma)); +#elif lumi_tonemap == reinhardJodie + a.rgb = pow(hdr_reinhardJodieTonemap(a.rgb), vec3(1.0 / hdr_gamma)); +#else + a.rgb = pow(frx_toneMap(a.rgb), vec3(1.0 / hdr_gamma)); +#endif + } + + // PERF: varyings better here? + if (_cv_getFlag(_CV_FLAG_CUTOUT) == 1.0) { + float t = _cv_getFlag(_CV_FLAG_TRANSLUCENT_CUTOUT) == 1.0 ? _CV_TRANSLUCENT_CUTOUT_THRESHOLD : 0.5; + + if (a.a < t) { + discard; + } + } + + // PERF: varyings better here? + if (_cv_getFlag(_CV_FLAG_FLASH_OVERLAY) == 1.0) { + a = a * 0.25 + 0.75; + } else if (_cv_getFlag(_CV_FLAG_HURT_OVERLAY) == 1.0) { + a = vec4(0.25 + a.r * 0.75, a.g * 0.75, a.b * 0.75, a.a); + } + + // TODO: need a separate fog pass? + gl_FragData[TARGET_BASECOLOR] = _cv_fog(a); + gl_FragDepth = gl_FragCoord.z; + +#if TARGET_EMISSIVE > 0 + gl_FragData[TARGET_EMISSIVE] = vec4(bloom * a.a, 1.0, 0.0, 1.0); +#endif +} diff --git a/run/resourcepacks/lumi/assets/canvas/shaders/internal/material_main.vert b/run/resourcepacks/lumi/assets/canvas/shaders/internal/material_main.vert new file mode 100644 index 0000000..354c409 --- /dev/null +++ b/run/resourcepacks/lumi/assets/canvas/shaders/internal/material_main.vert @@ -0,0 +1,89 @@ +#include canvas:shaders/internal/header.glsl +#include frex:shaders/api/context.glsl +#include canvas:shaders/internal/varying.glsl +#include canvas:shaders/internal/vertex.glsl +#include canvas:shaders/internal/flags.glsl +#include frex:shaders/api/vertex.glsl +#include frex:shaders/api/sampler.glsl +#include canvas:shaders/internal/diffuse.glsl +#include canvas:shaders/internal/program.glsl +#include lumi:shaders/api/varying.glsl + +#include canvas:apitarget + +/****************************************************** + canvas:shaders/internal/material_main.vert +******************************************************/ + +void _cv_startVertex(inout frx_VertexData data, in int cv_programId) { +#include canvas:startvertex +} + +void _cv_endVertex(inout frx_VertexData data, in int cv_programId) { +#include canvas:endvertex +} + +void main() { + frx_VertexData data = frx_VertexData( + gl_Vertex, + in_uv, + in_color, + (in_normal_flags.xyz - 127.0) / 127.0, + in_lightmap.rg * 0.00390625 + 0.03125 + ); + + // Adding +0.5 prevents striping or other strangeness in flag-dependent rendering + // due to FP error on some cards/drivers. Also made varying attribute invariant (rolls eyes at OpenGL) + _cvv_flags = uint(in_normal_flags.w + 0.5); + + wwv_specPower = 0.0; + + _cv_setupProgram(); + + int cv_programId = _cv_vertexProgramId(); + _cv_startVertex(data, cv_programId); + + if (_cvu_atlas[_CV_SPRITE_INFO_TEXTURE_SIZE] != 0.0) { + float spriteIndex = in_material.x; + // for sprite atlas textures, convert from normalized (0-1) to interpolated coordinates + vec4 spriteBounds = texture2DLod(frxs_spriteInfo, vec2(0, spriteIndex / _cvu_atlas[_CV_SPRITE_INFO_TEXTURE_SIZE]), 0); + + float atlasHeight = _cvu_atlas[_CV_ATLAS_HEIGHT]; + float atlasWidth = _cvu_atlas[_CV_ATLAS_WIDTH]; + + // snap sprite bounds to integer coordinates to correct for floating point error + spriteBounds *= vec4(atlasWidth, atlasHeight, atlasWidth, atlasHeight); + spriteBounds += vec4(0.5, 0.5, 0.5, 0.5); + spriteBounds -= fract(spriteBounds); + spriteBounds /= vec4(atlasWidth, atlasHeight, atlasWidth, atlasHeight); + + data.spriteUV = spriteBounds.xy + data.spriteUV * spriteBounds.zw; + } + + data.spriteUV = _cv_textureCoord(data.spriteUV, 0); + + vec4 viewCoord = gl_ModelViewMatrix * data.vertex; + gl_ClipVertex = viewCoord; + gl_FogFragCoord = length(viewCoord.xyz); + + //data.normal *= gl_NormalMatrix; + data.vertex = gl_ModelViewProjectionMatrix * data.vertex; + + gl_Position = data.vertex; + + _cv_endVertex(data, cv_programId); + + _cvv_texcoord = data.spriteUV; + _cvv_color = data.color; + _cvv_normal = data.normal; + +#if DIFFUSE_SHADING_MODE != DIFFUSE_MODE_NONE + _cvv_diffuse = _cv_diffuse(_cvv_normal); +#endif + +#if AO_SHADING_MODE != AO_MODE_NONE + _cvv_ao = in_lightmap.b / 255.0; +#endif + + _cvv_lightcoord = data.light; +} diff --git a/run/resourcepacks/lumi/assets/canvas/shaders/internal/process/emissive_color.frag b/run/resourcepacks/lumi/assets/canvas/shaders/internal/process/emissive_color.frag new file mode 100644 index 0000000..510acb2 --- /dev/null +++ b/run/resourcepacks/lumi/assets/canvas/shaders/internal/process/emissive_color.frag @@ -0,0 +1,30 @@ +/* + * Derived from Canvas source code (https://github.com/grondag/canvas/) + * + * Changes are made to add bloom to sky fragments. + */ + +#include canvas:shaders/internal/process/header.glsl +#include frex:shaders/lib/color.glsl +#include frex:shaders/lib/sample.glsl +#include frex:shaders/lib/math.glsl + +/****************************************************** + canvas:shaders/internal/process/emissive_color.frag +******************************************************/ +uniform sampler2D _cvu_base; +uniform sampler2D _cvu_emissive; +uniform ivec2 _cvu_size; + +varying vec2 _cvv_texcoord; + +void main() { + vec4 e = texture2D(_cvu_emissive, _cvv_texcoord); + + bool sky = e.g == 0.0; + float bloom = sky ? 0.25 : e.r; + + vec4 c = frx_fromGamma(texture2D(_cvu_base, _cvv_texcoord)); + + gl_FragData[0] = vec4(c.rgb * bloom, e.r); +} diff --git a/run/resourcepacks/lumi/assets/lumi/materials/tasty_liquid.json b/run/resourcepacks/lumi/assets/lumi/materials/tasty_liquid.json new file mode 100644 index 0000000..3032be1 --- /dev/null +++ b/run/resourcepacks/lumi/assets/lumi/materials/tasty_liquid.json @@ -0,0 +1,7 @@ +{ + "blendMode": "translucent", + "disableAo": true, + "disableDiffuse": true, + "vertexSource": "lumi:shaders/material/water.vert", + "fragmentSource": "lumi:shaders/material/water.frag" +} diff --git a/run/resourcepacks/lumi/assets/lumi/shaders/api/varying.glsl b/run/resourcepacks/lumi/assets/lumi/shaders/api/varying.glsl new file mode 100644 index 0000000..62ec007 --- /dev/null +++ b/run/resourcepacks/lumi/assets/lumi/shaders/api/varying.glsl @@ -0,0 +1,2 @@ +// If wwv_specPower is used, frx_var0 must be reserved for fragPos and frx_var1 must be reserved for cameraPos +varying float wwv_specPower; diff --git a/run/resourcepacks/lumi/assets/lumi/shaders/lib/noise.glsl b/run/resourcepacks/lumi/assets/lumi/shaders/lib/noise.glsl new file mode 100644 index 0000000..e0a6833 --- /dev/null +++ b/run/resourcepacks/lumi/assets/lumi/shaders/lib/noise.glsl @@ -0,0 +1,7 @@ +#include frex:shaders/lib/noise/noise3d.glsl + +float l2_noise(vec3 aPos, float renderTime, float scale, float amplitude) +{ + float invScale = 1/scale; + return (snoise(vec3(aPos.x*invScale, aPos.z*invScale, renderTime)) * 0.5+0.5) * amplitude; +} diff --git a/run/resourcepacks/lumi/assets/lumi/shaders/material/water.frag b/run/resourcepacks/lumi/assets/lumi/shaders/material/water.frag new file mode 100644 index 0000000..c168b28 --- /dev/null +++ b/run/resourcepacks/lumi/assets/lumi/shaders/material/water.frag @@ -0,0 +1,38 @@ +#include lumi:shaders/api/varying.glsl +#include lumi:shaders/lib/noise.glsl +#include frex:shaders/api/fragment.glsl +#include frex:shaders/api/world.glsl +#include respackopts:config_supplier + +void frx_startFragment(inout frx_FragmentData fragData) { + fragData.spriteColor.rgb *= fragData.spriteColor.rgb * lumi_waterColorFactor; + + if(fragData.vertexNormal.y > 0.01) { + + // hack + fragData.light.y += 0.077 * smoothstep(1.0, 0.99, fragData.vertexNormal.y); + fragData.light.y = min(0.96875, fragData.light.y); + + vec3 worldPos = frx_modelOriginWorldPos() + frx_var0.xyz; + // water wavyness parameter + float timeScale = 2; // speed + float noiseScale = 2; // wavelength + float noiseAmp = 0.03125 * noiseScale;// * timeScale; // amplitude + + // inferred parameter + float renderTime = frx_renderSeconds() * 0.5 * timeScale; + float microSample = 0.01 * noiseScale; + + // base noise + float noise = l2_noise(worldPos, renderTime, noiseScale, noiseAmp); + + // normal recalculation + vec3 noiseOrigin = vec3(0, noise, 0); + vec3 noiseTangent = vec3(microSample, l2_noise(worldPos + vec3(microSample,0,0), renderTime, noiseScale, noiseAmp), 0) - noiseOrigin; + vec3 noiseBitangent = vec3(0, l2_noise(worldPos + vec3(0,0,microSample), renderTime, noiseScale, noiseAmp), microSample) - noiseOrigin; + + // noisy normal + vec3 noisyNormal = normalize(cross(noiseBitangent, noiseTangent)); + fragData.vertexNormal = noisyNormal; + } +} diff --git a/run/resourcepacks/lumi/assets/lumi/shaders/material/water.vert b/run/resourcepacks/lumi/assets/lumi/shaders/material/water.vert new file mode 100644 index 0000000..6332634 --- /dev/null +++ b/run/resourcepacks/lumi/assets/lumi/shaders/material/water.vert @@ -0,0 +1,8 @@ +#include frex:shaders/api/vertex.glsl +#include lumi:shaders/api/varying.glsl + +void frx_startVertex(inout frx_VertexData data) { + frx_var0.xyz = data.vertex.xyz; + frx_var1.xyz = (gl_ModelViewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0)).xyz; + wwv_specPower = 100.0; +} diff --git a/run/resourcepacks/lumi/assets/minecraft/materialmaps/fluid/flowing_water.json b/run/resourcepacks/lumi/assets/minecraft/materialmaps/fluid/flowing_water.json new file mode 100644 index 0000000..077e5c9 --- /dev/null +++ b/run/resourcepacks/lumi/assets/minecraft/materialmaps/fluid/flowing_water.json @@ -0,0 +1,3 @@ +{ + "defaultMaterial": "lumi:tasty_liquid" +} diff --git a/run/resourcepacks/lumi/assets/minecraft/materialmaps/fluid/water.json b/run/resourcepacks/lumi/assets/minecraft/materialmaps/fluid/water.json new file mode 100644 index 0000000..077e5c9 --- /dev/null +++ b/run/resourcepacks/lumi/assets/minecraft/materialmaps/fluid/water.json @@ -0,0 +1,3 @@ +{ + "defaultMaterial": "lumi:tasty_liquid" +} diff --git a/run/resourcepacks/lumi/assets/respackopts/conf.json b/run/resourcepacks/lumi/assets/respackopts/conf.json new file mode 100644 index 0000000..c047af4 --- /dev/null +++ b/run/resourcepacks/lumi/assets/respackopts/conf.json @@ -0,0 +1,13 @@ +{ + "id": "lumi", + "version": 1, + "conf": { + "tonemap": [ + "reinhardJodie", + "vibrant", + "fxr" + ], + "sunColorFactor": 0.8, + "waterColorFactor": 1 + } +} diff --git a/run/resourcepacks/lumi/pack.mcmeta b/run/resourcepacks/lumi/pack.mcmeta new file mode 100644 index 0000000..779bb20 --- /dev/null +++ b/run/resourcepacks/lumi/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 6, + "description": "§3Aesthetic shader pack\n§8by awoo [v1.0-wip]" + } +} diff --git a/run/resourcepacks/lumi/pack.png b/run/resourcepacks/lumi/pack.png new file mode 100644 index 0000000..e48cdab Binary files /dev/null and b/run/resourcepacks/lumi/pack.png differ diff --git a/src/main/java/io/gitlab/jfronny/respackopts/GuiFactory.java b/src/main/java/io/gitlab/jfronny/respackopts/GuiFactory.java index db2a027..db53bcd 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/GuiFactory.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/GuiFactory.java @@ -6,6 +6,8 @@ import com.google.gson.JsonPrimitive; import me.shedaniel.clothconfig2.api.ConfigBuilder; import me.shedaniel.clothconfig2.api.ConfigCategory; import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import me.shedaniel.clothconfig2.gui.entries.DropdownBoxEntry; +import me.shedaniel.clothconfig2.impl.builders.DropdownMenuBuilder; import net.minecraft.client.gui.screen.FatalErrorScreen; import net.minecraft.client.gui.screen.Screen; import net.minecraft.text.*; @@ -13,6 +15,7 @@ import net.minecraft.util.Language; import java.util.HashMap; import java.util.Map; +import java.util.TreeSet; public class GuiFactory { public void buildCategory(JsonObject source, String screenId, ConfigCategory config, ConfigEntryBuilder entryBuilder) { @@ -22,6 +25,8 @@ public class GuiFactory { Respackopts.numVals.put(screenId, new HashMap<>()); if (!Respackopts.strVals.containsKey(screenId)) Respackopts.strVals.put(screenId, new HashMap<>()); + if (!Respackopts.enumKeys.containsKey(screenId)) + Respackopts.enumKeys.put(screenId, new HashMap<>()); String b = "respackopts.field." + screenId; for (Map.Entry entry : source.entrySet()) { String n = entry.getKey(); @@ -41,23 +46,19 @@ public class GuiFactory { Respackopts.boolVals.get(screenId).put(n, defaultValue); config.addEntry(entryBuilder.startBooleanToggle(getText(n, b), currentValue) .setDefaultValue(defaultValue) - .setSaveConsumer(v -> { - Respackopts.boolVals.get(screenId).put(n, v); - }) + .setSaveConsumer(v -> Respackopts.boolVals.get(screenId).put(n, v)) .build()); } else if (p.isNumber()) { double defaultValue = p.getAsDouble(); - double currentValue = defaultValue; + Double currentValue = defaultValue; if (Respackopts.numVals.get(screenId).containsKey(n)) currentValue = Respackopts.numVals.get(screenId).get(n); else Respackopts.numVals.get(screenId).put(n, defaultValue); config.addEntry(entryBuilder.startDoubleField(getText(n, b), currentValue) .setDefaultValue(defaultValue) - .setSaveConsumer(v -> { - Respackopts.numVals.get(screenId).put(n, v); - }) + .setSaveConsumer(v -> Respackopts.numVals.get(screenId).put(n, v)) .build()); } else if (p.isString()) { @@ -69,12 +70,34 @@ public class GuiFactory { Respackopts.strVals.get(screenId).put(n, defaultValue); config.addEntry(entryBuilder.startStrField(getText(n, b), currentValue) .setDefaultValue(defaultValue) - .setSaveConsumer(v -> { - Respackopts.strVals.get(screenId).put(n, v); - }) + .setSaveConsumer(v -> Respackopts.strVals.get(screenId).put(n, v)) .build()); } } + else if (e.isJsonArray()) { + TreeSet ev = Respackopts.enumKeys.get(screenId).get(n); + Double c = Respackopts.numVals.get(screenId).get(n); + String sel = ev.first(); + int i = 0; + for (String s1 : ev) { + if (c.intValue() == i) { + sel = s1; + } + i++; + } + config.addEntry(entryBuilder.startDropdownMenu(getText(n, b), (DropdownBoxEntry.SelectionTopCellElement) DropdownMenuBuilder.TopCellElementBuilder.of(sel, (s) -> s, (s) -> getText(s, n + "." + b)), new DropdownBoxEntry.DefaultSelectionCellCreator()) + .setSuggestionMode(false) + .setDefaultValue(ev.first()) + .setSelections(() -> ev.iterator()) + .setSaveConsumer(v -> { + int j = 0; + for (String s1 : ev) { + if (s1.equals(v)) + Respackopts.numVals.get(screenId).put(n, (double) j); + j++; + } + }).build()); + } else { System.err.println("[respackopts] Unsupported non-primitive datatype"); } @@ -88,11 +111,7 @@ public class GuiFactory { .setParentScreen(parent) .setTitle(getText(resourcepackid, "respackopts.title")); ConfigEntryBuilder entryBuilder = builder.entryBuilder(); - builder.setSavingRunnable(() -> { - System.out.println("Save"); - Respackopts.updateShaders(); - Respackopts.save(); - }); + builder.setSavingRunnable(Respackopts::save); ConfigCategory config = builder.getOrCreateCategory(getText(resourcepackid, "respackopts.category")); buildCategory(source, resourcepackid, config, entryBuilder); return builder.build(); diff --git a/src/main/java/io/gitlab/jfronny/respackopts/MMI.java b/src/main/java/io/gitlab/jfronny/respackopts/MMI.java index 9d0d90f..a12045c 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/MMI.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/MMI.java @@ -18,10 +18,7 @@ public class MMI implements ModMenuApi { .setParentScreen(parent) .setTitle(new TranslatableText("respackopts.mainconfig")); ConfigEntryBuilder entryBuilder = builder.entryBuilder(); - builder.setSavingRunnable(() -> { - Respackopts.updateShaders(); - Respackopts.save(); - }); + builder.setSavingRunnable(Respackopts::save); Respackopts.resPackMetas.forEach((s, v) -> { ConfigCategory config = builder.getOrCreateCategory(new TranslatableText("respackopts.category." + v.id)); Respackopts.factory.buildCategory(v.conf, v.id, config, entryBuilder); diff --git a/src/main/java/io/gitlab/jfronny/respackopts/Respackopts.java b/src/main/java/io/gitlab/jfronny/respackopts/Respackopts.java index b0b518a..3eb1286 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/Respackopts.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/Respackopts.java @@ -8,11 +8,13 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.loader.api.FabricLoader; +import javax.swing.*; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.HashSet; +import java.util.TreeSet; import java.util.function.Consumer; @Environment(EnvType.CLIENT) @@ -20,19 +22,20 @@ public class Respackopts implements ClientModInitializer { public static HashMap> boolVals; public static HashMap> numVals; public static HashMap> strVals; + public static HashMap>> enumKeys; public static Gson g = new Gson(); public static GuiFactory factory = new GuiFactory(); public static final Integer metaVersion = 1; public static HashMap resPackMetas = new HashMap<>(); public static final String ID = "respackopts"; static final Path p = FabricLoader.getInstance().getConfigDir().resolve("respackopts"); - public static final HashSet> shaderConsumers = new HashSet<>(); + public static final HashSet saveActions = new HashSet<>(); @Override public void onInitializeClient() { load(); - Respackopts.updateShaders(); + Respackopts.save(); if (FabricLoader.getInstance().isDevelopmentEnvironment()) - shaderConsumers.add(System.out::println); + saveActions.add(() -> System.out.println("Save")); } public static void save() { @@ -61,6 +64,9 @@ public class Respackopts implements ClientModInitializer { e.printStackTrace(); } } + for (Runnable action : saveActions) { + action.run(); + } } public static void load() { @@ -90,37 +96,14 @@ public class Respackopts implements ClientModInitializer { deNull(); } - public static void deNull() { + private static void deNull() { if (boolVals == null) boolVals = new HashMap<>(); if (numVals == null) numVals = new HashMap<>(); if (strVals == null) strVals = new HashMap<>(); - } - - public static void updateShaders() { - StringBuilder sb = new StringBuilder(); - numVals.forEach((s, v) -> v.forEach((s1, v1) -> { - sb.append("\n"); - sb.append("#define "); - sb.append(s); - sb.append("_"); - sb.append(s1); - sb.append(" "); - sb.append(v1); - })); - boolVals.forEach((s, v) -> v.forEach((s1, v1) -> { - if (v1) { - sb.append("\n"); - sb.append("#define "); - sb.append(s); - sb.append("_"); - sb.append(s1); - } - })); - for (Consumer consumer : shaderConsumers) { - consumer.accept(sb.toString()); - } + if (enumKeys == null) + enumKeys = new HashMap<>(); } } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/integration/FrexCompat.java b/src/main/java/io/gitlab/jfronny/respackopts/integration/FrexCompat.java index f926db0..dea19c7 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/integration/FrexCompat.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/integration/FrexCompat.java @@ -6,14 +6,52 @@ import io.gitlab.jfronny.respackopts.Respackopts; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.util.Identifier; +import java.util.concurrent.atomic.AtomicInteger; + public class FrexCompat implements FrexInitializer { - String currentShaderCode = ""; @Override public void onInitalizeFrex() { - System.out.println("[respackopts] enabling canvas support"); - ShaderConfig.registerShaderConfigSupplier(new Identifier(Respackopts.ID, "config_supplier"), () -> currentShaderCode); - Respackopts.shaderConsumers.add((s) -> { - currentShaderCode = s; + ShaderConfig.registerShaderConfigSupplier(new Identifier(Respackopts.ID, "config_supplier"), () -> { + StringBuilder sb = new StringBuilder(); + Respackopts.numVals.forEach((s, v) -> v.forEach((s1, v1) -> { + sb.append("\n"); + sb.append("#define "); + sb.append(s); + sb.append("_"); + sb.append(s1); + sb.append(" "); + String tmp = v1.toString(); + if (tmp.endsWith(".0")) + tmp = tmp.substring(0, tmp.length() - 2); + sb.append(tmp); + })); + Respackopts.boolVals.forEach((s, v) -> v.forEach((s1, v1) -> { + if (v1) { + sb.append("\n"); + sb.append("#define "); + sb.append(s); + sb.append("_"); + sb.append(s1); + } + })); + Respackopts.enumKeys.forEach((s, v) -> v.forEach((s1, v1) -> { + AtomicInteger i = new AtomicInteger(0); + v1.forEach((s2) -> { + sb.append("\n"); + sb.append("#define "); + sb.append(s); + sb.append("_"); + sb.append(s1); + sb.append("_"); + sb.append(s2); + sb.append(" "); + sb.append(i.getAndIncrement()); + }); + })); + return sb.toString(); + }); + System.out.println("[respackopts] enabled frex/canvas support"); + Respackopts.saveActions.add(() -> { try { ShaderConfig.invalidateShaderConfig(); } diff --git a/src/main/java/io/gitlab/jfronny/respackopts/mixin/ResourcePackManagerMixin.java b/src/main/java/io/gitlab/jfronny/respackopts/mixin/ResourcePackManagerMixin.java index 8dbbd94..99bdab8 100644 --- a/src/main/java/io/gitlab/jfronny/respackopts/mixin/ResourcePackManagerMixin.java +++ b/src/main/java/io/gitlab/jfronny/respackopts/mixin/ResourcePackManagerMixin.java @@ -1,9 +1,6 @@ package io.gitlab.jfronny.respackopts.mixin; -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; +import com.google.gson.*; import io.gitlab.jfronny.respackopts.Respackopts; import io.gitlab.jfronny.respackopts.data.Respackmeta; import net.minecraft.resource.ResourcePackManager; @@ -20,7 +17,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.TreeSet; @Mixin(ResourcePackManager.class) public class ResourcePackManagerMixin { @@ -38,9 +37,11 @@ public class ResourcePackManagerMixin { Respackopts.numVals.put(conf.id, new HashMap<>()); if (!Respackopts.strVals.containsKey(conf.id)) Respackopts.strVals.put(conf.id, new HashMap<>()); + if (!Respackopts.enumKeys.containsKey(conf.id)) + Respackopts.enumKeys.put(conf.id, new HashMap<>()); for (Map.Entry entry : conf.conf.entrySet()) { String n = entry.getKey(); - if (n.contains(":") || n.contains(".")) { + if (n.contains(":") || n.contains(".") || n.contains("_")) { System.err.println(n + " contains invalid characters"); continue; } @@ -60,6 +61,32 @@ public class ResourcePackManagerMixin { Respackopts.strVals.get(conf.id).put(n, p.getAsString()); } } + else if (e.isJsonArray()) { + JsonArray a = e.getAsJsonArray(); + for (JsonElement element : a) { + if (!element.isJsonPrimitive()) { + System.err.println("[respackopts] Unsupported non-primitive datatype"); + continue; + } + if (!Respackopts.enumKeys.get(conf.id).containsKey(n)) { + Respackopts.enumKeys.get(conf.id).put(n, new TreeSet<>()); + } + JsonPrimitive p = element.getAsJsonPrimitive(); + if (!p.isString()) { + System.err.println("[respackopts] Unsupported non-string enum key"); + continue; + } + String b = p.getAsString(); + if (b.contains(":") || b.contains(".") || b.contains("_")) { + System.err.println(b + " contains invalid characters"); + continue; + } + Respackopts.enumKeys.get(conf.id).get(n).add(b); + } + if (!Respackopts.numVals.get(conf.id).containsKey(n)) { + Respackopts.numVals.get(conf.id).put(n, 0d); + } + } else { System.err.println("[respackopts] Unsupported non-primitive datatype"); } @@ -75,9 +102,8 @@ public class ResourcePackManagerMixin { } } }); - Respackopts.load(); - Respackopts.updateShaders(); Respackopts.save(); + Respackopts.load(); } private boolean hasMetadata(ResourcePackProfile v, String fname) { diff --git a/src/main/resources/assets/respackopts/lang/en_us.json b/src/main/resources/assets/respackopts/lang/en_us.json index 3431a25..96e80b0 100644 --- a/src/main/resources/assets/respackopts/lang/en_us.json +++ b/src/main/resources/assets/respackopts/lang/en_us.json @@ -2,5 +2,6 @@ "respackopts.loadFailed": "Failed to load resourcepacks", "respackopts.loadError": "There is an issue with the resourcepack configs. Please take a look at the game log", "respackopts.configure": "Configure", - "respackopts.mainconfig": "ResPack Opts" + "respackopts.mainconfig": "ResPack Opts", + "respackopts.invalid": "Invalider Wert" } \ No newline at end of file