gnome-model-thumbnailer-0.0~git20210521.9352fb2/ 0000775 0000000 0000000 00000000000 14051734037 0020602 5 ustar 00root root 0000000 0000000 gnome-model-thumbnailer-0.0~git20210521.9352fb2/.gitignore 0000664 0000000 0000000 00000000023 14051734037 0022565 0 ustar 00root root 0000000 0000000 builddir/*
.vscode
gnome-model-thumbnailer-0.0~git20210521.9352fb2/LICENSE 0000664 0000000 0000000 00000002056 14051734037 0021612 0 ustar 00root root 0000000 0000000 MIT 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.md 0000664 0000000 0000000 00000001030 14051734037 0022053 0 ustar 00root root 0000000 0000000 # 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.in 0000664 0000000 0000000 00000001637 14051734037 0024271 0 ustar 00root root 0000000 0000000
Wavefront OBJ Scene
Quake II Model
Quake III Model
Milkshape 3D Scene
gnome-model-thumbnailer-0.0~git20210521.9352fb2/gnome-model.thumbnailer.in 0000664 0000000 0000000 00000000311 14051734037 0025641 0 ustar 00root root 0000000 0000000 [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.build 0000664 0000000 0000000 00000001156 14051734037 0022747 0 ustar 00root root 0000000 0000000 project('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/ 0000775 0000000 0000000 00000000000 14051734037 0021371 5 ustar 00root root 0000000 0000000 gnome-model-thumbnailer-0.0~git20210521.9352fb2/src/main.cpp 0000664 0000000 0000000 00000021657 14051734037 0023034 0 ustar 00root root 0000000 0000000 #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;
}