Quake II RTX doxygen  1.0 dev
models.c File Reference
#include "vkpt.h"
#include "format/md2.h"
#include "format/md3.h"
#include "format/sp2.h"
#include "material.h"
#include <assert.h>

Go to the source code of this file.

Functions

static void computeTangents (model_t *model)
 
static void export_obj_frames (model_t *model, const char *path_pattern)
 
qerror_t MOD_LoadMD2_RTX (model_t *model, const void *rawdata, size_t length)
 
void MOD_Reference_RTX (model_t *model)
 

Function Documentation

◆ computeTangents()

static void computeTangents ( model_t *  model)
static

Definition at line 56 of file models.c.

57 {
58  for (int idx_mesh = 0; idx_mesh < model->nummeshes; ++idx_mesh)
59  {
60  maliasmesh_t * mesh = &model->meshes[idx_mesh];
61 
62  assert(mesh->tangents);
63  float * stangents = Z_Malloc(mesh->numverts * 2 * 3 * sizeof(float));
64  float * ttangents = stangents + (mesh->numverts * 3);
65 
66  for (int idx_frame = 0; idx_frame < model->numframes; ++idx_frame)
67  {
68  memset(stangents, 0, mesh->numverts * 2 * 3 * sizeof(float));
69 
70  uint32_t ntriangles = mesh->numindices / 3,
71  offset = idx_frame * mesh->numverts;
72 
73  for (int idx_tri = 0; idx_tri < mesh->numtris; ++idx_tri)
74  {
75  uint32_t iA = mesh->indices[idx_tri * 3 + 0];
76  uint32_t iB = mesh->indices[idx_tri * 3 + 1];
77  uint32_t iC = mesh->indices[idx_tri * 3 + 2];
78 
79  float const * pA = (float const *)mesh->positions + ((offset + iA) * 3);
80  float const * pB = (float const *)mesh->positions + ((offset + iB) * 3);
81  float const * pC = (float const *)mesh->positions + ((offset + iC) * 3);
82 
83  float const * tA = (float const *)mesh->tex_coords + ((offset + iA) * 2);
84  float const * tB = (float const *)mesh->tex_coords + ((offset + iB) * 2);
85  float const * tC = (float const *)mesh->tex_coords + ((offset + iC) * 2);
86 
87  vec3_t dP0, dP1;
88  VectorSubtract(pB, pA, dP0);
89  VectorSubtract(pC, pA, dP1);
90 
91  vec2_t dt0, dt1;
92  Vector2Subtract(tB, tA, dt0);
93  Vector2Subtract(tC, tA, dt1);
94 
95  float r = 1.f / (dt0[0] * dt1[1] - dt1[0] * dt0[1]);
96 
97  vec3_t sdir = {
98  (dt1[1] * dP0[0] - dt0[1] * dP1[0]) * r,
99  (dt1[1] * dP0[1] - dt0[1] * dP1[1]) * r,
100  (dt1[1] * dP0[2] - dt0[1] * dP1[2]) * r };
101 
102  vec3_t tdir = {
103  (dt0[0] * dP1[0] - dt1[0] * dP0[0]) * r,
104  (dt0[0] * dP1[1] - dt1[0] * dP0[1]) * r,
105  (dt0[0] * dP1[2] - dt1[0] * dP0[2]) * r };
106 
107  VectorAdd(stangents + (iA * 3), sdir, stangents + (iA * 3));
108  VectorAdd(stangents + (iB * 3), sdir, stangents + (iB * 3));
109  VectorAdd(stangents + (iC * 3), sdir, stangents + (iC * 3));
110 
111  VectorAdd(ttangents + (iA * 3), tdir, ttangents + (iA * 3));
112  VectorAdd(ttangents + (iB * 3), tdir, ttangents + (iB * 3));
113  VectorAdd(ttangents + (iC * 3), tdir, ttangents + (iC * 3));
114  }
115 
116  for (int idx_vert = 0; idx_vert < mesh->numverts; ++idx_vert)
117  {
118  float const * normal = (float const *)mesh->normals + ((offset + idx_vert) * 3);
119  float const * stan = stangents + (idx_vert * 3);
120  float const * ttan = ttangents + (idx_vert * 3);
121 
122  float * tangent = (float *)mesh->tangents + ((offset+idx_vert) * 4);
123 
124  vec3_t t;
125  VectorScale(normal, DotProduct(normal, stan), t);
126  VectorSubtract(stan, t, t);
127  VectorNormalize2(t, tangent); // Graham-Schmidt : t = normalize(t - n * (n.t))
128 
129  vec3_t cross;
130  CrossProduct(normal, t, cross);
131  float dot = DotProduct(cross, ttan);
132  tangent[3] = dot < 0.0f ? -1.0f : 1.0f; // handedness
133  }
134  }
135  Z_Free(stangents);
136  }
137 }

Referenced by MOD_LoadMD2_RTX().

◆ export_obj_frames()

static void export_obj_frames ( model_t *  model,
const char *  path_pattern 
)
static

Definition at line 139 of file models.c.

140 {
141  for (int idx_frame = 0; idx_frame < model->numframes; ++idx_frame)
142  {
143  char path[MAX_OSPATH];
144  sprintf(path, path_pattern, idx_frame);
145  FILE* file = fopen(path, "w");
146 
147  if (!file)
148  continue;
149 
150  int mesh_vertex_offset = 1; // obj indexing starts at 1
151 
152  for (int idx_mesh = 0; idx_mesh < model->nummeshes; ++idx_mesh)
153  {
154  maliasmesh_t * mesh = &model->meshes[idx_mesh];
155  uint32_t ntriangles = mesh->numindices / 3,
156  offset = idx_frame * mesh->numverts;
157 
158  for (int idx_vert = 0; idx_vert < mesh->numverts; ++idx_vert)
159  {
160  float const * p = (float const*)mesh->positions + (offset + idx_vert) * 3;
161  float const * n = (float const*)mesh->normals + (offset + idx_vert) * 3;
162  float const * t = (float const*)mesh->tex_coords + (offset + idx_vert) * 2;
163  fprintf(file, "v %.3f %.3f %.3f\n", p[0], p[1], p[2]);
164  fprintf(file, "vn %.3f %.3f %.3f\n", n[0], n[1], n[2]);
165  fprintf(file, "vt %.3f %.3f\n", t[0], t[1]);
166  }
167 
168  fprintf(file, "g mesh_%d\n", idx_mesh);
169 
170  for (int idx_tri = 0; idx_tri < mesh->numtris; ++idx_tri)
171  {
172  int iA = mesh->indices[idx_tri * 3 + 0] + mesh_vertex_offset;
173  int iB = mesh->indices[idx_tri * 3 + 1] + mesh_vertex_offset;
174  int iC = mesh->indices[idx_tri * 3 + 2] + mesh_vertex_offset;
175 
176  fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", iA, iA, iA, iB, iB, iB, iC, iC, iC);
177  }
178 
179  mesh_vertex_offset += mesh->numverts;
180  }
181 
182  fclose(file);
183  }
184 }

◆ MOD_LoadMD2_RTX()

qerror_t MOD_LoadMD2_RTX ( model_t *  model,
const void rawdata,
size_t  length 
)

Definition at line 186 of file models.c.

187 {
188  dmd2header_t header;
189  dmd2frame_t *src_frame;
190  dmd2trivertx_t *src_vert;
191  dmd2triangle_t *src_tri;
192  dmd2stvert_t *src_tc;
193  char *src_skin;
194  maliasframe_t *dst_frame;
195  maliasmesh_t *dst_mesh;
196  int val;
197  uint16_t remap[TESS_MAX_INDICES];
198  uint16_t vertIndices[TESS_MAX_INDICES];
199  uint16_t tcIndices[TESS_MAX_INDICES];
200  uint16_t finalIndices[TESS_MAX_INDICES];
201  int numverts, numindices;
202  char skinname[MAX_QPATH];
203  vec_t scale_s, scale_t;
204  vec3_t mins, maxs;
205  qerror_t ret;
206 
207  if (length < sizeof(header)) {
208  return Q_ERR_FILE_TOO_SMALL;
209  }
210 
211  // byte swap the header
212  header = *(dmd2header_t *)rawdata;
213  for (int i = 0; i < sizeof(header) / 4; i++) {
214  ((uint32_t *)&header)[i] = LittleLong(((uint32_t *)&header)[i]);
215  }
216 
217  // validate the header
218  ret = MOD_ValidateMD2(&header, length);
219  if (ret) {
220  if (ret == Q_ERR_TOO_FEW) {
221  // empty models draw nothing
222  model->type = MOD_EMPTY;
223  return Q_ERR_SUCCESS;
224  }
225  return ret;
226  }
227 
228  // load all triangle indices
229  numindices = 0;
230  src_tri = (dmd2triangle_t *)((byte *)rawdata + header.ofs_tris);
231  for (int i = 0; i < header.num_tris; i++) {
232  int good = 1;
233  for (int j = 0; j < 3; j++) {
234  uint16_t idx_xyz = LittleShort(src_tri->index_xyz[j]);
235  uint16_t idx_st = LittleShort(src_tri->index_st[j]);
236 
237  // some broken models have 0xFFFF indices
238  if (idx_xyz >= header.num_xyz || idx_st >= header.num_st) {
239  good = 0;
240  break;
241  }
242 
243  vertIndices[numindices + j] = idx_xyz;
244  tcIndices[numindices + j] = idx_st;
245  }
246  if (good) {
247  // only count good triangles
248  numindices += 3;
249  }
250  src_tri++;
251  }
252 
253  if (numindices < 3) {
254  return Q_ERR_TOO_FEW;
255  }
256 
257  qboolean all_normals_same = qtrue;
258  int same_normal = -1;
259 
260  src_frame = (dmd2frame_t *)((byte *)rawdata + header.ofs_frames);
261  for (int i = 0; i < numindices; i++)
262  {
263  int v = vertIndices[i];
264  int normal = src_frame->verts[v].lightnormalindex;
265 
266  // detect if the model has broken normals - they are all the same in that case
267  // it happens with players/w_<weapon>.md2 models for example
268  if (same_normal < 0)
269  same_normal = normal;
270  else if (normal != same_normal)
271  all_normals_same = qfalse;
272  }
273 
274  for (int i = 0; i < numindices; i++) {
275  remap[i] = 0xFFFF;
276  }
277 
278  // remap all triangle indices
279  numverts = 0;
280  src_tc = (dmd2stvert_t *)((byte *)rawdata + header.ofs_st);
281  for (int i = 0; i < numindices; i++) {
282  if (remap[i] != 0xFFFF) {
283  continue; // already remapped
284  }
285 
286  // only dedup vertices if we're not regenerating normals
287  if (!all_normals_same)
288  {
289  for (int j = i + 1; j < numindices; j++) {
290  if (vertIndices[i] == vertIndices[j] &&
291  (src_tc[tcIndices[i]].s == src_tc[tcIndices[j]].s &&
292  src_tc[tcIndices[i]].t == src_tc[tcIndices[j]].t)) {
293  // duplicate vertex
294  remap[j] = i;
295  finalIndices[j] = numverts;
296  }
297  }
298  }
299 
300  // new vertex
301  remap[i] = i;
302  finalIndices[i] = numverts++;
303  }
304 
305  Hunk_Begin(&model->hunk, 50u<<20);
306  model->type = MOD_ALIAS;
307  model->nummeshes = 1;
308  model->numframes = header.num_frames;
309  model->meshes = MOD_Malloc(sizeof(maliasmesh_t));
310  model->frames = MOD_Malloc(header.num_frames * sizeof(maliasframe_t));
311 
312  dst_mesh = model->meshes;
313  dst_mesh->numtris = numindices / 3;
314  dst_mesh->numindices = numindices;
315  dst_mesh->numverts = numverts;
316  dst_mesh->numskins = header.num_skins;
317  dst_mesh->positions = MOD_Malloc(numverts * header.num_frames * sizeof(vec3_t));
318  dst_mesh->normals = MOD_Malloc(numverts * header.num_frames * sizeof(vec3_t));
319  dst_mesh->tex_coords = MOD_Malloc(numverts * header.num_frames * sizeof(vec2_t));
320  dst_mesh->tangents = MOD_Malloc(numverts * header.num_frames * sizeof(vec4_t));
321  dst_mesh->indices = MOD_Malloc(numindices * sizeof(int));
322 
323  if (dst_mesh->numtris != header.num_tris) {
324  Com_DPrintf("%s has %d bad triangles\n", model->name, header.num_tris - dst_mesh->numtris);
325  }
326 
327  // store final triangle indices
328  for (int i = 0; i < numindices; i++) {
329  dst_mesh->indices[i] = finalIndices[i];
330  }
331 
332  // load all skins
333  src_skin = (char *)rawdata + header.ofs_skins;
334  for (int i = 0; i < header.num_skins; i++) {
335  if (!Q_memccpy(skinname, src_skin, 0, sizeof(skinname))) {
336  ret = Q_ERR_STRING_TRUNCATED;
337  goto fail;
338  }
339  FS_NormalizePath(skinname, skinname);
340 
341  pbr_material_t * mat = MAT_FindPBRMaterial(skinname);
342  if (!mat)
343  Com_EPrintf("error finding material '%s'\n", skinname);
344 
345  image_t* image_diffuse = IMG_Find(skinname, IT_SKIN, IF_SRGB);
346  image_t* image_normals = NULL;
347  image_t* image_emissive = NULL;
348 
349  if (image_diffuse != R_NOTEXTURE)
350  {
351  // attempt loading the normals texture
352  if (!Q_strlcpy(skinname, src_skin, strlen(src_skin) - 3))
353  return Q_ERR_STRING_TRUNCATED;
354 
355  Q_concat(skinname, sizeof(skinname), skinname, "_n.tga", NULL);
356  FS_NormalizePath(skinname, skinname);
357  image_normals = IMG_Find(skinname, IT_SKIN, IF_NONE);
358  if (image_normals == R_NOTEXTURE) image_normals = NULL;
359 
360  // attempt loading the emissive texture
361  if (!Q_strlcpy(skinname, src_skin, strlen(src_skin) - 3))
362  return Q_ERR_STRING_TRUNCATED;
363 
364  Q_concat(skinname, sizeof(skinname), skinname, "_light.tga", NULL);
365  FS_NormalizePath(skinname, skinname);
366  image_emissive = IMG_Find(skinname, IT_SKIN, IF_SRGB);
367  if (image_emissive == R_NOTEXTURE) image_emissive = NULL;
368  }
369 
370  MAT_RegisterPBRMaterial(mat, image_diffuse, image_normals, image_emissive);
371 
372  dst_mesh->materials[i] = mat;
373 
374  src_skin += MD2_MAX_SKINNAME;
375  }
376 
377  // load all tcoords
378  src_tc = (dmd2stvert_t *)((byte *)rawdata + header.ofs_st);
379  scale_s = 1.0f / header.skinwidth;
380  scale_t = 1.0f / header.skinheight;
381 
382  // load all frames
383  src_frame = (dmd2frame_t *)((byte *)rawdata + header.ofs_frames);
384  dst_frame = model->frames;
385  for (int j = 0; j < header.num_frames; j++) {
386  LittleVector(src_frame->scale, dst_frame->scale);
387  LittleVector(src_frame->translate, dst_frame->translate);
388 
389  // load frame vertices
390  ClearBounds(mins, maxs);
391 
392  for (int i = 0; i < numindices; i++) {
393  if (remap[i] != i) {
394  continue;
395  }
396  src_vert = &src_frame->verts[vertIndices[i]];
397  vec3_t *dst_pos = &dst_mesh->positions [j * numverts + finalIndices[i]];
398  vec3_t *dst_nrm = &dst_mesh->normals [j * numverts + finalIndices[i]];
399  vec2_t *dst_tc = &dst_mesh->tex_coords[j * numverts + finalIndices[i]];
400 
401  (*dst_tc)[0] = scale_s * src_tc[tcIndices[i]].s;
402  (*dst_tc)[1] = scale_t * src_tc[tcIndices[i]].t;
403 
404  (*dst_pos)[0] = src_vert->v[0] * dst_frame->scale[0] + dst_frame->translate[0];
405  (*dst_pos)[1] = src_vert->v[1] * dst_frame->scale[1] + dst_frame->translate[1];
406  (*dst_pos)[2] = src_vert->v[2] * dst_frame->scale[2] + dst_frame->translate[2];
407 
408  (*dst_nrm)[0] = 0.0f;
409  (*dst_nrm)[1] = 0.0f;
410  (*dst_nrm)[2] = 0.0f;
411 
412  val = src_vert->lightnormalindex;
413 
414  if (val < NUMVERTEXNORMALS) {
415  (*dst_nrm)[0] = bytedirs[val][0];
416  (*dst_nrm)[1] = bytedirs[val][1];
417  (*dst_nrm)[2] = bytedirs[val][2];
418  }
419 
420  for (int k = 0; k < 3; k++) {
421  val = (*dst_pos)[k];
422  if (val < mins[k])
423  mins[k] = val;
424  if (val > maxs[k])
425  maxs[k] = val;
426  }
427  }
428 
429  // if all normals are the same, rebuild them as flat triangle normals
430  if (all_normals_same)
431  {
432  for (int tri = 0; tri < numindices / 3; tri++)
433  {
434  int i0 = j * numverts + finalIndices[tri * 3 + 0];
435  int i1 = j * numverts + finalIndices[tri * 3 + 1];
436  int i2 = j * numverts + finalIndices[tri * 3 + 2];
437 
438  vec3_t *p0 = &dst_mesh->positions[i0];
439  vec3_t *p1 = &dst_mesh->positions[i1];
440  vec3_t *p2 = &dst_mesh->positions[i2];
441 
442  vec3_t e1, e2, n;
443  VectorSubtract(*p1, *p0, e1);
444  VectorSubtract(*p2, *p0, e2);
445  CrossProduct(e2, e1, n);
446  VectorNormalize(n);
447 
448  VectorCopy(n, dst_mesh->normals[i0]);
449  VectorCopy(n, dst_mesh->normals[i1]);
450  VectorCopy(n, dst_mesh->normals[i2]);
451  }
452  }
453 
454  VectorVectorScale(mins, dst_frame->scale, mins);
455  VectorVectorScale(maxs, dst_frame->scale, maxs);
456 
457  dst_frame->radius = RadiusFromBounds(mins, maxs);
458 
459  VectorAdd(mins, dst_frame->translate, dst_frame->bounds[0]);
460  VectorAdd(maxs, dst_frame->translate, dst_frame->bounds[1]);
461 
462  src_frame = (dmd2frame_t *)((byte *)src_frame + header.framesize);
463  dst_frame++;
464  }
465 
466  // fix winding order
467  for (int i = 0; i < dst_mesh->numindices; i += 3) {
468  int tmp = dst_mesh->indices[i + 1];
469  dst_mesh->indices[i + 1] = dst_mesh->indices[i + 2];
470  dst_mesh->indices[i + 2] = tmp;
471  }
472 
473  computeTangents(model);
474 
475  Hunk_End(&model->hunk);
476  return Q_ERR_SUCCESS;
477 
478 fail:
479  Hunk_Free(&model->hunk);
480  return ret;
481 }

Referenced by R_RegisterFunctionsRTX().

◆ MOD_Reference_RTX()

void MOD_Reference_RTX ( model_t *  model)

Definition at line 731 of file models.c.

732 {
733  int mesh_idx, skin_idx, frame_idx;
734 
735  // register any images used by the models
736  switch (model->type) {
737  case MOD_ALIAS:
738  for (mesh_idx = 0; mesh_idx < model->nummeshes; mesh_idx++) {
739  maliasmesh_t *mesh = &model->meshes[mesh_idx];
740  for (skin_idx = 0; skin_idx < mesh->numskins; skin_idx++) {
741  MAT_UpdateRegistration(mesh->materials[skin_idx]);
742  }
743  }
744  break;
745  case MOD_SPRITE:
746  for (frame_idx = 0; frame_idx < model->numframes; frame_idx++) {
747  model->spriteframes[frame_idx].image->registration_sequence = registration_sequence;
748  }
749  break;
750  case MOD_EMPTY:
751  break;
752  default:
753  Com_Error(ERR_FATAL, "%s: bad model type", __func__);
754  }
755 
756  model->registration_sequence = registration_sequence;
757 }

Referenced by R_RegisterFunctionsRTX().

RadiusFromBounds
vec_t RadiusFromBounds(const vec3_t mins, const vec3_t maxs)
Definition: shared.c:127
maliasmesh_s
Definition: gl.h:232
TESS_MAX_INDICES
#define TESS_MAX_INDICES
Definition: gl.h:460
maliasmesh_s::numindices
int numindices
Definition: gl.h:235
maliasframe_s::scale
vec3_t scale
Definition: gl.h:226
image_t
struct image_s image_t
Definition: material.h:27
Hunk_Begin
void Hunk_Begin(memhunk_t *hunk, size_t maxsize)
Definition: hunk.c:23
computeTangents
static void computeTangents(model_t *model)
Definition: models.c:56
MAT_UpdateRegistration
void MAT_UpdateRegistration(pbr_material_t *mat)
Definition: material.c:421
MOD_ValidateMD2
qerror_t MOD_ValidateMD2(dmd2header_t *header, size_t length)
Definition: models.c:142
maliasmesh_s::indices
QGL_INDEX_TYPE * indices
Definition: gl.h:236
MAT_RegisterPBRMaterial
qerror_t MAT_RegisterPBRMaterial(pbr_material_t *mat, image_t *image_diffuse, image_t *image_normals, image_t *image_emissive)
Definition: material.c:397
maliasmesh_s::tex_coords
vec2_t * tex_coords
Definition: vkpt.h:701
maliasframe_s::translate
vec3_t translate
Definition: gl.h:227
maliasmesh_s::numverts
int numverts
Definition: gl.h:233
Hunk_Free
void Hunk_Free(memhunk_t *hunk)
Definition: hunk.c:75
maliasmesh_s::positions
vec3_t * positions
Definition: vkpt.h:699
Com_Error
void Com_Error(error_type_t type, const char *fmt,...)
Definition: g_main.c:258
maliasframe_s::bounds
vec3_t bounds[2]
Definition: gl.h:228
maliasframe_s::radius
vec_t radius
Definition: gl.h:229
pbr_material_s
Definition: material.h:33
maliasmesh_s::materials
struct pbr_material_s * materials[MAX_ALIAS_SKINS]
Definition: vkpt.h:703
Z_Free
void Z_Free(void *ptr)
Definition: zone.c:147
ClearBounds
void ClearBounds(vec3_t mins, vec3_t maxs)
Definition: shared.c:91
Hunk_End
void Hunk_End(memhunk_t *hunk)
Definition: hunk.c:66
Q_strlcpy
size_t Q_strlcpy(char *dst, const char *src, size_t size)
Definition: shared.c:715
maliasmesh_s::tangents
vec4_t * tangents
Definition: vkpt.h:702
VectorNormalize2
vec_t VectorNormalize2(vec3_t v, vec3_t out)
Definition: shared.c:73
IMG_Find
image_t * IMG_Find(const char *name, imagetype_t type, imageflags_t flags)
Definition: images.c:1122
registration_sequence
int registration_sequence
Definition: main.c:34
maliasmesh_s::numtris
int numtris
Definition: gl.h:234
maliasmesh_s::normals
vec3_t * normals
Definition: vkpt.h:700
Q_memccpy
void * Q_memccpy(void *dst, const void *src, int c, size_t size)
Definition: shared.c:895
Q_concat
size_t Q_concat(char *dest, size_t size,...)
Definition: shared.c:758
bytedirs
const vec3_t bytedirs[NUMVERTEXNORMALS]
Definition: math.c:80
maliasframe_s
Definition: gl.h:225
VectorNormalize
vec_t VectorNormalize(vec3_t v)
Definition: shared.c:55
FS_NormalizePath
size_t FS_NormalizePath(char *out, const char *in)
Definition: files.c:331
maliasmesh_s::numskins
int numskins
Definition: gl.h:240
MAT_FindPBRMaterial
pbr_material_t * MAT_FindPBRMaterial(char const *name)
Definition: material.c:465