Open-source веб-студия для создания игр Рублокса, двойная лицензия AGPL-3.0 + Коммерческая. Главное: - Vite 5 + React 18 + Babylon 7.54.3 + Monaco Editor + Colyseus 0.16 - Самодостаточный движок ~28к строк (66 файлов): BlockManager, TerrainVoxelBuilder, ModelManager, DecoManager, PlayerController, ScriptSandboxWorker, MultiplayerSync, 30+ GD-гейммодов - Главный редактор KubikonEditor (~37к строк) + панели, ScriptEditor (Monaco) - Витрина игр (KubikonFeed, KubikonStudio, KubikonDocs, KubikonLearn) - Geometry Dash sub-app (GdMenu, GdShop, GdRules, GdCoverArt) - 10 admin-preview каталогов для дизайнеров (скины, музыка, порталы и т.д.) - Конфигурируемый бэкенд через VITE_API_BASE — работает со staging (dev-api.rublox.pro) без настройки - Standalone-режим (VITE_STANDALONE=true) — открыть пустой редактор без бэка - Полная документация (на русском): README, ARCHITECTURE, CONTRIBUTING, SECURITY, CHANGELOG - ESLint + Prettier + EditorConfig - Legal: LICENSE (AGPL-3.0), LICENSE-COMMERCIAL.md, CLA.md, COPYRIGHT.md - Issue templates: bug_report, feature_request, security_disclosure Перед публикацией: - Все импорты из minecraftia заменены на локальные - Все хардкоды URL (minecraftia-school.ru) и внутренних IP убраны → env - Admin-эндпоинты Kubikon3DService вырезаны (остаются в приватном репо) - AdminKubikonModeration не публикуется (модерация — в team.rublox.pro) - 93 МБ ассетов public/kubikon-assets вынесены в .gitignore (раздаются через release artifact)
117 lines
4.9 KiB
JavaScript
117 lines
4.9 KiB
JavaScript
/**
|
||
* GdPostFx — пост-обработка для GD-уровней (этап G9).
|
||
*
|
||
* Подключает Babylon DefaultRenderingPipeline с:
|
||
* - Bloom — свечение ярких объектов (куб с glow, неон-края, конфетти)
|
||
* - Vignette — мягкое затемнение углов
|
||
* - ImageProcessing tone-mapping + лёгкий тёплый сдвиг
|
||
*
|
||
* Также подкручивает hemi/sun свет: тёплый свет сверху, прохладный снизу.
|
||
*
|
||
* Использование:
|
||
* const fx = new GdPostFx();
|
||
* fx.attach(scene, camera, scene3d);
|
||
* fx.dispose();
|
||
*/
|
||
import {
|
||
DefaultRenderingPipeline, Color3, Color4, ImageProcessingConfiguration,
|
||
} from '@babylonjs/core';
|
||
|
||
export class GdPostFx {
|
||
constructor() {
|
||
this.scene = null;
|
||
this._scene3d = null;
|
||
this._pipeline = null;
|
||
// Backup света (восстанавливаем при dispose)
|
||
this._lightBackup = null;
|
||
}
|
||
|
||
attach(scene, camera, scene3d) {
|
||
if (!scene || !camera) return;
|
||
this.scene = scene;
|
||
this._scene3d = scene3d;
|
||
|
||
// Качество: 'high' (default) | 'low' — из localStorage
|
||
const quality = (typeof localStorage !== 'undefined'
|
||
&& localStorage.getItem('gd_graphics_quality')) || 'high';
|
||
|
||
// === DefaultRenderingPipeline ===
|
||
const pipeline = new DefaultRenderingPipeline(
|
||
'gd_post_pipeline',
|
||
quality === 'high', // HDR только в high
|
||
scene,
|
||
[camera],
|
||
);
|
||
|
||
// Bloom — только в high quality (самый дорогой эффект)
|
||
pipeline.bloomEnabled = quality === 'high';
|
||
if (pipeline.bloomEnabled) {
|
||
pipeline.bloomThreshold = 0.85;
|
||
pipeline.bloomWeight = 0.35;
|
||
pipeline.bloomKernel = 64;
|
||
pipeline.bloomScale = 0.5;
|
||
}
|
||
|
||
// Image processing — лёгкая коррекция (без затемнения)
|
||
pipeline.imageProcessingEnabled = true;
|
||
if (pipeline.imageProcessing) {
|
||
// Tone mapping отключаем — он сжимает яркие участки и общая
|
||
// картинка темнеет. Без него bloom+vignette не «съедают» свет.
|
||
pipeline.imageProcessing.toneMappingEnabled = false;
|
||
pipeline.imageProcessing.exposure = 1.0;
|
||
pipeline.imageProcessing.contrast = 1.0;
|
||
// Лёгкий vignette — только по краям, не глобальное затемнение
|
||
pipeline.imageProcessing.vignetteEnabled = true;
|
||
pipeline.imageProcessing.vignetteWeight = 0.6;
|
||
pipeline.imageProcessing.vignetteColor = new Color4(0, 0, 0, 0);
|
||
pipeline.imageProcessing.vignetteStretch = 0.3;
|
||
pipeline.imageProcessing.vignetteCameraFov = 0.5;
|
||
pipeline.imageProcessing.vignetteBlendMode = ImageProcessingConfiguration.VIGNETTEMODE_MULTIPLY;
|
||
// Чуть-чуть насыщенности (без сдвига hue)
|
||
pipeline.imageProcessing.colorCurvesEnabled = true;
|
||
try {
|
||
const curves = pipeline.imageProcessing.colorCurves;
|
||
if (curves) {
|
||
curves.globalSaturation = 8;
|
||
}
|
||
} catch (e) {}
|
||
}
|
||
|
||
// FXAA — антиалиасинг (бесплатно с pipeline)
|
||
pipeline.fxaaEnabled = true;
|
||
pipeline.samples = 4;
|
||
|
||
this._pipeline = pipeline;
|
||
|
||
// Свет дефолтный движка не трогаем — наш предыдущий «тёплый» сдвиг
|
||
// делал картинку темнее (intensity 1.1 + diffuse 0.82B не компенсирует).
|
||
console.log('[GdPostFx] подключен (bloom + лёгкий vignette)');
|
||
}
|
||
|
||
dispose() {
|
||
if (this._pipeline) {
|
||
try { this._pipeline.dispose(); } catch (e) {}
|
||
this._pipeline = null;
|
||
}
|
||
if (this._lightBackup && this._scene3d) {
|
||
try {
|
||
const sun = this._scene3d._sunLight;
|
||
const hemi = this._scene3d._hemiLight;
|
||
if (sun && this._lightBackup.sunDiffuse) {
|
||
sun.diffuse = this._lightBackup.sunDiffuse;
|
||
sun.specular = this._lightBackup.sunSpecular;
|
||
sun.intensity = this._lightBackup.sunIntensity;
|
||
}
|
||
if (hemi && this._lightBackup.hemiDiffuse) {
|
||
hemi.diffuse = this._lightBackup.hemiDiffuse;
|
||
hemi.groundColor = this._lightBackup.hemiGroundColor;
|
||
hemi.intensity = this._lightBackup.hemiIntensity;
|
||
}
|
||
} catch (e) {}
|
||
this._lightBackup = null;
|
||
}
|
||
this._scene3d = null;
|
||
this.scene = null;
|
||
}
|
||
}
|