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