-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathos_anim.cpp
More file actions
239 lines (195 loc) · 7.75 KB
/
os_anim.cpp
File metadata and controls
239 lines (195 loc) · 7.75 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
//
// openSAM: open source SAM emulator for X Plane
//
// Copyright (C) 2024, 2025 Holger Teutsch
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
// USA
//
#include <cstddef>
#include <cstdio>
#include <cstring>
#include "openSAM.h"
#include "os_anim.h"
static constexpr float kSam2ObjMax = 2.5; // m, max delta between coords in sam.xml and object
static constexpr float kSam2ObjHdgMax = 5; // °, likewise for heading
static Scenery* cur_sc; // the current scenery determined by dref access
static float cur_sc_ts = -100.0f; // timestamp of selection of cur_sc
static Scenery* menu_sc; // the scenery the menu is built of
//
// Accessor for the "sam/..." custom animation datarefs
//
// This function is called from draw loops, efficient coding required.
//
// ref is uint64_t is the index into the global dref table
//
static float AnimAcc(void* ref) {
float obj_x = XPLMGetDataf(draw_object_x_dr);
float obj_z = XPLMGetDataf(draw_object_z_dr);
float obj_psi = XPLMGetDataf(draw_object_psi_dr);
if (obj_x == 0.0f && obj_z == 0.0f && obj_psi == 0.0f)
return 0.0f; // likely uninitialized, datareftool poll etc.
stat_anim_acc_called++;
// check for shift of reference frame
float lat_r = XPLMGetDataf(lat_ref_dr);
float lon_r = XPLMGetDataf(lon_ref_dr);
if (lat_r != lat_ref || lon_r != lon_ref) {
lat_ref = lat_r;
lon_ref = lon_r;
ref_gen++;
LogMsg("reference frame shift");
}
int drf_idx = (uint64_t)ref;
for (auto sc : sceneries) {
for (auto anim : sc->sam_anims) {
if (drf_idx != anim->drf_idx)
continue;
SamObj* obj = sc->sam_objs[anim->obj_idx];
if (fabsf(RA(obj->heading - obj_psi)) > kSam2ObjHdgMax)
continue;
if (obj->xml_ref_gen < ref_gen) {
double x, y, z;
XPLMWorldToLocal(obj->latitude, obj->longitude, obj->elevation, &x, &y, &z);
obj->xml_x = x;
obj->xml_y = y;
obj->xml_z = z;
obj->xml_ref_gen = ref_gen;
}
if (fabs(obj_x - obj->xml_x) > kSam2ObjMax || fabs(obj_z - obj->xml_z) > kSam2ObjMax) {
stat_near_skip++;
continue;
}
SamDrf* drf = sam_drfs[drf_idx];
// LogMsg("acc %s called, %s %s", drf->name, anim->label, anim->title);
if (now > cur_sc_ts + 20.0f) { // avoid high freq flicker
cur_sc = sc;
cur_sc_ts = now;
}
if (anim->state == ANIM_OFF_2_ON || anim->state == ANIM_ON_2_OFF) {
now = XPLMGetDataf(total_running_time_sec_dr);
float dt = now - anim->start_ts;
if (anim->state == ANIM_ON_2_OFF)
dt = drf->t[drf->n_tv - 1] - dt; // downwards
if (dt < 0.0f)
anim->state = ANIM_OFF;
else if (dt > drf->t[drf->n_tv - 1])
anim->state = ANIM_ON;
else {
for (int j = 1; j < drf->n_tv; j++)
if (dt < drf->t[j])
return drf->v[j - 1] + drf->s[j] * (dt - drf->t[j - 1]);
}
}
if (anim->state == ANIM_OFF)
return drf->v[0];
if (anim->state == ANIM_ON)
return drf->v[drf->n_tv - 1];
}
}
return 0.0;
}
//
// Accessor for the "sam/..." autoplay datarefs
//
// This function is called from draw loops, efficient coding required.
//
// ref is a ptr to the dref
//
static float AutoDrfAcc(void* ref) {
stat_auto_drf_called++;
const SamDrf* drf = (const SamDrf*)ref;
float t = XPLMGetDataf(total_running_time_sec_dr);
if (drf->randomize_phase) {
// that should give a deterministic per object random value
float obj_x = XPLMGetDataf(draw_object_x_dr);
float obj_y = XPLMGetDataf(draw_object_y_dr);
t += fabsf(obj_x * 0.5f + obj_y);
}
float dt = fmodf(t, drf->t[drf->n_tv - 1]);
for (int j = 1; j < drf->n_tv; j++)
if (dt < drf->t[j])
return drf->v[j - 1] + drf->s[j] * (dt - drf->t[j - 1]);
return 0.0f; // should never be reached
}
void AnimMenuCb([[maybe_unused]] void* menu_ref, void* item_ref) {
if (NULL == menu_sc) // just in case
return;
unsigned int idx = (uint64_t)item_ref;
SamAnim* anim = menu_sc->sam_anims[idx];
LogMsg("AnimMenuCb: label: %s, menu_item: %d", anim->label.c_str(), anim->menu_item);
now = XPLMGetDataf(total_running_time_sec_dr);
bool reverse;
if (anim->state == ANIM_OFF || anim->state == ANIM_ON_2_OFF) {
XPLMCheckMenuItem(anim_menu, anim->menu_item, xplm_Menu_Checked);
reverse = (anim->state == ANIM_ON_2_OFF);
anim->state = ANIM_OFF_2_ON;
} else {
XPLMCheckMenuItem(anim_menu, anim->menu_item, xplm_Menu_Unchecked);
reverse = (anim->state == ANIM_OFF_2_ON);
anim->state = ANIM_ON_2_OFF;
}
if (reverse) {
SamDrf* drf = sam_drfs[anim->drf_idx];
float t_rel = now - anim->start_ts;
float dt = drf->t[drf->n_tv - 1] - t_rel;
anim->start_ts = now - dt;
} else
anim->start_ts = now;
}
static void BuildMenu(Scenery* sc) {
LogMsg("build menu for scenery %s", sc->name.c_str());
XPLMClearAllMenuItems(anim_menu);
for (unsigned i = 0; i < sc->sam_anims.size(); i++) {
SamAnim* anim = sc->sam_anims[i];
XPLMMenuCheck chk =
(anim->state == ANIM_OFF || anim->state == ANIM_ON_2_OFF) ? xplm_Menu_Unchecked : xplm_Menu_Checked;
std::string menu_line;
menu_line = anim->label + " " + anim->title;
LogMsg("%s", menu_line.c_str());
anim->menu_item = XPLMAppendMenuItem(anim_menu, menu_line.c_str(), (void*)(uint64_t)i, 0);
XPLMCheckMenuItem(anim_menu, anim->menu_item, chk);
}
}
// not much state here, just regularly check for a current scenery
// and maintain the menu
float AnimStateMachine(void) {
// check whether we have recently seen a scenery
if (cur_sc && now > cur_sc_ts + 180.0f) {
LogMsg("have not seen a custom animated scenery recently");
cur_sc = NULL;
}
if (cur_sc != menu_sc) {
menu_sc = cur_sc;
if (menu_sc)
BuildMenu(menu_sc);
else {
LogMsg("clear menu");
XPLMClearAllMenuItems(anim_menu);
}
}
return 5.0f;
}
bool AnimInit() {
for (unsigned i = 0; i < sam_drfs.size(); i++) {
const SamDrf* drf = sam_drfs[i];
if (drf->autoplay)
XPLMRegisterDataAccessor(drf->name.c_str(), xplmType_Float, 0, NULL, NULL, AutoDrfAcc, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, (void*)drf, NULL);
else
XPLMRegisterDataAccessor(drf->name.c_str(), xplmType_Float, 0, NULL, NULL, AnimAcc, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, (void*)(uint64_t)i, NULL);
}
return true;
}