-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathorbit.lua
More file actions
313 lines (262 loc) · 9.28 KB
/
orbit.lua
File metadata and controls
313 lines (262 loc) · 9.28 KB
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
-----------------------------------------
-- Sun + Two Planets + Moon Orbit (Icosahedra)
-- - Full 3D orbits in X–Z plane
-- - Orbit plane tilts with touch
-----------------------------------------
math.randomseed(millis() % 1000000)
-----------------------------------------
-- Camera + light setup
-----------------------------------------
local FOV = 220
local CAM_DISTANCE = 180
lge.set_3d_camera(FOV, CAM_DISTANCE)
local LIGHT_AMBIENT = 0.25
local LIGHT_DIFFUSE = 0.9
-- Will be updated every frame to follow the sun
local light_dx, light_dy, light_dz = 0.0, 1.0, -1.0
lge.set_3d_light(light_dx, light_dy, light_dz, LIGHT_AMBIENT, LIGHT_DIFFUSE)
local SCREEN_W, SCREEN_H = lge.get_canvas_size()
-----------------------------------------
-- Icosahedron model
-----------------------------------------
local vertices = {0.000000, -0.525731, 0.850651, 0.850651, 0.000000, 0.525731, 0.850651, 0.000000, -0.525731, -0.850651,
0.000000, -0.525731, -0.850651, 0.000000, 0.525731, -0.525731, 0.850651, 0.000000, 0.525731, 0.850651,
0.000000, 0.525731, -0.850651, 0.000000, -0.525731, -0.850651, 0.000000, 0.000000, -0.525731,
-0.850651, 0.000000, 0.525731, -0.850651, 0.000000, 0.525731, 0.850651}
local faces = {2, 3, 7, 2, 8, 3, 4, 5, 6, 5, 4, 9, 7, 6, 12, 6, 7, 11, 10, 11, 3, 11, 10, 4, 8, 9, 10, 9, 8, 1, 12, 1,
2, 1, 12, 5, 7, 3, 11, 2, 7, 12, 4, 6, 11, 6, 5, 12, 3, 8, 10, 8, 2, 1, 4, 10, 9, 5, 9, 1}
local MODEL_ID = lge.create_3d_model(vertices, faces)
-----------------------------------------
-- Colors for sun, planets, moon
-----------------------------------------
local function make_colors_sun()
local tri_colors = {}
local face_count = #faces / 3
for i = 1, face_count do
tri_colors[i] = (i % 2 == 0) and "#ffcc33" or "#ff9933"
end
return tri_colors
end
local function make_colors_planet1()
local tri_colors = {}
local face_count = #faces / 3
for i = 1, face_count do
tri_colors[i] = (i % 2 == 0) and "#00ff80" or "#00ffaa"
end
return tri_colors
end
local function make_colors_planet2()
local tri_colors = {}
local face_count = #faces / 3
for i = 1, face_count do
tri_colors[i] = (i % 2 == 0) and "#ff66ff" or "#ff99ff"
end
return tri_colors
end
local function make_colors_moon()
local tri_colors = {}
local face_count = #faces / 3
for i = 1, face_count do
tri_colors[i] = (i % 2 == 0) and "#dddddd" or "#bbbbbb"
end
return tri_colors
end
local SUN_INSTANCE_ID = lge.create_3d_instance(MODEL_ID, make_colors_sun())
local PLANET1_INSTANCE_ID = lge.create_3d_instance(MODEL_ID, make_colors_planet1())
local PLANET2_INSTANCE_ID = lge.create_3d_instance(MODEL_ID, make_colors_planet2())
local MOON_INSTANCE_ID = lge.create_3d_instance(MODEL_ID, make_colors_moon())
-----------------------------------------
-- Projection helper: world (x,y,z,r3d) -> screen (sx,sy,sr)
-----------------------------------------
local function project_point(x, y, z, radius3d)
local depth = z
if depth < 1 then
depth = 1
end
local factor = FOV / depth
local sx = x * factor + SCREEN_W / 2
local sy = y * factor + SCREEN_H / 2
local sr = radius3d * factor
return sx, sy, sr
end
-----------------------------------------
-- Sun + Planets + Moon state
-----------------------------------------
local SUN_Z = 180
local SUN_RADIUS = 28
local ORBIT_RADIUS_1 = 70
local ORBIT_RADIUS_2 = 110
local PLANET1_RADIUS = 12
local PLANET2_RADIUS = 9
local ORBIT_SPEED_1 = 0.02 -- radians per frame
local ORBIT_SPEED_2 = -0.013 -- opposite direction, slower
local MOON_ORBIT_RADIUS = 20
local MOON_RADIUS = 5
local MOON_ORBIT_SPEED = 0.08
local sun = {
x = 0.0,
y = 0.0,
z = SUN_Z,
r = SUN_RADIUS,
angle_x = 0,
angle_y = 0,
angle_z = 0,
d_angle_x = 0.01,
d_angle_y = 0.013,
d_angle_z = 0.008,
instance_id = SUN_INSTANCE_ID
}
local planet1 = {
orbit_angle = 0.0,
x = 0.0,
y = 0.0,
z = SUN_Z + ORBIT_RADIUS_1,
r = PLANET1_RADIUS,
angle_x = 0,
angle_y = 0,
angle_z = 0,
d_angle_x = 0.03,
d_angle_y = 0.017,
d_angle_z = 0.025,
instance_id = PLANET1_INSTANCE_ID
}
local planet2 = {
orbit_angle = math.pi * 0.4, -- phase offset
x = 0.0,
y = 0.0,
z = SUN_Z + ORBIT_RADIUS_2,
r = PLANET2_RADIUS,
angle_x = 0,
angle_y = 0,
angle_z = 0,
d_angle_x = 0.02,
d_angle_y = 0.019,
d_angle_z = 0.03,
instance_id = PLANET2_INSTANCE_ID
}
local moon = {
orbit_angle = 0.0,
x = 0.0,
y = 0.0,
z = 0.0,
r = MOON_RADIUS,
angle_x = 0,
angle_y = 0,
angle_z = 0,
d_angle_x = 0.04,
d_angle_y = 0.03,
d_angle_z = 0.05,
instance_id = MOON_INSTANCE_ID
}
-----------------------------------------
-- Orbit plane tilt (controlled by mouse/touch)
-----------------------------------------
local MAX_TILT = math.rad(45) -- max 45° tilt in each axis
local tiltX = 0.0 -- rotation around X (pitch)
local tiltY = 0.0 -- rotation around Y (yaw)
local function update_tilt_from_mouse()
local _, mx, my = lge.get_mouse_position()
if not mx then
return
end
-- Normalize to [-1,1]
local nx = (mx - SCREEN_W / 2) / (SCREEN_W / 2)
local ny = (my - SCREEN_H / 2) / (SCREEN_H / 2)
-- Horizontal: yaw (around Y), Vertical: pitch (around X)
tiltY = nx * MAX_TILT
tiltX = ny * MAX_TILT
end
-- Apply tilt to a local orbit-space point (around origin), then translate by sun position
local function apply_orbit_tilt(local_x, local_y, local_z)
-- Rotate around X (tiltX)
local cosX = math.cos(tiltX)
local sinX = math.sin(tiltX)
local y1 = local_y * cosX - local_z * sinX
local z1 = local_y * sinX + local_z * cosX
local x1 = local_x
-- Rotate around Y (tiltY)
local cosY = math.cos(tiltY)
local sinY = math.sin(tiltY)
local x2 = x1 * cosY + z1 * sinY
local z2 = -x1 * sinY + z1 * cosY
local y2 = y1
-- Translate to sun position
return sun.x + x2, sun.y + y2, sun.z + z2
end
-----------------------------------------
-- Main loop
-----------------------------------------
local function main_loop()
local clear_canvas = lge.clear_canvas
local draw_text = lge.draw_text
local fps_func = lge.fps
local present = lge.present
local delay = lge.delay
while true do
clear_canvas("#000000")
---------------------------------
-- 0. Update tilt from touch + light from sun
---------------------------------
update_tilt_from_mouse()
---------------------------------
-- 1. Update sun rotation
---------------------------------
sun.angle_x = sun.angle_x + sun.d_angle_x
sun.angle_y = sun.angle_y + sun.d_angle_y
sun.angle_z = sun.angle_z + sun.d_angle_z
---------------------------------
-- 2. Update planet orbits + rotations
---------------------------------
-- Planet 1
planet1.orbit_angle = planet1.orbit_angle + ORBIT_SPEED_1
local px1 = math.cos(planet1.orbit_angle) * ORBIT_RADIUS_1
local py1 = 0
local pz1 = math.sin(planet1.orbit_angle) * ORBIT_RADIUS_1
planet1.x, planet1.y, planet1.z = apply_orbit_tilt(px1, py1, pz1)
planet1.angle_x = planet1.angle_x + planet1.d_angle_x
planet1.angle_y = planet1.angle_y + planet1.d_angle_y
planet1.angle_z = planet1.angle_z + planet1.d_angle_z
-- Planet 2
planet2.orbit_angle = planet2.orbit_angle + ORBIT_SPEED_2
local px2 = math.cos(planet2.orbit_angle) * ORBIT_RADIUS_2
local py2 = 0
local pz2 = math.sin(planet2.orbit_angle) * ORBIT_RADIUS_2
planet2.x, planet2.y, planet2.z = apply_orbit_tilt(px2, py2, pz2)
planet2.angle_x = planet2.angle_x + planet2.d_angle_x
planet2.angle_y = planet2.angle_y + planet2.d_angle_y
planet2.angle_z = planet2.angle_z + planet2.d_angle_z
-- Moon orbiting Planet 1 in the same tilted plane
moon.orbit_angle = moon.orbit_angle + MOON_ORBIT_SPEED
local mpx = px1 + math.cos(moon.orbit_angle) * MOON_ORBIT_RADIUS
local mpy = 0
local mpz = pz1 + math.sin(moon.orbit_angle) * MOON_ORBIT_RADIUS
moon.x, moon.y, moon.z = apply_orbit_tilt(mpx, mpy, mpz)
moon.angle_x = moon.angle_x + moon.d_angle_x
moon.angle_y = moon.angle_y + moon.d_angle_y
moon.angle_z = moon.angle_z + moon.d_angle_z
---------------------------------
-- 3. Depth sort (far -> near)
---------------------------------
local bodies = {sun, planet1, planet2, moon}
table.sort(bodies, function(a, b)
return a.z > b.z
end)
---------------------------------
-- 4. Draw all bodies
---------------------------------
for i = 1, #bodies do
local o = bodies[i]
lge.draw_3d_instance(o.instance_id, o.x, o.y, o.z, o.r, o.angle_x, o.angle_y, o.angle_z)
end
---------------------------------
-- 5. UI
---------------------------------
local fps = fps_func()
fps = math.floor(fps * 100 + 0.5) / 100
draw_text(5, 5, "Orbit demo FPS: " .. fps, "#FFFFFF")
draw_text(5, 20, "Touch/drag = tilt orbit plane", "#AAAAAA")
present()
delay(1)
end
end
main_loop()