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)
115 lines
4.0 KiB
JavaScript
115 lines
4.0 KiB
JavaScript
/**
|
||
* OBB (Oriented Bounding Box) — AABB пересечение через SAT.
|
||
*
|
||
* Используется для столкновения игрока (AABB) с повёрнутыми примитивами (OBB).
|
||
*
|
||
* OBB задаётся:
|
||
* - центр (cx, cy, cz)
|
||
* - полуразмеры по локальным осям (hx, hy, hz)
|
||
* - 3 ортонормированных локальных оси (axes), задающих ориентацию
|
||
*
|
||
* AABB задаётся:
|
||
* - центр (cx, cy, cz)
|
||
* - полуразмеры (hw, hh, hd) — по мировым X/Y/Z
|
||
*
|
||
* Возвращает true если есть пересечение.
|
||
*
|
||
* Алгоритм SAT (Separating Axis Theorem): два выпуклых тела не пересекаются
|
||
* тогда и только тогда, когда есть ось, на проекции которой их интервалы
|
||
* не пересекаются. Для AABB×OBB достаточно проверить 15 осей:
|
||
* - 3 оси AABB (X/Y/Z)
|
||
* - 3 оси OBB (axes[0..2])
|
||
* - 9 cross-products пар осей
|
||
*/
|
||
|
||
const EPS = 1e-6;
|
||
|
||
/**
|
||
* Построить axes для OBB с произвольным rotation (Euler XYZ).
|
||
* Возвращает 3 единичных вектора локальных осей в мировых координатах.
|
||
*/
|
||
export function buildOBBAxes(rotX, rotY, rotZ) {
|
||
const cx = Math.cos(rotX), sx = Math.sin(rotX);
|
||
const cy = Math.cos(rotY), sy = Math.sin(rotY);
|
||
const cz = Math.cos(rotZ), sz = Math.sin(rotZ);
|
||
// Babylon использует Euler XYZ: M = Rx * Ry * Rz (Right-handed Y-up).
|
||
// Локальная ось X в мире:
|
||
const axisX = [
|
||
cy * cz,
|
||
cy * sz,
|
||
-sy,
|
||
];
|
||
const axisY = [
|
||
sx * sy * cz - cx * sz,
|
||
sx * sy * sz + cx * cz,
|
||
sx * cy,
|
||
];
|
||
const axisZ = [
|
||
cx * sy * cz + sx * sz,
|
||
cx * sy * sz - sx * cz,
|
||
cx * cy,
|
||
];
|
||
return [axisX, axisY, axisZ];
|
||
}
|
||
|
||
/**
|
||
* AABB (cx,cy,cz, hw,hh,hd) пересекается с OBB (ocx,ocy,ocz, hx,hy,hz, axes).
|
||
*/
|
||
export function aabbIntersectsOBB(
|
||
cx, cy, cz, hw, hh, hd,
|
||
ocx, ocy, ocz, hx, hy, hz, axes
|
||
) {
|
||
// Вектор от центра OBB к центру AABB
|
||
const dx = cx - ocx, dy = cy - ocy, dz = cz - ocz;
|
||
|
||
// 3 оси AABB — стандартные X/Y/Z
|
||
const aabbAxes = [
|
||
[1, 0, 0],
|
||
[0, 1, 0],
|
||
[0, 0, 1],
|
||
];
|
||
|
||
// Проекция AABB на ось — это всегда hw*|ax| + hh*|ay| + hd*|az|
|
||
const projectAABB = (a) =>
|
||
hw * Math.abs(a[0]) + hh * Math.abs(a[1]) + hd * Math.abs(a[2]);
|
||
|
||
// Проекция OBB на ось — h0*|axis0·a| + h1*|axis1·a| + h2*|axis2·a|
|
||
const obbHalves = [hx, hy, hz];
|
||
const projectOBB = (a) =>
|
||
obbHalves[0] * Math.abs(dot(axes[0], a)) +
|
||
obbHalves[1] * Math.abs(dot(axes[1], a)) +
|
||
obbHalves[2] * Math.abs(dot(axes[2], a));
|
||
|
||
// Расстояние между центрами по оси
|
||
const distOnAxis = (a) => Math.abs(dx * a[0] + dy * a[1] + dz * a[2]);
|
||
|
||
// Проверяем 3 + 3 + 9 = 15 осей
|
||
const allAxes = [];
|
||
allAxes.push(aabbAxes[0], aabbAxes[1], aabbAxes[2]);
|
||
allAxes.push(axes[0], axes[1], axes[2]);
|
||
for (let i = 0; i < 3; i++) {
|
||
for (let j = 0; j < 3; j++) {
|
||
const c = cross(aabbAxes[i], axes[j]);
|
||
if (lenSq(c) > EPS) allAxes.push(c);
|
||
}
|
||
}
|
||
|
||
for (const ax of allAxes) {
|
||
const ra = projectAABB(ax);
|
||
const rb = projectOBB(ax);
|
||
const d = distOnAxis(ax);
|
||
if (d > ra + rb + EPS) return false; // нашли разделяющую ось
|
||
}
|
||
return true;
|
||
}
|
||
|
||
function dot(a, b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
|
||
function cross(a, b) {
|
||
return [
|
||
a[1]*b[2] - a[2]*b[1],
|
||
a[2]*b[0] - a[0]*b[2],
|
||
a[0]*b[1] - a[1]*b[0],
|
||
];
|
||
}
|
||
function lenSq(a) { return a[0]*a[0] + a[1]*a[1] + a[2]*a[2]; }
|