/* * Copyright 2017 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 "util.h" #include #include #include #include #include #include "jni_interface.h" namespace hello_ar { namespace util { void CheckGlError(const char *operation) { bool anyError = false; for (GLint error = glGetError(); error; error = glGetError()) { LOGE("after %s() glError (0x%x)\n", operation, error); anyError = true; } if (anyError) { abort(); } } // Convenience function used in CreateProgram below. static GLuint LoadShader(GLenum shader_type, const char *shader_source) { GLuint shader = glCreateShader(shader_type); if (!shader) { return shader; } glShaderSource(shader, 1, &shader_source, nullptr); glCompileShader(shader); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if (!compiled) { GLint info_len = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len); if (!info_len) { return shader; } char *buf = reinterpret_cast(malloc(info_len)); if (!buf) { return shader; } glGetShaderInfoLog(shader, info_len, nullptr, buf); LOGE("hello_ar::util::Could not compile shader %d:\n%s\n", shader_type, buf); free(buf); glDeleteShader(shader); shader = 0; } return shader; } GLuint CreateProgram(const char *vertex_source, const char *fragment_source) { GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertex_source); if (!vertexShader) { return 0; } GLuint fragment_shader = LoadShader(GL_FRAGMENT_SHADER, fragment_source); if (!fragment_shader) { return 0; } GLuint program = glCreateProgram(); if (program) { glAttachShader(program, vertexShader); CheckGlError("hello_ar::util::glAttachShader"); glAttachShader(program, fragment_shader); CheckGlError("hello_ar::util::glAttachShader"); glLinkProgram(program); GLint link_status = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &link_status); if (link_status != GL_TRUE) { GLint buf_length = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &buf_length); if (buf_length) { char *buf = reinterpret_cast(malloc(buf_length)); if (buf) { glGetProgramInfoLog(program, buf_length, nullptr, buf); LOGE("hello_ar::util::Could not link program:\n%s\n", buf); free(buf); } } glDeleteProgram(program); program = 0; } } return program; } bool LoadPngFromAssetManager(int target, const std::string &path) { JNIEnv *env = GetJniEnv(); // Put all the JNI values in a structure that is statically initalized on the // first call to this method. This makes it thread safe in the unlikely case // of multiple threads calling this method. static struct JNIData { jclass helper_class; jmethodID load_image_method; jmethodID load_texture_method; } jniIds = [env]() -> JNIData { constexpr char kHelperClassName[] = "org/skia/arcore/JniInterface"; constexpr char kLoadImageMethodName[] = "loadImage"; constexpr char kLoadImageMethodSignature[] = "(Ljava/lang/String;)Landroid/graphics/Bitmap;"; constexpr char kLoadTextureMethodName[] = "loadTexture"; constexpr char kLoadTextureMethodSignature[] = "(ILandroid/graphics/Bitmap;)V"; jclass helper_class = FindClass(kHelperClassName); if (helper_class) { helper_class = static_cast(env->NewGlobalRef(helper_class)); jmethodID load_image_method = env->GetStaticMethodID( helper_class, kLoadImageMethodName, kLoadImageMethodSignature); jmethodID load_texture_method = env->GetStaticMethodID( helper_class, kLoadTextureMethodName, kLoadTextureMethodSignature); return {helper_class, load_image_method, load_texture_method}; } LOGE("hello_ar::util::Could not find Java helper class %s", kHelperClassName); return {}; }(); if (!jniIds.helper_class) { return false; } jstring j_path = env->NewStringUTF(path.c_str()); jobject image_obj = env->CallStaticObjectMethod( jniIds.helper_class, jniIds.load_image_method, j_path); if (j_path) { env->DeleteLocalRef(j_path); } env->CallStaticVoidMethod(jniIds.helper_class, jniIds.load_texture_method, target, image_obj); return true; } void GetTransformMatrixFromPose(ArSession *ar_session, const ArPose *ar_pose, glm::mat4 *out_model_mat) { if (out_model_mat == nullptr) { LOGE("util::GetTransformMatrixFromPose model_mat is null."); return; } ArPose_getMatrix(ar_session, ar_pose, glm::value_ptr(*out_model_mat)); } glm::vec3 GetPlaneNormal(const ArSession *ar_session, const ArPose &plane_pose) { float plane_pose_raw[7] = {0.f}; ArPose_getPoseRaw(ar_session, &plane_pose, plane_pose_raw); glm::quat plane_quaternion(plane_pose_raw[3], plane_pose_raw[0], plane_pose_raw[1], plane_pose_raw[2]); // Get normal vector, normal is defined to be positive Y-position in local // frame. return glm::rotate(plane_quaternion, glm::vec3(0., 1.f, 0.)); } float CalculateDistanceToPlane(const ArSession *ar_session, const ArPose &plane_pose, const ArPose &camera_pose) { float plane_pose_raw[7] = {0.f}; ArPose_getPoseRaw(ar_session, &plane_pose, plane_pose_raw); glm::vec3 plane_position(plane_pose_raw[4], plane_pose_raw[5], plane_pose_raw[6]); glm::vec3 normal = GetPlaneNormal(ar_session, plane_pose); float camera_pose_raw[7] = {0.f}; ArPose_getPoseRaw(ar_session, &camera_pose, camera_pose_raw); glm::vec3 camera_P_plane(camera_pose_raw[4] - plane_position.x, camera_pose_raw[5] - plane_position.y, camera_pose_raw[6] - plane_position.z); return glm::dot(normal, camera_P_plane); } glm::mat4 GetCameraRotationMatrix(float cameraOutRaw[]) { glm::mat4 cameraRotation(1); glm::quat cameraQuat = glm::quat(cameraOutRaw[0], cameraOutRaw[1], cameraOutRaw[2], cameraOutRaw[3]); cameraRotation = glm::toMat4(cameraQuat); glm::vec4 temp = cameraRotation[0]; cameraRotation[0] = cameraRotation[2]; cameraRotation[2] = temp; return cameraRotation; } void GetCameraInfo(ArSession* arSession, ArFrame* arFrame, glm::vec3& cameraPos, glm::mat4& cameraRotation) { //Acquire camera ArCamera *ar_camera; ArFrame_acquireCamera(arSession, arFrame, &ar_camera); //Get camera pose ArPose *camera_pose = nullptr; ArPose_create(arSession, nullptr, &camera_pose); ArCamera_getDisplayOrientedPose(arSession, ar_camera, camera_pose); //Get camera raw info float outCameraRaw[] = {0, 0, 0, 0, 0, 0, 0}; ArPose_getPoseRaw(arSession, camera_pose, outCameraRaw); ArPose_destroy(camera_pose); //Write to out variables cameraPos = glm::vec3(outCameraRaw[4], outCameraRaw[5], outCameraRaw[6]); cameraRotation = util::GetCameraRotationMatrix(outCameraRaw); //Release camera ArCamera_release(ar_camera); } SkMatrix44 GlmMatToSkMat(const glm::mat4 m) { SkMatrix44 skMat = SkMatrix44::kIdentity_Constructor; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { skMat.set(j, i, m[i][j]); } } return skMat; } glm::mat4 SkMatToGlmMat(const SkMatrix44 m) { glm::mat4 glmMat(1); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { glmMat[i][j] = m.get(j, i); } } return glmMat; } void Log4x4Matrix(float raw_matrix[16]) { LOGI( "%f, %f, %f, %f\n" "%f, %f, %f, %f\n" "%f, %f, %f, %f\n" "%f, %f, %f, %f\n", raw_matrix[0], raw_matrix[1], raw_matrix[2], raw_matrix[3], raw_matrix[4], raw_matrix[5], raw_matrix[6], raw_matrix[7], raw_matrix[8], raw_matrix[9], raw_matrix[10], raw_matrix[11], raw_matrix[12], raw_matrix[13], raw_matrix[14], raw_matrix[15]); } void LogGlmMat(glm::mat4 m, char *type) { std::string str = glm::to_string(m); LOGE("glm Matrix - %s: %s\n", type, str.c_str()); } void LogSkMat44(SkMatrix44 m, char *type) { LOGE("SkMatrix - %s: [%g, %g, %g, %g] || [%g, %g, %g, %g] || [%g, %g, %g, %g] || [%g, %g, %g, %g] \n", type, m.get(0, 0), m.get(1, 0), m.get(2, 0), m.get(3, 0), m.get(0, 1), m.get(1, 1), m.get(2, 1), m.get(3, 1), m.get(0, 2), m.get(1, 2), m.get(2, 2), m.get(3, 2), m.get(0, 3), m.get(1, 3), m.get(2, 3), m.get(3, 3) ); } void LogSkMat(SkMatrix m, char *type) { LOGE("SkMatrix - %s: [%g, %g, %g] || [%g, %g, %g] || [%g, %g, %g] \n", type, m.get(0), m.get(3), m.get(6), m.get(1), m.get(4), m.get(7), m.get(2), m.get(5), m.get(8) ); } void LogOrientation(float rotationDirection, float angleRad, char *type) { LOGI("Plane orientation: %s", type); LOGI("Cross dotted with zDir:", rotationDirection); if (rotationDirection == -1) { LOGI("Counter Clockwise %.6f degrees rotation: ", glm::degrees(angleRad)); } else { LOGI("Clockwise %.6f degrees rotation: ", glm::degrees(angleRad)); } } float Dot(glm::vec3 u, glm::vec3 v) { float result = u.x * v.x + u.y * v.y + u.z * v.z; return result; } float Magnitude(glm::vec3 u) { float result = u.x * u.x + u.y * u.y + u.z * u.z; return sqrt(result); } float AngleRad(glm::vec3 u, glm::vec3 v) { float dot = util::Dot(u, v); float scale = (util::Magnitude(u) * util::Magnitude(v)); float cosine = dot / scale; float acosine = acos(cosine); return acosine; } glm::vec3 ProjectOntoPlane(glm::vec3 in, glm::vec3 normal) { float dot = util::Dot(in, normal); float multiplier = dot / (util::Magnitude(normal) * util::Magnitude(normal)); glm::vec3 out = in - multiplier * normal; return out; } } // namespace util } // namespace hello_ar