/*
 * Volumetric_fog.glsl — Visuality / Megabound
 * "Aura Pillar" — kabut volumetrik biru-putih yang naik spiral dari kaki
 * pillar obsidian The End, 4 layer raymarch saja (sesuai permintaan).
 *
 * RIWAYAT SINGKAT:
 * Versi sebelumnya di file ini adalah kabut ambient ungu/pink untuk seluruh
 * void (8 layer, tidak terikat ke lokasi pillar manapun) dan TIDAK PERNAH
 * di-#include dari manapun (dead code). Versi ini menggantikannya total
 * dengan efek "Aura Pillar": energi biru-putih yang menempel di sekitar
 * ring pillar The End, naik spiral dari kaki pillar, makin transparan
 * makin tinggi — sesuai referensi foto yang diberikan.
 *
 * KENAPA FILE INI CUMA JALAN DI THE END:
 * Tetap 100% lokal di shaders/world1/lib/ (BUKAN di folder lib bersama
 * shaders/lib/), sejajar dengan world1/lib/waterBump.glsl yang juga
 * override lokal. Overworld (world0) dan Nether (world-1) tidak punya
 * salinan file ini sama sekali, jadi efeknya otomatis tidak pernah aktif
 * di kedua dimensi itu.
 *
 * KENAPA DI-INCLUDE DARI composite2.fsh (BUKAN composite.fsh):
 * world1/composite.fsh sengaja dipertahankan tidak berubah (baseline yang
 * sudah diketahui bekerja baik — lihat WORLD1_BLOOM_MAINTENANCE.txt).
 * Satu-satunya tahap composite yang "normal"/aktif untuk sentuhan visual
 * tambahan di world1 adalah composite2.fsh — persis tempat bloom End
 * sudah diisolasi. Supaya konsisten, Aura Pillar (volumetric fog) ini
 * juga hidup di composite2.fsh, disertakan lewat "lib/Volumetric_fog.glsl"
 * (path relatif ini resolve ke folder lokal ini, dari file yang meng-include).
 *
 * KENAPA TIDAK ADA DETEKSI BLOK OBSIDIAN PER-PIXEL:
 * world1/gbuffers_terrain.fsh cuma menulis ke colortex0 (DRAWBUFFERS:0),
 * tidak ada buffer normal/material ID sama sekali (sengaja dijaga simpel
 * sejak "custom MRT chain" versi lama dibuang karena bikin frame putih/
 * magenta rusak — lihat WORLD1_MAINTENANCE.txt). Makanya efek ini TIDAK
 * mendeteksi blok obsidian secara langsung, melainkan memakai anchor ring
 * di world-space (posisi 10 pillar didekati sebagai lingkaran di sekitar
 * pusat pulau utama The End, yang di dunia vanilla manapun selalu berada
 * di sekitar X=0, Z=0). Radius ring memakai nilai rata-rata vanilla
 * (~43 block) sebagai default; kalau pillar di dunia/build tertentu sedikit
 * meleset dari radius ini, cukup ubah AURA_RING_RADIUS / AURA_CENTER_X /
 * AURA_CENTER_Z di bawah supaya menempel pas. Karena efek ini dirancang
 * sebagai energi lembut yang membungkus (bukan garis tajam sepixel-perfect),
 * pergeseran beberapa block masih terlihat wajar.
 *
 * KENAPA NOISE-NYA PROCEDURAL (bukan pakai noisetex):
 * Sama seperti sebelumnya: semua noise dibikin sendiri lewat hash
 * function, 100% tidak bergantung ke noisetex/noise32.png.
 *
 * KENAPA CUMA 4 LAYER (bukan 8 seperti sebelumnya) TAPI TETAP HALUS:
 * Sesuai permintaan: 4 layer saja. Supaya tetap tidak kelihatan "flat"/
 * berlapis-lapis dengan sample sesedikit itu, titik awal ray tetap digeser
 * pakai dither per-pixel (interleaved gradient noise, dikirim dari
 * composite2.fsh lewat parameter "dither" — tidak butuh noisetex), dan
 * bentuk kabutnya sendiri pakai interpolasi smooth (kurva f*f*(3-2f))
 * supaya transisi antar 4 layer tetap menyatu, bukan berbentuk pelat.
 *
 * Yang harus sudah tersedia di file yang meng-include ini (composite2.fsh
 * world1 sudah punya semua ini SEBELUM baris #include):
 *   uniform vec3  cameraPosition
 *   uniform mat4  gbufferModelViewInverse
 *   uniform float frameTimeCounter
 */

// ============================ PENGATURAN ============================
#define AURA_PILLAR                 //non-aktifkan (comment baris ini) untuk mematikan Aura Pillar sepenuhnya
#define AURA_LAYERS 4                //JUMLAH LAYER RAYMARCH — sesuai permintaan, tetap di 4, jangan dinaikkan

// --- posisi & bentuk ring pillar (approksimasi world-space, lihat catatan di atas) ---
#define AURA_RING_COUNT 10           //jumlah pillar The End (vanilla = 10)
const float AURA_CENTER_X = 0.0;            //pusat pulau utama The End secara world-space (X) — vanilla selalu 0
const float AURA_CENTER_Z = 0.0;            //pusat pulau utama The End secara world-space (Z) — vanilla selalu 0

// --- ketebalan energi menempel di pillar ---

// --- ketinggian: paling kuat di kaki, makin tinggi makin transparan ---
const float AURA_BASE_Y = 64.0;             //ketinggian (y) kaki pillar / permukaan pulau utama The End

// --- gerakan spiral naik perlahan ---

// --- kekuatan & warna ---

#ifdef AURA_PILLAR

// Biru terang + putih sebagai inti (sesuai permintaan): tepi lembut biru
// tua, badan energi biru saturasi tinggi, inti paling padat memutih.
const vec3 auraEdgeColor = vec3(0.07, 0.20, 0.48);
const vec3 auraMidColor  = vec3(0.35, 0.68, 1.00);
const vec3 auraCoreColor = vec3(0.92, 0.98, 1.00);

// ------------------------- hash & noise 3D -------------------------
float auraHash(vec3 p){
	p = fract(p*0.3183099 + vec3(0.1, 0.2, 0.3));
	p *= 17.0;
	return fract(p.x*p.y*p.z*(p.x + p.y + p.z));
}

// Value noise 3D, interpolasi trilinear + kurva halus f*f*(3-2f) — kurva
// ini yang menjaga transisi antar 4 layer raymarch tetap menyatu.
float auraNoise3D(vec3 p){
	vec3 i = floor(p);
	vec3 f = fract(p);
	f = f*f*(3.0 - 2.0*f);

	float n000 = auraHash(i + vec3(0.0,0.0,0.0));
	float n100 = auraHash(i + vec3(1.0,0.0,0.0));
	float n010 = auraHash(i + vec3(0.0,1.0,0.0));
	float n110 = auraHash(i + vec3(1.0,1.0,0.0));
	float n001 = auraHash(i + vec3(0.0,0.0,1.0));
	float n101 = auraHash(i + vec3(1.0,0.0,1.0));
	float n011 = auraHash(i + vec3(0.0,1.0,1.0));
	float n111 = auraHash(i + vec3(1.0,1.0,1.0));

	float nx00 = mix(n000, n100, f.x);
	float nx10 = mix(n010, n110, f.x);
	float nx01 = mix(n001, n101, f.x);
	float nx11 = mix(n011, n111, f.x);

	float nxy0 = mix(nx00, nx10, f.y);
	float nxy1 = mix(nx01, nx11, f.y);

	return mix(nxy0, nxy1, f.z);
}

// FBM 3 oktaf (dikurangi dari 4 karena cuma 4 layer, biar tetap ringan).
// Lacunarity sedikit "ganjil" (2.02) supaya tidak gampang kebaca tiling.
float auraFBM(vec3 p){
	float sum  = 0.0;
	float amp  = 0.5;
	float freq = 1.0;
	for (int i = 0; i < 3; i++){
		sum  += auraNoise3D(p*freq)*amp;
		freq *= 2.02;
		amp  *= 0.5;
	}
	return sum;
}

// ------------------- ring pillar (polar domain-repeat) -------------------
// Mencari sumbu pillar terdekat dari sebuah titik world-space (x,z), tanpa
// perlu loop 10 pillar satu-satu: sudut polar dibagi ke AURA_RING_COUNT
// "petal" dan dibulatkan ke petal terdekat. O(1) berapapun jumlah pillarnya.
vec2 auraNearestPillarXZ(vec2 posXZ){
	vec2 rel = posXZ - vec2(AURA_CENTER_X, AURA_CENTER_Z);
	float ang = atan(rel.y, rel.x) - AURA_RING_ROTATION;
	float seg = 6.28318530718 / float(AURA_RING_COUNT);
	float nearestAngle = floor(ang/seg + 0.5)*seg + AURA_RING_ROTATION;
	return vec2(AURA_CENTER_X, AURA_CENTER_Z) + vec2(cos(nearestAngle), sin(nearestAngle))*AURA_RING_RADIUS;
}

// ------------------------- bentuk & warna energi -------------------------
// Menghitung kepadatan + warna Aura Pillar di sebuah titik world-space.
// density dikembalikan lewat "outDensity" supaya warna & bobot raymarch
// bisa dipakai konsisten oleh calcAuraPillar().
vec3 auraSampleAt(vec3 worldPos, out float outDensity){

	vec2  axisXZ  = auraNearestPillarXZ(worldPos.xz);
	vec2  local   = worldPos.xz - axisXZ;
	float distXZ  = length(local);

	// radial: inti ketat menempel ke permukaan pillar -> sebar lembut di
	// luarnya -> kolam cahaya yang melebar khusus dekat kaki pillar.
	float core    = exp(-pow(distXZ/AURA_CORE_WIDTH, 2.0));
	float glow    = exp(-pow(distXZ/AURA_GLOW_WIDTH, 2.0));
	float footPool= exp(-pow(distXZ/AURA_FOOT_SPREAD, 2.0));

	// vertikal: paling kuat persis di kaki pillar, transparan makin naik.
	float footBlend  = clamp(1.0 - (worldPos.y - AURA_BASE_Y)/6.0, 0.0, 1.0); // kolam kaki cuma di sekitar dasar
	float aboveFoot   = smoothstep(AURA_BASE_Y - 4.0, AURA_BASE_Y + 2.0, worldPos.y);
	float riseFade    = clamp(1.0 - max(worldPos.y - AURA_BASE_Y, 0.0)/AURA_RISE_HEIGHT, 0.0, 1.0);
	float vertical    = aboveFoot * riseFade*riseFade; // dikuadratkan supaya ekor atas jauh lebih transparan

	float radial = mix(core, max(core, footPool), footBlend*0.85) ;
	radial = max(radial, glow*0.45);

	// spiral naik perlahan: pita energi yang melilit sumbu pillar.
	float localAngle   = atan(local.y, local.x);
	float spiralPhase  = localAngle*AURA_SPIRAL_TWISTS
	                    + (worldPos.y - AURA_BASE_Y)*0.085
	                    - frameTimeCounter*AURA_SPIRAL_SPEED;
	float strand = 0.55 + 0.45*sin(spiralPhase*2.0);

	// noise organik pelan, ikut naik supaya terasa "mengalir" ke atas.
	float n = auraFBM(vec3(worldPos.xz*0.05, worldPos.y*0.05 - frameTimeCounter*AURA_SPIRAL_SPEED*1.3));

	float density = radial * vertical * mix(0.6, 1.0, strand) * mix(0.65, 1.15, n);
	density *= AURA_DENSITY;

	outDensity = density;

	// gradasi warna: makin padat & makin "strand" -> makin putih (inti),
	// makin tipis -> biru tua (tepi). Biru terang jadi warna badan utama.
	vec3 col = mix(auraEdgeColor, auraMidColor, clamp(density*1.8, 0.0, 1.0));
	col = mix(col, auraCoreColor, clamp(core*strand*1.4 - 0.30, 0.0, 1.0));
	return col;
}

/*
 * calcAuraPillar — raymarch 4 layer dari kamera menuju posisi fragment
 * (dibatasi AURA_MAX_DIST), mengumpulkan energi Aura Pillar sepanjang ray.
 * Hasilnya ADDITIVE murni (energi/cahaya, bukan kabut yang menggelapkan
 * scene), jadi tinggal dijumlahkan ke "color" oleh composite2.fsh.
 *
 * fposition : posisi fragment dalam VIEW SPACE (sama seperti "fragpos"
 *             yang sudah ada di composite2.fsh, dari toScreenSpace()).
 * dither    : nilai dither per-pixel [0,1) — kirim interleaved-gradient
 *             noise dari composite2.fsh (dipakai buat geser titik awal
 *             ray, bukan buat warna, jadi tidak bikin energi jadi noisy).
 *
 * Skala hasil: physical-space, satuan yang sama seperti colortex0.rgb/100.0
 * (BUKAN skala x100 milik "color" di composite2.fsh — kalikan 100 dulu di
 * titik pemanggilan kalau mau dijumlahkan ke variable "color").
 */
vec3 calcAuraPillar(in vec3 fposition, in float dither){

	float trueDist  = length(fposition);
	float marchDist = min(trueDist, AURA_MAX_DIST);

	vec3 rayDirView  = fposition/max(trueDist, 0.0001);
	vec3 rayDirWorld = mat3(gbufferModelViewInverse)*rayDirView;

	float stepSize = marchDist/float(AURA_LAYERS);
	float rayStart = stepSize*dither; // kunci anti-flat/banding dengan cuma 4 layer

	float transmittance = 1.0;
	vec3  scattered      = vec3(0.0);

	for (int i = 0; i < AURA_LAYERS; i++){
		float currentDist = rayStart + stepSize*float(i);
		vec3  worldPos    = cameraPosition + rayDirWorld*currentDist;

		float density;
		vec3  col = auraSampleAt(worldPos, density);

		// kontribusi per-layer dibatasi (min) supaya sudut pandang landai
		// (path panjang menembus "tabung" energi) tidak meledak terlalu
		// terang — tetap "tipis", bukan oversaturated.
		float contribution = min(density*stepSize*0.045, 0.22);

		scattered     += col*contribution*transmittance;
		transmittance *= exp(-density*stepSize*0.012);
	}

	return scattered*AURA_BRIGHTNESS;
}

#endif // AURA_PILLAR
