1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20 // r_sprite.c
21
22 #include "console.h"
23 #include "model.h"
24 #include "quakedef.h"
25 #include "r_local.h"
26 #include "sys.h"
27
28 static int clip_current;
29 static vec5_t clip_verts[2][MAXWORKINGVERTS];
30 static int sprite_width, sprite_height;
31
32 spritedesc_t r_spritedesc;
33
34 int
R_SpriteDataSize(int pixels)35 R_SpriteDataSize(int pixels)
36 {
37 return pixels * r_pixbytes;
38 }
39
R_SpriteDataStore(mspriteframe_t * frame,const char * modelname,int framenum,byte * pixels)40 void R_SpriteDataStore(mspriteframe_t *frame, const char *modelname,
41 int framenum, byte *pixels)
42 {
43 int i;
44 int size = frame->width * frame->height;
45
46 if (r_pixbytes == 1)
47 {
48 memcpy(&frame->rdata[0], pixels, size);
49 }
50 else if (r_pixbytes == 2)
51 {
52 unsigned short *pixout = (unsigned short *)&frame->rdata[0];
53 for (i = 0; i < size; i++)
54 pixout[i] = d_8to16table[pixels[i]];
55 }
56 else
57 {
58 Sys_Error("%s: driver set invalid r_pixbytes: %d", __func__,
59 r_pixbytes);
60 }
61 }
62
63 /*
64 ================
65 R_RotateSprite
66 ================
67 */
R_RotateSprite(float beamlength)68 static void R_RotateSprite(float beamlength)
69 {
70 vec3_t vec;
71
72 if (beamlength == 0.0)
73 return;
74
75 VectorScale(r_spritedesc.vpn, -beamlength, vec);
76 VectorAdd(r_entorigin, vec, r_entorigin);
77 VectorSubtract(modelorg, vec, modelorg);
78 }
79
80
81 /*
82 =============
83 R_ClipSpriteFace
84
85 Clips the winding at clip_verts[clip_current] and changes clip_current
86 Throws out the back side
87 ==============
88 */
R_ClipSpriteFace(int nump,clipplane_t * pclipplane)89 static int R_ClipSpriteFace(int nump, clipplane_t *pclipplane)
90 {
91 int i, outcount;
92 float dists[MAXWORKINGVERTS + 1];
93 float frac;
94 float *in, *instep, *outstep, *vert2;
95 float clipdist = pclipplane->plane.dist;
96 float *pclipnormal = pclipplane->plane.normal;
97
98 // calc dists
99 if (clip_current)
100 {
101 in = clip_verts[1][0];
102 outstep = clip_verts[0][0];
103 clip_current = 0;
104 } else {
105 in = clip_verts[0][0];
106 outstep = clip_verts[1][0];
107 clip_current = 1;
108 }
109
110 instep = in;
111 for (i = 0; i < nump; i++, instep += sizeof(vec5_t) / sizeof(float))
112 dists[i] = DotProduct(instep, pclipnormal) - clipdist;
113
114 // handle wraparound case
115 dists[nump] = dists[0];
116 memcpy(instep, in, sizeof(vec5_t));
117
118
119 // clip the winding
120 instep = in;
121 outcount = 0;
122
123 for (i = 0; i < nump; i++, instep += sizeof(vec5_t) / sizeof(float))
124 {
125 if (dists[i] >= 0)
126 {
127 memcpy(outstep, instep, sizeof(vec5_t));
128 outstep += sizeof(vec5_t) / sizeof(float);
129 outcount++;
130 }
131
132 if (dists[i] == 0 || dists[i + 1] == 0)
133 continue;
134
135 if ((dists[i] > 0) == (dists[i + 1] > 0))
136 continue;
137
138 // split it into a new vertex
139 frac = dists[i] / (dists[i] - dists[i + 1]);
140
141 vert2 = instep + sizeof(vec5_t) / sizeof(float);
142
143 outstep[0] = instep[0] + frac * (vert2[0] - instep[0]);
144 outstep[1] = instep[1] + frac * (vert2[1] - instep[1]);
145 outstep[2] = instep[2] + frac * (vert2[2] - instep[2]);
146 outstep[3] = instep[3] + frac * (vert2[3] - instep[3]);
147 outstep[4] = instep[4] + frac * (vert2[4] - instep[4]);
148
149 outstep += sizeof(vec5_t) / sizeof(float);
150 outcount++;
151 }
152
153 return outcount;
154 }
155
156
157 /*
158 ================
159 R_SetupAndDrawSprite
160 ================
161 */
R_SetupAndDrawSprite(void)162 static void R_SetupAndDrawSprite(void)
163 {
164 int i, nump;
165 float scale, *pv;
166 vec5_t *pverts;
167 vec3_t left, up, right, down, transformed, local;
168 emitpoint_t *outverts = malloc(sizeof(emitpoint_t)*MAXWORKINGVERTS+1);
169 emitpoint_t *pout;
170 float dot = DotProduct(r_spritedesc.vpn, modelorg);
171
172 // backface cull
173 if (dot >= 0)
174 return;
175
176 // build the sprite poster in worldspace
177 VectorScale(r_spritedesc.vright, r_spritedesc.pspriteframe->right, right);
178 VectorScale(r_spritedesc.vup, r_spritedesc.pspriteframe->up, up);
179 VectorScale(r_spritedesc.vright, r_spritedesc.pspriteframe->left, left);
180 VectorScale(r_spritedesc.vup, r_spritedesc.pspriteframe->down, down);
181
182 pverts = clip_verts[0];
183
184 pverts[0][0] = r_entorigin[0] + up[0] + left[0];
185 pverts[0][1] = r_entorigin[1] + up[1] + left[1];
186 pverts[0][2] = r_entorigin[2] + up[2] + left[2];
187 pverts[0][3] = 0;
188 pverts[0][4] = 0;
189
190 pverts[1][0] = r_entorigin[0] + up[0] + right[0];
191 pverts[1][1] = r_entorigin[1] + up[1] + right[1];
192 pverts[1][2] = r_entorigin[2] + up[2] + right[2];
193 pverts[1][3] = sprite_width;
194 pverts[1][4] = 0;
195
196 pverts[2][0] = r_entorigin[0] + down[0] + right[0];
197 pverts[2][1] = r_entorigin[1] + down[1] + right[1];
198 pverts[2][2] = r_entorigin[2] + down[2] + right[2];
199 pverts[2][3] = sprite_width;
200 pverts[2][4] = sprite_height;
201
202 pverts[3][0] = r_entorigin[0] + down[0] + left[0];
203 pverts[3][1] = r_entorigin[1] + down[1] + left[1];
204 pverts[3][2] = r_entorigin[2] + down[2] + left[2];
205 pverts[3][3] = 0;
206 pverts[3][4] = sprite_height;
207
208 // clip to the frustum in worldspace
209 nump = 4;
210 clip_current = 0;
211
212 for (i = 0; i < 4; i++) {
213 nump = R_ClipSpriteFace(nump, &view_clipplanes[i]);
214 if (nump < 3)
215 return;
216 if (nump >= MAXWORKINGVERTS)
217 Sys_Error("%s: too many points", __func__);
218 }
219
220 // transform vertices into viewspace and project
221 pv = &clip_verts[clip_current][0][0];
222 r_spritedesc.nearzi = -999999;
223
224 for (i = 0; i < nump; i++) {
225 VectorSubtract(pv, r_origin, local);
226 TransformVector(local, transformed);
227
228 if (transformed[2] < NEAR_CLIP)
229 transformed[2] = NEAR_CLIP;
230
231 pout = &outverts[i];
232 pout->zi = 1.0 / transformed[2];
233 if (pout->zi > r_spritedesc.nearzi)
234 r_spritedesc.nearzi = pout->zi;
235
236 pout->s = pv[3];
237 pout->t = pv[4];
238
239 scale = xscale * pout->zi;
240 pout->u = (xcenter + scale * transformed[0]);
241
242 scale = yscale * pout->zi;
243 pout->v = (ycenter - scale * transformed[1]);
244
245 pv += sizeof(vec5_t) / sizeof(*pv);
246 }
247
248 // draw it
249 r_spritedesc.nump = nump;
250 r_spritedesc.pverts = outverts;
251 D_DrawSprite();
252 free(outverts);
253 }
254
255 /*
256 ================
257 R_DrawSprite
258 ================
259 */
R_DrawSprite(const entity_t * e)260 void R_DrawSprite(const entity_t *e)
261 {
262 int i;
263 vec3_t tvec;
264 float dot, angle, sr, cr;
265 msprite_t *psprite = (msprite_t*)e->model->cache.data;
266
267 r_spritedesc.pspriteframe = Mod_GetSpriteFrame(e, psprite, cl.time + e->syncbase);
268
269 sprite_width = r_spritedesc.pspriteframe->width;
270 sprite_height = r_spritedesc.pspriteframe->height;
271
272 // TODO: make this caller-selectable
273 if (psprite->type == SPR_FACING_UPRIGHT) {
274 // generate the sprite's axes, with vup straight up in worldspace, and
275 // r_spritedesc.vright perpendicular to modelorg.
276 // This will not work if the view direction is very close to straight up or
277 // down, because the cross product will be between two nearly parallel
278 // vectors and starts to approach an undefined state, so we don't draw if
279 // the two vectors are less than 1 degree apart
280 tvec[0] = -modelorg[0];
281 tvec[1] = -modelorg[1];
282 tvec[2] = -modelorg[2];
283 VectorNormalize(tvec);
284 dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because
285 // r_spritedesc.vup is 0, 0, 1
286 if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848
287 return;
288 r_spritedesc.vup[0] = 0;
289 r_spritedesc.vup[1] = 0;
290 r_spritedesc.vup[2] = 1;
291 r_spritedesc.vright[0] = tvec[1];
292 // CrossProduct(r_spritedesc.vup, -modelorg,
293 r_spritedesc.vright[1] = -tvec[0];
294 // r_spritedesc.vright)
295 r_spritedesc.vright[2] = 0;
296 VectorNormalize(r_spritedesc.vright);
297 r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
298 r_spritedesc.vpn[1] = r_spritedesc.vright[0];
299 r_spritedesc.vpn[2] = 0;
300 // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
301 // r_spritedesc.vpn)
302 } else if (psprite->type == SPR_VP_PARALLEL) {
303 // generate the sprite's axes, completely parallel to the viewplane. There
304 // are no problem situations, because the sprite is always in the same
305 // position relative to the viewer
306 for (i = 0; i < 3; i++) {
307 r_spritedesc.vup[i] = vup[i];
308 r_spritedesc.vright[i] = vright[i];
309 r_spritedesc.vpn[i] = vpn[i];
310 }
311 } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) {
312 // generate the sprite's axes, with vup straight up in worldspace, and
313 // r_spritedesc.vright parallel to the viewplane.
314 // This will not work if the view direction is very close to straight up or
315 // down, because the cross product will be between two nearly parallel
316 // vectors and starts to approach an undefined state, so we don't draw if
317 // the two vectors are less than 1 degree apart
318 dot = vpn[2]; // same as DotProduct (vpn, r_spritedesc.vup) because
319 // r_spritedesc.vup is 0, 0, 1
320 if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848
321 return;
322 r_spritedesc.vup[0] = 0;
323 r_spritedesc.vup[1] = 0;
324 r_spritedesc.vup[2] = 1;
325 r_spritedesc.vright[0] = vpn[1];
326 // CrossProduct (r_spritedesc.vup, vpn,
327 r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright)
328 r_spritedesc.vright[2] = 0;
329 VectorNormalize(r_spritedesc.vright);
330 r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
331 r_spritedesc.vpn[1] = r_spritedesc.vright[0];
332 r_spritedesc.vpn[2] = 0;
333 // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
334 // r_spritedesc.vpn)
335 } else if (psprite->type == SPR_ORIENTED) {
336 // generate the sprite's axes, according to the sprite's world orientation
337 AngleVectors(e->angles, r_spritedesc.vpn, r_spritedesc.vright,
338 r_spritedesc.vup);
339 } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) {
340 // generate the sprite's axes, parallel to the viewplane, but rotated in
341 // that plane around the center according to the sprite entity's roll
342 // angle. So vpn stays the same, but vright and vup rotate
343 angle = e->angles[ROLL] * (M_PI * 2 / 360);
344 sr = sin(angle);
345 cr = cos(angle);
346
347 for (i = 0; i < 3; i++) {
348 r_spritedesc.vpn[i] = vpn[i];
349 r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr;
350 r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr;
351 }
352 } else {
353 Sys_Error("%s: Bad sprite type %d", __func__, psprite->type);
354 }
355
356 R_RotateSprite(psprite->beamlength);
357
358 R_SetupAndDrawSprite();
359 }
360