6
6
#include " F3DOptionsParser.h"
7
7
#include " F3DSystemTools.h"
8
8
9
+ #define DMON_IMPL
10
+ #ifdef WIN32
11
+ #pragma warning(push)
12
+ #pragma warning(disable : 4505)
13
+ #include " dmon.h"
14
+ // dmon includes Windows.h which defines 'ERROR' and conflicts with log.h
15
+ #undef ERROR
16
+ #pragma warning(pop)
17
+ #else
18
+ #include " dmon.h"
19
+ #endif
20
+
9
21
#include " engine.h"
10
22
#include " interactor.h"
11
23
#include " log.h"
12
24
#include " options.h"
13
25
#include " window.h"
14
26
15
27
#include < algorithm>
28
+ #include < atomic>
16
29
#include < cassert>
17
30
#include < filesystem>
18
31
#include < iostream>
32
+ #include < mutex>
19
33
#include < set>
20
34
21
35
namespace fs = std::filesystem;
@@ -133,15 +147,35 @@ class F3DStarter::F3DInternals
133
147
}
134
148
}
135
149
150
+ static void dmonFolderChanged (
151
+ dmon_watch_id, dmon_action, const char *, const char * filename, const char *, void * userData)
152
+ {
153
+ F3DStarter* self = reinterpret_cast <F3DStarter*>(userData);
154
+ const std::lock_guard<std::mutex> lock (self->Internals ->FilesListMutex );
155
+ fs::path filePath = self->Internals ->FilesList [self->Internals ->CurrentFileIndex ];
156
+ if (filePath.filename ().string () == std::string (filename))
157
+ {
158
+ self->Internals ->ReloadFileRequested = true ;
159
+ }
160
+ }
161
+
136
162
F3DOptionsParser Parser;
137
163
F3DAppOptions AppOptions;
138
164
f3d::options DynamicOptions;
139
165
f3d::options FileOptions;
140
166
std::unique_ptr<f3d::engine> Engine;
141
167
std::vector<fs::path> FilesList;
142
- int CurrentFileIndex = - 1 ;
168
+ dmon_watch_id FolderWatchId ;
143
169
bool LoadedFile = false ;
144
170
bool UpdateWithCommandLineParsing = true ;
171
+
172
+ // dmon used atomic and mutex
173
+ std::atomic<int > CurrentFileIndex = -1 ;
174
+ std::mutex FilesListMutex;
175
+
176
+ // Event loop atomics
177
+ std::atomic<bool > RenderRequested = false ;
178
+ std::atomic<bool > ReloadFileRequested = false ;
145
179
};
146
180
147
181
// ----------------------------------------------------------------------------
@@ -151,10 +185,17 @@ F3DStarter::F3DStarter()
151
185
// Set option outside of command line and config file
152
186
this ->Internals ->DynamicOptions .set (
153
187
" ui.dropzone-info" , " Drop a file or HDRI to load it\n Press H to show cheatsheet" );
188
+
189
+ // Initialize dmon
190
+ dmon_init ();
154
191
}
155
192
156
193
// ----------------------------------------------------------------------------
157
- F3DStarter::~F3DStarter () = default ;
194
+ F3DStarter::~F3DStarter ()
195
+ {
196
+ // deinit dmon
197
+ dmon_deinit ();
198
+ }
158
199
159
200
// ----------------------------------------------------------------------------
160
201
int F3DStarter::Start (int argc, char ** argv)
@@ -219,41 +260,17 @@ int F3DStarter::Start(int argc, char** argv)
219
260
interactor.setKeyPressCallBack (
220
261
[this ](int , const std::string& keySym) -> bool
221
262
{
222
- const auto loadFile = [this ](int index , bool restoreCamera = false ) -> bool
223
- {
224
- this ->Internals ->Engine ->getInteractor ().stopAnimation ();
225
-
226
- f3d::log::debug (" ========== Loading 3D file ==========" );
227
-
228
- if (restoreCamera)
229
- {
230
- f3d::camera& cam = this ->Internals ->Engine ->getWindow ().getCamera ();
231
- const auto camState = cam.getState ();
232
- this ->LoadFile (index , true );
233
- cam.setState (camState);
234
- }
235
- else
236
- {
237
- this ->LoadFile (index , true );
238
- }
239
-
240
- f3d::log::debug (" ========== Rendering ==========" );
241
-
242
- this ->Render ();
243
- return true ;
244
- };
245
-
246
263
if (keySym == " Left" )
247
264
{
248
- return loadFile (-1 );
265
+ return this -> LoadRelativeFile (-1 );
249
266
}
250
267
if (keySym == " Right" )
251
268
{
252
- return loadFile (+1 );
269
+ return this -> LoadRelativeFile (+1 );
253
270
}
254
271
if (keySym == " Up" )
255
272
{
256
- return loadFile (0 , true );
273
+ return this -> LoadRelativeFile (0 , true );
257
274
}
258
275
if (keySym == " Down" )
259
276
{
@@ -264,7 +281,7 @@ int F3DStarter::Start(int argc, char** argv)
264
281
this ->Internals ->FilesList [static_cast <size_t >(this ->Internals ->CurrentFileIndex )]
265
282
.parent_path (),
266
283
true );
267
- return loadFile (0 );
284
+ return this -> LoadRelativeFile (0 );
268
285
}
269
286
return true ;
270
287
}
@@ -299,7 +316,7 @@ int F3DStarter::Start(int argc, char** argv)
299
316
{
300
317
this ->LoadFile (index );
301
318
}
302
- this ->Render ();
319
+ this ->RequestRender ();
303
320
return true ;
304
321
});
305
322
window
@@ -337,7 +354,6 @@ int F3DStarter::Start(int argc, char** argv)
337
354
338
355
if (!this ->Internals ->AppOptions .NoRender )
339
356
{
340
- f3d::log::debug (" ========== Rendering ==========" );
341
357
f3d::window& window = this ->Internals ->Engine ->getWindow ();
342
358
f3d::interactor& interactor = this ->Internals ->Engine ->getInteractor ();
343
359
@@ -460,7 +476,9 @@ int F3DStarter::Start(int argc, char** argv)
460
476
f3d::log::error (" This is a headless build of F3D, interactive rendering is not supported" );
461
477
return EXIT_FAILURE;
462
478
#else
463
- this ->Render ();
479
+ // Create the event loop repeating timer
480
+ interactor.createTimerCallBack (30 , [this ]() { this ->EventLoop (); });
481
+ this ->RequestRender ();
464
482
interactor.start ();
465
483
#endif
466
484
}
@@ -472,6 +490,7 @@ int F3DStarter::Start(int argc, char** argv)
472
490
// ----------------------------------------------------------------------------
473
491
void F3DStarter::LoadFile (int index, bool relativeIndex)
474
492
{
493
+ f3d::log::debug (" ========== Loading 3D file ==========" );
475
494
// When loading a file, store any changed options
476
495
// into the dynamic options and use these dynamic option as the default
477
496
// for loading the file while still applying file specific options on top of it
@@ -621,7 +640,20 @@ void F3DStarter::LoadFile(int index, bool relativeIndex)
621
640
}
622
641
}
623
642
624
- if (!this ->Internals ->LoadedFile )
643
+ if (this ->Internals ->LoadedFile )
644
+ {
645
+ if (this ->Internals ->AppOptions .Watch )
646
+ {
647
+ // Always unwatch and watch current folder, even on reload
648
+ if (this ->Internals ->FolderWatchId .id > 0 )
649
+ {
650
+ dmon_unwatch (this ->Internals ->FolderWatchId );
651
+ }
652
+ this ->Internals ->FolderWatchId = dmon_watch (
653
+ filePath.parent_path ().string ().c_str (), &F3DInternals::dmonFolderChanged, 0 , this );
654
+ }
655
+ }
656
+ else
625
657
{
626
658
// No file loaded, remove any previously loaded file
627
659
loader.loadGeometry (" " , true );
@@ -631,9 +663,17 @@ void F3DStarter::LoadFile(int index, bool relativeIndex)
631
663
this ->Internals ->Engine ->getOptions ().set (" ui.filename-info" , filenameInfo);
632
664
}
633
665
666
+ // ----------------------------------------------------------------------------
667
+ void F3DStarter::RequestRender ()
668
+ {
669
+ // Render will be called by the next event loop
670
+ this ->Internals ->RenderRequested = true ;
671
+ }
672
+
634
673
// ----------------------------------------------------------------------------
635
674
void F3DStarter::Render ()
636
675
{
676
+ f3d::log::debug (" ========== Rendering ==========" );
637
677
this ->Internals ->Engine ->getWindow ().render ();
638
678
f3d::log::debug (" Render done" );
639
679
}
@@ -671,6 +711,8 @@ int F3DStarter::AddFile(const fs::path& path, bool quiet)
671
711
672
712
if (it == this ->Internals ->FilesList .end ())
673
713
{
714
+ // In the main thread, we only need to guard writing
715
+ const std::lock_guard<std::mutex> lock (this ->Internals ->FilesListMutex );
674
716
this ->Internals ->FilesList .push_back (tmpPath);
675
717
return static_cast <int >(this ->Internals ->FilesList .size ()) - 1 ;
676
718
}
@@ -684,3 +726,40 @@ int F3DStarter::AddFile(const fs::path& path, bool quiet)
684
726
}
685
727
}
686
728
}
729
+
730
+ // ----------------------------------------------------------------------------
731
+ bool F3DStarter::LoadRelativeFile (int index, bool restoreCamera)
732
+ {
733
+ this ->Internals ->Engine ->getInteractor ().stopAnimation ();
734
+
735
+ if (restoreCamera)
736
+ {
737
+ f3d::camera& cam = this ->Internals ->Engine ->getWindow ().getCamera ();
738
+ const auto camState = cam.getState ();
739
+ this ->LoadFile (index , true );
740
+ cam.setState (camState);
741
+ }
742
+ else
743
+ {
744
+ this ->LoadFile (index , true );
745
+ }
746
+
747
+ this ->RequestRender ();
748
+
749
+ return true ;
750
+ }
751
+
752
+ // ----------------------------------------------------------------------------
753
+ void F3DStarter::EventLoop ()
754
+ {
755
+ if (this ->Internals ->ReloadFileRequested )
756
+ {
757
+ this ->LoadRelativeFile (0 , true );
758
+ this ->Internals ->ReloadFileRequested = false ;
759
+ }
760
+ if (this ->Internals ->RenderRequested )
761
+ {
762
+ this ->Render ();
763
+ this ->Internals ->RenderRequested = false ;
764
+ }
765
+ }
0 commit comments