220 lines
5.6 KiB
Python
220 lines
5.6 KiB
Python
"""Project layout and schema helpers for MetaCore project v2."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import uuid
|
|
from dataclasses import dataclass
|
|
|
|
|
|
ENGINE_NAME = "MetaCore"
|
|
PROJECT_SCHEMA_VERSION = 2
|
|
ASSET_DB_SCHEMA_VERSION = 1
|
|
METASCENE_SCHEMA_VERSION = 2
|
|
RUNTIME_SCENE_SCHEMA_VERSION = 2
|
|
MANIFEST_SCHEMA_VERSION = 1
|
|
SCENE_DESCRIPTION_EXTENSION = ".metascene"
|
|
|
|
|
|
MODEL_EXTENSIONS = {
|
|
".bam",
|
|
".egg",
|
|
".obj",
|
|
".fbx",
|
|
".gltf",
|
|
".glb",
|
|
".ply",
|
|
".stl",
|
|
}
|
|
TEXTURE_EXTENSIONS = {
|
|
".png",
|
|
".jpg",
|
|
".jpeg",
|
|
".bmp",
|
|
".tga",
|
|
".tif",
|
|
".tiff",
|
|
".dds",
|
|
".hdr",
|
|
".exr",
|
|
}
|
|
AUDIO_EXTENSIONS = {".wav", ".mp3", ".ogg", ".flac"}
|
|
VIDEO_EXTENSIONS = {".mp4", ".avi", ".mov", ".mkv", ".wmv", ".webm"}
|
|
SCRIPT_EXTENSIONS = {".py", ".pyc"}
|
|
UI_EXTENSIONS = {".json", ".ttf", ".otf", ".woff", ".woff2"}
|
|
|
|
|
|
def generate_guid() -> str:
|
|
return uuid.uuid4().hex
|
|
|
|
|
|
def normalize_path(path: str) -> str:
|
|
return os.path.normpath(os.path.abspath(path))
|
|
|
|
|
|
def relative_project_path(project_root: str, target_path: str) -> str:
|
|
project_root = normalize_path(project_root)
|
|
target_path = normalize_path(target_path)
|
|
try:
|
|
relative_path = os.path.relpath(target_path, project_root)
|
|
except ValueError:
|
|
return ""
|
|
if relative_path.startswith(".."):
|
|
return ""
|
|
return relative_path.replace("\\", "/")
|
|
|
|
|
|
def detect_asset_type(file_path: str) -> str:
|
|
extension = os.path.splitext(str(file_path or ""))[1].lower()
|
|
if extension in MODEL_EXTENSIONS:
|
|
return "model"
|
|
if extension in TEXTURE_EXTENSIONS:
|
|
return "texture"
|
|
if extension in AUDIO_EXTENSIONS:
|
|
return "audio"
|
|
if extension in VIDEO_EXTENSIONS:
|
|
return "video"
|
|
if extension in SCRIPT_EXTENSIONS:
|
|
return "script"
|
|
if extension in UI_EXTENSIONS:
|
|
return "ui"
|
|
return "other"
|
|
|
|
|
|
def get_asset_subdir_for_type(asset_type: str) -> str:
|
|
return {
|
|
"model": "Models",
|
|
"texture": "Textures",
|
|
"audio": "Audio",
|
|
"video": "Video",
|
|
"ui": "UI",
|
|
"script": "Scripts",
|
|
"other": "Misc",
|
|
}.get(asset_type, "Misc")
|
|
|
|
|
|
def default_build_settings() -> dict:
|
|
return {
|
|
"enabled_scene_guids": [],
|
|
"output_format": "directory",
|
|
"windows_player": True,
|
|
"active_profile": "default",
|
|
"profiles": {
|
|
"default": {
|
|
"name": "Default",
|
|
"target_platform": "windows",
|
|
"output_format": "directory",
|
|
"output_dir": "Builds",
|
|
"windows": {
|
|
"enabled": True,
|
|
"exe_name": "",
|
|
"windowed": True,
|
|
},
|
|
"runtime": {
|
|
"startup_scene_guid": "",
|
|
"enabled_scene_guids": [],
|
|
"script_mode": "pyc",
|
|
},
|
|
}
|
|
},
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ProjectLayout:
|
|
project_root: str
|
|
|
|
def __post_init__(self):
|
|
object.__setattr__(self, "project_root", normalize_path(self.project_root))
|
|
|
|
@property
|
|
def project_file(self) -> str:
|
|
return os.path.join(self.project_root, "project.json")
|
|
|
|
@property
|
|
def assets_root(self) -> str:
|
|
return os.path.join(self.project_root, "Assets")
|
|
|
|
@property
|
|
def scenes_root(self) -> str:
|
|
return os.path.join(self.project_root, "Scenes")
|
|
|
|
@property
|
|
def library_root(self) -> str:
|
|
return os.path.join(self.project_root, "Library")
|
|
|
|
@property
|
|
def builds_root(self) -> str:
|
|
return os.path.join(self.project_root, "Builds")
|
|
|
|
@property
|
|
def asset_db_path(self) -> str:
|
|
return os.path.join(self.library_root, "AssetDB.json")
|
|
|
|
@property
|
|
def imported_root(self) -> str:
|
|
return os.path.join(self.library_root, "Imported")
|
|
|
|
@property
|
|
def build_cache_root(self) -> str:
|
|
return os.path.join(self.library_root, "BuildCache")
|
|
|
|
@property
|
|
def scripts_root(self) -> str:
|
|
return os.path.join(self.assets_root, "Scripts")
|
|
|
|
@property
|
|
def models_root(self) -> str:
|
|
return os.path.join(self.assets_root, "Models")
|
|
|
|
@property
|
|
def textures_root(self) -> str:
|
|
return os.path.join(self.assets_root, "Textures")
|
|
|
|
@property
|
|
def audio_root(self) -> str:
|
|
return os.path.join(self.assets_root, "Audio")
|
|
|
|
@property
|
|
def video_root(self) -> str:
|
|
return os.path.join(self.assets_root, "Video")
|
|
|
|
@property
|
|
def ui_root(self) -> str:
|
|
return os.path.join(self.assets_root, "UI")
|
|
|
|
@property
|
|
def misc_root(self) -> str:
|
|
return os.path.join(self.assets_root, "Misc")
|
|
|
|
def meta_path_for_asset(self, asset_abs_path: str) -> str:
|
|
return f"{normalize_path(asset_abs_path)}.meta"
|
|
|
|
def imported_asset_dir(self, asset_guid: str) -> str:
|
|
return os.path.join(self.imported_root, asset_guid)
|
|
|
|
def build_cache_dir(self, scene_guid: str) -> str:
|
|
return os.path.join(self.build_cache_root, scene_guid)
|
|
|
|
def scene_file(self, scene_name: str) -> str:
|
|
return os.path.join(self.scenes_root, f"{scene_name}{SCENE_DESCRIPTION_EXTENSION}")
|
|
|
|
|
|
def ensure_project_directories(layout: ProjectLayout) -> None:
|
|
for path in (
|
|
layout.assets_root,
|
|
layout.models_root,
|
|
layout.textures_root,
|
|
layout.audio_root,
|
|
layout.video_root,
|
|
layout.ui_root,
|
|
layout.scripts_root,
|
|
layout.misc_root,
|
|
layout.scenes_root,
|
|
layout.library_root,
|
|
layout.imported_root,
|
|
layout.build_cache_root,
|
|
layout.builds_root,
|
|
):
|
|
os.makedirs(path, exist_ok=True)
|