feat: 50 игр на Lua + импорт Roblox для всех + поддержка Lua в плеере #39
@ -526,11 +526,73 @@ const InspectorPanel = ({
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ padding: '4px 0' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 4 }}>
|
||||
<span>Заливка теней</span>
|
||||
<span style={{ opacity: 0.6 }}>{(selection.sceneAmbient ?? 0.3).toFixed(2)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="range" min="0" max="1" step="0.05"
|
||||
value={selection.sceneAmbient ?? 0.3}
|
||||
onChange={(e) => props.onSetLightingProps?.({ sceneAmbient: parseFloat(e.target.value) })}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
<div style={{ fontSize: 10, color: 'var(--text-dim)', marginTop: 2 }}>
|
||||
Подсветка теней — цвет в затенённых гранях. 0 = чёрные тени, 1 = плоско.
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: 'var(--text-dim)', fontStyle: 'italic', marginTop: 4 }}>
|
||||
<Icon name="sparkle" size={11} /> Цвет окружающего света подбирается автоматически по времени суток.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Цветокоррекция */}
|
||||
<div className={cl.section}>
|
||||
<div className={cl.sectionTitle}><Icon name="sparkle" size={12} /> Цветокоррекция</div>
|
||||
<div style={{ padding: '4px 0' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 4 }}>
|
||||
<span>Экспозиция</span>
|
||||
<span style={{ opacity: 0.6 }}>{(selection.exposure ?? 1.0).toFixed(2)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="range" min="0.3" max="2" step="0.05"
|
||||
value={selection.exposure ?? 1.0}
|
||||
onChange={(e) => props.onSetLightingProps?.({ exposure: parseFloat(e.target.value) })}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
<div style={{ fontSize: 10, color: 'var(--text-dim)', marginTop: 2 }}>
|
||||
Общая яркость. <1 = темнее, >1 = светлее.
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ padding: '4px 0' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 4 }}>
|
||||
<span>Контраст</span>
|
||||
<span style={{ opacity: 0.6 }}>{(selection.contrast ?? 1.0).toFixed(2)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="range" min="0.5" max="2" step="0.05"
|
||||
value={selection.contrast ?? 1.0}
|
||||
onChange={(e) => props.onSetLightingProps?.({ contrast: parseFloat(e.target.value) })}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ padding: '4px 0' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 4 }}>
|
||||
<span>Насыщенность</span>
|
||||
<span style={{ opacity: 0.6 }}>{(selection.saturation ?? 1.0).toFixed(2)}</span>
|
||||
</div>
|
||||
<input
|
||||
type="range" min="0" max="2" step="0.05"
|
||||
value={selection.saturation ?? 1.0}
|
||||
onChange={(e) => props.onSetLightingProps?.({ saturation: parseFloat(e.target.value) })}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
<div style={{ fontSize: 10, color: 'var(--text-dim)', marginTop: 2 }}>
|
||||
0 = чёрно-белое, 1 = норма, 2 = очень сочно.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Туман */}
|
||||
<div className={cl.section}>
|
||||
<div className={cl.sectionTitle}><Icon name="fog" size={12} /> Туман</div>
|
||||
|
||||
@ -37,6 +37,7 @@ import {
|
||||
Ray,
|
||||
PointerEventTypes,
|
||||
Tools as BabylonTools,
|
||||
ColorCurves,
|
||||
} from '@babylonjs/core';
|
||||
import { PlacementManager } from './PlacementManager';
|
||||
import { ShopInventoryUi } from './ShopInventoryUi';
|
||||
@ -1885,9 +1886,41 @@ export class BabylonScene {
|
||||
}
|
||||
if (typeof patch.sunIntensity === 'number' && this._sunLight) {
|
||||
this._sunLight.intensity = Math.max(0, patch.sunIntensity);
|
||||
this._sunIntensity = patch.sunIntensity;
|
||||
}
|
||||
if (typeof patch.hemiIntensity === 'number' && this._hemiLight) {
|
||||
this._hemiLight.intensity = Math.max(0, patch.hemiIntensity);
|
||||
this._hemiIntensity = patch.hemiIntensity;
|
||||
}
|
||||
// Окружающий свет (scene.ambientColor) — отдельный множитель.
|
||||
// Применяется ко всем материалам через ambient*ambient.
|
||||
if (typeof patch.sceneAmbient === 'number') {
|
||||
const v = Math.max(0, Math.min(1, patch.sceneAmbient));
|
||||
this.scene.ambientColor = new Color3(v, v, v);
|
||||
this._sceneAmbient = v;
|
||||
}
|
||||
// Цветокоррекция — экспозиция, контраст, насыщенность через
|
||||
// imageProcessingConfiguration (включает HDR pipeline).
|
||||
if (typeof patch.exposure === 'number' || typeof patch.contrast === 'number'
|
||||
|| typeof patch.saturation === 'number') {
|
||||
const ipc = this.scene.imageProcessingConfiguration;
|
||||
ipc.isEnabled = true;
|
||||
if (typeof patch.exposure === 'number') {
|
||||
ipc.exposure = Math.max(0.1, Math.min(3, patch.exposure));
|
||||
this._exposure = ipc.exposure;
|
||||
}
|
||||
if (typeof patch.contrast === 'number') {
|
||||
ipc.contrast = Math.max(0.5, Math.min(2.5, patch.contrast));
|
||||
this._contrast = ipc.contrast;
|
||||
}
|
||||
if (typeof patch.saturation === 'number') {
|
||||
// colorCurves для saturation (стандартный Babylon приём)
|
||||
if (!ipc.colorCurves) ipc.colorCurves = new ColorCurves();
|
||||
const s = Math.max(-100, Math.min(100, (patch.saturation - 1) * 100));
|
||||
ipc.colorCurves.globalSaturation = s;
|
||||
ipc.colorCurvesEnabled = true;
|
||||
this._saturation = patch.saturation;
|
||||
}
|
||||
}
|
||||
if (this.environment && typeof this.environment.setFog === 'function') {
|
||||
// Текущие значения берём из Environment, поверх накладываем patch
|
||||
|
||||
@ -506,11 +506,9 @@ export class PrimitiveManager {
|
||||
_applyMaterial(mesh, typeDef, color, material, textureUrl) {
|
||||
const matName = `${mesh.name}_mat`;
|
||||
const mat = new StandardMaterial(matName, this.scene);
|
||||
const dc = Color3.FromHexString(color || '#888888');
|
||||
mat.diffuseColor = dc;
|
||||
// ambient = 40% от цвета. Roblox-look: тень окрашена, но не
|
||||
// суммируется с прямым светом в пересвет. Белые остаются белыми.
|
||||
mat.ambientColor = new Color3(dc.r * 0.4, dc.g * 0.4, dc.b * 0.4);
|
||||
mat.diffuseColor = Color3.FromHexString(color || '#888888');
|
||||
// ambient = default (0,0,0). Освещение настраивается через
|
||||
// глобальную панель «Свет и атмосфера» (sun/hemi/saturation).
|
||||
|
||||
// Если задан textureUrl — подгружаем PNG как diffuseTexture. Это
|
||||
// используется для GD-скинов куба (например /gd/skins/cube_smile.png).
|
||||
|
||||
@ -282,6 +282,11 @@ export class SelectionManager {
|
||||
fogColor: env ? `#${[env.fogColor?.[0] ?? 0.7, env.fogColor?.[1] ?? 0.8, env.fogColor?.[2] ?? 0.9].map(c => Math.round(Math.max(0, Math.min(1, c)) * 255).toString(16).padStart(2, '0')).join('')}` : '#b0c8e6',
|
||||
shadowQuality: this._scene3d.getShadowQuality?.() || 'soft',
|
||||
ssaoEnabled: this._scene3d.getSsaoEnabled?.() || false,
|
||||
// Новые: глобальный ambient + image processing
|
||||
sceneAmbient: this._scene3d._sceneAmbient ?? 0.3,
|
||||
exposure: this._scene3d._exposure ?? 1.0,
|
||||
contrast: this._scene3d._contrast ?? 1.0,
|
||||
saturation: this._scene3d._saturation ?? 1.0,
|
||||
};
|
||||
this._notifyChange();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user