HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MocapStreamRokokoHDK.C
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: MocapStreamRokokoHDK.C ( KineFX Library, C++)
7  *
8  * The parser and server receiver for motion capture data from Rokoko device.
9  * This is used by the MocapStream SOP.
10  *
11  * Information for receiving and parsing the data can be found here:
12  * https://support.rokoko.com/hc/en-us/articles/4410416376977-Custom-Streaming
13  */
14 #include "MocapStreamRokokoHDK.h"
15 
16 #include <OP/OP_Director.h>
18 
19 #include <MC/MC_LZ4Decompressor.h>
20 
22 #include <FS/FS_EventGenerator.h>
23 #include <UT/UT_UniquePtr.h>
24 #include <UT/UT_NetSocket.h>
25 #include <UT/UT_NetFDSet.h>
26 #include <UT/UT_Thread.h>
27 #include <UT/UT_Options.h>
28 #include <UT/UT_ConcurrentVector.h>
29 #include <UT/UT_ScopeExit.h>
30 #include <UT/UT_JSONValue.h>
31 #include <UT/UT_JSONValueArray.h>
32 #include <UT/UT_JSONValueMap.h>
33 #include <SYS/SYS_Pragma.h>
34 
37 
38 #include <hboost/asio.hpp>
39 
41 
42 using namespace HDK_Sample;
43 using namespace UT::Literal;
44 
45 // Initialization function.
46 void
48 {
50 }
51 
52 // A custom server function which establishes a UDP connection with the computer
53 // running Rokoko Studio.
54 //
55 // Once it receives the data, the data is decompressed and then passed onto the
56 // device using the MC_MocapStreamImpl::ServerState::gotPacket(...) function.
57 void
59  const MC_MocapStreamImpl::ServerOptions& options,
61 {
62  using hboost::asio::ip::udp;
63  using hboost::asio::ip::address;
64 
65  try
66  {
67  // Rokoko's data streaming protocol uses LZ4 compression so we need
68  // to create an LZ4 decompressor for this.
69  MC_LZ4Decompressor decompressor;
70 
71  // Open a UDP socket so that we can state receiving data.
72  hboost::asio::io_service io_service;
73  udp::socket socket(io_service, udp::endpoint(udp::v4(), options.myPort));
74 
75  // sizes from the unreal implementation
76  UT_Array<char> buf(options.myBufferSize, options.myBufferSize);
77  UT_Array<char> uncompressed_buf(options.myBufferSize*64, options.myBufferSize*64);
78 
79  int native_socket = socket.native_handle();
80  UT_NetFDSet fd_set;
81  fd_set.add(native_socket, UT_NetFDEvent::NETFD_READ);
82 
83  // Tell the ServerState that the port has been opened. If this is not
84  // done, the callback for the "Connect" button will not end.
85  state.setPortOpened(true);
86 
87  // Loop until the server is killed. This happens when the "Disconnect"
88  // button is pressed on the MotionStream SOP.
89  while (!state.killed())
90  {
91  udp::endpoint remote_endpoint;
92  hboost::system::error_code error;
93 
94  // Synchronous boost sockets don't support timouts,
95  // so we'll do it manually with the native handle.
96 
97  // Check the events on the socket to see if we have receive new
98  // data.
99  int res = fd_set.checkEvents(100);
100  if (res <= 0)
101  {
102  // If no data was received within the time limit, add this
103  // to the log.
104  state.setError("Request timed out...");
105  continue;
106  }
107 
108  // Get the data from the socket.
109  size_t num_read = socket.receive_from(
110  hboost::asio::buffer(buf.data(), buf.size()),
111  remote_endpoint, 0, error);
112 
113  // If we ran into an error getting the data, throw the error.
114  // This is caught, a message is added to the log, and then the
115  // connection is ended.
116  if (error && error != hboost::asio::error::message_size)
117  throw hboost::system::system_error(error);
118 
119  // Once we have received a packet, tell the server state that we are
120  // connected.
121  state.setConnected(true);
122 
123  // Decompress the packet
124  size_t src_size = num_read;
125  size_t dst_size = uncompressed_buf.size();
126  size_t decompress_result;
127  decompress_result = decompressor.decompress(
128  uncompressed_buf.data(), dst_size, buf.data(), src_size);
129 
130  if (decompress_result == 0)
131  {
132  // If we successfully decompressed the data, reset the server
133  // state's error and then call ServerState::getPacket(...)
134  // to queue the decompressed packet for parsing.
135  state.resetError();
136  state.gotPacket(UT_StringHolder(uncompressed_buf.data(), dst_size));
137  }
138  else
139  {
140  // Otherwise, we send an error to the log.
141  state.setError("Could not decompress packet");
142  }
143  }
144  }
146  {
147  // If a boost error was encountered, add the error message to the log.
149  }
150 }
151 
152 // Define the server function and options in the contructor for the device.
153 //
154 // Either the MC_MocapStreamImpl::basicUdpReceiver or
155 // MC_MocapStreamImpl::basicTcpReceiver function could be used instead of
156 // rokokoServer for a more simple protocol.
157 MocapStreamRokokoHDK::MocapStreamRokokoHDK(const MC_MocapStreamImpl::ServerOptions& opts)
159 { }
160 
162 { }
163 
164 // Rokoko uses a pre-defined skeleton for streaming, the following arrays define
165 // the joints names, their parents, and their world space rest transforms.
166 UT_StringArray JOINTS{
167  "hip",
168  "leftUpLeg", "leftLeg", "leftFoot", "leftToe", "leftToeEnd",
169  "rightUpLeg", "rightLeg", "rightFoot", "rightToe", "rightToeEnd",
170  "spine", "chest",
171  "leftShoulder", "leftUpperArm", "leftLowerArm","leftHand",
172  "leftIndexProximal", "leftIndexMedial", "leftIndexDistal", "leftIndexTip",
173  "leftLittleProximal", "leftLittleMedial", "leftLittleDistal", "leftLittleTip",
174  "leftMiddleProximal", "leftMiddleMedial", "leftMiddleDistal", "leftMiddleTip",
175  "leftRingProximal", "leftRingMedial", "leftRingDistal", "leftRingTip",
176  "leftThumbProximal", "leftThumbMedial", "leftThumbDistal", "leftThumbTip",
177  "neck", "head",
178  "rightShoulder", "rightUpperArm", "rightLowerArm", "rightHand",
179  "rightIndexProximal", "rightIndexMedial", "rightIndexDistal", "rightIndexTip",
180  "rightLittleProximal", "rightLittleMedial", "rightLittleDistal", "rightLittleTip",
181  "rightMiddleProximal", "rightMiddleMedial", "rightMiddleDistal", "rightMiddleTip",
182  "rightRingProximal", "rightRingMedial", "rightRingDistal", "rightRingTip",
183  "rightThumbProximal", "rightThumbMedial", "rightThumbDistal", "rightThumbTip",
184 };
185 
187  -1, // Hips
188  0, 1, 2, 3, 4, // Left Leg
189  0, 6, 7, 8, 9, // Right Leg
190  0, 11, // Spine
191  12, 13, 14, 15, // Left Arm
192  16, 17, 18, 19, // Left Index Finger
193  16, 21, 22, 23, // Left Litter Finger
194  16, 25, 26, 27, // Left Middle Finger
195  16, 29, 30, 31, // Left Ring Finder
196  16, 33, 34, 35, // Left Thumb
197  12, 37, // Neck
198  12, 39, 40, 41, // Right Arm
199  42, 43, 44, 45, // Right Index Finger
200  42, 47, 48, 49, // Right Litter Finger
201  42, 51, 52, 53, // Right Middle Finger
202  42, 55, 56, 57, // Right Ring Finder
203  42, 59, 60, 61, // Right Thumb
204 };
205 
207  UT_Matrix4F(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0.9449999928474426, 0.02500000037252903, 1),
208  UT_Matrix4F(0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0.08799999952316284, 0.9449999928474426, 0.02500000037252903, 1),
209  UT_Matrix4F(0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0.08799999952316284, 0.4740000069141388, 0.02500000037252903, 1),
210  UT_Matrix4F(-1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0.08900000154972076, 0.002000000094994903, 0.02500000037252903, 1),
211  UT_Matrix4F(-1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0.08900000154972076, -0.06599999964237213, 0.1679999977350235, 1),
212  UT_Matrix4F(-1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0.08900000154972076, -0.07999999821186066, 0.2489999979734421, 1),
213  UT_Matrix4F(0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, -0.08900000154972076, 0.9449999928474426, 0.02500000037252903, 1),
214  UT_Matrix4F(0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, -0.08900000154972076, 0.4740000069141388, 0.02500000037252903, 1),
215  UT_Matrix4F(-1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -0.08900000154972076, 0.002000000094994903, 0.02500000037252903, 1),
216  UT_Matrix4F(-1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -0.08900000154972076, -0.06599999964237213, 0.1679999977350235, 1),
217  UT_Matrix4F(-1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -0.08900000154972076, -0.07999999821186066, 0.2489999979734421, 1),
218  UT_Matrix4F(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1.036999940872192, -0.01799999922513962, 1),
219  UT_Matrix4F(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1.202999949455261, -0.02099999971687794, 1),
220  UT_Matrix4F(0, 1, 0, 0, -0.9661348462104797, 0, 0.258037656545639, 0, 0.258037656545639, 0, 0.9661348462104797, 0, 0.07699999958276749, 1.478000044822693, 0, 1),
221  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.221000000834465, 1.478000044822693, -0.03799999877810478, 1),
222  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.5049999952316284, 1.478000044822693, -0.03799999877810478, 1),
223  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.8190000057220459, 1.478000044822693, -0.03799999877810478, 1),
224  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9070000052452087, 1.476999998092651, -0.009999999776482582, 1),
225  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9589999914169312, 1.476999998092651, -0.009999999776482582, 1),
226  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9900000095367432, 1.476999998092651, -0.009999999776482582, 1),
227  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 1.021000027656555, 1.476999998092651, -0.009999999776482582, 1),
228  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.8960000276565552, 1.476999998092651, -0.07299999892711639, 1),
229  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9369999766349792, 1.476999998092651, -0.07299999892711639, 1),
230  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9639999866485596, 1.476999998092651, -0.07299999892711639, 1),
231  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9900000095367432, 1.476999998092651, -0.07299999892711639, 1),
232  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9079999923706055, 1.476999998092651, -0.03200000151991844, 1),
233  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9599999785423279, 1.476999998092651, -0.03200000151991844, 1),
234  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9950000047683716, 1.476999998092651, -0.03200000151991844, 1),
235  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 1.027999997138977, 1.476999998092651, -0.03200000151991844, 1),
236  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9049999713897705, 1.476999998092651, -0.05400000140070915, 1),
237  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9539999961853027, 1.476999998092651, -0.05400000140070915, 1),
238  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0.9850000143051147, 1.476999998092651, -0.05400000140070915, 1),
239  UT_Matrix4F(0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 1.016000032424927, 1.476999998092651, -0.05400000140070915, 1),
240  UT_Matrix4F(-0.3532975912094116, -0.7077658176422119, 0.6117584705352783, 0, -0.8660871982574463, 0.0002441254764562473, -0.4998929500579834, 0, 0.3536577820777893, -0.7064471244812012, -0.6130731105804443, 0, 0.8420000076293945, 1.476999998092651, -0.00800000037997961, 1),
241  UT_Matrix4F(0, -0.7061478495597839, 0.7080644369125366, 0, -1, 0, 0, 0, 0, -0.7080644369125366, -0.7061478495597839, 0, 0.8790000081062317, 1.476999998092651, 0.01400000043213367, 1),
242  UT_Matrix4F(0, -0.7061478495597839, 0.7080644369125366, 0, -1, 0, 0, 0, 0, -0.7080644369125366, -0.7061478495597839, 0, 0.9120000004768372, 1.476999998092651, 0.01400000043213367, 1),
243  UT_Matrix4F(0, -0.7061478495597839, 0.7080644369125366, 0, -1, 0, 0, 0, 0, -0.7080644369125366, -0.7061478495597839, 0, 0.949999988079071, 1.476999998092651, 0.01400000043213367, 1),
244  UT_Matrix4F(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1.564000010490417, -0.02099999971687794, 1),
245  UT_Matrix4F(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1.677999973297119, -0.02099999971687794, 1),
246  UT_Matrix4F(0, -1, 0, 0, 0.9661348462104797, 0, 0.258037656545639, 0, -0.258037656545639, 0, 0.9661348462104797, 0, -0.07599999755620956, 1.478000044822693, 0, 1),
247  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.2199999988079071, 1.478000044822693, -0.03799999877810478, 1),
248  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.5040000081062317, 1.478000044822693, -0.03799999877810478, 1),
249  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.8180000185966492, 1.478000044822693, -0.03799999877810478, 1),
250  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.9070000052452087, 1.476999998092651, -0.009999999776482582, 1),
251  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.9580000042915344, 1.476999998092651, -0.009999999776482582, 1),
252  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.9890000224113464, 1.476999998092651, -0.009999999776482582, 1),
253  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -1.019999980926514, 1.476999998092651, -0.009999999776482582, 1),
254  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.8949999809265137, 1.476999998092651, -0.07299999892711639, 1),
255  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.9369999766349792, 1.476999998092651, -0.07299999892711639, 1),
256  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.9629999995231628, 1.476999998092651, -0.07299999892711639, 1),
257  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.9890000224113464, 1.476999998092651, -0.07299999892711639, 1),
258  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.9079999923706055, 1.476999998092651, -0.03200000151991844, 1),
259  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.9599999785423279, 1.476999998092651, -0.03200000151991844, 1),
260  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.9940000176429749, 1.476999998092651, -0.03200000151991844, 1),
261  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -1.026999950408936, 1.476999998092651, -0.03200000151991844, 1),
262  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.9039999842643738, 1.476999998092651, -0.05400000140070915, 1),
263  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.953000009059906, 1.476999998092651, -0.05400000140070915, 1),
264  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -0.984000027179718, 1.476999998092651, -0.05400000140070915, 1),
265  UT_Matrix4F(0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, -1.014999985694885, 1.476999998092651, -0.05400000140070915, 1),
266  UT_Matrix4F(-0.3532975912094116, 0.7077658176422119, -0.6117584705352783, 0, 0.8660871982574463, 0.0002441254764562473, -0.4998929500579834, 0, -0.3536577820777893, -0.7064471244812012, -0.6130731105804443, 0, -0.8410000205039978, 1.476999998092651, -0.00800000037997961, 1),
267  UT_Matrix4F(0, 0.7061478495597839, -0.7080644369125366, 0, 1, 0, 0, 0, 0, -0.7080644369125366, -0.7061478495597839, 0, -0.878000020980835, 1.476999998092651, 0.01400000043213367, 1),
268  UT_Matrix4F(0, 0.7061478495597839, -0.7080644369125366, 0, 1, 0, 0, 0, 0, -0.7080644369125366, -0.7061478495597839, 0, -0.9120000004768372, 1.476999998092651, 0.01400000043213367, 1),
269  UT_Matrix4F(0, 0.7061478495597839, -0.7080644369125366, 0, 1, 0, 0, 0, 0, -0.7080644369125366, -0.7061478495597839, 0, -0.9490000009536743, 1.476999998092651, 0.01400000043213367, 1),
270 };
271 
272 // The function which parses the decompressed packets defined earlier.
273 // Rokoko studio uses JSON to define its skeleton and rigid body data.
274 //
275 // This should return true if the packet was successfully parsed and contained
276 // skeleton or rigid body data. In any other situation, it should return false.
277 bool
279  UT_StringHolder& packet,
280  fpreal packet_time,
281  bool &parse_incomplete)
282 {
283  UT_JSONValue json;
284  if (!json.parseValue(packet))
285  {
286  return false;
287  }
288 
289  UT_JSONValueMap *root = json.getMap();
290  UT_JSONValueMap *scene = root->getObject("scene");
291  UT_JSONValueArray *actors = scene->getArray("actors");
292 
293  fpreal version = 2.0f;
294  const UT_JSONValue *version_val = scene->get("version");
295  if (version_val)
296  version = version_val->getF();
297 
298  // Set the packet time based upon the timestamp of the pose sent by
299  // Rokoko Studio. This is used when recording the stream and storing
300  // the poses in a MotionClip.
301  UT_JSONValue *timestamp_val = scene->get("timestamp");
302  if (timestamp_val)
303  setPacketTime(timestamp_val->getF());
304 
305  bool is_missing_joints = false;
306  int num_actors = actors->entries();
307  int num_joints = JOINTS.size();
308  for (int i = 0; i < num_actors; i++)
309  {
310  UT_JSONValueMap *actor = actors->getObject(i);
311  UT_JSONValueMap *body = actor->getObject("body");
312  const UT_JSONValue *actor_name = actor->get("name");
313  if (!actor_name)
314  continue;
315 
316  const char *name = actor_name->getS();
317  // Always make sure to note when a skeleton or other kind of motion
318  // capture object was received. This tells the Mocap Stream SOP
319  // to update its skeleton.
320  receivedSkeleton(name);
321 
322  // Get the meta-data for the skeleton
323  bool has_left_glove = true;
324  bool has_right_glove = true;
325  bool has_body = true;
326  bool has_face = true;
327  UT_JSONValueMap *meta = actor->getObject("meta");
328  if (version >= 3.0f && meta)
329  {
330  const UT_JSONValue *has_gloves_value = meta->get("hasGloves");
331  const UT_JSONValue *has_left_glove_value = meta->get("hasLeftGolve");
332  const UT_JSONValue *has_right_glove_value = meta->get("hasRightGlove");
333  const UT_JSONValue *has_body_value = meta->get("hasBody");
334  const UT_JSONValue *has_face_value = meta->get("hasFace");
335 
336  bool has_gloves = (has_gloves_value && has_gloves_value->getB());
337  has_left_glove = has_gloves && has_left_glove_value
338  && has_left_glove_value->getB();
339  has_right_glove = has_gloves && has_right_glove_value
340  && has_right_glove_value->getB();
341  has_body = has_body_value && has_body_value->getB();
342  has_face = has_face_value && has_face_value->getB();
343  }
344 
345  // Get/initialise skeletona
346  Actor *skeleton = mySkeletons.findActor(name);
347  if (!skeleton)
348  {
349  skeleton = mySkeletons.addActor(name);
350  skeleton->myPoints.setSize(num_joints);
351  }
352  skeleton->myIsDirty = true;
353 
354  for (int j = 0; j < num_joints; j++)
355  {
356  ActorPoint &joint = skeleton->myPoints[j];
357 
358  const UT_StringHolder& joint_name = JOINTS[j];
359  UT_JSONValueMap *joint_data = body->getObject(joint_name);
360  UT_JSONValueMap *joint_pos, *joint_rot;
361  const UT_JSONValue *pos_x, *pos_y, *pos_z;
362  const UT_JSONValue *rot_x, *rot_y, *rot_z, *rot_w;
363  if (joint_data)
364  {
365  joint_pos = joint_data->getObject("position");
366  joint_rot = joint_data->getObject("rotation");
367  if (joint_pos && joint_rot)
368  {
369  pos_x = joint_pos->get("x");
370  pos_y = joint_pos->get("y");
371  pos_z = joint_pos->get("z");
372 
373  rot_x = joint_rot->get("x");
374  rot_y = joint_rot->get("y");
375  rot_z = joint_rot->get("z");
376  rot_w = joint_rot->get("w");
377  }
378  }
379 
380  if (!joint_data || !joint_pos || !joint_rot
381  || !pos_x || !pos_y || !pos_z
382  || !rot_x || !rot_y || !rot_z || !rot_w)
383  {
384  joint.myDidReceive = false;
385 
386  if (joint_name.fcontain("Index")
387  || joint_name.fcontain("Little")
388  || joint_name.fcontain("Middle")
389  || joint_name.fcontain("Ring")
390  || joint_name.fcontain("Thumb"))
391  {
392  bool is_right_hand = joint_name.fcontain("right");
393  is_missing_joints = (is_right_hand && has_right_glove)
394  || (!is_right_hand && has_left_glove);
395  }
396  else if (has_body)
397  {
398  is_missing_joints = true;
399  }
400 
401  continue;
402  }
403 
404  UT_Vector3F pos(-pos_x->getF(), pos_y->getF(), pos_z->getF());
405  UT_QuaternionF rot(-rot_x->getF(), rot_y->getF(),
406  rot_z->getF(), -rot_w->getF());
407 
408  UT_Matrix3F rot_mat;
409  rot.getRotationMatrix(rot_mat);
410 
411  joint.myWorldXform = rot_mat;
412  joint.myWorldXform.setTranslates(pos);
413 
414  joint.myDidReceive = true;
415  }
416 
417  // We'll just throw the face blendShapes right onto detail attributes.
418  UT_JSONValueMap *face = actor->getObject("face");
419  if (face && has_face)
420  {
421  skeleton->myBlendShapes.clear();
422  for (const auto& item : *face)
423  {
424  UT_JSONValue *val = item.second;
425  if (val->getType() == UT_JSONValue::JSON_REAL)
426  {
427  // Ensure the blend shapes match the 0-1 range supported
428  // by Houdini
429  skeleton->myBlendShapes[item.first] = val->getF()/100.f;
430  }
431  }
432  }
433  }
434 
435  if (is_missing_joints)
436  addParseWarning("Did not receive data for every joint.");
437 
438  // we'll treat a prop as a skeleton with a single point
439  UT_JSONValueArray *props = scene->getArray("props");
440  int num_props = props->entries();
441  for (int i = 0; i < num_props; i++)
442  {
443  UT_JSONValueMap *prop = props->getObject(i);
444  const UT_JSONValue *prop_name = prop->get("name");
445  if (!prop_name)
446  continue;
447 
448  const char *name = prop_name->getS();
449 
450  UT_JSONValueMap *joint_pos = prop->getObject("position");
451  UT_JSONValueMap *joint_rot = prop->getObject("rotation");
452  if (!joint_pos || !joint_rot)
453  continue;
454 
455  const UT_JSONValue *pos_x = joint_pos->get("x");
456  const UT_JSONValue *pos_y = joint_pos->get("y");
457  const UT_JSONValue *pos_z = joint_pos->get("z");
458 
459  const UT_JSONValue *rot_x = joint_rot->get("x");
460  const UT_JSONValue *rot_y = joint_rot->get("y");
461  const UT_JSONValue *rot_z = joint_rot->get("z");
462  const UT_JSONValue *rot_w = joint_rot->get("w");
463  if (!pos_x || !pos_y || !pos_z
464  || !rot_x || !rot_y || !rot_z || !rot_w)
465  continue;
466 
467  receivedSkeleton(name);
468 
469  // Get/initialise a prop
470  Actor *device_prop = myProps.findActor(name);
471  if (!device_prop)
472  {
473  device_prop = myProps.addActor(name);
474  device_prop->myPoints.setSize(1);
475  }
476 
477  UT_Vector3F pos(-pos_x->getF(), pos_y->getF(), pos_z->getF());
478  UT_QuaternionF rot(-rot_x->getF(), rot_y->getF(),
479  rot_z->getF(), -rot_w->getF());
480 
481  UT_Matrix3F rot_mat;
482  rot.getRotationMatrix(rot_mat);
483 
484  ActorPoint &point = device_prop->myPoints[0];
485  point.myWorldXform = rot_mat;
486  point.myWorldXform.setTranslates(pos);
487 
488  point.myDidReceive = true;
489  }
490 
491  return true;
492 }
493 
494 // This function computes the local transforms of all of the joints that were
495 // just received and uses the previously received local transforms of the other
496 // joints to compute the world space transform of each joints.
497 //
498 // Rokoko Studio can be configured to not send fingure data. This ensures that
499 // the fingures are always place appropriately on the skeleton.
500 void
501 MocapStreamRokokoHDK::computeSkeletonTransforms()
502 {
503  int num_skeletons = mySkeletons.size();
504  int num_joints = JOINTS.size();
505  for (int i = 0; i < num_skeletons; i++)
506  {
507  Actor &skeleton = mySkeletons(i);
508  if (!skeleton.myIsDirty)
509  continue;
510 
511  skeleton.myIsDirty = false;
512  if (skeleton.myPoints.size() != JOINTS.size())
513  continue;
514 
515  bool received_all_points = true;
516 
517  // Compute the local transforms of the joints
518  for (int j = 0; j < num_joints; j++)
519  {
520  // If a joint and its parent were received, compute the joint's
521  // local transform. Otherwise, we use the last received local
522  // transform.
523  ActorPoint &joint = skeleton.myPoints[j];
524  if (!joint.myDidReceive)
525  {
526  received_all_points = false;
527  continue;
528  }
529 
530  if (PARENTS[j] < 0)
531  {
532  joint.myLocalXform = joint.myWorldXform;
533  continue;
534  }
535 
536  ActorPoint &parent = skeleton.myPoints[PARENTS[j]];
537  if (!parent.myDidReceive)
538  continue;
539 
540  UT_Matrix4F parent_xform_inv = parent.myWorldXform;
541  parent_xform_inv.invert();
542 
543  joint.myLocalXform = joint.myWorldXform * parent_xform_inv;
544  }
545 
546  if (received_all_points)
547  continue;
548 
549  // Compute the world transforms of the joints
550  for (int j = 0; j < num_joints; j++)
551  {
552  ActorPoint &joint = skeleton.myPoints[j];
553  if (PARENTS[j] < 0)
554  {
555  joint.myWorldXform = joint.myLocalXform;
556  continue;
557  }
558 
559  ActorPoint &parent = skeleton.myPoints[PARENTS[j]];
560  joint.myWorldXform = joint.myLocalXform * parent.myWorldXform;
561  }
562  }
563 }
564 
565 // This function is called by MC_MocapStreamImpl to update the joints of the
566 // streamed skeletons on the geometry for the MocapStream SOP.
567 void
569  GU_Detail *gdp,
570  const MC_MocapStreamCookParms &cookparms,
571  const MC_MocapStreamCookParms &prev_cookparms)
572 {
573  GA_ROHandleS actor_name_h(gdp->addStringTuple(
576 
577  // Compute the local and world transforms of the skeletons
578  computeSkeletonTransforms();
579 
580  // Reset the number of times we've seen a point for each skeleton to 0
581  mySkeletons.resetOccurances();
582  myProps.resetOccurances();
583 
584  // Keep a record of the first actor we see that has blendshapes.
585  // This will be used to define the blendshapes for the output geometry.
586  // Since we do this on updateJoints, the actor filter will work to select
587  // the blendshapes for different skeletons.
588  MocapStreamRokokoHDK::Actor *first_blendshapes_actor = nullptr;
589 
590  // Set the world spaces transforms for each joint
591  GA_Offset ptoff;
592  GA_FOR_ALL_PTOFF(gdp, ptoff)
593  {
594  const auto& actor_name = actor_name_h.get(ptoff);
595 
596  // Get the actor that corresponds to the point
597  MocapStreamRokokoHDK::Actor *actor = mySkeletons.findActor(actor_name);
598 
599  if (!actor)
600  actor = myProps.findActor(actor_name);
601  else if (!first_blendshapes_actor && actor->myBlendShapes.size() > 0)
602  first_blendshapes_actor = actor;
603 
604  if (!actor)
605  continue;
606 
607  // Get the number of times we've seen this actor. This tells us which
608  // point to set.
609  int i = actor->myOccurances;
610  actor->myOccurances++;
611  if (i >= actor->myPoints.entries())
612  continue;
613 
614  // Set the transform of the point.
615  MocapStreamRokokoHDK::ActorPoint &point = actor->myPoints(i);
616 
617  UT_Matrix3F transform(point.myWorldXform);
618  transform_h.set(ptoff, transform);
619 
620  UT_Vector3F pos;
621  point.myWorldXform.getTranslates(pos);
622  gdp->setPos3(ptoff, pos);
623  }
624  gdp->getP()->bumpDataId();
625  transform_h.bumpDataId();
626 
627  // Get our previous list of blendshapes for this node
628  UT_StringArray &prev_blendshapes = myNodeBlendShapes[cookparms.myNodePath];
629 
630  // If no skeletons with blendshapes were seen, remove the blendshapes
631  // attributes and exit early.
632  if (!first_blendshapes_actor)
633  {
634  for (const auto &blendshape : prev_blendshapes)
635  {
636  UT_StringHolder atr_name = prev_cookparms.myFacialAttribs;
637  atr_name.substitute("*", blendshape);
639  }
640  prev_blendshapes.clear();
641  return;
642  }
643 
644  // Destroy any blendshapes attributes which were on the previous geometry
645  // but not on this one.
646  for (const auto &blendshape : prev_blendshapes)
647  {
648  if (first_blendshapes_actor->myBlendShapes.contains(blendshape))
649  continue;
650 
651  UT_StringHolder atr_name = prev_cookparms.myFacialAttribs;
652  atr_name.substitute("*", blendshape);
654  }
655  prev_blendshapes.clear();
656 
657  // Set the blendshapes of the geometry to the blendshapes
658  // of the first seen actor that had blendshapes
659  for (auto& item : first_blendshapes_actor->myBlendShapes)
660  {
661  // Call the MC_MocapStreamImpl::updateAttribName function to update
662  // the name of the blend shapes's detail attribute names based upon
663  // the Facial Attributes parameter.
664  UT_StringHolder attrib_name =
665  updateAttribName(gdp, item.first, cookparms.myFacialAttribs,
666  prev_cookparms.myFacialAttribs);
667  gdp->setDetailAttributeF(attrib_name, item.second);
668 
669  // Added the non-renamed blendshape to our list for this node
670  prev_blendshapes.append(item.first);
671  }
672 }
673 
674 // This function is used by MC_MocapStreamImpl to clear the recorded data
675 // for a skeleton. The most common use case for this would be when the data for
676 // a particular skeleton stops being streamed.
677 void
679 {
680  if (mySkeletons.removeActor(name))
681  return;
682 
683  myProps.removeActor(name);
684 }
685 
686 // This function is used by MC_MocapStreamImpl to add any new skeletons or
687 // rigid bodies to the MocapStream SOPs geometry.
688 void
690 {
691  GA_RWHandleS joint_name_h(gdp->addStringTuple(
693  GA_RWHandleS actor_name_h(gdp->addStringTuple(
695  GA_RWHandleM4 rest_transform_h(gdp->addFloatTuple(
696  GA_ATTRIB_POINT, "rest_transform", 16));
697 
698  MocapStreamRokokoHDK::Actor *actor = myProps.findActor(name);
699  if (actor)
700  {
701  GA_Offset ptoff = gdp->appendPoint();
702  joint_name_h.set(ptoff, name);
703  actor_name_h.set(ptoff, name);
704  }
705  else
706  {
707  actor = mySkeletons.findActor(name);
708  if (!actor)
709  return;
710 
711  // update the rest pose bone lengths to match the received pose
712  UT_Array<UT_Matrix4F> rest_xforms(REST_XFORMS);
713 
714  int num_joints = JOINTS.size();
715  if (actor->myPoints.size() == num_joints)
716  {
717  for (int i = 0; i < num_joints; i++)
718  {
719  if (PARENTS[i] < 0)
720  continue;
721 
722  auto& xform = rest_xforms[i];
723  MocapStreamRokokoHDK::ActorPoint &joint
724  = actor->myPoints[i];
725  MocapStreamRokokoHDK::ActorPoint &parent
726  = actor->myPoints[PARENTS[i]];
727 
728  // This works because the joints are sorted such that the parent
729  // will always be processed first
730  UT_Vector3F pt;
731  xform.getTranslates(pt);
732  xform = UT_Matrix3F(xform);
733  UT_Vector3F old_origin(0.0);
734  UT_Vector3F new_origin(0.0);
735 
736  fpreal bone_length;
737  REST_XFORMS[PARENTS[i]].getTranslates(old_origin);
738  rest_xforms[PARENTS[i]].getTranslates(new_origin);
739  if (joint.myDidReceive && parent.myDidReceive)
740  {
741  UT_Vector3F joint_pos, parent_pos;
742  joint.myWorldXform.getTranslates(joint_pos);
743  parent.myWorldXform.getTranslates(parent_pos);
744  bone_length = (joint_pos - parent_pos).length();
745  }
746  else
747  {
748  bone_length = (pt - old_origin).length();
749  }
750 
751  UT_Vector3F offset = pt - old_origin;
752 
753  pt = new_origin + offset*bone_length/offset.length();
754  xform.setTranslates(pt);
755  }
756  }
757 
758  GA_Offset ptoff = gdp->appendPointBlock(JOINTS.size());
759 
760  for (int i = 0; i < num_joints; i++)
761  {
762  MocapStreamRokokoHDK::ActorPoint &joint
763  = actor->myPoints[i];
764 
765  joint_name_h.set(ptoff+i, JOINTS[i]);
766  actor_name_h.set(ptoff+i, name);
767  rest_transform_h.set(ptoff+i, UT_Matrix4F(rest_xforms[i]));
768 
769  UT_Vector3F pt;
770  rest_xforms[i].getTranslates(pt);
771  gdp->setPos3(ptoff + i, pt);
772 
773  if (PARENTS[i] != -1)
774  {
775  GA_Offset vtx_off;
776  gdp->appendPrimitivesAndVertices(GEO_PRIMPOLY, 1,2, vtx_off, false);
777  gdp->setVertexPoint(vtx_off, ptoff+PARENTS[i]);
778  gdp->setVertexPoint(vtx_off + 1, ptoff+i);
779 
780  // Set the default local transform of the skeleton
781  UT_Matrix4F parent_xform_inv = rest_xforms[PARENTS[i]];
782  parent_xform_inv.invert();
783  joint.myLocalXform = rest_xforms[i] * parent_xform_inv;
784  }
785  else
786  {
787  // Set the default local transform of the skeleton
788  joint.myLocalXform = rest_xforms[i];
789  }
790  }
791 
792 
793  }
794  gdp->bumpDataIdsForAddOrRemove(true, true, true);
795 }
796 
797 #if 0
798 // An implementation of the MC_MocapStreamImpl::basicUdpReceiver.
799 // This is an example of the most basic setup to receive motion capture data
800 // through a UDP connection and pass it onto your device to parse.
801 //
802 // This function can be used as the server function for your state instead
803 // of defining your own.
804 void basicUdpReceiver(ServerState& state, const ServerOptions& options,
806 {
807  using hboost::asio::ip::udp;
808  using hboost::asio::ip::address;
809 
810  try
811  {
812  // Open a socket so that we are prepared to receive data via the udp
813  // connection.
814  hboost::asio::io_service io_service;
815  udp::socket socket(io_service, udp::endpoint(udp::v4(), options.myPort));
816  UT_Array<char> buf(options.myBufferSize, options.myBufferSize);
817 
818  int native_socket = socket.native_handle();
819  UT_NetFDSet fd_set;
820  fd_set.add(native_socket, UT_NetFDEvent::NETFD_READ);
821 
822  // Tell the ServerState that the port has been opened. If this is not
823  // done, the callback for the "Connect" button will not end.
824  state.setPortOpened(true);
825 
826  // Loop until the server is killed. This happens when the "Disconnect"
827  // button is pressed on the MotionStream SOP.
828  while (!state.killed())
829  {
830  udp::endpoint remote_endpoint;
831  hboost::system::error_code error;
832 
833  // Synchronous boost sockets don't support timouts,
834  // so we'll do it manually with the native handle.
835 
836  // Check the events on the socket to see if we have receive new
837  // data.
838  int res = fd_set.checkEvents(100);
839  if (res <= 0)
840  {
841  // If no data was received within the time limit, add this
842  // to the log.
843  state.setError("Request timed out...");
844  continue;
845  }
846 
847  // Get the data from the socket.
848  size_t num_read = socket.receive_from(
849  hboost::asio::buffer(buf.data(), buf.size()),
850  remote_endpoint, 0, error);
851 
852  // If we ran into an error getting the data, throw the error.
853  // This is caught, a message is added to the log, and then the
854  // connection is ended.
855  if (error && error != hboost::asio::error::message_size)
856  throw hboost::system::system_error(error);
857 
858  // Once we have received a packet, tell the server state that we are
859  // connected, clear the error buffer, and call
860  // ServerState::gotPacket to queue the packet for parsing.
861  state.setConnected(true);
862  state.resetError();
863  state.gotPacket(UT_StringHolder(buf.data(), num_read));
864  }
865  }
867  {
868  // If a boost error was encountered, add the error message to the log.
870  }
871 }
872 
873 // An implementation of the MC_MocapStreamImpl::basicTcpReceiver.
874 // This is an example of the most basic setup to receive motion capture data
875 // through a UDP connection and pass it onto your device to parse.
876 //
877 // This function can be used as the server function for your state instead
878 // of defining your own.
879 void basicTcpClient(ServerState& state, const ServerOptions& options,
880  MC_MocapStreamImpl *data)
881 {
882  // Open a connection
884  options.myHostIP, options.myPort, true);
885  UT_Array<char> buf(options.myBufferSize, options.myBufferSize);
886 
887  // Tell the ServerState that the port has been opened. If this is not
888  // done, the callback for the "Connect" button will not end.
889  state.setPortOpened(true);
890 
891  int num_read;
892  // Ensure that the client is valid. If not, send a message to the log and
893  // then end the server function.
894  if (client->isValid())
895  {
896  // Loop until the server is killed. This happens when the "Disconnect"
897  // button is pressed on the MotionStream SOP.
898  while (!state.killed())
899  {
900  // Establish a connection with the host computer.
901  int cond = client->connect(500);
902 
903  // Tell the ServerState whether the connection was successful
904  state.setConnected(cond == UT_NetSocket::UT_CONNECT_SUCCESS);
905 
906  if (!state.connected())
907  {
908  // If the connection failed, send an error to the log.
909  state.setError("Could not connect");
910  }
911 
912  // Loop while we are still connected and while the server has not
913  // been killed.
914  while (state.connected() && !state.killed())
915  {
916  // Attempt to receive data from the host.
917  int status = client->read(buf.data(), buf.size(), &num_read, 200);
918  if (status == UT_NetSocket::UT_CONNECT_SUCCESS && num_read > 0)
919  {
920  // If a packet was received, call ServerState::gotPacket
921  // to queue the packet for parsing.
922  state.gotPacket(UT_StringHolder(buf.data(), num_read));
923  }
924  else if (status != UT_NetSocket::UT_WOULD_BLOCK)
925  {
926  state.setError("Error receiving data");
927  //state.setError(UT_NetSocket::getErrorName(status));
928  state.setConnected(false);
929  client->close();
931  options.myHostIP, options.myPort, true);
932  }
933 #if UT_ASSERT_LEVEL > 1
934  else
935  {
936  state.setError("No data received within 500ms");
937  }
938 #endif
939  }
940  }
941  }
942  else
943  {
944  state.setError("Invalid request");
945  }
946 }
947 #endif
void setPacketTime(fpreal64 time)
SYS_FORCE_INLINE void bumpDataId()
Definition: GA_Attribute.h:306
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glcorearb.h:2540
const char * fcontain(const char *pattern, bool case_sensitive=true) const
SYS_NO_DISCARD_RESULT const UT_JSONValueMap * getObject(const UT_StringRef &k) const
UT_JSONValueMap stores a map/dictionary of UT_JSONValue objects.
GA_RWHandleM3D createTransform3(GEO_Detail &detail, GA_AttributeOwner owner=GA_ATTRIB_POINT)
bool parseValue(UT_JSONParser &parser, UT_IStream *is=0, bool record_source_offsets=false)
#define SYS_PRAGMA_PUSH_WARN()
Definition: SYS_Pragma.h:34
Abstracts the socket file descriptor set for select() or poll() calls.
Definition: UT_NetFDSet.h:51
UT_Array< UT_Matrix4F > REST_XFORMS
bool parsePacket(UT_StringHolder &packet, fpreal packet_time, bool &parse_incomplete) override
size_t decompress(char *dst_buf, size_t dst_size, char *src_buf, size_t src_size)
#define SYS_PRAGMA_DISABLE_OVERLOADED_VIRTUAL()
Definition: SYS_Pragma.h:59
GA_Attribute * addFloatTuple(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringHolder &name, int tuple_size, const GA_Defaults &defaults=GA_Defaults(0.0), const UT_Options *creation_args=0, const GA_AttributeOptions *attribute_options=0, GA_Storage storage=GA_STORE_REAL32, const GA_ReuseStrategy &reuse=GA_ReuseStrategy())
SYS_NO_DISCARD_RESULT const UT_JSONValue * get(int64 i) const
Access a const entry by index.
auto system_error(int error_code, format_string< T...> fmt, T &&...args) -> std::system_error
Definition: format.h:2240
UT_JSONValueArray stores a list of UT_JSONValue objects.
GA_API const UT_StringHolder rot
void clear()
Clear the entries in the map.
SYS_NO_DISCARD_RESULT const UT_JSONValueMap * getObject(int64 i) const
Access a const object by index.
GA_Attribute * getP()
Convenience method to access the P attribute.
Definition: GA_Detail.h:164
const UT_StringHolder theActorAttribName
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:795
UT_StringHolder updateAttribName(GU_Detail *gdp, const UT_StringHolder &base_name, const UT_StringHolder &rename_pattern, const UT_StringHolder &prev_rename_pattern)
constexpr SYS_FORCE_INLINE T length() const noexcept
Definition: UT_Vector3.h:361
SYS_NO_DISCARD_RESULT const char * getS() const
Get the string value (may return a NULL pointer)
Standard user attribute level.
Definition: GA_Types.h:148
bool killed()
Whether the connection has been killed.
void receivedSkeleton(const UT_StringHolder &name)
void setVertexPoint(GA_Offset vertex, GA_Offset ptoff)
Given a vertex, set the corresponding point offset.
Definition: GA_Detail.h:536
GLuint buffer
Definition: glcorearb.h:660
exint size() const
Definition: UT_Array.h:646
int substitute(const char *find, const char *replacement, int count=-1)
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
< returns > If no error
Definition: snippets.dox:2
GA_API const UT_StringHolder name
GA_Size GA_Offset
Definition: GA_Types.h:641
GLfloat f
Definition: glcorearb.h:1926
GLintptr offset
Definition: glcorearb.h:665
void gotPacket(UT_StringHolder &&packet)
SYS_FORCE_INLINE GA_Offset appendPointBlock(GA_Size npoints)
Append new points, returning the first offset of the contiguous block.
Definition: GA_Detail.h:330
SYS_NO_DISCARD_RESULT UT_JSONValueMap * getMap() const
Get the map value (may return a NULL pointer)
SYS_FORCE_INLINE GA_Offset appendPoint()
Append a new point, returning its new data offset.
Definition: GA_Detail.h:326
UT_Matrix4T< fpreal32 > UT_Matrix4F
UT_Matrix3T< fpreal32 > UT_Matrix3F
#define SYS_PRAGMA_POP_WARN()
Definition: SYS_Pragma.h:35
GLuint const GLchar * name
Definition: glcorearb.h:786
SYS_NO_DISCARD_RESULT bool getB() const
Get the bool value. Interprets integer/float as bool.
#define GA_FOR_ALL_PTOFF(gdp, ptoff)
Definition: GA_GBMacros.h:88
GA_API const UT_StringHolder transform
void removeSkeleton(const UT_StringHolder &name) override
Removes the stored data for a skeleton.
SYS_NO_DISCARD_RESULT fpreal64 getF() const
Get the real value. Interprets bool/int as reals.
void addParseWarning(const char *fmt, Args &&...args)
Call this to report a warning when parsing.
static UT_UniquePtr< UT_NetSocket > newSocketFromAddr(const char *address, int port, bool blocking=false, int localport=-1)
void newMocapStreamDevice(void *)
SYS_FORCE_INLINE int64 entries() const
Return the number of entries in the array.
GT_API const UT_StringHolder version
void buildSkeleton(GU_Detail *gdp, UT_StringHolder name) override
GLint j
Definition: glad.h:2733
int checkEvents(int timeout_ms) const
void updateJoints(GU_Detail *gdp, const MC_MocapStreamCookParms &cookparms, const MC_MocapStreamCookParms &prev_cookparms) override
SYS_FORCE_INLINE void setPos3(GA_Offset ptoff, const UT_Vector3 &pos)
Set P from a UT_Vector3.
Definition: GA_Detail.h:237
fpreal64 fpreal
Definition: SYS_Types.h:277
void resetError()
Resets the error state.
T * data()
Definition: UT_Array.h:822
void setPortOpened(bool port_opened)
bool setDetailAttributeF(const UT_StringHolder &aname, float v)
GLuint GLfloat * val
Definition: glcorearb.h:1608
UT_StringHolder myFacialAttribs
SYS_NO_DISCARD_RESULT Type getType() const
Get the type of data stored in the object.
Definition: UT_JSONValue.h:165
Class to store JSON objects as C++ objects.
Definition: UT_JSONValue.h:99
int invert(T tol=0.0F)
bool add(int fd, UT_NetFDEvent event)
void rokokoServer(MC_MocapStreamImpl::ServerState &state, const MC_MocapStreamImpl::ServerOptions &options, MC_MocapStreamImpl *data)
SYS_NO_DISCARD_RESULT const UT_JSONValueArray * getArray(const UT_StringRef &k) const
void destroyAttribute(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringRef &name, const GA_AttributeFilter *filter=0)
void bumpDataIdsForAddOrRemove(bool added_or_removed_points, bool added_or_removed_vertices, bool added_or_removed_primitives)
UT_StringArray JOINTS UT_Array< exint > PARENTS
Definition: format.h:895
GLenum GLuint GLsizei const GLenum * props
Definition: glcorearb.h:2525
static void handleBoostError(hboost::system::error_code e, ServerState &state)
Adds an error message to the log corresponding to an hboost error code.
GA_Attribute * addStringTuple(GA_AttributeOwner owner, GA_AttributeScope scope, const UT_StringHolder &name, int tuple_size, const UT_Options *creation_args=0, const GA_AttributeOptions *attribute_options=0, const GA_ReuseStrategy &reuse=GA_ReuseStrategy())
GA_Offset appendPrimitivesAndVertices(const GA_PrimitiveTypeId &type, GA_Size nprimitives, GA_Size nvertices_each, GA_Offset &vertex_block_start, bool closed_flag=false)