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