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