summaryrefslogtreecommitdiff
path: root/viewer.c
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 /viewer.c
Initial working commit
Diffstat (limited to 'viewer.c')
-rw-r--r--viewer.c233
1 files changed, 233 insertions, 0 deletions
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);
+}