commit c72bf3be8e19cf44b414fe4ef4758e52a54ecf52 Author: mrbesen Date: Fri Apr 9 18:47:08 2021 +0200 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c2998e --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.bin +*.out +build/ +*.exe +*.dll +.gdb_history + +*.so +*.bmp +*.d + +test +.vscode/settings.json + +chac +chac_strip +log.txt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..af5cc60 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "thirdparty/openvr"] + path = thirdparty/openvr + url = https://github.com/ValveSoftware/openvr +[submodule "thirdparty/rawdraw"] + path = thirdparty/rawdraw + url = git@github.com:cntools/rawdraw.git diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..f22ac9a --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,41 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/thirdparty/Log/", + "${workspaceFolder}/thirdparty/openvr/headers", + "${workspaceFolder}/thirdparty/libmrbesen/inc", + "${workspaceFolder}/inc/", + "${workspaceFolder}/thirdparty/rawdraw/", + "${workspaceFolder}/thirdparty/openvr/headers" + ], + "defines": ["ENABLE_DEBUG=1"], + "compilerPath": "/usr/bin/g++", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-x64" + }, + { + "name": "Win64", + "includePath": [ + "${workspaceFolder}/thirdparty/Log/", + "${workspaceFolder}/thirdparty/openvr/headers", + "${workspaceFolder}/thirdparty/libmrbesen/inc", + "${workspaceFolder}/thirdparty/FreeImage/Dist/x64", + "${workspaceFolder}/thirdparty/glew-2.1.0/include", + "${workspaceFolder}/thirdparty/glfw-3.3.2.bin.WIN64/include", + "${workspaceFolder}/thirdparty/", + "${workspaceFolder}/inc/", + "${workspaceFolder}/thirdparty/rawdraw/", + "${workspaceFolder}/thirdparty/openvr/headers" + ], + "defines": ["ENABLE_DEBUG=1"], + "compilerPath": "C:/mingw64/bin/g++.exe", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "gcc-x64" + } + ], + "version": 4 +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6c92abd --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,71 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debuggen (gdb)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/chac", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [{"name": "LD_LIBRARY_PATH", "value": "thirdparty/openvr/bin/linux64/" }], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Automatische Strukturierung und Einrückung für \"gdb\" aktivieren", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "make all", + "miDebuggerPath": "/usr/bin/gdb" + }, + { + "name": "Debuggen WIN64 (gdb)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/chac.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Automatische Strukturierung und Einrückung für \"gdb\" aktivieren", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "make win64", + "miDebuggerPath": "C:/mingw64/bin/gdb.exe" + }, + { + "name": "test Debuggen (gdb)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/test", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Automatische Strukturierung und Einrückung für \"gdb\" aktivieren", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "make test", + "miDebuggerPath": "/usr/bin/gdb" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..1fcde2a --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,43 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "make all", + "type": "shell", + "command": "make -j all", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "make win64", + "type": "shell", + "command": "mingw32-make.exe -f Makefile.win -j chac.exe", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "make clean", + "type": "shell", + "command": "make clean", + "problemMatcher": [] + }, + { + "label": "make test", + "type": "shell", + "command": "make -j test", + "problemMatcher": ["$gcc"], + } + ] +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ee70726 --- /dev/null +++ b/Makefile @@ -0,0 +1,75 @@ +# Author Yannis Gerlach + +# `make clean all` nicht mit -j verwenden! -> race condition im make file +# statdessen: `make clean; make all -j` verwenden + +NAME = chac +NAMETEST = test +CFLAGS = -O2 -g -pipe -Wall -Wextra -Wno-unused-parameter -Wpedantic # -rdynamic +CXX = gcc +SRCF = src/ +BUILDDIR = build/ +TESTF = tests/ +DEPF = $(BUILDDIR)deps/ +INCF = inc/ +INCFS = $(shell find $(INCF) -type d) + +TPF = ./thirdparty/ +OPENVRPATH ?= $(TPF)openvr/bin/linux64/ +RAWDRAWF = $(TPF)rawdraw/ + +INCLUDES += -I$(RAWDRAWF) -I$(TPF)openvr/headers/ + +LDFLAGS = -lX11 -lm -lpthread -lXinerama -lXext -lGL -L$(TPF)openvr/bin/linux64/ -lopenvr_api +DEFINES = ENABLE_DEBUG=1 + +SRCFILES = $(shell find $(SRCF) -name "*.c") +OBJFILES = $(patsubst $(SRCF)%, $(BUILDDIR)%, $(patsubst %.c, %.o, $(SRCFILES))) +DEPFILES = $(wildcard $(DEPF)*.d) + +SOURCEDIRS = $(shell find $(SRCF) -type d -printf "%p/\n") +BUILDDIRS = $(patsubst $(SRCF)%, $(BUILDDIR)%, $(SOURCEDIRS)) + +OBJFILESTEST = $(filter-out $(BUILDDIR)main.o, $(OBJFILES)) + +INCLUDES += $(addprefix -I, $(INCFS)) +CFLAGS += $(addprefix -D, $(DEFINES)) + +all: $(NAME) # runtest + +$(NAME): $(BUILDDIRS) $(DEPF) $(OBJFILES) + @echo "Linking $@" + $(CXX) $(CFLAGS) -o $@ $(filter %.o, $^) $(filter %.a, $^) $(LDFLAGS) + +$(BUILDDIR)%.o: $(SRCF)%.c + @echo "Compiling: $@" + @$(CXX) $(CFLAGS) $(INCLUDES) $< -MM -MT $@ > $(DEPF)$(subst /,_,$*).d + @$(CXX) -c -o $@ $(CFLAGS) $(INCLUDES) $< + +$(NAME)_strip: $(NAME) + @echo "Strip $<" + @strip -o $@ $< + +%/: + mkdir -p $@ + +clean-depends: + $(RM) -r $(DEPF) + +clean: + $(RM) -r $(NAME) $(BUILDDIR) $(NAMETEST) $(NAME)_strip + +$(NAMETEST): $(BUILDDIRS) $(DEPF) $(TESTF)*.c $(OBJFILESTEST) + @echo "Compiling tests" + @$(CXX) -o $@ $(filter %.o, $^) $(filter %.c, $^) $(CFLAGS) -I$(SRCF) $(INCLUDES) $(LDFLAGS) + +runtest: $(NAMETEST) + @echo "Running tests" + ./$< + +# fix assets : +# find assets/ -name '*.*' -exec sh -c 'a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "$0" "$a" ' {} \; + +.PHONY: clean all $(NAMETEST) clean-depends runtest + +include $(DEPFILES) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..9bd9cb1 --- /dev/null +++ b/src/main.c @@ -0,0 +1,209 @@ +// this is mostly copied / inspired by this: https://gist.github.com/cnlohr/6e452dc6cc2df7f48d5ade66059358d9 +// made for this video: https://www.youtube.com/watch?v=r6kM3tR03g4 +// thanks @cnlohr for rawdraw, alot of other functions, my motivation and the idea + + +#include +#include +#include + +#define CNFG_IMPLEMENTATION +#define CNFGOGL +#include "os_generic.h" +#include "CNFG.h" + +#undef EXTERN_C +#include "openvr_capi.h" + +intptr_t VR_InitInternal( EVRInitError *peError, EVRApplicationType eType ); +void VR_ShutdownInternal(); +bool VR_IsHmdPresent(); +intptr_t VR_GetGenericInterface( const char *pchInterfaceVersion, EVRInitError *peError ); +bool VR_IsRuntimeInstalled(); +const char * VR_GetVRInitErrorAsSymbol( EVRInitError error ); +const char * VR_GetVRInitErrorAsEnglishDescription( EVRInitError error ); + +//rawdraw callbacks +void HandleKey( int keycode, int bDown ) { } +void HandleButton( int x, int y, int button, int bDown ) { } +void HandleMotion( int x, int y, int mask ) { } +void HandleDestroy() { + exit(0); +} + +// This function was copy-pasted from cnovr. +void* CNOVRGetOpenVRFunctionTable( const char * interfacename ) { + EVRInitError e; + char fnTableName[128]; + int result1 = snprintf( fnTableName, 128, "FnTable:%s", interfacename ); + void* ret = (void *)VR_GetGenericInterface( fnTableName, &e ); + printf( "Getting System FnTable: %s = %p (%d)\n", fnTableName, ret, e ); + if( !ret ) { + exit( 1 ); + } + return ret; +} + +// These are interfaces into OpenVR, they are basically function call tables. +struct VR_IVRSystem_FnTable* oSystem; +struct VR_IVROverlay_FnTable* oOverlay; + +// The OpenVR Overlay handle. +VROverlayHandle_t overlayID; + +const uint32_t WIDTH = 120, HEIGHT = 24; + +int initVR() { + EVRInitError ierr; + uint32_t token = VR_InitInternal( &ierr, EVRApplicationType_VRApplication_Overlay ); + if( !token ) { + printf( "Error!!!! Could not initialize OpenVR\n" ); + return 1; + } + + // Get the system and overlay interfaces. We pass in the version of these + // interfaces that we wish to use, in case the runtime is newer, we can still + // get the interfaces we expect. + oSystem = CNOVRGetOpenVRFunctionTable( IVRSystem_Version ); + oOverlay = CNOVRGetOpenVRFunctionTable( IVROverlay_Version ); + + + oOverlay->CreateOverlay( "controlerhandmountedclock", "Controller Hand Mounted Clock", &overlayID ); + oOverlay->SetOverlayWidthInMeters( overlayID, .2 ); + oOverlay->SetOverlayColor( overlayID, 1., .8, .7 ); + + // Control texture bounds to control the way the texture is mapped to the overlay. + VRTextureBounds_t bounds; + bounds.uMin = 0; + bounds.uMax = 1; + bounds.vMin = 0; + bounds.vMax = 1; + oOverlay->SetOverlayTextureBounds( overlayID, &bounds ); + + return 0; +} + +GLuint initOverlayTexture() { + // Initialize the texture with junk data. + uint8_t * myjunkdata = malloc( 128 * 128 * 4 ); + int x, y; + for( y = 0; y < 128; y++ ) + for( x = 0; x < 128; x++ ) + { + myjunkdata[ ( x + y * 128 ) * 4 + 0 ] = x * 2; + myjunkdata[ ( x + y * 128 ) * 4 + 1 ] = y * 2; + myjunkdata[ ( x + y * 128 ) * 4 + 2 ] = 0; + myjunkdata[ ( x + y * 128 ) * 4 + 3 ] = 255; + } + + // We aren't doing it, but we could write directly into the overlay. + //err = oOverlay->SetOverlayRaw( overlayID, myjunkdata, 128, 128, 4 ); + + // Generate the texture. + GLuint textureid; + glGenTextures( 1, &textureid ); + glBindTexture( GL_TEXTURE_2D, textureid ); + + // It is required to setup the min and mag filter of the texture. + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + // Load the texture with our dummy data. Optionally we could pass 0 in where we are + // passing in myjunkdata. That would allocate the RAM on the GPU but not do anything with it. + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, WIDTH, HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, myjunkdata); + + free(myjunkdata); + + return textureid; +} + +void attach() { + TrackedDeviceIndex_t index; + index = oSystem->GetTrackedDeviceIndexForControllerRole( ETrackedControllerRole_TrackedControllerRole_LeftHand ); + + if( index == k_unTrackedDeviceIndexInvalid || index == k_unTrackedDeviceIndex_Hmd ) { + printf( "Couldn't find your controller to attach our overlay to (%d)\n", index ); + } else { + // We have a ETrackedControllerRole_TrackedControllerRole_LeftHand. Associate it. + EVROverlayError err; + + // Transform that puts the text somewhere reasonable. + HmdMatrix34_t transform = { 0 }; + + //rotation + transform.m[0][0] = -1; + transform.m[1][2] = -1; + transform.m[2][1] = -1; + + //offset + transform.m[0][3] = 0; + transform.m[1][3] = -0.05; + transform.m[2][3] = 0.15; + + // Apply the transform and attach the overlay to that tracked device object. + err = oOverlay->SetOverlayTransformTrackedDeviceRelative( overlayID, index, &transform ); + + // Notify the terminal that this was associated. + printf( "Successfully associated your clock to the tracked device (%d %d %08x).\n", + err, index, overlayID ); + } +} + +int main() { + if(initVR()) return 1; + + CNFGSetup( "CHAC", -WIDTH, -HEIGHT ); + + oOverlay->ShowOverlay( overlayID ); + GLuint texture = initOverlayTexture(); + + //associate with controller + attach(); + + CNFGBGColor = 0x00000000; //Background + while(1) { + CNFGClearFrame(); + + //Change color to white. + CNFGColor( 0xffffffff ); + + CNFGPenX = 1; CNFGPenY = 1; + //draw clock + char clock[9]; + time_t curr = time(NULL); + struct tm * formtime = localtime(&curr); + strftime(clock, 9, "%H:%M:%S", formtime); + CNFGDrawText( clock, 5 ); + + // Finish rendering any pending draw operations. + CNFGFlushRender(); + + // Bind the texture we will be sending to OpenVR. + glBindTexture( GL_TEXTURE_2D, texture ); + + // Copy the current framebuffer into that texture. + glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, WIDTH, HEIGHT, 0 ); + + { + // Setup a Texture_t object to send in the texture. + struct Texture_t tex; + tex.eColorSpace = EColorSpace_ColorSpace_Auto; + tex.eType = ETextureType_TextureType_OpenGL; + tex.handle = (void*)(intptr_t)texture; + + // Send texture into OpenVR as the overlay. + oOverlay->SetOverlayTexture( overlayID, &tex ); + } + + + // We have to process through texture events. + { + struct VREvent_t nEvent; + oOverlay->PollNextOverlayEvent( overlayID, &nEvent, 0xffffff ); + } + + // Display the image and wait for time to display next frame. + CNFGSwapBuffers(); + OGUSleep( (int)( 0.1 * 1000000 ) ); + } +} \ No newline at end of file diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..783d0a7 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,26 @@ +#include +#include "test.h" + +//tests + + +test_t tests[] = {NULL}; + +int main(int argc, char** argv) { + test_t* current = tests; + int failcount = 0; + int testcount = 0; + for(; *current; current++) { + testcount++; + printf("\033[1mRunning test number: %d ", testcount); + if((*current)()) { + printf("\033[1;92msucceeded\033[0;1m!\n"); + } else { + printf("\033[1;91mfailed\033[0;1m\n"); + failcount++; + } + } + + printf("\033[1;93m%d\033[0;1m/%d failed\n", failcount, testcount); + return failcount > 0; +} diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 0000000..890d25b --- /dev/null +++ b/tests/test.h @@ -0,0 +1,11 @@ +#define TESTFAILED 0 +#define TESTGOOD 1 + +#include + +#define TESTDATA "./tests/data/" + +#define ASSERT(BED, ERR) if(!(BED)) { std::cout << __FILE__ << ":" << __LINE__ << " " << ERR << std::endl; return TESTFAILED; } +// #define ASSERT(BED) ASSERT(BED, "") + +typedef int (*test_t)(); diff --git a/thirdparty/openvr b/thirdparty/openvr new file mode 160000 index 0000000..4c85abc --- /dev/null +++ b/thirdparty/openvr @@ -0,0 +1 @@ +Subproject commit 4c85abcb7f7f1f02adaf3812018c99fc593bc341 diff --git a/thirdparty/rawdraw b/thirdparty/rawdraw new file mode 160000 index 0000000..e352fc1 --- /dev/null +++ b/thirdparty/rawdraw @@ -0,0 +1 @@ +Subproject commit e352fc1dfb050a30aa5b86c4943dda5c30790316