feat: initialize sunrise with 100-player shared rooms

This commit is contained in:
2026-10-30 16:36:35 +09:00
commit 31d6eac4c2
257 changed files with 66063 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
import { Box3, BufferAttribute, BufferGeometry, Group, Sphere, Vector3, Vector4 } from 'three';
import { BUILDINGS } from '../constants/buildings.ts';
import { createBuildingMesh, finishBuildingScene } from './buildingScene.ts';
import type { BuildingCounts, BuildingHeightSource } from './buildingScene.ts';
import { restoreBuildingLightSpill } from './buildingLightSpill.ts';
import type { TerrainMetadata } from './terrainGeometry.ts';
export interface BuildingAssetManifest {
version: number;
sourceHash: string;
bufferHash: string;
byteLength: number;
terrain: Pick<TerrainMetadata, 'origin' | 'observerSurfaceElevation'>;
counts: BuildingCounts;
target: [number, number, number];
meshes: {
source: BuildingHeightSource;
vertices: number;
position: number;
normal: number;
lightHeight: number;
index: number;
indexCount: number;
indexType: 'uint16' | 'uint32';
box: [number, number, number, number, number, number];
sphere: [number, number, number, number];
}[];
spill: { offset: number; size: number; bounds: [number, number, number, number] };
}
export interface BuildingAssets { manifest: BuildingAssetManifest; buffer: ArrayBuffer }
export async function loadBuildingAssets(): Promise<BuildingAssets> {
const response = await fetch(BUILDINGS.assetManifestUrl);
if (!response.ok) throw new Error('건물 메타데이터를 불러오지 못했습니다.');
const manifest = await response.json() as BuildingAssetManifest;
if (manifest.version !== BUILDINGS.assetVersion) throw new Error('건물 파일 버전이 다릅니다. 건물을 다시 생성해 주세요.');
const binary = await fetch(`${BUILDINGS.assetBufferUrl}?v=${manifest.bufferHash}`);
if (!binary.ok) throw new Error('건물 버퍼를 불러오지 못했습니다.');
const buffer = await binary.arrayBuffer();
if (buffer.byteLength !== manifest.byteLength) throw new Error('건물 버퍼 크기가 올바르지 않습니다.');
return { manifest, buffer };
}
export function createBakedBuildings({ manifest, buffer }: BuildingAssets, terrain: TerrainMetadata) {
if (manifest.version !== BUILDINGS.assetVersion || buffer.byteLength !== manifest.byteLength) {
throw new Error('건물 파일 버전 또는 버퍼 크기가 올바르지 않습니다.');
}
if (manifest.terrain.origin.latitude !== terrain.origin.latitude || manifest.terrain.origin.longitude !== terrain.origin.longitude ||
manifest.terrain.observerSurfaceElevation !== terrain.observerSurfaceElevation) {
throw new Error('건물과 지형 기준점이 다릅니다. 건물을 다시 생성해 주세요.');
}
const group = new Group();
for (const mesh of manifest.meshes) {
const geometry = new BufferGeometry();
// Views of the downloaded buffer: no extrusion, deduplication or array copies.
geometry.setAttribute('position', new BufferAttribute(new Float32Array(buffer, mesh.position, mesh.vertices * 3), 3));
geometry.setAttribute('normal', new BufferAttribute(new Float32Array(buffer, mesh.normal, mesh.vertices * 3), 3));
geometry.setAttribute('buildingLightHeight', new BufferAttribute(new Float32Array(buffer, mesh.lightHeight, mesh.vertices), 1));
const indices = mesh.indexType === 'uint32' ? new Uint32Array(buffer, mesh.index, mesh.indexCount) : new Uint16Array(buffer, mesh.index, mesh.indexCount);
geometry.setIndex(new BufferAttribute(indices, 1));
geometry.boundingBox = new Box3(new Vector3().fromArray(mesh.box), new Vector3().fromArray(mesh.box, 3));
geometry.boundingSphere = new Sphere(new Vector3().fromArray(mesh.sphere), mesh.sphere[3]);
group.add(createBuildingMesh(mesh.source, geometry));
}
const spill = restoreBuildingLightSpill(new Uint8Array(buffer, manifest.spill.offset, manifest.spill.size ** 2),
manifest.spill.size, new Vector4().fromArray(manifest.spill.bounds));
return finishBuildingScene(group, manifest.counts, new Vector3().fromArray(manifest.target), spill);
}