Blender V4.3
GHOST_XrControllerModel.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cassert>
10
11#include <Eigen/Core>
12#include <Eigen/Geometry>
13
14#include "GHOST_Types.h"
15#include "GHOST_XrException.hh"
16#include "GHOST_Xr_intern.hh"
17
19
20#define TINYGLTF_IMPLEMENTATION
21#define TINYGLTF_NO_STB_IMAGE
22#define TINYGLTF_NO_STB_IMAGE_WRITE
23#define STBIWDEF static inline
24#include "tiny_gltf.h"
25
31
32/* -------------------------------------------------------------------- */
40 std::vector<GHOST_XrControllerModelVertex> vertices;
41 std::vector<uint32_t> indices;
42};
43
48static void validate_accessor(const tinygltf::Accessor &accessor,
49 const tinygltf::BufferView &buffer_view,
50 const tinygltf::Buffer &buffer,
51 size_t byte_stride,
52 size_t element_size)
53{
54 /* Make sure the accessor does not go out of range of the buffer view. */
55 if (accessor.byteOffset + (accessor.count - 1) * byte_stride + element_size >
56 buffer_view.byteLength)
57 {
58 throw GHOST_XrException("glTF: Accessor goes out of range of bufferview.");
59 }
60
61 /* Make sure the buffer view does not go out of range of the buffer. */
62 if (buffer_view.byteOffset + buffer_view.byteLength > buffer.data.size()) {
63 throw GHOST_XrException("glTF: BufferView goes out of range of buffer.");
64 }
65}
66
67template<float (GHOST_XrControllerModelVertex::*field)[3]>
68static void read_vertices(const tinygltf::Accessor &accessor,
69 const tinygltf::BufferView &buffer_view,
70 const tinygltf::Buffer &buffer,
71 GHOST_XrPrimitive &primitive)
72{
73 if (accessor.type != TINYGLTF_TYPE_VEC3) {
75 "glTF: Accessor for primitive attribute has incorrect type (VEC3 expected).");
76 }
77
78 if (accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) {
80 "glTF: Accessor for primitive attribute has incorrect component type (FLOAT expected).");
81 }
82
83 /* If stride is not specified, it is tightly packed. */
84 constexpr size_t packed_size = sizeof(float) * 3;
85 const size_t stride = buffer_view.byteStride == 0 ? packed_size : buffer_view.byteStride;
86 validate_accessor(accessor, buffer_view, buffer, stride, packed_size);
87
88 /* Resize the vertices vector, if necessary, to include room for the attribute data.
89 * If there are multiple attributes for a primitive, the first one will resize, and the
90 * subsequent will not need to. */
91 primitive.vertices.resize(accessor.count);
92
93 /* Copy the attribute value over from the glTF buffer into the appropriate vertex field. */
94 const uint8_t *buffer_ptr = buffer.data.data() + buffer_view.byteOffset + accessor.byteOffset;
95 for (size_t i = 0; i < accessor.count; i++, buffer_ptr += stride) {
96 memcpy(primitive.vertices[i].*field, buffer_ptr, stride);
97 }
98}
99
100static void load_attribute_accessor(const tinygltf::Model &gltf_model,
101 const std::string &attribute_name,
102 int accessor_id,
103 GHOST_XrPrimitive &primitive)
104{
105 const auto &accessor = gltf_model.accessors.at(accessor_id);
106
107 if (accessor.bufferView == -1) {
108 throw GHOST_XrException("glTF: Accessor for primitive attribute specifies no bufferview.");
109 }
110
111 const tinygltf::BufferView &buffer_view = gltf_model.bufferViews.at(accessor.bufferView);
112 if (buffer_view.target != TINYGLTF_TARGET_ARRAY_BUFFER && buffer_view.target != 0) {
113 throw GHOST_XrException(
114 "glTF: Accessor for primitive attribute uses bufferview with invalid 'target' type.");
115 }
116
117 const tinygltf::Buffer &buffer = gltf_model.buffers.at(buffer_view.buffer);
118
119 if (attribute_name.compare("POSITION") == 0) {
121 accessor, buffer_view, buffer, primitive);
122 }
123 else if (attribute_name.compare("NORMAL") == 0) {
125 accessor, buffer_view, buffer, primitive);
126 }
127}
128
133template<typename TSrcIndex>
134static void read_indices(const tinygltf::Accessor &accessor,
135 const tinygltf::BufferView &buffer_view,
136 const tinygltf::Buffer &buffer,
137 GHOST_XrPrimitive &primitive)
138{
139
140 /* Allow 0 (not specified) even though spec doesn't seem to allow this (BoomBox GLB fails). */
141 if (buffer_view.target != TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER && buffer_view.target != 0) {
142 throw GHOST_XrException(
143 "glTF: Accessor for indices uses bufferview with invalid 'target' type.");
144 }
145
146 constexpr size_t component_size_bytes = sizeof(TSrcIndex);
147 /* Index buffer must be packed per glTF spec. */
148 if (buffer_view.byteStride != 0 && buffer_view.byteStride != component_size_bytes) {
149 throw GHOST_XrException(
150 "glTF: Accessor for indices uses bufferview with invalid 'byteStride'.");
151 }
152
153 validate_accessor(accessor, buffer_view, buffer, component_size_bytes, component_size_bytes);
154
155 /* Since only triangles are supported, enforce that the number of indices is divisible by 3. */
156 if ((accessor.count % 3) != 0) {
157 throw GHOST_XrException("glTF: Unexpected number of indices for triangle primitive");
158 }
159
160 const TSrcIndex *index_buffer = reinterpret_cast<const TSrcIndex *>(
161 buffer.data.data() + buffer_view.byteOffset + accessor.byteOffset);
162 for (uint32_t i = 0; i < accessor.count; i++) {
163 primitive.indices.push_back(*(index_buffer + i));
164 }
165}
166
170static void load_index_accessor(const tinygltf::Model &gltf_model,
171 const tinygltf::Accessor &accessor,
172 GHOST_XrPrimitive &primitive)
173{
174 if (accessor.type != TINYGLTF_TYPE_SCALAR) {
175 throw GHOST_XrException("glTF: Accessor for indices specifies invalid 'type'.");
176 }
177
178 if (accessor.bufferView == -1) {
179 throw GHOST_XrException("glTF: Index accessor without bufferView is currently not supported.");
180 }
181
182 const tinygltf::BufferView &buffer_view = gltf_model.bufferViews.at(accessor.bufferView);
183 const tinygltf::Buffer &buffer = gltf_model.buffers.at(buffer_view.buffer);
184
185 if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
186 read_indices<uint8_t>(accessor, buffer_view, buffer, primitive);
187 }
188 else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
189 read_indices<uint16_t>(accessor, buffer_view, buffer, primitive);
190 }
191 else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
192 read_indices<uint32_t>(accessor, buffer_view, buffer, primitive);
193 }
194 else {
195 throw GHOST_XrException("glTF: Accessor for indices specifies invalid 'componentType'.");
196 }
197}
198
199static GHOST_XrPrimitive read_primitive(const tinygltf::Model &gltf_model,
200 const tinygltf::Primitive &gltf_primitive)
201{
202 if (gltf_primitive.mode != TINYGLTF_MODE_TRIANGLES) {
203 throw GHOST_XrException(
204 "glTF: Unsupported primitive mode. Only TINYGLTF_MODE_TRIANGLES is supported.");
205 }
206
207 GHOST_XrPrimitive primitive;
208
209 /* glTF vertex data is stored in an attribute dictionary.Loop through each attribute and insert
210 * it into the GHOST_XrPrimitive. */
211 for (const auto &[attr_name, accessor_idx] : gltf_primitive.attributes) {
212 load_attribute_accessor(gltf_model, attr_name, accessor_idx, primitive);
213 }
214
215 if (gltf_primitive.indices != -1) {
216 /* If indices are specified for the glTF primitive, read them into the GHOST_XrPrimitive. */
217 load_index_accessor(gltf_model, gltf_model.accessors.at(gltf_primitive.indices), primitive);
218 }
219
220 return primitive;
221}
222
226static void calc_node_transforms(const tinygltf::Node &gltf_node,
227 const float parent_transform[4][4],
228 float r_local_transform[4][4],
229 float r_world_transform[4][4])
230{
231 /* A node may specify either a 4x4 matrix or TRS (Translation - Rotation - Scale) values, but not
232 * both. */
233 if (gltf_node.matrix.size() == 16) {
234 const std::vector<double> &dm = gltf_node.matrix;
235 float m[4][4] = {{float(dm[0]), float(dm[1]), float(dm[2]), float(dm[3])},
236 {float(dm[4]), float(dm[5]), float(dm[6]), float(dm[7])},
237 {float(dm[8]), float(dm[9]), float(dm[10]), float(dm[11])},
238 {float(dm[12]), float(dm[13]), float(dm[14]), float(dm[15])}};
239 memcpy(r_local_transform, m, sizeof(float[4][4]));
240 }
241 else {
242 /* No matrix is present, so construct a matrix from the TRS values (each one is optional). */
243 std::vector<double> translation = gltf_node.translation;
244 std::vector<double> rotation = gltf_node.rotation;
245 std::vector<double> scale = gltf_node.scale;
246 Eigen::Matrix4f &m = *(Eigen::Matrix4f *)r_local_transform;
247 Eigen::Quaternionf q;
248 Eigen::Matrix3f scalemat;
249
250 if (translation.size() != 3) {
251 translation.resize(3);
252 translation[0] = translation[1] = translation[2] = 0.0;
253 }
254 if (rotation.size() != 4) {
255 rotation.resize(4);
256 rotation[0] = rotation[1] = rotation[2] = 0.0;
257 rotation[3] = 1.0;
258 }
259 if (scale.size() != 3) {
260 scale.resize(3);
261 scale[0] = scale[1] = scale[2] = 1.0;
262 }
263
264 q.w() = float(rotation[3]);
265 q.x() = float(rotation[0]);
266 q.y() = float(rotation[1]);
267 q.z() = float(rotation[2]);
268 q.normalize();
269
270 scalemat.setIdentity();
271 scalemat(0, 0) = float(scale[0]);
272 scalemat(1, 1) = float(scale[1]);
273 scalemat(2, 2) = float(scale[2]);
274
275 m.setIdentity();
276 m.block<3, 3>(0, 0) = q.toRotationMatrix() * scalemat;
277 m.block<3, 1>(0, 3) = Eigen::Vector3f(
278 float(translation[0]), float(translation[1]), float(translation[2]));
279 }
280
281 *(Eigen::Matrix4f *)r_world_transform = *(Eigen::Matrix4f *)parent_transform *
282 *(Eigen::Matrix4f *)r_local_transform;
283}
284
285static void load_node(const tinygltf::Model &gltf_model,
286 int gltf_node_id,
287 int32_t parent_idx,
288 const float parent_transform[4][4],
289 const std::string &parent_name,
290 const std::vector<XrControllerModelNodePropertiesMSFT> &node_properties,
291 std::vector<GHOST_XrControllerModelVertex> &vertices,
292 std::vector<uint32_t> &indices,
293 std::vector<GHOST_XrControllerModelComponent> &components,
294 std::vector<GHOST_XrControllerModelNode> &nodes,
295 std::vector<int32_t> &node_state_indices)
296{
297 const tinygltf::Node &gltf_node = gltf_model.nodes.at(gltf_node_id);
298 float world_transform[4][4];
299
300 GHOST_XrControllerModelNode &node = nodes.emplace_back();
301 const int32_t node_idx = int32_t(nodes.size() - 1);
302 node.parent_idx = parent_idx;
303 calc_node_transforms(gltf_node, parent_transform, node.local_transform, world_transform);
304
305 for (size_t i = 0; i < node_properties.size(); ++i) {
306 if ((node_state_indices[i] < 0) && (parent_name == node_properties[i].parentNodeName) &&
307 (gltf_node.name == node_properties[i].nodeName))
308 {
309 node_state_indices[i] = node_idx;
310 break;
311 }
312 }
313
314 if (gltf_node.mesh != -1) {
315 const tinygltf::Mesh &gltf_mesh = gltf_model.meshes.at(gltf_node.mesh);
316
317 GHOST_XrControllerModelComponent &component = components.emplace_back();
318 node.component_idx = components.size() - 1;
319 memcpy(component.transform, world_transform, sizeof(component.transform));
320 component.vertex_offset = vertices.size();
321 component.index_offset = indices.size();
322
323 for (const tinygltf::Primitive &gltf_primitive : gltf_mesh.primitives) {
324 /* Read the primitive data from the glTF buffers. */
325 const GHOST_XrPrimitive primitive = read_primitive(gltf_model, gltf_primitive);
326
327 const size_t start_vertex = vertices.size();
328 size_t offset = start_vertex;
329 size_t count = primitive.vertices.size();
330 vertices.resize(offset + count);
331 memcpy(vertices.data() + offset,
332 primitive.vertices.data(),
333 count * sizeof(decltype(primitive.vertices)::value_type));
334
335 offset = indices.size();
336 count = primitive.indices.size();
337 indices.resize(offset + count);
338 for (size_t i = 0; i < count; i += 3) {
339 indices[offset + i + 0] = start_vertex + primitive.indices[i + 0];
340 indices[offset + i + 1] = start_vertex + primitive.indices[i + 2];
341 indices[offset + i + 2] = start_vertex + primitive.indices[i + 1];
342 }
343 }
344
345 component.vertex_count = vertices.size() - component.vertex_offset;
346 component.index_count = indices.size() - component.index_offset;
347 }
348
349 /* Recursively load all children. */
350 for (const int child_node_id : gltf_node.children) {
351 load_node(gltf_model,
352 child_node_id,
353 node_idx,
354 world_transform,
355 gltf_node.name,
356 node_properties,
357 vertices,
358 indices,
359 components,
360 nodes,
361 node_state_indices);
362 }
363}
364
367/* -------------------------------------------------------------------- */
372static PFN_xrGetControllerModelKeyMSFT g_xrGetControllerModelKeyMSFT = nullptr;
373static PFN_xrLoadControllerModelMSFT g_xrLoadControllerModelMSFT = nullptr;
374static PFN_xrGetControllerModelPropertiesMSFT g_xrGetControllerModelPropertiesMSFT = nullptr;
375static PFN_xrGetControllerModelStateMSFT g_xrGetControllerModelStateMSFT = nullptr;
376static XrInstance g_instance = XR_NULL_HANDLE;
377
378static void init_controller_model_extension_functions(XrInstance instance)
379{
380 if (instance != g_instance) {
386 }
387
388 if (g_xrGetControllerModelKeyMSFT == nullptr) {
389 INIT_EXTENSION_FUNCTION(xrGetControllerModelKeyMSFT);
390 }
391 if (g_xrLoadControllerModelMSFT == nullptr) {
392 INIT_EXTENSION_FUNCTION(xrLoadControllerModelMSFT);
393 }
395 INIT_EXTENSION_FUNCTION(xrGetControllerModelPropertiesMSFT);
396 }
397 if (g_xrGetControllerModelStateMSFT == nullptr) {
398 INIT_EXTENSION_FUNCTION(xrGetControllerModelStateMSFT);
399 }
400}
401
404/* -------------------------------------------------------------------- */
410 const char *subaction_path_str)
411{
413
414 CHECK_XR(xrStringToPath(instance, subaction_path_str, &m_subaction_path),
415 (std::string("Failed to get user path \"") + subaction_path_str + "\".").data());
416}
417
419{
420 if (m_load_task.valid()) {
421 m_load_task.wait();
422 }
423}
424
425void GHOST_XrControllerModel::load(XrSession session)
426{
427 if (m_data_loaded || m_load_task.valid()) {
428 return;
429 }
430
431 /* Get model key. */
432 XrControllerModelKeyStateMSFT key_state{XR_TYPE_CONTROLLER_MODEL_KEY_STATE_MSFT};
433 CHECK_XR(g_xrGetControllerModelKeyMSFT(session, m_subaction_path, &key_state),
434 "Failed to get controller model key state.");
435
436 if (key_state.modelKey != XR_NULL_CONTROLLER_MODEL_KEY_MSFT) {
437 m_model_key = key_state.modelKey;
438 /* Load asynchronously. */
439 m_load_task = std::async(std::launch::async,
440 [&, session = session]() { return loadControllerModel(session); });
441 }
442}
443
444void GHOST_XrControllerModel::loadControllerModel(XrSession session)
445{
446 /* Load binary buffers. */
447 uint32_t buf_size = 0;
448 CHECK_XR(g_xrLoadControllerModelMSFT(session, m_model_key, 0, &buf_size, nullptr),
449 "Failed to get controller model buffer size.");
450
451 std::vector<uint8_t> buf((size_t(buf_size)));
452 CHECK_XR(g_xrLoadControllerModelMSFT(session, m_model_key, buf_size, &buf_size, buf.data()),
453 "Failed to load controller model binary buffers.");
454
455 /* Convert to glTF model. */
456 tinygltf::TinyGLTF gltf_loader;
457 tinygltf::Model gltf_model;
458 std::string err_msg;
459 {
460 /* Workaround for TINYGLTF_NO_STB_IMAGE define. Set custom image loader to prevent failure when
461 * parsing image data. */
462 auto load_img_func = [](tinygltf::Image *img,
463 const int p0,
464 std::string *p1,
465 std::string *p2,
466 int p3,
467 int p4,
468 const uchar *p5,
469 int p6,
470 void *user_pointer) -> bool {
471 (void)img;
472 (void)p0;
473 (void)p1;
474 (void)p2;
475 (void)p3;
476 (void)p4;
477 (void)p5;
478 (void)p6;
479 (void)user_pointer;
480 return true;
481 };
482 gltf_loader.SetImageLoader(load_img_func, nullptr);
483 }
484
485 if (!gltf_loader.LoadBinaryFromMemory(&gltf_model, &err_msg, nullptr, buf.data(), buf_size)) {
486 throw GHOST_XrException(("Failed to load glTF controller model: " + err_msg).c_str());
487 }
488
489 /* Get node properties. */
490 XrControllerModelPropertiesMSFT model_properties{XR_TYPE_CONTROLLER_MODEL_PROPERTIES_MSFT};
491 model_properties.nodeCapacityInput = 0;
492 CHECK_XR(g_xrGetControllerModelPropertiesMSFT(session, m_model_key, &model_properties),
493 "Failed to get controller model node properties count.");
494
495 std::vector<XrControllerModelNodePropertiesMSFT> node_properties(
496 model_properties.nodeCountOutput, {XR_TYPE_CONTROLLER_MODEL_NODE_PROPERTIES_MSFT});
497 model_properties.nodeCapacityInput = uint32_t(node_properties.size());
498 model_properties.nodeProperties = node_properties.data();
499 CHECK_XR(g_xrGetControllerModelPropertiesMSFT(session, m_model_key, &model_properties),
500 "Failed to get controller model node properties.");
501
502 m_node_state_indices.resize(node_properties.size(), -1);
503
504 /* Get mesh vertex data. */
505 const tinygltf::Scene &default_scene = gltf_model.scenes.at(
506 (gltf_model.defaultScene == -1) ? 0 : gltf_model.defaultScene);
507 const int32_t root_idx = -1;
508 const std::string root_name = "";
509 float root_transform[4][4] = {{0}};
510 root_transform[0][0] = root_transform[1][1] = root_transform[2][2] = root_transform[3][3] = 1.0f;
511
512 for (const int node_id : default_scene.nodes) {
513 load_node(gltf_model,
514 node_id,
515 root_idx,
516 root_transform,
517 root_name,
518 node_properties,
519 m_vertices,
520 m_indices,
521 m_components,
522 m_nodes,
523 m_node_state_indices);
524 }
525
526 m_data_loaded = true;
527}
528
530{
531 if (!m_data_loaded) {
532 return;
533 }
534
535 /* Get node states. */
536 XrControllerModelStateMSFT model_state{XR_TYPE_CONTROLLER_MODEL_STATE_MSFT};
537 model_state.nodeCapacityInput = 0;
538 CHECK_XR(g_xrGetControllerModelStateMSFT(session, m_model_key, &model_state),
539 "Failed to get controller model node state count.");
540
541 const uint32_t count = model_state.nodeCountOutput;
542 std::vector<XrControllerModelNodeStateMSFT> node_states(
543 count, {XR_TYPE_CONTROLLER_MODEL_NODE_STATE_MSFT});
544 model_state.nodeCapacityInput = count;
545 model_state.nodeStates = node_states.data();
546 CHECK_XR(g_xrGetControllerModelStateMSFT(session, m_model_key, &model_state),
547 "Failed to get controller model node states.");
548
549 /* Update node local transforms. */
550 assert(m_node_state_indices.size() == count);
551
552 for (uint32_t state_idx = 0; state_idx < count; ++state_idx) {
553 const int32_t &node_idx = m_node_state_indices[state_idx];
554 if (node_idx >= 0) {
555 const XrPosef &pose = node_states[state_idx].nodePose;
556 Eigen::Matrix4f &m = *(Eigen::Matrix4f *)m_nodes[node_idx].local_transform;
557 Eigen::Quaternionf q(
558 pose.orientation.w, pose.orientation.x, pose.orientation.y, pose.orientation.z);
559 m.setIdentity();
560 m.block<3, 3>(0, 0) = q.toRotationMatrix();
561 m.block<3, 1>(0, 3) = Eigen::Vector3f(pose.position.x, pose.position.y, pose.position.z);
562 }
563 }
564
565 /* Calculate component transforms (in world space). */
566 std::vector<Eigen::Matrix4f> world_transforms(m_nodes.size());
567 uint32_t i = 0;
568 for (const GHOST_XrControllerModelNode &node : m_nodes) {
569 world_transforms[i] = (node.parent_idx >= 0) ? world_transforms[node.parent_idx] *
570 *(Eigen::Matrix4f *)node.local_transform :
571 *(Eigen::Matrix4f *)node.local_transform;
572 if (node.component_idx >= 0) {
573 memcpy(m_components[node.component_idx].transform,
574 world_transforms[i].data(),
575 sizeof(m_components[node.component_idx].transform));
576 }
577 ++i;
578 }
579}
580
581void GHOST_XrControllerModel::getData(GHOST_XrControllerModelData &r_data)
582{
583 if (m_data_loaded) {
584 r_data.count_vertices = uint32_t(m_vertices.size());
585 r_data.vertices = m_vertices.data();
586 r_data.count_indices = uint32_t(m_indices.size());
587 r_data.indices = m_indices.data();
588 r_data.count_components = uint32_t(m_components.size());
589 r_data.components = m_components.data();
590 }
591 else {
592 r_data.count_vertices = 0;
593 r_data.vertices = nullptr;
594 r_data.count_indices = 0;
595 r_data.indices = nullptr;
596 r_data.count_components = 0;
597 r_data.components = nullptr;
598 }
599}
600
unsigned char uchar
static void init_controller_model_extension_functions(XrInstance instance)
static void load_node(const tinygltf::Model &gltf_model, int gltf_node_id, int32_t parent_idx, const float parent_transform[4][4], const std::string &parent_name, const std::vector< XrControllerModelNodePropertiesMSFT > &node_properties, std::vector< GHOST_XrControllerModelVertex > &vertices, std::vector< uint32_t > &indices, std::vector< GHOST_XrControllerModelComponent > &components, std::vector< GHOST_XrControllerModelNode > &nodes, std::vector< int32_t > &node_state_indices)
static void read_vertices(const tinygltf::Accessor &accessor, const tinygltf::BufferView &buffer_view, const tinygltf::Buffer &buffer, GHOST_XrPrimitive &primitive)
static XrInstance g_instance
static void validate_accessor(const tinygltf::Accessor &accessor, const tinygltf::BufferView &buffer_view, const tinygltf::Buffer &buffer, size_t byte_stride, size_t element_size)
static PFN_xrLoadControllerModelMSFT g_xrLoadControllerModelMSFT
static void read_indices(const tinygltf::Accessor &accessor, const tinygltf::BufferView &buffer_view, const tinygltf::Buffer &buffer, GHOST_XrPrimitive &primitive)
static PFN_xrGetControllerModelPropertiesMSFT g_xrGetControllerModelPropertiesMSFT
static void load_index_accessor(const tinygltf::Model &gltf_model, const tinygltf::Accessor &accessor, GHOST_XrPrimitive &primitive)
static PFN_xrGetControllerModelStateMSFT g_xrGetControllerModelStateMSFT
static PFN_xrGetControllerModelKeyMSFT g_xrGetControllerModelKeyMSFT
static GHOST_XrPrimitive read_primitive(const tinygltf::Model &gltf_model, const tinygltf::Primitive &gltf_primitive)
static void calc_node_transforms(const tinygltf::Node &gltf_node, const float parent_transform[4][4], float r_local_transform[4][4], float r_world_transform[4][4])
static void load_attribute_accessor(const tinygltf::Model &gltf_model, const std::string &attribute_name, int accessor_id, GHOST_XrPrimitive &primitive)
#define INIT_EXTENSION_FUNCTION(name)
#define CHECK_XR(call, error_msg)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object instance
void updateComponents(XrSession session)
GHOST_XrControllerModel(XrInstance instance, const char *subaction_path)
void getData(GHOST_XrControllerModelData &r_data)
draw_view in_light_buf[] float
int count
unsigned int uint32_t
Definition stdint.h:80
signed int int32_t
Definition stdint.h:77
unsigned char uint8_t
Definition stdint.h:78
std::vector< GHOST_XrControllerModelVertex > vertices
std::vector< uint32_t > indices