]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_models.c
most of the new alias mesh system is in place now...
[xonotic/darkplaces.git] / gl_models.c
1
2 #include "quakedef.h"
3 #include "cl_collision.h"
4 #include "r_shadow.h"
5
6 typedef struct
7 {
8         float m[3][4];
9 } zymbonematrix;
10
11 // LordHavoc: vertex arrays
12
13 float *aliasvertcolorbuf;
14 float *aliasvertcolor; // this may point at aliasvertcolorbuf or at vertex arrays in the mesh backend
15 float *aliasvert_svectors;
16 float *aliasvert_tvectors;
17 float *aliasvert_normals;
18
19 float *aliasvertcolor2;
20 int *aliasvertusage;
21 zymbonematrix *zymbonepose;
22
23 mempool_t *gl_models_mempool;
24
25 void gl_models_start(void)
26 {
27         // allocate vertex processing arrays
28         gl_models_mempool = Mem_AllocPool("GL_Models");
29         aliasvertcolor = aliasvertcolorbuf = Mem_Alloc(gl_models_mempool, sizeof(float[MD2MAX_VERTS][4]));
30         aliasvert_svectors = Mem_Alloc(gl_models_mempool, sizeof(float[MD2MAX_VERTS][4]));
31         aliasvert_tvectors = Mem_Alloc(gl_models_mempool, sizeof(float[MD2MAX_VERTS][4]));
32         aliasvert_normals = Mem_Alloc(gl_models_mempool, sizeof(float[MD2MAX_VERTS][4]));
33         aliasvertcolor2 = Mem_Alloc(gl_models_mempool, sizeof(float[MD2MAX_VERTS][4])); // used temporarily for tinted coloring
34         zymbonepose = Mem_Alloc(gl_models_mempool, sizeof(zymbonematrix[256]));
35         aliasvertusage = Mem_Alloc(gl_models_mempool, sizeof(int[MD2MAX_VERTS]));
36 }
37
38 void gl_models_shutdown(void)
39 {
40         Mem_FreePool(&gl_models_mempool);
41 }
42
43 void gl_models_newmap(void)
44 {
45 }
46
47 void GL_Models_Init(void)
48 {
49         R_RegisterModule("GL_Models", gl_models_start, gl_models_shutdown, gl_models_newmap);
50 }
51
52 void R_Model_Alias_GetVerts(const entity_render_t *ent, float *vertices, float *normals, float *svectors, float *tvectors)
53 {
54         int i, vertcount;
55         float lerp1, lerp2, lerp3, lerp4;
56         const aliasvertex_t *verts1, *verts2, *verts3, *verts4;
57
58         if (vertices == NULL)
59                 Host_Error("R_Model_Alias_GetVerts: vertices == NULL.\n");
60         if (svectors != NULL && (tvectors == NULL || normals == NULL))
61                 Host_Error("R_Model_Alias_GetVerts: svectors requires tvectors and normals.\n");
62         if (tvectors != NULL && (svectors == NULL || normals == NULL))
63                 Host_Error("R_Model_Alias_GetVerts: tvectors requires svectors and normals.\n");
64
65         vertcount = ent->model->numverts;
66         verts1 = ent->model->mdlmd2data_pose + ent->frameblend[0].frame * vertcount;
67         lerp1 = ent->frameblend[0].lerp;
68         if (ent->frameblend[1].lerp)
69         {
70                 verts2 = ent->model->mdlmd2data_pose + ent->frameblend[1].frame * vertcount;
71                 lerp2 = ent->frameblend[1].lerp;
72                 if (ent->frameblend[2].lerp)
73                 {
74                         verts3 = ent->model->mdlmd2data_pose + ent->frameblend[2].frame * vertcount;
75                         lerp3 = ent->frameblend[2].lerp;
76                         if (ent->frameblend[3].lerp)
77                         {
78                                 verts4 = ent->model->mdlmd2data_pose + ent->frameblend[3].frame * vertcount;
79                                 lerp4 = ent->frameblend[3].lerp;
80                                 // generate vertices
81                                 if (svectors != NULL)
82                                 {
83                                         for (i = 0;i < vertcount;i++, vertices += 4, normals += 4, svectors += 4, tvectors += 4, verts1++, verts2++, verts3++, verts4++)
84                                         {
85                                                 VectorMAMAMAM(lerp1, verts1->origin, lerp2, verts2->origin, lerp3, verts3->origin, lerp4, verts4->origin, vertices);
86                                                 VectorMAMAMAM(lerp1, verts1->normal, lerp2, verts2->normal, lerp3, verts3->normal, lerp4, verts4->normal, normals);
87                                                 VectorMAMAMAM(lerp1, verts1->svector, lerp2, verts2->svector, lerp3, verts3->svector, lerp4, verts4->svector, svectors);
88                                                 CrossProduct(svectors, normals, tvectors);
89                                         }
90                                 }
91                                 else if (normals != NULL)
92                                 {
93                                         for (i = 0;i < vertcount;i++, vertices += 4, normals += 4, verts1++, verts2++, verts3++, verts4++)
94                                         {
95                                                 VectorMAMAMAM(lerp1, verts1->origin, lerp2, verts2->origin, lerp3, verts3->origin, lerp4, verts4->origin, vertices);
96                                                 VectorMAMAMAM(lerp1, verts1->normal, lerp2, verts2->normal, lerp3, verts3->normal, lerp4, verts4->normal, normals);
97                                         }
98                                 }
99                                 else
100                                         for (i = 0;i < vertcount;i++, vertices += 4, verts1++, verts2++, verts3++, verts4++)
101                                                 VectorMAMAMAM(lerp1, verts1->origin, lerp2, verts2->origin, lerp3, verts3->origin, lerp4, verts4->origin, vertices);
102                         }
103                         else
104                         {
105                                 // generate vertices
106                                 if (svectors != NULL)
107                                 {
108                                         for (i = 0;i < vertcount;i++, vertices += 4, normals += 4, svectors += 4, tvectors += 4, verts1++, verts2++, verts3++)
109                                         {
110                                                 VectorMAMAM(lerp1, verts1->origin, lerp2, verts2->origin, lerp3, verts3->origin, vertices);
111                                                 VectorMAMAM(lerp1, verts1->normal, lerp2, verts2->normal, lerp3, verts3->normal, normals);
112                                                 VectorMAMAM(lerp1, verts1->svector, lerp2, verts2->svector, lerp3, verts3->svector, svectors);
113                                                 CrossProduct(svectors, normals, tvectors);
114                                         }
115                                 }
116                                 else if (normals != NULL)
117                                 {
118                                         for (i = 0;i < vertcount;i++, vertices += 4, normals += 4, verts1++, verts2++, verts3++)
119                                         {
120                                                 VectorMAMAM(lerp1, verts1->origin, lerp2, verts2->origin, lerp3, verts3->origin, vertices);
121                                                 VectorMAMAM(lerp1, verts1->normal, lerp2, verts2->normal, lerp3, verts3->normal, normals);
122                                         }
123                                 }
124                                 else
125                                         for (i = 0;i < vertcount;i++, vertices += 4, verts1++, verts2++, verts3++)
126                                                 VectorMAMAM(lerp1, verts1->origin, lerp2, verts2->origin, lerp3, verts3->origin, vertices);
127                         }
128                 }
129                 else
130                 {
131                         // generate vertices
132                         if (svectors != NULL)
133                         {
134                                 for (i = 0;i < vertcount;i++, vertices += 4, normals += 4, svectors += 4, tvectors += 4, verts1++, verts2++)
135                                 {
136                                         VectorMAM(lerp1, verts1->origin, lerp2, verts2->origin, vertices);
137                                         VectorMAM(lerp1, verts1->normal, lerp2, verts2->normal, normals);
138                                         VectorMAM(lerp1, verts1->svector, lerp2, verts2->svector, svectors);
139                                         CrossProduct(svectors, normals, tvectors);
140                                 }
141                         }
142                         else if (normals != NULL)
143                         {
144                                 for (i = 0;i < vertcount;i++, vertices += 4, normals += 4, verts1++, verts2++)
145                                 {
146                                         VectorMAM(lerp1, verts1->origin, lerp2, verts2->origin, vertices);
147                                         VectorMAM(lerp1, verts1->normal, lerp2, verts2->normal, normals);
148                                 }
149                         }
150                         else
151                                 for (i = 0;i < vertcount;i++, vertices += 4, verts1++, verts2++)
152                                         VectorMAM(lerp1, verts1->origin, lerp2, verts2->origin, vertices);
153                 }
154         }
155         else
156         {
157                 // generate vertices
158                 if (svectors != NULL)
159                 {
160                         for (i = 0;i < vertcount;i++, vertices += 4, normals += 4, svectors += 4, tvectors += 4, verts1++)
161                         {
162                                 VectorM(lerp1, verts1->origin, vertices);
163                                 VectorM(lerp1, verts1->normal, normals);
164                                 VectorM(lerp1, verts1->svector, svectors);
165                                 CrossProduct(svectors, normals, tvectors);
166                         }
167                 }
168                 else if (normals != NULL)
169                 {
170                         for (i = 0;i < vertcount;i++, vertices += 4, normals += 4, verts1++)
171                         {
172                                 VectorM(lerp1, verts1->origin, vertices);
173                                 VectorM(lerp1, verts1->normal, normals);
174                         }
175                 }
176                 else
177                         for (i = 0;i < vertcount;i++, vertices += 4, verts1++)
178                                 VectorM(lerp1, verts1->origin, vertices);
179         }
180 }
181
182 skinframe_t *R_FetchSkinFrame(const entity_render_t *ent)
183 {
184         model_t *model = ent->model;
185         int s = ent->skinnum;
186         if ((unsigned int)s >= (unsigned int)model->numskins)
187                 s = 0;
188         if (model->skinscenes[s].framecount > 1)
189                 return &model->skinframes[model->skinscenes[s].firstframe + (int) (cl.time * 10) % model->skinscenes[s].framecount];
190         else
191                 return &model->skinframes[model->skinscenes[s].firstframe];
192 }
193
194 aliasskin_t *R_FetchAliasSkin(const entity_render_t *ent, const aliasmesh_t *mesh)
195 {
196         model_t *model = ent->model;
197         int s = ent->skinnum;
198         if ((unsigned int)s >= (unsigned int)model->numskins)
199                 s = 0;
200         if (model->skinscenes[s].framecount > 1)
201                 s = model->skinscenes[s].firstframe + (int) (cl.time * model->skinscenes[s].framerate) % model->skinscenes[s].framecount;
202         else
203                 s = model->skinscenes[s].firstframe;
204         if (s > mesh->num_skins)
205                 return mesh->data_skins;
206         return mesh->data_skins + s;
207 }
208
209 void R_DrawAliasModelCallback (const void *calldata1, int calldata2)
210 {
211         int c, fullbright, layernum;
212         float tint[3], fog, ifog, colorscale;
213         vec3_t diff;
214         qbyte *bcolor;
215         rmeshstate_t m;
216         const entity_render_t *ent = calldata1;
217         aliasmesh_t *mesh = ent->model->mdlmd2data_meshes + calldata2;
218         aliaslayer_t *layer;
219         aliasskin_t *skin;
220
221         R_Mesh_Matrix(&ent->matrix);
222
223         fog = 0;
224         if (fogenabled)
225         {
226                 VectorSubtract(ent->origin, r_origin, diff);
227                 fog = DotProduct(diff,diff);
228                 if (fog < 0.01f)
229                         fog = 0.01f;
230                 fog = exp(fogdensity/fog);
231                 if (fog > 1)
232                         fog = 1;
233                 if (fog < 0.01f)
234                         fog = 0;
235                 // fog method: darken, additive fog
236                 // 1. render model as normal, scaled by inverse of fog alpha (darkens it)
237                 // 2. render fog as additive
238         }
239         ifog = 1 - fog;
240
241         memset(&m, 0, sizeof(m));
242         skin = R_FetchAliasSkin(ent, mesh);
243         R_Mesh_ResizeCheck(mesh->num_vertices);
244         R_Model_Alias_GetVerts(ent, varray_vertex, aliasvert_normals, NULL, NULL);
245         memcpy(varray_texcoord[0], mesh->data_texcoords, mesh->num_vertices * sizeof(float[4]));
246         for (layernum = 0, layer = skin->data_layers;layernum < skin->num_layers;layernum++, layer++)
247         {
248                 if (((layer->flags & ALIASLAYER_NODRAW_IF_NOTCOLORMAPPED) && ent->colormap < 0)
249                  || ((layer->flags & ALIASLAYER_NODRAW_IF_COLORMAPPED) && ent->colormap >= 0)
250                  ||  (layer->flags & ALIASLAYER_DRAW_PER_LIGHT))
251                         continue;
252                 if (layer->flags & ALIASLAYER_FOG)
253                 {
254                         m.blendfunc1 = GL_SRC_ALPHA;
255                         m.blendfunc2 = GL_ONE;
256                         colorscale = r_colorscale;
257                         m.texrgbscale[0] = 1;
258                         m.tex[0] = R_GetTexture(layer->texture);
259                         R_Mesh_State(&m);
260                         GL_Color(fogcolor[0] * fog * colorscale, fogcolor[1] * fog * colorscale, fogcolor[2] * fog * colorscale, ent->alpha);
261                         c_alias_polys += mesh->num_triangles;
262                         R_Mesh_Draw(mesh->num_vertices, mesh->num_triangles, mesh->data_elements);
263                         continue;
264                 }
265                 if ((layer->flags & ALIASLAYER_ADD) || ((layer->flags & ALIASLAYER_ALPHA) && (ent->effects & EF_ADDITIVE)))
266                 {
267                         m.blendfunc1 = GL_SRC_ALPHA;
268                         m.blendfunc2 = GL_ONE;
269                 }
270                 else if ((layer->flags & ALIASLAYER_ALPHA) || ent->alpha != 1.0)
271                 {
272                         m.blendfunc1 = GL_SRC_ALPHA;
273                         m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
274                 }
275                 else
276                 {
277                         m.blendfunc1 = GL_ONE;
278                         m.blendfunc2 = GL_ZERO;
279                 }
280                 colorscale = r_colorscale;
281                 m.texrgbscale[0] = 1;
282                 if (gl_combine.integer)
283                 {
284                         colorscale *= 0.25f;
285                         m.texrgbscale[0] = 4;
286                 }
287                 m.tex[0] = R_GetTexture(layer->texture);
288                 R_Mesh_State(&m);
289                 if (layer->flags & ALIASLAYER_COLORMAP_PANTS)
290                 {
291                         // 128-224 are backwards ranges
292                         c = (ent->colormap & 0xF) << 4;c += (c >= 128 && c < 224) ? 4 : 12;
293                         bcolor = (qbyte *) (&palette_complete[c]);
294                         fullbright = c >= 224;
295                         VectorScale(bcolor, (1.0f / 255.0f), tint);
296                 }
297                 else if (layer->flags & ALIASLAYER_COLORMAP_SHIRT)
298                 {
299                         // 128-224 are backwards ranges
300                         c = (ent->colormap & 0xF0);c += (c >= 128 && c < 224) ? 4 : 12;
301                         bcolor = (qbyte *) (&palette_complete[c]);
302                         fullbright = c >= 224;
303                         VectorScale(bcolor, (1.0f / 255.0f), tint);
304                 }
305                 else
306                 {
307                         tint[0] = tint[1] = tint[2] = 1;
308                         fullbright = false;
309                 }
310                 VectorScale(tint, ifog * colorscale, tint);
311                 if (!(layer->flags & ALIASLAYER_DIFFUSE))
312                         fullbright = true;
313                 if (ent->effects & EF_FULLBRIGHT)
314                         fullbright = true;
315                 if (fullbright)
316                         GL_Color(tint[0], tint[1], tint[2], ent->alpha);
317                 else
318                         R_LightModel(ent, mesh->num_vertices, varray_vertex, aliasvert_normals, varray_color, tint[0], tint[1], tint[2], false);
319                 c_alias_polys += mesh->num_triangles;
320                 R_Mesh_Draw(mesh->num_vertices, mesh->num_triangles, mesh->data_elements);
321         }
322 }
323
324 void R_Model_Alias_Draw(entity_render_t *ent)
325 {
326         int meshnum;
327         aliasmesh_t *mesh;
328         if (ent->alpha < (1.0f / 64.0f))
329                 return; // basically completely transparent
330
331         c_models++;
332
333         for (meshnum = 0, mesh = ent->model->mdlmd2data_meshes;meshnum < ent->model->mdlmd2num_meshes;meshnum++, mesh++)
334         {
335                 if (ent->effects & EF_ADDITIVE || ent->alpha != 1.0 || R_FetchAliasSkin(ent, mesh)->flags & ALIASSKIN_TRANSPARENT)
336                         R_MeshQueue_AddTransparent(ent->origin, R_DrawAliasModelCallback, ent, meshnum);
337                 else
338                         R_DrawAliasModelCallback(ent, meshnum);
339         }
340 }
341
342 void R_Model_Alias_DrawFakeShadow (entity_render_t *ent)
343 {
344         int i, meshnum;
345         aliasmesh_t *mesh;
346         aliasskin_t *skin;
347         rmeshstate_t m;
348         float *v, planenormal[3], planedist, dist, projection[3], floororigin[3], surfnormal[3], lightdirection[3], v2[3];
349
350         if ((ent->effects & EF_ADDITIVE) || ent->alpha < 1)
351                 return;
352
353         lightdirection[0] = 0.5;
354         lightdirection[1] = 0.2;
355         lightdirection[2] = -1;
356         VectorNormalizeFast(lightdirection);
357
358         VectorMA(ent->origin, 65536.0f, lightdirection, v2);
359         if (CL_TraceLine(ent->origin, v2, floororigin, surfnormal, 0, false, NULL) == 1)
360                 return;
361
362         R_Mesh_Matrix(&ent->matrix);
363
364         memset(&m, 0, sizeof(m));
365         m.blendfunc1 = GL_SRC_ALPHA;
366         m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
367         R_Mesh_State(&m);
368         GL_Color(0, 0, 0, 0.5);
369
370         // put a light direction in the entity's coordinate space
371         Matrix4x4_Transform3x3(&ent->inversematrix, lightdirection, projection);
372         VectorNormalizeFast(projection);
373
374         // put the plane's normal in the entity's coordinate space
375         Matrix4x4_Transform3x3(&ent->inversematrix, surfnormal, planenormal);
376         VectorNormalizeFast(planenormal);
377
378         // put the plane's distance in the entity's coordinate space
379         VectorSubtract(floororigin, ent->origin, floororigin);
380         planedist = DotProduct(floororigin, surfnormal) + 2;
381
382         dist = -1.0f / DotProduct(projection, planenormal);
383         VectorScale(projection, dist, projection);
384         for (meshnum = 0, mesh = ent->model->mdlmd2data_meshes;meshnum < ent->model->mdlmd2num_meshes;meshnum++)
385         {
386                 skin = R_FetchAliasSkin(ent, mesh);
387                 if (skin->flags & ALIASSKIN_TRANSPARENT)
388                         continue;
389                 R_Mesh_ResizeCheck(mesh->num_vertices);
390                 R_Model_Alias_GetVerts(ent, varray_vertex, NULL, NULL, NULL);
391                 for (i = 0, v = varray_vertex;i < mesh->num_vertices;i++, v += 4)
392                 {
393                         dist = DotProduct(v, planenormal) - planedist;
394                         if (dist > 0)
395                                 VectorMA(v, dist, projection, v);
396                 }
397                 c_alias_polys += mesh->num_triangles;
398                 R_Mesh_Draw(mesh->num_vertices, mesh->num_triangles, mesh->data_elements);
399         }
400 }
401
402 void R_Model_Alias_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius)
403 {
404         int meshnum;
405         aliasmesh_t *mesh;
406         aliasskin_t *skin;
407         float projectdistance;
408         if (ent->effects & EF_ADDITIVE || ent->alpha < 1)
409                 return;
410         projectdistance = lightradius + ent->model->radius - sqrt(DotProduct(relativelightorigin, relativelightorigin));
411         if (projectdistance > 0.1)
412         {
413                 R_Mesh_Matrix(&ent->matrix);
414                 for (meshnum = 0, mesh = ent->model->mdlmd2data_meshes;meshnum < ent->model->mdlmd2num_meshes;meshnum++)
415                 {
416                         skin = R_FetchAliasSkin(ent, mesh);
417                         if (skin->flags & ALIASSKIN_TRANSPARENT)
418                                 continue;
419                         R_Mesh_ResizeCheck(mesh->num_vertices * 2);
420                         R_Model_Alias_GetVerts(ent, varray_vertex, NULL, NULL, NULL);
421                         R_Shadow_Volume(mesh->num_vertices, mesh->num_triangles, mesh->data_elements, mesh->data_neighbors, relativelightorigin, lightradius, projectdistance);
422                 }
423         }
424 }
425
426 void R_Model_Alias_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor)
427 {
428         int c, meshnum, layernum;
429         float fog, ifog, lightcolor2[3];
430         vec3_t diff;
431         qbyte *bcolor;
432         aliasmesh_t *mesh;
433         aliaslayer_t *layer;
434         aliasskin_t *skin;
435
436         if (ent->effects & (EF_ADDITIVE | EF_FULLBRIGHT) || ent->alpha < 1)
437                 return;
438
439         R_Mesh_Matrix(&ent->matrix);
440
441         fog = 0;
442         if (fogenabled)
443         {
444                 VectorSubtract(ent->origin, r_origin, diff);
445                 fog = DotProduct(diff,diff);
446                 if (fog < 0.01f)
447                         fog = 0.01f;
448                 fog = exp(fogdensity/fog);
449                 if (fog > 1)
450                         fog = 1;
451                 if (fog < 0.01f)
452                         fog = 0;
453                 // fog method: darken, additive fog
454                 // 1. render model as normal, scaled by inverse of fog alpha (darkens it)
455                 // 2. render fog as additive
456         }
457         ifog = 1 - fog;
458
459         for (meshnum = 0, mesh = ent->model->mdlmd2data_meshes;meshnum < ent->model->mdlmd2num_meshes;meshnum++, mesh++)
460         {
461                 skin = R_FetchAliasSkin(ent, mesh);
462                 if (skin->flags & ALIASSKIN_TRANSPARENT)
463                         continue;
464                 R_Mesh_ResizeCheck(mesh->num_vertices);
465                 R_Model_Alias_GetVerts(ent, varray_vertex, aliasvert_normals, aliasvert_svectors, aliasvert_tvectors);
466                 for (layernum = 0, layer = skin->data_layers;layernum < skin->num_layers;layernum++, layer++)
467                 {
468                         if (!(layer->flags & ALIASLAYER_DRAW_PER_LIGHT)
469                          || ((layer->flags & ALIASLAYER_NODRAW_IF_NOTCOLORMAPPED) && ent->colormap < 0)
470                          || ((layer->flags & ALIASLAYER_NODRAW_IF_COLORMAPPED) && ent->colormap >= 0))
471                                 continue;
472                         lightcolor2[0] = lightcolor[0] * ifog;
473                         lightcolor2[1] = lightcolor[1] * ifog;
474                         lightcolor2[2] = lightcolor[2] * ifog;
475                         if (layer->flags & ALIASLAYER_SPECULAR)
476                         {
477                                 c_alias_polys += mesh->num_triangles;
478                                 R_Shadow_SpecularLighting(mesh->num_vertices, mesh->num_triangles, mesh->data_elements, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, mesh->data_texcoords, relativelightorigin, relativeeyeorigin, lightradius, lightcolor2, layer->texture, layer->nmap, NULL);
479                         }
480                         else if (layer->flags & ALIASLAYER_DIFFUSE)
481                         {
482                                 if (layer->flags & ALIASLAYER_COLORMAP_PANTS)
483                                 {
484                                         // 128-224 are backwards ranges
485                                         c = (ent->colormap & 0xF) << 4;c += (c >= 128 && c < 224) ? 4 : 12;
486                                         // fullbright passes were already taken care of, so skip them in realtime lighting passes
487                                         if (c >= 224)
488                                                 continue;
489                                         bcolor = (qbyte *) (&palette_complete[c]);
490                                         lightcolor2[0] *= bcolor[0] * (1.0f / 255.0f);
491                                         lightcolor2[1] *= bcolor[1] * (1.0f / 255.0f);
492                                         lightcolor2[2] *= bcolor[2] * (1.0f / 255.0f);
493                                 }
494                                 else if (layer->flags & ALIASLAYER_COLORMAP_SHIRT)
495                                 {
496                                         // 128-224 are backwards ranges
497                                         c = (ent->colormap & 0xF0);c += (c >= 128 && c < 224) ? 4 : 12;
498                                         // fullbright passes were already taken care of, so skip them in realtime lighting passes
499                                         if (c >= 224)
500                                                 continue;
501                                         bcolor = (qbyte *) (&palette_complete[c]);
502                                         lightcolor2[0] *= bcolor[0] * (1.0f / 255.0f);
503                                         lightcolor2[1] *= bcolor[1] * (1.0f / 255.0f);
504                                         lightcolor2[2] *= bcolor[2] * (1.0f / 255.0f);
505                                 }
506                                 c_alias_polys += mesh->num_triangles;
507                                 R_Shadow_DiffuseLighting(mesh->num_vertices, mesh->num_triangles, mesh->data_elements, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, mesh->data_texcoords, relativelightorigin, lightradius, lightcolor2, layer->texture, layer->nmap, NULL);
508                         }
509                 }
510         }
511 /*
512         int c;
513         float lightcolor2[3];
514         qbyte *bcolor;
515         skinframe_t *skinframe;
516         R_Mesh_Matrix(&ent->matrix);
517         R_Mesh_ResizeCheck(ent->model->numverts);
518         R_Model_Alias_GetVerts(ent, varray_vertex, aliasvert_normals, aliasvert_svectors, aliasvert_tvectors);
519         skinframe = R_FetchSkinFrame(ent);
520
521         // note: to properly handle fog this should scale the lightcolor into lightcolor2 according to 1-fog scaling
522
523         R_Shadow_SpecularLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, NULL, NULL, NULL);
524
525         if (!skinframe->base && !skinframe->pants && !skinframe->shirt && !skinframe->glow)
526         {
527                 R_Shadow_DiffuseLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, lightradius, lightcolor, r_notexture, NULL, NULL);
528                 return;
529         }
530
531         if (!skinframe->merged || (ent->colormap >= 0 && skinframe->base && (skinframe->pants || skinframe->shirt)))
532         {
533                 // 128-224 are backwards ranges
534                 // we only render non-fullbright ranges here
535                 if (skinframe->pants && (ent->colormap & 0xF) < 0xE)
536                 {
537                         c = (ent->colormap & 0xF) << 4;c += (c >= 128 && c < 224) ? 4 : 12;
538                         bcolor = (qbyte *) (&palette_complete[c]);
539                         lightcolor2[0] = lightcolor[0] * bcolor[0] * (1.0f / 255.0f);
540                         lightcolor2[1] = lightcolor[1] * bcolor[1] * (1.0f / 255.0f);
541                         lightcolor2[2] = lightcolor[2] * bcolor[2] * (1.0f / 255.0f);
542                         R_Shadow_DiffuseLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, lightradius, lightcolor2, skinframe->pants, skinframe->nmap, NULL);
543                 }
544
545                 // we only render non-fullbright ranges here
546                 if (skinframe->shirt && (ent->colormap & 0xF0) < 0xE0)
547                 {
548                         c = (ent->colormap & 0xF0);c += (c >= 128 && c < 224) ? 4 : 12;
549                         bcolor = (qbyte *) (&palette_complete[c]);
550                         lightcolor2[0] = lightcolor[0] * bcolor[0] * (1.0f / 255.0f);
551                         lightcolor2[1] = lightcolor[1] * bcolor[1] * (1.0f / 255.0f);
552                         lightcolor2[2] = lightcolor[2] * bcolor[2] * (1.0f / 255.0f);
553                         R_Shadow_DiffuseLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, lightradius, lightcolor2, skinframe->shirt, skinframe->nmap, NULL);
554                 }
555
556                 if (skinframe->base)
557                         R_Shadow_DiffuseLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, lightradius, lightcolor, skinframe->base, skinframe->nmap, NULL);
558         }
559         else
560                 if (skinframe->merged)
561                         R_Shadow_DiffuseLighting(ent->model->numverts, ent->model->numtris, ent->model->mdlmd2data_indices, aliasvert_svectors, aliasvert_tvectors, aliasvert_normals, ent->model->mdlmd2data_texcoords, relativelightorigin, lightradius, lightcolor, skinframe->merged, skinframe->nmap, NULL);
562 */
563 }
564
565 int ZymoticLerpBones(int count, const zymbonematrix *bonebase, const frameblend_t *blend, const zymbone_t *bone)
566 {
567         int i;
568         float lerp1, lerp2, lerp3, lerp4;
569         zymbonematrix *out, rootmatrix, m;
570         const zymbonematrix *bone1, *bone2, *bone3, *bone4;
571
572         rootmatrix.m[0][0] = 1;
573         rootmatrix.m[0][1] = 0;
574         rootmatrix.m[0][2] = 0;
575         rootmatrix.m[0][3] = 0;
576         rootmatrix.m[1][0] = 0;
577         rootmatrix.m[1][1] = 1;
578         rootmatrix.m[1][2] = 0;
579         rootmatrix.m[1][3] = 0;
580         rootmatrix.m[2][0] = 0;
581         rootmatrix.m[2][1] = 0;
582         rootmatrix.m[2][2] = 1;
583         rootmatrix.m[2][3] = 0;
584
585         bone1 = bonebase + blend[0].frame * count;
586         lerp1 = blend[0].lerp;
587         if (blend[1].lerp)
588         {
589                 bone2 = bonebase + blend[1].frame * count;
590                 lerp2 = blend[1].lerp;
591                 if (blend[2].lerp)
592                 {
593                         bone3 = bonebase + blend[2].frame * count;
594                         lerp3 = blend[2].lerp;
595                         if (blend[3].lerp)
596                         {
597                                 // 4 poses
598                                 bone4 = bonebase + blend[3].frame * count;
599                                 lerp4 = blend[3].lerp;
600                                 for (i = 0, out = zymbonepose;i < count;i++, out++)
601                                 {
602                                         // interpolate matrices
603                                         m.m[0][0] = bone1->m[0][0] * lerp1 + bone2->m[0][0] * lerp2 + bone3->m[0][0] * lerp3 + bone4->m[0][0] * lerp4;
604                                         m.m[0][1] = bone1->m[0][1] * lerp1 + bone2->m[0][1] * lerp2 + bone3->m[0][1] * lerp3 + bone4->m[0][1] * lerp4;
605                                         m.m[0][2] = bone1->m[0][2] * lerp1 + bone2->m[0][2] * lerp2 + bone3->m[0][2] * lerp3 + bone4->m[0][2] * lerp4;
606                                         m.m[0][3] = bone1->m[0][3] * lerp1 + bone2->m[0][3] * lerp2 + bone3->m[0][3] * lerp3 + bone4->m[0][3] * lerp4;
607                                         m.m[1][0] = bone1->m[1][0] * lerp1 + bone2->m[1][0] * lerp2 + bone3->m[1][0] * lerp3 + bone4->m[1][0] * lerp4;
608                                         m.m[1][1] = bone1->m[1][1] * lerp1 + bone2->m[1][1] * lerp2 + bone3->m[1][1] * lerp3 + bone4->m[1][1] * lerp4;
609                                         m.m[1][2] = bone1->m[1][2] * lerp1 + bone2->m[1][2] * lerp2 + bone3->m[1][2] * lerp3 + bone4->m[1][2] * lerp4;
610                                         m.m[1][3] = bone1->m[1][3] * lerp1 + bone2->m[1][3] * lerp2 + bone3->m[1][3] * lerp3 + bone4->m[1][3] * lerp4;
611                                         m.m[2][0] = bone1->m[2][0] * lerp1 + bone2->m[2][0] * lerp2 + bone3->m[2][0] * lerp3 + bone4->m[2][0] * lerp4;
612                                         m.m[2][1] = bone1->m[2][1] * lerp1 + bone2->m[2][1] * lerp2 + bone3->m[2][1] * lerp3 + bone4->m[2][1] * lerp4;
613                                         m.m[2][2] = bone1->m[2][2] * lerp1 + bone2->m[2][2] * lerp2 + bone3->m[2][2] * lerp3 + bone4->m[2][2] * lerp4;
614                                         m.m[2][3] = bone1->m[2][3] * lerp1 + bone2->m[2][3] * lerp2 + bone3->m[2][3] * lerp3 + bone4->m[2][3] * lerp4;
615                                         if (bone->parent >= 0)
616                                                 R_ConcatTransforms(&zymbonepose[bone->parent].m[0][0], &m.m[0][0], &out->m[0][0]);
617                                         else
618                                                 R_ConcatTransforms(&rootmatrix.m[0][0], &m.m[0][0], &out->m[0][0]);
619                                         bone1++;
620                                         bone2++;
621                                         bone3++;
622                                         bone4++;
623                                         bone++;
624                                 }
625                         }
626                         else
627                         {
628                                 // 3 poses
629                                 for (i = 0, out = zymbonepose;i < count;i++, out++)
630                                 {
631                                         // interpolate matrices
632                                         m.m[0][0] = bone1->m[0][0] * lerp1 + bone2->m[0][0] * lerp2 + bone3->m[0][0] * lerp3;
633                                         m.m[0][1] = bone1->m[0][1] * lerp1 + bone2->m[0][1] * lerp2 + bone3->m[0][1] * lerp3;
634                                         m.m[0][2] = bone1->m[0][2] * lerp1 + bone2->m[0][2] * lerp2 + bone3->m[0][2] * lerp3;
635                                         m.m[0][3] = bone1->m[0][3] * lerp1 + bone2->m[0][3] * lerp2 + bone3->m[0][3] * lerp3;
636                                         m.m[1][0] = bone1->m[1][0] * lerp1 + bone2->m[1][0] * lerp2 + bone3->m[1][0] * lerp3;
637                                         m.m[1][1] = bone1->m[1][1] * lerp1 + bone2->m[1][1] * lerp2 + bone3->m[1][1] * lerp3;
638                                         m.m[1][2] = bone1->m[1][2] * lerp1 + bone2->m[1][2] * lerp2 + bone3->m[1][2] * lerp3;
639                                         m.m[1][3] = bone1->m[1][3] * lerp1 + bone2->m[1][3] * lerp2 + bone3->m[1][3] * lerp3;
640                                         m.m[2][0] = bone1->m[2][0] * lerp1 + bone2->m[2][0] * lerp2 + bone3->m[2][0] * lerp3;
641                                         m.m[2][1] = bone1->m[2][1] * lerp1 + bone2->m[2][1] * lerp2 + bone3->m[2][1] * lerp3;
642                                         m.m[2][2] = bone1->m[2][2] * lerp1 + bone2->m[2][2] * lerp2 + bone3->m[2][2] * lerp3;
643                                         m.m[2][3] = bone1->m[2][3] * lerp1 + bone2->m[2][3] * lerp2 + bone3->m[2][3] * lerp3;
644                                         if (bone->parent >= 0)
645                                                 R_ConcatTransforms(&zymbonepose[bone->parent].m[0][0], &m.m[0][0], &out->m[0][0]);
646                                         else
647                                                 R_ConcatTransforms(&rootmatrix.m[0][0], &m.m[0][0], &out->m[0][0]);
648                                         bone1++;
649                                         bone2++;
650                                         bone3++;
651                                         bone++;
652                                 }
653                         }
654                 }
655                 else
656                 {
657                         // 2 poses
658                         for (i = 0, out = zymbonepose;i < count;i++, out++)
659                         {
660                                 // interpolate matrices
661                                 m.m[0][0] = bone1->m[0][0] * lerp1 + bone2->m[0][0] * lerp2;
662                                 m.m[0][1] = bone1->m[0][1] * lerp1 + bone2->m[0][1] * lerp2;
663                                 m.m[0][2] = bone1->m[0][2] * lerp1 + bone2->m[0][2] * lerp2;
664                                 m.m[0][3] = bone1->m[0][3] * lerp1 + bone2->m[0][3] * lerp2;
665                                 m.m[1][0] = bone1->m[1][0] * lerp1 + bone2->m[1][0] * lerp2;
666                                 m.m[1][1] = bone1->m[1][1] * lerp1 + bone2->m[1][1] * lerp2;
667                                 m.m[1][2] = bone1->m[1][2] * lerp1 + bone2->m[1][2] * lerp2;
668                                 m.m[1][3] = bone1->m[1][3] * lerp1 + bone2->m[1][3] * lerp2;
669                                 m.m[2][0] = bone1->m[2][0] * lerp1 + bone2->m[2][0] * lerp2;
670                                 m.m[2][1] = bone1->m[2][1] * lerp1 + bone2->m[2][1] * lerp2;
671                                 m.m[2][2] = bone1->m[2][2] * lerp1 + bone2->m[2][2] * lerp2;
672                                 m.m[2][3] = bone1->m[2][3] * lerp1 + bone2->m[2][3] * lerp2;
673                                 if (bone->parent >= 0)
674                                         R_ConcatTransforms(&zymbonepose[bone->parent].m[0][0], &m.m[0][0], &out->m[0][0]);
675                                 else
676                                         R_ConcatTransforms(&rootmatrix.m[0][0], &m.m[0][0], &out->m[0][0]);
677                                 bone1++;
678                                 bone2++;
679                                 bone++;
680                         }
681                 }
682         }
683         else
684         {
685                 // 1 pose
686                 if (lerp1 != 1)
687                 {
688                         // lerp != 1.0
689                         for (i = 0, out = zymbonepose;i < count;i++, out++)
690                         {
691                                 // interpolate matrices
692                                 m.m[0][0] = bone1->m[0][0] * lerp1;
693                                 m.m[0][1] = bone1->m[0][1] * lerp1;
694                                 m.m[0][2] = bone1->m[0][2] * lerp1;
695                                 m.m[0][3] = bone1->m[0][3] * lerp1;
696                                 m.m[1][0] = bone1->m[1][0] * lerp1;
697                                 m.m[1][1] = bone1->m[1][1] * lerp1;
698                                 m.m[1][2] = bone1->m[1][2] * lerp1;
699                                 m.m[1][3] = bone1->m[1][3] * lerp1;
700                                 m.m[2][0] = bone1->m[2][0] * lerp1;
701                                 m.m[2][1] = bone1->m[2][1] * lerp1;
702                                 m.m[2][2] = bone1->m[2][2] * lerp1;
703                                 m.m[2][3] = bone1->m[2][3] * lerp1;
704                                 if (bone->parent >= 0)
705                                         R_ConcatTransforms(&zymbonepose[bone->parent].m[0][0], &m.m[0][0], &out->m[0][0]);
706                                 else
707                                         R_ConcatTransforms(&rootmatrix.m[0][0], &m.m[0][0], &out->m[0][0]);
708                                 bone1++;
709                                 bone++;
710                         }
711                 }
712                 else
713                 {
714                         // lerp == 1.0
715                         for (i = 0, out = zymbonepose;i < count;i++, out++)
716                         {
717                                 if (bone->parent >= 0)
718                                         R_ConcatTransforms(&zymbonepose[bone->parent].m[0][0], &bone1->m[0][0], &out->m[0][0]);
719                                 else
720                                         R_ConcatTransforms(&rootmatrix.m[0][0], &bone1->m[0][0], &out->m[0][0]);
721                                 bone1++;
722                                 bone++;
723                         }
724                 }
725         }
726         return true;
727 }
728
729 void ZymoticTransformVerts(int vertcount, float *vertex, int *bonecounts, zymvertex_t *vert)
730 {
731         int c;
732         float *out = vertex;
733         zymbonematrix *matrix;
734         while(vertcount--)
735         {
736                 c = *bonecounts++;
737                 // FIXME: validate bonecounts at load time (must be >= 1)
738                 // FIXME: need 4th component in origin, for how much of the translate to blend in
739                 if (c == 1)
740                 {
741                         matrix = &zymbonepose[vert->bonenum];
742                         out[0] = vert->origin[0] * matrix->m[0][0] + vert->origin[1] * matrix->m[0][1] + vert->origin[2] * matrix->m[0][2] + matrix->m[0][3];
743                         out[1] = vert->origin[0] * matrix->m[1][0] + vert->origin[1] * matrix->m[1][1] + vert->origin[2] * matrix->m[1][2] + matrix->m[1][3];
744                         out[2] = vert->origin[0] * matrix->m[2][0] + vert->origin[1] * matrix->m[2][1] + vert->origin[2] * matrix->m[2][2] + matrix->m[2][3];
745                         vert++;
746                 }
747                 else
748                 {
749                         VectorClear(out);
750                         while(c--)
751                         {
752                                 matrix = &zymbonepose[vert->bonenum];
753                                 out[0] += vert->origin[0] * matrix->m[0][0] + vert->origin[1] * matrix->m[0][1] + vert->origin[2] * matrix->m[0][2] + matrix->m[0][3];
754                                 out[1] += vert->origin[0] * matrix->m[1][0] + vert->origin[1] * matrix->m[1][1] + vert->origin[2] * matrix->m[1][2] + matrix->m[1][3];
755                                 out[2] += vert->origin[0] * matrix->m[2][0] + vert->origin[1] * matrix->m[2][1] + vert->origin[2] * matrix->m[2][2] + matrix->m[2][3];
756                                 vert++;
757                         }
758                 }
759                 out += 4;
760         }
761 }
762
763 void ZymoticCalcNormals(int vertcount, float *vertex, float *normals, int shadercount, int *renderlist)
764 {
765         int a, b, c, d;
766         float *out, v1[3], v2[3], normal[3], s;
767         int *u;
768         // clear normals
769         memset(normals, 0, sizeof(float) * vertcount * 3);
770         memset(aliasvertusage, 0, sizeof(int) * vertcount);
771         // parse render list and accumulate surface normals
772         while(shadercount--)
773         {
774                 d = *renderlist++;
775                 while (d--)
776                 {
777                         a = renderlist[0]*4;
778                         b = renderlist[1]*4;
779                         c = renderlist[2]*4;
780                         v1[0] = vertex[a+0] - vertex[b+0];
781                         v1[1] = vertex[a+1] - vertex[b+1];
782                         v1[2] = vertex[a+2] - vertex[b+2];
783                         v2[0] = vertex[c+0] - vertex[b+0];
784                         v2[1] = vertex[c+1] - vertex[b+1];
785                         v2[2] = vertex[c+2] - vertex[b+2];
786                         CrossProduct(v1, v2, normal);
787                         VectorNormalizeFast(normal);
788                         // add surface normal to vertices
789                         a = renderlist[0] * 3;
790                         normals[a+0] += normal[0];
791                         normals[a+1] += normal[1];
792                         normals[a+2] += normal[2];
793                         aliasvertusage[renderlist[0]]++;
794                         a = renderlist[1] * 3;
795                         normals[a+0] += normal[0];
796                         normals[a+1] += normal[1];
797                         normals[a+2] += normal[2];
798                         aliasvertusage[renderlist[1]]++;
799                         a = renderlist[2] * 3;
800                         normals[a+0] += normal[0];
801                         normals[a+1] += normal[1];
802                         normals[a+2] += normal[2];
803                         aliasvertusage[renderlist[2]]++;
804                         renderlist += 3;
805                 }
806         }
807         // FIXME: precalc this
808         // average surface normals
809         out = normals;
810         u = aliasvertusage;
811         while(vertcount--)
812         {
813                 if (*u > 1)
814                 {
815                         s = ixtable[*u];
816                         out[0] *= s;
817                         out[1] *= s;
818                         out[2] *= s;
819                 }
820                 u++;
821                 out += 3;
822         }
823 }
824
825 void R_DrawZymoticModelMeshCallback (const void *calldata1, int calldata2)
826 {
827         float fog, ifog, colorscale;
828         vec3_t diff;
829         int i, *renderlist, *elements;
830         rtexture_t *texture;
831         rmeshstate_t mstate;
832         const entity_render_t *ent = calldata1;
833         int shadernum = calldata2;
834         int numverts, numtriangles;
835
836         R_Mesh_Matrix(&ent->matrix);
837
838         // find the vertex index list and texture
839         renderlist = ent->model->zymdata_renderlist;
840         for (i = 0;i < shadernum;i++)
841                 renderlist += renderlist[0] * 3 + 1;
842         texture = ent->model->zymdata_textures[shadernum];
843
844         numverts = ent->model->zymnum_verts;
845         numtriangles = *renderlist++;
846         elements = renderlist;
847         R_Mesh_ResizeCheck(numverts);
848
849         fog = 0;
850         if (fogenabled)
851         {
852                 VectorSubtract(ent->origin, r_origin, diff);
853                 fog = DotProduct(diff,diff);
854                 if (fog < 0.01f)
855                         fog = 0.01f;
856                 fog = exp(fogdensity/fog);
857                 if (fog > 1)
858                         fog = 1;
859                 if (fog < 0.01f)
860                         fog = 0;
861                 // fog method: darken, additive fog
862                 // 1. render model as normal, scaled by inverse of fog alpha (darkens it)
863                 // 2. render fog as additive
864         }
865         ifog = 1 - fog;
866
867         memset(&mstate, 0, sizeof(mstate));
868         if (ent->effects & EF_ADDITIVE)
869         {
870                 mstate.blendfunc1 = GL_SRC_ALPHA;
871                 mstate.blendfunc2 = GL_ONE;
872         }
873         else if (ent->alpha != 1.0 || R_TextureHasAlpha(texture))
874         {
875                 mstate.blendfunc1 = GL_SRC_ALPHA;
876                 mstate.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
877         }
878         else
879         {
880                 mstate.blendfunc1 = GL_ONE;
881                 mstate.blendfunc2 = GL_ZERO;
882         }
883         colorscale = r_colorscale;
884         if (gl_combine.integer)
885         {
886                 mstate.texrgbscale[0] = 4;
887                 colorscale *= 0.25f;
888         }
889         mstate.tex[0] = R_GetTexture(texture);
890         R_Mesh_State(&mstate);
891         ZymoticLerpBones(ent->model->zymnum_bones, (zymbonematrix *) ent->model->zymdata_poses, ent->frameblend, ent->model->zymdata_bones);
892         ZymoticTransformVerts(numverts, varray_vertex, ent->model->zymdata_vertbonecounts, ent->model->zymdata_verts);
893         ZymoticCalcNormals(numverts, varray_vertex, aliasvert_normals, ent->model->zymnum_shaders, ent->model->zymdata_renderlist);
894         memcpy(varray_texcoord[0], ent->model->zymdata_texcoords, ent->model->zymnum_verts * sizeof(float[4]));
895         GL_UseColorArray();
896         R_LightModel(ent, numverts, varray_vertex, aliasvert_normals, varray_color, ifog * colorscale, ifog * colorscale, ifog * colorscale, false);
897         R_Mesh_Draw(numverts, numtriangles, elements);
898         c_alias_polys += numtriangles;
899
900         if (fog)
901         {
902                 memset(&mstate, 0, sizeof(mstate));
903                 mstate.blendfunc1 = GL_SRC_ALPHA;
904                 mstate.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
905                 // FIXME: need alpha mask for fogging...
906                 //mstate.tex[0] = R_GetTexture(texture);
907                 R_Mesh_State(&mstate);
908                 GL_Color(fogcolor[0] * r_colorscale, fogcolor[1] * r_colorscale, fogcolor[2] * r_colorscale, ent->alpha * fog);
909                 R_Mesh_Draw(numverts, numtriangles, elements);
910                 c_alias_polys += numtriangles;
911         }
912 }
913
914 void R_Model_Zymotic_Draw(entity_render_t *ent)
915 {
916         int i;
917
918         if (ent->alpha < (1.0f / 64.0f))
919                 return; // basically completely transparent
920
921         c_models++;
922
923         for (i = 0;i < ent->model->zymnum_shaders;i++)
924         {
925                 if (ent->effects & EF_ADDITIVE || ent->alpha != 1.0 || R_TextureHasAlpha(ent->model->zymdata_textures[i]))
926                         R_MeshQueue_AddTransparent(ent->origin, R_DrawZymoticModelMeshCallback, ent, i);
927                 else
928                         R_DrawZymoticModelMeshCallback(ent, i);
929         }
930 }
931
932 void R_Model_Zymotic_DrawFakeShadow(entity_render_t *ent)
933 {
934         // FIXME
935 }
936
937 void R_Model_Zymotic_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor)
938 {
939         // FIXME
940 }
941
942 void R_Model_Zymotic_DrawOntoLight(entity_render_t *ent)
943 {
944         // FIXME
945 }