From 7366ab10acc2800a175de2d876c4fc71db7532ab Mon Sep 17 00:00:00 2001 From: prospanclaudebot Date: Sat, 11 Jul 2026 01:08:12 +0000 Subject: world: 🌭 карта ×2 (клиент+сервер синхронно), цикл день/ночь 5+5мин (солнце/луна/звёзды/Млечный путь + glow-небо), авианосец с мокрым асфальтом (cube-отражения) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- serve/index.html | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 7 deletions(-) (limited to 'serve/index.html') diff --git a/serve/index.html b/serve/index.html index 8a97a72..f0105e3 100644 --- a/serve/index.html +++ b/serve/index.html @@ -1165,7 +1165,7 @@ function fbm(x, y) { for (let i = 0; i < 5; i++) { s += a * vnoise(x * f, y * f); f *= 2.03; a *= 0.5; } return s; } -const TERR = 300; // half-extent (m) +const TERR = 600; // half-extent (m) — карта ×2 (синхронно с mp_server.TERR) function groundH(x, z) { if (MAP && MAP.flat) return MAP.floorY; // flooded bowl valley in the middle, tall mountains rising at the rim @@ -1178,7 +1178,7 @@ function groundH(x, z) { // ---------- scene ---------- // ---------- КАРТЫ (тема Планеты Жопа поверх той же геометрии; выбор на старте) ---------- const PZ_MAPS = { - jopa: { label:'🌊 Жопоград (затоплен)', sky:0x8fb7e8, fog:0xc2d6ea, fogN:240, fogF:900, sun:0xfff2d8, hemiS:0xbcd6ff, hemiG:0x55442f, waterY:9.0, water:0x17a89e, cLow:0x4a7a3a, cHigh:0x8f9a6a, cRock:0x6b6660, cSnow:0xe8ecf0 }, + jopa: { label:'🌊 Жопоград (затоплен)', sky:0x8fb7e8, fog:0xc2d6ea, fogN:400, fogF:1700, sun:0xfff2d8, hemiS:0xbcd6ff, hemiG:0x55442f, waterY:9.0, water:0x17a89e, cLow:0x4a7a3a, cHigh:0x8f9a6a, cRock:0x6b6660, cSnow:0xe8ecf0 }, dust: { label:'🏜 de_dust_жопа', flat:true, floorY:12, sky:0xd9c39a, fog:0xd8c7a0, fogN:200, fogF:820, sun:0xfff0d0, hemiS:0xe8d8b0, hemiG:0x6b5636, waterY:-60.0, water:0x2a3a2a, cLow:0xc2a867, cHigh:0xd9c79b, cRock:0xa8894f, cSnow:0xe8dcc0 }, mirage: { label:'🕌 de_mirage_жопа', sky:0xe6c79a, fog:0xdcc29a, fogN:210, fogF:840, sun:0xffe6c0, hemiS:0xe8cfa0, hemiG:0x7a5a3a, waterY:-60.0, water:0x2f4a4a, cLow:0xb98a5a, cHigh:0xd8b88a, cRock:0x9c7b54, cSnow:0xead9be }, perdun: { label:'🌋 Окраина пердунов', sky:0x4a3b30, fog:0x5a4a3a, fogN:120, fogF:600, sun:0xd8905a, hemiS:0x6a5540, hemiG:0x2a1e14, waterY:7.0, water:0x3a2a12, cLow:0x4a3420, cHigh:0x6a5236, cRock:0x3a2e24, cSnow:0x8a7a5a } @@ -1210,10 +1210,10 @@ sun.shadow.mapSize.set(2048, 2048); const sc = sun.shadow.camera; sc.left = -TERR; sc.right = TERR; sc.top = TERR; sc.bottom = -TERR; sc.near = 1; sc.far = 700; sc.updateProjectionMatrix(); scene.add(sun); -scene.add(new THREE.HemisphereLight(MAP.hemiS, MAP.hemiG, 0.5)); +const hemiLight = new THREE.HemisphereLight(MAP.hemiS, MAP.hemiG, 0.5); scene.add(hemiLight); // ---------- terrain mesh ---------- -const GS = 200; +const GS = 256; const tgeo = new THREE.PlaneGeometry(TERR * 2, TERR * 2, GS, GS); tgeo.rotateX(-Math.PI / 2); const pos = tgeo.attributes.position; @@ -1773,6 +1773,33 @@ function rcTick() { })(); })(); +// ---------- АВИАНОСЕЦ в центре: мокрый асфальт с отражениями (cube env) ---------- +(function buildCarrier() { + const wl = WATER_Y; + const g = new THREE.Group(); g.position.set(0, wl, 0); scene.add(g); + const cubeRT = new THREE.WebGLCubeRenderTarget(128, { generateMipmaps: true, minFilter: THREE.LinearMipmapLinearFilter }); + const cubeCam = new THREE.CubeCamera(1, 5000, cubeRT); cubeCam.position.set(0, wl + 26, 0); scene.add(cubeCam); + const hullMat = new THREE.MeshStandardMaterial({ color: 0x3a3f45, roughness: 0.7, metalness: 0.35 }); + const deckMat = new THREE.MeshStandardMaterial({ color: 0x17171b, roughness: 0.26, metalness: 0.4, envMap: cubeRT.texture, envMapIntensity: 1.15 }); // мокрый асфальт + const lineMat = new THREE.MeshStandardMaterial({ color: 0xe0e0e0, roughness: 0.5, emissive: 0x1a1a1a }); + const LEN = 320, WID = 84; + const hull = new THREE.Mesh(new THREE.BoxGeometry(WID * 0.92, 24, LEN), hullMat); hull.position.y = -5; hull.castShadow = true; g.add(hull); + const bow = new THREE.Mesh(new THREE.CylinderGeometry(0.01, WID * 0.46, 44, 4, 1), hullMat); bow.rotation.x = Math.PI / 2; bow.rotation.z = Math.PI / 4; bow.position.set(0, -5, LEN / 2 + 12); bow.scale.set(1, 1, 0.5); g.add(bow); + const deck = new THREE.Mesh(new THREE.BoxGeometry(WID, 2, LEN), deckMat); deck.position.y = 9; deck.receiveShadow = true; g.add(deck); + const angDeck = new THREE.Mesh(new THREE.BoxGeometry(30, 2, 168), deckMat); angDeck.position.set(-WID / 2 + 2, 9, -18); angDeck.rotation.y = 0.17; g.add(angDeck); + const cl = new THREE.Mesh(new THREE.BoxGeometry(1.8, 0.3, LEN * 0.86), lineMat); cl.position.set(0, 10.2, 0); g.add(cl); + for (let z = -LEN / 2 + 34; z < LEN / 2 - 12; z += 24) { const d = new THREE.Mesh(new THREE.BoxGeometry(9, 0.3, 3), lineMat); d.position.set(0, 10.25, z); g.add(d); } + const isl = new THREE.Mesh(new THREE.BoxGeometry(15, 28, 48), hullMat); isl.position.set(WID / 2 - 9, 24, 24); isl.castShadow = true; g.add(isl); + const mast = new THREE.Mesh(new THREE.CylinderGeometry(0.8, 0.8, 24, 6), hullMat); mast.position.set(WID / 2 - 9, 50, 24); g.add(mast); + const lbl = makeLabel('АВИАНОСЕЦ «ЖОПА-1»'); lbl.position.set(0, 64, 0); lbl.scale.set(46, 9, 1); g.add(lbl); + let lastCube = -1e9; + (function _cc() { + const t = performance.now(); + if (t - lastCube > 2800) { lastCube = t; g.visible = false; cubeCam.update(renderer, scene); g.visible = true; } + requestAnimationFrame(_cc); + })(); +})(); + // ---------- монумент ПАСОЧКИ (кулич с яйцами) по заявке @lzcnt ---------- (function buildPaska() { const gx = 60, gz = 120, gy = groundH(gx, gz); @@ -1855,10 +1882,14 @@ loadGallery(); // ---------- небо: градиентный купол, солнце, планета Хер + Полужопия ---------- const skyMat = new THREE.ShaderMaterial({ - side: THREE.BackSide, depthWrite: false, - uniforms: { top: { value: new THREE.Color(0x2b5ca8) }, bot: { value: new THREE.Color(0xcadcee) } }, + side: THREE.BackSide, depthWrite: false, fog: false, + uniforms: { top: { value: new THREE.Color(0x2b5ca8) }, bot: { value: new THREE.Color(0xcadcee) }, + sunPos: { value: new THREE.Vector3(0, 1, 0) }, sunGlow: { value: new THREE.Color(0xffd9a0) }, glowA: { value: 1.0 } }, vertexShader: 'varying vec3 vp; void main(){ vp=position; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }', - fragmentShader: 'varying vec3 vp; uniform vec3 top; uniform vec3 bot; void main(){ float h=clamp(normalize(vp).y*0.5+0.5,0.0,1.0); gl_FragColor=vec4(mix(bot,top,pow(h,0.75)),1.0); }' + fragmentShader: 'varying vec3 vp; uniform vec3 top; uniform vec3 bot; uniform vec3 sunPos; uniform vec3 sunGlow; uniform float glowA;' + + 'void main(){ vec3 d=normalize(vp); float h=clamp(d.y*0.5+0.5,0.0,1.0); vec3 col=mix(bot,top,pow(h,0.75));' + + ' float sd=max(dot(d,normalize(sunPos)),0.0); col += sunGlow*(pow(sd,64.0)*1.3 + pow(sd,6.0)*0.28 + pow(sd,1.5)*0.09)*glowA;' + + ' gl_FragColor=vec4(col,1.0); }' }); const skyGroup = new THREE.Group(); scene.add(skyGroup); // follows the camera -> celestial bodies at infinity, never clipped skyGroup.add(new THREE.Mesh(new THREE.SphereGeometry(1400, 32, 16), skyMat)); @@ -1874,6 +1905,68 @@ function bigPlanet(radius, color, dx, dy, dz, dist, emiss) { bigPlanet(150, 0x7a5aa8, -0.35, 0.5, -1.0, 1150, 0.35); // Хер (газовый гигант) bigPlanet(40, 0xb6aec4, 0.55, 0.5, -0.85, 950, 0.25); // Полужопие Ближнее bigPlanet(28, 0x98a0ba, -0.7, 0.42, -0.7, 1000, 0.25); // Полужопие Дальнее + +// ---------- ЦИКЛ ДЕНЬ/НОЧЬ + луна + звёзды + Млечный путь ---------- +const dayNight = (function () { + const c = (h) => new THREE.Color(h); + const moonMesh = new THREE.Mesh(new THREE.SphereGeometry(38, 24, 16), new THREE.MeshBasicMaterial({ color: 0xdfe6f5, fog: false })); + skyGroup.add(moonMesh); + const moonLight = new THREE.DirectionalLight(0x8fb0e0, 0.0); scene.add(moonLight); + + const nightSky = new THREE.Group(); skyGroup.add(nightSky); + const STAR_N = 2600, R = 1320, sg = new THREE.BufferGeometry(); + const spos = new Float32Array(STAR_N * 3), scol = new Float32Array(STAR_N * 3); + const mwN = new THREE.Vector3(0.3, 0.55, 0.78).normalize(); + for (let i = 0; i < STAR_N; i++) { + let dir = new THREE.Vector3(), milky = i < STAR_N * 0.45; + for (let tr = 0; tr < 24; tr++) { + const u = Math.random(), v = Math.random(), th = u * 6.2831853, ph = Math.acos(2 * v - 1); + dir.set(Math.sin(ph) * Math.cos(th), Math.abs(Math.cos(ph)) * 0.7 + 0.12, Math.sin(ph) * Math.sin(th)).normalize(); + if (!milky || Math.abs(dir.dot(mwN)) < 0.13) break; + } + spos[i * 3] = dir.x * R; spos[i * 3 + 1] = dir.y * R; spos[i * 3 + 2] = dir.z * R; + const w = 0.75 + Math.random() * 0.25, cc = c(0xffffff).lerp(c(Math.random() < 0.5 ? 0xbcd0ff : 0xffe9c0), 0.4).multiplyScalar(w); + scol[i * 3] = cc.r; scol[i * 3 + 1] = cc.g; scol[i * 3 + 2] = cc.b; + } + sg.setAttribute('position', new THREE.BufferAttribute(spos, 3)); + sg.setAttribute('color', new THREE.BufferAttribute(scol, 3)); + const starMat = new THREE.PointsMaterial({ size: 7, sizeAttenuation: true, vertexColors: true, transparent: true, opacity: 0, depthWrite: false, fog: false }); + nightSky.add(new THREE.Points(sg, starMat)); + + const mwMat = new THREE.MeshBasicMaterial({ color: 0x9fb8e8, transparent: true, opacity: 0, blending: THREE.AdditiveBlending, depthWrite: false, fog: false, side: THREE.DoubleSide }); + const mw = new THREE.Mesh(new THREE.TorusGeometry(R * 0.98, R * 0.17, 2, 80), mwMat); + mw.quaternion.setFromUnitVectors(new THREE.Vector3(0, 0, 1), mwN); nightSky.add(mw); + + function updateSky(now) { + const DAYLEN = 600; + const dt = (typeof window.__t0 === 'number') ? window.__t0 : (now / 1000 % DAYLEN) / DAYLEN; + const a = (dt - 0.25) * 6.2831853; + sunDir.set(Math.cos(a), Math.sin(a), 0.32).normalize(); + const el = sunDir.y; + const dayF = THREE.MathUtils.clamp((el + 0.12) / 0.30, 0, 1); + const nightF = 1 - dayF; + const duskF = THREE.MathUtils.clamp(1.0 - Math.abs(el) / 0.28, 0, 1); + const hi = THREE.MathUtils.clamp(el / 0.4, 0, 1); + const sc = c(0xff8a3c).lerp(c(0xfff2d8), hi); + sunMesh.position.copy(sunDir).multiplyScalar(1150); sunMesh.visible = el > -0.14; sunMesh.material.color.copy(sc); + sun.position.copy(sunDir).multiplyScalar(300); sun.color.copy(sc); sun.intensity = dayF * 1.25; + const moonDir = sunDir.clone().negate(); + moonMesh.position.copy(moonDir).multiplyScalar(1150); moonMesh.visible = moonDir.y > -0.1; + moonLight.position.copy(moonDir).multiplyScalar(300); moonLight.intensity = nightF * 0.22; + hemiLight.intensity = 0.12 + dayF * 0.42; + hemiLight.color.copy(c(0x8098c8).lerp(c(MAP.hemiS), dayF)); + hemiLight.groundColor.copy(c(0x0a0c18).lerp(c(MAP.hemiG), dayF)); + skyMat.uniforms.top.value.copy(c(0x070b24)).lerp(c(0x2b5ca8), dayF); + skyMat.uniforms.bot.value.copy(c(0x0b1030)).lerp(c(0xcadcee), dayF).lerp(c(0xff9a5a), duskF * 0.45); + skyMat.uniforms.sunPos.value.copy(sunDir); + skyMat.uniforms.sunGlow.value.copy(c(0xffb060).lerp(c(0xfff0d0), hi)); + skyMat.uniforms.glowA.value = Math.max(dayF, duskF * 0.8); + if (scene.fog) scene.fog.color.copy(c(0x0a0e24)).lerp(c(MAP.fog), dayF).lerp(c(0xff9a5a), duskF * 0.28); + starMat.opacity = nightF; mwMat.opacity = nightF * 0.5; nightSky.visible = nightF > 0.02; + nightSky.rotation.y = now / 1000 * 0.006; + } + return { updateSky }; +})(); const _sunWorld = new THREE.Vector3(); // ---------- god rays (радиальный пост-эффект от солнца) ---------- @@ -2291,6 +2384,7 @@ function tick(now) { '° · онлайн: ' + (1 + Object.keys(peers).length) + (mpStatus ? ' ' + mpStatus : '') + (freezePos ? ' · ⏸ ПОЗИЦИЯ ЗАФИКС.' : '') + (dbgVec ? ' · ↑вектора' : ''); rcTick(); // ЖОПО-СВО: рубежи + гоночный HUD + dayNight.updateSky(now); // цикл день/ночь: солнце/луна/звёзды/небо/свет/туман skyGroup.position.copy(camera.position); // sky/planets ride with the camera (at infinity, no clip) if (lowQ) { renderer.render(scene, camera); // low quality: straight to screen, no god rays -- cgit v1.2.3