mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	 25d58faaad
			
		
	
	
		25d58faaad
		
	
	
	
	
		
			
			This implementation works with the 3DConnexion driver (sic!) if PrusaSlicer.xml is stored into c:\Program Files\3Dconnexion\3DxWare\3DxWinCore64\Cfg\ The implementation is inspired with Blender, see code inside WITH_INPUT_NDOF blocks.
		
			
				
	
	
		
			236 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifndef slic3r_Mouse3DController_hpp_
 | |
| #define slic3r_Mouse3DController_hpp_
 | |
| 
 | |
| // Enabled debug output to console and extended imgui dialog
 | |
| #define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 0
 | |
| 
 | |
| #include "libslic3r/Point.hpp"
 | |
| 
 | |
| #include "hidapi.h"
 | |
| 
 | |
| #include <queue>
 | |
| #include <thread>
 | |
| #include <vector>
 | |
| #include <chrono>
 | |
| #include <condition_variable>
 | |
| 
 | |
| #include <tbb/mutex.h>
 | |
| 
 | |
| namespace Slic3r {
 | |
| 
 | |
| class AppConfig;
 | |
| 
 | |
| namespace GUI {
 | |
| 
 | |
| struct Camera;
 | |
| class GLCanvas3D;
 | |
| 
 | |
| class Mouse3DController
 | |
| {
 | |
| 	// Parameters, which are configured by the ImGUI dialog when pressing Ctrl+M.
 | |
| 	// The UI thread modifies a copy of the parameters and indicates to the background thread that there was a change
 | |
| 	// to copy the parameters.
 | |
| 	struct Params
 | |
| 	{
 | |
| 		static constexpr double DefaultTranslationScale = 2.5;
 | |
| 		static constexpr double MaxTranslationDeadzone = 0.2;
 | |
| 		static constexpr double DefaultTranslationDeadzone = 0.5 * MaxTranslationDeadzone;
 | |
| 		static constexpr float  DefaultRotationScale = 1.0f;
 | |
| 		static constexpr float  MaxRotationDeadzone = 0.2f;
 | |
| 		static constexpr float  DefaultRotationDeadzone = 0.5f * MaxRotationDeadzone;
 | |
| 		static constexpr double DefaultZoomScale = 0.1;
 | |
| 
 | |
|         template <typename Number>
 | |
|         struct CustomParameters
 | |
|         {
 | |
|             Number scale;
 | |
|             Number deadzone;
 | |
|         };
 | |
| 
 | |
|         CustomParameters<double> translation { DefaultTranslationScale, DefaultTranslationDeadzone };
 | |
|         CustomParameters<float>  rotation { DefaultRotationScale, DefaultRotationDeadzone };
 | |
|         CustomParameters<double> zoom { DefaultZoomScale, 0.0 };
 | |
|         // Do not process button presses from 3DConnexion device, let the user map the 3DConnexion keys in 3DConnexion driver.
 | |
|         bool 					 buttons_enabled { false };
 | |
|         // The default value of 15 for max_size seems to work fine on all platforms
 | |
|         // The effects of changing this value can be tested by setting ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT to 1
 | |
|         // and playing with the imgui dialog which shows by pressing CTRL+M
 | |
|         size_t 					 input_queue_max_size { 15 };
 | |
|         // Whether to swap Y/Z axes or not.
 | |
|         bool 					 swap_yz{ false };
 | |
|     };
 | |
| 
 | |
| 	// Queue of the 3DConnexion input events (translations, rotations, button presses).
 | |
|     class State
 | |
|     {
 | |
|     public:
 | |
|         struct QueueItem {
 | |
|             static QueueItem translation(const Vec3d &translation) { QueueItem out; out.vector = translation; out.type_or_buttons = TranslationType; return out; }
 | |
|             static QueueItem rotation(const Vec3d &rotation) { QueueItem out; out.vector = rotation; out.type_or_buttons = RotationType; return out; }
 | |
|             static QueueItem buttons(unsigned int buttons) { QueueItem out; out.type_or_buttons = buttons; return out; }
 | |
| 
 | |
|             bool 			 is_translation() const { return this->type_or_buttons == TranslationType; }
 | |
|             bool 			 is_rotation() const { return this->type_or_buttons == RotationType; }
 | |
|             bool 			 is_buttons() const { return ! this->is_translation() && ! this->is_rotation(); }
 | |
| 
 | |
|             Vec3d        	 vector;
 | |
|             unsigned int 	 type_or_buttons;
 | |
| 
 | |
|             static constexpr unsigned int TranslationType = std::numeric_limits<unsigned int>::max();
 | |
|             static constexpr unsigned int RotationType    = TranslationType - 1;
 | |
|         };
 | |
| 
 | |
|     private:
 | |
|     	// m_input_queue is accessed by the background thread and by the UI thread. Access to m_input_queue
 | |
|     	// is guarded with m_input_queue_mutex.
 | |
|         std::deque<QueueItem> m_input_queue;
 | |
|         mutable tbb::mutex	  m_input_queue_mutex;
 | |
| 
 | |
| #ifdef WIN32
 | |
|         // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected.
 | |
|         // We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere
 | |
|         // by triggering unwanted zoom in/out of the scene
 | |
|         // The following variable is used to count the potential mouse wheel events triggered and is updated by: 
 | |
|         // Mouse3DController::collect_input() through the call to the append_rotation() method
 | |
|         // GLCanvas3D::on_mouse_wheel() through the call to the process_mouse_wheel() method
 | |
|         // GLCanvas3D::on_idle() through the call to the apply() method
 | |
|         unsigned int 		  m_mouse_wheel_counter { 0 };
 | |
| #endif /* WIN32 */
 | |
| 
 | |
|     public:
 | |
|         // Called by the background thread or by by Mouse3DHandlerMac.mm when a new event is received from 3DConnexion device.
 | |
|         void append_translation(const Vec3d& translation, size_t input_queue_max_size);
 | |
|         void append_rotation(const Vec3f& rotation, size_t input_queue_max_size);
 | |
|         void append_button(unsigned int id, size_t input_queue_max_size);
 | |
| 
 | |
| #ifdef WIN32
 | |
|         // Called by GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
 | |
|         // to filter out spurious mouse scroll events produced by the 3DConnexion driver on Windows.
 | |
|         bool process_mouse_wheel();
 | |
| #endif /* WIN32 */
 | |
| 
 | |
| #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | |
|         Vec3d               get_first_vector_of_type(unsigned int type) const {
 | |
|             tbb::mutex::scoped_lock lock(m_input_queue_mutex);
 | |
|             auto it = std::find_if(m_input_queue.begin(), m_input_queue.end(), [type](const QueueItem& item) { return item.type_or_buttons == type; });
 | |
|             return (it == m_input_queue.end()) ? Vec3d::Zero() : it->vector;
 | |
|         }
 | |
|         size_t              input_queue_size_current() const { 
 | |
|         	tbb::mutex::scoped_lock lock(m_input_queue_mutex); 
 | |
|         	return m_input_queue.size(); 
 | |
|         }
 | |
|         std::atomic<size_t> input_queue_max_size_achieved;
 | |
| #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
 | |
| 
 | |
|         // Apply the 3DConnexion events stored in the input queue, reset the input queue.
 | |
|         // Returns true if any change to the camera took place.
 | |
|         bool apply(const Params ¶ms, Camera& camera);
 | |
|     };
 | |
| 
 | |
|     // Background thread works with this copy.
 | |
|     Params 				m_params;
 | |
|     // UI thread will read / write this copy.
 | |
|     Params 				m_params_ui;
 | |
|     bool 	            m_params_ui_changed { false };
 | |
|     mutable tbb::mutex	m_params_ui_mutex;
 | |
| 
 | |
|     // This is a database of parametes of all 3DConnexion devices ever connected.
 | |
|     // This database is loaded from AppConfig on application start and it is stored to AppConfig on application exit.
 | |
|     // We need to do that as the AppConfig is not thread safe and we need read the parameters on device connect / disconnect,
 | |
|     // which is now done by a background thread.
 | |
|     std::map<std::string, Params> m_params_by_device;
 | |
| 
 | |
|     mutable State 		m_state;
 | |
|     std::atomic<bool> 	m_connected { false };
 | |
|     std::string 		m_device_str;
 | |
| 
 | |
| #if ! __APPLE__
 | |
|     // Worker thread for enumerating devices, connecting, reading data from the device and closing the device.
 | |
|     std::thread 		m_thread;
 | |
|     hid_device* 		m_device { nullptr };
 | |
|     // Using m_stop_condition_mutex to synchronize m_stop.
 | |
|     bool				m_stop { false };
 | |
| #ifdef _WIN32
 | |
|     std::atomic<bool>	m_wakeup { false };
 | |
| #endif /* _WIN32 */
 | |
|     // Mutex and condition variable for sleeping during the detection of 3DConnexion devices by polling while allowing
 | |
|     // cancellation before the end of the polling interval.
 | |
| 	std::mutex 			m_stop_condition_mutex;
 | |
|    	std::condition_variable m_stop_condition;
 | |
| #endif
 | |
| 
 | |
|    	// Is the ImGUI dialog shown? Accessed from UI thread only.
 | |
|     mutable bool 		m_show_settings_dialog { false };
 | |
|     // Set to true when ther user closes the dialog by clicking on [X] or [Close] buttons. Accessed from UI thread only.
 | |
|     mutable bool 		m_settings_dialog_closed_by_user { false };
 | |
| 
 | |
| public:
 | |
| 	// Load the device parameter database from appconfig. To be called on application startup.
 | |
| 	void load_config(const AppConfig &appconfig);
 | |
| 	// Store the device parameter database back to appconfig. To be called on application closeup.
 | |
| 	void save_config(AppConfig &appconfig) const;
 | |
| 	// Start the background thread to detect and connect to a HID device (Windows and Linux).
 | |
| 	// Connect to a 3DConnextion driver (OSX).
 | |
| 	// Call load_config() before init().
 | |
|     void init();
 | |
| 	// Stop the background thread (Windows and Linux).
 | |
| 	// Disconnect from a 3DConnextion driver (OSX).
 | |
| 	// Call save_config() after shutdown().
 | |
|     void shutdown();
 | |
| 
 | |
|     bool connected() const { return m_connected; }
 | |
| 
 | |
| #if __APPLE__
 | |
|     // Interfacing with the Objective C code (MouseHandlerMac.mm)
 | |
|     void connected(std::string device_name);
 | |
|     void disconnected();
 | |
|     typedef std::array<double, 6> DataPacketAxis;
 | |
| 	// Unpack a 3DConnexion packet provided by the 3DConnexion driver into m_state. Called by Mouse3DHandlerMac.mm
 | |
|     bool handle_input(const DataPacketAxis& packet);
 | |
| #endif // __APPLE__
 | |
| 
 | |
| #ifdef WIN32
 | |
| 	bool handle_raw_input_win32(const unsigned char *data, const int packet_lenght);
 | |
| 
 | |
|     // Called by Win32 HID enumeration callback.
 | |
|     void device_attached(const std::string &device);
 | |
| 
 | |
|     // On Windows, the 3DConnexion driver sends out mouse wheel rotation events to an active application
 | |
|     // if the application does not register at the driver. This is a workaround to ignore these superfluous
 | |
|     // mouse wheel events.
 | |
|     bool process_mouse_wheel() { return m_state.process_mouse_wheel(); }
 | |
| #endif // WIN32
 | |
| 
 | |
|     // Apply the received 3DConnexion mouse events to the camera. Called from the UI rendering thread.
 | |
|     bool apply(Camera& camera);
 | |
| 
 | |
|     bool is_settings_dialog_shown() const { return m_show_settings_dialog; }
 | |
|     void show_settings_dialog(bool show) { m_show_settings_dialog = show && this->connected(); }
 | |
|     void render_settings_dialog(GLCanvas3D& canvas) const;
 | |
| 
 | |
| #if ! __APPLE__
 | |
| private:
 | |
|     bool connect_device();
 | |
|     void disconnect_device();
 | |
| 
 | |
|     // secondary thread methods
 | |
|     void run();
 | |
|     void collect_input();
 | |
| 
 | |
|     typedef std::array<unsigned char, 13> DataPacketRaw;
 | |
| 
 | |
| 	// Unpack raw 3DConnexion HID packet of a wired 3D mouse into m_state. Called by the worker thread.
 | |
|     static bool handle_input(const DataPacketRaw& packet, const int packet_length, const Params ¶ms, State &state_in_out);
 | |
|     // The following is called by handle_input() from the worker thread.
 | |
|     static bool handle_packet(const DataPacketRaw& packet, const int packet_length, const Params ¶ms, State &state_in_out);
 | |
|     static bool handle_packet_translation(const DataPacketRaw& packet, const Params ¶ms, State &state_in_out);
 | |
|     static bool handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte, const Params ¶ms, State &state_in_out);
 | |
|     static bool handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size, const Params ¶ms, State &state_in_out);
 | |
| #endif /* __APPLE__ */
 | |
| };
 | |
| 
 | |
| } // namespace GUI
 | |
| } // namespace Slic3r
 | |
| 
 | |
| 
 | |
| #endif // slic3r_Mouse3DController_hpp_
 |