feat: OBJ model loader + fix mesh.h duplicates

- mesh_load_obj: parse vertices, UVs, faces from .obj files
- Auto-generate face normals
- Per-face vertex expansion (simple but correct)
This commit is contained in:
shuiyu1123 committed 2026-07-11 12:48:56 +08:00
1 parent d15a3947ab
commit ea3e0ab61c
2 files changed
+99

No files matched your search

+98
View File
@@ -1,5 +1,7 @@
#include "mesh.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifndef MATH_ASM_AVAILABLE
#include "../ASM/math_c_fallback.h"
@@ -108,3 +110,99 @@ Mesh* mesh_create_sphere(ID3D11Device* dev, float r, int seg) {
free(v); free(idx);
return m;
}
// OBJ 加载器
Mesh* mesh_load_obj(ID3D11Device* dev, const char* path) {
FILE* f = fopen(path, "r");
if (!f) return NULL;
// 先统计顶点和面数
int max_v = 4096, max_f = 8192;
float* verts = (float*)malloc(max_v * 3 * sizeof(float));
float* uvs = (float*)malloc(max_v * 2 * sizeof(float));
int vert_count = 0, uv_count = 0;
int* face_idx = (int*)malloc(max_f * 3 * sizeof(int));
int face_count = 0;
char line[256];
while (fgets(line, sizeof(line), f)) {
if (line[0] == 'v' && line[1] == ' ') {
float x, y, z;
if (sscanf(line + 2, "%f %f %f", &x, &y, &z) == 3 && vert_count < max_v) {
verts[vert_count * 3] = x;
verts[vert_count * 3 + 1] = y;
verts[vert_count * 3 + 2] = z;
vert_count++;
}
} else if (line[0] == 'v' && line[1] == 't') {
float u, v;
if (sscanf(line + 3, "%f %f", &u, &v) == 2 && uv_count < max_v) {
uvs[uv_count * 2] = u;
uvs[uv_count * 2 + 1] = v;
uv_count++;
}
} else if (line[0] == 'f' && line[1] == ' ') {
// 解析面: f v/vt v/vt v/vt 或 f v v v
int v0, v1, v2;
char* p = line + 2;
v0 = atoi(p) - 1; while (*p && *p != ' ') p++; p++;
v1 = atoi(p) - 1; while (*p && *p != ' ') p++; p++;
v2 = atoi(p) - 1;
if (v0 >= 0 && v1 >= 0 && v2 >= 0 && face_count < max_f) {
face_idx[face_count * 3] = v0;
face_idx[face_count * 3 + 1] = v1;
face_idx[face_count * 3 + 2] = v2;
face_count++;
}
}
}
fclose(f);
if (vert_count == 0 || face_count == 0) {
free(verts); free(uvs); free(face_idx);
return NULL;
}
// 构建去重顶点 (简单方案: 每个面一个顶点)
int out_vcount = face_count * 3;
Vertex* out_verts = (Vertex*)calloc(out_vcount, sizeof(Vertex));
uint32_t* out_idx = (uint32_t*)malloc(face_count * 3 * sizeof(uint32_t));
for (int i = 0; i < face_count; i++) {
for (int j = 0; j < 3; j++) {
int vi = face_idx[i * 3 + j];
int oi = i * 3 + j;
if (vi < vert_count) {
out_verts[oi].pos[0] = verts[vi * 3];
out_verts[oi].pos[1] = verts[vi * 3 + 1];
out_verts[oi].pos[2] = verts[vi * 3 + 2];
}
out_verts[oi].color[0] = out_verts[oi].color[1] = out_verts[oi].color[2] = 1.0f;
out_verts[oi].color[3] = 1.0f;
out_idx[oi] = oi;
}
}
// 简单法线计算 (面法线)
for (int i = 0; i < face_count; i++) {
float* p0 = out_verts[i * 3].pos;
float* p1 = out_verts[i * 3 + 1].pos;
float* p2 = out_verts[i * 3 + 2].pos;
float e1[3] = {p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]};
float e2[3] = {p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]};
float nx = e1[1]*e2[2] - e1[2]*e2[1];
float ny = e1[2]*e2[0] - e1[0]*e2[2];
float nz = e1[0]*e2[1] - e1[1]*e2[0];
float nl = sqrtf(nx*nx + ny*ny + nz*nz);
if (nl > 0.0001f) { nx /= nl; ny /= nl; nz /= nl; }
for (int j = 0; j < 3; j++) {
out_verts[i * 3 + j].norm[0] = nx;
out_verts[i * 3 + j].norm[1] = ny;
out_verts[i * 3 + j].norm[2] = nz;
}
}
Mesh* m = create_mesh(dev, out_verts, out_vcount, out_idx, face_count * 3, sizeof(Vertex));
free(out_verts); free(out_idx); free(verts); free(uvs); free(face_idx);
return m;
}
+1
View File
@@ -20,5 +20,6 @@ typedef struct {
Mesh* mesh_create_cube(ID3D11Device* dev);
Mesh* mesh_create_plane(ID3D11Device* dev, float size);
Mesh* mesh_create_sphere(ID3D11Device* dev, float r, int seg);
Mesh* mesh_load_obj(ID3D11Device* dev, const char* path);
void mesh_destroy(Mesh* m);
void mesh_draw(Mesh* m, ID3D11DeviceContext* ctx);