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