mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-23 19:16:21 +00:00
830 lines
20 KiB
C++
830 lines
20 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.
|
|
*
|
|
* 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "trecision/actor.h"
|
|
#include "trecision/anim.h"
|
|
#include "trecision/graphics.h"
|
|
#include "trecision/renderer3d.h"
|
|
#include "trecision/trecision.h"
|
|
#include "trecision/video.h"
|
|
|
|
namespace Trecision {
|
|
#define SHADOWVERTSNUM 42
|
|
|
|
static const int16 _shadowVerts[SHADOWVERTSNUM] = {
|
|
6, 15, 23,
|
|
24, 32, 78,
|
|
80, 81, 83,
|
|
86, 90, 99,
|
|
107, 108, 116,
|
|
155, 157, 158,
|
|
160, 164, 168,
|
|
169, 173, 174,
|
|
187, 188, 192,
|
|
193, 213, 215,
|
|
227, 229, 235,
|
|
238, 249, 250,
|
|
252, 253, 299,
|
|
306, 330, 336
|
|
};
|
|
#define SHADOWFACESNUM 48
|
|
|
|
const int16 _shadowFaces[SHADOWFACESNUM][3] = {
|
|
{22, 21, 5}, {7, 5, 22},
|
|
{7, 19, 5}, {5, 2, 19},
|
|
{27, 24, 16}, {27, 16, 18},
|
|
{18, 16, 9}, {18, 13, 9},
|
|
{13, 9, 2}, {3, 19, 12},
|
|
{25, 26, 17}, {17, 15, 25},
|
|
{17, 19, 15}, {15, 12, 19},
|
|
{20, 23, 8}, {8, 6, 20},
|
|
{6, 9, 3}, {3, 8, 6},
|
|
{12, 3, 4}, {4, 11, 12},
|
|
{35, 4, 11}, {13, 2, 1},
|
|
{1, 14, 13}, {14, 37, 1},
|
|
{1, 34, 37}, {31, 36, 37},
|
|
{37, 30, 31}, {29, 34, 35},
|
|
{35, 29, 28}, {36, 11, 31},
|
|
{30, 37, 14}, {29, 1, 34},
|
|
{28, 4, 35}, {36, 10, 35},
|
|
{35, 32, 10}, {37, 0, 34},
|
|
{37, 33, 0}, {0, 33, 39},
|
|
{39, 40, 0}, {10, 38, 32},
|
|
{32, 41, 38}, {36, 35, 34},
|
|
{36, 37, 35}, {11, 36, 35},
|
|
{38, 40, 41}, {41, 38, 39},
|
|
{2, 19, 13}, {3, 9, 12}
|
|
};
|
|
|
|
Renderer3D::Renderer3D(TrecisionEngine *vm) : _vm(vm) {
|
|
_zBuffer = new int16[ZBUFFERSIZE / 2];
|
|
|
|
_minXClip = 0;
|
|
_minYClip = 0;
|
|
_maxXClip = 0;
|
|
_maxYClip = 0;
|
|
_zBufStartX = 0;
|
|
_zBufStartY = 0;
|
|
_zBufWid = 0;
|
|
_shadowLightNum = 0;
|
|
_totalShadowVerts = 0;
|
|
|
|
// data for the triangle routines
|
|
for (int i = 0; i < 480; ++i) {
|
|
_lEdge[i] = 0;
|
|
_rEdge[i] = 0;
|
|
_lColor[i] = 0;
|
|
_rColor[i] = 0;
|
|
_lZ[i] = 0;
|
|
_rZ[i] = 0;
|
|
_lTextX[i] = 0;
|
|
_rTextX[i] = 0;
|
|
_lTextY[i] = 0;
|
|
_rTextY[i] = 0;
|
|
}
|
|
|
|
for (int i = 0; i < 10; ++i)
|
|
_shadowIntens[i] = 0;
|
|
|
|
for (int i = 0; i < MAXVERTEX; ++i) {
|
|
_vVertex[i].clear();
|
|
_shVertex[i].clear();
|
|
}
|
|
}
|
|
|
|
Renderer3D::~Renderer3D() {
|
|
delete[] _zBuffer;
|
|
}
|
|
|
|
void Renderer3D::textureTriangle(int32 x1, int32 y1, int32 z1, int32 c1, int32 tx1, int32 ty1,
|
|
int32 x2, int32 y2, int32 z2, int32 c2, int32 tx2, int32 ty2,
|
|
int32 x3, int32 y3, int32 z3, int32 c3, int32 tx3, int32 ty3,
|
|
const STexture *t) {
|
|
if (y1 > _maxYClip)
|
|
y1 = _maxYClip;
|
|
if (y1 < _minYClip)
|
|
y1 = _minYClip;
|
|
|
|
int16 yBottom = y1;
|
|
int16 yTop = y1;
|
|
const uint8 *texture = t->_texture;
|
|
|
|
if (yBottom > y2) {
|
|
if (y2 < _minYClip)
|
|
y2 = _minYClip;
|
|
yBottom = y2;
|
|
}
|
|
if (yTop < y2) {
|
|
if (y2 > _maxYClip)
|
|
y2 = _maxYClip;
|
|
yTop = y2;
|
|
}
|
|
if (yBottom > y3) {
|
|
if (y3 < _minYClip)
|
|
y3 = _minYClip;
|
|
yBottom = y3;
|
|
}
|
|
if (yTop < y3) {
|
|
if (y3 > _maxYClip)
|
|
y3 = _maxYClip;
|
|
yTop = y3;
|
|
}
|
|
for (int16 y = yBottom; y < yTop; ++y) {
|
|
_lEdge[y] = _maxXClip;
|
|
_rEdge[y] = _minXClip;
|
|
}
|
|
|
|
// scan the edges of the triangle
|
|
textureScanEdge(x1, y1, z1, c1, tx1, ty1, x2, y2, z2, c2, tx2, ty2);
|
|
textureScanEdge(x2, y2, z2, c2, tx2, ty2, x3, y3, z3, c3, tx3, ty3);
|
|
textureScanEdge(x3, y3, z3, c3, tx3, ty3, x1, y1, z1, c1, tx1, ty1);
|
|
|
|
// Gouraud fill the horizontal scanlines
|
|
for (int16 y = yBottom; y < yTop; ++y) {
|
|
int32 el = _lEdge[y];
|
|
if (el < _minXClip)
|
|
el = _minXClip;
|
|
int32 er = _rEdge[y];
|
|
if (er > _maxXClip)
|
|
er = _maxXClip;
|
|
|
|
// edge right - edge left
|
|
int16 dx = er - el;
|
|
|
|
if (dx > 0) {
|
|
// color of left edge of horizontal scanline
|
|
int32 cl = _lColor[y];
|
|
// slope dc/_dx
|
|
int32 mc = ((int16)(_rColor[y] - cl) << 8) / dx;
|
|
// zbuffer of left edge of horizontal scanline
|
|
int32 zl = _lZ[y];
|
|
// slope _dz/_dx
|
|
int32 mz = ((int32)(_rZ[y] - zl) << 16) / dx;
|
|
// texture x of left edge of horizontal scanline
|
|
int32 olx = _lTextX[y];
|
|
// slope dty/_dx
|
|
int32 mtx = ((int32)(_rTextX[y] - olx) << 16) / dx;
|
|
// texture y of left edge of horizontal scanline
|
|
int32 oly = _lTextY[y];
|
|
// slope dty/_dx
|
|
int32 mty = ((int32)(_rTextY[y] - oly) << 16) / dx;
|
|
// pointer to zbuffer
|
|
int16 *z = _zBuffer + (y - _zBufStartY) * _zBufWid + (el - _zBufStartX);
|
|
uint16 x = el;
|
|
|
|
zl <<= 16;
|
|
cl <<= 8;
|
|
olx <<= 16;
|
|
oly <<= 16;
|
|
// loop through every pixel in horizontal scanline
|
|
while (dx) {
|
|
const int32 screenOffset = zl >> 16;
|
|
if (*z > screenOffset) {
|
|
const uint16 textureX = (uint16)(cl >> 9);
|
|
const uint16 textureY = texture[(olx >> 16) + t->_dx * (oly >> 16)];
|
|
_vm->_graphicsMgr->drawTexturePixel(textureX, textureY, x, y);
|
|
*z = (int16)screenOffset;
|
|
}
|
|
++x; // increase screen x
|
|
++z; // increase zbuffer
|
|
zl += mz; // increase the zbuffer by _dz/_dx
|
|
cl += mc; // increase the color by dc/_dx
|
|
olx += mtx;
|
|
oly += mty;
|
|
--dx; // pixel to do --
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer3D::textureScanEdge(int32 x1, int32 y1, int32 z1, int32 c1, int32 tx1, int32 ty1, int32 x2, int32 y2, int32 z2, int32 c2, int32 tx2, int32 ty2) {
|
|
// make sure that edge goes from top to bottom
|
|
int16 dy = y2 - y1;
|
|
if (dy < 0) {
|
|
SWAP(y1, y2);
|
|
SWAP(x1, x2);
|
|
SWAP(c1, c2);
|
|
SWAP(z1, z2);
|
|
SWAP(tx1, tx2);
|
|
SWAP(ty1, ty2);
|
|
|
|
dy = -dy;
|
|
}
|
|
|
|
if (dy == 0)
|
|
dy = 1;
|
|
|
|
// initialize for stepping
|
|
int32 mx = ((x2 - x1) << 16) / dy; // dx/dy
|
|
int32 mz = ((z2 - z1) << 16) / dy; // dz/dy
|
|
int32 mc = ((c2 - c1) << 8) / dy; // dc/dy
|
|
int32 mtx = ((tx2 - tx1) << 16) / dy;
|
|
int32 mty = ((ty2 - ty1) << 16) / dy;
|
|
|
|
x1 <<= 16; // starting x coordinate
|
|
z1 <<= 16; // starting z coordinate
|
|
c1 <<= 8; // starting c color
|
|
|
|
tx1 <<= 16;
|
|
ty1 <<= 16;
|
|
|
|
// step through edge and record color values along the way
|
|
for (int32 count = y1; count < y2; ++count) {
|
|
int16 x = (uint16)(x1 >> 16);
|
|
if (x < _lEdge[count]) {
|
|
_lEdge[count] = x;
|
|
_lZ[count] = (int16)(z1 >> 16);
|
|
_lTextX[count] = (uint16)(tx1 >> 16);
|
|
_lTextY[count] = (uint16)(ty1 >> 16);
|
|
_lColor[count] = (uint8)(c1 >> 8);
|
|
}
|
|
if (x > _rEdge[count]) {
|
|
_rEdge[count] = x;
|
|
_rZ[count] = (int16)(z1 >> 16);
|
|
_rTextX[count] = (uint16)(tx1 >> 16);
|
|
_rTextY[count] = (uint16)(ty1 >> 16);
|
|
_rColor[count] = (uint8)(c1 >> 8);
|
|
}
|
|
|
|
x1 += mx; // x = x + dx/dy
|
|
c1 += mc; // c = c + dc/dy
|
|
z1 += mz; // z = z + dz/dy
|
|
|
|
tx1 += mtx;
|
|
ty1 += mty;
|
|
}
|
|
}
|
|
|
|
void Renderer3D::shadowTriangle(int32 x1, int32 y1, int32 x2, int32 y2,
|
|
int32 x3, int32 y3, uint8 cv, int32 zv) {
|
|
if (y1 > _maxYClip)
|
|
y1 = _maxYClip;
|
|
if (y1 < _minYClip)
|
|
y1 = _minYClip;
|
|
|
|
int16 yBottom = y1;
|
|
int16 yTop = y1;
|
|
|
|
if (yBottom > y2) {
|
|
if (y2 < _minYClip)
|
|
y2 = _minYClip;
|
|
yBottom = y2;
|
|
}
|
|
if (yTop < y2) {
|
|
if (y2 > _maxYClip)
|
|
y2 = _maxYClip;
|
|
yTop = y2;
|
|
}
|
|
if (yBottom > y3) {
|
|
if (y3 < _minYClip)
|
|
y3 = _minYClip;
|
|
yBottom = y3;
|
|
}
|
|
if (yTop < y3) {
|
|
if (y3 > _maxYClip)
|
|
y3 = _maxYClip;
|
|
yTop = y3;
|
|
}
|
|
|
|
for (int16 y = yBottom; y < yTop; ++y) {
|
|
_lEdge[y] = _maxXClip;
|
|
_rEdge[y] = _minXClip;
|
|
}
|
|
|
|
// scan the edges of the triangle
|
|
shadowScanEdge(x1, y1, x2, y2);
|
|
shadowScanEdge(x2, y2, x3, y3);
|
|
shadowScanEdge(x3, y3, x1, y1);
|
|
|
|
// gouraud fill the horizontal scanlines
|
|
for (int16 y = yBottom; y < yTop; ++y) {
|
|
// coordinate of left edge of horizontal scanline
|
|
int32 el = _lEdge[y];
|
|
if (el < _minXClip)
|
|
el = _minXClip;
|
|
// coordinate of right edge of horizontal scanline
|
|
int32 er = _rEdge[y];
|
|
if (er > _maxXClip)
|
|
er = _maxXClip;
|
|
|
|
// edge right - edge left
|
|
int16 dx = er - el;
|
|
|
|
if (dx > 0) {
|
|
// screen offset
|
|
int16 x = el;
|
|
int16 *zBufferPtr = _zBuffer + (y - _zBufStartY) * _zBufWid + (el - _zBufStartX);
|
|
|
|
// loop through every pixel in horizontal scanline
|
|
while (dx) {
|
|
if (*zBufferPtr != zv) {
|
|
_vm->_graphicsMgr->shadow(x, y, cv);
|
|
*zBufferPtr = zv;
|
|
}
|
|
++x; // increase screen x
|
|
++zBufferPtr; // increase zbuffer
|
|
--dx; // pixel to do --
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer3D::shadowScanEdge(int32 x1, int32 y1, int32 x2, int32 y2) {
|
|
// make sure that edge goes from top to bottom
|
|
int16 dy = y2 - y1;
|
|
if (dy < 0) {
|
|
SWAP(y1, y2);
|
|
SWAP(x1, x2);
|
|
|
|
dy = -dy;
|
|
}
|
|
|
|
if (dy == 0)
|
|
dy = 1;
|
|
|
|
// initialize for stepping
|
|
int32 mx = ((x2 - x1) << 16) / dy; // slope dx/dy
|
|
|
|
x1 <<= 16; // starting x coordinate
|
|
|
|
// step through edge and record color values along the way
|
|
for (int32 count = y1; count < y2; ++count) {
|
|
int16 x = (int16)(x1 >> 16);
|
|
if (x < _lEdge[count])
|
|
_lEdge[count] = x;
|
|
|
|
if (x > _rEdge[count])
|
|
_rEdge[count] = x;
|
|
|
|
x1 += mx; // x = x + dx/dy
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize a 3D Room
|
|
*/
|
|
void Renderer3D::init3DRoom() {
|
|
_vm->_cx = (MAXX - 1) / 2;
|
|
_vm->_cy = (MAXY - 1) / 2;
|
|
|
|
for (int c = 0; c < ZBUFFERSIZE / 2; ++c)
|
|
_zBuffer[c] = 0x7FFF;
|
|
}
|
|
|
|
void Renderer3D::resetZBuffer(Common::Rect area) {
|
|
if (!area.isValidRect())
|
|
return;
|
|
|
|
int size = area.width() * area.height();
|
|
if (size * 2 > ZBUFFERSIZE)
|
|
warning("Warning: _zBuffer size %d!\n", size * 2);
|
|
|
|
int16 *d = _zBuffer;
|
|
for (int i = 0; i < size; ++i)
|
|
*d++ = 0x7FFF;
|
|
}
|
|
|
|
/**
|
|
* Change the clipping area
|
|
*/
|
|
void Renderer3D::setClipping(int16 x1, int16 y1, int16 x2, int16 y2) {
|
|
_minXClip = x1;
|
|
_minYClip = y1;
|
|
_maxXClip = x2;
|
|
_maxYClip = y2;
|
|
}
|
|
|
|
void Renderer3D::setZBufferRegion(int16 sx, int16 sy, int16 dx) {
|
|
_zBufStartX = sx;
|
|
_zBufStartY = sy;
|
|
_zBufWid = dx;
|
|
}
|
|
|
|
/**
|
|
* Determines whether a triangle has clockwise
|
|
* or counterclockwise vertices
|
|
*/
|
|
int8 Renderer3D::clockWise(int16 x1, int16 y1, int16 x2, int16 y2, int16 x3, int16 y3) {
|
|
x2 -= x1;
|
|
y2 -= y1;
|
|
|
|
x3 -= x1;
|
|
y3 -= y1;
|
|
|
|
int32 a1 = ((int32)x2) * y3;
|
|
int32 a2 = ((int32)y2) * x3;
|
|
|
|
if (a1 > a2)
|
|
return 1; // clockwise
|
|
if (a1 < a2)
|
|
return -1; // counterclockwise
|
|
|
|
a1 = ((int32)x2) * x3;
|
|
a2 = ((int32)y2) * y3;
|
|
if (a1 < 0 || a2 < 0)
|
|
return -1;
|
|
|
|
a1 = ((int32)x2) * x2 + ((int32)y2) * y2;
|
|
a2 = ((int32)x3) * x3 + ((int32)y3) * y3;
|
|
if (a1 < a2)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Renderer3D::calcCharacterPoints() {
|
|
Actor *actor = _vm->_actor;
|
|
SCamera *camera = actor->_camera;
|
|
SLight *light = actor->_light;
|
|
int vertexNum = actor->_vertexNum;
|
|
|
|
if (actor->_curAction > hLAST)
|
|
error("Error in drawCharacter() - _curAction > hLAST");
|
|
|
|
int cfp = 0;
|
|
int cur = 0;
|
|
while (cur < actor->_curAction)
|
|
cfp += defActionLen[cur++];
|
|
|
|
if (actor->_curAction == hWALKOUT)
|
|
cfp = 1;
|
|
|
|
cfp += actor->_curFrame;
|
|
|
|
if (actor->_curAction == hLAST)
|
|
cfp = 0;
|
|
|
|
actor->_vertex = &actor->_characterArea[cfp * actor->_vertexNum];
|
|
|
|
_shadowLightNum = 0;
|
|
_totalShadowVerts = 0;
|
|
|
|
// camera matrix
|
|
float e10 = camera->_e1[0];
|
|
float e11 = camera->_e1[1];
|
|
float e12 = camera->_e1[2];
|
|
|
|
float e20 = camera->_e2[0];
|
|
float e21 = camera->_e2[1];
|
|
float e22 = camera->_e2[2];
|
|
|
|
float e30 = camera->_e3[0];
|
|
float e31 = camera->_e3[1];
|
|
float e32 = camera->_e3[2];
|
|
|
|
// Light directions
|
|
float l0 = 0.0f;
|
|
float l1 = 0.0f;
|
|
float l2 = 0.0f;
|
|
|
|
actor->_lim[0] = 32000;
|
|
actor->_lim[1] = -32000;
|
|
actor->_lim[2] = 32000;
|
|
actor->_lim[3] = -32000;
|
|
actor->_lim[4] = 32000;
|
|
actor->_lim[5] = -32000;
|
|
|
|
float t = (actor->_theta * PI2) / 360.0;
|
|
float cost = cos(t);
|
|
float sint = sin(t);
|
|
|
|
// Put all vertices in dark color
|
|
for (int i = 0; i < MAXVERTEX; ++i)
|
|
_vVertex[i]._angle = 180;
|
|
|
|
float dist;
|
|
float tx = 0.0f;
|
|
float ty = 0.0f;
|
|
float tz = 0.0f;
|
|
float pa0, pa1, pa2;
|
|
|
|
for (uint32 i = 0; i < actor->_lightNum; ++i) {
|
|
// if off lint == 0
|
|
// if it has a shadow lint & 0x80
|
|
|
|
int lint = light->_inten & 0x7F;
|
|
if (lint) { // if it's not turned off
|
|
tx = light->_x - actor->_px - actor->_dx; // computes direction vector
|
|
tz = light->_z - actor->_pz - actor->_dz; // between light and actor
|
|
ty = light->_y;
|
|
|
|
if (light->_position) { // if it's attenuated
|
|
dist = sqrt(tx * tx + ty * ty + tz * tz); // Distance light <--> actor
|
|
|
|
// adjust light intensity due to the distance
|
|
if (_vm->floatComp(dist, light->_outr) == 1) // if it's out of range it's off
|
|
lint = 0;
|
|
else if (_vm->floatComp(dist, light->_inr) == 1) // if it's inside the circle it's decreased
|
|
lint = (int)((float)lint * (light->_outr - dist) / (light->_outr - light->_inr));
|
|
}
|
|
}
|
|
|
|
if (lint) { // If it's still on
|
|
// Light rotates around the actor
|
|
l0 = tx * cost - tz * sint;
|
|
l2 = tx * sint + tz * cost;
|
|
l1 = ty;
|
|
t = sqrt(l0 * l0 + l1 * l1 + l2 * l2);
|
|
l0 /= t;
|
|
l1 /= t;
|
|
l2 /= t;
|
|
|
|
// Adjust light intensity according to the spot
|
|
tx = (float)light->_fallOff;
|
|
if (light->_fallOff) { // for light spot only
|
|
ty = (float)light->_hotspot;
|
|
|
|
pa0 = light->_dx * cost - light->_dz * sint;
|
|
pa1 = light->_dy;
|
|
pa2 = light->_dx * sint + light->_dz * cost;
|
|
|
|
t = sqrt(pa0 * pa0 + pa1 * pa1 + pa2 * pa2);
|
|
pa0 /= t;
|
|
pa1 /= t;
|
|
pa2 /= t;
|
|
|
|
tz = acos((pa0 * l0) + (pa1 * l1) + (pa2 * l2)) * 360.0 / PI2;
|
|
tz = CLIP(tz, 0.f, 180.f);
|
|
|
|
// tx falloff
|
|
// ty hotspot
|
|
// tz current angle
|
|
|
|
_shadowIntens[_shadowLightNum] = SHADOWAMBIENT;
|
|
|
|
if (_vm->floatComp(tz, tx) == 1) { // tz > tx - if it's out of the falloff
|
|
lint = 0;
|
|
_shadowIntens[_shadowLightNum] = 0;
|
|
} else if (_vm->floatComp(tz, ty) == 1) { // tz > ty - if it's between the falloff and the hotspot
|
|
lint = (int)((float)lint * (tx - tz) / (tx - ty));
|
|
_shadowIntens[_shadowLightNum] = (int)((float)_shadowIntens[_shadowLightNum] * (tx - tz) / (tx - ty));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((light->_inten & 0x80) && lint) { // if it's shadowed and still on
|
|
|
|
// casts shadow vertices
|
|
for (int j = 0; j < SHADOWVERTSNUM; ++j) {
|
|
pa0 = actor->_vertex[_shadowVerts[j]]._x;
|
|
pa1 = actor->_vertex[_shadowVerts[j]]._y;
|
|
pa2 = actor->_vertex[_shadowVerts[j]]._z;
|
|
|
|
_shVertex[vertexNum + _totalShadowVerts + j]._x = pa0 - (pa1 * l0);
|
|
_shVertex[vertexNum + _totalShadowVerts + j]._z = pa2 - (pa1 * l2);
|
|
_shVertex[vertexNum + _totalShadowVerts + j]._y = 0;
|
|
}
|
|
|
|
// per default all shadows are equally faint
|
|
// _shadowIntens[_shadowLightNum] = SHADOWAMBIENT;
|
|
|
|
++_shadowLightNum;
|
|
_totalShadowVerts += SHADOWVERTSNUM;
|
|
}
|
|
|
|
if (lint) { // if still on
|
|
// adapts the light vector o its intensity
|
|
t = (float)(lint) / 127.0;
|
|
l0 = l0 * t;
|
|
l1 = l1 * t;
|
|
l2 = l2 * t;
|
|
|
|
SVertex *curVertex = actor->_vertex;
|
|
for (int j = 0; j < vertexNum; ++j) {
|
|
pa0 = curVertex->_nx;
|
|
pa1 = curVertex->_ny;
|
|
pa2 = curVertex->_nz;
|
|
|
|
lint = (int)((acos(pa0 * l0 + pa1 * l1 + pa2 * l2) * 360.0) / PI);
|
|
lint = CLIP(lint, 0, 180);
|
|
|
|
_vVertex[j]._angle -= (180 - lint);
|
|
++curVertex;
|
|
}
|
|
}
|
|
|
|
++light;
|
|
}
|
|
|
|
// rearranged light values so they can be viewed
|
|
for (int i = 0; i < vertexNum; ++i)
|
|
_vVertex[i]._angle = CLIP<int32>(_vVertex[i]._angle, 0, 180);
|
|
|
|
// Calculate the distance of the character from the room
|
|
tx = camera->_ex - actor->_px;
|
|
ty = camera->_ey;
|
|
tz = camera->_ez - actor->_pz;
|
|
|
|
dist = tx * e30 + ty * e31 + tz * e32;
|
|
|
|
SVertex *curVertex = actor->_vertex;
|
|
|
|
for (int i = 0; i < vertexNum + _totalShadowVerts; ++i) {
|
|
if (i < vertexNum) {
|
|
l0 = curVertex->_x;
|
|
l1 = curVertex->_z;
|
|
pa1 = ty - curVertex->_y;
|
|
} else {
|
|
l0 = _shVertex[i]._x;
|
|
l1 = _shVertex[i]._z;
|
|
pa1 = ty - _shVertex[i]._y;
|
|
}
|
|
|
|
pa0 = tx - (l0 * cost + l1 * sint); // rotate _curVertex
|
|
pa2 = tz - (-l0 * sint + l1 * cost);
|
|
|
|
l0 = pa0 * e10 + pa1 * e11 + pa2 * e12; // project _curVertex
|
|
l1 = pa0 * e20 + pa1 * e21 + pa2 * e22;
|
|
l2 = pa0 * e30 + pa1 * e31 + pa2 * e32;
|
|
|
|
int x2d = _vm->_cx + (int)((l0 * camera->_fovX) / l2);
|
|
int y2d = _vm->_cy + (int)((l1 * camera->_fovY) / l2);
|
|
|
|
_vVertex[i]._x = x2d;
|
|
_vVertex[i]._y = y2d;
|
|
_vVertex[i]._z = (int32)((dist - l2) * 128.0);
|
|
|
|
actor->_lim[0] = MIN(x2d, actor->_lim[0]);
|
|
actor->_lim[1] = MAX(x2d, actor->_lim[1]);
|
|
actor->_lim[2] = MIN(y2d, actor->_lim[2]);
|
|
actor->_lim[3] = MAX(y2d, actor->_lim[3]);
|
|
|
|
actor->_lim[4] = MIN<int32>(_vVertex[i]._z, actor->_lim[4]);
|
|
actor->_lim[5] = MAX<int32>(_vVertex[i]._z, actor->_lim[5]);
|
|
|
|
++curVertex;
|
|
}
|
|
actor->_lim[4] = (int)dist;
|
|
actor->_lim[5] = (int)dist;
|
|
|
|
// vertex clipping
|
|
if (actor->_lim[0] <= _minXClip + 1) {
|
|
actor->_lim[0] = _minXClip;
|
|
} else {
|
|
--actor->_lim[0];
|
|
}
|
|
|
|
if (actor->_lim[1] >= _maxXClip - 1) {
|
|
actor->_lim[1] = _maxXClip;
|
|
} else {
|
|
++actor->_lim[1];
|
|
}
|
|
|
|
if (actor->_lim[2] <= _minYClip + 1) {
|
|
actor->_lim[2] = _minYClip;
|
|
} else {
|
|
--actor->_lim[2];
|
|
}
|
|
|
|
if (actor->_lim[3] >= _maxYClip - 1) {
|
|
actor->_lim[3] = _maxYClip;
|
|
} else {
|
|
++actor->_lim[3];
|
|
}
|
|
|
|
if (actor->_curAction == hLAST) // exit displacer
|
|
actor->_lim[2] = actor->_lim[3] - (((actor->_lim[3] - actor->_lim[2]) * actor->_curFrame) / defActionLen[hLAST]);
|
|
|
|
// set zbuffer vars
|
|
setZBufferRegion(actor->_lim[0], actor->_lim[2], actor->_lim[1] - actor->_lim[0]);
|
|
}
|
|
|
|
void Renderer3D::drawCharacterFaces() {
|
|
Actor *actor = _vm->_actor;
|
|
STexture *textures = actor->_textures;
|
|
SFace *face = actor->_face;
|
|
int vertexNum = actor->_vertexNum;
|
|
|
|
if (actor->_curAction == hLAST)
|
|
setClipping(0, actor->_lim[2], MAXX, actor->_lim[3]);
|
|
|
|
for (int i = 0; i < _shadowLightNum; ++i) {
|
|
for (int j = 0; j < SHADOWFACESNUM; ++j) {
|
|
int p0 = _shadowFaces[j][0] + vertexNum + i * SHADOWVERTSNUM;
|
|
int p1 = _shadowFaces[j][1] + vertexNum + i * SHADOWVERTSNUM;
|
|
int p2 = _shadowFaces[j][2] + vertexNum + i * SHADOWVERTSNUM;
|
|
|
|
int px0 = _vVertex[p0]._x;
|
|
int py0 = _vVertex[p0]._y;
|
|
int px1 = _vVertex[p1]._x;
|
|
int py1 = _vVertex[p1]._y;
|
|
int px2 = _vVertex[p2]._x;
|
|
int py2 = _vVertex[p2]._y;
|
|
|
|
shadowTriangle(px0, py0, px1, py1, px2, py2, 127 - _shadowIntens[i], (int16)(0x7FF0 + i));
|
|
}
|
|
}
|
|
|
|
for (uint i = 0; i < actor->_faceNum; ++i) {
|
|
int p0 = face->_a;
|
|
int p1 = face->_b;
|
|
int p2 = face->_c;
|
|
|
|
int px0 = _vVertex[p0]._x;
|
|
int py0 = _vVertex[p0]._y;
|
|
int px1 = _vVertex[p1]._x;
|
|
int py1 = _vVertex[p1]._y;
|
|
int px2 = _vVertex[p2]._x;
|
|
int py2 = _vVertex[p2]._y;
|
|
|
|
if (clockWise(px0, py0, px1, py1, px2, py2) > 0) {
|
|
uint16 textureId = face->_mat;
|
|
if (textureId < MAXMAT && textures[textureId].isActive()) {
|
|
textureTriangle(px0, py0, _vVertex[p0]._z, _vVertex[p0]._angle, actor->_textureCoord[i][0][0], actor->_textureCoord[i][0][1],
|
|
px1, py1, _vVertex[p1]._z, _vVertex[p1]._angle, actor->_textureCoord[i][1][0], actor->_textureCoord[i][1][1],
|
|
px2, py2, _vVertex[p2]._z, _vVertex[p2]._angle, actor->_textureCoord[i][2][0], actor->_textureCoord[i][2][1],
|
|
&textures[textureId]);
|
|
}
|
|
}
|
|
|
|
++face;
|
|
}
|
|
|
|
int p0 = 0;
|
|
for (int i = _zBufStartY; i < actor->_lim[3]; ++i) {
|
|
for (int j = 1; j < _zBufWid; ++j) {
|
|
int py1 = (_zBuffer[p0] >= 0x7FF0) * 0x8000;
|
|
int py2 = (_zBuffer[p0 + 1] >= 0x7FF0) * 0x8000;
|
|
|
|
int p1 = _zBuffer[p0] < 0x7FFF;
|
|
int p2 = _zBuffer[p0 + 1] < 0x7FFF;
|
|
|
|
if (p1 != p2) {
|
|
_vm->_graphicsMgr->pixelAliasing(j + _zBufStartX, i);
|
|
|
|
// if the first is the character
|
|
if (p1)
|
|
_zBuffer[p0] = 0x00BF | py1;
|
|
else
|
|
_zBuffer[p0] = 0x003F | py2;
|
|
|
|
if (j + 1 < _zBufWid) {
|
|
++p0;
|
|
++j;
|
|
|
|
// if the second is the character
|
|
if (p2)
|
|
_zBuffer[p0] = 0x00BF | py2;
|
|
else
|
|
_zBuffer[p0] = 0x003F | py1;
|
|
}
|
|
} else {
|
|
// set value alpha max
|
|
if (p1)
|
|
_zBuffer[p0] = 0x00FF | py1;
|
|
else
|
|
_zBuffer[p0] = 0x0000 | py1;
|
|
}
|
|
|
|
++p0;
|
|
|
|
// if it's the last of the line
|
|
if (j == _zBufWid - 1) {
|
|
if (p2)
|
|
_zBuffer[p0] = 0x00FF | py2;
|
|
else
|
|
_zBuffer[p0] = 0x0000 | py2;
|
|
}
|
|
}
|
|
++p0;
|
|
}
|
|
if (actor->_curAction == hLAST)
|
|
setClipping(0, TOP, MAXX, AREA + TOP);
|
|
}
|
|
|
|
/**
|
|
* Draw the character
|
|
*/
|
|
void Renderer3D::drawCharacter(uint8 flag) {
|
|
if (!_vm->_flagShowCharacter)
|
|
return;
|
|
|
|
// Compute pointer to frame
|
|
if (flag & CALCPOINTS)
|
|
calcCharacterPoints();
|
|
|
|
if (flag & DRAWFACES)
|
|
drawCharacterFaces();
|
|
}
|
|
|
|
} // End of namespace Trecision
|