gnome-model-thumbnailer-0.0~git20210521.9352fb2/000077500000000000000000000000001405173403700206025ustar00rootroot00000000000000gnome-model-thumbnailer-0.0~git20210521.9352fb2/.gitignore000066400000000000000000000000231405173403700225650ustar00rootroot00000000000000builddir/* .vscode gnome-model-thumbnailer-0.0~git20210521.9352fb2/LICENSE000066400000000000000000000020561405173403700216120ustar00rootroot00000000000000MIT License Copyright (c) 2021 Luke Benstead Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gnome-model-thumbnailer-0.0~git20210521.9352fb2/README.md000066400000000000000000000010301405173403700220530ustar00rootroot00000000000000# Gnome Model Thumbnailer This is a thumbnailer for the GNOME desktop that will generate previews of 3D models in your file manager (e.g GNOME Files) Currently it previews .obj, .md2, .md3, and .ms3d but it can easily be extended to produce previews for all formats supported by [assimp](https://github.com/assimp/assimp) ## Installation You'll need meson, ninja, and the required dependencies: ``` $ mkdir builddir $ cd builddir $ meson .. $ ninja $ ninja install ``` ## License This project is released under the MIT license. gnome-model-thumbnailer-0.0~git20210521.9352fb2/gnome-model-mime.in000066400000000000000000000016371405173403700242710ustar00rootroot00000000000000 Wavefront OBJ Scene Quake II Model Quake III Model Milkshape 3D Scene gnome-model-thumbnailer-0.0~git20210521.9352fb2/gnome-model.thumbnailer.in000066400000000000000000000003111405173403700256410ustar00rootroot00000000000000[Thumbnailer Entry] TryExec=gnome-model-thumbnailer Exec=gnome-model-thumbnailer -s %s -i %u -o %o MimeType=application/x-tgif;application/x-obj;application/x-md2;application/x-md3;application/x-ms3d; gnome-model-thumbnailer-0.0~git20210521.9352fb2/meson.build000066400000000000000000000011561405173403700227470ustar00rootroot00000000000000project('gnome-model-thumbnailer', 'cpp') egldep = dependency('egl') assimp = dependency('assimp') gl = dependency('gl') glu = dependency('glu') executable( 'gnome-model-thumbnailer', 'src/main.cpp', dependencies: [egldep, assimp, gl, glu], install: true, ) configure_file( input : 'gnome-model.thumbnailer.in', output : 'gnome-model.thumbnailer', copy: true, install: true, install_dir: 'share/thumbnailers' ) configure_file( input : 'gnome-model-mime.in', output : 'gnome-model-thumbnailer.xml', copy: true, install: true, install_dir: 'share/mime/packages' ) gnome-model-thumbnailer-0.0~git20210521.9352fb2/src/000077500000000000000000000000001405173403700213715ustar00rootroot00000000000000gnome-model-thumbnailer-0.0~git20210521.9352fb2/src/main.cpp000066400000000000000000000216571405173403700230340ustar00rootroot00000000000000#define EGL_EGLEXT_PROTOTYPES #include #include #include #include #include #include #include #include #include #include #include #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" FILE* LOG = stdout; const char* usage = "gnome-model-thumnailer -s 128 -i INPUT -o OUTPUT"; void close_log() { fprintf(LOG, "Exiting.\n"); fflush(LOG); fclose(LOG); } void quit(int status=1) { close_log(); quit(status); } static void calc_node_bounds( const C_STRUCT aiScene* scene, C_STRUCT aiNode* nd, C_STRUCT aiVector3D* min, C_STRUCT aiVector3D* max, C_STRUCT aiMatrix4x4* trafo) { C_STRUCT aiMatrix4x4 prev; unsigned int n = 0, t; prev = *trafo; aiMultiplyMatrix4(trafo,&nd->mTransformation); for (; n < nd->mNumMeshes; ++n) { const C_STRUCT aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]]; for (t = 0; t < mesh->mNumVertices; ++t) { C_STRUCT aiVector3D tmp = mesh->mVertices[t]; aiTransformVecByMatrix4(&tmp,trafo); min->x = std::min(min->x, tmp.x); min->y = std::min(min->y, tmp.y); min->z = std::min(min->z, tmp.z); max->x = std::max(max->x, tmp.x); max->y = std::max(max->y, tmp.y); max->z = std::max(max->z, tmp.z); } } for (n = 0; n < nd->mNumChildren; ++n) { calc_node_bounds(scene, nd->mChildren[n], min, max, trafo); } *trafo = prev; } void calc_scene_bounds(const C_STRUCT aiScene* scene, C_STRUCT aiVector3D* min, C_STRUCT aiVector3D* max) { C_STRUCT aiMatrix4x4 trafo; aiIdentityMatrix4(&trafo); min->x = min->y = min->z = std::numeric_limits::max(); max->x = max->y = max->z = std::numeric_limits::lowest(); calc_node_bounds(scene, scene->mRootNode, min, max, &trafo); } void init_GL(int32_t size) { constexpr EGLint context_attribs [] = { EGL_CONTEXT_MAJOR_VERSION, 2, EGL_CONTEXT_MINOR_VERSION, 1, EGL_NONE }; constexpr EGLint surface_attribs [] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE }; const EGLint pbuffer_attribs [] = { EGL_WIDTH, size, EGL_HEIGHT, size, EGL_NONE, }; static const int MAX_DEVICES = 4; EGLDeviceEXT eglDevs[MAX_DEVICES]; EGLint numDevices; PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC) eglGetProcAddress("eglQueryDevicesEXT"); eglQueryDevicesEXT(MAX_DEVICES, eglDevs, &numDevices); PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC) eglGetProcAddress("eglGetPlatformDisplayEXT"); fprintf(LOG, "Detected %d devices\n", numDevices); EGLDisplay display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, eglDevs[0], 0); if(display == EGL_NO_DISPLAY) { fprintf(LOG, "eglGetDisplay: Unable to get display"); quit(1); } EGLBoolean result = eglInitialize(display, 0, 0); if(result != EGL_TRUE) { fprintf(LOG, "eglInitialize: 0x%x\n", eglGetError()); quit(1); } int config_count; EGLConfig config; result = eglChooseConfig(display, surface_attribs, &config, 1, &config_count); if(result != EGL_TRUE) { fprintf(LOG, "eglChooseConfig: 0x%x\n", eglGetError()); quit(1); } if(config_count != 1) { fprintf(LOG, "Error: eglChooseConfig(): config not found.\n"); quit(-1); } fprintf(LOG, "Creating surface...\n"); EGLSurface surface; surface = eglCreatePbufferSurface(display, config, pbuffer_attribs); eglBindAPI(EGL_OPENGL_API); fprintf(LOG, "Creating context...\n"); EGLContext context; context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attribs); eglMakeCurrent(display, surface, surface, context); } void render_mesh_node(const C_STRUCT aiScene* scene, const C_STRUCT aiNode* node) { C_STRUCT aiMatrix4x4 m = node->mTransformation; /* update transform */ aiTransposeMatrix4(&m); glPushMatrix(); glMultMatrixf((float*)&m); /* draw all meshes assigned to this node */ for (uint32_t n = 0; n < node->mNumMeshes; ++n) { const C_STRUCT aiMesh* mesh = scene->mMeshes[node->mMeshes[n]]; //apply_material(sc->mMaterials[mesh->mMaterialIndex]); if(mesh->mNormals == NULL) { glDisable(GL_LIGHTING); } else { glEnable(GL_LIGHTING); } for (uint32_t t = 0; t < mesh->mNumFaces; ++t) { const C_STRUCT aiFace* face = &mesh->mFaces[t]; GLenum face_mode; switch(face->mNumIndices) { case 1: face_mode = GL_POINTS; break; case 2: face_mode = GL_LINES; break; case 3: face_mode = GL_TRIANGLES; break; default: face_mode = GL_POLYGON; break; } glBegin(face_mode); for(uint32_t i = 0; i < face->mNumIndices; i++) { int index = face->mIndices[i]; if(mesh->mColors[0] != NULL) glColor4fv((GLfloat*)&mesh->mColors[0][index]); if(mesh->mNormals != NULL) glNormal3fv(&mesh->mNormals[index].x); glVertex3fv(&mesh->mVertices[index].x); } glEnd(); } } for (uint32_t n = 0; n < node->mNumChildren; ++n) { render_mesh_node(scene, node->mChildren[n]); } glPopMatrix(); } void load_and_render(const std::string& model, uint32_t size, std::vector& data) { fprintf(LOG, "Rendering\n"); assert(glGetError() == GL_NO_ERROR); const float ratio = float(size) / float(size); const float fov = 45.0f; glEnable(GL_LIGHT0); glDisable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); /* Set up projection matrix */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(fov, ratio, 1.0, 1000.0); glViewport(0, 0, size, size); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); const C_STRUCT aiScene* scene = aiImportFile( model.c_str(), aiProcessPreset_TargetRealtime_MaxQuality ); if(!scene) { fprintf(LOG, "%s\n", aiGetErrorString()); quit(1); } assert(scene); GLuint scene_list = 0; C_STRUCT aiVector3D scene_min, scene_max, scene_center; calc_scene_bounds(scene, &scene_min, &scene_max); scene_center.x = scene_min.x + ((scene_max.x - scene_min.x) / 2.0f); scene_center.y = scene_min.y + ((scene_max.y - scene_min.y) / 2.0f); scene_center.z = scene_min.z + ((scene_max.z - scene_min.z) / 2.0f); float scale = scene_max.x - scene_min.x; scale = std::max(scene_max.y - scene_min.y, scale); scale = std::max(scene_max.z - scene_min.z, scale); printf("%f\n", scale); printf("%f %f %f\n", scene_min.x, scene_min.y, scene_min.z); printf("%f %f %f\n", scene_max.x, scene_max.y, scene_max.z); printf("%f %f %f\n", scene_center.x, scene_center.y, scene_center.z); gluLookAt( scene_center.x, scene_center.y, scene_center.z + 2.5f, scene_center.x, scene_center.y, scene_center.z, 0.0f, 1.0f, 0.0f ); /* Clear the background */ glClearColor(0.75f, 0.75f, 0.75f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDisable(GL_CULL_FACE); glScalef(1.0f / scale, 1.0f / scale, 1.0f / scale); /* Rendering time \o/ */ render_mesh_node(scene, scene->mRootNode); GLint format = 0, type = 0; glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &format); glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &type); // FIXME: Better checking printf("Format: 0x%x Type: 0x%x\n", format, type); assert(format == GL_RGBA); assert(type == GL_UNSIGNED_BYTE); data.resize(size * size * 4); glReadPixels(0, 0, size, size, format, type, &data[0]); } int main(int argc, char* argv[]) { LOG = fopen("/tmp/log.txt", "wt"); atexit(close_log); fprintf(LOG, "Initializing\n"); fflush(LOG); std::string size, input, output; for(int i = 1; i < argc - 1; i += 2) { std::string arg0 = argv[i]; std::string arg1 = argv[i + 1]; if(arg0 == "-s") { size = arg1; } else if(arg0 == "-i") { input = arg1; } else if(arg0 == "-o") { output = arg1; } else { printf("%s\n", usage); return -1; } } if(size.empty() || input.empty() || output.empty()) { printf("%s\n", usage); return -2; } fprintf(LOG, "Loading: %s\n", input.c_str()); fflush(LOG); if(input.find("file://") == 0) { auto l = std::string("file://").length(); input = input.substr(l, input.size() - l); } fprintf(LOG, "Loading: %s\n", input.c_str()); fflush(LOG); int px = std::stoi(size); init_GL(px); std::vector image_data; load_and_render(input, px, image_data); stbi_write_png(output.c_str(), px, px, 4, &image_data[0], px * 4); return 0; }