diff options
Diffstat (limited to 'experimental/viewer.cc')
-rw-r--r-- | experimental/viewer.cc | 748 |
1 files changed, 748 insertions, 0 deletions
diff --git a/experimental/viewer.cc b/experimental/viewer.cc new file mode 100644 index 0000000..be5053f --- /dev/null +++ b/experimental/viewer.cc @@ -0,0 +1,748 @@ +// +// Simple .obj viewer(vertex only) +// +#include <vector> +#include <string> +#include <cstdio> +#include <cstdlib> +#include <iostream> +#include <limits> +#include <cmath> +#include <cassert> +#include <cstring> +#include <algorithm> + +#if defined(ENABLE_ZLIB) +#include <zlib.h> +#endif + +#if defined(ENABLE_ZSTD) +#include <zstd.h> +#endif + +#include <GL/glew.h> + +#ifdef __APPLE__ +#include <OpenGL/glu.h> +#else +#include <GL/glu.h> +#endif + +#include <GLFW/glfw3.h> + +#include "trackball.h" + +#define TINYOBJ_LOADER_OPT_IMPLEMENTATION +#include "tinyobj_loader_opt.h" + +typedef struct { + GLuint vb; // vertex buffer + int numTriangles; +} DrawObject; + +std::vector<DrawObject> gDrawObjects; + +int width = 768; +int height = 768; + +double prevMouseX, prevMouseY; +bool mouseLeftPressed; +bool mouseMiddlePressed; +bool mouseRightPressed; +float curr_quat[4]; +float prev_quat[4]; +float eye[3], lookat[3], up[3]; + +GLFWwindow* window; + +void CheckErrors(std::string desc) { + GLenum e = glGetError(); + if (e != GL_NO_ERROR) { + fprintf(stderr, "OpenGL error in \"%s\": %d (%d)\n", desc.c_str(), e, e); + exit(20); + } +} + +void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) { + float v10[3]; + v10[0] = v1[0] - v0[0]; + v10[1] = v1[1] - v0[1]; + v10[2] = v1[2] - v0[2]; + + float v20[3]; + v20[0] = v2[0] - v0[0]; + v20[1] = v2[1] - v0[1]; + v20[2] = v2[2] - v0[2]; + + N[0] = v20[1] * v10[2] - v20[2] * v10[1]; + N[1] = v20[2] * v10[0] - v20[0] * v10[2]; + N[2] = v20[0] * v10[1] - v20[1] * v10[0]; + + float len2 = N[0] * N[0] + N[1] * N[1] + N[2] * N[2]; + if (len2 > 0.0f) { + float len = sqrtf(len2); + + N[0] /= len; + N[1] /= len; + } +} + +const char *mmap_file(size_t *len, const char* filename) +{ + (*len) = 0; +#ifdef _WIN32 + HANDLE file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + assert(file != INVALID_HANDLE_VALUE); + + HANDLE fileMapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL); + assert(fileMapping != INVALID_HANDLE_VALUE); + + LPVOID fileMapView = MapViewOfFile(fileMapping, FILE_MAP_READ, 0, 0, 0); + auto fileMapViewChar = (const char*)fileMapView; + assert(fileMapView != NULL); + + LARGE_INTEGER fileSize; + fileSize.QuadPart = 0; + GetFileSizeEx(file, &fileSize); + + (*len) = static_cast<size_t>(fileSize.QuadPart); + return fileMapViewChar; + +#else + + FILE* f = fopen(filename, "rb" ); + if (!f) { + fprintf(stderr, "Failed to open file : %s\n", filename); + return nullptr; + } + fseek(f, 0, SEEK_END); + long fileSize = ftell(f); + fclose(f); + + if (fileSize < 16) { + fprintf(stderr, "Empty or invalid .obj : %s\n", filename); + return nullptr; + } + + struct stat sb; + char *p; + int fd; + + fd = open (filename, O_RDONLY); + if (fd == -1) { + perror ("open"); + return nullptr; + } + + if (fstat (fd, &sb) == -1) { + perror ("fstat"); + return nullptr; + } + + if (!S_ISREG (sb.st_mode)) { + fprintf (stderr, "%s is not a file\n", "lineitem.tbl"); + return nullptr; + } + + p = (char*)mmap (0, fileSize, PROT_READ, MAP_SHARED, fd, 0); + + if (p == MAP_FAILED) { + perror ("mmap"); + return nullptr; + } + + if (close (fd) == -1) { + perror ("close"); + return nullptr; + } + + (*len) = fileSize; + + return p; + +#endif +} + +bool gz_load(std::vector<char>* buf, const char* filename) +{ +#ifdef ENABLE_ZLIB + gzFile file; + file = gzopen (filename, "r"); + if (! file) { + fprintf (stderr, "gzopen of '%s' failed: %s.\n", filename, + strerror (errno)); + exit (EXIT_FAILURE); + return false; + } + while (1) { + int err; + int bytes_read; + unsigned char buffer[1024]; + bytes_read = gzread (file, buffer, 1024); + buf->insert(buf->end(), buffer, buffer + 1024); + //printf ("%s", buffer); + if (bytes_read < 1024) { + if (gzeof (file)) { + break; + } + else { + const char * error_string; + error_string = gzerror (file, & err); + if (err) { + fprintf (stderr, "Error: %s.\n", error_string); + exit (EXIT_FAILURE); + return false; + } + } + } + } + gzclose (file); + return true; +#else + return false; +#endif +} + +#ifdef ENABLE_ZSTD +static off_t fsize_X(const char *filename) +{ + struct stat st; + if (stat(filename, &st) == 0) return st.st_size; + /* error */ + printf("stat: %s : %s \n", filename, strerror(errno)); + exit(1); +} + +static FILE* fopen_X(const char *filename, const char *instruction) +{ + FILE* const inFile = fopen(filename, instruction); + if (inFile) return inFile; + /* error */ + printf("fopen: %s : %s \n", filename, strerror(errno)); + exit(2); +} + +static void* malloc_X(size_t size) +{ + void* const buff = malloc(size); + if (buff) return buff; + /* error */ + printf("malloc: %s \n", strerror(errno)); + exit(3); +} +#endif + +bool zstd_load(std::vector<char>* buf, const char* filename) +{ +#ifdef ENABLE_ZSTD + off_t const buffSize = fsize_X(filename); + FILE* const inFile = fopen_X(filename, "rb"); + void* const buffer = malloc_X(buffSize); + size_t const readSize = fread(buffer, 1, buffSize, inFile); + if (readSize != (size_t)buffSize) { + printf("fread: %s : %s \n", filename, strerror(errno)); + exit(4); + } + fclose(inFile); + + unsigned long long const rSize = ZSTD_getDecompressedSize(buffer, buffSize); + if (rSize==0) { + printf("%s : original size unknown \n", filename); + exit(5); + } + + buf->resize(rSize); + + size_t const dSize = ZSTD_decompress(buf->data(), rSize, buffer, buffSize); + + if (dSize != rSize) { + printf("error decoding %s : %s \n", filename, ZSTD_getErrorName(dSize)); + exit(7); + } + + free(buffer); + + return true; +#else + return false; +#endif +} + +const char* get_file_data(size_t *len, const char* filename) +{ + + const char *ext = strrchr(filename, '.'); + + size_t data_len = 0; + const char* data = nullptr; + + if (strcmp(ext, ".gz") == 0) { + // gzipped data. + + std::vector<char> buf; + bool ret = gz_load(&buf, filename); + + if (ret) { + char *p = static_cast<char*>(malloc(buf.size() + 1)); // @fixme { implement deleter } + memcpy(p, &buf.at(0), buf.size()); + p[buf.size()] = '\0'; + data = p; + data_len = buf.size(); + } + + } else if (strcmp(ext, ".zst") == 0) { + printf("zstd\n"); + // Zstandard data. + + std::vector<char> buf; + bool ret = zstd_load(&buf, filename); + + if (ret) { + char *p = static_cast<char*>(malloc(buf.size() + 1)); // @fixme { implement deleter } + memcpy(p, &buf.at(0), buf.size()); + p[buf.size()] = '\0'; + data = p; + data_len = buf.size(); + } + } else { + + data = mmap_file(&data_len, filename); + + } + + (*len) = data_len; + return data; +} + + +bool LoadObjAndConvert(float bmin[3], float bmax[3], const char* filename, int num_threads, bool verbose) +{ + tinyobj_opt::attrib_t attrib; + std::vector<tinyobj_opt::shape_t> shapes; + std::vector<tinyobj_opt::material_t> materials; + + auto load_t_begin = std::chrono::high_resolution_clock::now(); + size_t data_len = 0; + const char* data = get_file_data(&data_len, filename); + if (data == nullptr) { + printf("failed to load file\n"); + exit(-1); + return false; + } + auto load_t_end = std::chrono::high_resolution_clock::now(); + std::chrono::duration<double, std::milli> load_ms = load_t_end - load_t_begin; + if (verbose) { + std::cout << "filesize: " << data_len << std::endl; + std::cout << "load time: " << load_ms.count() << " [msecs]" << std::endl; + } + + + tinyobj_opt::LoadOption option; + option.req_num_threads = num_threads; + option.verbose = verbose; + bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option); + + if (!ret) { + std::cerr << "Failed to parse .obj" << std::endl; + return false; + } + bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max(); + bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max(); + + //std::cout << "vertices.size() = " << attrib.vertices.size() << std::endl; + //std::cout << "normals.size() = " << attrib.normals.size() << std::endl; + + { + DrawObject o; + std::vector<float> vb; // pos(3float), normal(3float), color(3float) + size_t face_offset = 0; + for (size_t v = 0; v < attrib.face_num_verts.size(); v++) { + assert(attrib.face_num_verts[v] % 3 == 0); // assume all triangle face. + for (size_t f = 0; f < attrib.face_num_verts[v] / 3; f++) { + tinyobj_opt::index_t idx0 = attrib.indices[face_offset+3*f+0]; + tinyobj_opt::index_t idx1 = attrib.indices[face_offset+3*f+1]; + tinyobj_opt::index_t idx2 = attrib.indices[face_offset+3*f+2]; + + float v[3][3]; + for (int k = 0; k < 3; k++) { + int f0 = idx0.vertex_index; + int f1 = idx1.vertex_index; + int f2 = idx2.vertex_index; + assert(f0 >= 0); + assert(f1 >= 0); + assert(f2 >= 0); + + v[0][k] = attrib.vertices[3*f0+k]; + v[1][k] = attrib.vertices[3*f1+k]; + v[2][k] = attrib.vertices[3*f2+k]; + bmin[k] = std::min(v[0][k], bmin[k]); + bmin[k] = std::min(v[1][k], bmin[k]); + bmin[k] = std::min(v[2][k], bmin[k]); + bmax[k] = std::max(v[0][k], bmax[k]); + bmax[k] = std::max(v[1][k], bmax[k]); + bmax[k] = std::max(v[2][k], bmax[k]); + } + + float n[3][3]; + + if (attrib.normals.size() > 0) { + int nf0 = idx0.normal_index; + int nf1 = idx1.normal_index; + int nf2 = idx2.normal_index; + + if (nf0 >= 0 && nf1 >= 0 && nf2 >= 0) { + assert(3*nf0+2 < attrib.normals.size()); + assert(3*nf1+2 < attrib.normals.size()); + assert(3*nf2+2 < attrib.normals.size()); + for (int k = 0; k < 3; k++) { + n[0][k] = attrib.normals[3*nf0+k]; + n[1][k] = attrib.normals[3*nf1+k]; + n[2][k] = attrib.normals[3*nf2+k]; + } + } else { + // compute geometric normal + CalcNormal(n[0], v[0], v[1], v[2]); + n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2]; + n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2]; + } + } else { + // compute geometric normal + CalcNormal(n[0], v[0], v[1], v[2]); + n[1][0] = n[0][0]; n[1][1] = n[0][1]; n[1][2] = n[0][2]; + n[2][0] = n[0][0]; n[2][1] = n[0][1]; n[2][2] = n[0][2]; + } + + for (int k = 0; k < 3; k++) { + vb.push_back(v[k][0]); + vb.push_back(v[k][1]); + vb.push_back(v[k][2]); + vb.push_back(n[k][0]); + vb.push_back(n[k][1]); + vb.push_back(n[k][2]); + // Use normal as color. + float c[3] = {n[k][0], n[k][1], n[k][2]}; + float len2 = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]; + if (len2 > 1.0e-6f) { + float len = sqrtf(len2); + + c[0] /= len; + c[1] /= len; + c[2] /= len; + } + vb.push_back(c[0] * 0.5 + 0.5); + vb.push_back(c[1] * 0.5 + 0.5); + vb.push_back(c[2] * 0.5 + 0.5); + } + } + face_offset += attrib.face_num_verts[v]; + } + + o.vb = 0; + o.numTriangles = 0; + if (vb.size() > 0) { + glGenBuffers(1, &o.vb); + glBindBuffer(GL_ARRAY_BUFFER, o.vb); + glBufferData(GL_ARRAY_BUFFER, vb.size() * sizeof(float), &vb.at(0), GL_STATIC_DRAW); + o.numTriangles = vb.size() / 9 / 3; + } + + gDrawObjects.push_back(o); + } + + printf("bmin = %f, %f, %f\n", bmin[0], bmin[1], bmin[2]); + printf("bmax = %f, %f, %f\n", bmax[0], bmax[1], bmax[2]); + + return true; +} + +void reshapeFunc(GLFWwindow* window, int w, int h) +{ + (void)window; + // for retinal display. + int fb_w, fb_h; + glfwGetFramebufferSize(window, &fb_w, &fb_h); + + glViewport(0, 0, fb_w, fb_h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45.0, (float)w / (float)h, 0.01f, 100.0f); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + width = w; + height = h; +} + +void keyboardFunc(GLFWwindow *window, int key, int scancode, int action, int mods) { + (void)window; + (void)scancode; + (void)mods; + if(action == GLFW_PRESS || action == GLFW_REPEAT){ + // Move camera + float mv_x = 0, mv_y = 0, mv_z = 0; + if(key == GLFW_KEY_K) mv_x += 1; + else if(key == GLFW_KEY_J) mv_x += -1; + else if(key == GLFW_KEY_L) mv_y += 1; + else if(key == GLFW_KEY_H) mv_y += -1; + else if(key == GLFW_KEY_P) mv_z += 1; + else if(key == GLFW_KEY_N) mv_z += -1; + //camera.move(mv_x * 0.05, mv_y * 0.05, mv_z * 0.05); + // Close window + if(key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) glfwSetWindowShouldClose(window, GL_TRUE); + + //init_frame = true; + } +} + +void clickFunc(GLFWwindow* window, int button, int action, int mods){ + (void)window; + (void)mods; + if(button == GLFW_MOUSE_BUTTON_LEFT){ + if(action == GLFW_PRESS){ + mouseLeftPressed = true; + trackball(prev_quat, 0.0, 0.0, 0.0, 0.0); + } else if(action == GLFW_RELEASE){ + mouseLeftPressed = false; + } + } + if(button == GLFW_MOUSE_BUTTON_RIGHT){ + if(action == GLFW_PRESS){ + mouseRightPressed = true; + } else if(action == GLFW_RELEASE){ + mouseRightPressed = false; + } + } + if(button == GLFW_MOUSE_BUTTON_MIDDLE){ + if(action == GLFW_PRESS){ + mouseMiddlePressed = true; + } else if(action == GLFW_RELEASE){ + mouseMiddlePressed = false; + } + } +} + +void motionFunc(GLFWwindow* window, double mouse_x, double mouse_y){ + (void)window; + float rotScale = 1.0f; + float transScale = 2.0f; + + if(mouseLeftPressed){ + trackball(prev_quat, + rotScale * (2.0f * prevMouseX - width) / (float)width, + rotScale * (height - 2.0f * prevMouseY) / (float)height, + rotScale * (2.0f * mouse_x - width) / (float)width, + rotScale * (height - 2.0f * mouse_y) / (float)height); + + add_quats(prev_quat, curr_quat, curr_quat); + } else if (mouseMiddlePressed) { + eye[0] -= transScale * (mouse_x - prevMouseX) / (float)width; + lookat[0] -= transScale * (mouse_x - prevMouseX) / (float)width; + eye[1] += transScale * (mouse_y - prevMouseY) / (float)height; + lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height; + } else if (mouseRightPressed) { + eye[2] += transScale * (mouse_y - prevMouseY) / (float)height; + lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height; + } + + // Update mouse point + prevMouseX = mouse_x; + prevMouseY = mouse_y; +} + +void Draw(const std::vector<DrawObject>& drawObjects) +{ + glPolygonMode(GL_FRONT, GL_FILL); + glPolygonMode(GL_BACK, GL_FILL); + + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.0, 1.0); + glColor3f(1.0f, 1.0f, 1.0f); + for (size_t i = 0; i < drawObjects.size(); i++) { + DrawObject o = drawObjects[i]; + if (o.vb < 1) { + continue; + } + + glBindBuffer(GL_ARRAY_BUFFER, o.vb); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, 36, (const void*)0); + glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3)); + glColorPointer(3, GL_FLOAT, 36, (const void*)(sizeof(float)*6)); + + glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); + CheckErrors("drawarrays"); + } + + // draw wireframe + glDisable(GL_POLYGON_OFFSET_FILL); + glPolygonMode(GL_FRONT, GL_LINE); + glPolygonMode(GL_BACK, GL_LINE); + + glColor3f(0.0f, 0.0f, 0.4f); + for (size_t i = 0; i < drawObjects.size(); i++) { + DrawObject o = drawObjects[i]; + if (o.vb < 1) { + continue; + } + + glBindBuffer(GL_ARRAY_BUFFER, o.vb); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, 36, (const void*)0); + glNormalPointer(GL_FLOAT, 36, (const void*)(sizeof(float)*3)); + + glDrawArrays(GL_TRIANGLES, 0, 3 * o.numTriangles); + CheckErrors("drawarrays"); + } +} + +static void Init() { + trackball(curr_quat, 0, 0, 0, 0); + + eye[0] = 0.0f; + eye[1] = 0.0f; + eye[2] = 3.0f; + + lookat[0] = 0.0f; + lookat[1] = 0.0f; + lookat[2] = 0.0f; + + up[0] = 0.0f; + up[1] = 1.0f; + up[2] = 0.0f; +} + + +int main(int argc, char **argv) +{ + if (argc < 2) { + std::cout << "view input.obj <num_threads> <benchark_only> <verbose>" << std::endl; + return 0; + } + + bool benchmark_only = false; + int num_threads = -1; + bool verbose = false; + + if (argc > 2) { + num_threads = atoi(argv[2]); + } + + if (argc > 3) { + benchmark_only = (atoi(argv[3]) > 0) ? true : false; + } + + if (argc > 4) { + verbose = true; + } + + if (benchmark_only) { + + tinyobj_opt::attrib_t attrib; + std::vector<tinyobj_opt::shape_t> shapes; + std::vector<tinyobj_opt::material_t> materials; + + size_t data_len = 0; + const char* data = get_file_data(&data_len, argv[1]); + if (data == nullptr) { + printf("failed to load file\n"); + exit(-1); + return false; + } + + if (data_len < 4) { + printf("Empty file\n"); + exit(-1); + return false; + } + printf("filesize: %d\n", (int)data_len); + tinyobj_opt::LoadOption option; + option.req_num_threads = num_threads; + option.verbose = true; + + bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option); + + return ret; + } + + Init(); + + std::cout << "Initialize GLFW..." << std::endl; + + if(!glfwInit()){ + std::cerr << "Failed to initialize GLFW." << std::endl; + return -1; + } + + std::cout << "GLFW OK." << std::endl; + + + window = glfwCreateWindow(width, height, "Obj viewer", NULL, NULL); + if(window == NULL){ + std::cerr << "Failed to open GLFW window. " << std::endl; + glfwTerminate(); + return 1; + } + + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + + // Callback + glfwSetWindowSizeCallback(window, reshapeFunc); + glfwSetKeyCallback(window, keyboardFunc); + glfwSetMouseButtonCallback(window, clickFunc); + glfwSetCursorPosCallback(window, motionFunc); + + glewExperimental = true; + if (glewInit() != GLEW_OK) { + std::cerr << "Failed to initialize GLEW." << std::endl; + return -1; + } + + reshapeFunc(window, width, height); + + float bmin[3], bmax[3]; + if (false == LoadObjAndConvert(bmin, bmax, argv[1], num_threads, verbose)) { + printf("failed to load & conv\n"); + return -1; + } + + float maxExtent = 0.5f * (bmax[0] - bmin[0]); + if (maxExtent < 0.5f * (bmax[1] - bmin[1])) { + maxExtent = 0.5f * (bmax[1] - bmin[1]); + } + if (maxExtent < 0.5f * (bmax[2] - bmin[2])) { + maxExtent = 0.5f * (bmax[2] - bmin[2]); + } + + while(glfwWindowShouldClose(window) == GL_FALSE) { + glfwPollEvents(); + glClearColor(0.1f, 0.2f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glEnable(GL_DEPTH_TEST); + + // camera & rotate + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + GLfloat mat[4][4]; + gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0], up[1], up[2]); + build_rotmatrix(mat, curr_quat); + glMultMatrixf(&mat[0][0]); + + // Fit to -1, 1 + glScalef(1.0f / maxExtent, 1.0f / maxExtent, 1.0f / maxExtent); + + // Centerize object. + glTranslatef(-0.5*(bmax[0] + bmin[0]), -0.5*(bmax[1] + bmin[1]), -0.5*(bmax[2] + bmin[2])); + + Draw(gDrawObjects); + + glfwSwapBuffers(window); + } + + glfwTerminate(); +} |