Files
lity3d/Engine/Rendering/mesh.c
T
shuiyu1123 ea3e0ab61c 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)
2026-07-11 12:48:56 +08:00

209 lines
8.2 KiB
C

#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"
#endif
#define POS(n,x,y,z,nx,ny,nz,u,v,r,g,b,a) {x,y,z, nx,ny,nz, u,v, r,g,b,a}
static Mesh* create_mesh(ID3D11Device* dev, const void* verts, uint32_t vc, const uint32_t* idx, uint32_t ic, uint32_t stride) {
Mesh* m = (Mesh*)calloc(1, sizeof(Mesh));
m->stride = stride; m->vcount = vc; m->icount = ic;
D3D11_BUFFER_DESC bd = {0};
D3D11_SUBRESOURCE_DATA sd = {0};
bd.ByteWidth = stride * vc; bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = verts;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &m->vb);
if (idx && ic > 0) {
bd.ByteWidth = sizeof(uint32_t) * ic; bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = idx;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &m->ib);
}
return m;
}
void mesh_destroy(Mesh* m) {
if (!m) return;
if (m->ib) m->ib->lpVtbl->Release(m->ib);
if (m->vb) m->vb->lpVtbl->Release(m->vb);
free(m);
}
void mesh_draw(Mesh* m, ID3D11DeviceContext* ctx) {
UINT stride = m->stride, offset = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &m->vb, &stride, &offset);
if (m->ib) {
ctx->lpVtbl->IASetIndexBuffer(ctx, m->ib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->DrawIndexed(ctx, m->icount, 0, 0);
} else {
ctx->lpVtbl->Draw(ctx, m->vcount, 0);
}
}
Mesh* mesh_create_cube(ID3D11Device* dev) {
typedef struct { float p[3], n[3], uv[2], c[4]; } V;
V v[24] = {
POS(0,-.5f,-.5f,-.5f, 0,0,-1, 0,1, 1,1,1,1), POS(1, .5f,-.5f,-.5f, 0,0,-1, 1,1, 1,1,1,1),
POS(2, .5f, .5f,-.5f, 0,0,-1, 1,0, 1,1,1,1), POS(3,-.5f, .5f,-.5f, 0,0,-1, 0,0, 1,1,1,1),
POS(4, .5f,-.5f, .5f, 0,0, 1, 0,1, 1,1,1,1), POS(5,-.5f,-.5f, .5f, 0,0, 1, 1,1, 1,1,1,1),
POS(6,-.5f, .5f, .5f, 0,0, 1, 1,0, 1,1,1,1), POS(7, .5f, .5f, .5f, 0,0, 1, 0,0, 1,1,1,1),
POS(8,-.5f, .5f,-.5f, 0,1, 0, 0,1, 1,1,1,1), POS(9, .5f, .5f,-.5f, 0,1, 0, 1,1, 1,1,1,1),
POS(10,.5f, .5f, .5f, 0,1, 0, 1,0, 1,1,1,1), POS(11,-.5f, .5f, .5f, 0,1, 0, 0,0, 1,1,1,1),
POS(12,-.5f,-.5f, .5f, 0,-1,0, 0,1, 1,1,1,1), POS(13,.5f,-.5f, .5f, 0,-1,0, 1,1, 1,1,1,1),
POS(14,.5f,-.5f,-.5f, 0,-1,0, 1,0, 1,1,1,1), POS(15,-.5f,-.5f,-.5f, 0,-1,0, 0,0, 1,1,1,1),
POS(16,.5f,-.5f,-.5f, 1,0, 0, 0,1, 1,1,1,1), POS(17,.5f,-.5f, .5f, 1,0, 0, 1,1, 1,1,1,1),
POS(18,.5f, .5f, .5f, 1,0, 0, 1,0, 1,1,1,1), POS(19,.5f, .5f,-.5f, 1,0, 0, 0,0, 1,1,1,1),
POS(20,-.5f,-.5f, .5f, -1,0,0, 0,1, 1,1,1,1), POS(21,-.5f,-.5f,-.5f, -1,0,0, 1,1, 1,1,1,1),
POS(22,-.5f, .5f,-.5f, -1,0,0, 1,0, 1,1,1,1), POS(23,-.5f, .5f, .5f, -1,0,0, 0,0, 1,1,1,1),
};
uint32_t idx[36];
for (int i = 0; i < 6; i++) { int b = i*4; idx[i*6]=b; idx[i*6+1]=b+1; idx[i*6+2]=b+2; idx[i*6+3]=b; idx[i*6+4]=b+2; idx[i*6+5]=b+3; }
return create_mesh(dev, v, 24, idx, 36, sizeof(V));
}
Mesh* mesh_create_plane(ID3D11Device* dev, float size) {
typedef struct { float p[3], n[3], uv[2], c[4]; } V;
float h = size * 0.5f;
V v[4] = {
{{-h,0,h},{0,1,0},{0,1},{1,1,1,1}},
{{ h,0,h},{0,1,0},{1,1},{1,1,1,1}},
{{ h,0,-h},{0,1,0},{1,0},{1,1,1,1}},
{{-h,0,-h},{0,1,0},{0,0},{1,1,1,1}},
};
uint32_t idx[6] = {0,1,2,0,2,3};
return create_mesh(dev, v, 4, idx, 6, sizeof(V));
}
Mesh* mesh_create_sphere(ID3D11Device* dev, float r, int seg) {
typedef struct { float p[3], n[3], uv[2], c[4]; } V;
int vc = (seg+1)*(seg+1);
V* v = (V*)malloc(vc * sizeof(V));
int vi = 0;
for (int lat = 0; lat <= seg; lat++) {
float theta = 3.14159265f * lat / seg;
float st = sinf(theta), ct = cosf(theta);
for (int lon = 0; lon <= seg; lon++) {
float phi = 2.0f * 3.14159265f * lon / seg;
float sp = sinf(phi), cp = cosf(phi);
float nx = cp*st, ny = ct, nz = sp*st;
v[vi].p[0] = nx*r; v[vi].p[1] = ny*r; v[vi].p[2] = nz*r;
v[vi].n[0] = nx; v[vi].n[1] = ny; v[vi].n[2] = nz;
v[vi].uv[0] = (float)lon/seg; v[vi].uv[1] = (float)lat/seg;
v[vi].c[0]=1; v[vi].c[1]=1; v[vi].c[2]=1; v[vi].c[3]=1;
vi++;
}
}
int ic = seg*seg*6;
uint32_t* idx = (uint32_t*)malloc(ic * sizeof(uint32_t));
int ii = 0;
for (int lat = 0; lat < seg; lat++) {
for (int lon = 0; lon < seg; lon++) {
uint32_t f = lat*(seg+1)+lon, s = f+seg+1;
idx[ii++]=f; idx[ii++]=s; idx[ii++]=f+1;
idx[ii++]=s; idx[ii++]=s+1; idx[ii++]=f+1;
}
}
Mesh* m = create_mesh(dev, v, vc, idx, ic, sizeof(V));
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;
}