summaryrefslogtreecommitdiff
path: root/src/obj.c
diff options
context:
space:
mode:
authorConnor Thomson <blumatrikz@gmail.com>2026-09-08 18:27:39 -0700
committerConnor Thomson <blumatrikz@gmail.com>2026-09-08 18:27:39 -0700
commitf1ddc12b68b4e4c8087962de25260470e6df2bea (patch)
tree7d0440a6402a9b0ef75ded23fa64b68534255bba /src/obj.c
parent6025860a61386bf767b29c3ec5fd0d31285f1056 (diff)
Add tuxanci2 files
Diffstat (limited to 'src/obj.c')
-rw-r--r--src/obj.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/src/obj.c b/src/obj.c
new file mode 100644
index 0000000..7f3442c
--- /dev/null
+++ b/src/obj.c
@@ -0,0 +1,175 @@
+/*
+ * Tuxánci 2 - A first person shooter
+ * Copyright (C) 2007-2011 Tuxánci Development Team
+ * Copyright (C) 2025-2026 Connor Thomson
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "obj.h"
+
+typedef struct ta_obj_position {
+ float value[3];
+} ta_obj_position;
+
+typedef struct ta_obj_normal {
+ float value[3];
+} ta_obj_normal;
+
+static bool ta_obj_push_vertex(ta_obj_mesh *mesh, size_t *capacity, const ta_obj_vertex *vertex) {
+ if (mesh->vertex_count == *capacity) {
+ size_t next_capacity = *capacity == 0 ? 1024 : *capacity * 2;
+ ta_obj_vertex *vertices = realloc(mesh->vertices, next_capacity * sizeof(*vertices));
+ if (!vertices) {
+ return false;
+ }
+ mesh->vertices = vertices;
+ *capacity = next_capacity;
+ }
+ mesh->vertices[mesh->vertex_count++] = *vertex;
+ return true;
+}
+
+bool ta_obj_parse(const unsigned char *data, size_t size, ta_obj_mesh *mesh) {
+ ta_obj_position *positions = NULL;
+ ta_obj_normal *normals = NULL;
+ size_t position_count = 0;
+ size_t normal_count = 0;
+ size_t position_capacity = 0;
+ size_t normal_capacity = 0;
+ size_t vertex_capacity = 0;
+ bool success = false;
+
+ memset(mesh, 0, sizeof(*mesh));
+ char *text = malloc(size + 1);
+ if (!text) {
+ return false;
+ }
+ memcpy(text, data, size);
+ text[size] = '\0';
+
+ char *line = text;
+ while (line && *line) {
+ char *next_line = strchr(line, '\n');
+ if (next_line) {
+ *next_line = '\0';
+ }
+
+ if (line[0] == 'v' && line[1] == ' ') {
+ ta_obj_position position;
+ if (sscanf(line + 2, "%f %f %f", &position.value[0], &position.value[1], &position.value[2]) != 3) {
+ goto done;
+ }
+ if (position_count == position_capacity) {
+ size_t next_capacity = position_capacity == 0 ? 1024 : position_capacity * 2;
+ ta_obj_position *grown = realloc(positions, next_capacity * sizeof(*grown));
+ if (!grown) {
+ goto done;
+ }
+ positions = grown;
+ position_capacity = next_capacity;
+ }
+ positions[position_count++] = position;
+ } else if (line[0] == 'v' && line[1] == 'n' && line[2] == ' ') {
+ ta_obj_normal normal;
+ if (sscanf(line + 3, "%f %f %f", &normal.value[0], &normal.value[1], &normal.value[2]) != 3) {
+ goto done;
+ }
+ if (normal_count == normal_capacity) {
+ size_t next_capacity = normal_capacity == 0 ? 1024 : normal_capacity * 2;
+ ta_obj_normal *grown = realloc(normals, next_capacity * sizeof(*grown));
+ if (!grown) {
+ goto done;
+ }
+ normals = grown;
+ normal_capacity = next_capacity;
+ }
+ normals[normal_count++] = normal;
+ } else if (line[0] == 'f' && line[1] == ' ') {
+ int position_indices[3];
+ int normal_indices[3];
+ bool has_normals = sscanf(line + 2, "%d//%d %d//%d %d//%d",
+ &position_indices[0], &normal_indices[0],
+ &position_indices[1], &normal_indices[1],
+ &position_indices[2], &normal_indices[2]) == 6;
+ if (!has_normals && sscanf(line + 2, "%d %d %d",
+ &position_indices[0], &position_indices[1], &position_indices[2]) != 3) {
+ goto done;
+ }
+ for (int index = 0; index < 3; index++) {
+ if (position_indices[index] <= 0 ||
+ (size_t)position_indices[index] > position_count ||
+ (has_normals && (normal_indices[index] <= 0 ||
+ (size_t)normal_indices[index] > normal_count))) {
+ goto done;
+ }
+ }
+ float face_normal[3] = {0.0f, 0.0f, 1.0f};
+ if (!has_normals) {
+ const float *first = positions[position_indices[0] - 1].value;
+ const float *second = positions[position_indices[1] - 1].value;
+ const float *third = positions[position_indices[2] - 1].value;
+ float edge_a[3] = {second[0] - first[0], second[1] - first[1], second[2] - first[2]};
+ float edge_b[3] = {third[0] - first[0], third[1] - first[1], third[2] - first[2]};
+ face_normal[0] = edge_a[1] * edge_b[2] - edge_a[2] * edge_b[1];
+ face_normal[1] = edge_a[2] * edge_b[0] - edge_a[0] * edge_b[2];
+ face_normal[2] = edge_a[0] * edge_b[1] - edge_a[1] * edge_b[0];
+ float length = sqrtf(face_normal[0] * face_normal[0] + face_normal[1] * face_normal[1] + face_normal[2] * face_normal[2]);
+ if (length > 0.0f) {
+ face_normal[0] /= length;
+ face_normal[1] /= length;
+ face_normal[2] /= length;
+ }
+ }
+ for (int index = 0; index < 3; index++) {
+ ta_obj_vertex vertex;
+ memcpy(vertex.position, positions[position_indices[index] - 1].value, sizeof(vertex.position));
+ if (has_normals) {
+ memcpy(vertex.normal, normals[normal_indices[index] - 1].value, sizeof(vertex.normal));
+ } else {
+ memcpy(vertex.normal, face_normal, sizeof(vertex.normal));
+ }
+ if (!ta_obj_push_vertex(mesh, &vertex_capacity, &vertex)) {
+ goto done;
+ }
+ }
+ }
+
+ if (!next_line) {
+ break;
+ }
+ line = next_line + 1;
+ }
+
+ success = mesh->vertex_count > 0;
+done:
+ if (!success) {
+ ta_obj_free(mesh);
+ }
+ free(positions);
+ free(normals);
+ free(text);
+ return success;
+}
+
+void ta_obj_free(ta_obj_mesh *mesh) {
+ free(mesh->vertices);
+ mesh->vertices = NULL;
+ mesh->vertex_count = 0;
+} \ No newline at end of file