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