Fry3DX 0.4: an easy 3D modeller

Shapes with live settings, shape editing, knife and hole tools, drawing
shapes onto other objects, combining objects (add / subtract / overlap),
copies, thickness, painting with stickers and layers, a Layout tab, a
Print tab with checks, packed .fry files with autosave, export to GLB,
glTF, FBX, OBJ, 3MF, STL and PLY, folders in Layers, its own crash
screen, and a website for GitHub Pages in docs/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NMSrd5oe1GpUD7E6d825KP
This commit is contained in:
Nexoniarz 2026-10-19 21:18:35 +02:00
commit f3e8eca87b
135 changed files with 23423 additions and 0 deletions

View file

@ -0,0 +1,56 @@
#version 460
// Volumetric light (half resolution): the sun and the dynamic lights scattered towards the eye
// by the air along each view ray, where they reach it (12 jittered steps between the eye and the
// depth buffer; each takes the sun's shadow and, for every light in that spot's cluster, its
// falloff, cone and a one-tap shadow; Henyey-Greenstein phase), so window frames cut shafts out of
// the sun and lamps glow in the haze (like Source 2's volumetric fog). Once per pixel, whatever overdraw the scene had. Added onto the reflections
// target (TAA, or the tonemap, adds that to the scene). Rows: the targets' own, both backends.
#define KEEL_POST // screen-space: render targets, sampled clamped
#include "backend.glsl"
#define KEEL_FRAME_ONLY
#include "model.glsl"
PUSH_BLOCK {
#ifndef KEEL_GL
FrameData frame;
#endif
uint depth;
uint w, h, outW, outH;
float pad0, pad1, pad2;
vec4 proj; // tan(half fov x), tan(half fov y), near, far
} pc;
#ifndef KEEL_GL
#define FRAME pc.frame
#endif
layout(location = 0) out vec4 o_light;
#include "airlight.glsl"
void main() {
o_light = vec4(0.0);
if (FRAME.volumetric.x <= 0.0) return;
const bool sun = FRAME.sunColor.w > 0.5 && FRAME.volumetric.w > 0.5;
vec2 uv = gl_FragCoord.xy / vec2(pc.outW, pc.outH);
ivec2 px = clamp(ivec2(uv * vec2(pc.w, pc.h)), ivec2(0), ivec2(pc.w - 1, pc.h - 1));
float d = fetchTex(pc.depth, px).r;
float z = d >= 1.0 ? 1e9 : pc.proj.z * pc.proj.w / (pc.proj.w - d * (pc.proj.w - pc.proj.z));
// The view ray in world space (view: x right, y down the screen, z forward).
vec3 fwd = FRAME.camFwd.xyz, right = normalize(cross(fwd, vec3(0.0, 0.0, 1.0))), up = cross(right, fwd);
vec2 s = (uv * 2.0 - 1.0) * pc.proj.xy;
vec3 rd = normalize(fwd + right * s.x - up * s.y);
float dist = z * length(vec3(s, 1.0));
float len = min(dist, FRAME.volumetric.y);
float jitter = fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));
float lit = 0.0;
vec3 lamps = vec3(0.0);
const int N = 12;
for (int i = 0; i < N; ++i) {
vec3 w = FRAME.camPos.xyz + rd * len * ((float(i) + jitter) / float(N));
if (sun) lit += sunlitAt(w);
lamps += lightsAt(w, rd, uv);
}
float scattered = 1.0 - exp(-len * FRAME.volumetric.z);
vec3 sunPart = sun ? FRAME.sunColor.rgb * (lit / float(N)) * phaseHG(dot(rd, FRAME.sunDir.xyz), 0.6) : vec3(0.0);
// Lamps: each step's light over the stretch it stands for (the air between them is lit too).
vec3 lampPart = lamps / float(N) * scattered * 7.0;
o_light = vec4((sunPart * scattered + lampPart) * FRAME.volumetric.x, 0.0);
}