HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MC_MocapStreamImpl.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: MC_MocapStreamImpl.h (C++)
7  *
8  */
9 #ifndef __MC_MOCAPSTREAMIMPL_H_INCLUDED__
10 #define __MC_MOCAPSTREAMIMPL_H_INCLUDED__
11 
12 #include "MC_API.h"
13 #include "MC_MocapStreamLog.h"
14 
15 #include <GU/GU_Detail.h>
16 #include <GU/GU_MotionClip.h>
17 #include <UT/UT_Array.h>
18 #include <UT/UT_ConcurrentQueue.h>
19 #include <UT/UT_Function.h>
20 #include <UT/UT_StopWatch.h>
21 #include <UT/UT_WorkBuffer.h>
22 
23 #include <hboost/system/system_error.hpp>
24 
25 #include <atomic>
26 #include <vector>
27 
28 const UT_StringHolder theActorAttribName = "actorname";
29 
30 extern "C" {
31  /// Function called in DSO to install new functions
32  /// All files found in $HOUDINI_MOCAPSTREAM_DSO_PATH are automatically
33  /// scanned for this function.
34  SYS_VISIBILITY_EXPORT extern void newMocapStreamDevice(void *);
35 }
36 
37 /// A structure for passing the values of the MocapStream SOPs parameters
38 /// and node path to the MocapStream devices.
39 /// MC_MocapStreamImpl::updateJoints(...) receives the current value of
40 /// these parameters as well as the value of these parameters the last
41 /// time the joints were updated for that node.
43 {
48  bool myDoConvertUnits = false;
49  bool myDoConvertUpAxis = false;
50 };
51 
52 /// A structure used to store the MotionClip geometry that is created
53 /// when recording a motion capture stream.
54 /// This structure also has its own MC_MocapStreamCookparms which is a
55 /// snapshot of the value of those parms on the MocapStream SOP from when
56 /// the recording started.
58 {
62 
64 
66  bool myHasStartTime = false;
67  fpreal64 myStartTime = 0.0f;
68  uint64 mySkeletonVersion = 0;
69 };
70 
72 {
73 public:
74  // we want to access connect methods in the manager but not
75  // in the implementations
76  friend class MC_MocapStreamManager;
77 
78 
80  {
81  public:
82  virtual UT_UniquePtr<MC_MocapStreamImpl> getInstance() = 0;
83  virtual ~RegisterBase() {};
84  };
85 
86  /// Create a static instance of this to register an implementation
87  ///
88  /// The first time a SOP_MocapStream is initialised it will solidify the
89  /// list of implementations and ignore any more registrations/unregistrations
90  template <typename T>
91  class Register : public RegisterBase
92  {
93  public:
95  {
96  MC_MocapStreamImpl::registerDevice(this);
97  }
98  ~Register() override
99  {
100  MC_MocapStreamImpl::unregisterDevice(this);
101  }
103  {
104  return UTmakeUnique<T>();
105  }
106  };
107 
108 
109  /// Keeps track of the state of a mocap stream server and is used as a
110  /// method of communcating between a server function and the base class.
111  ///
112  /// For thread safety, the server function should only call setConnected(),
113  /// setPortOpened(), setError(), gotPacket(), and killed(). While the server
114  /// function is running, it should be the only thread calling these methods.
115  struct ServerState {
116  UT_ConcurrentQueue<std::pair<UT_StringHolder, fpreal64>> myPackets;
117  UT_ConcurrentQueue<UT_Function<void(void*)>> myTasks;
118 
119  ServerState() : myConnected(false), myError(false),
120  myKilled(false) { }
121 
122  /// Whether a connection is currently established.
123  bool connected() { return myConnected; }
124  void setConnected(bool connected) { myConnected = connected; }
125 
126  /// Whether the connection has been killed.
127  bool killed() { return myKilled; }
128  void setKilled(bool killed) { myKilled = killed; }
129 
130  /// Whether the port for receiving data has been opened.
131  /// setPortOpened(True) must be called for the data to begin parsing.
132  bool portOpened() { return myPortOpened; }
133  void setPortOpened(bool port_opened) { myPortOpened = port_opened; }
134 
135  /// Sends an error message from the server function to be added to the
136  /// log.
137  void setError(const char* msg)
138  {
139  if (!myError)
140  {
141  myErrorMsg = msg;
142  myError = true;
143  }
144  }
145 
146  /// Reads the error message if one has been added.
147  bool getError(UT_StringHolder& res) const
148  {
149  if (myError)
150  {
151  res = myErrorMsg;
152  return true;
153  }
154  return false;
155  }
156 
157  /// Resets the error state.
158  void resetError()
159  {
160  myError = false;
161  }
162 
163  /// Resets the server state to its default values.
164  void reset()
165  {
166  myConnected = false;
167  myKilled = false;
168  myPortOpened = false;
169  myError = false;
170  myTasks.clear();
171  myPackets.clear();
172  myTimer.start();
173  }
174 
175  /// Called in the server function to pass a packet along to be parsed
176  /// once it has been received.
177  void gotPacket(UT_StringHolder&& packet)
178  {
179  if (myPackets.unsafe_size() > 250)
180  {
181  myPackets.clear();
182  }
183  myPackets.push(std::make_pair<UT_StringHolder, fpreal64>(
184  std::move(packet),
185  myTimer.getTime()
186  ));
187  }
188 
189  private:
190  std::atomic<bool> myConnected;
191  std::atomic<bool> myKilled;
192  std::atomic<bool> myPortOpened;
193  std::atomic<bool> myError;
194  UT_StringHolder myErrorMsg;
195  UT_StopWatch myTimer;
196  };
197 
198  /// Keeps track of the options used to connect to a server and defines which
199  /// parameters should be enabled for the device on the MocapStream SOP.
201  {
202  // These are the value for the corresponding parameters on the Mocap
203  // Stream SOP. When a subclass of MC_MocapStreamImpl defines the values
204  // for these variables in its constructor, that defines the default
205  // values for the corresponding parameters for that device.
206  int myPort;
213 
214  // This structure defines which parameters are made available for any
215  // mocap stream device defined by a subclass of MC_MocapStreamImpl.
216  struct Enabled
217  {
218  bool localPort;
219  bool localIP;
220  bool hostIP;
222  bool dataPort;
228  } myEnabled;
229 
230  bool isSameConnection(const ServerOptions& other) const
231  {
232  return myPort == other.myPort
233  && myHostIP == other.myHostIP
234  && myMulticastIP == other.myMulticastIP
235  && myDataPort == other.myDataPort
236  && myCommandPort == other.myCommandPort
237  && myBufferSize == other.myBufferSize;
238  }
239  };
240 
241  typedef void (*MC_MocapServerFunc)(ServerState& state, const ServerOptions& options,
243 
244  /// A function for establishing a basic UDP connection to a server,
245  /// receiving motion capture data, and passing it along to be parsed.
246  static void basicUdpReceiver(ServerState& state, const ServerOptions& options,
248 
249  /// A function for establishing a basic TPC connection, receiving motion
250  /// capture data, and passing it along to be parsed.
251  static void basicTcpClient(ServerState& state, const ServerOptions& options,
253 
254  /// Gets the error message corresponding to an hboost error code.
255  static void getBoostErrorMessage(hboost::system::error_code e,
256  UT_StringHolder &err_msg);
257  /// Gets the error message corresponding to an hboost error code.
258  static void getBoostErrorMessage(hboost::system::system_error& e,
259  UT_StringHolder &err_msg);
260  /// Adds an error message to the log corresponding to an hboost error code.
261  static void handleBoostError(hboost::system::error_code e,
262  ServerState& state);
263  /// Adds an error message to the log corresponding to an hboost error code.
264  static void handleBoostError(hboost::system::system_error& e,
265  ServerState& state);
266 
267 
268  MC_MocapStreamImpl(MC_MocapServerFunc f, const ServerOptions& myServerOptions);
269  virtual ~MC_MocapStreamImpl();
270 
271  virtual UT_UniquePtr<MC_MocapStreamImpl> clone(const ServerOptions& opts) = 0;
272 
273  /// Called for each packet received from the server.
274  /// Return true when a packet which defines a pose is valid.
275  /// parse_incomplete is set to true when a portion of the packet
276  /// remains to be parsed. In this case, packet is set to the appropriate
277  /// value to call this method again.
278  ///
279  /// NOTE: If this returns true and parse_incomplete is set to true, that means
280  /// that the "packet" that was passed in contained parts of more that one packet.
281  /// This can occur when the data is from a constant stream that has been cut up.
282  virtual bool parsePacket(UT_StringHolder &packet,
283  fpreal packet_time,
284  bool &parse_incomplete) = 0;
285 
286  /// Appends the skeleton's geometry to the gdp
287  /// The theActorAttribName attribute should store the
288  /// name of the skeleton, and the name attribute will store the names
289  /// of each joint.
290  virtual void buildSkeleton(GU_Detail*, UT_StringHolder name) = 0;
291 
292  /// Removes the stored data for a skeleton.
293  virtual void removeSkeleton(const UT_StringHolder& name);
294 
295  // Indicates whether a skeleton that has expired should be removed
296  virtual bool removeExpiredSkeletons() const { return true; }
297 
298  /// Each time we sucessfully parse a packet we increment a counter,
299  /// if receivedSkeleton has not been called the last 'expirationCount'
300  /// of successful parses then we will remove the skeleton.
301  /// Return -1 to disable this.
302  virtual int expirationCount();
303 
304  /// Updates the joints' attributes in place based on the results of parsePacket
305  /// you can assume that all desired skeletons have already been built in the gdp,
306  /// so you should keep the same geometry, just update the attributes you need.
307  virtual void updateJoints(GU_Detail *gdp,
308  const MC_MocapStreamCookParms &cookparms,
309  const MC_MocapStreamCookParms &prev_cookparms) = 0;
310 
311  /// Should be called when an actor's data has been parsed.
312  /// The base class will keep track of the actors and and call
313  /// build/removeSkeleton when needed.
314  void receivedSkeleton(const UT_StringHolder& name);
315 
316  /// Call this to let the base class know an actor's name has changed
317  void renamedSkeleton(const UT_StringHolder& old_name, const UT_StringHolder& new_name);
318 
319  /// Call this in parsePacket to specify the time in seconds of the
320  /// received packet. Otherwise it will default to the time it was received
321  /// This is used for timing in the recorded MotionClip.
322  void setPacketTime(fpreal64 time);
323 
324  /// Call this to report an error when parsing.
325  template <typename... Args>
326  void addParseError(const char* fmt, Args&&... args)
327  {
328  MC_MocapStream_Log(UT_ERROR_WARNING, "Parse Error", fmt, std::forward<Args>(args)...);
329  myHitError = true;
330  }
331 
332  /// Call this to report a warning when parsing.
333  template <typename... Args>
334  void addParseWarning(const char* fmt, Args&&... args)
335  {
336  MC_MocapStream_Log(UT_ERROR_WARNING, "Parse Warning", fmt, std::forward<Args>(args)...);
337  }
338 
339  // Call this to trigger a rebuild of the skeleton geometries.
340  void rebuildSkeletons() { mySkeletonVersion++; }
341 
342  /// Used by the main thread to queue a task for the server.
343  /// The server function will access these tasks via state.myTasks
344  /// so it can call them. Ensure your task functions
345  /// accept the same data type your server passes into them.
346  ///
347  /// Your functions should not bind any data that would be unsafe
348  /// to access within the server thread.
349  void queueServerTask(const UT_Function<void(void*)> &task);
350 
351  /// The unique name used to identify a subclass's mocap stream device.
352  virtual const char* name() const = 0;
353 
354  /// The label for a subclass's mocap stream device which appears in the "Device"
355  /// menu on the MocapStream SOP.
356  virtual const char* label() const = 0;
357 
358  /// The main entry point into MocapStreamImpl for creating or updating
359  /// a SOP's skeleton geometry.
360  /// Returns true if it has successfully parsed new data.
361  bool cook(GU_Detail *gdp, uint64 &skeleton_version, fpreal time,
362  const MC_MocapStreamCookParms &cookparms,
363  const MC_MocapStreamCookParms &prev_cookparms,
364  bool &is_killed);
365 
366  /// Removes all actors/skeletons.
367  void clear();
368 
369  /// Starts recording the motion capture data and stores it in a MotionClip.
370  void startRecording(UT_SharedPtr<MC_MocapRecorder> recorder,
371  const MC_MocapStreamCookParms &cookparms);
372 
373  /// Stops recording the motion capture data.
374  void stopRecording(UT_SharedPtr<MC_MocapRecorder> recorder);
375 
376  // Maps each actor name to the number of packets processed
377  // since it was last received.
378  const UT_ArrayStringMap<exint>& getActorMap();
379 
380  /// Used to determine if the stream has run into an error.
381  bool hasHitError();
382 protected:
383 
384  /// Call this from updateJoints to rename any of the attributes for storing
385  /// the parsed blendshapes.
386  UT_StringHolder updateAttribName(GU_Detail *gdp,
387  const UT_StringHolder &base_name,
388  const UT_StringHolder &rename_pattern,
389  const UT_StringHolder &prev_rename_pattern);
390 
391 private:
392 
393  static void registerDevice(RegisterBase*);
394  static void unregisterDevice(RegisterBase*);
395 
396  static void* threadBody(void* data);
397 
398  bool connect();
399  void disconnect();
400  void endServerThread();
401 
402  void updateOutput(GU_Detail *gdp,
403  uint64 &skeleton_ver,
404  const MC_MocapStreamCookParms &cookparms,
405  const MC_MocapStreamCookParms &prev_cookparms,
406  const UT_ArrayStringMap<exint> &old_actors);
407 
408  // maps each actor name to the number of packets processed
409  // since it was last received.
410  UT_ArrayStringMap<exint> myActors;
411  uint64 mySkeletonVersion;
412 
413  bool myHitError;
414 
415  const MC_MocapServerFunc myServerFunc;
416  const ServerOptions myServerOptions;
417  ServerState myServerState;
418  unsigned long long myParseCount;
419 
421  fpreal64 myPacketTime;
422 
423  UT_UniquePtr<UT_Thread> myServerThread;
425 };
426 
427 #endif
GLuint GLsizei const GLchar * label
Definition: glcorearb.h:2545
UT_StringHolder myActorPattern
#define SYS_VISIBILITY_EXPORT
void
Definition: png.h:1083
GLboolean * data
Definition: glcorearb.h:131
GT_API const UT_StringHolder time
SYS_VISIBILITY_EXPORT void newMocapStreamDevice(void *)
UT_ConcurrentQueue< std::pair< UT_StringHolder, fpreal64 > > myPackets
auto system_error(int error_code, format_string< T...> fmt, T &&...args) -> std::system_error
Definition: format.h:2240
void addParseError(const char *fmt, Args &&...args)
Call this to report an error when parsing.
const UT_StringHolder theActorAttribName
#define MC_API
Definition: MC_API.h:10
unsigned long long uint64
Definition: SYS_Types.h:117
bool killed()
Whether the connection has been killed.
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
double fpreal64
Definition: SYS_Types.h:201
MC_MocapStreamCookParms myStreamCookParms
GLfloat f
Definition: glcorearb.h:1926
void gotPacket(UT_StringHolder &&packet)
std::shared_ptr< T > UT_SharedPtr
Wrapper around std::shared_ptr.
Definition: UT_SharedPtr.h:36
virtual bool removeExpiredSkeletons() const
UT_StringHolder myConfigAttrib
UT_UniquePtr< MC_MocapStreamImpl > getInstance() override
GLuint const GLchar * name
Definition: glcorearb.h:786
std::function< T > UT_Function
Definition: UT_Function.h:37
void addParseWarning(const char *fmt, Args &&...args)
Call this to report a warning when parsing.
bool getError(UT_StringHolder &res) const
Reads the error message if one has been added.
UT_WorkBuffer myErrorMsg
fpreal64 fpreal
Definition: SYS_Types.h:277
void resetError()
Resets the error state.
bool isSameConnection(const ServerOptions &other) const
void setPortOpened(bool port_opened)
UT_StringHolder myFacialAttribs
**If you just want to fire and args
Definition: thread.h:609
UT_UniquePtr< GU_MotionClipRW > myClip
UT_ConcurrentQueue< UT_Function< void(void *)> > myTasks
void reset()
Resets the server state to its default values.
bool connected()
Whether a connection is currently established.
Definition: format.h:895