/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * Additional copyright for this file: * Copyright (C) 1999-2000 Revolution Software Ltd. * This code is based on source code created by Revolution Software, * used with permission. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "engines/icb/common/px_common.h" #include "engines/icb/debug.h" #include "engines/icb/shadow_pc.h" #include "engines/icb/softskin_pc.h" #include "engines/icb/drawpoly_pc.h" #include "engines/icb/global_objects_psx.h" #include "engines/icb/actor_pc.h" #include "engines/icb/common/px_capri_maths.h" #include "common/system.h" namespace ICB { // 128 so then it fits into scratch pad ! #define MAX_VECTORS 512 #define MAX_SCRATCHPAD_VECTORS 128 int32 sverttpc; int32 st1pc; int32 st2pc; int32 st3pc; void MakeShadowPC(RapAPI *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); void DrawShadow1PC(RapAPI *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) { if (nShadows == 0) return; SVECTORPC local[MAX_VECTORS]; // compute the current animation positions of the // shadow mesh (rap) vertices int16 xminLocal = +32767; int16 xmaxLocal = -32767; int16 yminLocal = +32767; int16 ymaxLocal = -32767; int16 zminLocal = +32767; int16 zmaxLocal = -32767; sverttpc = g_system->getMillis(); int32 screenScale = 0; int32 nVertices = softskinPC(srap, poseBone, lw, local, &xminLocal, &xmaxLocal, &yminLocal, &ymaxLocal, &zminLocal, &zmaxLocal, screenScale); gte_SetScreenScaleShift_pc(screenScale); // So all the local positions have been made sverttpc = g_system->getMillis() - sverttpc; int32 s; SVECTORPC *pp_n = p_n; int32 *pp_d = p_d; SVECTORPC *pldirs = ldirs; CVECTOR *plcolours = lcolours; for (s = 0; s < nShadows; s++) { MakeShadowPC(srap, local, nVertices, pp_n, *pp_d, pldirs, plcolours, world2screen, local2world, debug, shadowBox[s], shadowBoxMin + s, shadowBoxMax + s, xminLocal, xmaxLocal, yminLocal, ymaxLocal, zminLocal, zmaxLocal); pldirs++; plcolours++; } } void MakeShadowPC(RapAPI *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) { SVECTORPC workVerts[MAX_VECTORS]; SVECTORPC *vertices = nullptr; vertices = workVerts; /* Make the shadow projection matrix projection equation is: P = V + ( D - V.N ) L - - - - - Where: P = is the projection point of vertex V V = vertex to be projected N = normal vector of the plane to project onto (doesn't have to be normalised) D = 'd' in the plane equation of the plane to project onto i.e. D = N.(point_plane) L = normalised light direction = L_direction / (L_direction . N ) This expands into the following "rotation" matrix: / \ | (1 - Nx*Lx) -Ny*Lx -Nz*Lx | | | | -Nx*Ly (1 - Ny*Ly) -Nz*Ly | | | | -Nx*Lz -Ny*Lz (1 - Nz*Lz) | \ / and "transformation" matrix / \ | +Lx*D | | | | +Ly*D | | | | +Lz*D | \ / */ st1pc = g_system->getMillis(); // First-up let us make normalised_light_direction int32 ld = p_n->vx * ldir->vx + p_n->vy * ldir->vy + p_n->vz * ldir->vz; // Can't do a shadow if ld == 0 : light perpendicular to the plane if (ld == 0) return; // To match names in the comments int32 D; SVECTORPC *N; SVECTORPC L; int32 work; D = p_d; N = p_n; // * 4096 to get some fixed point accuracy in there! work = (ldir->vx << 12) / ld; if (work > 32767) work = 32767; if (work < -32767) work = -32767; L.vx = (int32)work; work = (ldir->vy << 12) / ld; if (work > 32767) work = 32767; if (work < -32767) work = -32767; L.vy = (int32)work; work = (ldir->vz << 12) / ld; if (work > 32767) work = 32767; if (work < -32767) work = -32767; L.vz = (int32)work; MATRIXPC sproj; // | (1 - Nx*Lx) -Ny*Lx -Nz*Lx | sproj.m[0][0] = (int32)(4096 - N->vx * L.vx); sproj.m[0][1] = (int32)(-N->vy * L.vx); sproj.m[0][2] = (int32)(-N->vz * L.vx); // | -Nx*Ly (1 - Ny*Ly) -Nz*Ly | sproj.m[1][0] = (int32)(-N->vx * L.vy); sproj.m[1][1] = (int32)(4096 - N->vy * L.vy); sproj.m[1][2] = (int32)(-N->vz * L.vy); // | -Nx*Lz -Ny*Lz (1 - Nz*Lz) | sproj.m[2][0] = (int32)(-N->vx * L.vz); sproj.m[2][1] = (int32)(-N->vy * L.vz); sproj.m[2][2] = (int32)(4096 - N->vz * L.vz); // | +Lx*D | sproj.t[0] = (L.vx * D) >> 12; // go back to integer maths not fixed point // | +Ly*D | sproj.t[1] = (L.vy * D) >> 12; // go back to integer maths not fixed point // | +Lz*D | sproj.t[2] = (L.vz * D) >> 12; // go back to integer maths not fixed point st1pc = g_system->getMillis() - st1pc; st2pc = g_system->getMillis(); SVECTORPC *world = vertices; SVECTORPC *pvert = vertices; int32 flag; SVECTORPC *pworld; SVECTORPC *plocal; SVECTOR *pbbox; plocal = local; pworld = world; VECTOR lpvert; int32 i; // Transform the local vertices into world vertices gte_SetRotMatrix_pc(local2world); gte_SetTransMatrix_pc(local2world); for (i = 0; i < nVertices; i++) { gte_RotTrans_pc(plocal, &lpvert, &flag); pworld->vx = (int32)lpvert.vx; pworld->vy = (int32)lpvert.vy; pworld->vz = (int32)lpvert.vz; plocal++; pworld++; } // Convert the bounding box from local co-ordinates into world co-ordinates bbox[0].vx = xminLocal; bbox[0].vy = yminLocal; bbox[0].vz = zminLocal; bbox[1].vx = xminLocal; bbox[1].vy = yminLocal; bbox[1].vz = zmaxLocal; bbox[2].vx = xmaxLocal; bbox[2].vy = yminLocal; bbox[2].vz = zminLocal; bbox[3].vx = xmaxLocal; bbox[3].vy = yminLocal; bbox[3].vz = zmaxLocal; bbox[4].vx = xmaxLocal; bbox[4].vy = ymaxLocal; bbox[4].vz = zminLocal; bbox[5].vx = xmaxLocal; bbox[5].vy = ymaxLocal; bbox[5].vz = zmaxLocal; bbox[6].vx = xminLocal; bbox[6].vy = ymaxLocal; bbox[6].vz = zminLocal; bbox[7].vx = xminLocal; bbox[7].vy = ymaxLocal; bbox[7].vz = zmaxLocal; pbbox = bbox; for (i = 0; i < 8; i++) { gte_RotTrans_pc(pbbox, &lpvert, &flag); pbbox->vx = (int16)lpvert.vx; pbbox->vy = (int16)lpvert.vy; pbbox->vz = (int16)lpvert.vz; pbbox++; } // So basically do a load of RotTrans to project the world vertices // into a new set of world vertices gte_SetRotMatrix_pc(&sproj); gte_SetTransMatrix_pc(&sproj); SVECTORPC *ppvert; plocal = world; ppvert = pvert; for (i = 0; i < nVertices; i++) { gte_RotTrans_pc(plocal, &lpvert, &flag); ppvert->vx = lpvert.vx; ppvert->vy = lpvert.vy; ppvert->vz = lpvert.vz; plocal++; ppvert++; } // Do the same for the bounding box pbbox = bbox; for (i = 0; i < 8; i++) { gte_RotTrans_pc(pbbox, &lpvert, &flag); pbbox->vx = (int16)lpvert.vx; pbbox->vy = (int16)lpvert.vy; pbbox->vz = (int16)lpvert.vz; pbbox++; } // Put the correct rot and trans matrix in place // transform vertices from world space to screen space gte_SetRotMatrix_pc(world2screen); gte_SetTransMatrix_pc(world2screen); // Loop over the vector pool converting them all to screen co-ordinates int32 p; if (debug == 0) ConvertToScreenCoords(pvert, pvert, nVertices); // Do the same for the bounding box pbbox = bbox; SVECTORPC sxy0; for (i = 0; i < 8; i++) { gte_RotTransPers_pc(pbbox, &sxy0, &p, &flag, (int32 *)&(pbbox->vz)); pbbox->vx = (int16)sxy0.vx; pbbox->vy = (int16)sxy0.vy; pbbox++; } // Find the minimum and maximum screen positions (plus z) pbbox = bbox; copyVector(minbbox, pbbox); copyVector(maxbbox, pbbox); pbbox++; for (i = 1; i < 8; i++, pbbox++) { if (pbbox->vx < minbbox->vx) minbbox->vx = pbbox->vx; if (pbbox->vy < minbbox->vy) minbbox->vy = pbbox->vy; if (pbbox->vz < minbbox->vz) minbbox->vz = pbbox->vz; if (pbbox->vx > maxbbox->vx) maxbbox->vx = pbbox->vx; if (pbbox->vy > maxbbox->vy) maxbbox->vy = pbbox->vy; if (pbbox->vz > maxbbox->vz) maxbbox->vz = pbbox->vz; } st2pc = g_system->getMillis() - st2pc; st3pc = g_system->getMillis(); // Now draw the little blighters unlitPoly.r = lcolour->r; unlitPoly.g = lcolour->g; unlitPoly.b = lcolour->b; unlitPoly.cd = 0x80; // Switch to 0x80 for subtractive once the dutch are fixed. // Now go and find the actual polygon data for this primitive uint32 *polyStart; uint32 nPolys; nPolys = srap->nTRI3; if (nPolys != 0) { polyStart = RapAPIObject::GetTRI3Ptr(srap); // Do the drawing using internal C based debugging drawing code if (debug) { drawTRI3PC(polyStart, nPolys, pvert); } else { fastDrawTRI3PC(polyStart, nPolys, pvert); } } st3pc = g_system->getMillis() - st3pc; } } // End of namespace ICB