1 /*
2 	sw32_riqm.c
3 
4 	32bit SW IQM rendering
5 
6 	Copyright (C) 2012 Bill Currie <bill@taniwha.org>
7 
8 	Author: Bill Currie <bill@taniwha.org>
9 	Date: 2012/5/18
10 
11 	This program is free software; you can redistribute it and/or
12 	modify it under the terms of the GNU General Public License
13 	as published by the Free Software Foundation; either version 2
14 	of the License, or (at your option) any later version.
15 
16 	This program is distributed in the hope that it will be useful,
17 	but WITHOUT ANY WARRANTY; without even the implied warranty of
18 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 
20 	See the GNU General Public License for more details.
21 
22 	You should have received a copy of the GNU General Public License
23 	along with this program; if not, write to:
24 
25 		Free Software Foundation, Inc.
26 		59 Temple Place - Suite 330
27 		Boston, MA  02111-1307, USA
28 
29 */
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33 
34 #ifdef HAVE_STRING_H
35 # include <string.h>
36 #endif
37 #ifdef HAVE_STRINGS_H
38 # include <strings.h>
39 #endif
40 #include <stdlib.h>
41 
42 #define NH_DEFINE
43 #include "namehack.h"
44 
45 #include "QF/cvar.h"
46 #include "QF/image.h"
47 #include "QF/render.h"
48 #include "QF/skin.h"
49 #include "QF/sys.h"
50 
51 #include "d_ifacea.h"
52 #include "r_internal.h"
53 
54 #define LIGHT_MIN	5					// lowest light value we'll allow, to
55 										// avoid the need for inner-loop light
56 										// clamping
57 
58 static vec3_t r_plightvec;
59 static int    r_ambientlight;
60 static float  r_shadelight;
61 
62 static inline int
calc_light(float * normal)63 calc_light (float *normal)
64 {
65 	float       lightcos = DotProduct (normal, r_plightvec);
66 	int         temp = r_ambientlight;
67 
68 	if (lightcos < 0) {
69 		temp += (int) (r_shadelight * lightcos);
70 
71 		// clamp; because we limited the minimum ambient and shading
72 		// light, we don't have to clamp low light, just bright
73 		if (temp < 0)
74 			temp = 0;
75 	}
76 	return temp;
77 }
78 
79 static void
R_IQMTransformAndProjectFinalVerts(iqm_t * iqm,swiqm_t * sw,iqmframe_t * frame)80 R_IQMTransformAndProjectFinalVerts (iqm_t *iqm, swiqm_t *sw, iqmframe_t *frame)
81 {
82 	finalvert_t *fv = pfinalverts;
83 	float       zi;
84 	int         i;
85 
86 	for (i = 0; i < iqm->num_verts; i++, fv++) {
87 		byte       *vert = iqm->vertices + i * iqm->stride;
88 		uint32_t    bind = *(uint32_t *) (vert + sw->bindices->offset);
89 		vec_t      *mat = (vec_t *) &frame[bind];
90 		float      *position = (float *) (vert + sw->position->offset);
91 		float      *normal = (float *) (vert + sw->normal->offset);
92 		int32_t    *texcoord = (int32_t *) (vert + sw->texcoord->offset);
93 		vec3_t      tv, tn;
94 		Mat4MultVec (mat, position, tv);
95 		Mat4as3MultVec (mat, normal, tn);
96 		zi = 1.0 / (DotProduct (tv, sw32_aliastransform[2])
97 					+ sw32_aliastransform[2][3]);
98 		fv->v[5] = zi;
99 		fv->v[0] = (DotProduct (tv, sw32_aliastransform[0])
100 					+ sw32_aliastransform[0][3]) * zi + aliasxcenter;
101 		fv->v[1] = (DotProduct (tv, sw32_aliastransform[1])
102 					+ sw32_aliastransform[1][3]) * zi + aliasxcenter;
103 		fv->v[2] = texcoord[0];
104 		fv->v[3] = texcoord[1];
105 		fv->v[4] = calc_light (tn);
106 	}
107 }
108 
109 static void
iqm_setup_skin(swiqm_t * sw,int skinnum)110 iqm_setup_skin (swiqm_t *sw, int skinnum)
111 {
112 	tex_t      *skin = sw->skins[skinnum];
113 
114 	sw32_r_affinetridesc.pskin = skin->data;
115 	sw32_r_affinetridesc.skinwidth = skin->width;
116 	sw32_r_affinetridesc.skinheight = skin->height;
117 	sw32_r_affinetridesc.seamfixupX16 = (skin->width >> 1) << 16;
118 }
119 
120 static void
R_IQMPrepareUnclippedPoints(iqm_t * iqm,swiqm_t * sw,iqmframe_t * frame)121 R_IQMPrepareUnclippedPoints (iqm_t *iqm, swiqm_t *sw, iqmframe_t *frame)
122 {
123 	int         i;
124 
125 	R_IQMTransformAndProjectFinalVerts (iqm, sw, frame);
126 
127 	sw32_r_affinetridesc.pfinalverts = pfinalverts;
128 	for (i = 0; i < iqm->num_meshes; i++) {
129 		iqmmesh    *mesh = &iqm->meshes[i];
130 		uint16_t   *tris;
131 
132 		iqm_setup_skin (sw, i);
133 
134 		tris = iqm->elements + mesh->first_triangle;
135 		sw32_r_affinetridesc.ptriangles = (mtriangle_t *) tris;
136 		sw32_r_affinetridesc.numtriangles = mesh->num_triangles;
137 		sw32_D_PolysetDraw ();
138 	}
139 }
140 
141 static void
R_IQMPreparePoints(iqm_t * iqm,swiqm_t * sw,iqmframe_t * frame)142 R_IQMPreparePoints (iqm_t *iqm, swiqm_t *sw, iqmframe_t *frame)
143 {
144 	finalvert_t *fv = pfinalverts;
145 	auxvert_t  *av = pauxverts;
146 	int         i;
147 	uint32_t    j;
148 	finalvert_t *pfv[3];
149 
150 	for (i = 0; i < iqm->num_verts; i++, fv++, av++) {
151 		byte       *vert = iqm->vertices + i * iqm->stride;
152 		uint32_t    bind = *(uint32_t *) (vert + sw->bindices->offset);
153 		vec_t      *mat = (vec_t *) &frame[bind];
154 		float      *position = (float *) (vert + sw->position->offset);
155 		float      *normal = (float *) (vert + sw->normal->offset);
156 		int32_t    *texcoord = (int32_t *) (vert + sw->texcoord->offset);
157 		vec3_t      tv, tn;
158 		Mat4MultVec (mat, position, tv);
159 		Mat4as3MultVec (mat, normal, tn);
160 		av->fv[0] = DotProduct (tv, sw32_aliastransform[0])
161 			+ sw32_aliastransform[0][3];
162 		av->fv[1] = DotProduct (tv, sw32_aliastransform[1])
163 			+ sw32_aliastransform[1][3];
164 		av->fv[2] = DotProduct (tv, sw32_aliastransform[2])
165 			+ sw32_aliastransform[2][3];
166 		fv->v[2] = texcoord[0];
167 		fv->v[3] = texcoord[1];
168 		fv->flags = 0;
169 		fv->v[4] = calc_light (tn);
170 		sw32_R_AliasClipAndProjectFinalVert (fv, av);
171 	}
172 
173 	for (i = 0; i < iqm->num_meshes; i++) {
174 		iqmmesh    *mesh = &iqm->meshes[i];
175 		mtriangle_t *mtri;
176 
177 		iqm_setup_skin (sw, i);
178 
179 		mtri = (mtriangle_t *) iqm->elements + mesh->first_triangle;
180 		sw32_r_affinetridesc.numtriangles = 1;
181 		for (j = 0; j < mesh->num_triangles; j++, mtri++) {
182 			pfv[0] = &pfinalverts[mtri->vertindex[0]];
183 			pfv[1] = &pfinalverts[mtri->vertindex[1]];
184 			pfv[2] = &pfinalverts[mtri->vertindex[2]];
185 
186 			if (pfv[0]->flags & pfv[1]->flags & pfv[2]->flags
187 				& (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP))
188 				continue;					// completely clipped
189 
190 			if (!((pfv[0]->flags | pfv[1]->flags | pfv[2]->flags)
191 				  & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP))) {// totally unclipped
192 				sw32_r_affinetridesc.pfinalverts = pfinalverts;
193 				sw32_r_affinetridesc.ptriangles = mtri;
194 				sw32_D_PolysetDraw ();
195 			} else {						// partially clipped
196 				sw32_R_AliasClipTriangle (mtri);
197 			}
198 		}
199 	}
200 }
201 
202 static void
R_IQMSetupLighting(entity_t * ent,alight_t * plighting)203 R_IQMSetupLighting (entity_t *ent, alight_t *plighting)
204 {
205 	// guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't
206 	// have to clamp off the bottom
207 	r_ambientlight = plighting->ambientlight;
208 
209 	if (r_ambientlight < LIGHT_MIN)
210 		r_ambientlight = LIGHT_MIN;
211 
212 	r_ambientlight = (255 - r_ambientlight) << VID_CBITS;
213 
214 	if (r_ambientlight < LIGHT_MIN)
215 		r_ambientlight = LIGHT_MIN;
216 
217 	r_shadelight = plighting->shadelight;
218 
219 	if (r_shadelight < 0)
220 		r_shadelight = 0;
221 
222 	r_shadelight *= VID_GRADES;
223 
224 	// rotate the lighting vector into the model's frame of reference
225 	r_plightvec[0] = DotProduct (plighting->plightvec, ent->transform + 0);
226 	r_plightvec[1] = DotProduct (plighting->plightvec, ent->transform + 4);
227 	r_plightvec[2] = DotProduct (plighting->plightvec, ent->transform + 8);
228 }
229 
230 static void
R_IQMSetUpTransform(int trivial_accept)231 R_IQMSetUpTransform (int trivial_accept)
232 {
233 	int         i;
234 	float       rotationmatrix[3][4];
235 	static float viewmatrix[3][4];
236 	vec3_t      forward, left, up;
237 
238 	VectorCopy (currententity->transform + 0, forward);
239 	VectorCopy (currententity->transform + 4, left);
240 	VectorCopy (currententity->transform + 8, up);
241 
242 // TODO: can do this with simple matrix rearrangement
243 
244 	for (i = 0; i < 3; i++) {
245 		rotationmatrix[i][0] = forward[i];
246 		rotationmatrix[i][1] = left[i];
247 		rotationmatrix[i][2] = up[i];
248 	}
249 
250 	rotationmatrix[0][3] = -modelorg[0];
251 	rotationmatrix[1][3] = -modelorg[1];
252 	rotationmatrix[2][3] = -modelorg[2];
253 
254 // TODO: should be global, set when vright, etc., set
255 	VectorCopy (vright, viewmatrix[0]);
256 	VectorCopy (vup, viewmatrix[1]);
257 	VectorNegate (viewmatrix[1], viewmatrix[1]);
258 	VectorCopy (vpn, viewmatrix[2]);
259 
260 //	viewmatrix[0][3] = 0;
261 //	viewmatrix[1][3] = 0;
262 //	viewmatrix[2][3] = 0;
263 
264 	R_ConcatTransforms (viewmatrix, rotationmatrix, sw32_aliastransform);
265 
266 // do the scaling up of x and y to screen coordinates as part of the transform
267 // for the unclipped case (it would mess up clipping in the clipped case).
268 // Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y
269 // correspondingly so the projected x and y come out right
270 // FIXME: make this work for clipped case too?
271 
272 	if (trivial_accept) {
273 		for (i = 0; i < 4; i++) {
274 			sw32_aliastransform[0][i] *= aliasxscale *
275 				(1.0 / ((float) 0x8000 * 0x10000));
276 			sw32_aliastransform[1][i] *= aliasyscale *
277 				(1.0 / ((float) 0x8000 * 0x10000));
278 			sw32_aliastransform[2][i] *= 1.0 / ((float) 0x8000 * 0x10000);
279 		}
280 	}
281 }
282 
283 void
sw32_R_IQMDrawModel(alight_t * plighting)284 sw32_R_IQMDrawModel (alight_t *plighting)
285 {
286 	entity_t   *ent = currententity;
287 	model_t    *model = ent->model;
288 	iqm_t      *iqm = (iqm_t *) model->aliashdr;
289 	swiqm_t    *sw = (swiqm_t *) iqm->extra_data;
290 	int         size;
291 	float       blend;
292 	iqmframe_t *frame;
293 
294 	size = (CACHE_SIZE - 1)
295 		+ sizeof (finalvert_t) * (iqm->num_verts + 1)
296 		+ sizeof (auxvert_t) * iqm->num_verts;
297 	blend = R_IQMGetLerpedFrames (ent, iqm);
298 	frame = R_IQMBlendPalette (iqm, ent->pose1, ent->pose2, blend, size,
299 							   sw->blend_palette, sw->palette_size);
300 
301 	pfinalverts = (finalvert_t *) &frame[sw->palette_size];
302 	pfinalverts = (finalvert_t *)
303 		(((intptr_t) &pfinalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
304 	pauxverts = (auxvert_t *) &pfinalverts[iqm->num_verts + 1];
305 
306 	R_IQMSetUpTransform (ent->trivial_accept);
307 
308 	R_IQMSetupLighting (ent, plighting);
309 
310 	//if (!sw32_acolormap)
311 		sw32_acolormap = vid.colormap8;
312 
313 	if (ent != vr_data.view_model)
314 		sw32_ziscale = (float) 0x8000 *(float) 0x10000;
315 	else
316 		sw32_ziscale = (float) 0x8000 *(float) 0x10000 *3.0;
317 
318 	if (ent->trivial_accept)
319 		R_IQMPrepareUnclippedPoints (iqm, sw, frame);
320 	else
321 		R_IQMPreparePoints (iqm, sw, frame);
322 }
323