1 // Copyright 2013, Fredrik Hultin.
2 // Copyright 2013, Jakob Bornecrantz.
3 // SPDX-License-Identifier: BSL-1.0
4 /*
5  * OpenHMD - Free and Open Source API and drivers for immersive technology.
6  */
7 
8 /* Main Lib Implementation */
9 
10 
11 #include "openhmdi.h"
12 #include "shaders.h"
13 #include <stdlib.h>
14 #include <string.h>
15 #include <stdio.h>
16 
17 // Running automatic updates at 1000 Hz
18 #define AUTOMATIC_UPDATE_SLEEP (1.0 / 1000.0)
19 
ohmd_ctx_create(void)20 OHMD_APIENTRYDLL ohmd_context* OHMD_APIENTRY ohmd_ctx_create(void)
21 {
22 	ohmd_context* ctx = calloc(1, sizeof(ohmd_context));
23 	if(!ctx){
24 		LOGE("could not allocate RAM for context");
25 		return NULL;
26 	}
27 
28 	ohmd_monotonic_init(ctx);
29 
30 #if DRIVER_OCULUS_RIFT
31 	ctx->drivers[ctx->num_drivers++] = ohmd_create_oculus_rift_drv(ctx);
32 #endif
33 
34 #if DRIVER_DEEPOON
35 	ctx->drivers[ctx->num_drivers++] = ohmd_create_deepoon_drv(ctx);
36 #endif
37 
38 #if DRIVER_HTC_VIVE
39 	ctx->drivers[ctx->num_drivers++] = ohmd_create_htc_vive_drv(ctx);
40 #endif
41 
42 #if DRIVER_WMR
43 	ctx->drivers[ctx->num_drivers++] = ohmd_create_wmr_drv(ctx);
44 #endif
45 
46 #if DRIVER_PSVR
47 	ctx->drivers[ctx->num_drivers++] = ohmd_create_psvr_drv(ctx);
48 #endif
49 
50 #if DRIVER_NOLO
51 	ctx->drivers[ctx->num_drivers++] = ohmd_create_nolo_drv(ctx);
52 #endif
53 
54 #if DRIVER_XGVR
55 	ctx->drivers[ctx->num_drivers++] = ohmd_create_xgvr_drv(ctx);
56 #endif
57 
58 #if DRIVER_VRTEK
59 	ctx->drivers[ctx->num_drivers++] = ohmd_create_vrtek_drv(ctx);
60 #endif
61 
62 #if DRIVER_ANDROID
63 	ctx->drivers[ctx->num_drivers++] = ohmd_create_android_drv(ctx);
64 #endif
65 
66 #if DRIVER_EXTERNAL
67 	ctx->drivers[ctx->num_drivers++] = ohmd_create_external_drv(ctx);
68 #endif
69 	// add dummy driver last to make it the lowest priority
70 
71 	ctx->update_request_quit = false;
72 
73 	return ctx;
74 }
75 
ohmd_ctx_destroy(ohmd_context * ctx)76 OHMD_APIENTRYDLL void OHMD_APIENTRY ohmd_ctx_destroy(ohmd_context* ctx)
77 {
78 	ctx->update_request_quit = true;
79 
80 	for(int i = 0; i < ctx->num_active_devices; i++){
81 		ctx->active_devices[i]->close(ctx->active_devices[i]);
82 	}
83 
84 	for(int i = 0; i < ctx->num_drivers; i++){
85 		ctx->drivers[i]->destroy(ctx->drivers[i]);
86 	}
87 
88 	if(ctx->update_thread){
89 		ohmd_destroy_thread(ctx->update_thread);
90 		ohmd_destroy_mutex(ctx->update_mutex);
91 	}
92 
93 	free(ctx);
94 }
95 
ohmd_ctx_update(ohmd_context * ctx)96 OHMD_APIENTRYDLL void OHMD_APIENTRY ohmd_ctx_update(ohmd_context* ctx)
97 {
98 	for(int i = 0; i < ctx->num_active_devices; i++){
99 		ohmd_device* dev = ctx->active_devices[i];
100 		if(!dev->settings.automatic_update && dev->update)
101 			dev->update(dev);
102 
103 		ohmd_lock_mutex(ctx->update_mutex);
104 		dev->getf(dev, OHMD_POSITION_VECTOR, (float*)&dev->position);
105 		dev->getf(dev, OHMD_ROTATION_QUAT, (float*)&dev->rotation);
106 		ohmd_unlock_mutex(ctx->update_mutex);
107 	}
108 }
109 
ohmd_ctx_get_error(ohmd_context * ctx)110 OHMD_APIENTRYDLL const char* OHMD_APIENTRY ohmd_ctx_get_error(ohmd_context* ctx)
111 {
112 	return ctx->error_msg;
113 }
114 
ohmd_ctx_probe(ohmd_context * ctx)115 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_ctx_probe(ohmd_context* ctx)
116 {
117 	memset(&ctx->list, 0, sizeof(ohmd_device_list));
118 	for(int i = 0; i < ctx->num_drivers; i++){
119 		ctx->drivers[i]->get_device_list(ctx->drivers[i], &ctx->list);
120 	}
121 
122 	return ctx->list.num_devices;
123 }
124 
ohmd_gets(ohmd_string_description type,const char ** out)125 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_gets(ohmd_string_description type, const char ** out)
126 {
127 	switch(type){
128 	case OHMD_GLSL_DISTORTION_VERT_SRC:
129 		*out = distortion_vert;
130 		return OHMD_S_OK;
131 	case OHMD_GLSL_DISTORTION_FRAG_SRC:
132 		*out = distortion_frag;
133 		return OHMD_S_OK;
134 	case OHMD_GLSL_330_DISTORTION_VERT_SRC:
135 		*out = distortion_vert_330;
136 		return OHMD_S_OK;
137 	case OHMD_GLSL_330_DISTORTION_FRAG_SRC:
138 		*out = distortion_frag_330;
139 		return OHMD_S_OK;
140 	case OHMD_GLSL_ES_DISTORTION_VERT_SRC:
141 		*out = distortion_vert_es;
142 		return OHMD_S_OK;
143 	case OHMD_GLSL_ES_DISTORTION_FRAG_SRC:
144 		*out = distortion_frag_es;
145 		return OHMD_S_OK;
146 	default:
147 		return OHMD_S_UNSUPPORTED;
148 	}
149 }
150 
ohmd_list_gets(ohmd_context * ctx,int index,ohmd_string_value type)151 OHMD_APIENTRYDLL const char* OHMD_APIENTRY ohmd_list_gets(ohmd_context* ctx, int index, ohmd_string_value type)
152 {
153 	if(index >= ctx->list.num_devices)
154 		return NULL;
155 
156 	switch(type){
157 	case OHMD_VENDOR:
158 		return ctx->list.devices[index].vendor;
159 	case OHMD_PRODUCT:
160 		return ctx->list.devices[index].product;
161 	case OHMD_PATH:
162 		return ctx->list.devices[index].path;
163 	default:
164 		return NULL;
165 	}
166 }
167 
ohmd_list_geti(ohmd_context * ctx,int index,ohmd_int_value type,int * out)168 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_list_geti(ohmd_context* ctx, int index, ohmd_int_value type, int* out)
169 {
170 	if(index >= ctx->list.num_devices)
171 		return OHMD_S_INVALID_PARAMETER;
172 
173 	switch(type){
174 	case OHMD_DEVICE_CLASS:
175 		*out = ctx->list.devices[index].device_class;
176 		return OHMD_S_OK;
177 
178 	case OHMD_DEVICE_FLAGS:
179 		*out = ctx->list.devices[index].device_flags;
180 		return OHMD_S_OK;
181 
182 	default:
183 		return OHMD_S_INVALID_PARAMETER;
184 	}
185 }
186 
ohmd_update_thread(void * arg)187 static unsigned int ohmd_update_thread(void* arg)
188 {
189 	ohmd_context* ctx = (ohmd_context*)arg;
190 
191 	while(!ctx->update_request_quit)
192 	{
193 		ohmd_lock_mutex(ctx->update_mutex);
194 
195 		for(int i = 0; i < ctx->num_active_devices; i++){
196 			if(ctx->active_devices[i]->settings.automatic_update && ctx->active_devices[i]->update)
197 				ctx->active_devices[i]->update(ctx->active_devices[i]);
198 		}
199 
200 		ohmd_unlock_mutex(ctx->update_mutex);
201 
202 		ohmd_sleep(AUTOMATIC_UPDATE_SLEEP);
203 	}
204 
205 	return 0;
206 }
207 
ohmd_set_up_update_thread(ohmd_context * ctx)208 static void ohmd_set_up_update_thread(ohmd_context* ctx)
209 {
210 	if(!ctx->update_thread){
211 		ctx->update_mutex = ohmd_create_mutex(ctx);
212 		ctx->update_thread = ohmd_create_thread(ctx, ohmd_update_thread, ctx);
213 	}
214 }
215 
ohmd_list_open_device_s(ohmd_context * ctx,int index,ohmd_device_settings * settings)216 OHMD_APIENTRYDLL ohmd_device* OHMD_APIENTRY ohmd_list_open_device_s(ohmd_context* ctx, int index, ohmd_device_settings* settings)
217 {
218 	ohmd_lock_mutex(ctx->update_mutex);
219 
220 	if(index >= 0 && index < ctx->list.num_devices){
221 
222 		ohmd_device_desc* desc = &ctx->list.devices[index];
223 		ohmd_driver* driver = (ohmd_driver*)desc->driver_ptr;
224 		ohmd_device* device = driver->open_device(driver, desc);
225 
226 		if (device == NULL) {
227 			ohmd_set_error(ctx, "Could not open device with index: %d, check device permissions?", index);
228 			ohmd_unlock_mutex(ctx->update_mutex);
229 			return NULL;
230 		}
231 
232 		device->rotation_correction.w = 1;
233 
234 		device->settings = *settings;
235 
236 		device->ctx = ctx;
237 		device->active_device_idx = ctx->num_active_devices;
238 		ctx->active_devices[ctx->num_active_devices++] = device;
239 
240 		ohmd_unlock_mutex(ctx->update_mutex);
241 
242 		if(device->settings.automatic_update)
243 			ohmd_set_up_update_thread(ctx);
244 
245 		return device;
246 	}
247 
248 	ohmd_unlock_mutex(ctx->update_mutex);
249 
250 	ohmd_set_error(ctx, "no device with index: %d", index);
251 	return NULL;
252 }
253 
ohmd_list_open_device(ohmd_context * ctx,int index)254 OHMD_APIENTRYDLL ohmd_device* OHMD_APIENTRY ohmd_list_open_device(ohmd_context* ctx, int index)
255 {
256 	ohmd_device_settings settings;
257 
258 	settings.automatic_update = true;
259 
260 	return ohmd_list_open_device_s(ctx, index, &settings);
261 }
262 
ohmd_close_device(ohmd_device * device)263 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_close_device(ohmd_device* device)
264 {
265 	ohmd_lock_mutex(device->ctx->update_mutex);
266 
267 	ohmd_context* ctx = device->ctx;
268 	int idx = device->active_device_idx;
269 
270 	memmove(ctx->active_devices + idx, ctx->active_devices + idx + 1,
271 		sizeof(ohmd_device*) * (ctx->num_active_devices - idx - 1));
272 
273 	device->close(device);
274 
275 	ctx->num_active_devices--;
276 
277 	for(int i = idx; i < ctx->num_active_devices; i++)
278 		ctx->active_devices[i]->active_device_idx--;
279 
280 	ohmd_unlock_mutex(ctx->update_mutex);
281 
282 	return OHMD_S_OK;
283 }
284 
ohmd_device_getf_unp(ohmd_device * device,ohmd_float_value type,float * out)285 static int ohmd_device_getf_unp(ohmd_device* device, ohmd_float_value type, float* out)
286 {
287 	switch(type){
288 	case OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX: {
289 			quatf rot = device->rotation;
290 			oquatf_mult_me(&rot, &device->rotation_correction);
291 			mat4x4f central_view, eye_shift, result;
292 			omat4x4f_init_look_at(&central_view, &rot, &device->position);
293 			omat4x4f_init_translate(&eye_shift, +(device->properties.ipd / 2.0f), 0.0f, 0.0f);
294 			omat4x4f_mult(&eye_shift, &central_view, &result);
295 			omat4x4f_transpose(&result, (mat4x4f*)out);
296 			return OHMD_S_OK;
297 		}
298 	case OHMD_RIGHT_EYE_GL_MODELVIEW_MATRIX: {
299 			quatf rot = device->rotation;
300 			oquatf_mult_me(&rot, &device->rotation_correction);
301 			mat4x4f central_view, eye_shift, result;
302 			omat4x4f_init_look_at(&central_view, &rot, &device->position);
303 			omat4x4f_init_translate(&eye_shift, -(device->properties.ipd / 2.0f), 0.0f, 0.0f);
304 			omat4x4f_mult(&eye_shift, &central_view, &result);
305 			omat4x4f_transpose(&result, (mat4x4f*)out);
306 			return OHMD_S_OK;
307 		}
308 	case OHMD_LEFT_EYE_GL_PROJECTION_MATRIX:
309 		omat4x4f_transpose(&device->properties.proj_left, (mat4x4f*)out);
310 		return OHMD_S_OK;
311 	case OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX:
312 		omat4x4f_transpose(&device->properties.proj_right, (mat4x4f*)out);
313 		return OHMD_S_OK;
314 
315 	case OHMD_SCREEN_HORIZONTAL_SIZE:
316 		*out = device->properties.hsize;
317 		return OHMD_S_OK;
318 	case OHMD_SCREEN_VERTICAL_SIZE:
319 		*out = device->properties.vsize;
320 		return OHMD_S_OK;
321 
322 	case OHMD_LENS_HORIZONTAL_SEPARATION:
323 		*out = device->properties.lens_sep;
324 		return OHMD_S_OK;
325 	case OHMD_LENS_VERTICAL_POSITION:
326 		*out = device->properties.lens_vpos;
327 		return OHMD_S_OK;
328 
329 	case OHMD_RIGHT_EYE_FOV:
330 	case OHMD_LEFT_EYE_FOV:
331 		*out = device->properties.fov;
332 		return OHMD_S_OK;
333 	case OHMD_RIGHT_EYE_ASPECT_RATIO:
334 	case OHMD_LEFT_EYE_ASPECT_RATIO:
335 		*out = device->properties.ratio;
336 		return OHMD_S_OK;
337 
338 	case OHMD_EYE_IPD:
339 		*out = device->properties.ipd;
340 		return OHMD_S_OK;
341 
342 	case OHMD_PROJECTION_ZFAR:
343 		*out = device->properties.zfar;
344 		return OHMD_S_OK;
345 	case OHMD_PROJECTION_ZNEAR:
346 		*out = device->properties.znear;
347 		return OHMD_S_OK;
348 
349 	case OHMD_ROTATION_QUAT:
350 	{
351 		*(quatf*)out = device->rotation;
352 
353 		oquatf_mult_me((quatf*)out, &device->rotation_correction);
354 		return OHMD_S_OK;
355 	}
356 	case OHMD_POSITION_VECTOR:
357 	{
358 		*(vec3f*)out = device->position;
359 		for(int i = 0; i < 3; i++)
360 			out[i] += device->position_correction.arr[i];
361 
362 		return OHMD_S_OK;
363 	}
364 	case OHMD_UNIVERSAL_DISTORTION_K: {
365 		for (int i = 0; i < 4; i++) {
366 			out[i] = device->properties.universal_distortion_k[i];
367 		}
368 		return OHMD_S_OK;
369 	}
370 	case OHMD_UNIVERSAL_ABERRATION_K: {
371 		for (int i = 0; i < 3; i++) {
372 			out[i] = device->properties.universal_aberration_k[i];
373 		}
374 		return OHMD_S_OK;
375 	}
376 	default:
377 		return device->getf(device, type, out);
378 	}
379 }
380 
ohmd_device_getf(ohmd_device * device,ohmd_float_value type,float * out)381 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_getf(ohmd_device* device, ohmd_float_value type, float* out)
382 {
383 	ohmd_lock_mutex(device->ctx->update_mutex);
384 	int ret = ohmd_device_getf_unp(device, type, out);
385 	ohmd_unlock_mutex(device->ctx->update_mutex);
386 
387 	return ret;
388 }
389 
ohmd_device_setf_unp(ohmd_device * device,ohmd_float_value type,const float * in)390 int ohmd_device_setf_unp(ohmd_device* device, ohmd_float_value type, const float* in)
391 {
392 	switch(type){
393 	case OHMD_EYE_IPD:
394 		device->properties.ipd = *in;
395 		return OHMD_S_OK;
396 	case OHMD_PROJECTION_ZFAR:
397 		device->properties.zfar = *in;
398 		return OHMD_S_OK;
399 	case OHMD_PROJECTION_ZNEAR:
400 		device->properties.znear = *in;
401 		return OHMD_S_OK;
402 	case OHMD_ROTATION_QUAT:
403 		{
404 			// adjust rotation correction
405 			quatf q;
406 			int ret = device->getf(device, OHMD_ROTATION_QUAT, (float*)&q);
407 
408 			if(ret != 0){
409 				return ret;
410 			}
411 
412 			oquatf_diff(&q, (quatf*)in, &device->rotation_correction);
413 			return OHMD_S_OK;
414 		}
415 	case OHMD_POSITION_VECTOR:
416 		{
417 			// adjust position correction
418 			vec3f v;
419 			int ret = device->getf(device, OHMD_POSITION_VECTOR, (float*)&v);
420 
421 			if(ret != 0){
422 				return ret;
423 			}
424 
425 			for(int i = 0; i < 3; i++)
426 				device->position_correction.arr[i] = in[i] - v.arr[i];
427 
428 			return OHMD_S_OK;
429 		}
430 	case OHMD_EXTERNAL_SENSOR_FUSION:
431 		{
432 			if(device->setf == NULL)
433 				return OHMD_S_UNSUPPORTED;
434 
435 			return device->setf(device, type, in);
436 		}
437 	default:
438 		return OHMD_S_INVALID_PARAMETER;
439 	}
440 }
441 
ohmd_device_setf(ohmd_device * device,ohmd_float_value type,const float * in)442 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_setf(ohmd_device* device, ohmd_float_value type, const float* in)
443 {
444 	ohmd_lock_mutex(device->ctx->update_mutex);
445 	int ret = ohmd_device_setf_unp(device, type, in);
446 	ohmd_unlock_mutex(device->ctx->update_mutex);
447 
448 	return ret;
449 }
450 
ohmd_device_geti(ohmd_device * device,ohmd_int_value type,int * out)451 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_geti(ohmd_device* device, ohmd_int_value type, int* out)
452 {
453 	switch(type){
454 		case OHMD_SCREEN_HORIZONTAL_RESOLUTION:
455 			*out = device->properties.hres;
456 			return OHMD_S_OK;
457 
458 		case OHMD_SCREEN_VERTICAL_RESOLUTION:
459 			*out = device->properties.vres;
460 			return OHMD_S_OK;
461 
462 		case OHMD_CONTROL_COUNT:
463 			*out = device->properties.control_count;
464 			return OHMD_S_OK;
465 
466 		case OHMD_CONTROLS_TYPES:
467 			memcpy(out, device->properties.controls_types, device->properties.control_count * sizeof(int));
468 			return OHMD_S_OK;
469 
470 		case OHMD_CONTROLS_HINTS:
471 			memcpy(out, device->properties.controls_hints, device->properties.control_count * sizeof(int));
472 			return OHMD_S_OK;
473 
474 		default:
475 				return OHMD_S_INVALID_PARAMETER;
476 	}
477 }
478 
ohmd_device_seti(ohmd_device * device,ohmd_int_value type,const int * in)479 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_seti(ohmd_device* device, ohmd_int_value type, const int* in)
480 {
481 	switch(type){
482 	default:
483 		return OHMD_S_INVALID_PARAMETER;
484 	}
485 }
486 
487 
ohmd_device_set_data_unp(ohmd_device * device,ohmd_data_value type,const void * in)488 int ohmd_device_set_data_unp(ohmd_device* device, ohmd_data_value type, const void* in)
489 {
490     switch(type){
491     case OHMD_DRIVER_DATA:
492 			device->set_data(device, OHMD_DRIVER_DATA, in);
493 			return OHMD_S_OK;
494 
495     case OHMD_DRIVER_PROPERTIES:
496 			device->set_data(device, OHMD_DRIVER_PROPERTIES, in);
497 			return OHMD_S_OK;
498 
499     default:
500       return OHMD_S_INVALID_PARAMETER;
501     }
502 }
503 
ohmd_device_set_data(ohmd_device * device,ohmd_data_value type,const void * in)504 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_set_data(ohmd_device* device, ohmd_data_value type, const void* in)
505 {
506 	ohmd_lock_mutex(device->ctx->update_mutex);
507 	int ret = ohmd_device_set_data_unp(device, type, in);
508 	ohmd_unlock_mutex(device->ctx->update_mutex);
509 
510 	return ret;
511 }
512 
ohmd_device_settings_seti(ohmd_device_settings * settings,ohmd_int_settings key,const int * val)513 OHMD_APIENTRYDLL ohmd_status OHMD_APIENTRY ohmd_device_settings_seti(ohmd_device_settings* settings, ohmd_int_settings key, const int* val)
514 {
515 	switch(key){
516 	case OHMD_IDS_AUTOMATIC_UPDATE:
517 		settings->automatic_update = val[0] == 0 ? false : true;
518 		return OHMD_S_OK;
519 
520 	default:
521 		return OHMD_S_INVALID_PARAMETER;
522 	}
523 }
524 
ohmd_device_settings_create(ohmd_context * ctx)525 OHMD_APIENTRYDLL ohmd_device_settings* OHMD_APIENTRY ohmd_device_settings_create(ohmd_context* ctx)
526 {
527 	return ohmd_alloc(ctx, sizeof(ohmd_device_settings));
528 }
529 
ohmd_device_settings_destroy(ohmd_device_settings * settings)530 OHMD_APIENTRYDLL void OHMD_APIENTRY ohmd_device_settings_destroy(ohmd_device_settings* settings)
531 {
532 	free(settings);
533 }
534 
ohmd_allocfn(ohmd_context * ctx,const char * e_msg,size_t size)535 void* ohmd_allocfn(ohmd_context* ctx, const char* e_msg, size_t size)
536 {
537 	void* ret = calloc(1, size);
538 	if(!ret)
539 		ohmd_set_error(ctx, "%s", e_msg);
540 	return ret;
541 }
542 
ohmd_set_default_device_properties(ohmd_device_properties * props)543 void ohmd_set_default_device_properties(ohmd_device_properties* props)
544 {
545 	props->ipd = 0.061f;
546 	props->znear = 0.1f;
547 	props->zfar = 1000.0f;
548 	ohmd_set_universal_distortion_k(props, 0, 0, 0, 1);
549 	ohmd_set_universal_aberration_k(props, 1.0, 1.0, 1.0);
550 }
551 
ohmd_calc_default_proj_matrices(ohmd_device_properties * props)552 void ohmd_calc_default_proj_matrices(ohmd_device_properties* props)
553 {
554 	mat4x4f proj_base; // base projection matrix
555 
556 	// Calculate where the lens is on each screen,
557 	// and with the given value offset the projection matrix.
558 	float screen_center = props->hsize / 4.0f;
559 	float lens_shift = screen_center - props->lens_sep / 2.0f;
560 	// XXX: on CV1, props->hsize > props->lens_sep / 2.0,
561 	// I am not sure about the implications, but just taking the absolute
562 	// value of the offset seems to work.
563 	float proj_offset = fabs(4.0f * lens_shift / props->hsize);
564 
565 	// Setup the base projection matrix. Each eye mostly have the
566 	// same projection matrix with the exception of the offset.
567 	omat4x4f_init_perspective(&proj_base, props->fov, props->ratio, props->znear, props->zfar);
568 
569 	// Setup the two adjusted projection matrices. Each is setup to deal
570 	// with the fact that the lens is not in the center of the screen.
571 	// These matrices only change of the hardware changes, so static.
572 	mat4x4f translate;
573 
574 	omat4x4f_init_translate(&translate, proj_offset, 0, 0);
575 	omat4x4f_mult(&translate, &proj_base, &props->proj_left);
576 
577 	omat4x4f_init_translate(&translate, -proj_offset, 0, 0);
578 	omat4x4f_mult(&translate, &proj_base, &props->proj_right);
579 }
580 
ohmd_set_universal_distortion_k(ohmd_device_properties * props,float a,float b,float c,float d)581 void ohmd_set_universal_distortion_k(ohmd_device_properties* props, float a, float b, float c, float d)
582 {
583 	props->universal_distortion_k[0] = a;
584 	props->universal_distortion_k[1] = b;
585 	props->universal_distortion_k[2] = c;
586 	props->universal_distortion_k[3] = d;
587 }
588 
ohmd_set_universal_aberration_k(ohmd_device_properties * props,float r,float g,float b)589 void ohmd_set_universal_aberration_k(ohmd_device_properties* props, float r, float g, float b)
590 {
591 	props->universal_aberration_k[0] = r;
592 	props->universal_aberration_k[1] = g;
593 	props->universal_aberration_k[2] = b;
594 }
595 
ohmd_monotonic_per_sec(ohmd_context * ctx)596 uint64_t ohmd_monotonic_per_sec(ohmd_context* ctx)
597 {
598 	return ctx->monotonic_ticks_per_sec;
599 }
600 
601 /*
602  * Grabbed from druntime, good thing it's BOOST v1.0 as well.
603  */
ohmd_monotonic_conv(uint64_t ticks,uint64_t srcTicksPerSecond,uint64_t dstTicksPerSecond)604 uint64_t ohmd_monotonic_conv(uint64_t ticks, uint64_t srcTicksPerSecond, uint64_t dstTicksPerSecond)
605 {
606 	// This would be more straightforward with floating point arithmetic,
607 	// but we avoid it here in order to avoid the rounding errors that that
608 	// introduces. Also, by splitting out the units in this way, we're able
609 	// to deal with much larger values before running into problems with
610 	// integer overflow.
611 	return ticks / srcTicksPerSecond * dstTicksPerSecond +
612 		ticks % srcTicksPerSecond * dstTicksPerSecond / srcTicksPerSecond;
613 }
614 
ohmd_get_version(int * out_major,int * out_minor,int * out_patch)615 void ohmd_get_version(int* out_major, int* out_minor, int* out_patch)
616 {
617 	*out_major = OHMD_VERSION_MAJOR;
618 	*out_minor = OHMD_VERSION_MINOR;
619 	*out_patch = OHMD_VERSION_PATCH;
620 }
621 
ohmd_require_version(int major,int minor,int patch)622 ohmd_status ohmd_require_version(int major, int minor, int patch)
623 {
624 	int curr_major, curr_minor, curr_patch;
625 	ohmd_get_version(&curr_major, &curr_minor, &curr_patch);
626 
627 	if(curr_major != major){
628 		// require same major version
629 		return OHMD_S_UNSUPPORTED;
630 	}
631 
632 	if(curr_minor == minor){
633 		// check patch version if the required minor version matches current
634 		if(curr_patch < patch){
635 			// fail is patch is too low
636 			return OHMD_S_UNSUPPORTED;
637 		}
638 	}
639 	else if(curr_minor < minor)
640 	{
641 		// fail if minor is too low
642 		return OHMD_S_UNSUPPORTED;
643 	}
644 
645 	return OHMD_S_OK;
646 }
647