/*# Copyright 2016-2018 The NOMAD Developers Group*/ /* Copyright 2016 Google Inc. All rights reserved. * * 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 "AudioToolbox/AudioToolbox.h" #include "treasure_hunt_renderer.h" #include #include #include "NOMADVRLib/MyGL.h" #include "NOMADVRLib/atoms.hpp" #include "NOMADVRLib/ConfigFile.h" #include "NOMADVRLib/atomsGL.h" #include "NOMADVRLib/UnitCellShaders.h" #include "NOMADVRLib/TessShaders.h" #include "NOMADVRLib/polyhedron.h" #include "NOMADVRLib/IsosurfacesGL.h" #include "NOMADVRLib/eprintf.h" #include "GoogleCardboard/auxiliary.h" #define LOG_TAG "NOMADVRIOS" #define LOGW(...) NSLog(@__VA_ARGS__) void eprintf( const char *fmt, ... ) { va_list args; char buffer[ 2048 ]; va_start( args, fmt ); vsprintf( buffer, fmt, args ); va_end( args ); LOGW("Message in NOMADgvrT"); if (*fmt=='\0') LOGW("Empty format"); LOGW("<%s>", buffer); } // TODO(sanjayc): Refactor this app to reuse treasure_hunt_renderer from: // vr/gvr/demos/treasurehunt. http://b/28880883 namespace { //static const int kTextureFormat = GL_RGB; //static const int kTextureType = GL_UNSIGNED_BYTE; //static const float kZNear = 1.0f; //static const float kZFar = 100.0f; static const float kZNear = 0.01f; static const float kZFar = 1000.0f; static const uint64_t kPredictionTimeWithoutVsyncNanos = 50000000; static std::array MatrixToGLArray(const gvr::Mat4f& matrix) { // Note that this performs a *tranpose* to a column-major matrix array, as // expected by GL. std::array result; for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { result[j * 4 + i] = matrix.m[i][j]; } } return result; } static std::array MatrixVectorMul(const gvr::Mat4f& matrix, const std::array& vec) { std::array result; for (int i = 0; i < 4; ++i) { result[i] = 0; for (int k = 0; k < 4; ++k) { result[i] += matrix.m[i][k]*vec[k]; } } return result; } static gvr::Mat4f MatrixMul(const gvr::Mat4f& matrix1, const gvr::Mat4f& matrix2) { gvr::Mat4f result; for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { result.m[i][j] = 0.0f; for (int k = 0; k < 4; ++k) { result.m[i][j] += matrix1.m[i][k]*matrix2.m[k][j]; } } } return result; } static gvr::Mat4f PerspectiveMatrixFromView(const gvr::Rectf& fov, float z_near, float z_far) { gvr::Mat4f result; const float x_left = -std::tan(fov.left * M_PI / 180.0f) * z_near; const float x_right = std::tan(fov.right * M_PI / 180.0f) * z_near; const float y_bottom = -std::tan(fov.bottom * M_PI / 180.0f) * z_near; const float y_top = std::tan(fov.top * M_PI / 180.0f) * z_near; const float zero = 0.0f; assert(x_left < x_right && y_bottom < y_top && z_near < z_far && z_near > zero && z_far > zero); const float X = (2 * z_near) / (x_right - x_left); const float Y = (2 * z_near) / (y_top - y_bottom); const float A = (x_right + x_left) / (x_right - x_left); const float B = (y_top + y_bottom) / (y_top - y_bottom); const float C = (z_near + z_far) / (z_near - z_far); const float D = (2 * z_near * z_far) / (z_near - z_far); for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { result.m[i][j] = 0.0f; } } result.m[0][0] = X; result.m[0][2] = A; result.m[1][1] = Y; result.m[1][2] = B; result.m[2][2] = C; result.m[2][3] = D; result.m[3][2] = -1; return result; } static gvr::Rectf ModulateRect(const gvr::Rectf& rect, float width, float height) { gvr::Rectf result = {rect.left * width, rect.right * width, rect.bottom * height, rect.top * height}; return result; } static gvr::Recti CalculatePixelSpaceRect(const gvr::Sizei& texture_size, const gvr::Rectf& texture_rect) { float width = static_cast(texture_size.width); float height = static_cast(texture_size.height); gvr::Rectf rect = ModulateRect(texture_rect, width, height); gvr::Recti result = { static_cast(rect.left), static_cast(rect.right), static_cast(rect.bottom), static_cast(rect.top)}; return result; } static void CheckGLError(const char* label) { int gl_error = glGetError(); if (gl_error != GL_NO_ERROR) { LOGW("GL error @ %s: %d", label, gl_error); } assert(glGetError() == GL_NO_ERROR); } } // namespace void TreasureHuntRenderer::setConfigFile (NSString * filename) { configfilename=filename; loadConfigFile(); } void TreasureHuntRenderer::keypress (int k) { switch (k) { //A B C D -> Y A X B //case 'u': case 'h': animateMovement=!animateMovement; break; case 'y': animateTimesteps=!animateTimesteps; break; //case 'j': //up down left right case 'w': currentSet++; if (currentSet>TIMESTEPS-1) currentSet=0; break; case 'x': currentSet--; if (currentSet<0) currentSet=TIMESTEPS-1; break; case 'a': currentIso++; if (currentIso>ISOS) currentIso=0; break; case 'd': currentIso--; if (currentIso<0) currentIso=ISOS; break; } } void TreasureHuntRenderer::loadConfigFile(void) { if (configfilename==nullptr) return; if ((error=::loadConfigFile(configfilename.UTF8String))<0) { if (-100CreateBufferViewport()) { controllers=[GCController controllers]; //won't have config file until later // loadConfigFile(); } TreasureHuntRenderer::~TreasureHuntRenderer() {} void TreasureHuntRenderer::InitializeGl() { gvr_api_->InitializeGl(); glGenTextures(2, textures); glGenTextures(2+ZLAYERS, textDepthPeeling); //white unsigned char data2[4]={255,255,255,255}; //white texture for non-textured meshes glBindTexture(GL_TEXTURE_2D, textures[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2); // Create the programs //Leave atoms until we check if android 7 has gles 3.2 or if we use the old icosahedron method with no tesselation if (!PrepareUnitCellShader (&UnitCellP, &UnitCellMatrixLoc, &UnitCellColourLoc)) { eprintf("OneTimeInit, failure compiling Unit Cell Shader"); error=-401; return ; } //rgh: for now, we don't have any tess-ready phones //if (!PrepareAtomShader(&AtomsP, &AtomMatrixLoc)) { hasTess=false; if (!PrepareAtomShaderNoTess(&AtomsP, &AtomMatrixLoc, &totalatomsLocation)) { error=-402; eprintf ("PrepareAtomShaderNoTess failed"); } //}; //atom texture int e; e=atomTexture(textures[1]); if (e!=GL_NO_ERROR) { eprintf ("atomTexture error %d", e); error=-403; } e=SetupAtoms(&AtomTVAO, &AtomTBuffer, &BondIndices); if (e!=GL_NO_ERROR) { eprintf ("SetupAtoms error %d", e); error=-404; } if (!hasTess) e=SetupAtomsNoTess(&AtomVAO, &AtomBuffer, &AtomIndices); if (e!=GL_NO_ERROR) { eprintf ("SetupAtomsNoTess error %d, tess=%d", e, hasTess); error=-405; } e=SetupUnitCell(&UnitCellVAO, &UnitCellBuffer, &UnitCellIndexBuffer); if (e!=GL_NO_ERROR) { eprintf ("SetupUnitCell error %d", e); error=-406; } e=SetupMarkerNoTess(&MarkerVAO, &MarkerVertBuffer, &MarkerIndexBuffer); if (e!=GL_NO_ERROR) { eprintf ("SetupMarkerNoTess error %d", e); error=-411; } std::vector specs; specs.push_back(gvr_api_->CreateBufferSpec()); render_size_ = specs[0].GetSize(); swapchain_.reset(new gvr::SwapChain(gvr_api_->CreateSwapChain(specs))); viewport_list_.reset(new gvr::BufferViewportList(gvr_api_->CreateEmptyBufferViewportList())); openGLIsInitialized=true; } void TreasureHuntRenderer::DrawFrame() { if (!openGLIsInitialized) return; viewport_list_->SetToRecommendedBufferViewports(); if (animateTimesteps) { if (animationspeed>1) currentSet+=animationspeed; else { static float current=0; current+=animationspeed; if (current>1) { currentSet++; current=0; } } if (currentSet>TIMESTEPS-1) currentSet=0; } gvr::Frame frame = swapchain_->AcquireFrame(); // A client app does its rendering here. gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow(); target_time.monotonic_system_time_nanos += kPredictionTimeWithoutVsyncNanos; head_view_ = gvr_api_->GetHeadSpaceFromStartSpaceRotation(target_time); gvr::Mat4f left_eye_view_pose = MatrixMul(gvr_api_->GetEyeFromHeadMatrix(GVR_LEFT_EYE), head_view_); gvr::Mat4f right_eye_view_pose = MatrixMul(gvr_api_->GetEyeFromHeadMatrix(GVR_RIGHT_EYE), head_view_); if (animateMovement) { const float speed=0.01; gvr::Mat4f inv=invert(head_view_); std::array dir({0,0,1,0}); // {0,0,1,0} std::array dir2=MatrixVectorMul(inv, dir); for (int i=0;i<3;i++) UserTranslation[i]+=dir2[i]*speed*movementspeed; } glEnable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); //glCullFace(GL_FRONT); glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); frame.BindBuffer(0); if (error<0) { //rgh: FIXME, add textures with messages here if (-100GetBufferViewport(0, &scratch_viewport_); DrawEye(GVR_LEFT_EYE, left_eye_view_pose, scratch_viewport_); viewport_list_->GetBufferViewport(1, &scratch_viewport_); DrawEye(GVR_RIGHT_EYE, right_eye_view_pose, scratch_viewport_); // Bind back to the default framebuffer. frame.Unbind(); frame.Submit(*viewport_list_, head_view_); CheckGLError("onDrawFrame"); } void TreasureHuntRenderer::OnTriggerEvent() { animateMovement=!animateMovement; AudioServicesPlayAlertSound(kSystemSoundID_Vibrate); } void TreasureHuntRenderer::OnPause() { gvr_api_->PauseTracking(); } void TreasureHuntRenderer::OnResume() { gvr_api_->RefreshViewerProfile(); gvr_api_->ResumeTracking(); } void TreasureHuntRenderer::RenderUnitCell(const gvr::Mat4f eyeViewProjection) { //eprintf ("eyeViewProjection"); //for (int i=0;i<4;i++) // for (int j=0;j<4;j++) // eprintf ("%d %d = %f", i, j, eyeViewProjection.m[i][j]); //eprintf ("RenderUnitCell, has_abc=%d", has_abc); if (!has_abc) return; if (UnitCellVAO==0) eprintf ("Error, Unit Cell VAO not loaded"); int e; int p[3]; gvr::Mat4f sc=ScalingMatrix(scaling); for (p[0]=0;p[0]nFaces); int e; if (numAtoms==0) return; if (hasTess) { //FIXME, unimplemented LOGW("FIXME, No Tess code for atoms yet!"); return; } else { //no tess if (AtomVAO==nullptr) //not yet loaded return; glBindVertexArray(AtomVAO[0]); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, AtomIndices[0]); if ((e = glGetError()) != GL_NO_ERROR) eprintf("1 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); glBindBuffer(GL_ARRAY_BUFFER, AtomBuffer[0]); if ((e = glGetError()) != GL_NO_ERROR) eprintf("2 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7*sizeof(float), (const void *)0); if ((e = glGetError()) != GL_NO_ERROR) eprintf("3 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (const void *)(3*sizeof(float))); if ((e = glGetError()) != GL_NO_ERROR) eprintf("4 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (const void *)(6 * sizeof(float))); if ((e = glGetError()) != GL_NO_ERROR) eprintf("5 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); glBindTexture(GL_TEXTURE_2D, textures[1]); if ((e = glGetError()) != GL_NO_ERROR) eprintf("6 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); glUseProgram(AtomsP); glUniform1f(totalatomsLocation, (float)getTotalAtomsInTexture()); if ((e = glGetError()) != GL_NO_ERROR) eprintf("7 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); glUniformMatrix4fv(AtomMatrixLoc, 1, GL_FALSE, m); if ((e = glGetError()) != GL_NO_ERROR) eprintf("8 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); if (currentSet==0) { glDrawElements(GL_TRIANGLES, numAtoms[currentSet]* 3 * solid->nFaces, #ifndef INDICESGL32 GL_UNSIGNED_SHORT, #else GL_UNSIGNED_INT, #endif 0); if ((e = glGetError()) != GL_NO_ERROR) eprintf("9 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); } else { glDrawElements(GL_TRIANGLES, (numAtoms[currentSet]-numAtoms[currentSet-1]) * 3 * solid->nFaces, #ifndef INDICESGL32 GL_UNSIGNED_SHORT, (void*)(numAtoms[currentSet-1]*sizeof(unsigned short)*3*solid->nFaces) #else GL_UNSIGNED_INT, (void*)(numAtoms[currentSet-1]*sizeof(unsigned int)*3*solid->nFaces) #endif ); if ((e = glGetError()) != GL_NO_ERROR) eprintf("10 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); } if ((e = glGetError()) != GL_NO_ERROR) eprintf("Gl error after Render Atom timestep =%d: %d\n", currentSet, e); //now cloned atoms if (numClonedAtoms!=0 && currentSet==0) { glBindVertexArray(AtomVAO[1]); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, AtomIndices[1]); glBindBuffer(GL_ARRAY_BUFFER, AtomBuffer[1]); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7*sizeof(float), (const void *)0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (const void *)(3*sizeof(float))); glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (const void *)(6 * sizeof(float))); if ((e = glGetError()) != GL_NO_ERROR) eprintf("5 Gl error RenderAtom timestep =%d: %d\n", currentSet, e); glDrawElements(GL_TRIANGLES, numClonedAtoms* 3 * solid->nFaces, #ifndef INDICESGL32 GL_UNSIGNED_SHORT, #else GL_UNSIGNED_INT, #endif 0); if ((e = glGetError()) != GL_NO_ERROR) eprintf("Gl error after Render cloned Atom timestep =%d: %d\n", currentSet, e); } // painting cloned atoms //now bonds if (numBonds && displaybonds /*&& showAtoms*/) { glLineWidth(bondThickness); glBindVertexArray(AtomTVAO[2]); glUseProgram(UnitCellP); glUniformMatrix4fv(UnitCellMatrixLoc, 1, GL_FALSE, m); glUniform4fv(UnitCellColourLoc, 1, bondscolours); if ((e = glGetError()) != GL_NO_ERROR) eprintf("Gl error after Render Atom bonds uniform timestep =%d: %d\n", currentSet, e); if (currentSet==0||fixedAtoms) glDrawElements(GL_LINES, numBonds[0], GL_UNSIGNED_INT, (void*)0); else glDrawElements(GL_LINES, numBonds[currentSet]-numBonds[currentSet-1], GL_UNSIGNED_INT, (void*)(sizeof(int)*numBonds[currentSet-1]) ); glLineWidth(1.0f); if ((e = glGetError()) != GL_NO_ERROR) eprintf("Gl error after Render Atom bonds timestep =%d: %d\n", currentSet, e); glBindVertexArray(0); } } // no tess } void TreasureHuntRenderer::RenderAtomTrajectories(const gvr::Mat4f eyeViewProjection) { int e; if (!numAtoms) return; gvr::Mat4f sc=ScalingMatrix(scaling); gvr::Mat4f trans=TranslationMatrix (UserTranslation[0]/scaling, UserTranslation[1]/scaling, UserTranslation[2]/scaling); const gvr::Mat4f rot={.m={1,0,0,0, 0,0,1,0, 0,-1,0,0, 0,0,0,1}}; //trans.translate(iPos).rotateX(-90).translate(UserPosition); gvr::Mat4f transform = MatrixMul(eyeViewProjection, MatrixMul(sc, MatrixMul(trans,rot))); //gvr::Mat4f transform=eyeViewProjection; float t[16]; for (int i=0;i<4;i++) for (int j=0;j<4;j++) t[j*4+i]=transform.m[i][j]; glUseProgram(UnitCellP); glUniformMatrix4fv(UnitCellMatrixLoc, 1, GL_FALSE, t); if ((e = glGetError()) != GL_NO_ERROR) eprintf("Gl error after glUniform4fv 1 RenderAtomTrajectories: %d\n", e); RenderAtomTrajectoriesUnitCell(); RenderAtoms(t); RenderMarker(t); } void TreasureHuntRenderer::RenderIsos(const gvr::Mat4f eyeViewProjection, int curDataPos) { GLenum e; gvr::Mat4f trans=TranslationMatrix (UserTranslation[0], UserTranslation[1], UserTranslation[2]); const gvr::Mat4f rot={.m={1,0,0,0, 0,0,1,0, 0,-1,0,0, 0,0,0,1}}; gvr::Mat4f transform = MatrixMul(eyeViewProjection, MatrixMul(trans,rot)); float t[16]; for (int i=0;i<4;i++) for (int j=0;j<4;j++) t[j*4+i]=transform.m[i][j]; glUseProgram(ISOP); if ((e = glGetError()) != GL_NO_ERROR) eprintf("1 Gl error RenderIsos timestep =%d: %d\n", currentSet, e); glUniformMatrix4fv(ISOMatrixLoc, 1, GL_FALSE, t); if ((e = glGetError()) != GL_NO_ERROR) eprintf("2 Gl error RenderIsos timestep =%d: %d\n", currentSet, e); if (curDataPos!=ISOS) { glBindVertexArray(ISOVAO[currentSet*ISOS+curDataPos]); if ((e = glGetError()) != GL_NO_ERROR) eprintf("3 Gl error RenderIsos timestep =%d: %d\n", currentSet, e); //eprintf ("Drawing %d vertices, isos", numISOIndices[currentSet*ISOS+curDataPos]); glDrawElements(GL_TRIANGLES,numISOIndices[currentSet*ISOS+curDataPos] , GL_UNSIGNED_INT, 0); if ((e = glGetError()) != GL_NO_ERROR) eprintf("4 Gl error RenderIsos timestep =%d: %d\n", currentSet, e); } else { /*transparency*/ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glDepthMask(GL_FALSE); for (int i=0;i