diff --git a/NOMADVRLib/ConfigFile.cpp b/NOMADVRLib/ConfigFile.cpp index ef19a3b6d6122d49c393f71e6a39d770850d6274..365a442dfd7fb7e3466eb6f1f6acca4029180ca8 100644 --- a/NOMADVRLib/ConfigFile.cpp +++ b/NOMADVRLib/ConfigFile.cpp @@ -213,6 +213,9 @@ void cleanConfig() numAtoms=nullptr; atoms=nullptr; } + + delete[] server; + server=nullptr; } void initState() diff --git a/OpenVR/TimestepData/hellovr_opengl_main.cpp b/OpenVR/TimestepData/hellovr_opengl_main.cpp index 8f8eeda93df915c060b4b01d98336b20633b02f3..2bc4e31e6e9184a8a8bb74637a55696f383561e6 100644 --- a/OpenVR/TimestepData/hellovr_opengl_main.cpp +++ b/OpenVR/TimestepData/hellovr_opengl_main.cpp @@ -42,6 +42,8 @@ #include <string> #include <cstdlib> #include <algorithm> +#include <mutex> +#include <condition_variable> #include <winsock2.h> @@ -84,6 +86,17 @@ #define NUMPLY (TIMESTEPS * ISOS) + +typedef struct remotes { + int id; + bool valid[3]; + float head[16]; + vr::HmdMatrix34_t c1, c2; + Vector3 UserPos; + unsigned char colour[3]; +} remotes_t; + + class CGLRenderModel { public: @@ -352,16 +365,33 @@ private: // OpenGL bookkeeping char **myargv; int currentConfig; - std::thread *tcpconn; + std::thread *tcpconn, *udpconn; void connectTCP(); - int sock; + void UDP(); + int UDPContr(SOCKET s, char c, int device); + int UDPHead(SOCKET s); + int sock, s; + struct sockaddr_in serv_addr; void Send(char c, int32_t value); void Send(char c, bool value); void SendConfigFile(); void SendTimestep(); void SendIso(); void SendShowAtoms(); + void SendUserPos(); + void SendDragDrop(Vector3 pos); + + std::vector<remotes_t> remotes; + int32_t identifier; + int FindRemote(int32_t remote); + Vector3 initDrag; + Uint32 msDrag; + char whenDrag;// whenDrag=0: start; 1: middle; 2: end; 3: not sending + + char cleanup; //synch TCP and drawing thread on scene load + std::mutex cleanupmutex; + std::condition_variable cleanupcond; }; const float CMainApplication::videospeed = 0.01f; @@ -456,10 +486,155 @@ int CMainApplication::LoadConfigFile (const char *c) return r; } +int CMainApplication::UDPHead(SOCKET s) +{ + int n=0; + char buff[sizeof(float)*16+5]; + if ( m_rTrackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid ) { + buff[0]='h'; + memcpy(buff+1, &identifier, 4); + memcpy (buff+5, (char*)m_mat4HMDPose.get(), sizeof(float)*16); + n=send(s, buff, sizeof(float)*16+5, 0); + if (n<0) { + closesocket (s); + s=INVALID_SOCKET; + } + } + return n; +} + +int CMainApplication::UDPContr(SOCKET s, char c, int device) +{ +//datagrams can be reordered, so we cannot split type and payload +//reading a subset of the datagrams discards the rest, so we need to listen to the largest possible. + int n=0; + vr::VRControllerState_t cs; + vr::TrackedDevicePose_t dp; + char buff[sizeof(vr::HmdMatrix34_t) +5]; + if (!m_pHMD) + return -1; + m_pHMD->GetControllerStateWithPose( vr::TrackingUniverseStanding, device, &cs, &dp ); + if (dp.bPoseIsValid) { + vr::HmdMatrix34_t mat=dp.mDeviceToAbsoluteTracking; + buff[0]=c; + memcpy(buff+1, &identifier, 4); + memcpy(buff+5, mat.m, sizeof(mat.m)); + n=send(s, buff, sizeof(mat.m)+5, 0); + if (n<0) { + closesocket (s); + } + } + return n; +} + +void CMainApplication::UDP() +{ + //struct hostent *he; + //if ( (he = gethostbyname(server) ) == nullptr ) { + // eprintf ("Connect to server, could not get host name %s\n", server); + // return; /* error */ + //} + //memset((char *) &serv_addr, 0, sizeof(serv_addr)); + //memcpy(&serv_addr.sin_addr, he->h_addr_list[0], he->h_length); + //serv_addr.sin_family = AF_INET; + //serv_addr.sin_port = htons(port); + s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + if (s==INVALID_SOCKET) { + eprintf ("udp socket creation error %d, closing\n", WSAGetLastError()); + return; + } + if ( connect(s, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) { + eprintf ("Udp Connect to server, could not get connection %s, got error %d\n", server, WSAGetLastError()); + return; /* error */ + } + + char buf[100]; + while (true) { + int n; + //Sleep(2000); //rgh fixme, should be approx one frame + Sleep (10); + if (sock==INVALID_SOCKET || s==INVALID_SOCKET) + return; + + struct sockaddr_in sin; + int addrlen = sizeof(sin); + if(getsockname(s, (struct sockaddr *)&sin, &addrlen) == 0 && + sin.sin_family == AF_INET && + addrlen == sizeof(sin)) { + int local_port = sin.sin_port; + } + //send head and controllerpos + n=0; + if (identifier==0) + continue; + if (firstdevice!=-1 ) { + n=UDPContr(s, '1', firstdevice); + if (n<0) + return; + } + if (seconddevice!=-1) { + n=UDPContr(s, '2', seconddevice); + if (n<0) + return; + } + + if ( UDPHead(s)<0 ) { + return; + } + //receive head and controllers + unsigned long l; + do { + ioctlsocket(s, FIONREAD, &l); + if (l>0) { //we have data + int32_t remote; + n=recv(s, buf, 5*4*4*sizeof(float), 0); + if (n<0) { //disconnected + eprintf ("udp disconnect\n"); + return; + } + //Beep( 750, 300 ); //rgh fixme + memcpy (&remote, buf+1, 4); + int i=FindRemote(remote); + if (buf[0]=='h') { + memcpy ((char*)&(remotes[i].head[0]), buf+5, sizeof(float)*16); + remotes[i].valid[0]=true; + } else if (buf[0]=='1') { + memcpy ((char*)remotes[i].c1.m, buf+5, sizeof(float)*12); + remotes[i].valid[1]=true; + } else if (buf[0]=='2') { + memcpy ((char*)remotes[i].c2.m, buf+5, sizeof(float)*12); + remotes[i].valid[2]=true; + } else { + eprintf ("Unknown udp command '%c'", buf[0]); + } + + } + } while (l>0); + } +} + +int CMainApplication::FindRemote(int32_t remote) +{ + int found=-1; + for (int i=0;i<remotes.size();i++) { + if (remotes[i].id==remote) { + return i; + } + } + + remotes.push_back(remotes_t()); + remotes.back().id=remote; + for (int i=0;i<3;i++) + remotes.back().valid[i]=false; + //for now, random, non-consistent colours. Send from server later + for (int i=0;i<3;i++) + remotes.back().colour[i]=(unsigned char)(rand()/RAND_MAX*256.f); + return remotes.size()-1; +} + void CMainApplication::connectTCP() { //https://stackoverflow.com/questions/5444197/converting-host-to-ip-by-sockaddr-in-gethostname-etc - struct sockaddr_in serv_addr; struct hostent *he; if ( (he = gethostbyname(server) ) == nullptr ) { eprintf ("Connect to server, could not get host name %s\n", server); @@ -474,20 +649,39 @@ void CMainApplication::connectTCP() eprintf ("Connect to server, could not get connection %s\n", server); return; /* error */ } + struct sockaddr_in sin; + int addrlen = sizeof(sin); + if(getsockname(sock, (struct sockaddr *)&sin, &addrlen) == 0 && + sin.sin_family == AF_INET && + addrlen == sizeof(sin)) { + int local_port = sin.sin_port; + } //read state int n; int32_t tmp; tmp=htonl (secret); n = send(sock, (char*)&tmp , sizeof(tmp), 0); - if (n<sizeof(tmp)) + if (n<sizeof(tmp)) { + closesocket(sock); + sock=INVALID_SOCKET; + return; + } + SendUserPos(); + if (sock==INVALID_SOCKET) return; - char what; + udpconn=new std::thread(&CMainApplication::UDP, this); + + char what=0; + int found; + char previous=0; while (true) { + previous=what; n=recv(sock, &what, sizeof(what), 0); if (n<1) { eprintf ("closed socket\n"); return; } + //Beep( 1750, 300 ); //rgh fixme switch (what) { case 't': n=recv (sock, (char*)&tmp, sizeof(tmp), 0); @@ -521,15 +715,63 @@ void CMainApplication::connectTCP() return; } //load config file - if (currentConfig!=ntohl(tmp)%myargc) { + //force reload because hidden user may have requested it +// if (currentConfig!=ntohl(tmp)%myargc) + { currentConfig=ntohl(tmp)%myargc; - CleanScene(); - LoadConfigFile(myargv[currentConfig]); - SetupScene(); + //avoid race with drawing thread + cleanupmutex.lock(); + cleanup=1; + cleanupmutex.unlock(); + std::unique_lock<std::mutex> lk(cleanupmutex); + cleanupcond.wait(lk); + + //OpenGL commands only in main thread + //CleanScene(); + //LoadConfigFile(myargv[currentConfig]); + //SetupScene(); + //cleanupmutex.lock(); + //cleanup=0; + //cleanupmutex.unlock(); + } + break; + case 'X': + n=recv (sock, (char*)&identifier, sizeof(tmp), 0); + if (n<sizeof(tmp)) { + eprintf ("short read at socket\n"); + return; + } + break; + case 'p': + int32_t remote; + n=recv (sock, (char*)&remote, sizeof(remote), 0); + found=-1; + for (int i=0;i<remotes.size();i++) { + if (remotes[i].id==remote) { + found=i; + break; + } + } + if (found<0) { + remotes.push_back(remotes_t()); + remotes.back().id=remote; + for (int i=0;i<3;i++) + remotes.back().valid[i]=false; + //for now, random, non-consistent colours. Send from server later + for (int i=0;i<3;i++) + remotes.back().colour[i]=(unsigned char)((float)rand()/RAND_MAX*256.f); + found=remotes.size()-1; } + n=recv (sock, (char*)&(remotes[found].UserPos.x), sizeof(float), 0); + n=recv (sock, (char*)&(remotes[found].UserPos.y), sizeof(float), 0); + n=recv (sock, (char*)&(remotes[found].UserPos.z), sizeof(float), 0); + break; + case 'D': //discard remote Drag&Drop + char buff[6*sizeof(float)+1+2*sizeof(UINT32)]; + n=recv (sock, buff, 6*sizeof(float)+1+2*sizeof(UINT32), 0); break; default: - eprintf ("Unknown state sent from server: %c\n", what); + eprintf ("Unknown state sent from server: %c %d\n", what, what); } } } @@ -538,8 +780,8 @@ void CMainApplication::connectTCP() // Purpose: Constructor //----------------------------------------------------------------------------- CMainApplication::CMainApplication(int argc, char *argv[]) - : m_pWindow(NULL) - , m_pContext(NULL) + : m_pWindow(nullptr) + , m_pContext(nullptr) , m_nWindowWidth(1920) , m_nWindowHeight(1080) , m_unSceneProgramID(0) @@ -548,8 +790,8 @@ CMainApplication::CMainApplication(int argc, char *argv[]) , m_unRenderModelProgramID(0) , m_unAtomsProgramID(0) , m_unUnitCellProgramID(0) - , m_pHMD(NULL) - , m_pRenderModels(NULL) + , m_pHMD(nullptr) + , m_pRenderModels(nullptr) , m_bDebugOpenGL(false) , m_bVerbose(false) , m_bPerf(false) @@ -558,10 +800,10 @@ CMainApplication::CMainApplication(int argc, char *argv[]) , m_glControllerVertBuffer(0) , m_unControllerVAO(0) , m_unLensVAO(0) - , m_unSceneVAO(0) - , m_unSceneVAOIndices(0) - , m_unAtomVAO(0) - , m_glAtomVertBuffer(0) + , m_unSceneVAO(nullptr) + , m_unSceneVAOIndices(nullptr) + , m_unAtomVAO(nullptr) + , m_glAtomVertBuffer(nullptr) , m_glUnitCellVertBuffer(-1) , m_glUnitCellIndexBuffer(-1) , m_unUnitCellVAO(0) @@ -585,15 +827,14 @@ CMainApplication::CMainApplication(int argc, char *argv[]) , currentiso(-1) // (-> ISOS, but at this point ISOS is not yet initialized) , firstdevice(-1) , seconddevice(-1) - , m_iTexture(0) - , axisTextures(0) + , m_iTexture(nullptr) + , axisTextures(nullptr) , peelingFramebuffer(0) , m_uiVertcount(0) , m_glSceneVertBuffer(0) - //, UserPosition(Vector3(-101.0f * 0.15f*0.5f*GRID + 12.5f, -15.0f, -101.0f * 0.15f*0.5f*GRID + 101.0f * 0.15f*0.25f)) , UserPosition(Vector3(-userpos[0] /** 0.04f*/, -userpos[1] /** 0.04f*/, -userpos[2] /** 0.04f*/)) - , vertdataarray(0) - , vertindicesarray(0) + , vertdataarray(nullptr) + , vertindicesarray(nullptr) , pixels(0) , framecounter(0) , savetodisk(false) @@ -602,8 +843,13 @@ CMainApplication::CMainApplication(int argc, char *argv[]) , myargc(argc) , myargv(argv) , currentConfig(1) - , tcpconn(0) - , sock(-1) + , tcpconn(nullptr) + , udpconn(nullptr) + , sock(INVALID_SOCKET) + , s(INVALID_SOCKET) + , identifier(0) + , whenDrag (3) + , cleanup (0) { LoadConfigFile(argv[currentConfig]); for (int j=0;j<3;j++) @@ -763,7 +1009,7 @@ bool CMainApplication::BInit() m_strDriver = GetTrackedDeviceString( m_pHMD, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_TrackingSystemName_String ); m_strDisplay = GetTrackedDeviceString( m_pHMD, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SerialNumber_String ); - std::string strWindowTitle = "Geophysics OpenVR - " + m_strDriver + " " + m_strDisplay; + std::string strWindowTitle = "NOMAD OpenVR - " + m_strDriver + " " + m_strDisplay; SDL_SetWindowTitle( m_pWindow, strWindowTitle.c_str() ); // cube array @@ -885,6 +1131,22 @@ if( *vaos != 0 ) //----------------------------------------------------------------------------- void CMainApplication::Shutdown() { + if (tcpconn) { + closesocket(sock); + sock=INVALID_SOCKET; + tcpconn->join(); + delete tcpconn; + tcpconn=nullptr; + } + + if (udpconn) { + closesocket(s); + s=INVALID_SOCKET; + udpconn->join(); + delete udpconn; + udpconn=nullptr; + } + if( m_pHMD ) { vr::VR_Shutdown(); @@ -1001,41 +1263,98 @@ void CMainApplication::Shutdown() SDL_Quit(); } +void CMainApplication::SendUserPos() +{ + if (sock!=INVALID_SOCKET) { + int n; + const int size=sizeof(float)*3+1; + char buff[size]; + buff[0]='p'; + memcpy(buff+1, &(UserPosition.x), sizeof(float)); + memcpy(buff+1+sizeof(float), &(UserPosition.y), sizeof(float)); + memcpy(buff+1+2*sizeof(float), &(UserPosition.z), sizeof(float)); + n=send(sock, buff, size, 0); + if (n<size) { + closesocket(sock); + sock=INVALID_SOCKET; + } + } +} + +void CMainApplication::SendDragDrop(Vector3 pos) // when=0: start; 1: middle; 2: end; 3: not sending +{//rgh: send init and end via tcp, but middle via udp? + + if (sock!=INVALID_SOCKET) { + if (whenDrag == 3) + return; + if (whenDrag == 0) { + initDrag=pos; + msDrag=SDL_GetTicks(); + whenDrag=1; + return; + } + int n; + const int size=sizeof(float)*6+2+sizeof(UINT32); + UINT32 elapsed=SDL_GetTicks()-msDrag; + if (whenDrag == 1 && elapsed < 100) + return; + char buff[size]; + buff[0]='D'; + memcpy(buff+1, &(initDrag.x), sizeof(float)); + memcpy(buff+1+sizeof(float), &(initDrag.y), sizeof(float)); + memcpy(buff+1+2*sizeof(float), &(initDrag.z), sizeof(float)); + memcpy(buff+1+3*sizeof(float), &(pos.x), sizeof(float)); + memcpy(buff+1+4*sizeof(float), &(pos.y), sizeof(float)); + memcpy(buff+1+5*sizeof(float), &(pos.z), sizeof(float)); + memcpy(buff+1+6*sizeof(float), &(elapsed), sizeof(UINT32)); + memcpy(buff+1+6*sizeof(float)+ sizeof(UINT32), &whenDrag, 1); + n=send(sock, buff, size, 0); + if (n<size) { + closesocket(sock); + sock=INVALID_SOCKET; + } + if (whenDrag==2) + whenDrag=3; + } + +} + void CMainApplication::Send(char c, int32_t value) { - if (sock>=0) { + if (sock!=INVALID_SOCKET) { int32_t tmp; tmp=htonl(value); int n; n=send(sock, &c, sizeof(c), 0); if (n<sizeof(c)) { closesocket(sock); - sock=-1; + sock=INVALID_SOCKET; } n=send(sock, (char*)&tmp, sizeof(tmp), 0); if (n<sizeof(tmp)) { closesocket(sock); - sock=-1; + sock=INVALID_SOCKET; } } } void CMainApplication::Send(char c, bool value) { - if (sock>=0) { + if (sock!=INVALID_SOCKET) { int n; n=send(sock, &c, sizeof(c), 0); if (n<sizeof(c)) { closesocket(sock); - sock=-1; + sock=INVALID_SOCKET; } n=send(sock, (char*)&value, 1, 0); if (n<1) { closesocket(sock); - sock=-1; + sock=INVALID_SOCKET; } } } + void CMainApplication::SendConfigFile() { Send('n', currentConfig); @@ -1117,19 +1436,23 @@ bool CMainApplication::HandleInput() if (sdlEvent.key.keysym.sym == SDLK_a) { Matrix4 tmp = m_mat4HMDPose; UserPosition += tmp.invert()*Vector3(0, 0, speed); + SendUserPos(); //UserPosition.x += speed; } if (sdlEvent.key.keysym.sym == SDLK_y) { //UserPosition.x -= speed; Matrix4 tmp = m_mat4HMDPose; UserPosition -= tmp.invert()*Vector3(0, 0, speed); + SendUserPos(); } if (sdlEvent.key.keysym.sym == SDLK_o) { UserPosition[2] -= 0.1f; + SendUserPos(); dprintf("%f %f\n", UserPosition[0], UserPosition[2]); } if (sdlEvent.key.keysym.sym == SDLK_p) { UserPosition[2] += 0.1f; + SendUserPos(); dprintf("%f %f\n", UserPosition[0], UserPosition[2]); } } @@ -1178,18 +1501,44 @@ bool CMainApplication::HandleInput() if (currentConfig>=myargc) currentConfig=1; SendConfigFile(); - CleanScene(); - LoadConfigFile(myargv[currentConfig]); - SetupScene(); + //we will soon receive an order to reload the scene, so avoid double-reloading + race here + //CleanScene(); + //LoadConfigFile(myargv[currentConfig]); + //SetupScene(); } else if (state.rAxis[0].y < -0.7 && state.rAxis[0].x > -0.4 && state.rAxis[0].x < 0.4) { //prev config file currentConfig--; if (currentConfig<=0) currentConfig=myargc-1; SendConfigFile(); - CleanScene(); - LoadConfigFile(myargv[currentConfig]); - SetupScene(); + //CleanScene(); + //LoadConfigFile(myargv[currentConfig]); + //SetupScene(); + } + } + } else { //Drag and Drop + if (buttonPressed[2][unDevice] && + /*(state.ulButtonTouched&vr::ButtonMaskFromId(vr::k_EButton_Axis0)) == 0 &&*/ + (state.ulButtonPressed&vr::ButtonMaskFromId(vr::k_EButton_Axis0)) == 0) { + buttonPressed[2][unDevice]=false; + whenDrag=2; + //rgh FIXME send final Drag command + } + if ( + (/*state.ulButtonTouched&vr::ButtonMaskFromId(vr::k_EButton_Axis0) || */ + state.ulButtonPressed&vr::ButtonMaskFromId(vr::k_EButton_Axis0) + )){ + + if (state.rAxis[0].y > -0.4 && state.rAxis[0].y < 0.4 && state.rAxis[0].x<-0.7) { + if (!buttonPressed[2][unDevice]) { + buttonPressed[2][unDevice]=true; + whenDrag=0; + } + //rgh FIXME send initial Drag command + //} else { + // whenDrag=1; + // //rgh FIXME send intermediate Drag command + //} } } } @@ -1260,12 +1609,14 @@ bool CMainApplication::HandleInput() if (gazenavigation) { Matrix4 tmp = m_mat4HMDPose; UserPosition += tmp.invert()*Vector3(0, 0, speed); + SendUserPos(); } else { //vr::VRControllerState_t cs; //vr::TrackedDevicePose_t dp; //m_pHMD->GetControllerStateWithPose( vr::TrackingUniverseStanding, firstdevice, &cs, &dp ); const Matrix4 tmp = m_rmat4DevicePose[firstdevice]; - UserPosition += tmp*Vector3(0, 0, speed); + UserPosition += tmp*Vector3(0, 0, speed); + SendUserPos(); } } else { @@ -1433,6 +1784,18 @@ void CMainApplication::HapticFeedback(int device) //----------------------------------------------------------------------------- void CMainApplication::RenderFrame() { + + if (cleanup==1) { + CleanScene(); + LoadConfigFile(myargv[currentConfig]); + SetupScene(); + cleanupmutex.lock(); + cleanup=0; + cleanupmutex.unlock(); + cleanupcond.notify_one(); + //return; + } + int e; // for now as fast as possible if ( m_pHMD ) @@ -1688,7 +2051,7 @@ bool CMainApplication::SetupDepthPeeling() void CMainApplication::CleanScene() { //delete, opposite order from creation //isos - if (ISOS) { + if (m_glSceneVertBuffer && ISOS) {//if unable to init openvr runtime, all these gl variables are null for (int i=0;i<NUMLODS;i++) { glDeleteBuffers(NUMPLY, m_glSceneVertBuffer[i]); glDeleteBuffers(NUMPLY, m_unSceneVAOIndices[i]); @@ -1712,7 +2075,7 @@ void CMainApplication::CleanScene() m_unSceneVAO=nullptr; vertdataarray=nullptr; vertindicesarray=nullptr; - ISOS=0; + //ISOS=0; } //atoms if (atoms) { @@ -1728,6 +2091,7 @@ void CMainApplication::CleanScene() //infocube ::CleanInfoCube(&m_unInfoVAO, &m_unInfoVertBuffer, &m_unInfoIndexBuffer); cleanConfig(); + ISOS=0; } //----------------------------------------------------------------------------- @@ -1929,13 +2293,19 @@ void CMainApplication::RenderControllerGlyph (const vr::Hmd_Eye nEye, const int Vector3 pos; PrepareControllerGlyph(nEye, controller, &pos); if (controller == seconddevice) { - if (selectedAtom==-1) { //isos - sprintf (string, "%d", currentiso+1); - } else { + if (selectedAtom!=-1 || whenDrag !=3) { pos /=scaling; pos-=UserPosition; pos=Vector3(pos[0], -pos[2], pos[1]); - + } + + if (whenDrag != 3) { + SendDragDrop(pos); + } + + if (selectedAtom==-1) { //isos + sprintf (string, "%d", currentiso+1); + } else { pos-=Vector3(atoms[currentset][selectedAtom*4+0], atoms[currentset][selectedAtom*4+1], atoms[currentset][selectedAtom*4+2]); sprintf (string, "%0.2fa", pos.length()); @@ -2866,7 +3236,17 @@ glUseProgram(m_unRenderModelProgramID); Matrix4 globalScaling; globalScaling.scale(scaling, scaling, scaling); +int AtomsInCurrentset; +if (currentset==0) + AtomsInCurrentset=numAtoms[currentset]; +else + AtomsInCurrentset=numAtoms[currentset]-numAtoms[currentset-1]; + for (int i=0;i<info.size(); i++) { + if (info[i].atom-1 > AtomsInCurrentset) { + //wrong atom + continue; + } Matrix4 trans; Vector3 iPos(info[i].pos[0], info[i].pos[1], info[i].pos[2]); @@ -2886,10 +3266,12 @@ for (int i=0;i<info.size(); i++) { //now line glUseProgram(m_unUnitCellProgramID); + for (int i = 0; i < info.size(); i++) { if (info[i].atom < 1) continue; - if (info[i].atom-1 > numAtoms[currentset]) { + + if (info[i].atom-1 > AtomsInCurrentset) { //wrong atom continue; } @@ -3113,7 +3495,12 @@ void CMainApplication::RenderAllTrackedRenderModels(vr::Hmd_Eye nEye) if (!m_rTrackedDeviceToRenderModel[unTrackedDevice] || !m_rbShowTrackedDevice[unTrackedDevice]) continue; - const vr::TrackedDevicePose_t & pose = m_rTrackedDevicePose[unTrackedDevice]; + if (firstdevice==-1 && m_pHMD->GetTrackedDeviceClass( unTrackedDevice ) == vr::TrackedDeviceClass_Controller ) + firstdevice=unTrackedDevice; + else if (seconddevice==-1 && firstdevice!=unTrackedDevice && m_pHMD->GetTrackedDeviceClass( unTrackedDevice ) == vr::TrackedDeviceClass_Controller) + seconddevice=unTrackedDevice; + + //const vr::TrackedDevicePose_t & pose = m_rTrackedDevicePose[unTrackedDevice]; const Matrix4 & matDeviceToTracking = m_rmat4DevicePose[unTrackedDevice]; Matrix4 matMVP = GetCurrentViewProjectionMatrix(nEye) * matDeviceToTracking; @@ -3121,6 +3508,45 @@ void CMainApplication::RenderAllTrackedRenderModels(vr::Hmd_Eye nEye) m_rTrackedDeviceToRenderModel[unTrackedDevice]->Draw(); } + + //now paint remote ones too. Add colour later + for (int i=0;i<remotes.size();i++) { + if (remotes[i].id==identifier) + continue; + if (remotes[i].valid[0]) { + Vector3 relpos=UserPosition-remotes[i].UserPos; + Matrix4 rm(remotes[i].head); + rm.invert().translate(relpos*scaling); + Matrix4 matMVP = GetCurrentViewProjectionMatrix(nEye) *rm; + //add rest of transforms (their user trans, etc) + glUniformMatrix4fv(m_nRenderModelMatrixLocation, 1, GL_FALSE, matMVP.get()); + + if (nullptr==m_rTrackedDeviceToRenderModel[vr::k_unTrackedDeviceIndex_Hmd]) { + SetupRenderModelForTrackedDevice( vr::k_unTrackedDeviceIndex_Hmd ); + m_rbShowTrackedDevice[ vr::k_unTrackedDeviceIndex_Hmd ] = false; + } + m_rTrackedDeviceToRenderModel[vr::k_unTrackedDeviceIndex_Hmd]->Draw(); + } + if (remotes[i].valid[1] && firstdevice!=-1) { + Vector3 relpos=UserPosition-remotes[i].UserPos; + Matrix4 rm(ConvertSteamVRMatrixToMatrix4(remotes[i].c1)); + rm.translate(relpos*scaling); + Matrix4 matMVP = GetCurrentViewProjectionMatrix(nEye) *rm; + //add rest of transforms (their user trans, etc) + glUniformMatrix4fv(m_nRenderModelMatrixLocation, 1, GL_FALSE, matMVP.get()); + m_rTrackedDeviceToRenderModel[firstdevice]->Draw(); + } + if (remotes[i].valid[2] && seconddevice!=-1) { + Vector3 relpos=UserPosition-remotes[i].UserPos; + Matrix4 rm(ConvertSteamVRMatrixToMatrix4(remotes[i].c2)); + rm.translate(relpos*scaling); + Matrix4 matMVP = GetCurrentViewProjectionMatrix(nEye) *rm; + //add rest of transforms (their user trans, etc) + glUniformMatrix4fv(m_nRenderModelMatrixLocation, 1, GL_FALSE, matMVP.get()); + m_rTrackedDeviceToRenderModel[seconddevice]->Draw(); + } + } + GLuint e; if ((e = glGetError()) != GL_NO_ERROR) dprintf("Gl error marker 2: %d, %s\n", e, gluErrorString(e)); @@ -3529,13 +3955,16 @@ int main(int argc, char *argv[]) { //https://stackoverflow.com/questions/8544090/detected-memory-leaks /*_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); - _CrtSetBreakAlloc(89); - _CrtSetBreakAlloc(88); - _CrtSetBreakAlloc(87); - _CrtSetBreakAlloc(86); - _CrtSetBreakAlloc(85); - _CrtSetBreakAlloc(84); - */ + _CrtSetBreakAlloc(639); + _CrtSetBreakAlloc(783); + _CrtSetBreakAlloc(676); + _CrtSetBreakAlloc(675); + _CrtSetBreakAlloc(639); + _CrtSetBreakAlloc(125); + _CrtSetBreakAlloc(118); + _CrtSetBreakAlloc(117); + _CrtSetBreakAlloc(94); + _CrtSetBreakAlloc(93);*/ TMPDIR=".\\"; //http://stackoverflow.com/questions/4991967/how-does-wsastartup-function-initiates-use-of-the-winsock-dll WSADATA wsaData; diff --git a/RemoteVisualization/Makefile b/RemoteVisualization/Makefile index 360f85c24b8fdc0826276c1e70d81fd4c41d916f..6902f7b52a97a95bb9f6c0a88d05f42ae39882f1 100644 --- a/RemoteVisualization/Makefile +++ b/RemoteVisualization/Makefile @@ -1,4 +1,4 @@ -all: NOMADVRLib/atoms.o eprintf.o happyhttp/happyhttp.o NOMADVRLib/ConfigFileAtoms.o main.o +all: NOMADVRLib/atoms.o eprintf.o happyhttp/happyhttp.o NOMADVRLib/ConfigFileAtoms.o main.o exportXYZ.o g++ $^ -o nomad2xyz .cpp.o: diff --git a/RemoteVisualization/exportXYZ.cpp b/RemoteVisualization/exportXYZ.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d75c52d921901299367ce9429ed084968ebdc4d4 --- /dev/null +++ b/RemoteVisualization/exportXYZ.cpp @@ -0,0 +1,22 @@ +#include <stdio.h> + +#include "NOMADVRLib/ConfigFileAtoms.h" +#include "NOMADVRLib/atoms.hpp" +#include "exportXYZ.h" + +void exportXYZ(const char * file, const char *mat, int timesteps) +{ + +FILE * f=fopen (file, "w"); +for (int i=0;i<timesteps;i++) { + fprintf (f, "%d\n", numAtoms[i]); + fprintf (f, "Comment: Material=%s\n", mat); + + for (int j=0;j<numAtoms[i];j++) { + fprintf (f, "%s\t%f\t%f\t%f\n", atomNames[(int)(atoms[i][j*4+3])], + atoms[i][j*4+0], atoms[i][j*4+1], atoms[i][j*4+2]); + + + } +} +} diff --git a/RemoteVisualization/exportXYZ.h b/RemoteVisualization/exportXYZ.h new file mode 100644 index 0000000000000000000000000000000000000000..631a853dd3977bd180027cd459655720e5197935 --- /dev/null +++ b/RemoteVisualization/exportXYZ.h @@ -0,0 +1,6 @@ +#ifndef __EXPORTXYZ_H +#define __EXPORTXYZ_H + +void exportXYZ(const char * file, const char *mat, int timesteps); + +#endif diff --git a/RemoteVisualization/main.cpp b/RemoteVisualization/main.cpp index 47ca9d49eab3692b2a71c318226787b8e45f15b7..0adea73aa88a15b4df49f274c2344bb20d750acc 100644 --- a/RemoteVisualization/main.cpp +++ b/RemoteVisualization/main.cpp @@ -17,26 +17,10 @@ #include "NOMADVRLib/eprintf.h" #include "NOMADVRLib/ConfigFileAtoms.h" #include "NOMADVRLib/atoms.hpp" +#include "exportXYZ.h" int timesteps; -void exportXYZ(const char * file, const char *mat) -{ - -FILE * f=fopen (file, "w"); -for (int i=0;i<timesteps;i++) { - fprintf (f, "%d\n", numAtoms[i]); - fprintf (f, "Comment: Material=%s\n", mat); - - for (int j=0;j<numAtoms[i];j++) { - fprintf (f, "%s\t%f\t%f\t%f\n", atomNames[(int)(atoms[i][j*4+3])], - atoms[i][j*4+0], atoms[i][j*4+1], atoms[i][j*4+2]); - - - } -} -} - void usage (const char * argv0) { eprintf ("Usage: \n%s e <basename for encyclopedia json> xyz", argv0); @@ -56,10 +40,10 @@ int main (int argc, char **argv) { readAtomsJson (argv[2], &numAtoms, ×teps, &atoms, abc, &clonedAtoms); //now export xyz - exportXYZ(argv[3], argv[2]); + exportXYZ(argv[3], argv[2], timesteps); } else if (argv[1][0]=='a') { readAtomsAnalyticsJson (argv[2], &numAtoms, ×teps, &atoms, abc, &clonedAtoms); - exportXYZ (argv[3], argv[2]); + exportXYZ (argv[3], argv[2], timesteps); } else { usage(argv[0]); return 2; diff --git a/proxy/MD-driver/Makefile b/proxy/MD-driver/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..01a6b1141cd996c21c71f7647c391f72ae933762 --- /dev/null +++ b/proxy/MD-driver/Makefile @@ -0,0 +1,16 @@ +all: PeriodicTable SimpleMove + +SimpleMove: SimpleMove.o PeriodicTable.o exportXYZ.o NOMADVRLib/ConfigFileAtoms.o NOMADVRLib/atoms.o \ + happyhttp/happyhttp.o eprintf.o myrecv.o + g++ -g $^ -o $@ + +PeriodicTable: PeriodicTable.o NOMADVRLib/ConfigFileAtoms.o NOMADVRLib/atoms.o exportXYZ.o happyhttp/happyhttp.o \ + eprintf.o PeriodicTableMain.o + g++ -g $^ -o $@ + +.cpp.o: + g++ -g -I. -std=c++11 $< -c -o $*.o + +clean: + rm -f *.o PeriodicTable SimpleMove + diff --git a/proxy/MD-driver/PT.xyz b/proxy/MD-driver/PT.xyz new file mode 100644 index 0000000000000000000000000000000000000000..5b9730693269f4c515b8bc4a1532ae3df7b59e5c --- /dev/null +++ b/proxy/MD-driver/PT.xyz @@ -0,0 +1,120 @@ +118 +Comment: Material=PeriodicTable +H 0.000000 0.000000 0.000000 +He 17.000000 0.000000 0.000000 +Li 0.000000 0.000000 1.000000 +Be 1.000000 0.000000 1.000000 +B 10.000000 0.000000 1.000000 +C 11.000000 0.000000 1.000000 +N 12.000000 0.000000 1.000000 +O 13.000000 0.000000 1.000000 +F 14.000000 0.000000 1.000000 +Ne 15.000000 0.000000 1.000000 +Na 0.000000 0.000000 2.000000 +Mg 1.000000 0.000000 2.000000 +Al 10.000000 0.000000 2.000000 +Si 11.000000 0.000000 2.000000 +P 12.000000 0.000000 2.000000 +S 13.000000 0.000000 2.000000 +Cl 14.000000 0.000000 2.000000 +Ar 15.000000 0.000000 2.000000 +K 0.000000 0.000000 3.000000 +Ca 1.000000 0.000000 3.000000 +Sc 2.000000 0.000000 3.000000 +Ti 3.000000 0.000000 3.000000 +V 4.000000 0.000000 3.000000 +Cr 5.000000 0.000000 3.000000 +Mn 6.000000 0.000000 3.000000 +Fe 7.000000 0.000000 3.000000 +Co 8.000000 0.000000 3.000000 +Ni 9.000000 0.000000 3.000000 +Cu 10.000000 0.000000 3.000000 +Zn 11.000000 0.000000 3.000000 +Ga 12.000000 0.000000 3.000000 +Ge 13.000000 0.000000 3.000000 +As 14.000000 0.000000 3.000000 +Se 15.000000 0.000000 3.000000 +Br 16.000000 0.000000 3.000000 +Kr 17.000000 0.000000 3.000000 +Rb 0.000000 0.000000 4.000000 +Sr 1.000000 0.000000 4.000000 +Y 2.000000 0.000000 4.000000 +Zr 3.000000 0.000000 4.000000 +Nb 4.000000 0.000000 4.000000 +Mo 5.000000 0.000000 4.000000 +Tc 6.000000 0.000000 4.000000 +Ru 7.000000 0.000000 4.000000 +Rh 8.000000 0.000000 4.000000 +Pd 9.000000 0.000000 4.000000 +Ag 10.000000 0.000000 4.000000 +Cd 11.000000 0.000000 4.000000 +In 12.000000 0.000000 4.000000 +Sn 13.000000 0.000000 4.000000 +Sb 14.000000 0.000000 4.000000 +Te 15.000000 0.000000 4.000000 +I 16.000000 0.000000 4.000000 +Xe 17.000000 0.000000 4.000000 +Cs 0.000000 0.000000 5.000000 +Ba 1.000000 0.000000 5.000000 +La 2.000000 0.000000 5.000000 +Ce 4.000000 0.000000 9.000000 +Pr 5.000000 0.000000 9.000000 +Nd 6.000000 0.000000 9.000000 +Pm 7.000000 0.000000 9.000000 +Sm 8.000000 0.000000 9.000000 +Eu 9.000000 0.000000 9.000000 +Gd 10.000000 0.000000 9.000000 +Tb 11.000000 0.000000 9.000000 +Dy 12.000000 0.000000 9.000000 +Ho 13.000000 0.000000 9.000000 +Er 14.000000 0.000000 9.000000 +Tm 15.000000 0.000000 9.000000 +Yb 16.000000 0.000000 9.000000 +Lu 17.000000 0.000000 9.000000 +Hf 3.000000 0.000000 5.000000 +Ta 4.000000 0.000000 5.000000 +W 5.000000 0.000000 5.000000 +Re 6.000000 0.000000 5.000000 +Os 7.000000 0.000000 5.000000 +Ir 8.000000 0.000000 5.000000 +Pt 9.000000 0.000000 5.000000 +Au 10.000000 0.000000 5.000000 +Hg 11.000000 0.000000 5.000000 +Tl 12.000000 0.000000 5.000000 +Pb 13.000000 0.000000 5.000000 +Bi 14.000000 0.000000 5.000000 +Po 15.000000 0.000000 5.000000 +At 16.000000 0.000000 5.000000 +Rn 17.000000 0.000000 5.000000 +Fr 0.000000 0.000000 6.000000 +Ra 1.000000 0.000000 6.000000 +Ac 2.000000 0.000000 6.000000 +Th 4.000000 0.000000 10.000000 +Pa 5.000000 0.000000 10.000000 +U 6.000000 0.000000 10.000000 +Np 7.000000 0.000000 10.000000 +Pu 8.000000 0.000000 10.000000 +Am 9.000000 0.000000 10.000000 +Cm 10.000000 0.000000 10.000000 +Bk 11.000000 0.000000 10.000000 +Cf 12.000000 0.000000 10.000000 +Es 13.000000 0.000000 10.000000 +Fm 14.000000 0.000000 10.000000 +Md 15.000000 0.000000 10.000000 +No 16.000000 0.000000 10.000000 +Lr 17.000000 0.000000 10.000000 +Rf 3.000000 0.000000 6.000000 +Ha 4.000000 0.000000 6.000000 +Sg 5.000000 0.000000 6.000000 +Ns 6.000000 0.000000 6.000000 +Hs 7.000000 0.000000 6.000000 +Mt 8.000000 0.000000 6.000000 +Ds 9.000000 0.000000 6.000000 +Rg 10.000000 0.000000 6.000000 +Cn 11.000000 0.000000 6.000000 +Nh 12.000000 0.000000 6.000000 +Fl 13.000000 0.000000 6.000000 +Mc 14.000000 0.000000 6.000000 +Lv 15.000000 0.000000 6.000000 +Ts 16.000000 0.000000 6.000000 +Og 17.000000 0.000000 6.000000 diff --git a/proxy/MD-driver/PeriodicTable.cpp b/proxy/MD-driver/PeriodicTable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7531fbe27b57363a5e5c0d42e9222843d872f127 --- /dev/null +++ b/proxy/MD-driver/PeriodicTable.cpp @@ -0,0 +1,89 @@ +#include "PeriodicTable.h" + +void CreatePeriodicTable (float * atoms, float scaling) +{ + +atoms[0]=0; //H +atoms[1]=0; +atoms[2]=0; +atoms[3]=0; +atoms[4]=17*scaling; //He +atoms[5]=0; +atoms[6]=0; +atoms[7]=1; + +int currentAtom=2; + +for (int row=1;row<3;row ++) { + for (int j=0; j<2;j++) {//left + atoms[currentAtom*4]=j*scaling; + atoms[currentAtom*4+1]=0; + atoms[currentAtom*4+2]=-row*scaling; + atoms[currentAtom*4+3]=currentAtom; + + currentAtom++; + } + + for (int j=0; j<6;j++) {//right + atoms[currentAtom*4]=(j+12)*scaling; + atoms[currentAtom*4+1]=0; + atoms[currentAtom*4+2]=-row*scaling; + atoms[currentAtom*4+3]=currentAtom; + + currentAtom++; + } + +} + +for (int row=3;row<5;row ++) { + for (int j=0; j<18;j++) {//left + atoms[currentAtom*4]=j*scaling; + atoms[currentAtom*4+1]=0; + atoms[currentAtom*4+2]=-row*scaling; + atoms[currentAtom*4+3]=currentAtom; + + currentAtom++; + } + +} + +for (int row=5;row<7;row ++) { + for (int j=0; j<3;j++) {//left + atoms[currentAtom*4]=j*scaling; + atoms[currentAtom*4+1]=0; + atoms[currentAtom*4+2]=-row*scaling; + atoms[currentAtom*4+3]=currentAtom; + + currentAtom++; + } + + currentAtom+=14; + + for (int j=0; j<15;j++) {//right + atoms[currentAtom*4]=(j+3)*scaling; + atoms[currentAtom*4+1]=0; + atoms[currentAtom*4+2]=-row*scaling; + atoms[currentAtom*4+3]=currentAtom; + + currentAtom++; + } + +} + +//rare earths +currentAtom=57; +for (int row=0;row<2;row ++) { + for (int j=0; j<14;j++) { + atoms[currentAtom*4]=(j+4)*scaling; + atoms[currentAtom*4+1]=0; + atoms[currentAtom*4+2]=(-row-8)*scaling; + atoms[currentAtom*4+3]=currentAtom; + + currentAtom++; + } + currentAtom=89; + +} + +} + diff --git a/proxy/MD-driver/PeriodicTable.h b/proxy/MD-driver/PeriodicTable.h new file mode 100644 index 0000000000000000000000000000000000000000..a68b60bf75af72710c20cb038315644b3ed79761 --- /dev/null +++ b/proxy/MD-driver/PeriodicTable.h @@ -0,0 +1,3 @@ + +void CreatePeriodicTable (float * atoms, float scaling); + diff --git a/proxy/MD-driver/PeriodicTableMain.cpp b/proxy/MD-driver/PeriodicTableMain.cpp new file mode 100644 index 0000000000000000000000000000000000000000..07cd0f07b6f7ea7c74fa7b970abc4493cd454efd --- /dev/null +++ b/proxy/MD-driver/PeriodicTableMain.cpp @@ -0,0 +1,27 @@ +#include "NOMADVRLib/ConfigFileAtoms.h" +#include "NOMADVRLib/atoms.hpp" +#include "exportXYZ.h" +#include "PeriodicTable.h" + +int main (int argc, char ** argv) +{ + +//create periodic table +if (argc!=2) { + fprintf (stderr, "Required argument, XYZ filename\n"); + return 1; +} + +numAtoms=new int[1]; +numAtoms[0]=118; + +atoms=new float* [1]; +atoms[0]=new float[118*4]; + +CreatePeriodicTable(atoms[0], 5); + +exportXYZ(argv[1], "PeriodicTable", 1); + +return 0; +} + diff --git a/proxy/MD-driver/Readme b/proxy/MD-driver/Readme new file mode 100644 index 0000000000000000000000000000000000000000..0360ac889c78332b55d95418364c90e95b880e8e --- /dev/null +++ b/proxy/MD-driver/Readme @@ -0,0 +1,19 @@ +Programs: +PeriodicTable + takes one filename as input and generates an XYZ with the atoms at the standard periodic table positions. +Simplemove + Parameters: <server> <port> <secret> + Usage: + Run the program in a shared filesystem folder. + The program connects to a NOMADVR proxy server, then generates config and xyz files + Other OpenVR NOMADVR instances can use these config files to enable atom Drag-and-drop functionality, + using the touchpad in the second controller. + The XYZ file in the disk contains the latest atom configuration. + +Simplemove can be used as a sample program to interface OpenVR NOMAD VR with interactive molecular dynamics codes. + +Compilation: +Requires: happyhttp NOMADVRLib rapidjson +Copy these folders here and run "make". + + diff --git a/proxy/MD-driver/SimpleMove.cpp b/proxy/MD-driver/SimpleMove.cpp new file mode 100644 index 0000000000000000000000000000000000000000..53d9d6f78326385f93f997f37693ad90963be76f --- /dev/null +++ b/proxy/MD-driver/SimpleMove.cpp @@ -0,0 +1,220 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + +#include "NOMADVRLib/ConfigFileAtoms.h" +#include "NOMADVRLib/atoms.hpp" +#include "PeriodicTable.h" +#include "exportXYZ.h" +#include "state.h" +#include "myrecv.h" + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#define closesocket close +#endif + +const int NUMATOMS=118; + +int main (int argc, char ** argv) +{ + +if (argc!=4) { + fprintf (stderr, "Parameters: <server> <port> <secret>\n"); + return 1; +} + +state_t state; + +numAtoms=new int[1]; +numAtoms[0]=NUMATOMS; + +atoms=new float* [1]; +atoms[0]=new float[NUMATOMS*4]; + +CreatePeriodicTable(atoms[0], 5); + +exportXYZ("A.xyz", "PeriodicTable", 1); + +FILE *ncfg=fopen ("A.ncfg", "w"); +fprintf (ncfg, "xyzfile A.xyz\n" + "server %s %s %s\n" + "menubutton Infobox\n" + "sidebuttontimestep 0\n", argv[1], argv[2], argv[3]); +fclose (ncfg); + +//connect to server, TCP + +int sock; + +struct hostent *he; +struct sockaddr_in serv_addr; + + if ( (he = gethostbyname(argv[1]) ) == nullptr ) { + fprintf (stderr, "Connect to server, could not get host name %s\n", argv[1]); + return 2; /* error */ + } + memset((char *) &serv_addr, 0, sizeof(serv_addr)); + memcpy(&serv_addr.sin_addr, he->h_addr_list[0], he->h_length); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(atoi(argv[2])); + sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); + if ( connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) { + fprintf (stderr, "Connect to server, could not get connection %s\n", argv[1]); + return 3; /* error */ + } + +int n; + int32_t tmp; + tmp=htonl (atoi(argv[3])); //secret + n = send(sock, (char*)&tmp , sizeof(tmp), 0); + if (n<sizeof(tmp)) { + closesocket(sock); + sock=INVALID_SOCKET; + fprintf (stderr, "couldn't send secret\n"); + return 4; + } + + +char buffer[100]; + +uint32_t ID; +int found; +while (true) { + n=myrecv(sock, buffer, 1, 0); + if (n<1) { + fprintf (stderr, "couldn't receive data command"); + return 5; + } + printf ("received order %c\n", buffer[0]); + switch (buffer[0]) { + case 'X': // ID + n=myrecv(sock, &ID, 4, 0); + if (n<4) { + fprintf (stderr, "couldn't receive data X"); + return 5; + } + break; + + case 't': + n=myrecv(sock, &state.timestep, 4, 0); + if (n<4) +{ + fprintf (stderr, "couldn't receive data t"); + return 5; + } + break; + + case 'i': + n=myrecv(sock, &state.iso, 4, 0); + if (n<4) +{ + fprintf (stderr, "couldn't receive data i\n"); + return 5; + } + break; + + case 's': + n=myrecv(sock, &state.showatoms, 1, 0); + if (n<1) +{ + fprintf (stderr, "couldn't receive data s\n"); + return 5; + } + break; + + case 'n': + n=myrecv(sock, &state.ncfg, 4, 0); + if (n<4) +{ + fprintf (stderr, "couldn't receive data n\n"); + return 5; + } + break; + + case 'p': // user position + n=myrecv (sock, buffer+1, sizeof(float)*3 + 4, 0); + if (n<sizeof(float)*3 + 4) { + fprintf (stderr, "couldn't receive data p\n"); + return 5; + } + break; + + case 'd': //disconnect + n=myrecv (sock, buffer+1, 4, 0); + if (n<4) +{ + fprintf (stderr, "couldn't receive data d\n"); + return 5; + } + break; + + case 'D': //Drag + n=myrecv (sock, buffer+1, 6*sizeof(float) + 4*2 +1, 0); + if (n<6*sizeof(float) + 4*2 +1) +{ + fprintf (stderr, "couldn't receive data D\n"); + return 5; + } + //find the nearest atom + //test, use atom 0 + float orig[3]; + float dest[3]; + uint32_t ID, time; + char button; + memcpy (&ID, buffer+1, 4); + memcpy (orig, buffer+5, 3*sizeof(float)); + memcpy (dest, buffer+5+3*sizeof(float), 3*sizeof(float)); + memcpy (&time, buffer+5+6*sizeof(float), 4); + button=buffer[1+6*sizeof(float)+8]; + if (button!=2) { + printf ("intermediate, discarding\n"); + continue; + } + found=-1; + for (int i=0;i<NUMATOMS;i++) { + float sum=0; + for (int j=0;j<3;j++) { + const float tmp=atoms[0][i*4+j]-orig[j]; + sum+=tmp*tmp; + } + const float r=atomRadius(static_cast<int>(atoms[0][i*4+3])); + if (sum < r*r) { + found=i; + break; + } + + } + if (found!=-1) { + printf ("Starting Drag atom %d\n", found); + //drag it + for (int i=0;i<3;i++) + atoms[0][found*4+i]+=dest[i]-orig[i]; + + exportXYZ("A.xyz", "PeriodicTable", 1); + //reload scene + + buffer[0]='n'; + memcpy(buffer+1, (char*)&state.ncfg, 4); + n=send (sock, buffer, 5, 0); + if (n<5) { + fprintf (stderr, "could not send reload command\n"); + return 6; + } + printf ("Reload sent\n"); + } + break; + + default: + fprintf (stderr, "Unknown command received: '%c', %d\n", buffer[0], buffer[0]); + return -5; + } +} + +return 0; +} diff --git a/proxy/MD-driver/eprintf.cpp b/proxy/MD-driver/eprintf.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a27b1cdd7f92884358bcaec49442482a2b29c15 --- /dev/null +++ b/proxy/MD-driver/eprintf.cpp @@ -0,0 +1,32 @@ +/* +# Copyright 2016-2018 Ruben Jesus Garcia Hernandez +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/ + +#include <stdarg.h> +#include <stdio.h> + +#include "NOMADVRLib/eprintf.h" + +void eprintf( const char *fmt, ... ) +{ + va_list args; + char buffer[ 2048 ]; + + va_start( args, fmt ); + vsprintf( buffer, fmt, args ); + va_end( args ); + + fprintf(stderr, "%s\n", buffer ); +} diff --git a/proxy/MD-driver/exportXYZ.cpp b/proxy/MD-driver/exportXYZ.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e07da7ce09e7d4af5ff48f1a39843a9f939e3f52 --- /dev/null +++ b/proxy/MD-driver/exportXYZ.cpp @@ -0,0 +1,23 @@ +#include <stdio.h> + +#include "NOMADVRLib/ConfigFileAtoms.h" +#include "NOMADVRLib/atoms.hpp" +#include "exportXYZ.h" + +void exportXYZ(const char * file, const char *mat, int timesteps) +{ + +FILE * f=fopen (file, "w"); +for (int i=0;i<timesteps;i++) { + fprintf (f, "%d\n", numAtoms[i]); + fprintf (f, "Comment: Material=%s\n", mat); + + for (int j=0;j<numAtoms[i];j++) { + fprintf (f, "%s\t%f\t%f\t%f\n", atomNames[(int)(atoms[i][j*4+3])], + atoms[i][j*4+0], atoms[i][j*4+1], atoms[i][j*4+2]); + + + } +} +fclose(f); +} diff --git a/proxy/MD-driver/exportXYZ.h b/proxy/MD-driver/exportXYZ.h new file mode 100644 index 0000000000000000000000000000000000000000..631a853dd3977bd180027cd459655720e5197935 --- /dev/null +++ b/proxy/MD-driver/exportXYZ.h @@ -0,0 +1,6 @@ +#ifndef __EXPORTXYZ_H +#define __EXPORTXYZ_H + +void exportXYZ(const char * file, const char *mat, int timesteps); + +#endif diff --git a/proxy/MD-driver/myrecv.cpp b/proxy/MD-driver/myrecv.cpp new file mode 100644 index 0000000000000000000000000000000000000000..928ffc36f46245d90ff7d29b61edd49cc9f9fb13 --- /dev/null +++ b/proxy/MD-driver/myrecv.cpp @@ -0,0 +1,18 @@ +#include <sys/socket.h> + +#include "myrecv.h" + +int myrecv(int sock,void *buffer,int len, int flags) { + int n=len; + char *mybuff=(char*)buffer; + int r; + do { + r=recv (sock, mybuff, n, flags); + if (r<0) + return r; + mybuff+=r; + n-=r; + } while (n>0); + return len; +} + diff --git a/proxy/MD-driver/myrecv.h b/proxy/MD-driver/myrecv.h new file mode 100644 index 0000000000000000000000000000000000000000..3f02e12c045a8e62e546d4f9911e73a999628b22 --- /dev/null +++ b/proxy/MD-driver/myrecv.h @@ -0,0 +1,2 @@ +int myrecv(int sock,void *buffer,int len, int flags); + diff --git a/proxy/MD-driver/state.h b/proxy/MD-driver/state.h new file mode 100644 index 0000000000000000000000000000000000000000..eea804a8f6ede3afca3d7f8f99b213012da93ef3 --- /dev/null +++ b/proxy/MD-driver/state.h @@ -0,0 +1,11 @@ +#ifndef __STATE_H +#define __STATE_H + +struct state_t { + int32_t timestep; + int32_t iso; + bool showatoms; + int32_t ncfg; +} state; + +#endif diff --git a/proxy/Makefile b/proxy/Makefile index 15fd70c545f436a282455ed2f7883829172d8067..a1ce0b485d7440a000215a18f5d2df35f5c49be5 100644 --- a/proxy/Makefile +++ b/proxy/Makefile @@ -1,5 +1,5 @@ -proxy: proxy.cpp Makefile - g++ -std=c++11 -g proxy.cpp -o proxy +NOMADVRproxy: proxy.cpp Makefile + g++ -std=c++11 -g proxy.cpp -o NOMADVRproxy clean: - rm -f proxy.o proxy + rm -f proxy.o proxy NOMADVRproxy diff --git a/proxy/proxy.cpp b/proxy/proxy.cpp index 561295f7be9d0c4f00caf6e56eca65018de352bc..5f82448efa45474b55b07cd932532076db78d83c 100644 --- a/proxy/proxy.cpp +++ b/proxy/proxy.cpp @@ -15,6 +15,9 @@ */ #include <stdio.h> #include <stdint.h> +#include <math.h> +#include <limits> + #ifdef WIN32 #include <winsock2.h> #include <ws2tcpip.h> @@ -26,6 +29,11 @@ #include <sys/socket.h> #include <netinet/in.h> #include <sys/select.h> +#include <sys/ioctl.h> +#include <sys/signal.h> + +#define closesocket close +#define INVALID_SOCKET (-1) //http://stackoverflow.com/questions/4291149/difference-between-string-h-and-strings-h #endif #include <string.h> @@ -33,6 +41,11 @@ #include <vector> std::vector<int> sockfds; +std::vector<struct sockaddr_in> cli_addrs; +std::vector<struct sockaddr_in> udp_cli_addrs; +std::vector<float> cli_userpos; +std::vector<unsigned char> cli_colour; + void error(const char *msg) { @@ -63,45 +76,70 @@ struct state_t { bool initNewSocket (unsigned int secret) { + int n; int32_t tmp; int32_t rsec; - char buffer[17]; - buffer[0]='t'; + char buffer[22]; + buffer[0]='X'; + tmp=cli_addrs.size(); + memcpy(buffer+1, &tmp, sizeof(tmp)); + buffer[5]='t'; tmp=htonl(state.timestep); - memcpy (buffer+1, &tmp, sizeof(state.timestep)); - buffer[5]='i'; + memcpy (buffer+6, &tmp, sizeof(state.timestep)); + buffer[10]='i'; tmp=htonl(state.iso); - memcpy (buffer+6, &tmp, sizeof(state.iso)); - buffer[10]='s'; - buffer[11]=(char)state.showatoms; - buffer[12]='n'; + memcpy (buffer+11, &tmp, sizeof(state.iso)); + buffer[15]='s'; + buffer[16]=(char)state.showatoms; + buffer[17]='n'; tmp=htonl(state.ncfg); - memcpy (buffer+13, &tmp, sizeof(state.iso)); + memcpy (buffer+18, &tmp, sizeof(state.iso)); - //struct sockaddr_in cli_addr; - //socklen_t clilen; - sockfds.push_back(accept(sockfds[0], nullptr, nullptr)); + struct sockaddr_in cli_addr; + socklen_t clilen=sizeof(cli_addr); + sockfds.push_back(accept(sockfds[0], (sockaddr*)&cli_addr, &clilen)); #ifdef WIN32 if (sockfds.back() == INVALID_SOCKET) #else if (sockfds.back() < 0) #endif error("ERROR on accept"); - + + cli_addrs.push_back(cli_addr); + for (int i=0;i<3;i++) + cli_userpos.push_back(0); + for (int i=0;i<3;i++) + cli_colour.push_back((unsigned char)(rand()/RAND_MAX*256.f)); tmp=htonl (secret); n=recv(sockfds.back(), (char*)&rsec, sizeof(rsec), 0); if (tmp!=rsec) { + //verify if extraneous HTTP GET and redirect + if (rsec==542393671){ // 'GET ' + printf ("extraneous HTTP GET, send to NOMAD VR web\n"); + const char* response="HTTP/1.1 302 Found\r\n" + "Location: https://www.nomad-coe.eu/the-project/graphics/VR-prototype\r\n"; + + n=send(sockfds.back(), response, strlen(response), 0); + + } #ifdef WIN32 closesocket (sockfds.back()); #else + printf ("expected %d, got %d\n", secret, ntohl(rsec)); + printf ("tmp=%d, rsec=%d\n", tmp, rsec); + char l[5]; + l[4]=0; + memcpy(l, &rsec, 4); + printf ("ascii %s\n",l); + close (sockfds.back()); #endif return false; } - n = send(sockfds.back(), buffer , 17, 0); + n = send(sockfds.back(), buffer , 22, 0); if (n < 0) { error("ERROR writing to socket"); return false; @@ -126,6 +164,8 @@ int myrecv(int sock,char *buffer,int len, int flags) { int main(int argc, char *argv[]) { + static_assert( std::numeric_limits<double>::is_iec559, "IEEE 754 floating point" ); + unsigned int secret; //windows sockets are only very loosely based on unix sockets. //initialization and error management are different. @@ -142,7 +182,6 @@ int main(int argc, char *argv[]) std::vector<socklen_t> clilens; char buffer[256]; struct sockaddr_in serv_addr; - std::vector<struct sockaddr_in> cli_addrs; int n; #ifdef WIN32 @@ -165,6 +204,8 @@ int main(int argc, char *argv[]) WSACleanup(); return 1; } +#else +signal(SIGPIPE, SIG_IGN); #endif @@ -174,6 +215,7 @@ int main(int argc, char *argv[]) exit(1); } secret=atoi(argv[2]); + printf ("NOMADVR proxy server, accepting connections with secret %d\n", secret); sockfds.push_back (socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); #ifdef WIN32 @@ -186,7 +228,10 @@ int main(int argc, char *argv[]) } memset((char *) &serv_addr, 0, sizeof(serv_addr)); cli_addrs.push_back(serv_addr); - cli_addrs.push_back(serv_addr); + for (int i=0;i<3;i++) + cli_userpos.push_back(0); //unused + for (int i=0;i<3;i++) + cli_colour.push_back(0); //unused portno = atoi(argv[1]); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; @@ -201,6 +246,19 @@ int main(int argc, char *argv[]) FD_ZERO (&active_fd_set); FD_SET (sockfds[0], &active_fd_set); + //now udp + int udpSock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + if (udpSock==INVALID_SOCKET) + { + error("ERROR on udp socket creation"); + } + if (bind(udpSock, (struct sockaddr *) &serv_addr, + sizeof(serv_addr)) < 0) + error("ERROR on binding"); + listen(udpSock,5); + + FD_SET (udpSock, &active_fd_set); + memset(buffer,0, 256); @@ -214,31 +272,50 @@ for (;;) { if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) #endif { - error ("select"); +// error ("select"); //descriptors not currently ready + sleep(1); + continue; } for (unsigned i=1;i<sockfds.size();i++) if (FD_ISSET (sockfds[i], &read_fd_set)) { - n = myrecv(sockfds[i],buffer,1, 0); - fprintf (stderr, "received %d bytes from socket %d\n", n, i); + //select activates this also on other conditions such as closed socket. Verify. + int bytes_available; + ioctl(sockfds[i],FIONREAD,&bytes_available); + if (bytes_available<1) { + fprintf (stderr, "socket %d, bytes=%d, disconnecting\n", + sockfds[i], bytes_available); + FD_CLR(sockfds[i], &active_fd_set); + closesocket(sockfds[i]); + sockfds[i]=-1; + continue; + } + n = myrecv(sockfds[i],buffer,1, 0); +// fprintf (stderr, "received %d bytes from socket %d\n", n, i); if (n<0) { //disconnected printf ("client closed socket\n"); + FD_CLR(sockfds[i], &active_fd_set); + closesocket(sockfds[i]); sockfds[i]=-1; continue; } buffer[n]=0; if (n==0) { printf ("client closed socket\n"); + FD_CLR(sockfds[i], &active_fd_set); + closesocket(sockfds[i]); sockfds[i]=-1; continue; } - printf("Here is the message: '%s'\n",buffer); +// printf("Here is the message: '%s'\n",buffer); //update state if (buffer[0]=='t') { n = myrecv(sockfds[i],buffer+1,4, 0); if (n<0) { //disconnected + FD_CLR(sockfds[i], &active_fd_set); + closesocket(sockfds[i]); sockfds[i]=-1; continue; } @@ -250,6 +327,8 @@ for (;;) { } else if (buffer[0]=='i') { n = myrecv(sockfds[i],buffer+1,4, 0); if (n<0) { //disconnected + FD_CLR(sockfds[i], &active_fd_set); + closesocket(sockfds[i]); sockfds[i]=-1; continue; } @@ -261,6 +340,8 @@ for (;;) { } else if (buffer[0]=='n') { n = myrecv(sockfds[i],buffer+1,4, 0); if (n<0) { //disconnected + FD_CLR(sockfds[i], &active_fd_set); + closesocket(sockfds[i]); sockfds[i]=-1; continue; } @@ -272,13 +353,38 @@ for (;;) { } else if (buffer[0]=='s') { n = myrecv(sockfds[i],buffer+1,1, 0); if (n<0) { //disconnected + FD_CLR(sockfds[i], &active_fd_set); + closesocket(sockfds[i]); sockfds[i]=-1; continue; } state.showatoms=buffer[1]!=0; printf("showatoms: %d\n",state.showatoms); + } else if (buffer[0]=='p') { //local state, response also has identifier + memcpy(buffer+1,&i, 4); + n = myrecv(sockfds[i],buffer+5,sizeof(float)*3, 0); + if (n<0) { //disconnected + FD_CLR(sockfds[i], &active_fd_set); + closesocket(sockfds[i]); + sockfds[i]=-1; + continue; + } + memcpy(&(cli_userpos[3*i]), buffer+5, sizeof(float)*3); + n+=5; + printf ("user pos\n"); + } else if (buffer[0]=='D') { //Dragging + memcpy(buffer+1,&i, 4); + n = myrecv(sockfds[i],buffer+5, sizeof(float)*6+4+1, 0); + if (n<0) { + FD_CLR(sockfds[i], &active_fd_set); + closesocket(sockfds[i]); + sockfds[i]=-1; + continue; + } + n+=5; + printf ("user drag\n"); } else { - fprintf (stderr, "Unknown state request '%c'\n", buffer[0]); + fprintf (stderr, "Unknown state request '%c',%d\n", buffer[0], buffer[0]); error ("unknown state" ); } @@ -286,11 +392,17 @@ for (;;) { if (sockfds[j]>=0) { if (buffer[0]=='s') n=2; - else + else if (buffer[0]=='p') + n=4*3+5; + else if (buffer[0]=='D') + n=6*sizeof(float)+4+1+5; + else n=5; n = send(sockfds[j], buffer , n, 0); if (n < 0) { fprintf(stderr, "ERROR writing to socket, closing\n"); + FD_CLR(sockfds[j], &active_fd_set); + closesocket(sockfds[j]); sockfds[j]=-1; } } @@ -300,10 +412,62 @@ for (;;) { //unblockingly see if new connections were made and add to sockfds if (FD_ISSET (sockfds[0], &read_fd_set)) { if (initNewSocket (secret)) { - printf ("Connected to new client\n"); + printf ("Connected to new client, %d\n", sockfds.back()); FD_SET (sockfds.back(), &active_fd_set); } } + //now udp + if (FD_ISSET (udpSock, &read_fd_set)) { +// printf ("fd_isset on udpSock\n"); +//datagrams can be reordered, so we cannot split type and payload +//reading a subset of the datagrams discards the rest, so we need all of them to be the same size, and read them in one chunk. + struct sockaddr_in src_addr; + socklen_t addrlen=sizeof(src_addr); + n = recvfrom(udpSock,buffer,5+4+4*4*sizeof(float), 0, (sockaddr*)&src_addr, &addrlen); + int found=-1; + for (std::vector<int>::size_type i=0;i<udp_cli_addrs.size();i++) { + if (src_addr.sin_port == udp_cli_addrs[i].sin_port && + src_addr.sin_addr.s_addr== udp_cli_addrs[i].sin_addr.s_addr) { + found=i; + break; + } + } + if (found==-1) { + udp_cli_addrs.push_back(src_addr); + found=udp_cli_addrs.size()-1; + printf ("adding new udp client %d, p=%d\n", found, + src_addr.sin_port); + + } +// printf ("received bytes: %d\nOrder: %c\n", n, buffer[0]); + if (n<5+3*4*sizeof(float)) { + //may receive a 3x4 from the controllers + printf ("error, continuing\n"); + continue; //error + } + else if (!(buffer[0]=='1' || buffer[0]=='2' || buffer[0]=='h')) { + printf ("udp, unknown message type\n"); + continue; + } + uint32_t rem; + memcpy (&rem, buffer+1, 4); +// printf ("received udp '%c' from %d, retransmitting\n", buffer[0], rem); + for (std::vector<int>::size_type i=0;i<udp_cli_addrs.size();i++) { + //do not sent to originator + if (i==found) + continue; +// printf ("sending to %d\n", i); + if (n!=sendto(udpSock, buffer, n, 0, (sockaddr*)&(udp_cli_addrs[i]), sizeof(sockaddr_in))){ + printf ("Error sending to %d\n", i); + udp_cli_addrs[i]=udp_cli_addrs.back(); + udp_cli_addrs.pop_back(); + if (found==udp_cli_addrs.size()) + found=i; + i--; + continue; + } + } + } } #ifdef WIN32 WSACleanup();