summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorꦌꦫꦶꦏ꧀ꦦꦿꦧꦮꦑꦩꦭ꧀ <erik@darapsa.co.id>2021-06-03 20:28:59 +0800
committerꦌꦫꦶꦏ꧀ꦦꦿꦧꦮꦑꦩꦭ꧀ <erik@darapsa.co.id>2021-06-03 20:28:59 +0800
commit6dfbf8fd561187ef23eacf9aaae606c0fb42381f (patch)
tree42af64e6988c9468b7abedbc9f980722d18a6ca6
Initial working commit
-rw-r--r--.gitignore25
-rw-r--r--COPYING20
-rw-r--r--Makefile.am3
-rw-r--r--README.md19
-rwxr-xr-xaarch64-linux-android-configure9
-rwxr-xr-xarm-apple-darwin11-configure6
-rw-r--r--configure.ac10
-rw-r--r--converter.c67
-rwxr-xr-xi686-w64-mingw32-configure6
-rw-r--r--product.frag7
-rw-r--r--product.vert9
-rw-r--r--productviewer.h18
-rw-r--r--viewer.c233
-rwxr-xr-xwasm-unknown-emscripten-configure5
-rwxr-xr-xx86_64-pc-linux-gnu-configure5
15 files changed, 442 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1c157ce
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+*~
+*.la
+*.lo
+*.o
+*.swp
+*.wasm
+.deps
+.libs
+Makefile
+Makefile.am.user*
+Makefile.in
+aclocal.m4
+ar-lib
+autom4te.cache
+autoscan-2.69.log
+compile
+config.*
+configure
+configure.scan
+depcomp
+install-sh
+libtool
+ltmain.sh
+m4
+missing
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..086fa3c
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Erik Prabowo Kamal
+
+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.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..e11069c
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,3 @@
+lib_LTLIBRARIES = libproductviewer.la
+libproductviewer_la_SOURCES = viewer.c converter.c
+include_HEADERS = productviewer.h
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8ef924e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# Portable 3D viewer using OpenGL ES 2
+
+# Building
+
+## Getting and preparing for configuration
+
+```sh
+$ git clone git://darapsa.org/libproductviewer.git
+$ cd libproductviewer
+$ autoreconf --install
+```
+
+## Configuring for various target hosts, compiling, linking, and installing
+
+```sh
+$ ./configure (or use the platform specific wrappers, and adjust as necessary)
+$ make # -jN (with N an integer number of parallel tasks you allow your computer to run for compiling this)
+$ sudo make install
+```
diff --git a/aarch64-linux-android-configure b/aarch64-linux-android-configure
new file mode 100755
index 0000000..8690ece
--- /dev/null
+++ b/aarch64-linux-android-configure
@@ -0,0 +1,9 @@
+#!/bin/sh
+export TOOLCHAIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64
+export TARGET=aarch64-linux-android
+export API=21
+export CC=$TOOLCHAIN/bin/$TARGET$API-clang
+export PREFIX=$TOOLCHAIN/sysroot/usr
+export CPPFLAGS="$CPPFLAGS -DDEBUG"
+export CFLAGS="$CFLAGS -g"
+./configure --host=$TARGET --prefix=$PREFIX --libdir=$PREFIX/lib/$TARGET/$API --disable-static
diff --git a/arm-apple-darwin11-configure b/arm-apple-darwin11-configure
new file mode 100755
index 0000000..c637d5d
--- /dev/null
+++ b/arm-apple-darwin11-configure
@@ -0,0 +1,6 @@
+#!/bin/sh
+export TARGET=arm-apple-darwin11
+export CC=ios-clang
+export PREFIX=/opt/iPhoneOS8.1.sdk
+export CFLAGS="$CFLAGS -g0 -O2 -I$PREFIX/System/Library/Frameworks/OpenGLES.framework/Headers"
+./configure --host=$TARGET --prefix=$PREFIX/usr --disable-shared
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..d3b25b2
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,10 @@
+AC_INIT([productviewer], [0.0], [pt@darapsa.co.id])
+AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AC_PROG_CC
+AC_CONFIG_FILES([Makefile])
+AC_TYPE_SIZE_T
+AC_CHECK_FUNCS([strrchr])
+AC_CHECK_FUNCS([memset])
+AM_PROG_AR
+LT_INIT
+AC_OUTPUT
diff --git a/converter.c b/converter.c
new file mode 100644
index 0000000..ded4f5d
--- /dev/null
+++ b/converter.c
@@ -0,0 +1,67 @@
+#define TINYOBJ_LOADER_C_IMPLEMENTATION
+#include <tinyobj_loader_c.h>
+
+static tinyobj_attrib_t attrib;
+static tinyobj_material_t *materials = NULL;
+static size_t num_materials;
+
+static void read_file(void *ctx, const char *filename, const int is_mtl, const char *obj_filename,
+ char **data, size_t *len)
+{
+ char *last_delim = strrchr(obj_filename,
+#ifdef _WIN32
+ '\\'
+#else
+ '/'
+#endif
+ );
+ char path[strlen(obj_filename) - (last_delim ? strlen(last_delim) + 1 : 0) + 1];
+ if (is_mtl && last_delim) {
+ last_delim[0] = '\0';
+ sprintf(path, "%s/%s", obj_filename, filename);
+ } else
+ strcpy(path, filename);
+ ((void (*)(const char *, char **, size_t *))ctx)(path, data, len);
+}
+
+void pv_parse(unsigned int *num_triangles, size_t *tex_name_len, const char *model, void *file_reader)
+{
+ tinyobj_shape_t *shapes = NULL;
+ size_t num_shapes;
+ tinyobj_parse_obj(&attrib, &shapes, &num_shapes, &materials, &num_materials, model, read_file, file_reader,
+ TINYOBJ_FLAG_TRIANGULATE);
+ tinyobj_shapes_free(shapes, num_shapes);
+ *num_triangles = attrib.num_face_num_verts;
+ *tex_name_len = strlen(materials[0].diffuse_texname);
+}
+
+void pv_convert(float *data, char *texture)
+{
+ static const size_t stride = 5;
+ for (unsigned int i = 0; i < attrib.num_face_num_verts; i++) {
+ tinyobj_vertex_index_t idx0 = attrib.faces[i * 3];
+ tinyobj_vertex_index_t idx1 = attrib.faces[i * 3 + 1];
+ tinyobj_vertex_index_t idx2 = attrib.faces[i * 3 + 2];
+ float v[3][3];
+ for (size_t j = 0; j < 3; j++) {
+ v[0][j] = attrib.vertices[idx0.v_idx * 3 + j];
+ v[1][j] = attrib.vertices[idx1.v_idx * 3 + j];
+ v[2][j] = attrib.vertices[idx2.v_idx * 3 + j];
+ }
+ float t[][2] = {
+ attrib.texcoords[idx0.vt_idx * 2], 1.0f - attrib.texcoords[idx0.vt_idx * 2 + 1],
+ attrib.texcoords[idx1.vt_idx * 2], 1.0f - attrib.texcoords[idx1.vt_idx * 2 + 1],
+ attrib.texcoords[idx2.vt_idx * 2], 1.0f - attrib.texcoords[idx2.vt_idx * 2 + 1]
+ };
+ for (size_t j = 0; j < 3; j++) {
+ data[(i * 3 + j) * stride + 0] = v[j][0];
+ data[(i * 3 + j) * stride + 1] = v[j][1];
+ data[(i * 3 + j) * stride + 2] = v[j][2];
+ data[(i * 3 + j) * stride + 3] = t[j][0];
+ data[(i * 3 + j) * stride + 4] = t[j][1];
+ }
+ }
+ strcpy(texture, materials[0].diffuse_texname);
+ tinyobj_attrib_free(&attrib);
+ tinyobj_materials_free(materials, num_materials);
+}
diff --git a/i686-w64-mingw32-configure b/i686-w64-mingw32-configure
new file mode 100755
index 0000000..39b2b15
--- /dev/null
+++ b/i686-w64-mingw32-configure
@@ -0,0 +1,6 @@
+#!/bin/sh
+export TARGET=i686-w64-mingw32
+export CC=$TARGET-gcc
+export CPPFLAGS="$CPPFLAGS -DDEBUG"
+export CFLAGS="$CFLAGS -g"
+./configure --host=$TARGET --prefix=/usr/$TARGET/usr --disable-shared
diff --git a/product.frag b/product.frag
new file mode 100644
index 0000000..6bbb24d
--- /dev/null
+++ b/product.frag
@@ -0,0 +1,7 @@
+precision mediump float;
+varying vec2 v_texcoord;
+uniform sampler2D s_texture;
+void main()
+{
+ gl_FragColor = texture2D(s_texture, v_texcoord);
+}
diff --git a/product.vert b/product.vert
new file mode 100644
index 0000000..b040d01
--- /dev/null
+++ b/product.vert
@@ -0,0 +1,9 @@
+uniform mat4 u_matrix;
+attribute vec4 a_position;
+attribute vec2 a_texcoord;
+varying vec2 v_texcoord;
+void main()
+{
+ gl_Position = u_matrix * a_position;
+ v_texcoord = a_texcoord;
+}
diff --git a/productviewer.h b/productviewer.h
new file mode 100644
index 0000000..191cfe5
--- /dev/null
+++ b/productviewer.h
@@ -0,0 +1,18 @@
+#ifndef PRODUCTVIEWER_H
+#define PRODUCTVIEWER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void pv_init(const char *model, void (*file_reader)(const char *path, char **data, size_t *len),
+ const char *color, unsigned int width, unsigned int height);
+void pv_draw(unsigned int width, unsigned int height);
+void pv_rotate(float angle);
+void pv_quit();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/viewer.c b/viewer.c
new file mode 100644
index 0000000..cc27e7f
--- /dev/null
+++ b/viewer.c
@@ -0,0 +1,233 @@
+#ifdef __APPLE__
+#include <ES2/gl.h>
+#else
+#include <GLES2/gl2.h>
+#endif
+#define STB_IMAGE_IMPLEMENTATION
+#include <stb_image.h>
+#if defined __ANDROID__ && defined DEBUG
+#include <android/log.h>
+#endif
+
+static struct {
+ void (*file_reader)(const char *, char **, size_t *);
+ GLfloat matrix[4][4];
+ GLfloat angle;
+ GLsizei num_triangles, num_textures;
+ GLuint program, buffers[1], textures[];
+} model = {
+ .angle = 0.0f,
+ .num_textures = 0
+};
+static const GLfloat pi = 3.1415926535897932384626433832795f;
+
+void pv_parse(GLsizei *num_triangles, size_t *tex_name_len, const char *model, void *file_reader);
+void pv_convert(float *data, char *texture);
+
+static GLuint compile(GLenum type, const char *shader)
+{
+ GLchar *data;
+ size_t len;
+ model.file_reader(shader, &data, &len);
+ GLuint id = glCreateShader(type);
+ glShaderSource(id, 1, &(const GLchar *){data}, NULL);
+ glCompileShader(id);
+ GLint compiled;
+ glGetShaderiv(id, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+#ifdef DEBUG
+ GLint len = 0;
+ glGetShaderiv(id, GL_INFO_LOG_LENGTH, &len);
+ if (len > 1) {
+ char log[len + 1];
+ glGetShaderInfoLog(id, len, NULL, log);
+#ifdef __ANDROID__
+ __android_log_print(ANDROID_LOG_ERROR, "libproductviewer", "%s", log);
+#else
+ printf("%s\n", log);
+#endif
+ }
+#endif
+ glDeleteShader(id);
+ return 0;
+ }
+ return id;
+}
+
+void pv_texturize(const char *texture)
+{
+ GLsizei w, h;
+ int comp;
+ unsigned char *image = stbi_load(texture, &w, &h, &comp, STBI_default);
+ GLint format = GL_RGBA;
+ if (comp == 3)
+ format = GL_RGB;
+ glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, image);
+ stbi_image_free(image);
+}
+
+void pv_init(const char *obj, void (*file_reader)(const char *path, char **data, size_t *len),
+ const char *color, unsigned int width, unsigned int height)
+{
+ size_t tex_name_len;
+ model.file_reader = file_reader;
+ pv_parse(&model.num_triangles, &tex_name_len, obj, file_reader);
+ GLfloat data[sizeof(GLfloat) * model.num_triangles * 5];
+ char texture[tex_name_len + 1];
+ pv_convert(data, texture);
+ GLfloat rgba[4] = { [3] = 1.0f };
+ for (size_t i = 0; i < strlen(color) / 2; i++) {
+ GLuint value;
+ sscanf((char[]){ color[i * 2], color[i * 2 + 1], '\0' }, "%x", &value);
+ rgba[i] = (GLfloat)value / 255.0f;
+ }
+ glClearColor(rgba[0], rgba[1], rgba[2], rgba[3]);
+ glViewport(0, 0, width, height);
+ glEnable(GL_CULL_FACE);
+ model.program = glCreateProgram();
+ GLuint vert_id = compile(GL_VERTEX_SHADER, "product.vert");
+ glAttachShader(model.program, vert_id);
+ glDeleteShader(vert_id);
+ GLuint frag_id = compile(GL_FRAGMENT_SHADER, "product.frag");
+ glAttachShader(model.program, frag_id);
+ glDeleteShader(frag_id);
+ glLinkProgram(model.program);
+ glUseProgram(model.program);
+ static const GLsizei stride = sizeof(GLfloat) * 5;
+ GLuint data_id;
+ glGenBuffers(1, &data_id);
+ glBindBuffer(GL_ARRAY_BUFFER, data_id);
+ model.buffers[0] = data_id;
+ glBufferData(GL_ARRAY_BUFFER, stride * model.num_triangles * 3, data, GL_STATIC_DRAW);
+ GLint position = glGetAttribLocation(model.program, "a_position");
+ glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, stride, 0);
+ glEnableVertexAttribArray(position);
+ GLint texcoord = glGetAttribLocation(model.program, "a_texcoord");
+ glVertexAttribPointer(texcoord, 2, GL_FLOAT, GL_FALSE, stride, (const void *)(sizeof(GLfloat) * 3));
+ glEnableVertexAttribArray(texcoord);
+ glActiveTexture(GL_TEXTURE0);
+ GLuint tex_id;
+ glGenTextures(1, &tex_id);
+ glBindTexture(GL_TEXTURE_2D, tex_id);
+ model.textures[model.num_textures++] = tex_id;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glUniform1i(glGetUniformLocation(model.program, "s_texture"), 0);
+ pv_texturize(texture);
+}
+
+static void identity(GLfloat matrix[][4])
+{
+ memset(matrix, 0x0, sizeof(GLfloat[4][4]));
+ for (int i = 0; i < 4; i++)
+ matrix[i][i] = 1.0f;
+}
+
+static void multiply(GLfloat matrix[][4], GLfloat a[][4], GLfloat b[][4])
+{
+ GLfloat tmp[4][4];
+ for (int i = 0; i < 4; i++) {
+ tmp[i][0]
+ = a[i][0] * b[0][0]
+ + a[i][1] * b[1][0]
+ + a[i][2] * b[2][0]
+ + a[i][3] * b[3][0];
+ tmp[i][1]
+ = a[i][0] * b[0][1]
+ + a[i][1] * b[1][1]
+ + a[i][2] * b[2][1]
+ + a[i][3] * b[3][1];
+ tmp[i][2]
+ = a[i][0] * b[0][2]
+ + a[i][1] * b[1][2]
+ + a[i][2] * b[2][2]
+ + a[i][3] * b[3][2];
+ tmp[i][3]
+ = a[i][0] * b[0][3]
+ + a[i][1] * b[1][3]
+ + a[i][2] * b[2][3]
+ + a[i][3] * b[3][3];
+ }
+ memcpy(matrix, tmp, sizeof(GLfloat[4][4]));
+}
+
+static void translate(GLfloat matrix[][4], GLfloat x, GLfloat y, GLfloat z)
+{
+ matrix[3][0] += (matrix[0][0] * x + matrix[1][0] * y + matrix[2][0] * z);
+ matrix[3][1] += (matrix[0][1] * x + matrix[1][1] * y + matrix[2][1] * z);
+ matrix[3][2] += (matrix[0][2] * x + matrix[1][2] * y + matrix[2][2] * z);
+ matrix[3][3] += (matrix[0][3] * x + matrix[1][3] * y + matrix[2][3] * z);
+}
+
+static void rotate(GLfloat matrix[][4], GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
+{
+ angle *= pi / 180.0f;
+ GLfloat sin_angle = sinf(angle);
+ GLfloat cos_angle = cosf(angle);
+ GLfloat mag = sqrtf(x * x + y * y + z * z);
+ if (mag > 0.0f) {
+ x /= mag;
+ y /= mag;
+ z /= mag;
+ GLfloat one_minus_cos = 1.0f - cos_angle;
+ multiply(matrix, (GLfloat[][4]){
+ (one_minus_cos * x * x) + cos_angle,
+ (one_minus_cos * x * y) - z * sin_angle,
+ (one_minus_cos * z * x) + y * sin_angle,
+ 0.0f,
+ (one_minus_cos * x * y) + z * sin_angle,
+ (one_minus_cos * y * y) + cos_angle,
+ (one_minus_cos * y * z) - x * sin_angle,
+ 0.0f,
+ (one_minus_cos * z * x) - y * sin_angle,
+ (one_minus_cos * y * z) + x * sin_angle,
+ (one_minus_cos * z * z) + cos_angle,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f
+ },
+ matrix);
+ }
+}
+
+void pv_draw(unsigned int width, unsigned int height)
+{
+ GLfloat perspective[4][4];
+ identity(perspective);
+ GLfloat near_z = 1.0f;
+ GLfloat far_z = 20.0f;
+ GLfloat delta_y = tanf(pi / 6.0f) * 2;
+ GLfloat delta_z = far_z - near_z;
+ multiply(perspective, (GLfloat[][4]){
+ 2.0f * near_z / delta_y / (GLfloat)width * (GLfloat)height, 0.0f, 0.0f, 0.0f,
+ 0.0f, 2.0f * near_z / delta_y, 0.0f, 0.0f,
+ 0.0f, 0.0f, -(near_z + far_z) / delta_z, -1.0f,
+ 0.0f, 0.0f, -2.0f * near_z * far_z / delta_z, 0.0f
+ },
+ perspective);
+ GLfloat model_view[4][4];
+ identity(model_view);
+ translate(model_view, 0.0, 0.0, -2.7);
+ rotate(model_view, -15.0, 1.0, 0.0, 0.0);
+ rotate(model_view, model.angle, 0.0, 1.0, 0.0);
+ multiply(model.matrix, model_view, perspective);
+ glUniformMatrix4fv(glGetUniformLocation(model.program, "u_matrix"), 1, GL_FALSE, &model.matrix[0][0]);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDrawArrays(GL_TRIANGLES, 0, model.num_triangles * 3);
+}
+
+void pv_rotate(GLfloat angle)
+{
+ model.angle += angle;
+ if (model.angle >= 360.0f)
+ model.angle -= 360.0f;
+}
+
+void pv_quit()
+{
+ glDeleteTextures(model.num_textures, model.textures);
+ glDeleteBuffers(1, model.buffers);
+ glDeleteProgram(model.program);
+}
diff --git a/wasm-unknown-emscripten-configure b/wasm-unknown-emscripten-configure
new file mode 100755
index 0000000..881c418
--- /dev/null
+++ b/wasm-unknown-emscripten-configure
@@ -0,0 +1,5 @@
+#!/bin/sh
+export PREFIX=$EMSDK/upstream/emscripten/cache/wasm
+export CPPFLAGS="$CPPFLAGS -DDEBUG"
+export CFLAGS="$CFLAGS -g"
+emconfigure ./configure --prefix=$PREFIX --libdir=$PREFIX --disable-shared
diff --git a/x86_64-pc-linux-gnu-configure b/x86_64-pc-linux-gnu-configure
new file mode 100755
index 0000000..d5f8898
--- /dev/null
+++ b/x86_64-pc-linux-gnu-configure
@@ -0,0 +1,5 @@
+#!/bin/sh
+export PREFIX=/usr/local
+export CPPFLAGS="$CPPFLAGS -DDEBUG"
+export CFLAGS="$CFLAGS -g"
+./configure --prefix=$PREFIX --libdir=$PREFIX/lib64 --disable-static