mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-24 03:24:50 +00:00
366 lines
10 KiB
C++
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
|