File size: 3,169 Bytes
57e0a01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""
Core data structures for InteriorGen3D scene representation.
"""
from dataclasses import dataclass, field
from typing import List, Optional, Dict, Tuple
import numpy as np


@dataclass
class BoundingBox3D:
    """3D oriented bounding box for furniture objects."""
    center: np.ndarray
    dimensions: np.ndarray
    rotation: np.ndarray
    confidence: float = 1.0
    
    @property
    def corners(self) -> np.ndarray:
        w, h, d = self.dimensions / 2
        corners_local = np.array([
            [-w, -h, -d], [w, -h, -d], [w, h, -d], [-w, h, -d],
            [-w, -h, d], [w, -h, d], [w, h, d], [-w, h, d]
        ])
        return (self.rotation @ corners_local.T).T + self.center
    
    @property
    def volume(self) -> float:
        return float(np.prod(self.dimensions))


@dataclass
class WallPlane:
    normal: np.ndarray
    offset: float
    height: float
    start_point: np.ndarray
    end_point: np.ndarray
    has_window: bool = False
    has_door: bool = False
    material_id: Optional[str] = None


@dataclass
class RoomLayout:
    walls: List[WallPlane] = field(default_factory=list)
    floor_height: float = 0.0
    ceiling_height: float = 2.7
    floor_polygon: Optional[np.ndarray] = None
    doors: List[Dict] = field(default_factory=list)
    windows: List[Dict] = field(default_factory=list)


@dataclass
class SceneObject:
    object_id: str
    category: str
    bbox: BoundingBox3D
    mesh_path: Optional[str] = None
    texture_paths: Optional[Dict[str, str]] = None
    gaussian_splat_path: Optional[str] = None
    confidence: float = 1.0
    material_type: Optional[str] = None
    is_on_floor: bool = True
    parent_object_id: Optional[str] = None


@dataclass
class SpatialRelation:
    subject_id: str
    object_id: str
    relation: str
    confidence: float = 1.0


@dataclass
class SceneGraph:
    room_layout: RoomLayout
    objects: List[SceneObject] = field(default_factory=list)
    relations: List[SpatialRelation] = field(default_factory=list)
    camera_pose: Optional[np.ndarray] = None
    camera_intrinsics: Optional[np.ndarray] = None
    metric_scale: float = 1.0
    
    def get_object(self, object_id: str) -> Optional[SceneObject]:
        for obj in self.objects:
            if obj.object_id == object_id:
                return obj
        return None
    
    def get_objects_by_category(self, category: str) -> List[SceneObject]:
        return [obj for obj in self.objects if obj.category == category]


@dataclass
class PBRMaterial:
    albedo_map: Optional[np.ndarray] = None
    metallic_map: Optional[np.ndarray] = None
    roughness_map: Optional[np.ndarray] = None
    normal_map: Optional[np.ndarray] = None
    base_color: Tuple[float, float, float] = (0.8, 0.8, 0.8)
    metallic: float = 0.0
    roughness: float = 0.5


@dataclass
class GeneratedScene:
    scene_graph: SceneGraph
    room_mesh_path: str
    object_meshes: Dict[str, str]
    materials: Dict[str, PBRMaterial]
    gaussian_splat_path: Optional[str] = None
    export_paths: Dict[str, str] = field(default_factory=dict)
    generation_time_seconds: float = 0.0
    metadata: Dict = field(default_factory=dict)