1 /* ResidualVM - A 3D game interpreter
2 *
3 * ResidualVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the AUTHORS
5 * file distributed with this source distribution.
6 *
7 * Additional copyright for this file:
8 * Copyright (C) 1999-2000 Revolution Software Ltd.
9 * This code is based on source code created by Revolution Software,
10 * used with permission.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * 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 the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 *
26 */
27
28 #include "engines/icb/common/px_common.h"
29 #include "engines/icb/debug.h"
30 #include "engines/icb/shadow_pc.h"
31 #include "engines/icb/softskin_pc.h"
32 #include "engines/icb/drawpoly_pc.h"
33 #include "engines/icb/global_objects_psx.h"
34 #include "engines/icb/actor_pc.h"
35 #include "engines/icb/common/px_capri_maths.h"
36
37 #include "common/system.h"
38
39 namespace ICB {
40
41 // 128 so then it fits into scratch pad !
42 #define MAX_VECTORS 512
43 #define MAX_SCRATCHPAD_VECTORS 128
44
45 int32 sverttpc;
46 int32 st1pc;
47 int32 st2pc;
48 int32 st3pc;
49
50 void MakeShadowPC(rap_API *srap, SVECTORPC *local, int32 nVertices, SVECTORPC *p_n, int32 p_d, SVECTORPC *ldir, CVECTOR *lcolour, MATRIXPC *world2screen, MATRIXPC *local2world,
51 int32 debug, SVECTOR *bbox, SVECTOR *minbbox, SVECTOR *maxbbox, int16 xminLocal, int16 xmaxLocal, int16 yminLocal, int16 ymaxLocal, int16 zminLocal,
52 int16 zmaxLocal);
53
DrawShadow1PC(rap_API * srap,int32 poseBone,MATRIXPC * lw,MATRIXPC * world2screen,MATRIXPC * local2world,int32 nShadows,SVECTORPC * ldirs,CVECTOR * lcolours,SVECTORPC * p_n,int32 * p_d,int32 debug,SVECTOR ** shadowBox,SVECTOR * shadowBoxMin,SVECTOR * shadowBoxMax)54 void DrawShadow1PC(rap_API *srap, int32 poseBone, MATRIXPC *lw, MATRIXPC *world2screen, MATRIXPC *local2world, int32 nShadows, SVECTORPC *ldirs, CVECTOR *lcolours, SVECTORPC *p_n,
55 int32 *p_d, int32 debug, SVECTOR **shadowBox, SVECTOR *shadowBoxMin, SVECTOR *shadowBoxMax) {
56 if (nShadows == 0)
57 return;
58
59 SVECTORPC local[MAX_VECTORS];
60 // compute the current animation positions of the
61 // shadow mesh (rap) vertices
62
63 int16 xminLocal = +32767;
64 int16 xmaxLocal = -32767;
65 int16 yminLocal = +32767;
66 int16 ymaxLocal = -32767;
67 int16 zminLocal = +32767;
68 int16 zmaxLocal = -32767;
69
70 sverttpc = g_system->getMillis();
71 int32 screenScale = 0;
72 int32 nVertices = softskinPC(srap, poseBone, lw, local, &xminLocal, &xmaxLocal, &yminLocal, &ymaxLocal, &zminLocal, &zmaxLocal, screenScale);
73
74 gte_SetScreenScaleShift_pc(screenScale);
75
76 // So all the local positions have been made
77 sverttpc = g_system->getMillis() - sverttpc;
78
79 int32 s;
80 SVECTORPC *pp_n = p_n;
81 int32 *pp_d = p_d;
82 SVECTORPC *pldirs = ldirs;
83 CVECTOR *plcolours = lcolours;
84 for (s = 0; s < nShadows; s++) {
85 MakeShadowPC(srap, local, nVertices, pp_n, *pp_d, pldirs, plcolours, world2screen, local2world, debug, shadowBox[s], shadowBoxMin + s, shadowBoxMax + s, xminLocal,
86 xmaxLocal, yminLocal, ymaxLocal, zminLocal, zmaxLocal);
87 pldirs++;
88 plcolours++;
89 }
90 }
91
MakeShadowPC(rap_API * srap,SVECTORPC * local,int32 nVertices,SVECTORPC * p_n,int32 p_d,SVECTORPC * ldir,CVECTOR * lcolour,MATRIXPC * world2screen,MATRIXPC * local2world,int32 debug,SVECTOR * bbox,SVECTOR * minbbox,SVECTOR * maxbbox,int16 xminLocal,int16 xmaxLocal,int16 yminLocal,int16 ymaxLocal,int16 zminLocal,int16 zmaxLocal)92 void MakeShadowPC(rap_API *srap, SVECTORPC *local, int32 nVertices, SVECTORPC *p_n, int32 p_d, SVECTORPC *ldir, CVECTOR *lcolour, MATRIXPC *world2screen, MATRIXPC *local2world,
93 int32 debug, SVECTOR *bbox, SVECTOR *minbbox, SVECTOR *maxbbox, int16 xminLocal, int16 xmaxLocal, int16 yminLocal, int16 ymaxLocal, int16 zminLocal,
94 int16 zmaxLocal) {
95
96 SVECTORPC workVerts[MAX_VECTORS];
97 SVECTORPC *vertices = NULL;
98
99 vertices = workVerts;
100
101 /*
102 Make the shadow projection matrix
103
104 projection equation is:
105 P = V + ( D - V.N ) L
106 - - - - -
107
108 Where:
109 P = is the projection point of vertex V
110 V = vertex to be projected
111 N = normal vector of the plane to project onto
112 (doesn't have to be normalised)
113 D = 'd' in the plane equation of the plane to project onto
114 i.e. D = N.(point_plane)
115 L = normalised light direction
116 = L_direction / (L_direction . N )
117
118
119 This expands into the following "rotation" matrix:
120
121 / \
122 | (1 - Nx*Lx) -Ny*Lx -Nz*Lx |
123 | |
124 | -Nx*Ly (1 - Ny*Ly) -Nz*Ly |
125 | |
126 | -Nx*Lz -Ny*Lz (1 - Nz*Lz) |
127 \ /
128
129 and "transformation" matrix
130
131 / \
132 | +Lx*D |
133 | |
134 | +Ly*D |
135 | |
136 | +Lz*D |
137 \ /
138 */
139
140 st1pc = g_system->getMillis();
141 // First-up let us make normalised_light_direction
142 int32 ld = p_n->vx * ldir->vx + p_n->vy * ldir->vy + p_n->vz * ldir->vz;
143
144 // Can't do a shadow if ld == 0 : light perpendicular to the plane
145 if (ld == 0)
146 return;
147
148 // To match names in the comments
149 int32 D;
150 SVECTORPC *N;
151 SVECTORPC L;
152 int32 work;
153
154 D = p_d;
155 N = p_n;
156 // * 4096 to get some fixed point accuracy in there!
157 work = (ldir->vx << 12) / ld;
158 if (work > 32767)
159 work = 32767;
160 if (work < -32767)
161 work = -32767;
162 L.vx = (int32)work;
163 work = (ldir->vy << 12) / ld;
164 if (work > 32767)
165 work = 32767;
166 if (work < -32767)
167 work = -32767;
168 L.vy = (int32)work;
169 work = (ldir->vz << 12) / ld;
170 if (work > 32767)
171 work = 32767;
172 if (work < -32767)
173 work = -32767;
174 L.vz = (int32)work;
175
176 MATRIXPC sproj;
177
178 // | (1 - Nx*Lx) -Ny*Lx -Nz*Lx |
179 sproj.m[0][0] = (int32)(4096 - N->vx * L.vx);
180 sproj.m[0][1] = (int32)(-N->vy * L.vx);
181 sproj.m[0][2] = (int32)(-N->vz * L.vx);
182
183 // | -Nx*Ly (1 - Ny*Ly) -Nz*Ly |
184 sproj.m[1][0] = (int32)(-N->vx * L.vy);
185 sproj.m[1][1] = (int32)(4096 - N->vy * L.vy);
186 sproj.m[1][2] = (int32)(-N->vz * L.vy);
187
188 // | -Nx*Lz -Ny*Lz (1 - Nz*Lz) |
189 sproj.m[2][0] = (int32)(-N->vx * L.vz);
190 sproj.m[2][1] = (int32)(-N->vy * L.vz);
191 sproj.m[2][2] = (int32)(4096 - N->vz * L.vz);
192
193 // | +Lx*D |
194 sproj.t[0] = (L.vx * D) >> 12; // go back to integer maths not fixed point
195
196 // | +Ly*D |
197 sproj.t[1] = (L.vy * D) >> 12; // go back to integer maths not fixed point
198
199 // | +Lz*D |
200 sproj.t[2] = (L.vz * D) >> 12; // go back to integer maths not fixed point
201
202 st1pc = g_system->getMillis() - st1pc;
203 st2pc = g_system->getMillis();
204
205 SVECTORPC *world = vertices;
206 SVECTORPC *pvert = vertices;
207
208 int32 flag;
209 SVECTORPC *pworld;
210 SVECTORPC *plocal;
211 SVECTOR *pbbox;
212
213 plocal = local;
214 pworld = world;
215 VECTOR lpvert;
216 int32 i;
217
218 // Transform the local vertices into world vertices
219 gte_SetRotMatrix_pc(local2world);
220 gte_SetTransMatrix_pc(local2world);
221 for (i = 0; i < nVertices; i++) {
222 gte_RotTrans_pc(plocal, &lpvert, &flag);
223 pworld->vx = (int32)lpvert.vx;
224 pworld->vy = (int32)lpvert.vy;
225 pworld->vz = (int32)lpvert.vz;
226 plocal++;
227 pworld++;
228 }
229
230 // Convert the bounding box from local co-ordinates into world co-ordinates
231 bbox[0].vx = xminLocal;
232 bbox[0].vy = yminLocal;
233 bbox[0].vz = zminLocal;
234
235 bbox[1].vx = xminLocal;
236 bbox[1].vy = yminLocal;
237 bbox[1].vz = zmaxLocal;
238
239 bbox[2].vx = xmaxLocal;
240 bbox[2].vy = yminLocal;
241 bbox[2].vz = zminLocal;
242
243 bbox[3].vx = xmaxLocal;
244 bbox[3].vy = yminLocal;
245 bbox[3].vz = zmaxLocal;
246
247 bbox[4].vx = xmaxLocal;
248 bbox[4].vy = ymaxLocal;
249 bbox[4].vz = zminLocal;
250
251 bbox[5].vx = xmaxLocal;
252 bbox[5].vy = ymaxLocal;
253 bbox[5].vz = zmaxLocal;
254
255 bbox[6].vx = xminLocal;
256 bbox[6].vy = ymaxLocal;
257 bbox[6].vz = zminLocal;
258
259 bbox[7].vx = xminLocal;
260 bbox[7].vy = ymaxLocal;
261 bbox[7].vz = zmaxLocal;
262
263 pbbox = bbox;
264 for (i = 0; i < 8; i++) {
265 gte_RotTrans_pc(pbbox, &lpvert, &flag);
266 pbbox->vx = (int16)lpvert.vx;
267 pbbox->vy = (int16)lpvert.vy;
268 pbbox->vz = (int16)lpvert.vz;
269 pbbox++;
270 }
271
272 // So basically do a load of RotTrans to project the world vertices
273 // into a new set of world vertices
274 gte_SetRotMatrix_pc(&sproj);
275 gte_SetTransMatrix_pc(&sproj);
276 SVECTORPC *ppvert;
277 plocal = world;
278 ppvert = pvert;
279
280 for (i = 0; i < nVertices; i++) {
281 gte_RotTrans_pc(plocal, &lpvert, &flag);
282 ppvert->vx = lpvert.vx;
283 ppvert->vy = lpvert.vy;
284 ppvert->vz = lpvert.vz;
285
286 plocal++;
287 ppvert++;
288 }
289
290 // Do the same for the bounding box
291 pbbox = bbox;
292 for (i = 0; i < 8; i++) {
293 gte_RotTrans_pc(pbbox, &lpvert, &flag);
294 pbbox->vx = (int16)lpvert.vx;
295 pbbox->vy = (int16)lpvert.vy;
296 pbbox->vz = (int16)lpvert.vz;
297 pbbox++;
298 }
299
300 // Put the correct rot and trans matrix in place
301 // transform vertices from world space to screen space
302 gte_SetRotMatrix_pc(world2screen);
303 gte_SetTransMatrix_pc(world2screen);
304
305 // Loop over the vector pool converting them all to screen co-ordinates
306 int32 p;
307 if (debug == 0)
308 ConvertToScreenCoords(pvert, pvert, nVertices);
309
310 // Do the same for the bounding box
311 pbbox = bbox;
312 SVECTORPC sxy0;
313 for (i = 0; i < 8; i++) {
314 gte_RotTransPers_pc(pbbox, &sxy0, &p, &flag, (int32 *)&(pbbox->vz));
315 pbbox->vx = (int16)sxy0.vx;
316 pbbox->vy = (int16)sxy0.vy;
317 pbbox++;
318 }
319
320 // Find the minimum and maximum screen positions (plus z)
321 pbbox = bbox;
322 copyVector(minbbox, pbbox);
323 copyVector(maxbbox, pbbox);
324 pbbox++;
325 for (i = 1; i < 8; i++, pbbox++) {
326 if (pbbox->vx < minbbox->vx)
327 minbbox->vx = pbbox->vx;
328 if (pbbox->vy < minbbox->vy)
329 minbbox->vy = pbbox->vy;
330 if (pbbox->vz < minbbox->vz)
331 minbbox->vz = pbbox->vz;
332 if (pbbox->vx > maxbbox->vx)
333 maxbbox->vx = pbbox->vx;
334 if (pbbox->vy > maxbbox->vy)
335 maxbbox->vy = pbbox->vy;
336 if (pbbox->vz > maxbbox->vz)
337 maxbbox->vz = pbbox->vz;
338 }
339
340 st2pc = g_system->getMillis() - st2pc;
341 st3pc = g_system->getMillis();
342
343 // Now draw the little blighters
344 unlitPoly.r = lcolour->r;
345 unlitPoly.g = lcolour->g;
346 unlitPoly.b = lcolour->b;
347 unlitPoly.cd = 0x80; // Switch to 0x80 for subtractive once the dutch are fixed.
348
349 // Now go and find the actual polygon data for this primitive
350 uint32 *polyStart;
351 uint32 nPolys;
352
353 nPolys = srap->nTRI3;
354 if (nPolys != 0) {
355 polyStart = srap->GetTRI3Ptr();
356 // Do the drawing using internal C based debugging drawing code
357 #if CD_MODE == 0
358 if (debug)
359 drawTRI3PC(polyStart, nPolys, pvert);
360 else
361 #endif // #if CD_MODE == 0
362 fastDrawTRI3PC(polyStart, nPolys, pvert);
363 }
364 st3pc = g_system->getMillis() - st3pc;
365 }
366
367 } // End of namespace ICB
368