Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,12 @@ viewer3d-core/builders/railing — classic scripts с общей глобаль
версию поднимать обязательно. Актуальный срез (совпадает с `index.html`):

```
styles-desktop.css?v=53 styles-mobile.css?v=5 state.js?v=78
canvas.js?v=66 shared/house-builder.js?v=89 ResourceManager.js?v=14
viewer3d-core.js?v=161 viewer3d-builders.js?v=67 viewer3d-railing.js?v=18
viewer3d-entourage.js?v=14 editor3d.js?v=21 nav-desktop.js?v=143
nav-mobile.js?v=6 project-io.js?v=7 backend_API/Calculator.js?v=3
color-management.js?v=1 styles-desktop.css?v=53 styles-mobile.css?v=5
state.js?v=78 canvas.js?v=66 shared/house-builder.js?v=90
ResourceManager.js?v=14 viewer3d-core.js?v=163 viewer3d-builders.js?v=68
viewer3d-railing.js?v=18 viewer3d-entourage.js?v=14 editor3d.js?v=22
nav-desktop.js?v=143 nav-mobile.js?v=6 project-io.js?v=7
backend_API/Calculator.js?v=3
```

`viewer3d-entourage.js` автоматически детектит `IS_MOBILE` (UA + `innerWidth<768`)
Expand Down Expand Up @@ -949,6 +950,33 @@ JSON-контракта `POST /api/calculate` и схемы БД лежит в g
- В «Порядок подключения скриптов» добавлен актуальный срез `?v=N`, чтобы версии не искать
по журналу; в решения — две строки про снап ограждения.

Ветка **Mobile**: цвета и свет сцены.

- **Цвета — sRGB на входе, линейные внутри** (`color-management.js`, подключается
сразу после three.min.js). Рендер выводит sRGB, а three r128 принимал
`color: 0x…` / `color.set('#…')` как линейные — цвета образцов выходили заметно
светлее (серый #7e7e7e → #b8bec6 в кадре, тёмно-серый #1d2630 → #596f87). Модуль
делает то же, что ColorManagement новых three: `setHex`/`setStyle` переводят
sRGB → linear, `getHex` отдаёт sRGB (прочитанный и записанный заново цвет не
переводится дважды). `setRGB`, конструктор из трёх чисел и цвета GLB (glTF
хранит их линейными) не трогаются.
- **Свет подобран замером** на стенде (серый куб на серой площадке):
`SUN_INTENSITY = 2.6`, `SCENE_EXPOSURE = 0.95`; полусферическая подсветка только без
HDRI (1.8), с HDRI — 0. Было: солнце 1.5 при physicallyCorrectLights, экспозиция
0.72 (её занизили, компенсируя высветление цветов, — отсюда тёмные текстуры) и
подсветка 0.3. Итог: #7e7e7e на солнце — #8e8f93 (чуть светлее образца), тень на
земле — 0.57 от освещённого места (было 0.67; промежуточный вариант с солнцем 5 и
тенью 0.43 продукт счёл слишком плотным).
- Цвета, подобранные под старое высветление, приведены к задуманным: подложка
`PAD_COLOR` 0x3c3c3c → 0x6a6a6a (≈130 в кадре, как и было задумано), рама забора
`FENCE_FRAME_COLOR` 0x2a2a2a → 0x4a4a4a. `_texAverageColor` отдаёт sRGB-hex.
- **Разметка редактора без тональной кривой** (`toneMapped: false` у линий,
маркеров, сетки, рамок фасада): оранжевый выбора теперь ровно акцент интерфейса
(раньше выходил #dab05e), белые ручки — белые. Зелёный «прочих объектов» и сетка
заданы цветами, которые раньше были видны на экране (0x73a782, 0xb6b6b6/0xc7c7c7).
- Cache-bust: `color-management.js?v=1`, `shared/house-builder.js?v=90`,
`viewer3d-core.js?v=163`, `viewer3d-builders.js?v=68`, `editor3d.js?v=22`.

Ветка **Mobile**: заглушки каталога убраны совсем.

- Несуществующих товаров интерфейс больше не показывает: `STUB_RESULTS`
Expand Down
3 changes: 3 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,9 @@
«По выбранным фильтрам товаров нет» и «Сбросить фильтры» вместо заглушек.
- [x] Заглушки каталога убраны совсем: пустой раздел — «В этом разделе каталога
пока нет товаров», недоступный каталог — «Каталог временно недоступен».
- [x] Цвета и свет: цвета образцов переводятся из sRGB в линейные (раньше на
рендере выходили светлее образцов), экспозиция и солнце подобраны замером —
текстуры не темнее, чем надо, тени плотнее (0.57 от освещённого вместо 0.67).
- [x] Дорожки: замкнутое кольцо сшивается на шве (миттер, как на внутренних
углах); сшитые точки (шов кольца, стык линий) перетаскиваются вместе.
- [x] Смета: остался только расчёт по спецификации; описание проекта и
Expand Down
43 changes: 43 additions & 0 deletions color-management.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// ══════════════════════════════════════════════
// ЦВЕТА: sRGB на входе, линейные внутри (правка 2026-10-08)
//
// Рендер выводит картинку в sRGB (renderer.outputEncoding = sRGBEncoding), а
// освещение считается в линейном пространстве. Three r128 цвета не переводит:
// `color: 0x7e7e7e` или `color.set('#7e7e7e')` он принимал как ЛИНЕЙНЫЕ, и на
// выходе цвет выходил заметно светлее образца (серый #7e7e7e — #b8bec6 в кадре,
// тёмно-серый #1d2630 — #596f87). Текстуры при этом кодировались правильно, и
// экспозицию когда-то занизили, чтобы «не разбеливало» — так потемнели они.
//
// Здесь — то, что новые версии three делают сами (ColorManagement): цвет,
// заданный шестнадцатеричным числом или CSS-строкой, считается sRGB и
// переводится в линейный; getHex() отдаёт обратно sRGB, поэтому цвет, прочитанный
// и записанный заново (средний цвет текстуры, цвет карниза), не переводится
// дважды. setRGB, конструктор с тремя числами и цвета из GLB (glTF хранит их
// линейными) не трогаются.
//
// Подключать СРАЗУ после three.min.js, до любого кода, создающего цвета.
// ══════════════════════════════════════════════
(function () {
if (typeof THREE === 'undefined' || !THREE.Color || THREE.Color.prototype._srgbPatched) return;
const P = THREE.Color.prototype;
const setHex = P.setHex, setStyle = P.setStyle, getHex = P.getHex;

P.setHex = function (hex) {
setHex.call(this, hex);
return this.convertSRGBToLinear();
};

// Имена цветов ('white') setStyle разбирает через setHex — они уже переведены.
// Остальные формы (#hex, rgb(), hsl()) он пишет в r, g, b напрямую.
P.setStyle = function (style) {
setStyle.call(this, style);
if (/^\s*(#|rgb|hsl)/i.test(String(style))) this.convertSRGBToLinear();
return this;
};

P.getHex = function () {
return getHex.call(this.clone().convertLinearToSRGB());
};

P._srgbPatched = true;
})();
14 changes: 12 additions & 2 deletions editor3d.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,13 @@ function _e3dClearGroup() {
return g;
}

// Разметка редактора — интерфейс, а не предмет в сцене: тональная кривая рендера
// (ACES) её не трогает, цвета на экране — ровно цвета интерфейса (правка 2026-10-08;
// раньше оранжевый выбора выходил грязно-жёлтым #dab05e, белые ручки — серыми).
function _e3dLineMat(color, width) {
return new THREE.LineBasicMaterial({
color, linewidth: width || 1, depthTest: false, transparent: true, opacity: 0.95,
toneMapped: false,
});
}

Expand All @@ -445,6 +449,7 @@ function _e3dMarker(np, color, radius) {
const geo = new THREE.CircleGeometry(radius || 0.18, 20);
const mesh = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({
color, depthTest: false, transparent: true, opacity: 0.9, side: THREE.DoubleSide,
toneMapped: false,
}));
mesh.rotation.x = -Math.PI / 2;
mesh.position.set(w.x, y, w.z);
Expand Down Expand Up @@ -473,7 +478,9 @@ function _e3dRectPts(r) {
}

const E3D_COL_SEL = 0xff7a1a; // выбранный объект — оранжевый, как акцент интерфейса
const E3D_COL_IDLE = 0x2f6f3f; // прочие объекты раздела — приглушённый зелёный
// Прочие объекты раздела — приглушённый зелёный. Значение — то, что раньше было
// видно на экране (0x2f6f3f проходил через высветление и ACES и выглядел так).
const E3D_COL_IDLE = 0x73a782;

// Сетка 1 м: видна, пока открыт раздел с разметкой на земле.
function _e3dSyncGrid(show) {
Expand All @@ -482,7 +489,9 @@ function _e3dSyncGrid(show) {
if (!show) { if (E3D.grid) E3D.grid.visible = false; return; }
if (!E3D.grid) {
const div = Math.round(GRID / E3D_GRID_STEP);
E3D.grid = new THREE.GridHelper(GRID, div, 0x8a8a8a, 0xb9b9b9);
// Цвета — видимые раньше (0x8a8a8a / 0xb9b9b9 после высветления и ACES).
E3D.grid = new THREE.GridHelper(GRID, div, 0xb6b6b6, 0xc7c7c7);
E3D.grid.material.toneMapped = false;
E3D.grid.material.transparent = true;
E3D.grid.material.opacity = 0.35;
E3D.grid.material.depthWrite = false;
Expand Down Expand Up @@ -563,6 +572,7 @@ function e3dSync() {
const box = new THREE.Box3().setFromObject(seg);
const h = new THREE.Box3Helper(box, E3D_COL_SEL);
h.material.depthTest = false;
h.material.toneMapped = false;
h.renderOrder = 999;
g.add(h);
}
Expand Down
9 changes: 5 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@
<!-- ═══ SCRIPTS ═══ -->
<!-- Three.js r128 — захостен локально (cdnjs/jsdelivr недоступны в некоторых сетях) -->
<script src="vendor/three/three.min.js"></script>
<script src="color-management.js?v=1"></script>
<script src="vendor/three/OrbitControls.js"></script>
<script src="vendor/three/RGBELoader.js"></script>
<script src="vendor/three/EXRLoader.js"></script>
Expand All @@ -287,7 +288,7 @@
<script src="state.js?v=78"></script>
<script src="canvas.js?v=66"></script>
<!-- Shared house builder (модульная сборка дома по JSON-дескриптору) -->
<script src="shared/house-builder.js?v=89"></script>
<script src="shared/house-builder.js?v=90"></script>
<!-- Каталожный API: домен выбирается по хосту.
• localhost/127.0.0.1 → '' (same-origin): локальный dev-прокси devserver.py
проксирует /api и /static на sollersdev.ru.
Expand All @@ -307,13 +308,13 @@
<script src="backend_API/Calculator.js?v=3"></script>
<!-- 3D viewer: core (сцена/материалы/оркестратор) → builders (конструкции) →
railing (ограждение/навесы). Общая глобальная область, порядок важен. -->
<script src="viewer3d-core.js?v=161"></script>
<script src="viewer3d-builders.js?v=67"></script>
<script src="viewer3d-core.js?v=163"></script>
<script src="viewer3d-builders.js?v=68"></script>
<script src="viewer3d-railing.js?v=18"></script>
<script src="viewer3d-entourage.js?v=14"></script>
<!-- Редактирование прямо в 3D (версия 3D-UI): выбор объектов, сетка, подписи.
Идёт после viewer3d-*, потому что опирается на threeState и worldToCanvas. -->
<script src="editor3d.js?v=21"></script>
<script src="editor3d.js?v=22"></script>
<!-- Desktop navigation & UI -->
<script src="nav-desktop.js?v=143"></script>
<!-- Вертикальная раскладка телефона: переставляет те же узлы и ведёт свои
Expand Down
9 changes: 5 additions & 4 deletions shared/house-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@
const FOUNDATION_OVERHANG = 0.10; // фундамент шире стены на это значение наружу
const ROOF_EAVE = 0.30; // свес карниза за стену
// Цвет подкладки (отмостки) — един для дома, крыльца, террасы и ступеней.
// Базовый цвет умножается на освещение сцены (~×2.3 и с холодным оттенком неба),
// поэтому «средне-серый В КАДРЕ» получается из более тёмного значения: замеры по
// рендеру — 0x808080 → ≈205, 0x585858 → ≈160, 0x3C3C3C → ≈130 (то, что нужно).
// Цель — «средне-серый В КАДРЕ», около 130 на солнце. С переводом цветов в
// линейные (color-management.js, 2026-10-08) и новым светом замер по рендеру:
// 0x606060 → ≈116, 0x6a6a6a → ≈129, 0x707070 → ≈137. Прежние 0x3c3c3c были
// подобраны под высветление цветов и теперь давали почти чёрный (≈68).
// Тот же источник читает buildConstructionPad (viewer3d-builders.js).
const PAD_COLOR = 0x3c3c3c;
const PAD_COLOR = 0x6a6a6a;
// Отметка ВЕРХА подкладки. Плита «притоплена»: над землёй остаётся 5 мм, остальное
// уходит вниз. Так не видно её торца (раньше 5 см тёмной боковины у террасы и
// лестницы) и нет z-fighting'а с дорожками: у них верх на 0.05, низ на 0 —
Expand Down
8 changes: 4 additions & 4 deletions viewer3d-builders.js
Original file line number Diff line number Diff line change
Expand Up @@ -2488,10 +2488,10 @@ const FENCE_PANEL_T = 0.04; // толщина полотна условно
const FENCE_GROUND_GAP = 0.05; // просвет под полотном, м
const FENCE_SCHEMATIC_COLOR = 0xb0a89c; // нейтральный «условный» цвет полотна без товара
// ВСЕ части забора, кроме полотна, — тёмно-серые: товаром красится ТОЛЬКО полотно
// (требование продукта 2026-08-23). Значение занижено относительно
// «настоящего» тёмно-серого: базовый цвет умножается на освещение сцены (та же
// история, что с PAD_COLOR), и 0x4a4a4a в кадре читался как средне-серый.
const FENCE_FRAME_COLOR = 0x2a2a2a;
// (требование продукта 2026-08-23). Раньше значение занижали до 0x2a2a2a: цвета
// высветлялись, и 0x4a4a4a в кадре читался как средне-серый. С переводом цветов
// в линейные (color-management.js, 2026-10-08) работает «настоящий» тёмно-серый.
const FENCE_FRAME_COLOR = 0x4a4a4a;

const _fenceCache = {}; // url → THREE.Group (прототип) | null (не загрузилась)
const _fenceLoading = {}; // url → Promise
Expand Down
Loading
Loading