← Documentation
· Source: docs/CHANGELOG.md in the repository
Changelog - NEXUS Bodycam
Complete development log: every session, every change, in order. Extracted from the original README so the README can stay short.
RULE (mandatory for all agents): ALWAYS record here any change made to the mod — without an entry in this section, the task is not complete.
v1.0.0 — mod creation
- Temporal core (spring/exp-smooth/Perlin), bobbing, idle shake, roll, presets (SUBTLE/NORMAL/INTENSE/EXTREME), Mod Menu screen, IrisCompat, icon, access widener, lang en/pt.
Crash fixes (14:08–14:20)
GameRendererMixin: signaturerenderWorld(float, long, MatrixStack, CallbackInfo)(the old one withbooleanthrew InvalidInjectionException and the game did not start).CameraMixin:update(BlockView, Entity, boolean, boolean, float, CallbackInfo).CameraMixin.getRotation: now returns a copy of the Quaternionf instead of mutating the shared field (Iris/DH call it every frame).- Cross-review of signatures with the realistic-grass agent: 3/3 javap OK.
Real roll + amplifications (14:42)
- Roll injected into the MatrixStack of
renderWorld(getRotation was never called by the render → roll did not appear). - Amplitudes increased: idle ±0.5°, roll clamp ±10°, vertical bob ~0.12 blocks.
disableVanillaBobbingimplemented with@Redirecton bobView (previously ignored).[diag]every 60 ticks + hook lines to prove that the mixins run.
Real dt and fixed cadence (14:55)
- dt via
System.nanoTime()(clamp 0.1 s) — the oldtickDelta/20was framerate-dependent (with shaders at ~25 fps everything ran ~2.4× slower; steps took ~2 s). - Step cadence fixed in time: 1.00 s cycle walking / 0.83 s running;
sprintMultiplier/sneakMultipliernow scale only the amplitude. - Step spring k 35→100, c 8→20 (settles in ~0.3 s).
Realism pass (15:08)
- Footsteps: velocity kick on the spring every half bob cycle, foot alternation.
- Yaw inertia (camera lags on turns), sprint lean (~1.8°), GoPro-style 8 Hz micro-vibration (Perlin, scales with speed), lateral lean while strafing, base breathing (4.5 s cycle while standing only).
Step sync with the AUDIO (15:35)
- Cause: impacts fired on their own phase, unrelated to the sounds.
In vanilla,
Entity.movecallsstepOnBlock → playStepSoundsevery 1.667 blocks (~0.39 s walking, ~0.30 s running). EntityMixinonEntity.playStepSounds(private; only reacts ifthis == player) → spring kick at the moment of the sound, foot flip, phase lock; cadence from the game (EMA →iv=); fallback after 3 s without sounds.- Spring k 100→160→220, c 20→26→30; kick 1.2→1.75→2.0.
Visual phase fixed (15:52)
bobVertical = -cos(phase*2): at the sound (phase=k·π) the camera is at the bottom of the bounce → each sound = one full bounce (before: 1 bounce every 2 sounds, always late); the phase snap became the cosine minimum (jumps quadratically smaller).
Damage and landing shakes + TACZ spring 10% slower (21:15)
- Damage shake: detection via drop in
getHealth()→ 0.4 s Perlin burst (clamp(damage×0.14, 0.25, 1.4) × swayIntensity). - Rotational landing jolt: spring k=180 c=22 on pitch (impact×50) and roll alternating per foot (impact×25×footSide).
- TACZ gun spring:
omega ×0.9(10% slower; gun only, camera untouched).
Full TACZ integration (18:55–19:02)
TaczGunTransformMixinonFirstPersonRenderGunEvent.applyFirstPersonGunTransform(anchor verified by javap; refmap generated and verified 1:1).TaczSway: 2nd-order spring (flick 90° → peak ~102° → settles), VISUAL ONLY.- View smoothing (
viewSmoothSpeed) inCameraMixin.setRotation. BodycamMixinPlugin:Tacz*mixins conditioned onisModLoaded("tacz").- Config/GUI/lang:
taczSway*fields +viewSmoothSpeed, category Weapon & View. - TACZ build pipeline (see §2):
modCompileOnly+ jar inlibsedited (manifest cleaned, 4 renderer classes removed) + inventedTargetClassLoadermethod removed from the plugin (does not exist inIMixinConfigPlugin).
Realistic breathing + sound (19:21–19:37 — current session)
- Audio:
breath-in-outconverted (it was MP3 in .wav) → mono OGG 3.06 s;sounds.json+ en/pt subtitles;SoundEventbodycam:breathregistered. - SOUND BUG fixed: the MC resolves entries without a prefix in the
minecraft:namespace (File minecraft:sounds/breath-in-out.ogg does not exist+Unable to play empty soundEvent: bodycam:breath). Fix: qualified entrybodycam:breath-in-out. - Cycle = audio duration (3.06 s) → exhale exactly halfway through the cycle (request: “exactly in the middle of the audio is the breath out”).
- Asymmetric shape (fast inhale / smooth exhale) + Perlin irregularity.
- Continuous running speeds up the breathing: ≥5 s → +35%, ramp up to 20 s →
+65%;
runTresets when stopping; sound plays each cycle with pitch=mult (sync kept at any speed); volume ×1.05 (requested). - VISUAL BUG fixed: amplitude was
idleShake×0.5(0.175° = invisible); now it has its own: pitch(shape−0.35)×(0.75+0.6·exhaustion)° and Y(shape−0.35)×(0.03+0.02·exhaustion)blocks. [diag]gainedbreath=andrunT=(level confirmation in the log).- Final build of this session:
AF947D1F…557D911, 1 270 658 bytes, installed.
Session 2026-10-07 — RESEARCH + PLAN (NO code changes)
Only research and planning in this session. No code file was modified; implementation left for the next session (requested by the user).
Done (research/audit):
- The research agent (Hound) delivered a full report on bodycam effect mathematics (GLSL formulas + parameters + URLs) — summary in §11.
Body Camera Shader - V1.6.1.zipextracted for inspection at%TEMP%\opencode\bcs-v161(it is a complete OptiFine/Iris shaderpack: composite1..6, final, settings.glsl, LUTs).- Vanilla APIs confirmed with
javapon the mapped jar (yarn 1.20.1+build.10):Framebuffer:fbo(public int),textureWidth/Height,beginRead()/endRead()/beginWrite(boolean)/endWrite(),draw(int,int[,boolean]),getColorAttachment(),delete(),resize().ShaderProgram:addSampler(String,Object),getUniform(String),bind(),markUniformsDirty().SimpleFramebuffer(int,int,boolean,boolean);BufferRenderer.drawWithGlobalProgram;Tessellator.getInstance().getBuffer()+begin(DrawMode,VertexFormat)+end().GameRenderer.getFovprivate (getFov(Camera,float,boolean)).RenderSystem.setShader(Supplier<ShaderProgram>),setShaderTexture(int,int).
- Core shader pattern proven on this machine (copy from realistic-grass):
CoreShaderRegistrationCallback.EVENT.register(ctx -> ctx.register( new Identifier(MOD_ID,"post"), VertexFormats.POSITION_TEXTURE, PostShader::set))assets/bodycam/shaders/core/post.{vsh,fsh}+post.json(samplerSampler0+ uniforms declared in the JSON).
- TACZ-Refabricated API listed from the
libs/jar:- Fabric/Forge-style events in the
cn.sh1rocu.tacz.api.event.*namespace (BaseEvent,ICancellableEvent,ComputeFovModifierEvent,InputEvent$MouseButton$Pre/Post,LivingHurtEvent,PlaySoundSourceEvent…) andcom.tacz.guns.api.event.common.*(GunFireEvent,GunShootEvent,GunReloadEvent, …) — confirm the Callback signature + side (client) before subscribing. - The current mixin on
com.tacz.guns.client.event.FirstPersonRenderGunEvent .applyFirstPersonGunTransformremains as the anchor for the gun effects.
- Fabric/Forge-style events in the
Architecture decision (post-processing):
- Effects IN THE MOD (not in the shaderpack): core shader
bodycam:post+ fullscreen quad drawn at the TAIL ofGameRenderer.renderWorld(after the hand/HUD, before the GUI), with a main→aux FBO copy viaglBlitFramebuffer(ordraw) to avoid a feedback-loop when sampling the texture while writing. - Compat: works with/without Iris+BSL (Iris returns the final image to the main framebuffer before the HUD); without a pack it also runs.
- Order in the fragment (physics: world→lens→sensor): 1) fisheye/barrel, 2) radial CA, 3) cosⁿ vignette, 4) lens dirt (light reaction), 5) sensor grain (last); everything pinned to the screen (never to the world).
Implementation plan (NEXT SESSION — not done yet):
- New shaders (
assets/bodycam/shaders/core/post.vsh/fsh+post.json):- Fisheye/barrel:
r' = r·(1 + k1·r² + k2·r⁴), k1 0.10–0.30 (default ~0.18), k2 −0.15..0 (keeping k1+k2>0.02); overscan FOV+10–15%; corners with clamp + rounded mask (crop ~0.94). - Fisheye vignette:
pow(cos(r·θmax), uCosPow)(uCosPow 2.0–2.5) + mechanical component1−uMech·smoothstep(0.75,1.45,r), floor 0.08–0.20, central gain 1.10–1.30; slightly decentered/polygonal (mix 0.15–0.35). - Lens dirty: procedural (fbm + specks + scratches), fixed to the screen,
always defocused (low frequency), reacts to light:
col += bloomProxy·dirt·I(intensity 0.8–3.0, veiling 0.03–0.10); bloom proxy from local luminance sampling (no separate bloom pass — use local max). - Bodycam grain: temporal hash quantized to 30 fps, cell 1.5–2.5 px,
weight
mix(1.0,0.12,smoothstep(0.25,0.85,L))(grain in the shadows), 70–85% mono + 15–30% chromatic, soft-light (not additive), 0.03–0.08. - Bonus (cheap, include): radial CA
ca·r²0.0015–0.006.
- Fisheye/barrel:
- Chest camera: in
CameraMixin.updateTAIL (1st person only):setPos(pos.add(0, chestOffsetY, 0))with default ≈ −0.30/−0.35 blocks (eyes 1.62 → chest ~1.25–1.35 m) + constant pitch tilt (~8–12° downward) added IN the finalsetRotation(safe: vanilla re-reads pitch on the next frame); does NOT touch the player’s real yaw/pitch. - Wide FOV:
@InjectRETURN mixin onGameRenderer.getFov(private,CallbackInfoReturnable<Double>) → multiply byfovWideMult(~1.15) when chest-cam is active; check interaction with TACZ ADS. - TACZ realism (100% visual, raycast untouched):
- gun lowering while running (rotate in the transform, with a spring),
- breathing/idle sway on the gun model (Perlin already exists),
- (if Callback confirmed) extra visual recoil kick on fire.
- Config new fields in
BodycamConfig+ sanitize + GUI + lang;[diag]with new fields; then: build (§2), jar installed + sha256.
Session 2026-10-08 — FULL IMPLEMENTATION (previous session’s plan + water + DOF)
Done (new/changed code):
- Post-FX core shader (plan §8 step 1):
assets/bodycam/shaders/core/post.{json,vsh,fsh}— fisheye/barrel (FisheyeK1/K2), radial CA, cosⁿ+mechanical vignette, lens dirt, grain at 30 fps,FovWideMult; samplerDirtyLensTex.dev/bodycam/camera/PostEffects.java— registration viaCoreShaderRegistrationCallback, fullscreen quad at the TAIL ofGameRenderer.renderWorld, per-frame uniforms, binding of texturebodycam:textures/dirty_lens.pngto samplerDirtyLensTex; guards the Iris shadow-pass.- User texture integrated:
textures/Dirty Lens Grime Overlay.png→ copied toassets/bodycam/textures/dirty_lens.png.
- Chest camera + wide FOV (plan step 2):
ChestCam.java(height offset, pitch tilt, FOV multiplier).CameraMixinrewritten: chest position + view smoothing (viewSmoothSpeed) + roll + TaczSway/TaczGunRealism update.GameRendererMixin:@Inject RETURNongetFov(multiplies byfovWideMult), vanillabobViewredirect (withdisableVanillaBobbing), roll hook (fix for the old crash:renderWorldsignature corrected toFJL...MatrixStack), TAIL →PostEffects.render().
- TACZ realism (plan step 3):
TaczGunRealism.java(lowering spring, recoil kick with decay).TaczGunTransformMixinrewritten (lowering + run sway + recoil kick on the gun transform).EntityMixin: detects sprint →triggerRecoil(cast(Object) thiscorrected to the mixininstanceofpattern).
- Realistic water (extra, requested by the user):
WaterEffects.java— surface waves, procedural caustics, underwater fog, tint; registered inWorldRenderEvents.AFTER_ENTITIESinBodycamClient.
- Config + GUI + lang (plan step 4):
BodycamConfig: new fields (chestCam, fovWideMult, taczLowering, taczRunSway, taczRecoilKick, postFX/fisheye/vignette/CA/lensDirt/grain, water* incl. underwater fog/tint) +sanitize()+ updated presets.BodycamConfigScreen: new sections (Camera, Post-FX, Water) with toggles/sliders; background with a DOF/blur effect (3 layers + dark overlay, requested by the user).en_us.json/pt_br.json: new keys for all the options.
- IrisCompat:
isRenderingShadowPass()used by PostEffects. renderDofBlur()in the GUI: blurred background when opening the config menu.
Build of this session:
gradlew clean build— BUILD SUCCESSFUL (fix for 30 compilation errors: importsGlStateManager/RenderSystem/IrisCompat,isInWater→isTouchingWater, mixininstanceofcast, removal of the anonymousWorldRenderContext→PostEffects.render()without arguments).- Jar installed at
mods\NEXUS-bodycam-1.0.1+1.20.1.jar- SHA256:
A3FE4C2DCA15827C3411BABA68509C830224565232344FCE355B6309B43C72FB - Size: 3 450 189 bytes
- SHA256:
- Gradle extracted from
%TEMP%\opencode\gradle-8.5-bin.zip(old wrapper deleted;gradle-wrapper.properties+gradlew.batrecreated in the project).
Notice to the realistic-grass agent: the crash bodycam.mixins.json:
GameRendererMixin (bodycam$onRenderWorld(FJZ...) vs FJL...;) was
fixed in this session (renderWorld signature + roll hook redone
on the INVOKE of setInverseViewRotationMatrix).
Session 2026-10-08 (continuation) — EntityMixin CRASH FIX + RESTORATION of the step hook
Reported in-game crash: EntityMixin pointed at
net.minecraft.entity.Entity, but injected into tickMovement — a method that
only exists on LivingEntity (ClassNotFoundException/mixin failure at
startup). The class also had extra braces (syntax error).
Done:
EntityMixin.javarewritten:@Mixin(Entity.class)with the original and correct hook —@InjectonplayStepSounds(private vanilla method, confirmed by javap) atHEAD; only reacts if(Object) this == MinecraftClient.getInstance().player→ callsBodycamEffects.onFootstep(). This restores the footstep/audio sync that had been left without callers in the previous rewrite (footstepCount, EMA offootInterval, foot flip, phase lock).- Sprint recoil moved to
ClientPlayerEntityMixin(which already injects into the player’stickMovement): same logic (speed > 0.5 && isSprinting()→TaczGunRealism.triggerRecoil(0.3, 0.15)), with guardplayer == MinecraftClient.getInstance().player. getVelocity/isSprinting@Shadowmixins removed (inherited methods — only produced warnings); direct calls.
Build of this session:
gradle.bat build --no-daemon— BUILD SUCCESSFUL (no mixin warnings).- Jar installed at
mods\NEXUS-bodycam-1.0.1+1.20.1.jar- SHA256:
EB04FBD38C6AC4DD116FB9D6B427526822E093602D26C243D32BDFA4B505FECA - Size: 3 450 319 bytes
- SHA256:
Session 2026-10-08 (cont. 2) — post-FX VFX FIX + real DOF + compact UI
User report: “make the UI smaller and the DOF doesn’t work, nor the vfx”.
Causes found:
- VFX (post-FX) invisible:
Sampler0(the screen) was never bound to the shader — the fsh read garbage;DirtyLensTexpassed anIdentifierthat theShaderProgram.bind()ignores (only acceptsFramebuffer/AbstractTexture/Integer);post.vshappliedProjMat*ModelViewMatto coordinates already in NDC (quad projected off-screen). - DOF was not DOF:
renderDofBluronly drew 3 translucent fills — there was never any blur.
Done:
post.vsh:gl_Position = vec4(Position.xy, 0.0, 1.0)(direct NDC).PostEffects.java:copyScreen()— blit from the main FBO to aWindowFramebufferscratch (GL30.glBlitFramebuffer), avoiding a feedback-loop when sampling its own texture;addSampler("Sampler0", scratch);ensureDirt()loadsdirty_lens.pngas aNativeImageBackedTextureregistered in theTextureManagerand binds it as anAbstractTexture; single log line[bodycam] postFX render ATIVOfor log diagnostics.- Real DOF (new shader
bodycam:dofblur): json/vsh/fsh — 17-tap Gaussian blur (2 rings) withBlurRadius/ScreenSize;PostEffects.renderBlur(radius)copies the screen and draws the blurred quad; the GUI draws sharp on top of it. Also new:renderDofBlur()in the GUI callsrenderBlur(6.0f)+ overlay0x50000000(fallback0x90000000if the shader fails). - Smaller UI:
COL_W145→122,ROW_Hadaptive (clamp((height-76)/23, 12, 18), previously fixed 24), widgets 20→ROW_H, headers +10→+6, columns centered at ±(COL_W+5), rows start at y=48.
Build of this session:
gradle.bat build --no-daemon— BUILD SUCCESSFUL.- Jar installed at
mods\NEXUS-bodycam-1.0.1+1.20.1.jar- SHA256:
7D2E113C77F9C7A970EE5689530CB3617B3DEC45C35E8C0033D0123598702512 - Size: 3 453 251 bytes
- Includes:
assets/bodycam/shaders/core/{post,dofblur}.{json,vsh,fsh}
- SHA256:
Session 2026-10-08 (cont. 3) — global shader FIX (VFX+DOF show up) + UI scroll
Report: “the VFX don’t work (there’s not even blur on the GUI)… the UI is all fucked up… there’s no scroll (bottom parts cut off!)”.
Diagnosis (log + bytecode):
- The log proved that
PostEffects.render()WAS RUNNING (postFX render ATIVO) but nothing appeared — becauseTessellator.draw()callsBufferRenderer.drawWithGlobalProgram, which usesRenderSystem.getShader()and ignores the manualshader.bind()→ the quad was drawn with the wrong/null shader. - UI: ROW_H minimum 12 + headers with only a 6 gap (text is 9px) → overlapping headers; labels wider > column; slider knob drawn ON TOP OF the text; content ~379px > screen (cut off at the bottom, no scroll).
Done:
RenderSystem.setShader(() -> postShader/blurShader)+ whitesetShaderColorbefore drawQuad (post FX and GUI blur) — the real VFX fix.- UI: scrolling with
mouseScrolled(clearAndInit, 12px step, clamped tocontentBottom); visible scrollbar (white thumb, 3px); header gap 6→11; adaptiveCOL_Wmin(150, max(96, (width-24)/2-5)); full-width widgets300→colW*2+10;BodycamSlider.renderButtonredraws the text on top of the knob; long labels shortened (Chrom. Aberration/Aberr. Cromatica); method correctedrenderWidget→renderButton(1.20.1).
Build of this session:
gradle.bat build --no-daemon— BUILD SUCCESSFUL.- Jar installed at
mods\NEXUS-bodycam-1.0.1+1.20.1.jar- SHA256:
9B5E1B84E720D0C72F3BEB78F9FC3612B62A34DB943CF20F4A542FCDAAC68D29 - Size: 3 454 176 bytes
- SHA256:
Session 2026-10-08 (cont. 4) — NEW mod LOGO
- New logo in
textures\logo.png(2000×2000) → resized to 256×256 (HighQualityBicubic) and copied tosrc\main\resources\assets\bodycam\icon.png(referenced byfabric.mod.json→"icon"); original kept. - Build of this session:
gradle.bat build --no-daemon— BUILD SUCCESSFUL.- Installed jar: SHA256
6E9F81E45057D4D1924FF3B14C4BD1151B276E1AD987791E79AB7B4AE1F6617E(3 519 019 bytes; icon.png in the jar: 66 799 bytes)
Session 2026-10-08 (cont. 5) — ALL TEXTS IN ENGLISH + NEW SVG LOGO + MODRINTH DESCRIPTION
- README translated to English in place; the original Portuguese README
was duplicated first to
README(pt-br).md(untouched backup, 47 357 bytes). Structure preserved (897 lines, 46 headings, all sha256/numbers verbatim). fabric.mod.jsondescription → English: “Realistic bodycam-style camera with temporal effects, post-processing, DOF and tactical HUD for Minecraft 1.20.1”.lang/pt_br.json→ English (now identical toen_us.json, so every locale shows English).- Java strings → English:
BodycamMixinPluginlog line (TACZ not detected -> mixin {} disabled) + remaining Portuguese comments inBodycamEffects/TaczSway. Verified: no Portuguese left insrc\main. - NEW LOGO as SVG (
textures/logo.svg, master source) built with researched advanced techniques (research notes in%TEMP%\opencode\svg_techniques_research.md): lens barrel with shareduserSpaceOnUsegradient, off-centerfx/fyNVG glass,spreadMethod="reflect"specular rings, chromatic aberration filter (R/BfeOffset±2px + screen blend), metallic rim (feSpecularLighting), film grain (feTurbulence fractalNoise 0.8 / 3 octaves, alpha 0.55, opacity 0.14), REC dot with glow, HUD corner brackets, scanlines, vignette. The oldtextures/logo.png(2000×2000) was moved out of the project (backed up in the agent’s temp);assets/bodycam/icon.pngregenerated 256×256 from the SVG (69 246 bytes, transparent corners). MODRINTH.mdcreated: large English description for the Modrinth listing (features, post-FX, water, TACZ, shaders, config, install, compatibility table).- Build of this session:
gradle.bat build --no-daemon— BUILD SUCCESSFUL.- Installed jar: SHA256
3321EF62C280C3A8BAF846CA37C00249E7B263E5976DC2F3CCF5870EE4D1934A(3 521 439 bytes; icon.png in the jar: 69 246 bytes)
Session 2026-10-08 (cont. 6) — RENDER CORRUPTION FIX (root cause: Sampler0 clobbered)
- Bug reported in-game: world rendered black, entity/skin textures sliced into floating horizontal strips across the screen, white bars, a stray “glowing eyes” texture fragment bottom-left — while the bodycam FX (fisheye, dirt, grain, CA) stayed visible on top.
- Root cause PROVEN by javap of the mapped 1.20.1 jar:
VertexBuffer.drawInternal()doesfor (i = 0; i < 12; i++) shader.addSampler("Sampler" + i, RenderSystem.getShaderTexture(i))right beforeshader.bind(). Those globals still hold the last world/entity texture (the block/entity atlas), so ourpostShader.addSampler("Sampler0", scratch)was overwritten every draw: the post shader sampled the ATLAS through the fisheye while the quad (blendone/zero) replaced the whole screen with it. - Fix (no global state touched): renamed the sampler
Sampler0→ScreenTex— the vanilla loop only writes names"Sampler" + i, so a custom name survives. Files changed:post.json,post.fsh,dofblur.json,dofblur.fsh,PostEffects.java(addSampler("ScreenTex", scratch)×2).DirtyLensTexwas never affected (not aSamplerNname). Verified inside the built jar:ScreenTexpresent,Sampler0absent; grep ofsrc\main= 0 occurrences. - Build of this session:
gradle.bat build --no-daemon— BUILD SUCCESSFUL.- Installed jar: SHA256
CD680D7ED1B0FD191E63AFD4C6960AA7754F32A6149BAAE011021F8022BD8DC9(3 521 436 bytes)
- USER CONFIRMED IN-GAME: the fix WORKS (world renders again, no more
sliced textures). Follow-up request: lens dirt too strong → live config
config\bodycam.jsontunedlensDirtIntensity3.0 → 1.0,lensDirtVeiling0.06 → 0.02 (user-data file, not source; presets inBodycamConfig.applyPresetuntouched).
Session 2026-10-08 (cont. 7) — LENS DIRT UNIFIED across ALL presets
- User request (after confirming the render fix in-game): make the lens dirt the same transparency everywhere.
BodycamConfig.java: field defaultslensDirtIntensity1.5 → 1.0,lensDirtVeiling0.06 → 0.02; ALL presets inapplyPresetnow setlensDirtIntensity = 1.0; lensDirtVeiling = 0.02(previously SUBTLE 0.8 / NORMAL 1.5 / INTENSE 2.2 / EXTREME 3.0, veiling never set by presets). Grain/fisheye/CA values per preset untouched.- Live config
config\bodycam.json(user-data, separate from source):lensDirtIntensity3.0 → 1.0,lensDirtVeiling0.06 → 0.02. - Build of this session:
gradle.bat build --no-daemon— BUILD SUCCESSFUL.- Installed jar: SHA256
D1F9476D42A5EE9A6F75AC3130CBC2C5470C4E723E4C6215E50EFEB56734F80E(3 521 427 bytes)
Session 2026-10-09 — PHASE A OPTIMIZATIONS + A0 CRITICAL FIX → v1.1.0+1.20.1
- A0 (critical, pre-existing): the camera POSITION was never applied.
Camera.setPoswas opened inbodycam.accesswidenerand shadowed inCameraMixin, but never called →getVerticalOffset()(step-smoothing Y, vertical bob, breathing Y) andChestCam.applyChestCam(chest height) were complete no-ops; only rotations ever reached the camera. Fixed:ChestCam.applyChestCamnow returns the offsetVec3d(it was assigning a local variable that everyone discarded) andCameraMixincallssetPos(pos)after the offsets. Playtest required: the camera physically moves vertically for the first time (bob/steps/chestCamHeight). - A1: dead
Camera.getRotationroll hook removed. javap proof:GameRenderer.renderWorldreadsgetPitch()/getYaw()directly — the roll lives in therenderWorldMatrixStack hook and always worked; thegetRotationinject was dead code AND caused double roll on Sodium particles (BillboardParticleMixincomposes the camera rotation into the same MatrixStack). Jar scans: Sodium=1 hit, Iris/DistantHorizons/TACZ/ ImmediatelyFast=0 (README L413 “Iris/DH call getRotation” was outdated). - A2: config cache —
BodycamConfig.loadCached()caches the parsed JSON by file mtime+size (invalidated bysave()), killing 2–4 I/O+Gson parses per frame (GameRendererMixin,PostEffects,IrisCompat, client tick). The GUI keeps a guaranteed-freshload(). - A3: single spring integrator — new
camera/Spring.java(semi-implicit Euler, dt clamp 0.1 s, sub-step 0.008, optional yaw-wrap for degrees); replaced the 3 hand-copied loops:BodycamEffects(step spring + land pitch/roll jolts),TaczSway(yaw+pitch),TaczGunRealism(lowering + recoil pitch/yaw). All constants identical (k/c/omega/zeta unchanged). - A4:
waterRefraction+foamIntensityfinally wired (present in config- sanitize since the water feature, read by nobody). New uniforms
WaterRefraction+Underwater(post.json + post.fsh + PostEffects): fbm ripple on the whole sample only while in water (uv clamped, skipped when PostFX off or degraded). Foam: crest-based white blend on the surface quads scaled byfoamIntensity(0–2, default 0.5; unchanged shape/geometry).
- sanitize since the water feature, read by nobody). New uniforms
- A5: auto-quality governor (
autoQualityboolean, default true): EMA of the real frame time while the post pass runs — <40 FPS degrades (×0.5 on CA/lens dirt/grain + refraction forced off → skips the expensive fbm path),55 FPS restores; hysteresis prevents flicker; one log line per transition (
auto-quality: DEGRADADO/restaurado). - A6:
dirty_lens.pngresized 1536×1024 → 1024×683 (high-quality bicubic; 2 172 841 → 997 581 bytes ≈ −54 %). NPOT + normalized UVs → no shader change; original backed up in the agent temp dir. VRAM ≈ 6 MB → ≈ 2.8 MB. - Version bump
1.0.1+1.20.1→1.1.0+1.20.1(gradle.properties;fabric.mod.jsonuses${version}— gotcha:processResourcesdoes NOT invalidate on gradle.properties changes, acleanis mandatory or the jar keeps the old embedded version under the new file name). - Build of this session:
gradle.bat clean build --no-daemon— BUILD SUCCESSFUL (11 s).- Installed jar: SHA256
4FE1A13081C69142D52BF84AC36A38AAEEC71CA55A4D791BFDD19112F7C8BBC6(2 354 908 bytes; embedded version verified1.1.0+1.20.1; dirty_lens in jar = 997 581 bytes; WaterRefraction/Underwater uniforms present)
Session 2026-10-09 (cont.) — ROADMAP C1: Camera Inertia + Gait Transition → v1.2.0+1.20.1
- Roadmap saved to disk:
PROJECT-MOD\ROADMAP-bodycam.md— the full user roadmap (01–14 effects + TACZ table + module architecture + presets + recommended order) written to a file so it can never be lost from context again. Source of truth for Phases C/D/E. - 01 Camera Inertia / Mass Lag: view smoothing is now a 2nd-order spring
(
CameraMixin.stepInertia) instead of plain exp smoothing — the body turns first and the camera finishes 30–70 ms later (omega = 1/inertiaDelay, defaultinertiaDelay = 0.05), with a short overshoot on sudden direction changes (zeta = 1 − 0.6·inertiaOvershoot, default 0.5 → ζ 0.7 ≈ 5 % overshoot) and slight carry-on when the player stops. Wrap-aware yaw (no snap crossing ±180°), instant snap on >100° jumps (teleports) and on re-enable after toggle (inertiaInit). Legacy exp smoothing kept intact behindinertiaEnabled=false—viewSmoothSpeedfield untouched. - 04 Gait Transition: cadence now blends through ALL gaits — sneak period
(1.45 s) → walk (1.0) → sprint (0.83), continuous by speed, phase never
reset; sprint footfalls ×(1 +
gaitSprintWeight·speedNorm) heavier (default 0.35); cadence + impulse get perlin irregularity (default ±12 %) while speed changes fast (accel gate via smoothed velocity derivative); deceleration settle:settleGatedips to 0.7 during hard decel and calms foot-roll + micro jitter, then recovers smoothly. Whole feature gated bygaitTransitionEnabled(default on). - GUI + lang: two new rows under Smoothing —
Camera Inertiatoggle +Inertia Delay (s)slider;Inertia Overshootslider +Gait Transitionstoggle (en_us/pt_br keys added, English labels per convention). - Version bump
1.1.0+1.20.1→1.2.0+1.20.1(cleanbuild — the gradle.properties gotcha from Phase A still applies). - Build of this session:
gradle.bat clean build --no-daemon— BUILD SUCCESSFUL (9 s).- Installed jar: SHA256
7EFDE65968AD29A74B8A4491DAF5D54E4A7B32B99E895EFA56B0AA66D4E2C750(2 356 952 bytes; embedded version verified1.2.0+1.20.1; 43 jars in mods folder)
Session 2026-10-09 (cont. 2) — ROADMAP C2: Auto Exposure + Flash Shock → v1.2.1+1.20.1
- 06 Auto Exposure / Exposure Adaptation: temporal gain state computed CPU-side
once per render frame in
BodycamEffects.updateExposure— luminance proxy is the combined light level at the player (cheap block lookup, no framebuffer readback / histogram). Target gain1 + pow(1 − L/15, 1.4) · strength(default strength 0.35 → up to ×1.35 in full darkness). Asymmetric curve: leaving darkness adapts fast (autoExposureSpeedBright = 4.0), entering darkness recovers slowly (autoExposureSpeedDark = 1.2) — both configurable. New shader uniformExposure(post.json + post.fsh) applied right after CA as sensor gain, so vignette/dirt/grain react to the adapted image. State is only CONSUMED by the shader (single update/frame, shadow passes excluded). - 13 Flash / Brightness Shock: on a sudden upward light jump (≥6 levels,
e.g. cave mouth → sunlight, entering a lit room) a brief envelope fires —
instant attack, quadratic decay over 0.35 s, cooldown 1 s, amplitude
clamp(jump/15, 0.3, 1.0) · flashShockStrength(default 0.5). Never random: driven by the real light-level delta. Phase D can add TACZ muzzle flashes to the same envelope. Independent toggle (flashShockEnabled). - GUI + lang: 2 new rows in Post-FX (
Auto Exposuretoggle +Exposure Gain (Dark)slider;Flash Shocktoggle +Flash Strengthslider). - Config:
autoExposureEnabled/autoExposureStrength/autoExposureSpeedDark/ autoExposureSpeedBright/flashShockEnabled/flashShockStrength+ sanitize (strength 0–1, speeds 0.2–10, flash 0–1.5). - Version bump
1.2.0→1.2.1+1.20.1(clean build). - Build of this session:
gradle.bat clean build --no-daemon— BUILD SUCCESSFUL (9 s).- Installed jar: SHA256
BCF93F22C2FB2D5ADF8DF13750439BB319A0F7679613D7EE40B2BE06E833DAD4(embedded version verified1.2.1+1.20.1; Exposure uniform verified inside post.json AND post.fsh; 43 jars in mods folder)
Session 2026-10-09 (cont. 3) — ROADMAP C3: Impact Recovery → v1.2.2+1.20.1
- 03 Impact Recovery: one shared impact SEQUENCE (short vertical
compression → forward tilt → small overshoot → damped recovery) fed by
three separate trigger envelopes, all reusing the existing landing
springs (
landPitchSpring/landRollSpring/stepSpring):- Falls (existing path, unchanged amplitude) — now also sets a short 0.15 s cooldown so a landing never collides with the other triggers.
- Combat hits (HP drop ≥0.05): pitch impulse
s·18, rolls·7(alternating sides), verticals·0.45, wheres = clamp(dmg·0.1, 0.2, 1.0) · impactStrength; 0.15 s cooldown so a rapid-fire burst fires the big jolt once while the per-damage noise shake keeps running. - Horizontal collisions: fires when
horizontalCollisionis true while the EMA’d horizontal speed was >0.06 — amplitude scales with the speed that was blocked (clamp(prevSpeed/0.25, 0.2, 1.0)); 0.3 s cooldown and the EMA decay below the threshold mean pushing against a wall never re-fires. Roll alternates sides (direction of the blow is unknown). - Overshoot: when enabled the shared landing spring runs underdamped (zeta 16/(2√180) ≈ 0.60 → visible small overshoot); with the toggle off it falls back to the legacy zeta ≈0.82 and the combat/collision triggers never fire (pure legacy landing behaviour).
- GUI + lang: Motion row
Impact Recoverytoggle +Impact Strengthslider (0–2). ConfigimpactRecoveryEnabled/impactStrength+ sanitize. - Version bump
1.2.1→1.2.2+1.20.1(clean build). - Build of this session:
gradle.bat clean build --no-daemon— BUILD SUCCESSFUL (9 s).- Installed jar: SHA256
615CDE625CF5DAF05BB0B98BC121A9F0C4928DCB73FC59735E4B7C528F8A3A40(2 359 281 bytes; embedded version verified1.2.2+1.20.1; 43 jars in mods folder)
Session 2026-10-10 — AUDIT + C4–C8 + Phase D recorded → v1.3.0+1.20.1
Audit found these features already implemented in the source (previous session wrote the code and bumped the version but NEVER recorded them here — this entry retroactively documents that work, verified line by line against the actual code before writing it).
- C4 (roadmap 08) Low-Light Sensor Noise:
lowLightNoiseEnabled/gain;getGrainStrength()multiplies grain by1 + gain·darkness(darkness =pow(1 − light/15, 1.4)fromupdateExposure). - C5 (roadmap 09) Lens Contamination: three CPU envelopes in
updateLens— drops saturate underwater + accumulate in rain, condensation fires on bright light jumps (2 s cooldown), smudge accrues from wall/combat impacts (smudgeLens);lensCleanSpeed0 = persistent. Shader blocks inpost.fsh(LensDrops/LensCond/LensSmudge). - C6 (roadmap 05 + 11) Surface Footsteps + Underwater Lens:
EntityMixin → onFootstep(BlockState);surfaceGainFormapsBlockSoundGroup(metal 1.30 → wood 1.15 → stone/glass 1.05 → soft 0.75), gain smoothed step-to-step;updateUnderwaterfast-onset/slow-release blend + sheets the lens on exiting water. - C7 (roadmap 07/10/12/14):
updateGhost(sun↔view vector dot > 0.94 veiling flare),EdgeProfile(CA grows toward the edge + peripheral softness, scales withfovWideMult),updateDust(motes gated to light window 1..8),updateCritical(HP ≤ 25% → grain boost + exposure flicker + roll tilt). - C8 (roadmap 02) Angular Motion Blur:
CameraMixin.updateAngularVelocity(final camera, λ25 smoothing) →BlurVecuniform; 4-tap symmetric smear inpost.fsh, skipped entirely while still/degraded. - Phase D — TACZ realism:
TaczBridge(ONLY class touching TACZ API, loaded only ifisModLoaded("tacz");GunShootEvent/GunReloadEvent), ADS stabilization (−55% sway at full aim), movement-to-aim penalty (moveSpring+ perlin drift), landing weapon lag (landLagSpring), wall pullback (1 m raycast → draw-in), reload nod (reloadSpring); all applied inTaczGunTransformMixin, camera never touched by gunfire. - GUI + lang for every new toggle/slider (both
en_us.jsonandpt_br.json), configsanitize()clamps for all new fields. - Version was already
1.3.0+1.20.1ingradle.properties; jarNEXUS-bodycam-1.3.0+1.20.1.jarexisted inbuild\libs— but the installed jar (12:54) differed from the built one (12:57) andcore/GameState.java/core/RenderCoordinator.javawere edited after that build → stale install, fixed in the session below.
Session 2026-10-10 (cont.) — Phase E presets + module architecture → v1.4.0+1.20.1
- Architecture (roadmap §5) — now actually WIRED (was dead code):
CameraMixinno longer callsBodycamEffects.onFramedirectly — it callsRenderCoordinator.cameraUpdate(tickDelta), which (a) skips Iris shadow passes (counted inshadowSkips), (b) guards against same-frame re-entry, (c) runs the Motion module once, then (d)GameState.samplepublishes one per-frame world snapshot (velocity, sprint/sneak, ground, light, darkness) for every module to read. - Phase E — 4 new presets in
BodycamConfig.applyPreset+ GUI cycling button (now 8 values):DOCUMENTARY— steady handheld: mild bob/inertia, FOV 1.10, light CA, grain 0.04, motion blur 0.35.HORROR— unstable sensor: heavy idle shake (1.2 Hz), deep vignette (0.60), grain 0.10 + low-light gain 1.1, ghost 1.2, critical 1.5, exposure gain 0.60.TACTICAL— readable sight picture: minimal wobble (idle 0.12), FOV 1.05, CA 0.0015, dirt 0.8, motion blur 0.30.RAW GOPRO— widest lens: FOV 1.40, fisheye K1 0.32, CA 0.006, dirt 1.4, motion blur 0.80, grain 0.08.- Each preset also sets
fovWideMult/motionBlurStrength/vignetteMech/ autoExposureStrength/lowLightNoiseGain/ghostFlareStrength/ dustStrength/criticalStrength;sanitize()still clamps everything.
- Version bump
1.3.0→1.4.0+1.20.1(convention:1.x.0= feature drop;1.0.x= fixes;x.0.0= gigantic updates). - Build of this session:
gradle.bat clean build --no-daemon— BUILD SUCCESSFUL (28 s).- Installed jar: SHA256
B16E82611DC8F781B0BA242765E1E0F3F7886F6C9057D4631997616ADABB907F(2 374 907 bytes;NEXUS-bodycam-1.4.0+1.20.1.jar; old 1.3.0 jar removed from mods; 43 jars in mods folder)
Session 2026-10-10 (cont.) — config UI rebuilt + crosshair alignment — v1.4.1+1.20.1
- Config screen (
BodycamConfigScreen) fully rebuilt (user report: “UI feia”, text bugged, buttons too small):- Scroll bug fixed: category headers are now items of the content layout
(content Y − scroll), drawn only when fully inside the viewport — no more
fixed
y=38“Smoothing” label that stayed on screen while scrolling, and no header/widget can ever paint over the top/bottom bars. - No overlap: widgets are instantiated ONLY when fully inside
[contentTop, contentBot], so scrolled rows never collide with title/Save/Reset/preset (the old “Reset to Defaults” over “Step Smoothing” glitch). Layout is built by a declarative builder (hdr/sl/tg/full) that auto-pairs the two columns and computescontentH. - Bigger controls: rows
18 → 22 px, sliders22 px, top Save/Reset110×18, Preset moved to a fixed bottom bar (fullW × 26), all inside a beveled panel with header accent bars + underline. - Custom slider render: vanilla knob removed (it was drawn on top of the label); dark card + hover state + gold value track at the bottom + trimmed centered label.
- Scrollbar kept (thumb sized by
contentH), DOF-blur background kept.
- Scroll bug fixed: category headers are now items of the content layout
(content Y − scroll), drawn only when fully inside the viewport — no more
fixed
- Crosshair / block-breaking alignment fixed (user report: “a mira de
quebrar blocos não está alinhada com a crosshair”): the rendered camera is
rotated by chest-cam pitch, inertia and shake, but vanilla aims with the raw
player yaw/pitch, so the block outline + crack overlay landed away from the
crosshair.
EntityMixinnow injectsEntity.getRotationVec(F)for the LOCAL player and returns the vector built fromCameraState.finalViewYaw/Pitch(guarded: config enabled,lastCamYawnot NaN) — crosshair ray, block outline and breaking progress all sit exactly under the crosshair. - Version bump
1.4.0→1.4.1+1.20.1(convention:1.0.x/1.x.1= fixes). - Build of this session:
gradle.bat clean build --no-daemon— BUILD SUCCESSFUL (10 s).- Installed jar: SHA256
FE3FA7540BEF94C543FF7F882E8D20C95E2603B5077FAD55481FDF0677BC6B42(2 377 704 bytes;NEXUS-bodycam-1.4.1+1.20.1.jar; old 1.4.0 jar removed from mods) MODRINTH.mdupdated (8 presets + jar1.4.1).
Session 2026-10-10 (cont. 2) — aim vector sign fix — v1.4.2+1.20.1
- Aim direction inverted 180° fixed (user report: “eu miro para cima e o
bloco quebra embaixo!”):
EntityMixin.bodycam$alignedAimrebuilt the look vector withcos/sin(-angle − π)and the resulting vector was exactly the negation of vanillaEntity.getRotationVector— the raycast pointed to the opposite side of the crosshair. The math now matches vanilla byte for byte:(sin(yawRad)·cos(pitchRad), −sin(pitchRad), cos(yawRad)·cos(pitchRad))withpitchRad = pitch·0.017453292,yawRad = −yaw·0.017453292. - Verified: numeric check old = −vanilla / new = vanilla (pitch/yaw sampled);
bytecode of the remapped jar inspected;
fabric.mod.jsonreports1.4.2+1.20.1. - Version bump
1.4.1→1.4.2+1.20.1(AVISO GERAL:1.0.x= small fix). - Build of this session:
gradle.bat clean build --no-daemon— BUILD SUCCESSFUL (11 s).- Installed jar: SHA256
5B0120B49A82046104EAEE6B891675D75CFCE8AEDB16C3D4C55398A31C7360D8(2 377 730 bytes;NEXUS-bodycam-1.4.2+1.20.1.jar; old 1.4.1 jar removed from mods, 41 jars total) MODRINTH.md+ this README updated to1.4.2.
Session 2026-10-11 — vignette blur (bodycam defocus) — v1.4.3+1.20.1
- Request (user): “a vignette has to be blurred (like a real bodycam)”.
- New config
vignetteBlur(0..1, default 0.6) — scales the effect;0turns it off,1is full defocus at the corner. - Shader
bodycam:post: new uniformVignetteBlur. Just before the vignette multiply, a 12-tap golden-angle disc blur is mixed in under the masksmoothstep(0.45, 1.05, r) * VignetteBlur: the frame is sharp in the centre and progressively goes out of focus toward the border, like a real wide bodycam lens. The disc is aspect-corrected (round in pixels), clamped to the screen, the radius is0.035 * mask(~3.5 % of the screen at the corner) and the whole block is skipped when the mask is negligible (cost ≈ 0 in the centre of the frame). - GUI: new Post-FX slider
Vignette Blur(sl("vignetteBlur", 0.0, 1.0)+ getter/setter cases); lang keysbodycam.config.vignetteBlur(en_us/pt_br). fabric.mod.json:licenseMIT→GPL-3.0;contact.sources/modmenu.sourcesplaceholder (seu-usuario/bodycam) → the real repository; newcontact.issues.- Version bump
1.4.2→1.4.3+1.20.1(AVISO GERAL:1.0.x= small fix). - Build of this session:
gradlew.bat clean build --no-daemon— BUILD SUCCESSFUL (1 m 46 s).- Jar:
NEXUS-bodycam-1.4.3+1.20.1.jar— SHA256D48FE4785AC0A53371DFF1C862BB48408170E20FA0DEF9AE4646F19EEEB49BFD(2 378 260 bytes). - Verified inside the jar:
version = 1.4.3+1.20.1,VignetteBluruniform and the blur block present inpost.fsh.
- Pending: playtest of the vignette blur. ⚠️ The old
1.4.2jar was still locked inmods(game running) — it must be removed before the next launch or Fabric will see twobodycamjars. - Docs: README / MODRINTH / TODO / DEVNOTES / wiki / this changelog updated to
1.4.3.
Session 2026-10-11 (cont.) — media deduplication
media/removed — the four GIFs were byte-identical duplicates of the files ingameplay/(user’s folder: 4 GIFs + 4 MP4 clips).- README preview now points at
gameplay/*.gif. - Tracked size 26.82 MB -> ~20.6 MB. The old blobs still exist in git history
(this repo has only local/one remote,
git filter-repoavailable if a full cleanup is ever wanted).
Session 2026-10-11 (cont. 3) — vignette blur FIX: the vignette itself is blurred now
- Feedback (user): with
vignetteBlur = 1.0nothing visible changed — “the base vignette’s edges are smooth, not blurred”. - Root cause: the defocus pass ran before the vignette multiply and its
taps sampled the raw
ScreenTex, so the dark ring was painted on top of an already-blurred image — its contour could never smear. The mask was also weak right where it matters (edge midpoints: mix ~0.4, radius ~15 px), which is why it read as “nothing happened”. - Fix (
post.fsh): order reversed — vignette first, defocus after. Each of the 12 golden-angle taps now re-appliesvignette(suv, ...)+Exposureat its own position, so the dark contour genuinely bleeds into the image (real bodycam periphery). Mask starts earlier:smoothstep(0.40, 1.05, r); radius up to0.045 * mask(~48 px at the corner with the slider at 1.0). - AUTO SAVE (
BodycamConfigScreen) — user request:setVal(),setToggle()andclose()now callconfig.save(). Previously only the Save button wrote the file, and sincePostEffectsreads the config from disk (mtime cache), a slider change only reached the shader after an explicit save — ESC threw the edit away. - Diagnostic:
PostEffectslogs once on the first frame[bodycam] VignetteBlur: uniform=OK|NULL! value=..., so a missing uniform is immediately visible inlogs/latest.log. - Debug notes from the previous build (log of session 13:45): shader
registered,
postFX render ATIVO ... shaderOK=true, no GL/compile errors, IrisenableShaders=false(no shaderpack overriding the pass), no resource pack shippingassets/bodycam/**, installed jar byte-identical to the build. - Build of this session:
gradlew.bat clean build --no-daemon— BUILD SUCCESSFUL (14 s). JarNEXUS-bodycam-1.4.3+1.20.1.jar— SHA2564EA7E9D5D1FF5D084010B5F38F24B0992604AA97E31340F7232A950D1A8BF2C5(2 378 560 bytes); installed inmods(game closed, 41 jars). - Pending: playtest of the blurred vignette + auto-save.
Session 2026-10-11 (cont. 4) - the black frame border now MELTS (blurred) + GPU probe
- Evidence first (no guessing): analysed the user’s screenshot
2026-10-11_13.46.22.png with a script (LockBits, ring buckets): the black
frame measures 31-79 px (matches the fisheye crop) and the radial gradient is
flat from ring 2 to ring 6 (~41) - i.e. with
vignetteBlur = 1.0the outer rings were exactly as sharp as the centre: the defocus had no effect. - What the user actually meant: ignette() returns ~1.15 for r >= 0.3
(
centerGainapplied with an increasing weight), so the base vignette never darkens the edge - the “black thing” they see is the fisheye early return, a razor-straight cut to black. They wanted THAT edge blurred. - Fix 3 (post.fsh): instead of
return blackthe border now samples the clamped edge through a defocus disc that grows with the distance out (0.025 + 0.055 * (1 - fade)) and fades to black (fade = (1 - smoothstep(0, feather, outD)) * smoothstep(0, 0.012, screenEdge)) withfeather = 0.05 * vignetteBlur. Slider at 0 = the old hard cut. - Mask strengthened: ring mask now
smoothstep(0.35, 1.05, rr)and radius0.05 * mask(~53 px at the corner with the slider at 1.0). - GPU probe (temporary, to end the guessing): the shader paints a ~4 px
marker at
uv ~ (0.105, 0.905)encodingR = VignetteBlur >= 0.5andG = edge mask > 0.15; PostEffects reads that pixel back withglReadPixelson the first frame and logs[bodycam] GPU probe: VignetteBlurAlive=... edgeMaskAlive=... rgba=.... That is the only way to separate “the uniform never reached the GPU” from “it arrived but is invisible”. - Ruled out: only our jar ships
assets/bodycam/**(scanned all 41 mods); duv is never reassigned; the DOF blur pass only runs in the config GUI; the pass samples scratch and writes main (no read/write hazard); injection is at the TAIL ofGameRenderer.renderWorld(after the world). - Build of this session:
gradlew.bat clean build --no-daemon- BUILD SUCCESSFUL (17 s). Jar NEXUS-bodycam-1.4.3+1.20.1.jar - SHA256FEB0538D77EE1DC0DA11EF1ACC3BA586588DC3D28504171426DF3D5B01090542(2 379 645 bytes); installed inmods(game closed, 41 jars, single copy). - Pending: playtest (feathered border + stronger ring blur + auto-save) and
the GPU probe line from
logs/latest.log.
Docs site — always-latest downloads (2026-10-22)
- All download buttons/links (site hero, nav, footer, install paragraph) now
carry
data-download-latestand point atreleases/latestas static fallback;assets/js/site.jsgained a resolver that fetches the newest release from the GitHub API and upgrades every link to the direct JAR asset. - Visible version texts (hero
REC · …badge, hero button label, install paragraph version) usedata-version-labeland refresh automatically. README.mddownload line reworded: always-latest, no per-version jar name.CONTRIBUTING.md: new Release-sync rule — after every release the site must be updated in the same task (resolver covers downloads; content and version fallbacks by hand).TODO.md: maintenance item mirrors it.- Why: publishing v1.4.4 will not require touching any download URL — the buttons follow the newest release on their own.
Privacy / metadata hygiene (2026-10-22)
fabric.mod.json:authors["Adriano"]→["stable-user1223"]— real name removed from public metadata (Mod Menu displays it in-game).docs/DEVNOTES.md: local machine paths sanitized (C:\Users\Adriano\→C:\Users\<user>\)..gitignore:DOCs.txtadded — internal notes with machine-specific paths must not be tracked.README.md: fixed mailto subject typo (NEXUS-bodyca-mmod→NEXUS-bodycam-mod).- Git history: the author email on one older commit was migrated from the personal address to the brand address (history rewritten; tags force-pushed).
REC overlay HUD + config-screen polish (2026-11-11)
- New HUD module
dev.bodycam.hud.RecOverlay(registered viaHudRenderCallback): blinking● REC+ session elapsed timer (top-left), battery icon with slow drain (1 %/3 real minutes, resets on world join, floors at 8 %; colour green/amber/red) top-right, real clockHH:mm:ss+ date bottom-centre, device labelNEXUS-7 BODYCAM+ real framebuffer resolution + real FPS bottom corners. Hidden while any screen is open, F1 (hudHidden) or when the mod/overlay is off. - 4 new config toggles —
recOverlayEnabled,recOverlayTimestamp,recOverlayBattery,recOverlayInfo— in a new HUD & Overlay category of the config screen. - New keybinding
key.bodycam.toggleRec(category NEXUS Bodycam, unbound by default) toggles the overlay in-game with an action-bar confirmation (REC overlay: ON/OFF); change is saved tobodycam.json. - Config screen: one-line preset description under the preset bar for all 8 presets (localised en/pt); bottom bar height 38 → 54 to fit it.
- Lang: all new strings in
en_us+pt_br; fixed the pt_br motion-sickness warning (⚠was English text). - Verified:
gradlew.bat build --no-daemon→ BUILD SUCCESSFUL on both the fabric and the forge trees;RecOverlay.classconfirmed inside the jar.