File size: 4,200 Bytes
84e8204 | 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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | extends Node3D
##
## StudioMain - root bootstrap for MonkeyStudio.
##
## Builds the runtime scene tree in code so the whole app is one auditable
## script rather than a fragile binary .tscn. Scene tree created:
##
## StudioMain (Node3D)
## βββ WorldEnvironment (mobile renderer settings)
## βββ Sun (DirectionalLight3D)
## βββ Camera3D (touch_camera.gd)
## βββ Geometry (Node3D) <- user-created meshes live here
## βββ Rig (Node3D) <- Skeleton3D lives here
## βββ Features (Node) <- lazily-instantiated feature modules
## βββ UI (CanvasLayer)
## βββ (built by studio_ui.gd)
##
const TouchCameraScript := preload("res://scripts/studio/touch_camera.gd")
const StudioUIScript := preload("res://scripts/studio/studio_ui.gd")
var camera: Camera3D
var geometry: Node3D
var rig: Node3D
var feature_host: Node
var ui: CanvasLayer
func _ready() -> void:
_build_environment()
_build_lighting()
_build_camera()
geometry = _make_node("Geometry")
rig = _make_node("Rig")
feature_host = Node.new()
feature_host.name = "Features"
add_child(feature_host)
FeatureRegistry.host = feature_host
_build_ui()
_spawn_starter_scene()
_connect_signals()
print("[StudioMain] ready. %d features available." % FeatureRegistry.feature_count())
func _build_environment() -> void:
var env := Environment.new()
env.background_mode = Environment.BG_COLOR
env.background_color = Color(0.08, 0.09, 0.11)
env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
env.ambient_light_color = Color(0.35, 0.38, 0.45)
env.ambient_light_energy = 0.6
# Mobile-friendly tonemap + no heavy SSAO/GI by default.
env.tonemap_mode = Environment.TONE_MAPPER_FILMIC
env.ssao_enabled = false
env.glow_enabled = false
var we := WorldEnvironment.new()
we.name = "WorldEnvironment"
we.environment = env
add_child(we)
func _build_lighting() -> void:
var sun := DirectionalLight3D.new()
sun.name = "Sun"
sun.rotation_degrees = Vector3(-50, -40, 0)
sun.light_energy = 1.1
sun.shadow_enabled = true
# Lower shadow resolution on mobile to save bandwidth.
sun.directional_shadow_max_distance = 40.0
add_child(sun)
func _build_camera() -> void:
camera = TouchCameraScript.new()
camera.name = "Camera3D"
camera.fov = 60.0
camera.position = Vector3(4, 4, 8)
add_child(camera)
camera.focus = Vector3.ZERO
func _make_node(n: String) -> Node3D:
var node := Node3D.new()
node.name = n
add_child(node)
return node
func _build_ui() -> void:
ui = StudioUIScript.new()
ui.name = "UI"
add_child(ui)
## Spawn a cube so the app is immediately interactive on first launch.
func _spawn_starter_scene() -> void:
FeatureRegistry.activate("mesh.cube")
var gen: Node = feature_host.get_node_or_null("Feature_mesh.cube")
if gen != null and gen.has_method("spawn"):
var mesh: MeshInstance3D = gen.call("spawn", "cube", {"size": 1.5})
_ensure_collision(mesh)
StudioBus.object_selected.emit(mesh)
if camera != null:
camera.frame_aabb(mesh.mesh.get_aabb())
## Give a mesh a static trimesh collider so sculpting raycasts hit it.
func _ensure_collision(mi: MeshInstance3D) -> void:
if mi == null or mi.mesh == null:
return
var body := StaticBody3D.new()
body.name = mi.name + "_Body"
var shape := CollisionShape3D.new()
var trimesh := mi.mesh.create_trimesh_shape()
if trimesh != null:
shape.shape = trimesh
else:
# Fallback to a box for exotic primitives without trimesh support.
var box := BoxShape3D.new()
box.size = mi.mesh.get_aabb().size
shape.shape = box
body.add_child(shape)
mi.add_child(body)
func _connect_signals() -> void:
StudioBus.fps_throttled.connect(_on_fps_throttled)
MemoryManager.memory_warning.connect(_on_memory_warning)
func _on_fps_throttled(fps: int) -> void:
# Shed load: disable shadows first, then reduce the sim substep budget.
var sun := get_node_or_null("Sun") as DirectionalLight3D
if sun != null and sun.shadow_enabled:
sun.shadow_enabled = false
print("[Perf] shadows off at %d fps" % fps)
func _on_memory_warning(used_mb: float, budget_mb: float) -> void:
print("[Mem] warning: %.0f/%.0f MB" % [used_mb, budget_mb])
|