scummvm/engines/icb/shadow_pc.cpp

366 lines
10 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
*/
#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