1 /*
2 * Copyright(c) 1997-2001 Id Software, Inc.
3 * Copyright(c) 2002 The Quakeforge Project.
4 * Copyright(c) 2006 Quetoo.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or(at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21 
22 #include "video.h"
23 
24 /*
25 
26   ALIAS MODELS
27 
28 */
29 
30 #define NUMVERTEXNORMALS 162
31 
32 float r_avertexnormals[NUMVERTEXNORMALS][3] = {
33 	#include "anorms.h"
34 };
35 
36 typedef float vec4_t[4];
37 
38 static vec4_t s_lerped[MAX_VERTS];
39 
40 vec3_t shadevector;
41 float shadelight[3];
42 
43 // precalculated dot products for quantized angles
44 #define SHADEDOT_QUANT 16
45 
46 float r_avertexnormal_dots[SHADEDOT_QUANT][256] =
47 	#include "anormtab.h"
48 ;
49 
50 float *shadedots = r_avertexnormal_dots[0];
51 
GL_LerpVerts(int nverts,dtrivertx_t * v,dtrivertx_t * ov,dtrivertx_t * verts,float * lerp,float move[3],float frontv[3],float backv[3])52 void GL_LerpVerts(int nverts, dtrivertx_t *v, dtrivertx_t *ov, dtrivertx_t *verts, float *lerp, float move[3], float frontv[3], float backv[3]){
53 	int i;
54 
55 	if(currententity->flags &(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE)){
56 		for(i = 0; i < nverts; i++, v++, ov++, lerp += 4){
57 			float * normal = r_avertexnormals[verts[i].lightnormalindex];
58 
59 			lerp[0] = move[0] + ov->v[0] * backv[0] + v->v[0] * frontv[0] + normal[0] * POWERSUIT_SCALE;
60 			lerp[1] = move[1] + ov->v[1] * backv[1] + v->v[1] * frontv[1] + normal[1] * POWERSUIT_SCALE;
61 			lerp[2] = move[2] + ov->v[2] * backv[2] + v->v[2] * frontv[2] + normal[2] * POWERSUIT_SCALE;
62 		}
63 	} else {
64 		for(i = 0; i < nverts; i++, v++, ov++, lerp += 4){
65 			lerp[0] = move[0] + ov->v[0] * backv[0] + v->v[0] * frontv[0];
66 			lerp[1] = move[1] + ov->v[1] * backv[1] + v->v[1] * frontv[1];
67 			lerp[2] = move[2] + ov->v[2] * backv[2] + v->v[2] * frontv[2];
68 		}
69 	}
70 }
71 
72 /*
73 GL_DrawAliasFrameLerp
74 
75 interpolates between two frames and origins
76 FIXME: batch lerp all vertexes
77 */
GL_DrawAliasFrameLerp(dmdl_t * paliashdr,float backlerp)78 void GL_DrawAliasFrameLerp(dmdl_t *paliashdr, float backlerp){
79 	float l;
80 	daliasframe_t *frame, *oldframe;
81 	dtrivertx_t *v, *ov, *verts;
82 	int *order;
83 	int count;
84 	float frontlerp;
85 	float alpha;
86 	vec3_t move, delta, vectors[3];
87 	vec3_t frontv, backv;
88 	int i;
89 	int index_xyz;
90 	float *lerp;
91 
92 	frame =(daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames
93 							  + currententity->frame * paliashdr->framesize);
94 	verts = v = frame->verts;
95 
96 	oldframe =(daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames
97 								 + currententity->oldframe * paliashdr->framesize);
98 	ov = oldframe->verts;
99 
100 	order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds);
101 
102 	if(currententity->flags & RF_TRANSLUCENT)
103 		alpha = currententity->alpha;
104 	else
105 		alpha = 1.0;
106 
107 	if(currententity->flags &(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE))
108 		qglDisable(GL_TEXTURE_2D);
109 
110 	frontlerp = 1.0 - backlerp;
111 
112 	// move should be the delta back to the previous frame * backlerp
113 	VectorSubtract(currententity->oldorigin, currententity->origin, delta);
114 	AngleVectors(currententity->angles, vectors[0], vectors[1], vectors[2]);
115 
116 	move[0] = DotProduct(delta, vectors[0]);  // forward
117 	move[1] = -DotProduct(delta, vectors[1]);  // left
118 	move[2] = DotProduct(delta, vectors[2]);  // up
119 
120 	VectorAdd(move, oldframe->translate, move);
121 
122 	for(i = 0; i < 3; i++){
123 		move[i] = backlerp * move[i] + frontlerp * frame->translate[i];
124 	}
125 
126 	for(i = 0; i < 3; i++){
127 		frontv[i] = frontlerp * frame->scale[i];
128 		backv[i] = backlerp * oldframe->scale[i];
129 	}
130 
131 	lerp = s_lerped[0];
132 
133 	GL_LerpVerts(paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv);
134 
135 	if(gl_vertex_arrays->value){
136 		float colorArray[MAX_VERTS * 4];
137 
138 		qglEnableClientState(GL_VERTEX_ARRAY);
139 		qglVertexPointer(3, GL_FLOAT, 16, s_lerped);  // padded for SIMD
140 
141 		if(currententity->flags &(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE)){
142 			qglColor4f(shadelight[0], shadelight[1], shadelight[2], alpha);
143 		} else {
144 			qglEnableClientState(GL_COLOR_ARRAY);
145 			qglColorPointer(3, GL_FLOAT, 0, colorArray);
146 
147 			// pre light everything
148 			for(i = 0; i < paliashdr->num_xyz; i++){
149 				float l = shadedots[verts[i].lightnormalindex];
150 
151 				colorArray[i * 3 + 0] = l * shadelight[0];
152 				colorArray[i * 3 + 1] = l * shadelight[1];
153 				colorArray[i * 3 + 2] = l * shadelight[2];
154 			}
155 		}
156 
157 		if(qglLockArraysEXT != 0)
158 			qglLockArraysEXT(0, paliashdr->num_xyz);
159 
160 		while(1){
161 			// get the vertex count and primitive type
162 			count = *order++;
163 			if(!count)
164 				break;  // done
165 			if(count < 0){
166 				count = -count;
167 				qglBegin(GL_TRIANGLE_FAN);
168 			} else {
169 				qglBegin(GL_TRIANGLE_STRIP);
170 			}
171 
172 			if(currententity->flags &(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE)){
173 				do {
174 					index_xyz = order[2];
175 					order += 3;
176 
177 					qglVertex3fv(s_lerped[index_xyz]);
178 
179 				} while(--count);
180 			} else {
181 				do {
182 					// texture coordinates come from the draw list
183 					qglTexCoord2f(((float *)order)[0],((float *)order)[1]);
184 					index_xyz = order[2];
185 
186 					order += 3;
187 
188 					qglArrayElement(index_xyz);
189 
190 				} while(--count);
191 			}
192 			qglEnd();
193 		}
194 
195 		if(qglUnlockArraysEXT != 0)
196 			qglUnlockArraysEXT();
197 	} else {
198 		while(1){
199 			// get the vertex count and primitive type
200 			count = *order++;
201 			if(!count)
202 				break;  // done
203 			if(count < 0){
204 				count = -count;
205 				qglBegin(GL_TRIANGLE_FAN);
206 			} else {
207 				qglBegin(GL_TRIANGLE_STRIP);
208 			}
209 
210 			if(currententity->flags &(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE)){
211 				do {
212 					index_xyz = order[2];
213 					order += 3;
214 
215 					qglColor4f(shadelight[0], shadelight[1], shadelight[2], alpha);
216 					qglVertex3fv(s_lerped[index_xyz]);
217 
218 				} while(--count);
219 			} else {
220 				do {
221 					// texture coordinates come from the draw list
222 					qglTexCoord2f(((float *)order)[0],((float *)order)[1]);
223 					index_xyz = order[2];
224 					order += 3;
225 
226 					// normals and vertexes come from the frame list
227 					l = shadedots[verts[index_xyz].lightnormalindex];
228 
229 					qglColor4f(l* shadelight[0], l*shadelight[1], l*shadelight[2], alpha);
230 					qglVertex3fv(s_lerped[index_xyz]);
231 				} while(--count);
232 			}
233 
234 			qglEnd();
235 		}
236 	}
237 
238 	if(currententity->flags &(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE))
239 		qglEnable(GL_TEXTURE_2D);
240 }
241 
242 
243 /*
244 GL_CullAliasModel
245 */
GL_CullAliasModel(vec3_t bbox[8],entity_t * e)246 static qboolean GL_CullAliasModel(vec3_t bbox[8], entity_t *e){
247 	int i;
248 	vec3_t mins, maxs;
249 	dmdl_t *paliashdr;
250 	vec3_t vectors[3];
251 	vec3_t thismins, oldmins, thismaxs, oldmaxs;
252 	daliasframe_t *pframe, *poldframe;
253 	vec3_t angles;
254 
255 	paliashdr = (dmdl_t *)currentmodel->extradata;
256 
257 	if((e->frame >= paliashdr->num_frames) ||(e->frame < 0)){
258 		Com_Printf("GL_CullAliasModel %s: no such frame %d\n",
259 					   currentmodel->name, e->frame);
260 		e->frame = 0;
261 	}
262 	if((e->oldframe >= paliashdr->num_frames) ||(e->oldframe < 0)){
263 		Com_Printf("GL_CullAliasModel %s: no such oldframe %d\n",
264 					   currentmodel->name, e->oldframe);
265 		e->oldframe = 0;
266 	}
267 
268 	pframe = (daliasframe_t *)((byte *) paliashdr +
269 								   paliashdr->ofs_frames +
270 								   e->frame * paliashdr->framesize);
271 
272 	poldframe = (daliasframe_t *)((byte *) paliashdr +
273 									  paliashdr->ofs_frames +
274 									  e->oldframe * paliashdr->framesize);
275 
276 	// compute axially aligned mins and maxs
277 	if(pframe == poldframe){
278 		for(i = 0; i < 3; i++){
279 			mins[i] = pframe->translate[i];
280 			maxs[i] = mins[i] + pframe->scale[i] * 255;
281 		}
282 	} else {
283 		for(i = 0; i < 3; i++){
284 			thismins[i] = pframe->translate[i];
285 			thismaxs[i] = thismins[i] + pframe->scale[i] * 255;
286 
287 			oldmins[i] = poldframe->translate[i];
288 			oldmaxs[i] = oldmins[i] + poldframe->scale[i] * 255;
289 
290 			if(thismins[i] < oldmins[i])
291 				mins[i] = thismins[i];
292 			else
293 				mins[i] = oldmins[i];
294 
295 			if(thismaxs[i] > oldmaxs[i])
296 				maxs[i] = thismaxs[i];
297 			else
298 				maxs[i] = oldmaxs[i];
299 		}
300 	}
301 
302 	// compute a full bounding box
303 	for(i = 0; i < 8; i++){
304 		vec3_t tmp;
305 
306 		if(i & 1)
307 			tmp[0] = mins[0];
308 		else
309 			tmp[0] = maxs[0];
310 
311 		if(i & 2)
312 			tmp[1] = mins[1];
313 		else
314 			tmp[1] = maxs[1];
315 
316 		if(i & 4)
317 			tmp[2] = mins[2];
318 		else
319 			tmp[2] = maxs[2];
320 
321 		VectorCopy(tmp, bbox[i]);
322 	}
323 
324 	// rotate the bounding box
325 	VectorCopy(e->angles, angles);
326 	angles[YAW] = -angles[YAW];
327 	AngleVectors(angles, vectors[0], vectors[1], vectors[2]);
328 
329 	for(i = 0; i < 8; i++){
330 		vec3_t tmp;
331 
332 		VectorCopy(bbox[i], tmp);
333 
334 		bbox[i][0] = DotProduct(vectors[0], tmp);
335 		bbox[i][1] = -DotProduct(vectors[1], tmp);
336 		bbox[i][2] = DotProduct(vectors[2], tmp);
337 
338 		VectorAdd(e->origin, bbox[i], bbox[i]);
339 	}
340 
341 	{
342 		int p, f, aggregatemask = ~0;
343 
344 		for(p = 0; p < 8; p++){
345 			int mask = 0;
346 
347 			for(f = 0; f < 4; f++){
348 				float dp = DotProduct(frustum[f].normal, bbox[p]);
349 
350 				if((dp - frustum[f].dist) < 0){
351 					mask |=(1 << f);
352 				}
353 			}
354 
355 			aggregatemask &= mask;
356 		}
357 
358 		if(aggregatemask){
359 			return true;
360 		}
361 
362 		return false;
363 	}
364 }
365 
366 /*
367 GL_DrawAliasModel
368 */
GL_DrawAliasModel(entity_t * e)369 void GL_DrawAliasModel(entity_t *e){
370 	int i;
371 	dmdl_t *paliashdr;
372 	float an;
373 	vec3_t bbox[8];
374 	image_t *skin;
375 
376 	if(GL_CullAliasModel(bbox, e))
377 		return;
378 
379 	paliashdr = (dmdl_t *)currentmodel->extradata;
380 
381 	// get lighting information
382 	if(currententity->flags & (RF_SHELL_GREEN | RF_SHELL_RED | RF_SHELL_BLUE)){
383 		VectorClear(shadelight);
384 		if(currententity->flags & RF_SHELL_RED)
385 			shadelight[0] = 1.0;
386 		if(currententity->flags & RF_SHELL_GREEN)
387 			shadelight[1] = 1.0;
388 		if(currententity->flags & RF_SHELL_BLUE)
389 			shadelight[2] = 1.0;
390 	} else if(currententity->flags & RF_FULLBRIGHT || r_fullbright->value){
391 		shadelight[0] = 1.0; shadelight[1] = 1.0; shadelight[2] = 1.0;
392 	} else {
393 		GL_LightPoint(currententity->origin, shadelight);
394 	}
395 
396 	if(currententity->flags & RF_MINLIGHT){
397 		for(i = 0; i < 3; i++)
398 			if(shadelight[i] > 0.1)
399 				break;
400 		if(i == 3){
401 			shadelight[0] = 0.1;
402 			shadelight[1] = 0.1;
403 			shadelight[2] = 0.1;
404 		}
405 	}
406 
407 	// lets not draw anything black
408 	if(shadelight[0] == 0 && shadelight[1] == 0 && shadelight[2] == 0)
409 		shadelight[0] = shadelight[1] = shadelight[2] = .4;
410 
411 	shadedots = r_avertexnormal_dots[((int)(currententity->angles[1] *
412 				(SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];
413 
414 	an = currententity->angles[1] / 180 * M_PI;
415 	shadevector[0] = cos(-an);
416 	shadevector[1] = sin(-an);
417 	shadevector[2] = 1;
418 	VectorNormalize(shadevector);
419 
420 	// locate the proper data
421 	c_alias_polys += paliashdr->num_tris;
422 
423 	// draw all the triangles
424 	qglPushMatrix();
425 	e->angles[PITCH] = -e->angles[PITCH];  // sigh.
426 	GL_RotateForEntity(e);
427 	e->angles[PITCH] = -e->angles[PITCH];  // sigh.
428 
429 	// select skin
430 	if(currententity->skin)
431 		skin = currententity->skin;  // custom player skin
432 	else {
433 		if(currententity->skinnum >= MAX_MD2SKINS)
434 			skin = currentmodel->skins[0];
435 		else {
436 			skin = currentmodel->skins[currententity->skinnum];
437 			if(!skin)
438 				skin = currentmodel->skins[0];
439 		}
440 	}
441 	if(!skin) skin = r_notexture;  // fallback
442 	GL_Bind(skin->texnum);
443 
444 	// draw it
445 	qglShadeModel(GL_SMOOTH);
446 
447 	GL_TexEnv(GL_MODULATE);
448 	if(currententity->flags & RF_TRANSLUCENT){
449 		qglEnable(GL_BLEND);
450 	}
451 
452 	if((currententity->frame >= paliashdr->num_frames) || (currententity->frame < 0)){
453 		Com_Printf( "GL_DrawAliasModel %s: no such frame %d\n",
454 					currentmodel->name, currententity->frame);
455 		currententity->frame = 0;
456 		currententity->oldframe = 0;
457 	}
458 
459 	if((currententity->oldframe >= paliashdr->num_frames) || (currententity->oldframe < 0)){
460 		Com_Printf( "GL_DrawAliasModel %s: no such oldframe %d\n",
461 					currentmodel->name, currententity->oldframe);
462 		currententity->frame = 0;
463 		currententity->oldframe = 0;
464 	}
465 
466 	if(!r_lerpmodels->value)
467 		currententity->backlerp = 0;
468 
469 	GL_DrawAliasFrameLerp(paliashdr, currententity->backlerp);
470 
471 	GL_TexEnv(GL_REPLACE);
472 	qglShadeModel(GL_FLAT);
473 
474 	qglPopMatrix();
475 
476 	if(currententity->flags & RF_TRANSLUCENT){
477 		qglDisable(GL_BLEND);
478 	}
479 
480 	qglColor4ubv(color_white);
481 }
482